On Wed, Feb 12, 2020 at 12:48:49PM +0100, Florian Weimer wrote: > In principle, Linux supports lchmod via O_PATH descriptors and chmod > on /proc/self/fd. (lchmod is the non-symbolic-link-following variant > of chmod.) > > This helper program can be used to do this: > > #define _GNU_SOURCE > #include <err.h> > #include <fcntl.h> > #include <stdio.h> > #include <stdlib.h> > #include <sys/stat.h> > #include <unistd.h> > > int > main (int argc, char **argv) > { > if (argc != 3) > { > fprintf (stderr, "usage: %s MODE FILE\n", argv[0]); > return 2; > } > > unsigned int mode; > if (sscanf (argv[1], "%o", &mode) != 1 > || mode != (mode_t) mode) > errx (1, "invalid mode: %s", argv[1]); > > int fd = open (argv[2], O_PATH | O_NOFOLLOW); > if (fd < 0) > err (1, "open"); > > char *fd_path; > if (asprintf (&fd_path, "/proc/self/fd/%d", fd) < 0) > err (1, "asprintf"); > > if (chmod (fd_path, mode) != 0) > err (1, "chmod"); > > free (fd_path); > if (close (fd) != 0) > err (1, "close"); > > return 0; > } > > When changing the permissions of on XFS in this way, the chmod > operation fails: > > $ ln -s does-not-exist /var/tmp/symlink > $ ls -l /var/tmp/symlink > lrwxrwxrwx. 1 fweimer fweimer 14 Feb 12 12:41 /var/tmp/symlink -> does-not-exist > $ strace ./lchmod 0 /var/tmp/symlink > […] > openat(AT_FDCWD, "/var/tmp/symlink", O_RDONLY|O_NOFOLLOW|O_PATH) = 3 > […] > chmod("/proc/self/fd/3", 000) = -1 EOPNOTSUPP (Operation not supported) > write(2, "lchmod: ", 8lchmod: ) = 8 > write(2, "chmod", 5chmod) = 5 > write(2, ": Operation not supported\n", 26: Operation not supported > ) = 26 > exit_group(1) = ? > > But the file system contents has changed nevertheless: > > $ ls -l /var/tmp/symlink > l---------. 1 fweimer fweimer 14 Feb 12 12:41 /var/tmp/symlink -> does-not-exist > $ echo 3 | sudo tee /proc/sys/vm/drop_caches > $ ls -l /var/tmp/symlink > l---------. 1 fweimer fweimer 14 Feb 12 12:41 /var/tmp/symlink -> does-not-exist > > This looks like an XFS bug to me. With tmpfs, the chmod succeeds and > is reflected in the file system. > > This bug also affects regular files, not just symbolic links. > > It causes the io/tst-lchmod glibc test to fail (after it has been > fixed, the in-tree version has another bug). xfs_setattr_nonsize calls posix_acl_chmod which returns EOPNOTSUPP because the xfs symlink inode_operations do not include a ->set_acl pointer. I /think/ that posix_acl_chmod code exists to enforce that the file mode reflects any acl that might be set on the inode, but in this case the inode is a symbolic link. I don't remember off the top of my head if ACLs are supposed to apply to symlinks, but what do you think about adding get_acl/set_acl pointers to xfs_symlink_inode_operations and xfs_inline_symlink_inode_operations ? --D