Skip to content

Commit

Permalink
rofiles-fuse: Fix lchown() (and pro-actively NOFOLLOW)
Browse files Browse the repository at this point in the history
If you lchown("symlink") then we were incorrectly trying to chown the
symlink target, rather than the symlink itself. In particular, this cause
cp -a to fail for a broken symlink. Additionally, it was using the
symlink target when verifying writability, rather than the symlink
itself.

To fix this, we just pass AT_SYMLINK_NOFOLLOW in these cases.

In general, the kernel itself will always resolve any symlinks for us
before calling into the fuse backend, so we should really never do any
symlink following in the fuse fs itself. So, we pro-actively add
NOFOLLOW flags to a few other places:

 truncate:
      In reality this will never be hit, because
      the kernel will resolve symlinks before calling us.
 chmod:
      Theoretically enable ownership setting on a symlink. This is
      not implemented on current kernels, but if it ever is, this
      will be required.
 access:
      It seems the current fuse implementation never calls this
      (faccessat w/AT_SYMLINK_NOFOLLOW never reaches the fuse fs)
      but if this ever is implemented this is the correct behaviour.
  • Loading branch information
alexlarsson committed Sep 6, 2017
1 parent 0fb8686 commit 3ad7c23
Showing 1 changed file with 5 additions and 5 deletions.
10 changes: 5 additions & 5 deletions src/rofiles-fuse/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ static int
can_write (const char *path)
{
struct stat stbuf;
if (fstatat (basefd, path, &stbuf, 0) == -1)
if (fstatat (basefd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
{
if (errno == ENOENT)
return 0;
Expand All @@ -231,7 +231,7 @@ callback_chmod (const char *path, mode_t mode)
{
path = ENSURE_RELPATH (path);
VERIFY_WRITE(path);
if (fchmodat (basefd, path, mode, 0) != 0)
if (fchmodat (basefd, path, mode, AT_SYMLINK_NOFOLLOW) != 0)
return -errno;
return 0;
}
Expand All @@ -241,7 +241,7 @@ callback_chown (const char *path, uid_t uid, gid_t gid)
{
path = ENSURE_RELPATH (path);
VERIFY_WRITE(path);
if (fchownat (basefd, path, uid, gid, 0) != 0)
if (fchownat (basefd, path, uid, gid, AT_SYMLINK_NOFOLLOW) != 0)
return -errno;
return 0;
}
Expand All @@ -254,7 +254,7 @@ callback_truncate (const char *path, off_t size)
path = ENSURE_RELPATH (path);
VERIFY_WRITE(path);

fd = openat (basefd, path, O_WRONLY);
fd = openat (basefd, path, O_NOFOLLOW|O_WRONLY);
if (fd == -1)
return -errno;

Expand Down Expand Up @@ -433,7 +433,7 @@ callback_access (const char *path, int mode)
* before trying to do an unlink. So...we'll just lie about
* writable access here.
*/
if (faccessat (basefd, path, mode, 0) == -1)
if (faccessat (basefd, path, mode, AT_SYMLINK_NOFOLLOW) == -1)
return -errno;
return 0;
}
Expand Down

0 comments on commit 3ad7c23

Please sign in to comment.