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
  ...



Reply via email to