hfsinspect

An open-source HFS+ filesystem explorer and debugger (in the spirit of hfsdebug)

View project on GitHub

hfsinspect

Build Status Stories in Ready

An open-source HFS+ filesystem explorer and debugger (in the spirit of hfsdebug)

This program is a work-in-progress and is quite buggy in its current state. That said, it's read-only and can display some really interesting information already so have a run with it, help where you can, and file some suggestions on the site.

https://github.com/ahknight/hfsinspect

Quick Guide

$ git clone https://github.com/ahknight/hfsinspect.git
$ cd hfsinspect
$ make
$ [sudo] make install # PREFIX defaults to /usr/local; for somewhere else do: PREFIX=/home/you make -e install

Once built and the product located, go ahead and hit "hfsinspect --help" for a quick blurb about the options.

Building on Linux

You'll need the @uuid-dev@ package for the @libuuid@ headers (in Ubuntu at least). I've tested a lot of the app in Linux but there are occassional issues with some disks and files so if you run into them please file a detailed issue or, if you can, submit a fix.

Building on BSD

Haven't tried. Please do and report back. As it works on OS X and Linux I would hope it functions on at least FreeBSD.

Testing in VitualBox

One thing to note when testing in VirtualBox is that it doesn't pass-through SSE4 instructions by default, so only the software CRC32C path can be tested. As of VirtualBox 4.3.8, however, there is a semi-hidden option to enable this:

VBoxManage setextradata "VM name" VBoxInternal/CPUM/SSE4.1 1
VBoxManage setextradata "VM name" VBoxInternal/CPUM/SSE4.2 1

Examples

$ hfsinspect -d ./fstest -P /

#   Record ID 1 (2/20) (offset 124; length: 32) (Node 1)

  Catalog Key
 +-----------------------------------------------------------------------------------+
 | length | parentID   | length | nodeName                                           |
 | 6      | 2          | 0      |                                                    |
 +-----------------------------------------------------------------------------------+
  recordType             = kHFSPlusFolderThreadRecord
  reserved               = 0
  parentID               = 1
  nodeName               = "FS Test" (7)

$ hfsinspect -d ./fstest -l -P /
Listing for FS Test
CNID      kind       mode       user      group     data            rsrc            name
18        folder     d--------- 0         0         -               -               ␀␀␀␀HFS+ Private Data
19        folder     dr-xr-xr-t 0         0         -               -               .HFS+ Private Directory Data
20        folder     d-wx-wx-wt 501       20        -               -               .Trashes
21        folder     drwx------ 501       20        -               -               .fseventsd
16        file       ---------- 0         0         512.00 KiB      0.00 bytes      .journal
17        file       ---------- 0         0         4.00 KiB        0.00 bytes      .journal_info_block
----------------------------------------------------------------------------------------------------
                                                    516.00 KiB      0.00 bytes
   Folders: 4          Data forks: 2          Hard links: 0
     Files: 2          RSRC forks: 0            Symlinks: 0

$ hfsinspect -d ./fstest -P /.journal

#   Record ID 6 (7/20) (offset 676; length: 272) (Node 1)

  Catalog Key
 +-----------------------------------------------------------------------------------+
 | length | parentID   | length | nodeName                                           |
 | 22     | 2          | 8      | .journal                                           |
 +-----------------------------------------------------------------------------------+
  recordType             = kHFSPlusFileRecord
  flags                  = 00000000000000000000000000000010
                         . kHFSThreadExistsMask
  reserved1              = 0
  fileID                 = 16
  createDate             = Sun May 12 00:42:42 2013 UTC
  contentModDate         = Sun May 12 00:42:42 2013 UTC
  attributeModDate       = Thu Jan  1 00:00:00 1970 UTC
  accessDate             = Thu Jan  1 00:00:00 1970 UTC
  backupDate             = Thu Jan  1 00:00:00 1970 UTC
  ownerID                = 0
  groupID                = 0
  adminFlags             = 000000
  ownerFlags             = 000000
  fileMode               = ----------
  fileMode               = 100000
                         . 100000 (S_IFREG)
  special.linkCount      = 1
  fdType                 = 0x6A726E6C (jrnl)
  fdCreator              = 0x6866732B (hfs+)
  fdFlags                = 00000000000000000000000001010000
                         . kNameLocked (4096)
                         . kIsInvisible (16384)
  fdLocation             = (0, 0)
  opaque                 = 0
  textEncoding           = 0
  reserved2              = 0
  fork                   = data
  logicalSize            = 512.00 KiB (524288 bytes)
  clumpSize              = 0.00 bytes (0 bytes)
  totalBlocks            = 512.00 KiB (128 blocks)
  extents                =   startBlock   blockCount    % of file
                                      3          128       100.00
                           --------------------------------------
                              1 extents          128       100.00
                           128.00 blocks per extent on average.
  fork                   = resource
  logicalSize            = (empty)

  $ hfsinspect -d ./fstest -v

  # HFSX Volume Format (v5)
    volume name            = FS Test
    case sensitivity       = case sensitive
    bootable               = no

  # HFS Plus Volume Header
    signature              = 0x4858 (HX)
    version                = 5
    attributes             = 00000000000000000000000000000000
                           = 00000000000000000010000000100001
                           . kHFSVolumeUnmountedMask (256)
                           . kHFSVolumeJournaledMask (8192)
                           . kHFSUnusedNodeFixMask (18446744071562067968)
                           . kHFSMDBAttributesMask (33664)
    lastMountedVersion     = 0x4846534A (HFSJ)
    journalInfoBlock       = 2
    createDate             = Sat May 11 19:42:41 2013 UTC
    modifyDate             = Sun May 12 06:24:19 2013 UTC
    backupDate             = Thu Jan  1 00:00:00 1970 UTC
    checkedDate            = Sun May 12 00:42:41 2013 UTC
    fileCount              = 5
    folderCount            = 4
    blockSize              = 4.00 KiB (4096 bytes)
    totalBlocks            = 100.00 MiB (25600 blocks)
    freeBlocks             = 97.13 MiB (24865 blocks)
    nextAllocation         = 4733
    rsrcClumpSize          = 64.00 KiB (65536 bytes)
    dataClumpSize          = 64.00 KiB (65536 bytes)
    nextCatalogID          = 25
    writeCount             = 6
    encodingsBitmap        = 00000000000000000000000000000000
                           = 00000000000000000000000000000000
                           = 00000000000000000000000000000000
                           = 00000000000000000000000000000001
                           . kTextEncodingMacRoman (0)

  # Finder Info
    bootDirID              = 0 ()
    bootParentID           = 0 ()
    openWindowDirID        = 0 ()
    os9DirID               = 0 ()
    reserved               = 00000000000000000000000000000000
                           = 00000000000000000000000000000000
    osXDirID               = 0 ()
    volID                  = 0x7EF9BE3682182A73

  # Journal Info Block
    flags                  = 00000000000000000000000000000000
                           = 00000000000000000000000000000001
                           . kJIJournalInFSMask (1)
    device_signature       = 0x00000000000000000000000000000000
                           = 0x00000000000000000000000000000000
    offset                 = 12.00 KiB (12288 bytes)
    size                   = 512.00 KiB (524288 bytes)
    ext_jnl_uuid           =
    machine_serial_num     =

  # Allocation Bitmap File
    fork                   = data
    logicalSize            = 4.00 KiB (4096 bytes)
    clumpSize              = 4.00 KiB (4096 bytes)
    totalBlocks            = 4.00 KiB (1 blocks)
    extents                =   startBlock   blockCount    % of file
                                        1            1       100.00
                             --------------------------------------
                                1 extents            1       100.00
                             1.00 blocks per extent on average.

  # Extents Overflow File
    fork                   = data
    logicalSize            = 800.00 KiB (819200 bytes)
    clumpSize              = 800.00 KiB (819200 bytes)
    totalBlocks            = 800.00 KiB (200 blocks)
    extents                =   startBlock   blockCount    % of file
                                      131          200       100.00
                             --------------------------------------
                                1 extents          200       100.00
                             200.00 blocks per extent on average.

  # Catalog File
    fork                   = data
    logicalSize            = 800.00 KiB (819200 bytes)
    clumpSize              = 800.00 KiB (819200 bytes)
    totalBlocks            = 800.00 KiB (200 blocks)
    extents                =   startBlock   blockCount    % of file
                                     2531          200       100.00
                             --------------------------------------
                                1 extents          200       100.00
                             200.00 blocks per extent on average.

  # Attributes File
    fork                   = data
    logicalSize            = 800.00 KiB (819200 bytes)
    clumpSize              = 800.00 KiB (819200 bytes)
    totalBlocks            = 800.00 KiB (200 blocks)
    extents                =   startBlock   blockCount    % of file
                                      331          200       100.00
                             --------------------------------------
                                1 extents          200       100.00
                             200.00 blocks per extent on average.

  # Startup File
    fork                   = data
    logicalSize            = (empty)

$ ls -l file
-rw-r--r--  1 ahknight  staff  10240 May 24 17:35 file

$ hfsinspect -p file

#   Record ID 14 (15/23) (offset 2252; length: 264) (Node 86272)

  Catalog Key
 +-----------------------------------------------------------------------------------+
 | length | parentID   | length | nodeName                                           |
 | 14     | 379620     | 4      | file                                               |
 +-----------------------------------------------------------------------------------+
  recordType             = kHFSPlusFileRecord
  flags                  = 00000000000000000000000010000010
                         . kHFSThreadExistsMask
                         . kHFSHasDateAddedMask
  reserved1              = 0
  fileID                 = 108228801
  createDate             = Fri May 24 22:35:34 2013 UTC
  contentModDate         = Fri May 24 22:35:34 2013 UTC
  attributeModDate       = Fri May 24 22:35:34 2013 UTC
  accessDate             = Tue Jun 18 03:47:49 2013 UTC
  backupDate             = Thu Jan  1 00:00:00 1970 UTC
  ownerID                = 501
  groupID                = 20
  adminFlags             = 000000
  ownerFlags             = 000000
  fileMode               = -rw-r--r--
  fileMode               = 100644
                         . 100000 (S_IFREG)
                         . 000400 (S_IRUSR)
                         . 000200 (S_IWUSR)
                         . 000040 (S_IRGRP)
                         . 000004 (S_IROTH)
  special.linkCount      = 1
  fdType                 = 0x00000000 ()
  fdCreator              = 0x00000000 ()
  fdFlags                = 00000000000000000000000000000000
  fdLocation             = (0, 0)
  opaque                 = 0
  textEncoding           = 0
  reserved2              = 0
  fork                   = data
  logicalSize            = 10.00 KiB (10240 bytes)
  clumpSize              = 0.00 bytes (0 bytes)
  totalBlocks            = 12.00 KiB (3 blocks)
  extents                =   startBlock   blockCount    % of file
                               95046227            3       100.00
                           --------------------------------------
                              1 extents            3       100.00
                           3.00 blocks per extent on average.
  fork                   = resource
  logicalSize            = (empty)

$ hfsinspect -p /Volumes/TARDIS/Backups.backupdb/Gallifrey/2013-05-29-011753/Hubert/ -l
Listing for Hubert
CNID      kind       mode       user      group     data            rsrc            name
10929881  hard link  -r--r--r-- 11151100  10812491  0.00 bytes      0.00 bytes      .apdisk
10929892  file       -rw-r--r-- 0         20        181.00 bytes    0.00 bytes      .com.apple.backupd.mvlist.plist
10929882  hard link  -r--r--r-- 11151101  10812492  0.00 bytes      0.00 bytes      .com.apple.timemachine.donotpresent
10929883  dir link   -r--r--r-- 0         0         0.00 bytes      464.00 bytes    .DocumentRevisions-V100
10929884  hard link  -r--r--r-- 11151103  0         0.00 bytes      0.00 bytes      .DS_Store
10929885  hard link  -r--r--r-- 11151104  10812495  0.00 bytes      0.00 bytes      .VolumeIcon.icns
10929886  dir link   -r--r--r-- 11151105  10812496  0.00 bytes      464.00 bytes    [redacted]
10929887  dir link   -r--r--r-- 11151106  0         0.00 bytes      464.00 bytes    Archived Music
10929888  dir link   -r--r--r-- 11151107  10812498  0.00 bytes      464.00 bytes    Backups
10929889  dir link   -r--r--r-- 11151108  0         0.00 bytes      464.00 bytes    [redacted]
----------------------------------------------------------------------------------------------------
                                              181.00 bytes    5.00 bytes
Folders: 0          Data forks: 1          Hard links: 9
  Files: 1          RSRC forks: 5            Symlinks: 0

$ hfsinspect -c 18 -l
Listing for �<90><80>�<90><80>�<90><80>�<90><80>HFS+ Private Data
CNID      kind       mode       user      group     data            rsrc            name
100043847 file       -rw-r--r-- 501       20        32.00 MiB       0.00 bytes      iNode100043847
100049839 file       -r--r--r-- 501       20        1.69 KiB        0.00 bytes      iNode100049839
100196873 file       -r--r--r-- 501       20        2.05 KiB        0.00 bytes      iNode100196873
...
86494855  file       -rw-r--r-- 0         0         0.00 bytes      10.36 KiB       temp86494855
86494857  file       -rw-r--r-- 0         0         0.00 bytes      111.80 KiB      temp86494857
86494861  file       -rw-r--r-- 0         0         0.00 bytes      13.33 KiB       temp86494861
----------------------------------------------------------------------------------------------------
                                                  14.18 GiB       129.00 bytes
 Folders: 68         Data forks: 7663       Hard links: 0
   Files: 8613       RSRC forks: 129          Symlinks: 0

$ hfsinspect -s -V /Volumes/Titania/
# Volume Summary
  nodeCount              = 20780
  recordCount            = 858836
  fileCount              = 366108
  folderCount            = 71988
  aliasCount             = 4
  hardLinkFileCount      = 0
  hardLinkFolderCount    = 0
  symbolicLinkCount      = 311
  invisibleFileCount     = 293
  emptyFileCount         = 8678
  emptyDirectoryCount    = 0

# Data Fork
  count                  = 357067
  fragmentedCount        = 7839
  blockCount             = 279.51 GiB (73271622 blocks)
  logicalSpace           = 278.83 GiB (299390329775 bytes)
  extentRecords          = 357067
  extentDescriptors      = 372671
  overflowExtentRecords  = 0
  overflowExtentDescriptors= 0

# Resource Fork
  count                  = 6575
  fragmentedCount        = 1
  blockCount             = 341.55 MiB (87437 blocks)
  logicalSpace           = 327.25 MiB (343150709 bytes)
  extentRecords          = 6575
  extentDescriptors      = 6576
  overflowExtentRecords  = 0
  overflowExtentDescriptors= 0

# Largest Files
#       Size       CNID
1   7.42 GiB     807918 MOSX.vdi
2   2.22 GiB     810796 [redacted]
3   1.62 GiB     807943 winxp-000001-s002.vmdk
4   1.49 GiB     808013 Virtual Disk-s001.vmdk
5   1.48 GiB     807964 winxp-s001.vmdk
6   1.47 GiB     799434 [redacted]
7   1.37 GiB     816888 [redacted]
8   1.28 GiB     810661 [redacted]
9   1.21 GiB     128733 [redacted]

Hopefully you get the idea. Have fun!