From: Moritz Sanft <58110325+msanft@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:03:40 +0000 (+0200) Subject: repart: respect `SOURCE_DATE_EPOCH` on `mkdir_p_root` X-Git-Tag: v256-rc2~92 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=34c3d574742e867ef97e79509e4051a82f1b7d9b;p=thirdparty%2Fsystemd.git repart: respect `SOURCE_DATE_EPOCH` on `mkdir_p_root` This let's systemd-repart respect the `SOURCE_DATE_EPOCH` environment variable when creating directories in the local tree through `CopyFiles` or `MakeDirectories`. To do this, we pass a timestamp `ts` to `mkdir_p_root`, which it will use to fix up `mtime` and `atime` of the directory it creates as well as the `mtime` of the directory it creates the other directory *in*, as the `mtime` of the latter is modified when creating a directory in it. For the same reason, it also needs to fixup the `mtime` of the upper directory when copying a file into it through `CopyFiles`. If `SOURCE_DATE_EPOCH`, times are left as is. (`UTIME_OMIT`) --- diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 7c1ad7dd64a..c378f896041 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -204,7 +204,7 @@ int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, g return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper); } -int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes) { +int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes) { _cleanup_free_ char *pp = NULL, *bn = NULL; _cleanup_close_ int dfd = -EBADF; int r; @@ -222,7 +222,7 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m return r; else { /* Extracting the parent dir worked, hence we aren't top-level? Recurse up first. */ - r = mkdir_p_root(root, pp, uid, gid, m, subvolumes); + r = mkdir_p_root_full(root, pp, uid, gid, m, ts, subvolumes); if (r < 0) return r; @@ -248,16 +248,27 @@ int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m return r; } - if (uid_is_valid(uid) || gid_is_valid(gid)) { - _cleanup_close_ int nfd = -EBADF; + if (ts == USEC_INFINITY && !uid_is_valid(uid) && !gid_is_valid(gid)) + return 1; - nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); - if (nfd < 0) + _cleanup_close_ int nfd = -EBADF; + nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); + if (nfd < 0) + return -errno; + + if (ts != USEC_INFINITY) { + struct timespec tspec; + + timespec_store_nsec(&tspec, ts); + if (futimens(dfd, (const struct timespec[2]) { { .tv_nsec = UTIME_OMIT }, tspec }) < 0) return -errno; - if (fchown(nfd, uid, gid) < 0) + if (futimens(nfd, (const struct timespec[2]) { tspec, tspec }) < 0) return -errno; } + if ((uid_is_valid(uid) || gid_is_valid(gid)) && fchown(nfd, uid, gid) < 0) + return -errno; + return 1; } diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h index e5387483e23..471f45b69a0 100644 --- a/src/basic/mkdir.h +++ b/src/basic/mkdir.h @@ -4,6 +4,8 @@ #include #include +#include "time-util.h" + typedef enum MkdirFlags { MKDIR_FOLLOW_SYMLINK = 1 << 0, MKDIR_IGNORE_EXISTING = 1 << 1, /* Quietly accept a preexisting directory (or file) */ @@ -23,7 +25,10 @@ static inline int mkdir_parents(const char *path, mode_t mode) { int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); int mkdir_p(const char *path, mode_t mode); int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); -int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, char **subvolumes); +int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, char **subvolumes); +static inline int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) { + return mkdir_p_root_full(root, p, uid, gid, m, USEC_INFINITY, NULL); +} /* The following are used to implement the mkdir_xyz_label() calls, don't use otherwise. */ typedef int (*mkdirat_func_t)(int dir_fd, const char *pathname, mode_t mode); diff --git a/src/partition/repart.c b/src/partition/repart.c index e35062f6822..8442388359c 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -4774,6 +4774,30 @@ static int make_subvolumes_set( return 0; } +static usec_t epoch_or_infinity(void) { + static usec_t cache; + static bool cached = false; + uint64_t epoch; + int r; + + if (cached) + return cache; + + r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch); + if (r >= 0) { + if (epoch <= UINT64_MAX / USEC_PER_SEC) { /* Overflow check */ + cached = true; + return (cache = epoch * USEC_PER_SEC); + } + r = -ERANGE; + } + if (r != -ENXIO) + log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m"); + + cached = true; + return (cache = USEC_INFINITY); +} + static int do_copy_files(Context *context, Partition *p, const char *root) { int r; @@ -4809,6 +4833,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { _cleanup_hashmap_free_ Hashmap *denylist = NULL; _cleanup_set_free_ Set *subvolumes_by_source_inode = NULL; _cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF; + usec_t ts = epoch_or_infinity(); r = make_copy_files_denylist(context, p, *source, *target, &denylist); if (r < 0) @@ -4847,7 +4872,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { if (r < 0) return log_error_errno(r, "Failed to extract directory from '%s': %m", *target); - r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755, p->subvolumes); + r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, p->subvolumes); if (r < 0) return log_error_errno(r, "Failed to create parent directory '%s': %m", dn); @@ -4873,6 +4898,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { strempty(arg_copy_source), *source, strempty(root), *target); } else { _cleanup_free_ char *dn = NULL, *fn = NULL; + struct timespec tspec; /* We are looking at a regular file */ @@ -4887,7 +4913,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { if (r < 0) return log_error_errno(r, "Failed to extract directory from '%s': %m", *target); - r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755, p->subvolumes); + r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, p->subvolumes); if (r < 0) return log_error_errno(r, "Failed to create parent directory: %m"); @@ -4906,6 +4932,11 @@ static int do_copy_files(Context *context, Partition *p, const char *root) { (void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS); (void) copy_access(sfd, tfd); (void) copy_times(sfd, tfd, 0); + + timespec_store_nsec(&tspec, ts); + + if (ts != USEC_INFINITY && futimens(pfd, (const struct timespec[2]) { { .tv_nsec = UTIME_OMIT }, tspec }) < 0) + return -errno; } } @@ -4919,7 +4950,7 @@ static int do_make_directories(Partition *p, const char *root) { assert(root); STRV_FOREACH(d, p->make_directories) { - r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755, p->subvolumes); + r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), p->subvolumes); if (r < 0) return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index cd49276bd31..471e83b0748 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -2008,7 +2008,7 @@ static int mount_partition( if (where) { if (directory) { /* Automatically create missing mount points inside the image, if necessary. */ - r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755, NULL); + r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755); if (r < 0 && r != -EROFS) return r; diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index 280abb53774..4e236bddff5 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -398,10 +398,10 @@ TEST(config_parse_standard_file_with_dropins_full) { int r; assert_se(mkdtemp_malloc(NULL, &root) >= 0); - assert_se(mkdir_p_root(root, "/etc/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); - assert_se(mkdir_p_root(root, "/run/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); - assert_se(mkdir_p_root(root, "/usr/lib/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); - assert_se(mkdir_p_root(root, "/usr/local/lib/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); + assert_se(mkdir_p_root(root, "/etc/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); + assert_se(mkdir_p_root(root, "/run/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); + assert_se(mkdir_p_root(root, "/usr/lib/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); + assert_se(mkdir_p_root(root, "/usr/local/lib/kernel/install.conf.d", UID_INVALID, GID_INVALID, 0755)); rfd = open(root, O_CLOEXEC|O_DIRECTORY); assert_se(rfd >= 0); @@ -459,10 +459,10 @@ TEST(config_parse_standard_file_with_dropins_full) { assert_se(strv_length(dropins) == 4); /* Make sure that we follow symlinks */ - assert_se(mkdir_p_root(root, "/etc/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); - assert_se(mkdir_p_root(root, "/run/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); - assert_se(mkdir_p_root(root, "/usr/lib/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); - assert_se(mkdir_p_root(root, "/usr/local/lib/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755, NULL)); + assert_se(mkdir_p_root(root, "/etc/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755)); + assert_se(mkdir_p_root(root, "/run/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755)); + assert_se(mkdir_p_root(root, "/usr/lib/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755)); + assert_se(mkdir_p_root(root, "/usr/local/lib/kernel/install2.conf.d", UID_INVALID, GID_INVALID, 0755)); /* (Those symlinks are only useful relative to . */ assert_se(symlinkat("/usr/lib/kernel/install.conf", rfd, "usr/lib/kernel/install2.conf") == 0); diff --git a/src/test/test-mkdir.c b/src/test/test-mkdir.c index 4820b3251a3..fcb8fc7a6b4 100644 --- a/src/test/test-mkdir.c +++ b/src/test/test-mkdir.c @@ -96,7 +96,7 @@ TEST(mkdir_p_root) { assert_se(mkdtemp_malloc("/tmp/test-mkdir-XXXXXX", &tmp) >= 0); assert_se(p = path_join(tmp, "run/aaa/bbb")); - assert_se(mkdir_p_root(tmp, "/run/aaa/bbb", UID_INVALID, GID_INVALID, 0755, NULL) >= 0); + assert_se(mkdir_p_root(tmp, "/run/aaa/bbb", UID_INVALID, GID_INVALID, 0755) >= 0); assert_se(is_dir(p, false) > 0); assert_se(is_dir(p, true) > 0); @@ -109,18 +109,18 @@ TEST(mkdir_p_root) { p = mfree(p); assert_se(p = path_join(tmp, "var/run/hoge/foo/baz")); - assert_se(mkdir_p_root(tmp, "/var/run/hoge/foo/baz", UID_INVALID, GID_INVALID, 0755, NULL) >= 0); + assert_se(mkdir_p_root(tmp, "/var/run/hoge/foo/baz", UID_INVALID, GID_INVALID, 0755) >= 0); assert_se(is_dir(p, false) > 0); assert_se(is_dir(p, true) > 0); p = mfree(p); assert_se(p = path_join(tmp, "not-exists")); - assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755, NULL) == -ENOENT); + assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755) == -ENOENT); p = mfree(p); assert_se(p = path_join(tmp, "regular-file")); assert_se(touch(p) >= 0); - assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755, NULL) == -ENOTDIR); + assert_se(mkdir_p_root(p, "/aaa", UID_INVALID, GID_INVALID, 0755) == -ENOTDIR); /* FIXME: The tests below do not work. p = mfree(p); @@ -138,4 +138,31 @@ TEST(mkdir_p_root) { */ } +TEST(mkdir_p_root_full) { + _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; + _cleanup_free_ char *p = NULL; + struct stat st; + + assert_se(mkdtemp_malloc("/tmp/test-mkdir-XXXXXX", &tmp) >= 0); + + assert_se(p = path_join(tmp, "foo")); + assert_se(mkdir_p_root_full(tmp, "/foo", UID_INVALID, GID_INVALID, 0755, 1234, NULL) >= 0); + assert_se(is_dir(p, false) > 0); + assert_se(is_dir(p, true) > 0); + assert_se(stat(p, &st) >= 0); + assert_se(st.st_mtim.tv_nsec == 1234); + assert_se(st.st_atim.tv_nsec == 1234); + + p = mfree(p); + assert_se(p = path_join(tmp, "dir-not-exists/foo")); + assert_se(mkdir_p_root_full(tmp, "/dir-not-exists/foo", UID_INVALID, GID_INVALID, 0755, 5678, NULL) >= 0); + assert_se(is_dir(p, false) > 0); + assert_se(is_dir(p, true) > 0); + p = mfree(p); + assert_se(p = path_join(tmp, "dir-not-exists")); + assert_se(stat(p, &st) >= 0); + assert_se(st.st_mtim.tv_nsec == 5678); + assert_se(st.st_atim.tv_nsec == 5678); +} + DEFINE_TEST_MAIN(LOG_DEBUG);