Skip to content
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

repart: respect SOURCE_DATE_EPOCH on mkdir_p_root #32502

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.
Jump to
Jump to file
Failed to load files.
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;
}

msanft marked this conversation as resolved.
Show resolved Hide resolved
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))
msanft marked this conversation as resolved.
Show resolved Hide resolved
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);
msanft marked this conversation as resolved.
Show resolved Hide resolved
if (nfd < 0)
return -errno;

if (ts != USEC_INFINITY) {
yuwata marked this conversation as resolved.
Show resolved Hide resolved
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;
msanft marked this conversation as resolved.
Show resolved Hide resolved

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;
msanft marked this conversation as resolved.
Show resolved Hide resolved
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);
}
msanft marked this conversation as resolved.
Show resolved Hide resolved
r = -ERANGE;
}
if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");

cached = true;
return (cache = USEC_INFINITY);
}

msanft marked this conversation as resolved.
Show resolved Hide resolved
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;
msanft marked this conversation as resolved.
Show resolved Hide resolved
}
}

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);