If you’ve ever snooped inside a Preference file or other Property List where you might have expected to see paths to files and folders, you may have been disappointed. Although some do contain what we recognise as paths, in the main, particularly in files written by macOS, all you’ll come across are blocks of seemingly random text characters like
Ym9va3wDAAAAAAQQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[and so on to end]
ACAgAACUAQAAAAAAADAgAADAAQAAAAAAAIDwAADIAQAAAAAAAA==
These are normally Bookmarks, a variant of the Finder Alias. This article explains how you can access and use them, and how they work.
Apple introduced Finder Aliases in System 7, almost 30 years ago in 1991. Prior to that, users didn’t have access to any form of link or alias, but when the largest hard disk available in a Mac was only 160 MB, that wasn’t such a problem. From the outset, Aliases have been presented as opaque data structures which are handled by the system. Although apps have been able to obtain information from them, Apple hasn’t intended any more general access to the data which they contain.
Bookmarks are a generalisation of Aliases which were introduced in OS X 10.6, and have been used extensively internally in macOS and applications since at least Mavericks 10.9 in 2013. They’re now used in a lot of preference files and other places, particularly by LaunchServices in its SharedFileList files stored in ~/Library/Application Support/com.apple.sharedfilelist.
Prior to macOS Sierra, Finder Aliases (and some Bookmarks) were usually quite large, containing icons and other data, and were typically just over 1 MB on disk. Early releases of Sierra, even 10.12.1, didn’t always work properly with the new compact Bookmark and Alias formats introduced then, but they have since become widespread and generally fully compatible with the system and apps. If you remember struggling with Aliases, it’s probably from that time of transition. In LaunchServices files, the original Bookmark format was usually contained in .sfl files, and the new in .sfl2 files.
The current Bookmark and Alias format is typically around 1 KB, making them almost as efficient in terms of storage as symbolic links, although macOS continues to use symlinks rather than Aliases in many places, particularly where paths have to be resolved by commands and scripts. macOS has no native support for the resolution of Aliases or Bookmarks at the command line, although my command tool alisma
puts that right.
Accessing and resolving Bookmarks
View and resolve Bookmarks in the Bookmark Resolver window in my free app Precize. When you’ve opened the app, use the Bookmark Resolver command in its Window menu to open a new Resolver window. Copy the Base-64 text from the Property List, paste it into the upper Paste Bookmark view in the resolver, and click the Analyse button. The lower view then fills with details about the Bookmark’s contents. Click on the Resolve button to see the path that the Bookmark resolves to.
Among the information stored in Bookmarks are the following:
- NSURLBookmarkOriginalRelativePathKey – this is the path to the item relative to the root of that volume, e.g. Users/username/Documents/test1.txt
- NSURLBookmarkOriginalVolumeNameKey – this is the name of that volume, typically Macintosh HD for internal storage
- NSURLCreationDateKey – this is the date of creation of the item, e.g. 2019-01-07 17:06:59 +0000
- NSURLDocumentIdentifierKey – an integer which uniquely identifies this item wherever it is on that volume, but this is not the inode number; e.g. 5747
- NSURLIsDirectoryKey – whether the item is a folder or not
- NSURLNameKey – the file or folder name, including any extension. e.g. test1.txt
- NSURLVolumeNameKey – the name of the volume on which the item is stored, which should be the same as that given above; there are also numerous entries giving details about that volume, including its total capacity, whether it’s removable, etc.
- NSURLVolumeUUIDStringKey – the UUID of the volume on which the item is stored
- _NSURLBookmarkURLStringKey – the URL to the item, e.g. file:///Users/username/Documents/test1.txt
- _NSURLFileIDKey – the inode number of the item
- userName and userUID – the short username of the owner of the item, and their system ID, e.g. 501.
It’s important to note that Bookmarks don’t appear to store the inode number of the volume. Instead, the volume name and UUID are used, which are more stable across reboots and hardware reconfiguration. This explains how Bookmarks and Aliases can preserve links even when both the alias and the original file/folder are moved: macOS first tries to resolve the path, rather like a symlink, and if that fails it has other systems, including inode numbers, which it can fall back on.
Precize shows all data twice in its lower view: the first time it’s structured by key, and the second it gives the data in its original structure.
If you want to inspect an Alias in Precize without it getting resolved, ensure that it doesn’t get previewed by QuickLook on the way, as that’s likely to cause it to be resolved before you can open it. That doesn’t, of course, apply to Bookmarks.
Access in code
To access Bookmarks in your own code, you’ll need to do the following.
If you’re working from text extracted from a file, first clean up all whitespace, as it often comes with spurious spaces, tabs and newlines. Try doing anything with that and it will break. Once that’s done, initialise a data object from the Base-64 encoded data. In Swift,
let theBMdata = Data.init(base64Encoded: theBMtext)
or in Objective-C use NSData’s initWithBase64EncodedData:options:
If you’ve already got the binary data for the Bookmark, skip this step, of course.
You can then access the data contained within the Bookmark using NSURL resource values. In Swift,
let theDat = NSURL.resourceValues(forKeys: [URLResourceKey(rawValue: "NSURLBookmarkAllPropertiesKey")], fromBookmarkData: theBMdata)
returns all the available keys and their values. In Objective-C, use NSURL’s resourceValuesForKeys:fromBookmarkData:
URLResourceKey
lists all the available keys which you can access.
In Swift, it’s simple to convert the Bookmark into a URL resolving its data using
let theURL = try URL.init(resolvingBookmarkData: theBMdata, bookmarkDataIsStale: &markAsStale)
ensuring that you provide a catch
to handle errors.
If you need to resolve an Alias or Bookmark in a command script, try my free command tool alisma
.
How Bookmarks resolve
The resolver is called frequently. Whenever anything wants to access an item which passes through an Alias or Bookmark, that needs to be resolved. This includes the generation of QuickLook thumbnails and previews, even merely selecting an Alias in an Open File dialog.
The resolver first checks whether the path saved in the Alias or Bookmark results in a valid URL. If it does, then that is returned. There appears to be no further checking performed, such as matching the inode number saved in the Alias against that of the item at the path. This allows spoofing, in which the file pointed to by an Alias/Bookmark can be changed without breaking resolution of that Alias/Bookmark.
However, that needs to be performed with care to prevent the alias being resolved before the imposter is in place and correctly-named. If a user attempts this by dragging a file to the Trash, that is likely to result in resolution of the Alias before the imposter is fully ready, and the Alias will merely break rather than be spoofed. I have tried this, and it is quite tricky to pull off in the Finder.
If the resolver cannot obtain a valid URL from the stored path, it then uses other information, probably including the inode number, stored in the Alias/Bookmark, which is likely to generate a valid path provided that file is accessible on the same volume. If that works, the new URL is returned and the information in an Alias is then updated to point to the item’s new location. Note that, unless a Bookmark is written back to the document which contains it, only standalone Aliases and Bookmark files are updated in this way.
If the resolver is unable to generate a valid URL from the information in the Alias/Bookmark, it then returns an error. There are options to automatically invoke the standard dialog which offers to delete or fix the Alias, or to pass the error back without any user interaction. Errors don’t result in any change to the internal data of Aliases.
Thanks to Jonathan Deutsch for pointing out that Bookmarks first became available to third-parties in OS X 10.6.