In <https://bugs.gnu.org/65599> Bruno Haible reports that GNU cp and mv
issue bogus diagnostics when copying from ext4 to cifs. We tracked this
down to fchownat failing with EACCES when it should fail with EPERM.
To reproduce the fchownat bug, compile and run the attached program in a
cifs subdirectory on Linux kernel 5.15. The program will issue
EACCES-related diagnostics like this:
fchownat: Permission denied
whereas it should issue EPERM-related diagnostics like this:
fchownat: Operation not permitted
EACCES is wrong because POSIX says that EACCES means that search
permission is denied on a component of the path prefix (a problem that
does not apply here), whereas EPERM means the calling process does not
have appropriate privileges to change ownership (the problem that does
apply here). See
<https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html>.
We discovered similar problems with fchown, chown, lchown, fchmodat,
lchmod, and chmod on CIFS. I assume fsetxattr etc. are also affected by
the bug.
Although most programs don't care about the difference between EACCES
and EPERM, GNU coreutils does care and I expect other programs will too,
and it'd be nice if CIFS were fixed to not generate these false alarms.#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int
main (int argc, char **argv)
{
char const *file = argv[1] ? argv[1] : ".";
struct stat st;
if (lstat (file, &st) < 0)
return perror ("lstat"), 1;
int status = 0;
if (lchown (file, st.st_uid, st.st_gid) < 0)
perror ("lchown"), status = 1;
if (fchownat (AT_FDCWD, file, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0)
perror ("fchownat"), status = 1;
if (!S_ISLNK (st.st_mode))
{
if (chown (file, st.st_uid, st.st_gid) < 0)
perror ("chown"), status = 1;
int fd = openat (AT_FDCWD, file, O_RDONLY | O_NOFOLLOW);
if (fd < 0)
perror ("openat"), status = 1;
else if (fchown (fd, st.st_uid, st.st_gid) < 0)
perror ("fchown"), status = 1;
}
return status;
}