Open Recent, inodes, and Bookmarks: How macOS remembers files

Every file and folder on an HFS+ or APFS volume has at least two unique means of identification: its path and name, such as /Users/hoakley/Documents/myNewFile.text, and its inode number, which has to be specified relative to that volume’s inode number, such as 16777224/350939540. You won’t see inode numbers in the Finder, but can inspect them using the latest version of Precize (from Downloads), or at the command line by adding the i option to the ls command, e.g.
ls -ila myNewFile.text

For inode numbers to be of any use, you need to be able to convert back from them to the URL (path and name) of the original item.

Command line

At the command line, this is currently easy because you can use volfs, the volume file system, provided that the item is on a mounted HFS+ or APFS volume. volfs is a virtual file system which uses the top-level hidden folder .vol, together with the inode numbers for the volume and the individual item (file or folder).

In volfs, to get the full path to any file or folder, you need those two inode numbers to specify the item. From the command line, use a command along the lines of
stat /
to return the inode number for the boot volume, for example. Then use
ls -i filename
to return the inode number of the file filename.

Precize gives those inode numbers immediately, as NSFileSystemNumber for the volume, and NSFileSystemFileNumber for the file.

Then try
ls -ila /.vol/[volnumber]/[filenumber]
where [volnumber] is the inode number of the volume containing the item, and [filenumber] is the inode number for the item itself. For example,
ls -ila /.vol/16777224/350939540
should return the usual listing of permissions, sizes, etc., for the file. However, this does not resolve the path and filename to the normal file. To get that, use
GetFileInfo /.vol/16777224/350939540
which should return something like
file: "/Users/hoakley/Documents/0newDownloads/"
type: "\0\0\0\0"
creator: "\0\0\0\0"
attributes: avbstclinmedz
created: 10/10/2017 14:13:43
modified: 10/10/2017 14:13:43

giving you the full path and filename of the original file corresponding to that reference in volfs. The only snag is that the GetFileInfo command is deprecated, and due for removal from macOS. That said, this still works in Sierra 10.12.6 and High Sierra 10.13.3.

Within an app

When writing code for a macOS app, this is all much more complicated, because of the multiplicity of file system references, and their changing features across different versions of macOS. Support for inodes has been provided in:

  • Carbon File Manager, which is almost entirely deprecated or removed,
  • Launch Services, which is largely deprecated,
  • URLs, where it has been almost entirely replaced by Bookmarks.

Carbon File Manager had two structures which could be used with volfs to reverse back to a path and filename: the FSSpec, composed of the volumeID (volnumber above), the directory ID, and the filename, and the FSRef, which could be obtained from the volfs path of /.vol/[volnumber]/[filenumber]. The FSRef is an opaque structure, though, defined as being a block of 80 bytes, and designed to work with 32-bit inode numbers. APFS uses 64-bit inode numbers, and those cause problems with FSRefs.

I have also been unable to find any remaining route by which you can start with a volfs path and derive a URL path, which is still supported in Sierra and High Sierra. In case you’re tempted to try, you can turn a volfs path into a URL, but the resulting URL appears incapable of doing anything useful.

Apps which use Cocoa Documents get free access to features including the Open Recent menu command, which relies on Launch Services to maintain a list of recently-opened documents which persists across restarts. Launch Services stores lists of recently-opened documents in the folder ~/Library/Application Support/, as SharedFileList property lists. In Sierra these have the extension .sfl, but High Sierra uses the .afl2 extension indicating a new format.

SharedFileLists reference documents using two conventions: a full URL path, and a block of binary data. One obvious change in High Sierra’s new format is the increase in size of the block of binary data, which may now represent a Bookmark, expanded to take into account changes in High Sierra including the use of 64-bit inode numbers in APFS.

Apple provides an interface to Launch Services which in the past has included functions such as LSOpenFSRef(), which enabled code to open a file specified by a supplied FSRef, which in turn could have been constructed from inode numbers. As far as I can see, all functions which worked usefully with the older Carbon structures such as FSRef have long been deprecated, and none are available in 10.11 or later.

In place of inode numbers, FSRefs, and Launch Services functions, macOS now offers the Bookmark: completely opaque to the developer, which undoubtedly contains 32-bit inode numbers from HFS+ or 64-bit inode numbers from APFS. Bookmarks are supported quite extensively, and URL/NSURL/CFURL objects can be initialised from resolved Bookmark data.

At present, there appear to be no command tools which work with Bookmarks instead of inodes, making Bookmarks as alien to command tools as inodes are now to apps. That isn’t very helpful to users, sysadmins or developers.