}
static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
+
int r;
assert(p);
r = copy_tree_at(
sfd, ".",
pfd, fn,
- UID_INVALID, GID_INVALID,
+ getuid(), getgid(),
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
denylist);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
- UID_INVALID, GID_INVALID,
+ getuid(), getgid(),
COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
denylist);
if (r < 0)
STRV_FOREACH(d, p->make_directories) {
- r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, *d, getuid(), getgid(), 0755);
if (r < 0)
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
}
return 0;
}
-static int partition_populate_directory(Partition *p, const Set *denylist, char **ret_root, char **ret_tmp_root) {
+static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
int r;
- assert(ret_root);
- assert(ret_tmp_root);
-
- /* If we only have a single directory that's meant to become the root directory of the filesystem,
- * we can shortcut this function and just use that directory as the root directory instead. If we
- * allocate a temporary directory, it's stored in "ret_tmp_root" to indicate it should be removed.
- * Otherwise, we return the directory to use in "root" to indicate it should not be removed. */
-
- if (strv_length(p->copy_files) == 2 && strv_length(p->make_directories) == 0 &&
- streq(p->copy_files[1], "/") && set_isempty(denylist)) {
- _cleanup_free_ char *s = NULL;
-
- r = chase_symlinks(p->copy_files[0], arg_root, CHASE_PREFIX_ROOT, &s, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to resolve source '%s%s': %m", strempty(arg_root), p->copy_files[0]);
-
- *ret_root = TAKE_PTR(s);
- *ret_tmp_root = NULL;
- return 0;
- }
+ assert(ret);
r = mkdtemp_malloc("/var/tmp/repart-XXXXXX", &root);
if (r < 0)
return log_error_errno(r, "Failed to create temporary directory: %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 = do_copy_files(p, root, denylist);
if (r < 0)
return r;
if (r < 0)
return r;
- *ret_root = NULL;
- *ret_tmp_root = TAKE_PTR(root);
+ *ret = TAKE_PTR(root);
return 0;
}
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- _cleanup_(rm_rf_physical_and_freep) char *tmp_root = NULL;
- _cleanup_free_ char *root = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
if (p->dropped)
continue;
* source tree when generating the read-only filesystem. */
if (mkfs_supports_root_option(p->format)) {
- r = partition_populate_directory(p, denylist, &root, &tmp_root);
+ r = partition_populate_directory(p, denylist, &root);
if (r < 0)
return r;
}
- r = make_filesystem(d->node, p->format, strempty(p->new_label), root ?: tmp_root, p->fs_uuid, arg_discard);
+ r = make_filesystem(d->node, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard);
if (r < 0)
return r;
return log_error_errno(r, "Could not determine temporary directory: %m");
LIST_FOREACH(partitions, p, context->partitions) {
- _cleanup_(rm_rf_physical_and_freep) char *tmp_root = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
_cleanup_(unlink_and_freep) char *temp = NULL;
- _cleanup_free_ char *root = NULL;
_cleanup_close_ int fd = -1;
sd_id128_t fs_uuid;
uint64_t fsz;
}
if (mkfs_supports_root_option(p->format)) {
- r = partition_populate_directory(p, denylist, &root, &tmp_root);
+ r = partition_populate_directory(p, denylist, &root);
if (r < 0)
return r;
}
- r = make_filesystem(temp, p->format, strempty(p->new_label), root ?: tmp_root, fs_uuid,
- arg_discard);
+ r = make_filesystem(temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard);
if (r < 0)
return r;
if (ftruncate(fd, fsz))
return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
- r = make_filesystem(temp, p->format, strempty(p->new_label), root ?: tmp_root, p->fs_uuid,
- arg_discard);
+ r = make_filesystem(temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard);
if (r < 0)
return r;
#include "dirent-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "id128-util.h"
#include "mkfs-util.h"
#include "mountpoint-util.h"
return 0;
}
+static int setup_userns(uid_t uid, gid_t gid) {
+ int r;
+
+ /* mkfs programs tend to keep ownership intact when bootstrapping themselves from a root directory.
+ * However, we'd like for the files to be owned by root instead, so we fork off a user namespace and
+ * inside of it, map the uid/gid of the root directory to root in the user namespace. mkfs programs
+ * will pick up on this and the files will be owned by root in the generated filesystem. */
+
+ r = write_string_filef("/proc/self/uid_map", WRITE_STRING_FILE_DISABLE_BUFFER,
+ UID_FMT " " UID_FMT " " UID_FMT, 0u, uid, 1u);
+ if (r < 0)
+ return log_error_errno(r,
+ "Failed to write mapping for "UID_FMT" to /proc/self/uid_map: %m",
+ uid);
+
+ r = write_string_file("/proc/self/setgroups", "deny", WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write 'deny' to /proc/self/setgroups: %m");
+
+ r = write_string_filef("/proc/self/gid_map", WRITE_STRING_FILE_DISABLE_BUFFER,
+ UID_FMT " " UID_FMT " " UID_FMT, 0u, gid, 1u);
+ if (r < 0)
+ return log_error_errno(r,
+ "Failed to write mapping for "UID_FMT" to /proc/self/gid_map: %m",
+ gid);
+
+ return 0;
+}
+
static int do_mcopy(const char *node, const char *root) {
_cleanup_strv_free_ char **argv = NULL;
_cleanup_closedir_ DIR *rootdir = NULL;
+ struct stat st;
int r;
assert(node);
if (r < 0)
return log_oom();
+ if (stat(root, &st) < 0)
+ return log_error_errno(errno, "Failed to stat '%s': %m", root);
+
r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_NEW_USERNS, NULL);
if (r < 0)
return r;
if (r == 0) {
+ r = setup_userns(st.st_uid, st.st_gid);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
/* Avoid failures caused by mismatch in expectations between mkfs.vfat and mcopy by disabling
* the stricter mcopy checks using MTOOLS_SKIP_CHECK. */
execvpe("mcopy", argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1"));
_cleanup_free_ char *mkfs = NULL, *mangled_label = NULL;
_cleanup_strv_free_ char **argv = NULL;
char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {};
+ struct stat st;
int r;
assert(node);
if (!argv)
return log_oom();
- r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, NULL);
+ if (root && stat(root, &st) < 0)
+ return log_error_errno(errno, "Failed to stat %s: %m", root);
+
+ r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|(root ? FORK_NEW_USERNS : 0), NULL);
if (r < 0)
return r;
if (r == 0) {
/* Child */
+ if (root) {
+ r = setup_userns(st.st_uid, st.st_gid);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+ }
+
execvp(mkfs, argv);
log_error_errno(errno, "Failed to execute %s: %m", mkfs);