From a4d2461c46f40c9ae5002a2aea35b35ccb60ef9c Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 13 Feb 2023 21:49:38 +0100 Subject: [PATCH 1/7] xattr-util: Add xsetxattr() Like getxattr_malloc() but for setxattr() and friends. --- src/basic/xattr-util.c | 74 ++++++++++++++++++++++++++++++++++++++++++ src/basic/xattr-util.h | 2 ++ 2 files changed, 76 insertions(+) diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 5b6131b56a..746e9f369e 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -293,3 +293,77 @@ int listxattr_at_malloc( l = (size_t) n; } } + +int xsetxattr(int fd, + const char *path, + const char *name, + const char *value, + size_t size, + int flags) { + + _cleanup_close_ int opened_fd = -EBADF; + bool by_procfs = false; + int r; + + assert(fd >= 0 || fd == AT_FDCWD); + assert(name); + assert(value); + assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0); + + /* So, this is a single function that does what setxattr()/lsetxattr()/fsetxattr() do, but in one go, + * and with additional bells and whistles. Specifically: + * + * 1. This works on O_PATH fds (which fsetxattr() does not) + * 2. Provides full openat()-style semantics, i.e. by-fd, by-path and combination thereof + * 3. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is NULL. + */ + + if (!path) /* If path is NULL, imply AT_EMPTY_PATH. – But if it's "", don't — for safety reasons. */ + flags |= AT_EMPTY_PATH; + + if (size == SIZE_MAX) + size = strlen(value); + + if (isempty(path)) { + if (!FLAGS_SET(flags, AT_EMPTY_PATH)) + return -EINVAL; + + if (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */ + path = "."; + else + path = NULL; + + } else if (fd != AT_FDCWD) { + + /* If both have been specified, then we go via O_PATH */ + opened_fd = openat(fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW)); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + path = NULL; + by_procfs = true; /* fsetxattr() is not going to work, go via /proc/ link right-away */ + } + + for (;;) { + if (path) + r = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, 0) + : lsetxattr(path, name, value, size, 0); + else + r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, 0) + : fsetxattr(fd, name, value, size, 0); + if (r < 0) { + if (errno == EBADF) { + if (by_procfs || path) + return -EBADF; + + by_procfs = true; /* Might be an O_PATH fd, try again via /proc/ link */ + continue; + } + + return -errno; + } + + return 0; + } +} diff --git a/src/basic/xattr-util.h b/src/basic/xattr-util.h index 0eb745a7a3..649a842fe2 100644 --- a/src/basic/xattr-util.h +++ b/src/basic/xattr-util.h @@ -36,3 +36,5 @@ static inline int llistxattr_malloc(const char *path, char **ret) { static inline int flistxattr_malloc(int fd, char **ret) { return listxattr_at_malloc(fd, NULL, AT_EMPTY_PATH, ret); } + +int xsetxattr(int fd, const char *path, const char *name, const char *value, size_t size, int flags); From c17cfe6ef7bc1ac4501b5f6e7d4f4efc08aef439 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 13 Feb 2023 21:51:11 +0100 Subject: [PATCH 2/7] copy: Make copy_xattr() more generic Let's make copy_xattr() a little more generic in preparation for copying symlink xattrs. --- src/dissect/dissect.c | 4 ++-- src/import/export-raw.c | 2 +- src/import/import-raw.c | 2 +- src/import/pull-raw.c | 2 +- src/locale/localed-util.c | 2 +- src/partition/repart.c | 2 +- src/shared/copy.c | 16 ++++++++-------- src/shared/copy.h | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index f435932815..8d3d3712d7 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -1105,7 +1105,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { if (r < 0) return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target); - (void) copy_xattr(source_fd, target_fd, 0); + (void) copy_xattr(source_fd, NULL, target_fd, NULL, 0); (void) copy_access(source_fd, target_fd); (void) copy_times(source_fd, target_fd, 0); @@ -1182,7 +1182,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { if (r < 0) return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image); - (void) copy_xattr(source_fd, target_fd, 0); + (void) copy_xattr(source_fd, NULL, target_fd, NULL, 0); (void) copy_access(source_fd, target_fd); (void) copy_times(source_fd, target_fd, 0); diff --git a/src/import/export-raw.c b/src/import/export-raw.c index 7c61aef3b6..44181fd9b2 100644 --- a/src/import/export-raw.c +++ b/src/import/export-raw.c @@ -216,7 +216,7 @@ static int raw_export_process(RawExport *e) { finish: if (r >= 0) { (void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME); - (void) copy_xattr(e->input_fd, e->output_fd, 0); + (void) copy_xattr(e->input_fd, NULL, e->output_fd, NULL, 0); } if (e->on_finished) diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 5d4dedf66d..3765b514bb 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -234,7 +234,7 @@ static int raw_import_finish(RawImport *i) { if (S_ISREG(i->input_stat.st_mode)) { (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME); - (void) copy_xattr(i->input_fd, i->output_fd, 0); + (void) copy_xattr(i->input_fd, NULL, i->output_fd, NULL, 0); } } diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index a4a844bf07..8a152d0f6f 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -396,7 +396,7 @@ static int raw_pull_make_local_copy(RawPull *i) { return log_error_errno(r, "Failed to make writable copy of image: %m"); (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME); - (void) copy_xattr(i->raw_job->disk_fd, dfd, 0); + (void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0); dfd = safe_close(dfd); diff --git a/src/locale/localed-util.c b/src/locale/localed-util.c index 8f18ecd0d2..d6b6593bea 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -921,7 +921,7 @@ int locale_gen_enable_locale(const char *locale) { r = copy_access(fileno(fr), fileno(fw)); if (r < 0) return r; - r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS); + r = copy_xattr(fileno(fr), NULL, fileno(fw), NULL, COPY_ALL_XATTRS); if (r < 0) log_debug_errno(r, "Failed to copy all xattrs from old to new /etc/locale.gen file, ignoring: %m"); } diff --git a/src/partition/repart.c b/src/partition/repart.c index 6ba48b528e..43f25e7e59 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3840,7 +3840,7 @@ static int do_copy_files(Partition *p, const char *root, const Set *denylist) { if (r < 0) return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target); - (void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS); + (void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS); (void) copy_access(sfd, tfd); (void) copy_times(sfd, tfd, 0); } diff --git a/src/shared/copy.c b/src/shared/copy.c index 4eb4f9f765..3e624d8844 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -754,7 +754,7 @@ static int fd_copy_regular( r = -errno; (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); - (void) copy_xattr(fdf, fdt, copy_flags); + (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags); if (copy_flags & COPY_FSYNC) { if (fsync(fdt) < 0) { @@ -1058,7 +1058,7 @@ static int fd_copy_directory( if (fchmod(fdt, st->st_mode & 07777) < 0) r = -errno; - (void) copy_xattr(dirfd(d), fdt, copy_flags); + (void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags); (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); } @@ -1313,7 +1313,7 @@ int copy_file_fd_full( * mode/ownership of that device node...) */ if (S_ISREG(st.st_mode)) { (void) copy_times(fdf, fdt, copy_flags); - (void) copy_xattr(fdf, fdt, copy_flags); + (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags); } if (copy_flags & COPY_FSYNC_FULL) { @@ -1385,7 +1385,7 @@ int copy_file_full( goto fail; (void) copy_times(fdf, fdt, copy_flags); - (void) copy_xattr(fdf, fdt, copy_flags); + (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags); if (chattr_mask != 0) (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); @@ -1570,11 +1570,11 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht) { return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid); } -int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) { +int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags) { _cleanup_free_ char *names = NULL; int ret = 0, r; - r = flistxattr_malloc(fdf, &names); + r = listxattr_at_malloc(df, from, 0, &names); if (r < 0) return r; @@ -1584,13 +1584,13 @@ int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) { if (!FLAGS_SET(copy_flags, COPY_ALL_XATTRS) && !startswith(p, "user.")) continue; - r = fgetxattr_malloc(fdf, p, &value); + r = getxattr_at_malloc(df, from, p, 0, &value); if (r == -ENODATA) continue; /* gone by now */ if (r < 0) return r; - if (fsetxattr(fdt, p, value, r, 0) < 0) + if (xsetxattr(dt, to, p, value, r, 0) < 0) ret = -errno; } diff --git a/src/shared/copy.h b/src/shared/copy.h index 1eb6d1ce05..22151ff83b 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -77,4 +77,4 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht); static inline int copy_rights(int fdf, int fdt) { return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */ } -int copy_xattr(int fdf, int fdt, CopyFlags copy_flags); +int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags); From da486c30fe6ed781d5cb865633b937c4edd93857 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 13 Feb 2023 21:56:31 +0100 Subject: [PATCH 3/7] copy: Copy symlink xattrs Symlinks can have xattrs as well, let's make sure we copy those as well. --- src/shared/copy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/copy.c b/src/shared/copy.c index 3e624d8844..d15875739a 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -504,6 +504,7 @@ static int fd_copy_symlink( AT_SYMLINK_NOFOLLOW) < 0) r = -errno; + (void) copy_xattr(df, from, dt, to, copy_flags); (void) utimensat(dt, to, (struct timespec[]) { st->st_atim, st->st_mtim }, AT_SYMLINK_NOFOLLOW); return r; } From 1eb86ddde4f36165a99732b53cc97cef1acc3aa7 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 14 Feb 2023 11:17:32 +0100 Subject: [PATCH 4/7] repart: Create temporary root directory using var_tmp_dir() This allows users to override the directory used with environment variables. --- src/partition/repart.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 43f25e7e59..93dfb5fb3e 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3872,17 +3872,22 @@ static bool partition_needs_populate(Partition *p) { static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) { _cleanup_(rm_rf_physical_and_freep) char *root = NULL; - _cleanup_close_ int rfd = -EBADF; + const char *vt; int r; assert(ret); - rfd = mkdtemp_open("/var/tmp/repart-XXXXXX", 0, &root); - if (rfd < 0) - return log_error_errno(rfd, "Failed to create temporary directory: %m"); + r = var_tmp_dir(&vt); + if (r < 0) + return log_error_errno(r, "Could not determine temporary directory: %m"); - if (fchmod(rfd, 0755) < 0) - return log_error_errno(errno, "Failed to change mode of temporary directory: %m"); + r = tempfn_random_child(vt, "repart", &root); + if (r < 0) + return log_error_errno(r, "Failed to generate temporary directory: %m"); + + r = mkdir(root, 0755); + if (r < 0) + return log_error_errno(errno, "Failed to create temporary directory: %m"); r = do_copy_files(p, root, denylist); if (r < 0) From 3e45146021136fc6ce758e328c4958c5145c28d9 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 14 Feb 2023 11:19:43 +0100 Subject: [PATCH 5/7] repart: Initialize root directory metadata correctly Let's make sure we copy the root directory metadata from an appropriate source directory. --- src/partition/repart.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/partition/repart.c b/src/partition/repart.c index 93dfb5fb3e..20f571e4cb 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3752,6 +3752,31 @@ static int do_copy_files(Partition *p, const char *root, const Set *denylist) { assert(p); assert(root); + /* 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 + * the root directory. */ + STRV_FOREACH_PAIR(source, target, p->copy_files) { + _cleanup_close_ int rfd = -EBADF, sfd = -EBADF; + + if (!path_equal(*target, "/")) + continue; + + rfd = open(root, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (rfd < 0) + return rfd; + + sfd = chase_symlinks_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL); + if (sfd < 0) + return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source); + + (void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS); + (void) copy_access(sfd, rfd); + (void) copy_times(sfd, rfd, 0); + + break; + } + STRV_FOREACH_PAIR(source, target, p->copy_files) { _cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF; From ab3a46f837ad7b847b0a95dcea5480182adcc113 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 14 Feb 2023 15:09:32 +0100 Subject: [PATCH 6/7] mount-setup: Fix typo --- src/shared/mount-setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c index f1ee528232..1303b6c71b 100644 --- a/src/shared/mount-setup.c +++ b/src/shared/mount-setup.c @@ -529,7 +529,7 @@ int mount_setup(bool loaded_policy, bool leave_propagation) { after_relabel = now(CLOCK_MONOTONIC); - log_info("Relabelled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.", + log_info("Relabeled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.", n_extra > 0 ? ", additional files" : "", FORMAT_TIMESPAN(after_relabel - before_relabel, 0)); } From d5b3a0497f62eac2374ff9639394be51dd555c53 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 14 Feb 2023 15:09:54 +0100 Subject: [PATCH 7/7] repart: Remove outdated comment --- src/partition/repart.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index 20f571e4cb..272bf19431 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3813,10 +3813,6 @@ static int do_copy_files(Partition *p, const char *root, const Set *denylist) { if (pfd < 0) return log_error_errno(pfd, "Failed to open parent directory of target: %m"); - /* Make sure everything is owned by the user running repart so that - * make_filesystem() can map the user running repart to "root" in a user - * namespace to have the files owned by root in the final image. */ - r = copy_tree_at( sfd, ".", pfd, fn,