An important safety note about chown and symlinks (also chmod and chgrp)

April 20, 2020

Today I had a little Twitter exchange about a little (or not so little) thing that is not as well known as it should be:

@thatcks: Ah yes, the eternal question: is it 'chown -Hr' or 'chown -hR' that I want?

(Answer: the second. Also, -h should be the default, at least with -R.)

@mrhoten: TIL I have run some pretty dang risky chown -R's and not even known it.

For those who haven't encountered this particular combination of arguments for the chown command, -R is a recursive chown through a directory hierarchy, while -h is, well, let me quote the manual (for GNU chown):

affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)

(Similar wording is in the FreeBSD chown manpage.)

Let me translate this. If you run plain 'chown -R dirtree', and the directory tree contains symlinks that point outside itself, your recursive chown will change the ownership of the files that those symlinks point to. Is there a symlink to /etc/shadow? Well, you just changed its ownership. A recursive chown without -h can do random damage to owners and groups anywhere on your system.

Recursive chowns aren't the only time this comes up. Consider 'chown *' in a directory, to fix ownership for the user. If there are any symlinks, congratulations, you once again changed the ownership of random files on your system. You should always use -h with chown unless you're sure you know what you're doing. You almost never want to change the ownership of the target of a symlink.

(If you have a tangled chain of symlinks and you need to fix the permissions of the real underlying file behind them all, generally use realpath to find out what it is first.)

Naturally the same thing applies to the chgrp command. The GNU chmod command unfortunately lacks a -h option, and will always change permissions on the target of symlinks given on the command line (which might happen in, for example, 'chmod g+r *'). FreeBSD's chmod does have a -h option and you should always use it unless you're sure.

Sidebar: The potential bad state of OpenBSD and chown

The OpenBSD chown manpage contains the following hair-raising statement about the -h option (emphasis mine):

Treat symbolic links like other files: modify links instead of following them. The -h and -R options are mutually exclusive.

OpenBSD's chown also has a -P argument, which is the default behavior in 'chown -R'. This is described as:

If the -R option is specified, no symbolic links are followed.

It's not clear what OpenBSD's manpage means by 'followed' here. Are symbolic links that point to directories not descended into but symbolic links are chown'd in general, or are symbolic links entirely ignored, being neither chown'd nor descended into? If OpenBSD chown really does follow the POSIX chown specification, then the POSIX specification is much clearer here and specifies the behavior we don't want:

If the -R option is specified and a symbolic link is specified on the command line or encountered during the traversal of a file hierarchy, chown shall change the owner ID (and group ID, if specified) of the symbolic link. The chown utility shall not follow the symbolic link to any other part of the file hierarchy.

Update: I think I misread this and was wrong about the POSIX behavior, because it specifies that with -P, the symbolic link itself is chown'd. That would make it the behavior we want.

If OpenBSD chown really uses the POSIX behavior, then you can't safely use chown on OpenBSD to change the ownership of a directory tree. You need to use a much more awkward find command line to chown everything that isn't a symlink.

(POSIX appears to implicitly forbid combining '-h' and '-R', but that's crazy and may be a mistake in POSIX.)

Written on 20 April 2020.
« Verifying the server hostname for a TLS certificate has two purposes
More on chown in combination with symlinks »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Apr 20 23:40:21 2020
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.