mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
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`)
This commit is contained in:
parent
4dc1be99d6
commit
34c3d57474
@ -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;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 <root>. */
|
||||
assert_se(symlinkat("/usr/lib/kernel/install.conf", rfd, "usr/lib/kernel/install2.conf") == 0);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user