SecCodeCopyPath and /System/Volumes/Preboot/Cryptexes/App/System

Looking at the path name for reasons, and ran into a thing: one of my coworkers was not getting /Applications/Safari.app as expected, but instead got /System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app. Which is annoying because I'm actually using spotlight to find the paths for applications, and that one doesn't show up.

Has anyone run into this? And know why?

(I figure I'll simply remove the prefix if it's there, and that should be fine, but I'm curious why it only seems to happen sometimes.)

Replies

I'm curious why it only seems to happen sometimes.

The short answer is… well… there isn’t a short answer )-:

Apple uses cryptex to securely ship small software updates. To learn more, read the cryptex man page.

The contents of the cryptex get grafted into the file system at the expected place, but the cryptex is also visible at its mount point. Consider this:

% ls -i /System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app/Contents/Info.plist
72138 /System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app/Contents/Info.plist
% ls -i /Applications/Safari.app/Contents/Info.plist 
72138 /Applications/Safari.app/Contents/Info.plist

Both of these files have the same inode number, meaning that they’re actually the same file.

Tools like Spotlight and Launch Services scans files in the order in which they discover them, so it’s possible for them to find the cryptex copy of Safari before the ‘real’ one, and vice versa.

Additionally, when you use the code signing APIs to find a path for a file, it ends up walking up the directory hierarchy to build that path. If there are multiple paths to the file, the exact one you get depends on the state of the name cache within the kernel.

Note Cryptexes aren’t the only way this crops up; you’ll see the same thing with hard links.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I knew they were the same object (I use stat -s 'cause I'm weird), but... is there a way to deal with this, or should I just do what I thought, and strip out the prefixes? (Although the man page for cryptex says it can be mounted in a random location.)

(The workflow for this is a network extension checking the path of a process; the path is lazily looked up and cached using the app unique identifier.)

The canonical way to tell whether two paths point to the same file is with .fileResourceIdentifierKey. Consider:

let u1 = URL(fileURLWithPath: "/Applications/Safari.app/Contents/Info.plist")
let u2 = URL(fileURLWithPath: "/System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app/Contents/Info.plist")
let rv1 = try u1.resourceValues(forKeys: [.fileResourceIdentifierKey])
let rv2 = try u2.resourceValues(forKeys: [.fileResourceIdentifierKey])
let i1 = rv1.fileResourceIdentifier!
let i2 = rv2.fileResourceIdentifier!
print(i1.isEqual(i2))   // true

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I don't have something to compare to -- the goal is to be able to say "if it's any part of Safari, do this; if it's any part of Pages, do this" and so forth. Since many applications can be anywhere, I use the metadata query to find the applications' paths, and then send that down to the extension. So... is there a way to ask for all of the paths for bundles that match the display name?

Another thing, that is an error on my part, is that I used the SecInfo to get the path -- but that's the path to the bundle, not the path for the process, so I should use procinfo. I wrote up some Swift code to do that yesterday.