GNU bug report logs - #79433
9.7: cp(1): lchownat(2/3) fallback bogus?

Previous Next

Package: coreutils;

Reported by: Steffen Nurpmeso <steffen <at> sdaoden.eu>

Date: Thu, 11 Sep 2025 11:51:02 UTC

Severity: normal

Found in version 9.7

Full log


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

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Steffen Nurpmeso <steffen <at> sdaoden.eu>
Cc: 79433 <at> debbugs.gnu.org
Subject: Re: bug#79433: 9.7: cp(1): lchownat(2/3) fallback bogus?
Date: Fri, 12 Sep 2025 08:17:26 -0700
On 2025-09-11 04:49, Steffen Nurpmeso wrote:
>              if (lchownat (dst_dirfd, relname, p->st.st_uid, p->st.st_gid)
>                  != 0)
>    ...
>                      error (0, errno, _("failed to preserve ownership for %s"),
>                             quoteaf (dst_name));
> 
>    Here there is no lchownat(3/2),

No kernel or C library has lchownat; that's a convenience function 
defined in coreutils/lib/openat.h (taken from Gnulib), equivalent to 
fchownat (..., AT_SYMLINK_NOFOLLOW).

What does "strace cp -a xb xc" say? I see this:

  ...
  geteuid32()                             = 1000
  openat(AT_FDCWD, "xb", O_RDONLY|O_LARGEFILE|O_PATH|O_DIRECTORY) = -1 
ENOENT (No such file or directory)
  statx(AT_FDCWD, "xa", 
AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, 
STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, 
stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=4, ...}) = 0
  lgetxattr("xa", "security.selinux", 
"unconfined_u:object_r:user_tmp_t"..., 255) = 36
  futex(0xf7f7ccc4, FUTEX_WAKE_PRIVATE, 2147483647) = 0
  openat(AT_FDCWD, "/proc/thread-self/attr/fscreate", 
O_RDWR|O_LARGEFILE|O_CLOEXEC) = 3
  write(3, "unconfined_u:object_r:user_tmp_t"..., 36) = 36
  close(3)                                = 0
  openat(AT_FDCWD, "xa", O_RDONLY|O_LARGEFILE|O_NOFOLLOW) = 3
  statx(3, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, 
STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, 
stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=4, ...}) = 0
  openat(AT_FDCWD, "xb", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
  ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EOPNOTSUPP (Operation 
not supported)
  statx(4, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, 
STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, 
stx_attributes=0, stx_mode=S_IFREG|0600, stx_size=0, ...}) = 0
  fadvise64_64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
  uname({sysname="Linux", nodename="penguin.cs.ucla.edu", ...}) = 0
  copy_file_range(3, NULL, 4, NULL, 1073741824, 0) = 4
  copy_file_range(3, NULL, 4, NULL, 1073741824, 0) = 0
  utimensat(4, NULL, [{tv_sec=1757689789, tv_nsec=946283700} /* 
2025-09-12T08:09:49.946283700-0700 */, {tv_sec=1757689789, 
tv_nsec=946283700} /* 2025-09-12T08:09:49.946283700-0700 */], 0) = 0
  flistxattr(3, NULL, 0)                  = 17
  flistxattr(3, "security.selinux\0", 17) = 17
  fchmod(4, 0100664)                      = 0
  flistxattr(3, NULL, 0)                  = 17
  flistxattr(3, "security.selinux\0", 17) = 17
  openat(AT_FDCWD, "/etc/xattr.conf", O_RDONLY|O_LARGEFILE) = 5
  statx(5, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, 
STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, 
stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=817, ...}) = 0
  read(5, "# /etc/xattr.conf\n#\n# Format:\n# "..., 4096) = 817
  read(5, "", 4096)                       = 0
  close(5)                                = 0
  openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.cache", 
O_RDONLY|O_CLOEXEC) = 5
  statx(5, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, 
STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, 
stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=27010, ...}) = 0
  mmap2(NULL, 27010, PROT_READ, MAP_SHARED, 5, 0) = 0xf7f99000
  close(5)                                = 0
  futex(0xf7f2eee8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
  close(4)                                = 0
  close(3)                                = 0
  ...

What happens if you use fchownat directly? Something like the following:

  #include <errno.h>
  #include <fcntl.h>
  #include <grp.h>
  #include <pwd.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>

  int
  main (void)
  {
    char const *user = getenv ("LOGNAME");
    struct passwd const *pwd = getpwnam (user);
    struct group const *grp = getgrnam (user);
    int x = fchownat (AT_FDCWD, "xb",
		      pwd->pw_uid, grp->gr_gid,
		      AT_SYMLINK_NOFOLLOW);
    if (x < 0)
      perror ("fchownat");
    int y = lchown ("xb", pwd->pw_uid, grp->gr_gid);
    if (y < 0)
      perror ("lchown");
  }

I see this:

  ...
  fchownat(AT_FDCWD, "xb", 1000, 1000, AT_SYMLINK_NOFOLLOW) = 0
  lchown("xb", 1000, 1000)                = 0
  ...




This bug report was last modified 1 day ago.

Previous Next


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