[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
use of lchmod
From: |
Eric Blake |
Subject: |
use of lchmod |
Date: |
Tue, 15 Sep 2009 16:39:51 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
POSIX does not require lchmod, and Linux does not supply it. On platforms that
lack lchmod, symlinks always have the same default permissions, so the
inability to change permissions is not a problem because the source that you
would be copying has the same permissions (0777) as the newly created symlink
destination.
On BSD, where lchmod is supported, only the 0555 bits matter (readlink() fails
if the current user can't read the symlink, and all other syscalls fail with
ENOENT for failure to follow the symlink if the current user can't
search/execute the symlink during file name resolution), and even then, only if
on a file system mounted with the symperm option.
Right now, gnulib has an lchmod replacement which always calls chmod; this will
do the wrong thing if it is ever called on a symlink, but is right for
everything else. Meanwhile, the fchmodat replacment (and hence the lchmodat
convenience wrapper) blindly fails with ENOSYS, even for non-symlinks; this is
different than native fchmodat on Linux.
POSIX requires that fchmodat(,O_SYMLINK_NOFOLLOW) fail with EOPNOTSUPP (which
is often equal to ENOTSUP, including on Linux), not ENOSYS, but only if the
argument is a symlink and symlink modes can't be changed, and that otherwise it
behaves like chmod.
In the current glibc.git, fchmodat blindly returns ENOTSUP for all file types,
rather than behaving like chmod.
I'm thinking we should change things to be a bit more consistent, as part of
preparing copy.c to be rewritten to use fts, but I'm not sure which way to go.
Does anyone have preference?
If we go with POSIX, then:
The replacement lchmod should use fchmodat() when it is natively available and
behaves like POSIX requires (getting ENOTSUP for free on symlinks, and no
overhead fallback to chmod otherwise). On Linux, it should lstat() the
argument, and fail with ENOTSUP only if attempted on a symlink, otherwise call
chmod (yes, this is a slight race, but so are most of our other emulations).
On Linux we wrap native fchmodat() to call fstatat(), so that we only fail on
true symlinks, but again with a slight race.
In coreutils, we can blindly call lchmod without regards to file type, and rely
on ENOTSUP occurring only on a symlink, at which point we safely ignore the
failure; no extra call to chmod is needed. But every call to lchmod adds an
extra lstat() on Linux, until some future day when the kernel and glibc give
atomic fchmodat support that obeys POSIX.
If we go with glibc behavior, then:
The replacement lchmod should be changed to blindly fail with ENOTSUP, rather
than the current behavior of calling chmod. No standards compliance issue,
since lchmod is not standardized.
We do not need to replace native Linux fchmodat(), and we only have to tweak
our existing replacement fchmodat() to blindly fail with ENOTSUP instead of
ENOSYS on other platforms. This is not strictly POSIX, but if used correctly,
is more efficient on Linux.
In coreutils, we have to be careful to call chmod rather than lchmod on non-
symlinks, saving lchmod for when we know that the target should be a symlink.
On ENOTSUP failure, we have to fall back to chmod for non-symlinks.
Either way:
cp.c has two uses of lchmod, to implement 'cp --parents --preserve a/b c/d'
with correct permissions on c/d/a according to the original a. One use is
always on a directory (making it temporarily writable); the other is in
re_protect to restore permissions, although I didn't spend enough time seeing
whether the restore path could ever happen on a symlink to a directory
installed in place of the original directory. So it seems like cp.c should be
using chmod, not lchmod.
copy.c has several uses of lchmod (including via the wrapper fchmod_or_lchmod);
but again, all of them look like they are used for temporarily adding
permissions to read-only destinations, or for granting additional permissions
that were intentionally omitted at the beginning. In particular, for the case
of fchmod_or_lchmod, chmod should always be sufficient, as you cannot have an
open fd on a symlink, but the function is designed for use on an open fd.
Meanwhile, it looks like on a BSD system, 'cp --preserve' is missing a use of
lchmod to preserve permissions on a copied symlink; here, we can safely ignore
ENOTSUP failure of lchmod on Linux.
Finally, we should consider adding 'chmod -h' to mirror 'chown -h', where it
would actually work on BSD, and would just print an error message on Linux when
attempted on symlinks but behave like plain 'chmod' on regular files. This
would always require an lstat() or reliable d_type (either in the gnulib
wrapper if we choose to make fchmodat match POSIX, or in chmod.c if we make
fchmodat match glibc).
Right now, I'm leaning towards matching glibc behavior, and making the burden
of the lstat() fall on the caller; particularly since the caller can use d_type
information instead of lstat() in some cases. Does all of this sound
reasonable?
--
Eric Blake
- use of lchmod,
Eric Blake <=