Journal mechanism of HFS+/HFSX is described by Apple's site. Here, I provide a specific example using sample data to understand journal behavior.
I use a small HFS+ image named "sample_journal". The size of the image is 32MB and .journal is 512KB.
The journal begins with a journal header. The values of sample data are the following.
(* The value inside of .journal is interpreted as little-endian)
Byte Range |
Description |
Value |
0-3 |
Signature |
0x784C4E4A |
4-7 |
Endian |
0x12345678 |
8-15 |
The start of the first transaction |
68096 (0x0000000000010A00) |
16-23 |
The end of the last transaction |
139776 (0x0000000000022200) |
24-31 |
The size of the journal in bytes |
524288 (0x0000000000080000) |
32-35 |
The size of one block list header in bytes |
8192 (0x00002000) |
36-39 |
The checksum of the journal header |
0x08CE9036 |
40-43 |
The size of journal header in bytes |
512 (00000200) |
If the start of the first transaction and the end of the last transaction are the same value, journal data was written to the actual file system. So in this case there is data which are not reflected in the file system. Let's move to offset 68096.
A transaction constitutes of block list header, block info and block data. The values of block list header in sample data are the following.
Byte Range |
Description |
Value |
0-1 |
The maximum number of blocks (on disk, this field is reserved) |
0x01FF |
2-3 |
The number of block info |
2 (0x0002) |
4-7 |
The number of bytes in this block (block list header, block info and block data) |
8704 (0x2200) |
8-11 |
The checksum of the block list header |
0x87ECDCB7 |
12-15 |
Alignment padding. This field is reserved. |
0x00000003 |
Alignment padding. This field is reserved.
0x00000003
A block list header is followed by block info. The size of one element of block info is 16 bytes, and the first element is used to indicate whether the transaction contains additional block lists. We don't have to pay attention to the first element of the block info because on-disk value of it has no meaning. Therefore we have an interest in the second element of block info. The structure of block info and values of the second element is the following.
Byte Range |
Description |
Value |
0-7 |
The sector number where the data in this block must be written |
2 (0x0000000000000002) |
8-11 |
The number of bytes to copied |
512 (0x00000200) |
12-15 |
This field is meaningful only for the first element of the block list (on disk, this field has no meaning) |
0xDD3A6BE1 |
The size of one block list header is 8192 bytes, so let's move to offset 76288 (=68096+8192).
The range of offset 76288-76799 (512 bytes) should be written to sector 2, which is located in $Volume Header.
You may notice a difference between current $Volume Header and block data corresponding to $Volume Header. In this case, the transaction has not processed yet. $Volume Header has core data for file system such as the number of files, the number of unused allocation blocks, etc. $Volume Header is updated with frequency so it contains many $Volume Header in the journal.
Just the same procedure, we can interpret remaining data. It contains metadata such as $Bitmap, $Catalog, $Attributes, etc. in block data of the transaction.