From: Zbigniew Jędrzejewski-Szmek Date: Wed, 27 May 2026 16:28:21 +0000 (+0200) Subject: repart: when copying files into vfat or similar, do not set ownership X-Git-Tag: v261-rc3~8^2 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=fe2b35bd587864186b6a2a814711ace062ebc2ec;p=thirdparty%2Fsystemd.git repart: when copying files into vfat or similar, do not set ownership $ mkdir /var/tmp/files $ touch /var/tmp/files/a $ mkdir /var/tmp/conf $ cat >>/var/tmp/conf/esp.conf [Partition] Type=esp Format=vfat CopyFiles=/var/tmp/files:/ $ truncate /var/tmp/disk -s 300M $ sudo systemd-repart --dry-run=no --empty=require --definitions=/var/tmp/conf /var/tmp/disk ... Populating vfat filesystem. Failed to copy '...' to '/run/systemd/mount-root/': Operation not permitted (sd-copy) failed with exit status 1. The issue is that if there's a file owned by non-root and we try to copy it into a newly-created DOS partition, fchown fails: fchown(11, 1000, 1000) = -1 EPERM (Operation not permitted) We want to ignore file ownership in such cases, so pass our own UID/GID to copy_tree_at(), which turns the fchown into a noop and let's the operation pass through. Fixes #38863. --- diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 7c043bb54a4..72d2a01af55 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -485,6 +485,22 @@ bool fstype_can_fmask_dmask(const char *fstype) { return streq(fstype, "vfat") || (mount_option_supported(fstype, "fmask", "0177") > 0 && mount_option_supported(fstype, "dmask", "0077") > 0); } +bool fstype_can_ownership(const char *fstype) { + /* File systems which are not known to not support uid/gid ownership. + * For some types, this can be a bit murky. So just exclude the ones that for sure + * don't support with the current implementations in Linux. */ + + return !STR_IN_SET(ASSERT_PTR(fstype), + "adfs", + "exfat", + "fat", + "hfs", + "hpfs", + "msdos", + "ntfs", + "vfat"); +} + bool fstype_can_uid_gid(const char *fstype) { /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and * directories, current and future. Note that this does *not* ask the kernel via diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index 8cc966751c9..dd27f36c2c3 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -66,6 +66,7 @@ bool fstype_is_api_vfs(const char *fstype); bool fstype_is_blockdev_backed(const char *fstype); bool fstype_is_ro(const char *fsype); bool fstype_can_discard(const char *fstype); +bool fstype_can_ownership(const char *fstype); bool fstype_can_uid_gid(const char *fstype); bool fstype_can_fmask_dmask(const char *fstype); diff --git a/src/repart/repart.c b/src/repart/repart.c index 6b0422dd6cc..879a90e1836 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -6712,6 +6712,8 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { return log_oom(); } + bool copy_ownership = fstype_can_ownership(p->format); + /* copy_tree_at() automatically copies the permissions of source directories to target directories if * it created them. However, the root directory is created by us, so we have to manually take care * that it is initialized. We use the first source directory targeting "/" as the metadata source for @@ -6733,12 +6735,16 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_copy_source), line->source); (void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS); - (void) copy_access(sfd, rfd); + if (copy_ownership) + (void) copy_access(sfd, rfd); (void) copy_times(sfd, rfd, 0); break; } + uid_t uid = copy_ownership ? UID_INVALID : getuid(); + gid_t gid = copy_ownership ? GID_INVALID : getgid(); + FOREACH_ARRAY(line, copy_files, n_copy_files) { _cleanup_hashmap_free_ Hashmap *denylist = NULL; _cleanup_hashmap_free_ Hashmap *subvolumes_by_source_inode = NULL; @@ -6795,14 +6801,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { r = copy_tree_at( sfd, ".", pfd, fn, - UID_INVALID, GID_INVALID, + uid, gid, line->flags, denylist, subvolumes_by_source_inode); } else r = copy_tree_at( sfd, ".", tfd, ".", - UID_INVALID, GID_INVALID, + uid, gid, line->flags, denylist, subvolumes_by_source_inode); if (r < 0) @@ -6849,7 +6855,8 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", line->source, strempty(arg_copy_source), line->target); (void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS); - (void) copy_access(sfd, tfd); + if (copy_ownership) + (void) copy_access(sfd, tfd); (void) copy_times(sfd, tfd, 0); if (ts != USEC_INFINITY) {