diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2022-09-23 21:15:01 +0200 |
---|---|---|
committer | Daan De Meyer <daan.j.demeyer@gmail.com> | 2022-11-10 16:40:33 +0100 |
commit | c0fad2d9b60eed5a1cb3c51848a2b57f77699000 (patch) | |
tree | 1192832eb236b27f5999653f142313c572cd9cbc | |
parent | 58b4ad76ca91309a570ae4811e90b8d7b8a09777 (diff) | |
download | systemd-c0fad2d9b60eed5a1cb3c51848a2b57f77699000.tar.gz |
repart: Don't descend into directories assigned to other partitions
Let's say we have the following repart definitions files root.conf
and home.conf:
```
[Partition]
Type=root
CopyFiles=/
```
```
[Partition]
Type=home
CopyFiles=/home
```
Currently, we'd end up copying /home to both the root partition and
the home partition. To prevent this from happening, let's adopt a
new policy when copying files for a partition: We won't copy any
files/directories that appear in the CopyFiles= list of another
partition, unless that directory explicitly appears in our own
CopyFiles= list.
This way, we prevent copying /home twice into the root and home
partition, but should a user really want that behavior, they can
have it by adding /home to the CopyFIles= list of the root partition
as well.
-rw-r--r-- | src/partition/repart.c | 69 |
1 files changed, 59 insertions, 10 deletions
diff --git a/src/partition/repart.c b/src/partition/repart.c index 96606b8997..46eda6bb08 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -51,6 +51,7 @@ #include "mkfs-util.h" #include "mount-util.h" #include "mountpoint-util.h" +#include "nulstr-util.h" #include "openssl-util.h" #include "parse-argument.h" #include "parse-helpers.h" @@ -3227,7 +3228,7 @@ static int context_copy_blocks(Context *context) { return 0; } -static int do_copy_files(Partition *p, const char *root) { +static int do_copy_files(Partition *p, const char *root, const Set *denylist) { int r; assert(p); @@ -3274,14 +3275,14 @@ static int do_copy_files(Partition *p, const char *root) { pfd, fn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS, - NULL); + denylist); } else r = copy_tree_at( sfd, ".", tfd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS, - NULL); + denylist); if (r < 0) return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target); } else { @@ -3341,7 +3342,7 @@ static int do_make_directories(Partition *p, const char *root) { return 0; } -static int partition_populate_directory(Partition *p, char **ret_root, char **ret_tmp_root) { +static int partition_populate_directory(Partition *p, const Set *denylist, char **ret_root, char **ret_tmp_root) { _cleanup_(rm_rf_physical_and_freep) char *root = NULL; int r; @@ -3364,7 +3365,8 @@ static int partition_populate_directory(Partition *p, char **ret_root, char **re * 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], "/")) { + 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); @@ -3380,7 +3382,7 @@ static int partition_populate_directory(Partition *p, char **ret_root, char **re if (r < 0) return log_error_errno(r, "Failed to create temporary directory: %m"); - r = do_copy_files(p, root); + r = do_copy_files(p, root, denylist); if (r < 0) return r; @@ -3393,7 +3395,7 @@ static int partition_populate_directory(Partition *p, char **ret_root, char **re return 0; } -static int partition_populate_filesystem(Partition *p, const char *node) { +static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) { int r; assert(p); @@ -3427,7 +3429,7 @@ static int partition_populate_filesystem(Partition *p, const char *node) { if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0) _exit(EXIT_FAILURE); - if (do_copy_files(p, fs) < 0) + if (do_copy_files(p, fs, denylist) < 0) _exit(EXIT_FAILURE); if (do_make_directories(p, fs) < 0) @@ -3446,13 +3448,60 @@ static int partition_populate_filesystem(Partition *p, const char *node) { return 0; } +static int make_copy_files_denylist(Context *context, Set **ret) { + _cleanup_set_free_ Set *denylist = NULL; + int r; + + assert(context); + assert(ret); + + LIST_FOREACH(partitions, p, context->partitions) { + const char *s; + + const char *sources = gpt_partition_type_mountpoint_nulstr(p->type_uuid); + if (!sources) + continue; + + NULSTR_FOREACH(s, sources) { + _cleanup_free_ char *d = NULL; + struct stat st; + + r = chase_symlinks_and_stat(s, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL); + if (r == -ENOENT) + continue; + if (r < 0) + return log_error_errno(r, "Failed to stat source file '%s%s': %m", + strempty(arg_root), s); + + if (set_contains(denylist, &st)) + continue; + + d = memdup(&st, sizeof(st)); + if (!d) + return log_oom(); + if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0) + return log_oom(); + + TAKE_PTR(d); + } + } + + *ret = TAKE_PTR(denylist); + return 0; +} + static int context_mkfs(Context *context) { + _cleanup_set_free_ Set *denylist = NULL; int fd = -1, r; assert(context); /* Make a file system */ + r = make_copy_files_denylist(context, &denylist); + if (r < 0) + return r; + LIST_FOREACH(partitions, p, context->partitions) { _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; @@ -3509,7 +3558,7 @@ static int context_mkfs(Context *context) { * using read-only filesystems such as squashfs, we can't populate after creating the * filesystem because it's read-only, so instead we create a temporary root to use as the * source tree when generating the read-only filesystem. */ - r = partition_populate_directory(p, &root, &tmp_root); + r = partition_populate_directory(p, denylist, &root, &tmp_root); if (r < 0) return r; @@ -3528,7 +3577,7 @@ static int context_mkfs(Context *context) { return log_error_errno(errno, "Failed to unlock LUKS device: %m"); /* Now, we can populate all the other filesystems that aren't read-only. */ - r = partition_populate_filesystem(p, fsdev); + r = partition_populate_filesystem(p, fsdev, denylist); if (r < 0) { encrypted_dev_fd = safe_close(encrypted_dev_fd); (void) deactivate_luks(cd, encrypted); |