Support HFS+ resource forks, extended attributes, and compressed files

1.  Support for HFS+ extended attributes.  These are “named” attributes associated with a file, stored in the HFS+ Attributes File.  These are loaded as TSK resident attributes, with type TSK_FS_ATTR_TYPE_HFS_EXT_ATTR or type TSK_FS_ATTR_TYPE_HFS_COMP_REC.  These show up in istat output, in the list of attributes.  Icat will be able to “cat” these attributes via the usual command-line syntax.

2.  Support for HFS+ resource fork.  Similarly to the DATA fork, the block-runs of the resource fork are loaded as a non-resident TSK attribute of type TSK_FS_ATTR_TYPE_HFS_DATA (128) and ID = 1. This will show up in istat output, in the list of attributes.  Istat also enumerates the blocks of the resource fork and gives its size.  If the resource fork has size zero, then this TSK attribute is not loaded.  (N.B., for regular files and symbolic links, the DATA fork attribute is always created, even if the size of the DATA fork is zero.)  Icat will “cat” this attribute, if it is present, via the usual command-line syntax.

3.  Istat also parses the resource fork, and prints a list of the individual resources it found.  For each resource, it shows the type (four ASCII characters), the ID (a number), the size, and the offset within the Resource fork, and the name of the resource, if it exits. (N.B., the beginning of each resource is four bytes of size.  Istat gives the size of the data not including those four bytes, and the offset that is just past the four bytes.)

4.  Support for compressed files.  Compressed files have an HFS+ extended attribute that describes the size and location of the compressed data, its uncompressed size, and possibly, contains the compressed data itself.  This attribute is loaded as a TSK resident attribute of type TSK_FS_ATTR_TYPE_HFS_COMP_REC (272).  If the compressed data is “inline” in the attribute, then it is also uncompressed, and the result is loaded as a resident TSK attribute of type TSK_FS_ATTR_TYPE_HFS_DATA with ID = 0  (where the DATA fork would usually be).

5.  The alternative is that the compressed data is in the Resource fork, which should contain a single resource. In this case, the block-runs of the Resource Fork are also loaded as a non-resident attribute at type-ID 128-0. 

6.  In either case with compressed data, icat applied to the file, or to the DATA fork (128-0) attribute, will “cat” the uncompressed file data. Istat prints a summary of the compressed file, giving its uncompressed size, where the compressed data is located, and whether it is really compressed or not.

Principal changes in code:

1.  Dependency on zlib.  TSK can be built either with or without zlib.  If it is built without zlib, then compressed files will still have an extended attribute of the correct type loaded.   However, if the data is really compressed then it will not be available in uncompressed form via the 128-0 attribute.

2.  Configure now takes an option “--with-zlib” or “--without-zlib” .  In the first case, which is the default, TSK is built with zlib if it is present, and otherwise without.  In the second case, TSK is built without zlib, even if it is present.  The first form can have indicate the location of the zlib installation like this:  “--with-zlib=<dir>”  where <dir> is supposed to be the parent directory of the “lib” directory that contains libz.a and also the parent of the “include” directory that contains zlib.h.

3.  Function zlib_inflate() in hfs.c is a wrapper around the zlib library, for convenience.

4.  Functions hfs_file_read_special() and hfs_attr_walk_special() are the “r”and “w” functions for a file that is compressed, with the compressed data in the resource fork.  These functions are NOT used for files where the compressed data is in the extended attribute containing the compression record.

5.  Function hfs_load_extended_attrs() finds all of the extended attributes of a given file by searching the Attributes file.  Each one is loaded as a TSK resident attribute of type TSK_FS_ATTR_TYPE_HFS_EXT_ATTR (224), unless the attribute contains a compression record.  In that case it would be loaded with type TSK_FS_ATTR_TYPE_HFS_COMP_REC (272).  If the attribute indicates compressed data, and if that data is “inline” in the attribute (rather than in the Resource fork) then this function will uncompress it (if necessary) and will load it as resident TSK attribute 128-0.  This function is called by hfs_load_attrs.

6.  Function hfs_load_attrs() now loads the blocks of the Resource fork, if it exists, as a non-resident TSK attribute, similarly to the DATA fork, with type-ID of 128-1.  For regular and symbolic link files, it (or hfs_load_extended_attrs) will always load a TSK attribute for the DATA fork, even if one does not exist, but will not do so for an empty Resource fork.

7.  Several of the “special” files of the HFS+ file system can have extended attributes.  For these files, the dedicated functions that load their attributes now call hfs_load_extended_attrs().

8.  Function hfs_istat() now reports on the Resource fork, on compressed files, and on the extended attributes, as described above.  It calls hfs_parse_resource_fork().

9.  Function hfs_parse_resource_fork() will read the “map” of the resource fork, and will produce a linked list of elements which describe each of the resources in the Resource fork.

10.  Enum TSK_FS_ATTR_TYPE_ENUM (in tsk_fs.h) has been extended with five HFS-specific attribute types.  Three of these are numerically equal to similar NTFS types. One new type is used for the compression attribute.  The last new type is intended for future assertion of individual resources as TSK attributes (which is not currently done).

Known problems and loose ends:

1.  Istat reports a “link count” that, in some cases, may not be a link count (but it is what's on disk).  This will be fixed when we tackle HFS+ hard links.

Most of this work was done by Matt Stillerman here (not just me!)