Stick a fork in it, and other flashbacks

I was a little disappointed when Apple didn’t adopt the name I suggested for macOS 10.14, Gormenghast, in honour of Mervyn Peake’s vast fictional castle. Now we seem to have stumbled across a room containing one of Titus Groan’s childhood toys: a resource fork.

When Apple’s engineers were designing the Mac’s file system, they did something very unusual. At that time, the early 1980s, the great majority of file systems used a single fork: one file, with one block of data for that file’s contents.

The Mac was going to be different. Each file would consist of two forks, one a regular data fork as in normal file systems, the other a structured database of resources, the resource fork. This enabled an app to keep different localisations of its menus and other text contents in resources. Resource forks were allowed for any file, so an app could store a document’s window settings in the document’s resource fork when it wanted to.

Resources were used to store a lot of standard structured data, such as the specifications for and contents of alerts and dialogs, menus, collections of text strings, keyboard definitions and layouts, icons, windows, fonts, and chunks of code to be used by apps. You could extend the types of resource supported by means of a template, itself stored as a resource, so developers could define new resource types appropriate to their own apps.

Apple developed a resource editor which quickly became one of the best-known apps on the Mac: ResEdit, last seen in version 2.1.3 way back in 1994. ResEdit was the Mac power user’s quintessential tool: if you didn’t like a particular dialog in an app, ResEdit could be used to change it; if you wanted to create your own custom keyboard layout, it was first choice for that too. If you wanted to mess someone’s Mac up, you could go into their system files and change things around when they were out at lunch. Not that we ever did that, of course.

resedit1

Each resource type had a four-character name, such as ALRT for an alert definition, DLOG for dialogs, KCHR for keyboard layouts, and so on.

resedit2

You could have many different KCHR resources, each numbered, so that changing your keyboard layout was a matter of switching from KCHR number 2, the standard British layout, to number 26, which was Dutch, for example. Adding your own custom keyboard layout was then merely a matter of defining a new KCHR resource with a unique ID number, here 128, and editing it to do what you wanted.

resedit3

Many resource types had custom editors: here that for KCHR has a simple keyboard layout and tabular form. The highlighted (black) keys indicate that the Euro character € being shown would be generated by holding the Opt (Alt) modifier key and pressing the 2 key on the top row of the main keyboard, but not the 2 on the numeric keypad.

Some of the Mac OS X engineers wanted to be rid of these awkward resource forks and use file bundles instead, but in the end macOS has greatly expanded this brilliant concept into extended attributes. These offer as many different ‘forks’ as you want, among which is one named com.apple.ResourceFork, the new incarnation of this old feature. For a long time, developers could access these resource forks either using a specialised path extension, with MyFile.doc/..namedfork/rsrc referring to the resource fork of MyFile.doc, or using calls to work with the specific extended attribute.

However you access them, resource forks still contain structured data in the form of resources, just like KCHR resources of the past. To simplify their access, OS X provided a Resource Manager. You won’t find many current apps using this, but they’re still used in AppleScript, both in Apple’s bundled Script Editor and third party scripting tools such as Late Night Software’s superb Script Debugger.

The namedfork path extension is also something of a security nightmare, as it’s readily accessible from shell scripts and almost anything else. Most if not all malware which affected classic Mac OS used the resource fork to its advantage, although it has seldom if ever featured in malware affecting macOS. Apple decided to deprecate the Resource Manager and other interfaces supporting traditional resource fork access in OS X 10.8, but it had remained functional until Catalina.

Last week, Shane Stanley and Mark Alldritt of Late Night Software discovered what looked like the death knell of the namedfork in Catalina. Reading resource forks repeatedly using the deprecated interfaces can be blocked by what appears to be a new security mechanism. Script Debugger now has a workaround to deal with this, but the same problem can also be demonstrated in Apple’s Script Editor, and is likely to afflict other apps which still use the deprecated interfaces.

The suspicious might wonder whether this really is a bug, or just another part of the many security changes in Catalina. Perhaps Apple is doing something to intentionally constrain namedfork and other resource access which has resulted in these problems appearing.

A little testing, though, makes that unlikely. Trying to replicate the problem using extended attribute calls fails to trigger the bug, and as far as I can see, writing and reading repeatedly to extended attributes seems safe in 10.15.1. If you want to access resource forks using the traditional namedfork method in Swift, for instance, using
let data = NSData.init(contentsOfFile: (self.theSourcePath + "/..namedfork/rsrc"))
no matter how many times you run that code, the resource fork works as intended in both 10.14 and 10.15.

With Shane Stanley’s help, I’ve been able to reproduce this problem and to examine an excerpt of the log detailing what happened on that occasion. In this case, trying to access a resource fork in an AppleScript .scpt file which had just been repeatedly edited using Apple’s Script Editor triggered a request to the Catalina privacy system (TCC), which required the requesting app to have been granted a kTCCServiceSystemPolicyAllFiles entitlement.

When TCC discovered that the app had no such entitlement, it refused to grant access, culminating in the kernel itself denying the request in a message of
sandboxd rejected approval request from Resource Fork Bu for kTCCServiceSystemPolicyAllFiles (/Users/hoakley/Documents/test2.scpt/..namedfork/rsrc): denied
This was then reported as a POSIX error of ‘operation not permitted’.

One way to avoid that particular error is to add apps which need access to resource forks, like Script Debugger and Script Editor, to the Full Disk Access list in the Privacy tab of the Security & Privacy pane. When this error occurred during testing, the requesting app was automatically added to that list but not enabled, and you may need to keep a watch on it being disabled again by the system. This may not account for all the problems encountered in trying to access resource forks in Catalina, though.

Thus Catalina does appear to have a bug which can cause unexpected behaviour when accessing resource forks using deprecated interfaces. In the right circumstances, this can lead to data loss in a few applications like Script Editor which are still thought to rely on those interfaces. Suggesting that Apple and third-party developers should stop using those interfaces isn’t helpful either: the Resource Manager and related support for the resource fork may have been deprecated for over seven years, but there are no suitable substitutes.

So long as AppleScript and its editors still rely on resource forks, Apple is going to need to support and maintain the Resource Manager and namedfork mechanism for accessing resource forks in the traditional way. Maybe it’s time to dedeprecate them after all.

Thanks to Shane Stanley for discussing this in detail with me, and for correcting some of my misunderstandings. Any remaining errors are my own. I’d also like to acknowledge Michael Tsai’s article which first drew my attention to this curious problem.