Skip to content

repart: respect SOURCE_DATE_EPOCH on mkdir_p_root #32502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions src/basic/mkdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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;
}
7 changes: 6 additions & 1 deletion src/basic/mkdir.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand All @@ -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);
Expand Down
37 changes: 34 additions & 3 deletions src/partition/repart.c
Original file line number Diff line number Diff line change
Expand Up @@ -4775,6 +4775,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;

Expand Down Expand Up @@ -4810,6 +4834,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)
Expand Down Expand Up @@ -4848,7 +4873,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);

Expand All @@ -4874,6 +4899,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 */

Expand All @@ -4888,7 +4914,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");

Expand All @@ -4907,6 +4933,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;
}
}

Expand All @@ -4920,7 +4951,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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/shared/dissect-image.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
16 changes: 8 additions & 8 deletions src/test/test-conf-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
35 changes: 31 additions & 4 deletions src/test/test-mkdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
Expand All @@ -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);