Xcode 8 Illegal Hard Links Prevent Cloning
After I installed Xcode 8 Beta 3, I could no longer back up my hard drive. SuperDuper reported errors like:
Error creating hard link /Volumes/HD Clone 14A/Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Frameworks/AccessibilityAudit.framework/Versions/A/Resources/en.lproj/AuditIssues.strings to /Volumes/HD Clone 14A/Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Resources/en.lproj/AuditIssues.strings for inode (null)
Cloning HD, error creating hard link for file in Xcode-beta.app.
At first I thought that the drive was damaged, but the error still occurred after reformatting it. SuperDuper’s Dave Nanian:
Structure became illegal (hard link into app bundle) in 10.10.3 - break link by duplicating file...
I assume they just did this in the latest beta. (It was previously an issue with Retrospect & Anaconda.)
Indeed, Xcode-beta.app does contain a hard-linked file:
ls -l /Applications/Xcode-beta.app/Contents/Applications/Accessibility\ Inspector.app/Contents/Frameworks/AccessibilityAudit.framework/Resources/en.lproj/ total 48 -rw-r--r--@ 2 mjt staff 6141 Jul 14 18:08 AuditIssues.strings -rw-r--r--@ 1 mjt staff 42 Jul 14 18:08 Localizable.strings -rw-r--r--@ 1 mjt staff 10726 Jul 14 18:08 LocalizableOSX.strings
Note the “2” for “AuditIssues.strings”. Here are the two files with the same inode:
find /Applications/Xcode-beta.app/ -name AuditIssues.strings -print -exec stat -f "%i" {} \; /Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Frameworks/AccessibilityAudit.framework/Versions/A/Resources/en.lproj/AuditIssues.strings 38530212 /Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Resources/en.lproj/AuditIssues.strings 38530212
The fix is to make the two files independent copies:
cd "/Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Resources/en.lproj/" mv AuditIssues.strings AuditIssues.strings.old cp AuditIssues.strings.old AuditIssues.strings rm AuditIssues.strings.old
Now the files have different inodes:
find /Applications/Xcode-beta.app/ -name AuditIssues.strings -print -exec stat -f "%i" {} \; /Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Frameworks/AccessibilityAudit.framework/Versions/A/Resources/en.lproj/AuditIssues.strings 38530212 /Applications/Xcode-beta.app/Contents/Applications/Accessibility Inspector.app/Contents/Resources/en.lproj/AuditIssues.strings 39006606
Update (2016-07-22): Mike Bombich (developer of Carbon Copy Cloner) on the similar issue with Anaconda:
This turns out to be an issue specific to *.app/Contents/PkgInfo and *.app/Contents/Resources/*.lproj files. OS X does not want to permit the creation of a hard link between one of these items in an application bundle to another file in a non-application-bundle folder. I was unable to find an explanation for this behavior in Apple’s documentation, nor in the source code for HFS or the OS X kernel.
4 Comments RSS · Twitter
So, I know it's still early days, but is it reasonable to assume if Apple doesn't correct course on this, it's the end of cloning utilities?
@Clark I’m not entirely sure what the rules are, but it sounds like a security restriction. If you could have a hard link to a file inside an app bundle, you could modify it from another file that has laxer permissions and therefore change the behavior of the app.
@Chucky Seems like the cloning utilities could, at worst, work around it by duplicating the file instead of hard linking it. Also, there may be a way for them to create this structure in an allowed way (e.g. different order of operations), as Archive Utility evidently did when expanding the .xip archive to Xcode-beta.app.
Those specific files/folders Mike mentioned come with "omit" (or at least "optional") in apps that use Xcode's standard resource rules. Could be related -- changing some of those things (via hard link, which means you don't necessarily need access to /Applications, for example) wouldn't break the app's code seal.