]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Don't descend into directories assigned to other partitions
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 23 Sep 2022 19:15:01 +0000 (21:15 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 10 Nov 2022 15:40:33 +0000 (16:40 +0100)
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.

src/partition/repart.c

index 96606b89970e94865bf3d97290faa880f23910de..46eda6bb081a9b47a85a3a1af1d651ce3758d9c5 100644 (file)
@@ -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);