Beyond TN1150: hard link chains and directory hard links

This fairly long post requires a detailed knowledge of HFS+ internals.
If you don't know what I am talking about, just skip it.

Apple's HFS+ has mostly been stable throughout its life, with few major changes (journaling, HFSX, extended metadata, named forks). It is also very well documented, unlike some other proprietary filesystems I won't name.
With OSX 10.5, Apple introduced Time Machine and threw in the mix two new undocumented HFS+ objects: hard link chains and directory hard links.

The first are a natural extension of the so-called hard link indirect inodes, they just have a new bit (0×020) set in the catalog entry flags to signal that the indirect inode is part of a double-linked list whose elements are all indirect inodes pointing to the same file. This has other ramification into link management and namei resolution which I won't discuss here – if you're curious I suggest you read the source code of XNU's hfs driver here (unless you want to port this feature to other OSes without incurring in Apple's wrath).
The file hard linking mechanism is exactly like the one from 10.3 (eg. old inode is moved into the filesystem's private metadata directory and named iNode%u, etc.). Owner ID and group ID fields are used as list pointers (previous and next element, respectively).

Directory hard linking, instead, is a whole new and inherently dangerous beast: they can be created only under specific circumstances and should not be created or modified by anything other than Time Machine and fsck.hfs.
The VFS on 10.5 sees these links as plain directories, and pre-10.5 systems see them as directory aliases. No other OS with HFS+ support is able to resolve them.

Let's take a closer look.
Directory hard links are in fact indirect hard link inodes: unlike file hard links, they must be part of an hard link chain, have different finder type and creator codes ('fdrp' and 'MACS' respectively), and have a resource fork which allows pre-10.5 systems to access the real directory.
The real directory itself is moved into a special top-level directory called ".HFS+ Private Directory Data\xd" and renamed dir_%u (where %u is its catalog id – inode number in UNIX parlance). It also gets a special extended attribute (com.apple.system.hfs.firstlink) pointing to the head of the link chain.
Like file hard links, the target inode number is stored in place of the link count field and the creation date is set to the filesystem creation date.

The resolution process is fairly straightforward, but that will be fodder for another entry :-)

Leave a Reply