HFS+ Journal Basics

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.

HFS Journal Basics 01

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.

HFS Journal Basics 02

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).

HFS Journal Basics 03

The range of offset 76288-76799 (512 bytes) should be written to sector 2, which is located in $Volume Header.

HFS Journal Basics 04

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.