Don’t Be That Guy
If your application dereferences symlinks by default, you are a jerk. Your software is bad, and you should feel bad. Why do you hate your users?
Won’t Someone Think Of The Users?
On OS-X in the Finder, there is a neat pane on the left where you can bookmark your favorite places to get to them quickly and easily. Just drag a folder into it, and you can get to it from any Finder window. It’s super convenient., Unless of course you make a symbolic link. Which is basically just another concept for an easy way to get to another place.
if you create a symlink, and then try to add it as a Favorite, Finder will dereference the link, and favorite what the link points to rather than the link itself. This is evil. It’s not what the user asked for! It’s an extreme violation of the Principle Of Least Surprise. The implicit contract between the user and the system is that if I favorite something, clicking on the thing and the favorite will always take me to the same place. The favorite represents the thing I was dragging into the Favorites bar, not whatever it may have been pointing at. If I ever change where the symlink points, the favorite and the symlink will now be doing two different things. For no obvious reason!
“But most users don’t care about symlinks!” you exclaim. “And if they did, they know what’s going on because they created them!” you further exclaim. Unfortunately, this is a real problem that has punched me in the face repeatedly in the real world. I’ll offer an example, based on many real events in several places.
Imagine that there is a server on the network arbitrarily named gryphon-01, and it is set to automount on a user’s workstation in /net/gryphon-01. A sysadmin might set up a symlink on all the users’ machines called “FileServer.” Any user who double clicks on “FileServer” will be blissfully isolated from the implementation details of the name of the particular file server they should be using. They’ll be taken to the right folder, and it will “Just Work.” On the other hand, if a user drags that icon named FileServer into their favorites in the Finder, it will actually favorite /net/gryphon-01. The user has no idea WTF gryphon is. A human being specifically decided to call it FileServer, but the software is a jerk written by a jerk, and it has decided to show gryphon-01 in the favorites bar. The sysadmin who set up the server won’t be terribly bothered by it at first, but the end user thinks the new name has come from nowhere, and may or may not even realize that their new favorite is actually referring to what they were dragging. Most users will simply think that bookmarking the location failed because FileServer never showed up in their favorites bar. This leads to user complaints that things don’t work.
I also hate your Open dialog box
If you are making a UI toolkit and your provide an “Open” dialog box, you should also be sure to provide the path that the user navigated, rather than a canonicalized version of it. If a user navigates to /projects/Client/Job/Bar, and your API returns /Volumes/Foo/Data/Client/Job/Bar, then the application using your API has no way of choosing between a canonical form or the user supplied form since dereferencing to get a canonical form is inherently a one-way transformation. It also means that a path the user Didn’t Pick is now supplied to the host application, which makes your API a liar. If the application has functionality like “Go To Parent Directory” the user may wind up somewhere unexpected and useless because you are a terrible person who made an API that is a liar. The client application can always dereference the path if that’s what they want. Or let them explicitly supply a flag to request something canonical. But don’t dereference by default!
I Love You
Obviously, I am being a bit over-the-top with my emotional hatred towards an API design decision that many users will never care about. But I think it’s useful. I am hoping that anybody who reads this will remember the vitriol the next time they are making design decisions about path handling, and do the right thing. Even if they don’t care strongly about how paths are handled, at least there remember that there is a lunatic on the Internet who will try to make them feel bad if they do it wrong, and he might eventually use their software.
On the bright side, not dereferencing a link is generally really easy. Just accept the path a user gives you and don’t screw with it. Even on Windows, NTFS has had real symlink support since Vista. So you can party like it’s the late 1970’s on any common platform in modern use.
Unfortunately even on platforms where it’s easy to do the wrong thing, many API’s do the wrong thing. Qt, for example requires a special flag to Don’t Resolve Symlinks rather than defaulting to that behavior and requiring a special flag to Resolve Symlinks, which would grammar better anyway. I’ve always been suspicious of flag naming conventions that are used to affirmatively don’t something. It makes sense sometimes, but all things being equal, affirmative flags to add behavior usually make more intuitive sense than negative flags. Having multiple negative flags often implies that something is doing too many things at once that aren’t obvious from the name, which means the code isn’t self documenting. When you make a QFileDialog, just remember to pass QFileDialog::DontResolveSymlinks, and you’ll be well on your way. Most broken apps only need a few lines of fixes similar to that to have correct behavior. Hopefully some day Qt decides to fix QFileDialog to switch the default behavior. In my ideal world, there would be something like QFileDialog::getOpenFileName() and also QFileDialog::getOpenCanonicalFileName() such that the client developer would have to be explicit about request automatic dereferencing behavior after the user selects a path.
One thought on “Don’t Dereference Symlinks”
I dug into this problem once in sufficient detail to learn that this was a “feature” of OS X. I used a different term.