wrong errno for chown etc. privilege failures in Linux CIFS client

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
}

[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux