Subject: bug#65599: mv and cp give a pointless warning when moving/copying a directory

From: Bruno Haible <[email-masked]>
Date: Sat, 02 Sep 2023 13:18:17 +0200

Paul Eggert wrote:
> Yes please. If chmod also does not work coreutils might need more 
> workarounds.
> 
> Just to be clear; please try it on a file where chmod should fail 
> because you don't own the file and you're not root. chmod should fail 
> with EPERM in this case; it it fails with EACCES it's buggy. Thanks.

chmod fails with EACCES, hence per POSIX
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html>
it's buggy.

See: On the server I have 2 files and 2 directories:

# ls -ld dir1 dir1/file1 dir2 dir2/file2
drwxrwxr-x    2 bruno    everyone      4096 Sep  2 12:58 dir1/
-rw-rw-r--    1 bruno    everyone         4 Sep  2 00:52 dir1/file1
drwxrwxrwx    2 admin    administ      4096 Sep  2 00:54 dir2/
-rw-rw-r--    1 admin    administ         4 Sep  2 00:54 dir2/file2

The user that exports the mount point is 'bruno'. But since dir2
and dir2/file2 are not owned by 'bruno', an error will (and should)
occur when these two are manipulated through the CIFS client.

chown fails; that's the problem you already committed a workaround for:

$ ./fchownat-test /media/nas/bruno/dir1/file1
lchown: Permission denied
fchownat: Permission denied
chown: Permission denied
fchown: Permission denied
$ ./fchownat-test /media/nas/bruno/dir1
lchown: Permission denied
fchownat: Permission denied
chown: Permission denied
fchown: Permission denied
$ ./fchownat-test /media/nas/bruno/dir2/file2
lchown: Permission denied
fchownat: Permission denied
chown: Permission denied
fchown: Permission denied
$ ./fchownat-test /media/nas/bruno/dir2
lchown: Permission denied
fchownat: Permission denied
chown: Permission denied
fchown: Permission denied

chmod also fails with EACCES instead of EPERM:

$ ./fchmodat-test /media/nas/bruno/dir1/file1
$ ./fchmodat-test /media/nas/bruno/dir1
$ ./fchmodat-test /media/nas/bruno/dir2/file2
lchmod: Permission denied
fchmodat: Permission denied
chmod: Permission denied
fchmod: Permission denied
$ ./fchmodat-test /media/nas/bruno/dir2
lchmod: Permission denied
fchmodat: Permission denied
chmod: Permission denied
fchmod: Permission denied

The same thing with strace:

$ strace ./fchmodat-test /media/nas/bruno/dir2/file2
...
newfstatat(AT_FDCWD, "/media/nas/bruno/dir2/file2", {st_mode=S_IFREG|0664, st_size=4, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "/media/nas/bruno/dir2/file2", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 3
newfstatat(3, "", {st_mode=S_IFREG|0664, st_size=4, ...}, AT_EMPTY_PATH) = 0
chmod("/proc/self/fd/3", 0664)          = -1 EACCES (Permission denied)
close(3)                                = 0
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
getrandom("\x68\x8f\xbd\x08\xf8\x4b\xa9\xa5", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x5563c5d01000
brk(0x5563c5d22000)                     = 0x5563c5d22000
newfstatat(3, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(3, "lchmod: Permission denied\n", 26lchmod: Permission denied
) = 26
close(3)                                = 0
openat(AT_FDCWD, "/media/nas/bruno/dir2/file2", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 3
newfstatat(3, "", {st_mode=S_IFREG|0664, st_size=4, ...}, AT_EMPTY_PATH) = 0
chmod("/proc/self/fd/3", 0664)          = -1 EACCES (Permission denied)
close(3)                                = 0
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
newfstatat(3, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(3, "fchmodat: Permission denied\n", 28fchmodat: Permission denied
) = 28
close(3)                                = 0
chmod("/media/nas/bruno/dir2/file2", 0664) = -1 EACCES (Permission denied)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
newfstatat(3, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(3, "chmod: Permission denied\n", 25chmod: Permission denied
) = 25
close(3)                                = 0
openat(AT_FDCWD, "/media/nas/bruno/dir2/file2", O_RDONLY|O_NOFOLLOW) = 3
fchmod(3, 0664)                         = -1 EACCES (Permission denied)
dup(2)                                  = 4
fcntl(4, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
newfstatat(4, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(4, "fchmod: Permission denied\n", 26fchmod: Permission denied
) = 26
close(4)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

$ strace ./fchmodat-test /media/nas/bruno/dir2
...
newfstatat(AT_FDCWD, "/media/nas/bruno/dir2", {st_mode=S_IFDIR|0777, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
openat(AT_FDCWD, "/media/nas/bruno/dir2", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0777, st_size=0, ...}, AT_EMPTY_PATH) = 0
chmod("/proc/self/fd/3", 0777)          = -1 EACCES (Permission denied)
close(3)                                = 0
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
getrandom("\xf3\x8f\xf7\xff\xb4\x91\xcf\xcf", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x55b7056a1000
brk(0x55b7056c2000)                     = 0x55b7056c2000
newfstatat(3, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(3, "lchmod: Permission denied\n", 26lchmod: Permission denied
) = 26
close(3)                                = 0
openat(AT_FDCWD, "/media/nas/bruno/dir2", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 3
newfstatat(3, "", {st_mode=S_IFDIR|0777, st_size=0, ...}, AT_EMPTY_PATH) = 0
chmod("/proc/self/fd/3", 0777)          = -1 EACCES (Permission denied)
close(3)                                = 0
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
newfstatat(3, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(3, "fchmodat: Permission denied\n", 28fchmodat: Permission denied
) = 28
close(3)                                = 0
chmod("/media/nas/bruno/dir2", 0777)    = -1 EACCES (Permission denied)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
newfstatat(3, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(3, "chmod: Permission denied\n", 25chmod: Permission denied
) = 25
close(3)                                = 0
openat(AT_FDCWD, "/media/nas/bruno/dir2", O_RDONLY|O_NOFOLLOW) = 3
fchmod(3, 0777)                         = -1 EACCES (Permission denied)
dup(2)                                  = 4
fcntl(4, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
newfstatat(4, "", {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x5), ...}, AT_EMPTY_PATH) = 0
write(4, "fchmod: Permission denied\n", 26fchmod: Permission denied
) = 26
close(4)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

Bruno