An open-source HFS+ filesystem explorer and debugger (in the spirit of hfsdebug)
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
docs
hfsinspect.xcodeproj
images
src
tools
vendor
.gitignore
.gitmodules
.travis.yml
CHANGELOG
LICENSE
Makefile
README.md
Vagrantfile
hfsinspect.1
uncrustify.cfg

README.md

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!