12.5. The Volume HeaderThe most critical structure of an HFS+ volume is the 512-byte volume header, which is stored at a 1024-byte offset from the start of the volumeimmediately after the first reserved area. The information contained in the volume header includes locations of various other important data structures. Unlike the volume header, these other structures do not have a predefined, fixed locationthe volume header serves as a starting point for the operating system (or other entities, such as a disk utility) while accessing the volume. A copy of the volume headerthe alternate volume headeris stored at a 1024-byte offset from the end of the volume, immediately before the last reserved area. Disk and file system repair utilities typically make use of this copy. 12.5.1. Viewing the Volume HeaderLet us use hfsdebug to display the volume header of an HFS+ volume, which we will first create using hdiutil. The following hdiutil command line creates a disk image containing a 32MB journaled HFS+ volume, with the volume name being HFSJ. We can then mount the volume using either the hdiutil or open command-line programs. $ hdiutil create -size 32m -fs HFSJ -volname HFSJ /tmp/hfsj.dmg ... created: /tmp/hfsj.dmg $ hdiutil mount /tmp/hfsj.dmg /dev/disk10 Apple_partition_scheme /dev/disk10s1 Apple_partition_map /dev/disk10s2 Apple_HFS /Volumes/HFSJ As Figure 127 shows, the volume header contains the extents of the HFS+ B-Trees and of other special files. Figure 127. The contents of an HFS+ volume header
Note in Figure 127 that the volume header's signature field contains the two characters H+. If this were a case-sensitive volume, this field would contain HX. Similarly, the version field would contain the value 5 (instead of 4) for a case-sensitive volume. While mounting an HFS+ volume, an HFS+ implementation is required to identify itself by setting the lastMountedVersion field in the volume header. This way, an implementation can detect whether there might be a problem because of an earlier mount (e.g., if a journaled volume was mounted without journaling). Examples of values contained in the lastMountedVersion field include 8.10 (mounted on Mac OS 8.1 through 9.2.2), 10.0 (mounted nonjournaled), HFSJ (mounted journaled), fsck (mounted by fsck), and registered creator codes (mounted by a third party represented by the creator code). The hfsdebug output in Figure 127 includes several dates. HFS+ dates are stored as 32-bit unsigned integers containing the number of seconds since midnight, January 1, 1904, GMT.[12] The volume creation date, however, is stored as local time instead of GMT. Since Unix-style dates are represented as the number of seconds since midnight, January 1, 1970, UTC, one must convert HFS+ dates to Unix-style dates before calling functions such as gmtime() and localtime().
Note that the backup date shown in Figure 127 is January 1, 1904. This is because the corresponding date integer contains a zerothat is, it has not been set, say, by a backup utility. The HFS+ volume header's information in Figure 127 indicates that there are three files and three folders[13] (in addition to the root folder) on the newly created volume. Let us account for these files and folders, while noting that some are created not along with the file system but when the volume is mounted in the Desktop environment. (See the sidebar "Disk Arbitration.")
On a volume without user home directories (typically a nonboot volume), the per-user trash folders, which are used for storing files that have been dragged to the trash, are named .TRashes/<uid>, with <uid> being the numerical user ID of a user. % id uid=501(amit) gid=501(amit) groups=501(amit) ... % sudo ls -l /Volumes/HFSJ/.Trashes total 0 drwx------ 2 amit amit 68 19 Apr 00:58 501
On a boot volume, the per-user trash folders are in the respective home directories (~/.trash). Thus, two folders are accounted for: .trashes and .trashes/501. These two folders will not exist if you mount our newly created volume manuallysay, using the mount_hfs program from the command line.
The third folder is the invisible private metadata folder, which is used internally by the file system and is created during volume creation. We discuss this private folder when we discuss hard links (Section 12.8.6). Note that the . (current directory) and .. (parent directory) directory entries do not reside physically on disk on an HFS+ volumethey are simulated by the HFS+ implementation. We can verify the number and names of the folders on this volume by using hfsdebug to print the folder thread records in the volume's Catalog B-Tree. % hfsdebug -b catalog -l folderthread -d /dev/rdisk10s2 # Folder Thread Record parentID = 1 nodeName = HFSJ # Folder Thread Record parentID = 2 nodeName = %00%00%00%00HFS+ Private Data # Folder Thread Record parentID = 2 nodeName = .Trashes # Folder Thread Record parentID = 17 nodeName = 501 Two of the three files on this volume are the invisible journal files: /.journal and /.journal_info_block. The third file is /.DS_Store,[14] which, again, will not exist if you manually mount the newly created volume in our example. We can verify the names of these files by using hfsdebug to print the file thread records in the Catalog B-Tree.
$ hfsdebug -b catalog -l filethread -d /dev/rdisk10s2 # File Thread Record parentID = 2 nodeName = .journal # File Thread Record parentID = 2 nodeName = .journal_info_block # File Thread Record parentID = 2 nodeName = .DS_Store
We will discuss file and folder thread records in greater detail in Section 12.7.2. The volume header also includes an array (the finderInfo field) containing eight 32-bit values, whose meanings are listed in Table 121.
Since several elements of the volume header's finderInfo array are related to booting, it would be more interesting to look at the volume header on a boot volume. When no volume or device is explicitly specified, hfsdebug operates on the root volume (Figure 128), which normally is also the boot volume. Figure 128. Finder information contained in the volume header of a boot volume
As Figure 128 shows, the volume contains both a bootable Mac OS X system and a bootable Mac OS 9 system, with finderInfo[5] and finderInfo[3] containing the respective system folder IDs. In the case of Mac OS X, the "system" folder is the one containing BootXthat is, /System/Library/CoreServices. The Boot UUID string shown is not part of finderInfoit is constructed by hfsdebug (and by BootX during bootstrapping)from the 64-bit volume identifier contained in the last two elements of finderInfo. As listed in Table 121, if finderInfo[2] contains a folder ID, the Finder will open a window displaying that directory when the volume is mounted. Let us verify this. We can use hdiutil to create an image with a designated "auto-open" folder. $ mkdir /tmp/auto-open $ mkdir /tmp/auto-open/directory $ echo Hello > /tmp/auto-open/directory/ReadMe.txt $ hdiutil makehybrid -hfs -hfs-openfolder /tmp/auto-open/directory \ -o /tmp/auto-open.dmg /tmp/auto-open Creating hybrid image... ... $ hdiutil mount /tmp/auto-open.dmg ... /dev/disk10s2 Apple_HFS /Volumes/auto-open As the volume is mounted, a Finder window should open displaying the folder named directory on the volume. Moreover, finderInfo[2] should be equal to the catalog node ID of directory. $ hfsdebug -V /Volumes/auto-open -v ... # Open folder ID finderInfo[2] = 0x10 (auto-open:/directory) ... $ ls -di /Volumes/auto-open/directory 16 /Volumes/auto-open/directory 12.5.2. Viewing a Volume Control BlockWhen an HFS+ volume is mounted, an in-kernel block of memory called a volume control block (VCB) holds most of the volume header's information, along with other, dynamic information about the volume. The VCB is represented by the hfsmount structure in the kernel. Given a mounted HFS+ volume, hfsdebug can retrieve the corresponding hfsmount structure's contents from kernel memory. $ sudo hfsdebug -m Volume name = Macintosh HD (volfs_id = 234881028) block device number = { major=14, minor=4 } HFS+ flags = 00000000000000000000000010001100 + HFS_WRITEABLE_MEDIA + HFS_CLEANED_ORPHANS + HFS_METADATA_ZONE default owner = { uid=99, gid=99 } ... free allocation blocks = 0x86fe4f start block for next allocation search = 0x2065a66 next unused catalog node ID = 3251700 file system write count = 61643383 free block reserve = 64000 blocks on loan for delayed allocations = 0 ... We will revisit the hfsmount structure later in this chapter. |