summaryrefslogtreecommitdiff
path: root/src/partition
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-05-08 18:44:01 +0200
committerDaan De Meyer <daan.j.demeyer@gmail.com>2023-05-10 18:07:47 +0200
commite57b7020d2ee89d9c906bcefa091d31b067dedcc (patch)
treee16f23cbadb2ad96a69423c4fce51456d2f74439 /src/partition
parent39a39f18f2bd2b1ef7b4fa4823472d129c514030 (diff)
downloadsystemd-e57b7020d2ee89d9c906bcefa091d31b067dedcc.tar.gz
repart: Fix deny list logic
Until now, we always excluded the top level directories that were covered by child partition mount points, regardless of the source directory and the target directory of the copy files operation. This means that even if we were populating a XBOOTLDR partition, if there was an EFI partition in the image, we'd exclude /boot unconditionally, leading to the XBOOTLDR partition to be empty. Also, because of the same cause, if we were copying a nested source directory (e.g. /abc/def) to the root directory in the root partition, if /abc/def/usr existed and was populated with files and directories, the root partition would have those files under /usr, even if a /usr partition was defined. To fix these issues, instead of unconditionally excluding the top level partition mount points under <source>, let's make sure that when we're copying files from any source directory to the root directory of a root partition, that we exclude the partition mount point directories under the source directory instead of the top level ones.
Diffstat (limited to 'src/partition')
-rw-r--r--src/partition/repart.c220
1 files changed, 129 insertions, 91 deletions
diff --git a/src/partition/repart.c b/src/partition/repart.c
index d6db6ee73e..44b0e461f2 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -96,6 +96,8 @@
/* LUKS2 volume key size. */
#define VOLUME_KEY_SIZE (512ULL/8ULL)
+#define APIVFS_TMP_DIRS_NULSTR "proc\0sys\0dev\0tmp\0run\0var/tmp\0"
+
/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
* are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
* sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
@@ -3859,7 +3861,119 @@ static int context_copy_blocks(Context *context) {
return 0;
}
-static int do_copy_files(Partition *p, const char *root, Hashmap *denylist) {
+static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type) {
+ _cleanup_free_ struct stat *st = NULL;
+ int r;
+
+ assert(path);
+ assert(denylist);
+
+ st = new(struct stat, 1);
+ if (!st)
+ return log_oom();
+
+ r = chase_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_root), path);
+
+ r = hashmap_ensure_put(denylist, &inode_hash_ops, st, INT_TO_PTR(type));
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ TAKE_PTR(st);
+
+ return 0;
+}
+
+static int make_copy_files_denylist(
+ Context *context,
+ const Partition *p,
+ const char *source,
+ const char *target,
+ Hashmap **ret) {
+
+ _cleanup_hashmap_free_ Hashmap *denylist = NULL;
+ int r;
+
+ assert(context);
+ assert(p);
+ assert(source);
+ assert(target);
+ assert(ret);
+
+ /* Always exclude the top level APIVFS and temporary directories since the contents of these
+ * directories are almost certainly not intended to end up in an image. */
+
+ NULSTR_FOREACH(s, APIVFS_TMP_DIRS_NULSTR) {
+ r = add_exclude_path(s, &denylist, DENY_CONTENTS);
+ if (r < 0)
+ return r;
+ }
+
+ /* Add the user configured excludes. */
+
+ STRV_FOREACH(e, p->exclude_files) {
+ r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
+ if (r < 0)
+ return r;
+ }
+
+ /* If we're populating a root partition, we don't want any files to end up under the APIVFS mount
+ * points. While we already exclude <source>/proc, users could still do something such as
+ * "CopyFiles=/abc:/". Now, if /abc has a proc subdirectory with files in it, those will end up in
+ * the top level proc directory in the root partition, which we want to avoid. To deal with these
+ * cases, whenever we're populating a root partition and the target of CopyFiles= is the root
+ * directory of the root partition, we exclude all directories under the source that are named after
+ * APIVFS directories or named after mount points of other partitions that are also going to be part
+ * of the image. */
+
+ if (p->type.designator == PARTITION_ROOT && empty_or_root(target)) {
+ LIST_FOREACH(partitions, q, context->partitions) {
+ if (q->type.designator == PARTITION_ROOT)
+ continue;
+
+ const char *sources = gpt_partition_type_mountpoint_nulstr(q->type);
+ if (!sources)
+ continue;
+
+ NULSTR_FOREACH(s, sources) {
+ _cleanup_free_ char *path = NULL;
+
+ /* Exclude only the children of partition mount points so that the nested
+ * partition mount point itself still ends up in the upper partition. */
+
+ path = path_join(source, s);
+ if (!path)
+ return -ENOMEM;
+
+ r = add_exclude_path(path, &denylist, DENY_CONTENTS);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ NULSTR_FOREACH(s, APIVFS_TMP_DIRS_NULSTR) {
+ _cleanup_free_ char *path = NULL;
+
+ path = path_join(source, s);
+ if (!path)
+ return -ENOMEM;
+
+ r = add_exclude_path(path, &denylist, DENY_CONTENTS);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = TAKE_PTR(denylist);
+ return 0;
+}
+
+static int do_copy_files(Context *context, Partition *p, const char *root) {
int r;
assert(p);
@@ -3891,8 +4005,13 @@ static int do_copy_files(Partition *p, const char *root, Hashmap *denylist) {
}
STRV_FOREACH_PAIR(source, target, p->copy_files) {
+ _cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
+ r = make_copy_files_denylist(context, p, *source, *target, &denylist);
+ if (r < 0)
+ return r;
+
sfd = chase_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL);
if (sfd < 0)
return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source);
@@ -4004,7 +4123,7 @@ static bool partition_needs_populate(Partition *p) {
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
}
-static int partition_populate_directory(Partition *p, Hashmap *denylist, char **ret) {
+static int partition_populate_directory(Context *context, Partition *p, char **ret) {
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
const char *vt;
int r;
@@ -4025,7 +4144,7 @@ static int partition_populate_directory(Partition *p, Hashmap *denylist, char **
if (r < 0)
return log_error_errno(errno, "Failed to create temporary directory: %m");
- r = do_copy_files(p, root, denylist);
+ r = do_copy_files(context, p, root);
if (r < 0)
return r;
@@ -4039,7 +4158,7 @@ static int partition_populate_directory(Partition *p, Hashmap *denylist, char **
return 0;
}
-static int partition_populate_filesystem(Partition *p, const char *node, Hashmap *denylist) {
+static int partition_populate_filesystem(Context *context, Partition *p, const char *node) {
int r;
assert(p);
@@ -4067,7 +4186,7 @@ static int partition_populate_filesystem(Partition *p, const char *node, Hashmap
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, denylist) < 0)
+ if (do_copy_files(context, p, fs) < 0)
_exit(EXIT_FAILURE);
if (do_make_directories(p, fs) < 0)
@@ -4086,77 +4205,6 @@ static int partition_populate_filesystem(Partition *p, const char *node, Hashmap
return 0;
}
-static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type) {
- _cleanup_free_ struct stat *st = NULL;
- int r;
-
- assert(path);
- assert(denylist);
-
- st = new(struct stat, 1);
- if (!st)
- return log_oom();
-
- r = chase_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st);
- if (r == -ENOENT)
- return 0;
- if (r < 0)
- return log_error_errno(r, "Failed to stat source file '%s%s': %m",
- strempty(arg_root), path);
-
- if (hashmap_contains(*denylist, st))
- return 0;
-
- if (hashmap_ensure_put(denylist, &inode_hash_ops, st, INT_TO_PTR(type)) < 0)
- return log_oom();
-
- TAKE_PTR(st);
-
- return 0;
-}
-
-static int make_copy_files_denylist(Context *context, const Partition *p, Hashmap **ret) {
- _cleanup_hashmap_free_ Hashmap *denylist = NULL;
- int r;
-
- assert(context);
- assert(p);
- assert(ret);
-
- LIST_FOREACH(partitions, q, context->partitions) {
- if (p == q)
- continue;
-
- const char *sources = gpt_partition_type_mountpoint_nulstr(q->type);
- if (!sources)
- continue;
-
- NULSTR_FOREACH(s, sources) {
- /* Exclude the children of partition mount points so that the nested partition mount
- * point itself still ends up in the upper partition. */
-
- r = add_exclude_path(s, &denylist, DENY_CONTENTS);
- if (r < 0)
- return r;
- }
- }
-
- FOREACH_STRING(s, "proc", "sys", "dev", "tmp", "run", "var/tmp") {
- r = add_exclude_path(s, &denylist, DENY_CONTENTS);
- if (r < 0)
- return r;
- }
-
- STRV_FOREACH(e, p->exclude_files) {
- r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
- if (r < 0)
- return r;
- }
-
- *ret = TAKE_PTR(denylist);
- return 0;
-}
-
static int context_mkfs(Context *context) {
int r;
@@ -4165,7 +4213,6 @@ static int context_mkfs(Context *context) {
/* Make a file system */
LIST_FOREACH(partitions, p, context->partitions) {
- _cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
_cleanup_(partition_target_freep) PartitionTarget *t = NULL;
_cleanup_strv_free_ char **extra_mkfs_options = NULL;
@@ -4190,10 +4237,6 @@ static int context_mkfs(Context *context) {
assert(p->new_size != UINT64_MAX);
assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
- r = make_copy_files_denylist(context, p, &denylist);
- if (r < 0)
- return r;
-
/* If we're doing encryption, we make sure we keep free space at the end which is required
* for cryptsetup's offline encryption. */
r = partition_target_prepare(context, p,
@@ -4215,7 +4258,7 @@ static int context_mkfs(Context *context) {
"Loop device access is required to populate %s filesystems.",
p->format);
- r = partition_populate_directory(p, denylist, &root);
+ r = partition_populate_directory(context, p, &root);
if (r < 0)
return r;
}
@@ -4237,7 +4280,7 @@ static int context_mkfs(Context *context) {
if (partition_needs_populate(p) && !root) {
assert(t->loop);
- r = partition_populate_filesystem(p, t->loop->node, denylist);
+ r = partition_populate_filesystem(context, p, t->loop->node);
if (r < 0)
return r;
}
@@ -5484,7 +5527,6 @@ static int context_minimize(Context *context) {
return log_error_errno(r, "Could not determine temporary directory: %m");
LIST_FOREACH(partitions, p, context->partitions) {
- _cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
_cleanup_(unlink_and_freep) char *temp = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
@@ -5516,10 +5558,6 @@ static int context_minimize(Context *context) {
log_info("Pre-populating %s filesystem of partition %s twice to calculate minimal partition size",
p->format, strna(hint));
- r = make_copy_files_denylist(context, p, &denylist);
- if (r < 0)
- return r;
-
r = tempfn_random_child(vt, "repart", &temp);
if (r < 0)
return log_error_errno(r, "Failed to generate temporary file path: %m");
@@ -5554,7 +5592,7 @@ static int context_minimize(Context *context) {
"Loop device access is required to populate %s filesystems",
p->format);
- r = partition_populate_directory(p, denylist, &root);
+ r = partition_populate_directory(context, p, &root);
if (r < 0)
return r;
}
@@ -5589,7 +5627,7 @@ static int context_minimize(Context *context) {
if (!root) {
assert(d);
- r = partition_populate_filesystem(p, d->node, denylist);
+ r = partition_populate_filesystem(context, p, d->node);
if (r < 0)
return r;
}
@@ -5638,7 +5676,7 @@ static int context_minimize(Context *context) {
if (!root) {
assert(d);
- r = partition_populate_filesystem(p, d->node, denylist);
+ r = partition_populate_filesystem(context, p, d->node);
if (r < 0)
return r;
}