Rixstep
 About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Home » Learning Curve » Developers Workshop

Don't Carry Coals to Cupertino, Apple!

A way to make more people happy.


Get It

Try It

Apple users (on real computers or at least 'Macs') have been plagued for the longest time by a two-bit hack in legacy NeXT framework code.

Or more specifically: in the code to NSDocumentController.

For those unfamiliar with the genius of the original version (and it really was genius) and especially for those having to grapple with Open Sauce and Microsoft I/O structures, NSDocumentController is the magician behind the curtains who makes most of the tedium of such operations invisible. Apps rarely have to worry about file names or locations - it's all taken care of by NSDocumentController.

The user wants you to save a file? You'll get a call. From NSDocumentController. Just reply to the call with the data to be saved. No fuss, no mess.

But leave grownup code unguarded so minors can get access? For it didn't take long for a clown to come forward with an absolutely brilliant idea to 'improve' things further. What happens, namely, if your target file is suddenly marked 'read-only'? (Yes, Unix has read/write/run privileges, but let's keep it simple like Apple's Loser Finder does, OK?)

What do you do?

Back in the days before recorded history, when Unix gurus like Doug McIlroy were still painting bison on cave walls in 7-bit ASCII crimson, one simply issued a diagnostic. Along the lines:

'Oops, you dumbfuck! You can't write to a file that you yourself protected! Yer mamma!'

Or perhaps something a bit more user-friendly, or more 'Mac-like'?

But then an absolute genius at Apple (hopefully not a member of the original NeXT team) had another idea. It's likely this person was actually computer-illiterate, and left the task of implementation to someone a bit more qualified (but not overly so, as we shall see).

His idea? Well, why deny a user? The file is write-protected? So what? We're Apple!

It works like this. And it's mostly pure genius. As you're the one editing your file, and no matter the file's 'type' or the editor in use, and as long as your app is written in Objective-C and uses the NeXT frameworks, that NSDocumentController is keeping tabs on things for you behind the scenes.

You might be getting intermittent intermediate saves without knowing it. It doesn't matter - your actual file won't be affected. NSDocumentController is making these saves to a separate temporary location.

(Today it's in /private/var/folders/.../T/TemporaryItems.)

And when it comes time to really save the file? There are two ways. The old safe way, and the dorky new way.

The old way: you now have a secure temporary copy on disk. (The last save by the user also goes to this temporary location.) You may even have checked that the copy is intact.

So now you effect a copy to the actual target destination, where your file is actually located. This operation fails of course, which is what got the panties of someone in Cupertino in a twist.

Otherwise, the copy op is carried out, the code for which can be riddled with exception handling, and perhaps NSDocumentController performs some kind of integrity check before turning control back to the user's app.

Now here's the stupid new way. Take the final temporary copy and move it to the actual expected location.

This has to do with Unix directories. On Unix, directories are just files. And to write to a directory, you need write permission. (That sounds reasonable, doesn't it?)

What's in a Unix directory? Not much, thanks to the good design of Dennis Ritchie and above all Ken Thompson. Unix directories contain but two things, then as now.

1. Inodes
2. Filenames

The filename isn't a full path. That full path can be computed if needed. The inode is a unique index into the file system's ilist. The ilist is like a volume control block, and this block is where you find all the goodies on files. Nothing but the two items above are found in a Unix directory. (And the ilist doesn't have filenames, opening things up to another interesting opportunity, which unhappily works like shit on Apple's Unix systems. Another topic, another time.)

A file's inode can never change, but a filename can. Moreover: you need write permissions to add filenames to a directory or to remove them. Try it on your own system and see: write-protect a directory, then try to change the name of a file in that directory, add a file, or remove a file. You can't if the directory itself doesn't have write permission.

OK, so let's go back to our original scenario. Joey Mac is about to save a file he's edited. Days earlier, Joey removed write permission from that file, but he forgot (and he can't remember why he'd do that anyway, but Joey isn't too bright).

So now Joey Mac is trying to save his file. But as it's not allowing writes because Joey himself removed that permission (and who else would be poking around in Joey's own files) then his app can't save the file, and Joey's told to check file permissions.

All Joey has to do - without losing his file which is still open - is check the file permissions, allow writes again, alternatively save to another location for the time being so his work isn't lost and he has to go out and make an Ellen Feiss advert to work off his boundless frustration.

But Joey Mac didn't like that. (Maybe he saw the Ellen Feiss advert and freaked out?) So he talks with an Apple engineer and bitches bitches bitches... Now the Apple engineer has to come up with a brilliant idea to placate Joey Mac aka the boss called (in the corridors of One Infinite) Mister Menopause.

The idea the engineer comes up with isn't too bright. (He's probably not a NeXT engineer, so cut him some slack.) If he just flips that secure copy operation into a move, then everything should work, right?

So you retool NSDocumentController a bit. You don't let everything go down deep and dirty as before. You check file permissions first - yes, at this high level. And if you find the file is actually write-protected (you don't really care because you're going to trash the file anyway - you just pretend, good Maccie method) then you issue a fake diagnostic, telling Joey Mac that his file is LOCKED.

Locked? Unix doesn't have file locks! There is no such thing!

'Do you want to save your changes to it anyway?' asks the new alert message. How do you save files 'anyway'? Apple ingenuity. You move the temporary copy into place.


[Note that the above breaks most everything related to file system intrinsics. Another topic, another time. Ed.]

Note of course that this only works if the directory itself is not write-protected - something the genius working on this code didn't think of. Or perhaps he did think of it, but he knew his boss Joey Mac was thick as a brick and wouldn't himself notice.

Whatever. That's the way they made it. Way back in the last millennium or whenever. And sure, it worked fine - until the directory is 'locked'. Then all hell breaks lose!

Then you're issued another fake message saying:

'You don't have permission to save the file [BLAH BLAH]'

And:

'To view or change permissions, select the item in the Finder and choose File > Get Info.'

But that's what you would have seen in the first place! That's what got Joey Mac pissed!

Except now you're directly misleading Mr Mac. You're telling him to check a file. But it's not about his file - it's about his directory! And how much does Joey Mac know about Unix directories? Care to hazard a guess? Remember that he's the guy who thought it's too much trouble to check file permissions he'd screwed up himself...

No, Joey Mac isn't going to be able to figure out what's wrong. And if he does, then he might as well have been given that message earlier and saved everyone a lot of grief and unnecessary work.

This has already been discussed elsewhere, and many times. It's a Really Dumb Idea™ and it's in the system code to this day. Someone at Apple really likes that feature and/or is too embarrassed to let everyone know Joey Mac's boss was behind it.

But there's another way. A way so simple it's not funny. Remember that we're talking about user files here. The kind found in a user's own directories.

And as we're writing software, and software is supposed to simplify things and make life easier for people, shouldn't we just offer to change those file permissions programmatically? For Joey Mac's convenience?

Why not instead issue:

'Do you want to change your file's permissions so you can save it?'

Followed by three buttons:

Don't Save | Change, Save, and Revert | Change and Save

That's it. NSDocumentController makes a note of the file permissions. (It's doing it already anyway, but wouldn't have to without Joey Mac's idiotic request.) If Joey wants to permanently restore write permission, then he clicks the rightmost button. If Joey wants the file saved but wants to keep the file protected, then he clicks the next button over. And finally, if Joey just screwed up again (but we observe the Forgiveness Principle, don't we) then he has the button on the far left to click - 'Don't Save'.

As the file is presumably owned by Joey, a change in permissions should work. (NSDocumentController can always run checks first on file ownership, user flags, and ACEs to make sure a write is possible, but that can be a bit too advanced for Joey's programmer friend.) Then the permissions (the 'mode' in Unix parlance) are loosened to allow the write - just put in an octal 2. Then, depending on, the original file permissions are restored.

Simple operations. Written correctly, there's less risk Joey will screw up. And we've also fulfilled our pledge to make life easier for our software users.

And we've also done so without mucking up, potentially getting Joey into even more trouble, or potentially trashing our file system.

Win-win.

APFS

APFS is Apple's new file system, to be released this autumn. There are caveats, which people should read up on. And hopefully the NSDocumentController nonsense will be gone.

See Also
Industry Watch: 10.6.3
Developers Workshop: Y.G.B.K.
Hotspots: A Document Being Saved on 10.4
Developers Workshop: Disastrous Design Decisions
Hotspots: (A Document Being Saved By Rixedit)/Untitled
Apple Developer Documentation: NSDocumentController
Hotspots: (A Document Being Saved By Rixedit)/CocoaDocument-Based.rtx

About | ACP | Buy | Industry Watch | Learning Curve | News | Products | Search | Substack
Copyright © Rixstep. All rights reserved.