GNU bug report logs - #70214
'install' fails to copy regular file to autofs/cifs, due to ACL or xattr handling

Previous Next

Package: coreutils;

Reported by: Bruno Haible <bruno <at> clisp.org>

Date: Fri, 5 Apr 2024 09:49:02 UTC

Severity: normal

To reply to this bug, email your comments to 70214 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Fri, 05 Apr 2024 09:49:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Bruno Haible <bruno <at> clisp.org>:
New bug report received and forwarded. Copy sent to bug-coreutils <at> gnu.org. (Fri, 05 Apr 2024 09:49:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Bruno Haible <bruno <at> clisp.org>
To: bug-coreutils <at> gnu.org
Subject: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Fri, 05 Apr 2024 11:48:14 +0200
Hi,

The 'install' program from coreutils-9.5 fails to copy a regular file
from an ext4 mount to an autofs/cifs mount.

The same operation, with 'cp -a', works fine.

Also, it works fine when coreutils was built with the configure options
"--disable-acl --disable-xattr".

How to reproduce
================

1) On the machine sparcdev.matoro.tk (Linux 6.8.2), I built coreutils-9.5
from source,
  - once with default options, in build-sparc64/,
  - once with "--disable-acl --disable-xattr", in build-sparc64-no-acl/.

2) Create a regular file on an ext4 mount:

$ echo hi > /var/tmp/foo3941
$ ls -lZ /var/tmp/foo3941
-rw-r----- 1 g-haible g-haible ? 3 Apr  4 13:29 /var/tmp/foo3941
$ getfacl /var/tmp/foo3941
getfacl: Removing leading '/' from absolute path names
# file: var/tmp/foo3941
# owner: g-haible
# group: g-haible
user::rw-
group::r--
other::---
$ df -m /var/tmp/
Filesystem     1M-blocks   Used Available Use% Mounted on
/dev/root         560245 123140    408574  24% /
$ mount | grep ' / '
/dev/sda2 on / type ext4 (rw,noatime)

3) Details about the destination directory:

$ echo $HOME
/media/guest-homedirs/haible
$ mount | grep /media/guest-homedirs/haible
/etc/autofs/auto.guest-homedirs on /media/guest-homedirs/haible type autofs (rw,relatime,fd=7,pgrp=2325,timeout=60,minproto=5,maxproto=5,direct,pipe_ino=46092)
//syslog.matoro.tk/guest-haible on /media/guest-homedirs/haible type cifs (rw,nosuid,relatime,vers=1.0,cache=strict,username=nobody,uid=30014,forceuid,gid=30014,forcegid,addr=fd05:0000:0000:0000:0000:0000:0000:0001,soft,unix,posixpaths,serverino,mapposix,acl,rsize=1048576,wsize=65536,bsize=1048576,retrans=1,echo_interval=60,actimeo=1,closetimeo=1)

4) The operation that fails:

$ build-sparc64/src/ginstall -c /var/tmp/foo3941 $HOME/foo3941; echo $?
build-sparc64/src/ginstall: setting permissions for '/media/guest-homedirs/haible/foo3941': Permission denied
1
$ build-sparc64-no-acl/src/ginstall -c /var/tmp/foo3941 $HOME/foo3941; echo $?
0

5) The same thing with 'cp -a' succeeds:

$ build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
0
$ build-sparc64-no-acl/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
0

6) 'strace' shows a failing call to fsetxattr:

$ strace build-sparc64/src/ginstall -c /var/tmp/foo3941 $HOME/foo3941
execve("build-sparc64/src/ginstall", ["build-sparc64/src/ginstall", "-c", "/var/tmp/foo3941", "/media/guest-homedirs/haible/foo"...], 0x7feffffce28 /* 43 vars */) = 0
brk(NULL)                               = 0x10000204000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=41866, ...}) = 0
mmap(NULL, 41866, PROT_READ, MAP_PRIVATE, 3, 0) = 0xfff8000100028000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\2\1\0\0\0\0\0\0\0\0\0\0\3\0+\0\0\0\1\0\0\0\0\0\0\0\0"..., 832) = 832
fstat64(3, {st_mode=S_IFREG|0755, st_size=1052312, ...}) = 0
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfff8000100034000
mmap(NULL, 3147696, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xfff8000100206000
mmap(0xfff8000100300000, 2099120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xfff8000100300000
munmap(0xfff8000100206000, 1024000)     = 0
munmap(0xfff8000100502000, 18352)       = 0
mprotect(0xfff8000100308000, 2056192, PROT_NONE) = 0
mmap(0xfff80001004fe000, 16384, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xfe000) = 0xfff80001004fe000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\2\1\0\0\0\0\0\0\0\0\0\0\3\0+\0\0\0\1\0\0\0\0\0\0\0\0"..., 832) = 832
fstat64(3, {st_mode=S_IFREG|0755, st_size=1051928, ...}) = 0
mmap(NULL, 3147288, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xfff8000100502000
mmap(0xfff8000100600000, 2098712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xfff8000100600000
munmap(0xfff8000100502000, 1040384)     = 0
munmap(0xfff8000100802000, 1560)        = 0
mprotect(0xfff8000100604000, 2072576, PROT_NONE) = 0
mmap(0xfff80001007fe000, 16384, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xfe000) = 0xfff80001007fe000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\2\1\3\0\0\0\0\0\0\0\0\0\3\0+\0\0\0\1\0\0\0\0\0\2\305\240"..., 832) = 832
fstat64(3, {st_mode=S_IFREG|0755, st_size=2110256, ...}) = 0
mmap(NULL, 4233760, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xfff8000100802000
mmap(0xfff8000100900000, 3185184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xfff8000100900000
munmap(0xfff8000100802000, 1040384)     = 0
munmap(0xfff8000100c0a000, 6688)        = 0
mprotect(0xfff8000100a8e000, 1499136, PROT_NONE) = 0
mmap(0xfff8000100bfc000, 24576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1fc000) = 0xfff8000100bfc000
mmap(0xfff8000100c02000, 31264, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xfff8000100c02000
close(3)                                = 0
set_tid_address(0xfff80001000365f0)     = 28674
set_robust_list(0xfff8000100036600, 24) = 0
mprotect(0xfff8000100bfc000, 16384, PROT_READ) = 0
mprotect(0xfff80001007fe000, 8192, PROT_READ) = 0
mprotect(0xfff80001004fe000, 8192, PROT_READ) = 0
mprotect(0x100001fe000, 8192, PROT_READ) = 0
mprotect(0xfff80001001fe000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0xfff8000100028000, 41866)       = 0
getrandom("\x78\x3e\xea\xde\x42\xab\xf0\x1b", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x10000204000
brk(0x10000226000)                      = 0x10000226000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=3061520, ...}) = 0
mmap(NULL, 3061520, PROT_READ, MAP_PRIVATE, 3, 0) = 0xfff8000100c0c000
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2998, ...}) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2998
read(3, "", 4096)                       = 0
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/locale/de_DE.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de_DE.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de_DE/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
geteuid()                               = 30014
umask(000)                              = 027
openat(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOTDIR (Not a directory)
fstatat64(AT_FDCWD, "/var/tmp/foo3941", {st_mode=S_IFREG|0640, st_size=3, ...}, 0) = 0
fstatat64(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", {st_mode=S_IFREG|0640, st_size=3, ...}, AT_SYMLINK_NOFOLLOW) = 0
unlinkat(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", 0) = 0
openat(AT_FDCWD, "/var/tmp/foo3941", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0640, st_size=3, ...}) = 0
openat(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EXDEV (Invalid cross-device link)
fstat64(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
fadvise64_64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
uname({sysname="Linux", nodename="matoro-sparcdev", ...}) = 0
copy_file_range(3, NULL, 4, NULL, 9223372035781033984, 0) = -1 EXDEV (Invalid cross-device link)
mmap(NULL, 1064960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfff8000100038000
read(3, "hi\n", 1048576)                = 3
write(4, "hi\n", 3)                     = 3
read(3, "", 1048576)                    = 0
fsetxattr(4, "system.posix_acl_access", "\2\0\0\0\1\0\6\0\377\377\377\377\4\0\0\0\377\377\377\377 \0\0\0\377\377\377\377", 28, 0) = -1 EACCES (Permission denied)
fchmod(4, 0600)                         = 0
write(2, "build-sparc64/src/ginstall: ", 28build-sparc64/src/ginstall: ) = 28
write(2, "setting permissions for '/media/"..., 62setting permissions for '/media/guest-homedirs/haible/foo3941') = 62
write(2, ": Permission denied", 19: Permission denied)     = 19
write(2, "\n", 1
)                       = 1
close(4)                                = 0
close(3)                                = 0
munmap(0xfff8000100038000, 1064960)     = 0
_llseek(0, 0, 0x7feffbca600, SEEK_CUR)  = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

7) Symbol differences between the two builds:

$ nm build-sparc64/src/ginstall | grep acl
000000000001725c T acl_access_nontrivial
000000000001732c T acl_default_nontrivial
                 U acl_delete_def_file <at> ACL_1.0
                 U acl_entries <at> ACL_1.0
0000000000018b24 T acl_errno_valid
                 U acl_free <at> ACL_1.0
                 U acl_from_mode <at> ACL_1.0
                 U acl_get_entry <at> ACL_1.0
                 U acl_get_tag_type <at> ACL_1.0
                 U acl_set_fd <at> ACL_1.0
                 U acl_set_file <at> ACL_1.0
000000000000b5f4 T copy_acl
000000000000fbb8 T qcopy_acl
000000000000fc3c T qset_acl
000000000000b6d4 T set_acl
0000000000017394 t set_acls.isra.0

$ nm build-sparc64-no-acl/src/ginstall | grep acl
000000000000ade8 T copy_acl
000000000000f37c T qcopy_acl
000000000000f40c T qset_acl
000000000000aec8 T set_acl


Notes
=====

The 'cp' program does *not* use fsetxattr() calls on the destination file
descriptor and therefore does not fail:

$ strace build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941
execve("build-sparc64/src/cp", ["build-sparc64/src/cp", "-a", "/var/tmp/foo3941", "/media/guest-homedirs/haible/foo"...], 0x7feffc96e38 /* 43 vars */) = 0
brk(NULL)                               = 0x10000202000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=41866, ...}) = 0
mmap(NULL, 41866, PROT_READ, MAP_PRIVATE, 3, 0) = 0xfff8000100028000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\2\1\0\0\0\0\0\0\0\0\0\0\3\0+\0\0\0\1\0\0\0\0\0\0\0\0"..., 832) = 832
fstat64(3, {st_mode=S_IFREG|0755, st_size=1052312, ...}) = 0
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfff8000100034000
mmap(NULL, 3147696, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xfff8000100494000
mmap(0xfff8000100500000, 2099120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xfff8000100500000
munmap(0xfff8000100494000, 442368)      = 0
munmap(0xfff8000100702000, 599984)      = 0
mprotect(0xfff8000100508000, 2056192, PROT_NONE) = 0
mmap(0xfff80001006fe000, 16384, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xfe000) = 0xfff80001006fe000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\2\1\0\0\0\0\0\0\0\0\0\0\3\0+\0\0\0\1\0\0\0\0\0\0\0\0"..., 832) = 832
fstat64(3, {st_mode=S_IFREG|0755, st_size=1051928, ...}) = 0
mmap(NULL, 3147288, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xfff8000100702000
mmap(0xfff8000100800000, 2098712, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xfff8000100800000
munmap(0xfff8000100702000, 1040384)     = 0
munmap(0xfff8000100a02000, 1560)        = 0
mprotect(0xfff8000100804000, 2072576, PROT_NONE) = 0
mmap(0xfff80001009fe000, 16384, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xfe000) = 0xfff80001009fe000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\2\1\3\0\0\0\0\0\0\0\0\0\3\0+\0\0\0\1\0\0\0\0\0\2\305\240"..., 832) = 832
fstat64(3, {st_mode=S_IFREG|0755, st_size=2110256, ...}) = 0
mmap(NULL, 4233760, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xfff8000100a02000
mmap(0xfff8000100b00000, 3185184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xfff8000100b00000
munmap(0xfff8000100a02000, 1040384)     = 0
munmap(0xfff8000100e0a000, 6688)        = 0
mprotect(0xfff8000100c8e000, 1499136, PROT_NONE) = 0
mmap(0xfff8000100dfc000, 24576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1fc000) = 0xfff8000100dfc000
mmap(0xfff8000100e02000, 31264, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xfff8000100e02000
close(3)                                = 0
set_tid_address(0xfff80001000365d0)     = 29091
set_robust_list(0xfff80001000365e0, 24) = 0
mprotect(0xfff8000100dfc000, 16384, PROT_READ) = 0
mprotect(0xfff80001009fe000, 8192, PROT_READ) = 0
mprotect(0xfff80001006fe000, 8192, PROT_READ) = 0
mprotect(0x100001fe000, 8192, PROT_READ) = 0
mprotect(0xfff80001001fe000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0xfff8000100028000, 41866)       = 0
getrandom("\x26\xa7\x29\x29\xf6\x69\x36\x3e", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x10000202000
brk(0x10000224000)                      = 0x10000224000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=3061520, ...}) = 0
mmap(NULL, 3061520, PROT_READ, MAP_PRIVATE, 3, 0) = 0xfff8000100e0c000
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2998, ...}) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2998
read(3, "", 4096)                       = 0
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/locale/de_DE.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de_DE.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de_DE/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de.utf8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/de/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
geteuid()                               = 30014
openat(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", O_RDONLY|O_PATH|O_DIRECTORY) = -1 ENOTDIR (Not a directory)
fstatat64(AT_FDCWD, "/var/tmp/foo3941", {st_mode=S_IFREG|0640, st_size=3, ...}, AT_SYMLINK_NOFOLLOW) = 0
fstatat64(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", {st_mode=S_IFREG|0600, st_size=3, ...}, 0) = 0
openat(AT_FDCWD, "/var/tmp/foo3941", O_RDONLY|O_NOFOLLOW) = 3
fstat64(3, {st_mode=S_IFREG|0640, st_size=3, ...}) = 0
openat(AT_FDCWD, "/media/guest-homedirs/haible/foo3941", O_WRONLY|O_TRUNC) = 4
ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EXDEV (Invalid cross-device link)
fstat64(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
fadvise64_64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
uname({sysname="Linux", nodename="matoro-sparcdev", ...}) = 0
copy_file_range(3, NULL, 4, NULL, 9223372035781033984, 0) = -1 EXDEV (Invalid cross-device link)
mmap(NULL, 1064960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfff8000100038000
read(3, "hi\n", 1048576)                = 3
write(4, "hi\n", 3)                     = 3
read(3, "", 1048576)                    = 0
utimensat(4, NULL, [{tv_sec=1712251745, tv_nsec=269582795} /* 2024-04-04T13:29:05.269582795-0400 */, {tv_sec=1712251745, tv_nsec=270582848} /* 2024-04-04T13:29:05.270582848-0400 */], 0) = 0
flistxattr(3, NULL, 0)                  = 0
flistxattr(3, 0x7feff9860a0, 0)         = 0
fchmod(4, 0100640)                      = 0
flistxattr(3, NULL, 0)                  = 0
flistxattr(3, 0x7feff9860c0, 0)         = 0
close(4)                                = 0
close(3)                                = 0
munmap(0xfff8000100038000, 1064960)     = 0
_llseek(0, 0, 0x7feff986600, SEEK_CUR)  = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

As you can see, it uses 4 flistxattr() calls on the source file descriptor,
apparently detecting that it's a regular file without ACLs, and proceeds to
do a simple fchmod() call on the destination file descriptor.

Probably the same logic is needed in the 'install' program.


Bruno







Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Sat, 13 Apr 2024 16:40:04 GMT) Full text and rfc822 format available.

Message #8 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Bruno Haible <bruno <at> clisp.org>, 70214 <at> debbugs.gnu.org
Cc: "linux-cifs <at> vger.kernel.org" <linux-cifs <at> vger.kernel.org>,
 Andreas Gruenbacher <andreas.gruenbacher <at> gmail.com>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Sat, 13 Apr 2024 17:39:07 +0100
[Message part 1 (text/plain, inline)]
On 05/04/2024 10:48, Bruno Haible wrote:
> Hi,
> 
> The 'install' program from coreutils-9.5 fails to copy a regular file
> from an ext4 mount to an autofs/cifs mount.
> 
> The same operation, with 'cp -a', works fine.
> 
> Also, it works fine when coreutils was built with the configure options
> "--disable-acl --disable-xattr".
> 
> How to reproduce
> ================
> 
> 1) On the machine sparcdev.matoro.tk (Linux 6.8.2), I built coreutils-9.5
> from source,
>    - once with default options, in build-sparc64/,
>    - once with "--disable-acl --disable-xattr", in build-sparc64-no-acl/.
> 
> 2) Create a regular file on an ext4 mount:
> 
> $ echo hi > /var/tmp/foo3941
> $ ls -lZ /var/tmp/foo3941
> -rw-r----- 1 g-haible g-haible ? 3 Apr  4 13:29 /var/tmp/foo3941
> $ getfacl /var/tmp/foo3941
> getfacl: Removing leading '/' from absolute path names
> # file: var/tmp/foo3941
> # owner: g-haible
> # group: g-haible
> user::rw-
> group::r--
> other::---
> $ df -m /var/tmp/
> Filesystem     1M-blocks   Used Available Use% Mounted on
> /dev/root         560245 123140    408574  24% /
> $ mount | grep ' / '
> /dev/sda2 on / type ext4 (rw,noatime)
> 
> 3) Details about the destination directory:
> 
> $ echo $HOME
> /media/guest-homedirs/haible
> $ mount | grep /media/guest-homedirs/haible
> /etc/autofs/auto.guest-homedirs on /media/guest-homedirs/haible type autofs (rw,relatime,fd=7,pgrp=2325,timeout=60,minproto=5,maxproto=5,direct,pipe_ino=46092)
> //syslog.matoro.tk/guest-haible on /media/guest-homedirs/haible type cifs (rw,nosuid,relatime,vers=1.0,cache=strict,username=nobody,uid=30014,forceuid,gid=30014,forcegid,addr=fd05:0000:0000:0000:0000:0000:0000:0001,soft,unix,posixpaths,serverino,mapposix,acl,rsize=1048576,wsize=65536,bsize=1048576,retrans=1,echo_interval=60,actimeo=1,closetimeo=1)
> 
> 4) The operation that fails:
> 
> $ build-sparc64/src/ginstall -c /var/tmp/foo3941 $HOME/foo3941; echo $?
> build-sparc64/src/ginstall: setting permissions for '/media/guest-homedirs/haible/foo3941': Permission denied
> 1
> $ build-sparc64-no-acl/src/ginstall -c /var/tmp/foo3941 $HOME/foo3941; echo $?
> 0
> 
> 5) The same thing with 'cp -a' succeeds:
> 
> $ build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
> 0
> $ build-sparc64-no-acl/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
> 0
> 
> 6) 'strace' shows a failing call to fsetxattr:
> 
> $ strace build-sparc64/src/ginstall -c /var/tmp/foo3941 $HOME/foo3941

> fsetxattr(4, "system.posix_acl_access", "\2\0\0\0\1\0\6\0\377\377\377\377\4\0\0\0\377\377\377\377 \0\0\0\377\377\377\377", 28, 0) = -1 EACCES (Permission denied)
> fchmod(4, 0600)                         = 0

> Notes
> =====
> 
> The 'cp' program does *not* use fsetxattr() calls on the destination file
> descriptor and therefore does not fail:
> 
> $ strace build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941

> flistxattr(3, NULL, 0)                  = 0
> flistxattr(3, 0x7feff9860a0, 0)         = 0
> fchmod(4, 0100640)                      = 0
> flistxattr(3, NULL, 0)                  = 0
> flistxattr(3, 0x7feff9860c0, 0)         = 0

> As you can see, it uses 4 flistxattr() calls on the source file descriptor,
> apparently detecting that it's a regular file without ACLs, and proceeds to
> do a simple fchmod() call on the destination file descriptor.
> 
> Probably the same logic is needed in the 'install' program.

install(1) defaults to mode 600 for new files, and uses set_acl() with that
(since 2007 https://github.com/coreutils/coreutils/commit/f634e8844 )
The psuedo code that install(1) uses is:

copy_reg()
  if (x->set_mode) /* install */
    set_acl(dest, x->mode /* 600 */)
      ctx->acl = acl_from_mode ( /* 600 */)
      acl_set_fd (ctx->acl) /* fails EACCES */
      if (! acls_set)
         must_chmod = true;
      if (must_chmod)
        saved_errno = EACCES;
        chmod (ctx->mode /* 600 */)
        if (save_errno)
          return -1;

This issue only only seems to be on CIFS.
I'm seeing lot of weird behavior with ACLs there:

  acl_set_fd (acl_from_mode (600)) -> EACCES
  acl_set_fd (acl_from_mode (755)) -> EINVAL
  getxattr ("system.posix_acl_access") -> EOPNOTSUPP

Note we ignore EINVAL and EOPNOTSUPP errors in set_acl(),
and it's just the EACCES that's problematic.
Note this is quite similar to https://debbugs.gnu.org/65599
where Paul also noticed EACCES with fsetxattr() (and others) on CIFS.

The attached is a potential solution which I tested as working
on the same matoro system that Bruno used.

I think I'll apply that after thinking a bit more about it.

cheers,
Pádraig.
[gnulib-set-acl-cifs.patch (text/x-patch, attachment)]

Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Sat, 13 Apr 2024 17:43:01 GMT) Full text and rfc822 format available.

Message #11 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Bruno Haible <bruno <at> clisp.org>, 70214 <at> debbugs.gnu.org
Cc: Andreas Gruenbacher <andreas.gruenbacher <at> gmail.com>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Sat, 13 Apr 2024 18:42:25 +0100
On 13/04/2024 17:39, Pádraig Brady wrote:
> install(1) defaults to mode 600 for new files, and uses set_acl() with that
> (since 2007 https://github.com/coreutils/coreutils/commit/f634e8844 )
> The psuedo code that install(1) uses is:
> 
> copy_reg()
>     if (x->set_mode) /* install */
>       set_acl(dest, x->mode /* 600 */)
>         ctx->acl = acl_from_mode ( /* 600 */)
>         acl_set_fd (ctx->acl) /* fails EACCES */
>         if (! acls_set)
>            must_chmod = true;
>         if (must_chmod)
>           saved_errno = EACCES;
>           chmod (ctx->mode /* 600 */)
>           if (save_errno)
>             return -1;
> 
> This issue only only seems to be on CIFS.
> I'm seeing lot of weird behavior with ACLs there:
> 
>     acl_set_fd (acl_from_mode (600)) -> EACCES
>     acl_set_fd (acl_from_mode (755)) -> EINVAL
>     getxattr ("system.posix_acl_access") -> EOPNOTSUPP
> 
> Note we ignore EINVAL and EOPNOTSUPP errors in set_acl(),
> and it's just the EACCES that's problematic.
> Note this is quite similar to https://debbugs.gnu.org/65599
> where Paul also noticed EACCES with fsetxattr() (and others) on CIFS.
> 
> The attached is a potential solution which I tested as working
> on the same matoro system that Bruno used.
> 
> I think I'll apply that after thinking a bit more about it.

Actually that probably isn't appropriate,
as fsetxattr() can validly return EACESS
even though not documented in the man page at least.
The following demonstrates that:
.
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <fcntl.h>
#include <attr/libattr.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int wfd;
    /* Note S_IWUSR is not set.  */
    if ((wfd=open("writable", O_CREAT|O_WRONLY|O_EXCL, S_IRUSR)) == -1)
        fprintf(stderr, "open('writable') error [%m]\n");
    if (write(wfd, "data", 1) == -1)
        fprintf(stderr, "write() error [%m]\n");
    if (fsetxattr(wfd, "user.test", "test", 4, 0) == -1)
        fprintf(stderr, "fsetxattr() error [%m]\n");
}

Another solution might be to file_has_acl() in copy.c
and skip if "not supported" or nothing is returned.
The following would do that, but I'm not sure about this
(as I'm not sure about ACLs in general TBH).
Note listxattr() returns 0 on CIFS here,
while getxattr ("system.posix_acl_access") returns EOPNOTSUPP,
and file_has_acl() uses listxattr() first.

diff --git a/src/copy.c b/src/copy.c
index d584a27eb..2145d89d5 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1673,8 +1673,13 @@ set_dest_mode:
     }
   else if (x->set_mode)
     {
-      if (set_acl (dst_name, dest_desc, x->mode) != 0)
-        return_val = false;
+      errno = 0;
+      int n = file_has_acl (dst_name, &sb);
+      if (0 < n || (errno && ! (is_ENOTSUP (errno) || errno == ENOSYS)))
+        {
+          if (set_acl (dst_name, dest_desc, x->mode) != 0)
+            return_val = false;
+        }
     }


BTW I'm surprised this wasn't reported previously for CIFS,
so I due to this bug and https://debbugs.gnu.org/65599
I suspect a recentish change in CIFS wrt EACCES.

cheers,
Pádraig




Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Sat, 13 Apr 2024 19:30:04 GMT) Full text and rfc822 format available.

Message #14 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Bruno Haible <bruno <at> clisp.org>
To: 70214 <at> debbugs.gnu.org, Pádraig Brady <P <at> draigbrady.com>
Cc: "linux-cifs <at> vger.kernel.org" <linux-cifs <at> vger.kernel.org>,
 Andreas Gruenbacher <andreas.gruenbacher <at> gmail.com>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Sat, 13 Apr 2024 21:29:08 +0200
Hi Pádraig,

I wrote:
> > 5) The same thing with 'cp -a' succeeds:
> > 
> > $ build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
> > 0
> > $ build-sparc64-no-acl/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
> > 0

You wrote:
> The psuedo code that install(1) uses is:
> 
> copy_reg()
>    if (x->set_mode) /* install */
>      set_acl(dest, x->mode /* 600 */)
>        ctx->acl = acl_from_mode ( /* 600 */)
>        acl_set_fd (ctx->acl) /* fails EACCES */
>        if (! acls_set)
>           must_chmod = true;
>        if (must_chmod)
>          saved_errno = EACCES;
>          chmod (ctx->mode /* 600 */)
>          if (save_errno)
>            return -1;

And, for comparison, what is the pseudo-code that 'cp -a' uses?
I would guess that there must be a relevant difference between both.

Bruno







Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Sat, 13 Apr 2024 22:45:02 GMT) Full text and rfc822 format available.

Message #17 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Bruno Haible <bruno <at> clisp.org>, 70214 <at> debbugs.gnu.org
Cc: "linux-cifs <at> vger.kernel.org" <linux-cifs <at> vger.kernel.org>,
 Andreas Gruenbacher <andreas.gruenbacher <at> gmail.com>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Sat, 13 Apr 2024 23:43:40 +0100
On 13/04/2024 20:29, Bruno Haible wrote:
> Hi Pádraig,
> 
> I wrote:
>>> 5) The same thing with 'cp -a' succeeds:
>>>
>>> $ build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
>>> 0
>>> $ build-sparc64-no-acl/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
>>> 0
> 
> You wrote:
>> The psuedo code that install(1) uses is:
>>
>> copy_reg()
>>     if (x->set_mode) /* install */
>>       set_acl(dest, x->mode /* 600 */)
>>         ctx->acl = acl_from_mode ( /* 600 */)
>>         acl_set_fd (ctx->acl) /* fails EACCES */
>>         if (! acls_set)
>>            must_chmod = true;
>>         if (must_chmod)
>>           saved_errno = EACCES;
>>           chmod (ctx->mode /* 600 */)
>>           if (save_errno)
>>             return -1;
> 
> And, for comparison, what is the pseudo-code that 'cp -a' uses?
> I would guess that there must be a relevant difference between both.

The cp pseudo code is:

copy_reg()
  if (preserve_xattr)
    copy_attr()
      ret = attr_copy_fd()
      if (ret == -1 && require_preserve_xattr /*false*/)
        return failure;
  if (preserve_mode)
    copy_acl()
      qcopy_acl()
        #if USE_XATTR /* true */
          fchmod() /* chmod before setting ACLs as doing after may reset */
          return attr_copy_fd() /* successful if no ACLs in source */
        #endif

If however you add ACLs in the source, you induce a similar failure:

$ setfacl -m u:nobody:r /var/tmp/foo3942
$ src/cp -a /var/tmp/foo3942 foo3942; echo $?
src/cp: preserving permissions for ‘foo3942’: Permission denied
1

The corresponding strace is:

fchmod(4, 0100640)                      = 0
flistxattr(3, NULL, 0)                  = 24
flistxattr(3, "system.posix_acl_access\0", 24) = 24
fgetxattr(3, "system.posix_acl_access", NULL, 0) = 44
fgetxattr(3, "system.posix_acl_access", "\2\0...\4", 44) = 44
fsetxattr(4, "system.posix_acl_access", "\2\0...\4", 44, 0) = -1 EACCES (Permission denied)

BTW I was wondering about the need for install(1) to set_acl() at all,
rather than just using chmod.
The following comment in lib/set-permissions.c may be pertinent:

/* If we can't set an acl which we expect to be able to set, try setting
   the permissions to ctx->mode. Due to possible inherited permissions,
   we cannot simply chmod */

BTW this is all under kernel version:

$ uname -r
6.8.5-gentoo-sparc64

With these cifs options:

$ mount | grep cifs
//syslog.matoro.tk/guest-pixelbeat on /media/guest-homedirs/pixelbeat type cifs
(rw,nosuid,relatime,vers=1.0,cache=strict,username=nobody,uid=30017,forceuid,
gid=30017,forcegid,addr=fd05:0000:0000:0000:0000:0000:0000:0001,
soft,unix,posixpaths,serverino,mapposix,acl,
rsize=1048576,wsize=65536,bsize=1048576,retrans=1,echo_interval=60,actimeo=1,closetimeo=1)

cheers,
Pádraig




Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Mon, 15 Apr 2024 14:38:02 GMT) Full text and rfc822 format available.

Message #20 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Andreas Grünbacher <andreas.gruenbacher <at> gmail.com>
To: Pádraig Brady <P <at> draigbrady.com>
Cc: 70214 <at> debbugs.gnu.org, Bruno Haible <bruno <at> clisp.org>,
 "linux-cifs <at> vger.kernel.org" <linux-cifs <at> vger.kernel.org>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Mon, 15 Apr 2024 16:37:25 +0200
Hello,

Am So., 14. Apr. 2024 um 00:43 Uhr schrieb Pádraig Brady <P <at> draigbrady.com>:
> On 13/04/2024 20:29, Bruno Haible wrote:
> > Hi Pádraig,
> >
> > I wrote:
> >>> 5) The same thing with 'cp -a' succeeds:
> >>>
> >>> $ build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
> >>> 0
> >>> $ build-sparc64-no-acl/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
> >>> 0
> >
> > You wrote:
> >> The psuedo code that install(1) uses is:
> >>
> >> copy_reg()
> >>     if (x->set_mode) /* install */
> >>       set_acl(dest, x->mode /* 600 */)
> >>         ctx->acl = acl_from_mode ( /* 600 */)
> >>         acl_set_fd (ctx->acl) /* fails EACCES */
> >>         if (! acls_set)
> >>            must_chmod = true;
> >>         if (must_chmod)
> >>           saved_errno = EACCES;
> >>           chmod (ctx->mode /* 600 */)
> >>           if (save_errno)
> >>             return -1;
> >
> > And, for comparison, what is the pseudo-code that 'cp -a' uses?
> > I would guess that there must be a relevant difference between both.
>
> The cp pseudo code is:
>
> copy_reg()
>    if (preserve_xattr)
>      copy_attr()
>        ret = attr_copy_fd()
>        if (ret == -1 && require_preserve_xattr /*false*/)
>          return failure;
>    if (preserve_mode)
>      copy_acl()
>        qcopy_acl()
>          #if USE_XATTR /* true */
>            fchmod() /* chmod before setting ACLs as doing after may reset */
>            return attr_copy_fd() /* successful if no ACLs in source */
>          #endif
>
> If however you add ACLs in the source, you induce a similar failure:
>
> $ setfacl -m u:nobody:r /var/tmp/foo3942
> $ src/cp -a /var/tmp/foo3942 foo3942; echo $?
> src/cp: preserving permissions for ‘foo3942’: Permission denied
> 1
>
> The corresponding strace is:
>
> fchmod(4, 0100640)                      = 0
> flistxattr(3, NULL, 0)                  = 24
> flistxattr(3, "system.posix_acl_access\0", 24) = 24
> fgetxattr(3, "system.posix_acl_access", NULL, 0) = 44
> fgetxattr(3, "system.posix_acl_access", "\2\0...\4", 44) = 44
> fsetxattr(4, "system.posix_acl_access", "\2\0...\4", 44, 0) = -1 EACCES (Permission denied)

Why does CIFS think EACCES is an appropriate error to return here? The
fchmod() succeeds, so changing the file permissions via fsetxattr()
should really succeed as well.

Thanks,
Andreas




Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Mon, 15 Apr 2024 15:29:05 GMT) Full text and rfc822 format available.

Message #23 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Andreas Grünbacher <andreas.gruenbacher <at> gmail.com>
Cc: 70214 <at> debbugs.gnu.org, Bruno Haible <bruno <at> clisp.org>,
 "linux-cifs <at> vger.kernel.org" <linux-cifs <at> vger.kernel.org>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Mon, 15 Apr 2024 16:28:23 +0100
On 15/04/2024 15:37, Andreas Grünbacher wrote:
> Hello,
> 
> Am So., 14. Apr. 2024 um 00:43 Uhr schrieb Pádraig Brady <P <at> draigbrady.com>:
>> On 13/04/2024 20:29, Bruno Haible wrote:
>>> Hi Pádraig,
>>>
>>> I wrote:
>>>>> 5) The same thing with 'cp -a' succeeds:
>>>>>
>>>>> $ build-sparc64/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
>>>>> 0
>>>>> $ build-sparc64-no-acl/src/cp -a /var/tmp/foo3941 $HOME/foo3941; echo $?
>>>>> 0
>>>
>>> You wrote:
>>>> The psuedo code that install(1) uses is:
>>>>
>>>> copy_reg()
>>>>      if (x->set_mode) /* install */
>>>>        set_acl(dest, x->mode /* 600 */)
>>>>          ctx->acl = acl_from_mode ( /* 600 */)
>>>>          acl_set_fd (ctx->acl) /* fails EACCES */
>>>>          if (! acls_set)
>>>>             must_chmod = true;
>>>>          if (must_chmod)
>>>>            saved_errno = EACCES;
>>>>            chmod (ctx->mode /* 600 */)
>>>>            if (save_errno)
>>>>              return -1;
>>>
>>> And, for comparison, what is the pseudo-code that 'cp -a' uses?
>>> I would guess that there must be a relevant difference between both.
>>
>> The cp pseudo code is:
>>
>> copy_reg()
>>     if (preserve_xattr)
>>       copy_attr()
>>         ret = attr_copy_fd()
>>         if (ret == -1 && require_preserve_xattr /*false*/)
>>           return failure;
>>     if (preserve_mode)
>>       copy_acl()
>>         qcopy_acl()
>>           #if USE_XATTR /* true */
>>             fchmod() /* chmod before setting ACLs as doing after may reset */
>>             return attr_copy_fd() /* successful if no ACLs in source */
>>           #endif
>>
>> If however you add ACLs in the source, you induce a similar failure:
>>
>> $ setfacl -m u:nobody:r /var/tmp/foo3942
>> $ src/cp -a /var/tmp/foo3942 foo3942; echo $?
>> src/cp: preserving permissions for ‘foo3942’: Permission denied
>> 1
>>
>> The corresponding strace is:
>>
>> fchmod(4, 0100640)                      = 0
>> flistxattr(3, NULL, 0)                  = 24
>> flistxattr(3, "system.posix_acl_access\0", 24) = 24
>> fgetxattr(3, "system.posix_acl_access", NULL, 0) = 44
>> fgetxattr(3, "system.posix_acl_access", "\2\0...\4", 44) = 44
>> fsetxattr(4, "system.posix_acl_access", "\2\0...\4", 44, 0) = -1 EACCES (Permission denied)
> 
> Why does CIFS think EACCES is an appropriate error to return here? The
> fchmod() succeeds, so changing the file permissions via fsetxattr()
> should really succeed as well.

Right, it seems like a CIFS bug (already CC'd)
Even if it returned ENOTSUP (like getxattr(...posix...) does) it would be ok as we handle that.
It would be good to avoid it though.

You confirmed privately to me that the set_acl() is to clear any default ACLs
that may have been added to the newly created file, so the posted solution in
https://bugs.gnu.org/70214#11 that only does the set_acl() iff file_has_acl()
should avoid the CIFS issue.  It would be a bit less efficient if there were
ACLs, but a bit more efficient in the common case where there weren't ACLs.

cheers,
Pádraig




Information forwarded to bug-coreutils <at> gnu.org:
bug#70214; Package coreutils. (Thu, 09 May 2024 12:18:02 GMT) Full text and rfc822 format available.

Message #26 received at 70214 <at> debbugs.gnu.org (full text, mbox):

From: Pádraig Brady <P <at> draigBrady.com>
To: Bruno Haible <bruno <at> clisp.org>, 70214 <at> debbugs.gnu.org
Cc: "linux-cifs <at> vger.kernel.org" <linux-cifs <at> vger.kernel.org>,
 Andreas Gruenbacher <andreas.gruenbacher <at> gmail.com>
Subject: Re: bug#70214: 'install' fails to copy regular file to autofs/cifs,
 due to ACL or xattr handling
Date: Thu, 9 May 2024 13:16:30 +0100
On 13/04/2024 18:42, Pádraig Brady wrote:
> On 13/04/2024 17:39, Pádraig Brady wrote:
>> install(1) defaults to mode 600 for new files, and uses set_acl() with that
>> (since 2007 https://github.com/coreutils/coreutils/commit/f634e8844 )
>> The psuedo code that install(1) uses is:
>>
>> copy_reg()
>>      if (x->set_mode) /* install */
>>        set_acl(dest, x->mode /* 600 */)
>>          ctx->acl = acl_from_mode ( /* 600 */)
>>          acl_set_fd (ctx->acl) /* fails EACCES */
>>          if (! acls_set)
>>             must_chmod = true;
>>          if (must_chmod)
>>            saved_errno = EACCES;
>>            chmod (ctx->mode /* 600 */)
>>            if (save_errno)
>>              return -1;
>>
>> This issue only only seems to be on CIFS.
>> I'm seeing lot of weird behavior with ACLs there:
>>
>>      acl_set_fd (acl_from_mode (600)) -> EACCES
>>      acl_set_fd (acl_from_mode (755)) -> EINVAL
>>      getxattr ("system.posix_acl_access") -> EOPNOTSUPP
>>
>> Note we ignore EINVAL and EOPNOTSUPP errors in set_acl(),
>> and it's just the EACCES that's problematic.
>> Note this is quite similar to https://debbugs.gnu.org/65599
>> where Paul also noticed EACCES with fsetxattr() (and others) on CIFS.
>>
>> The attached is a potential solution which I tested as working
>> on the same matoro system that Bruno used.
>>
>> I think I'll apply that after thinking a bit more about it.
> 
> Actually that probably isn't appropriate,
> as fsetxattr() can validly return EACESS
> even though not documented in the man page at least.
> The following demonstrates that:
> .
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <sys/xattr.h>
> #include <fcntl.h>
> #include <attr/libattr.h>
> #include <stdio.h>
> #include <unistd.h>
> 
> int main(void)
> {
>       int wfd;
>       /* Note S_IWUSR is not set.  */
>       if ((wfd=open("writable", O_CREAT|O_WRONLY|O_EXCL, S_IRUSR)) == -1)
>           fprintf(stderr, "open('writable') error [%m]\n");
>       if (write(wfd, "data", 1) == -1)
>           fprintf(stderr, "write() error [%m]\n");
>       if (fsetxattr(wfd, "user.test", "test", 4, 0) == -1)
>           fprintf(stderr, "fsetxattr() error [%m]\n");
> }
> 
> Another solution might be to file_has_acl() in copy.c
> and skip if "not supported" or nothing is returned.
> The following would do that, but I'm not sure about this
> (as I'm not sure about ACLs in general TBH).
> Note listxattr() returns 0 on CIFS here,
> while getxattr ("system.posix_acl_access") returns EOPNOTSUPP,
> and file_has_acl() uses listxattr() first.
> 
> diff --git a/src/copy.c b/src/copy.c
> index d584a27eb..2145d89d5 100644
> --- a/src/copy.c
> +++ b/src/copy.c
> @@ -1673,8 +1673,13 @@ set_dest_mode:
>        }
>      else if (x->set_mode)
>        {
> -      if (set_acl (dst_name, dest_desc, x->mode) != 0)
> -        return_val = false;
> +      errno = 0;
> +      int n = file_has_acl (dst_name, &sb);
> +      if (0 < n || (errno && ! (is_ENOTSUP (errno) || errno == ENOSYS)))
> +        {
> +          if (set_acl (dst_name, dest_desc, x->mode) != 0)
> +            return_val = false;
> +        }
>        }
> 
> 
> BTW I'm surprised this wasn't reported previously for CIFS,
> so I due to this bug and https://debbugs.gnu.org/65599
> I suspect a recentish change in CIFS wrt EACCES.

Thinking more about this, the solution presented above wouldn't work,
and I think this needs to be addressed in CIFS.
I.e. we may still need to reset the mode even if the file has no ACLs,
as generally only dirs get default ACLs copied, as demonstrated below:

$ mkdir acl$ setfacl -d -m o::rw acl
$ getfacl acl
# file: acl
# owner: padraig
# group: padraig
user::rwx
group::r-x
other::r-x
default:user::rwx
default:group::r-x
default:other::rw-

$ touch acl/file
$ ls -l acl/file
-rw-r--rw-. 1 padraig padraig 0 May  9 13:11 acl/file
$ getfacl acl/file
# file: acl/file
# owner: padraig
# group: padraig
user::rw-
group::r--
other::rw-

cheers,
Pádraig.




This bug report was last modified 179 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.