]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Add ExcludeFiles= option 26437/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 16 Feb 2023 12:23:47 +0000 (13:23 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 20 Feb 2023 12:51:46 +0000 (13:51 +0100)
man/repart.d.xml
src/partition/repart.c
test/units/testsuite-58.sh

index 0303a7f53624dc47b4ccf3760907ab1d0b206dbd..fb5d34baea00d27f96cd645c88daec6a098a02dd 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ExcludeFiles=</varname></term>
+
+        <listitem><para>Takes an absolute file system path referring to a source file or directory on the
+        host. This setting may be used to exclude files or directories from the host from being copied into
+        the file system when <varname>CopyFiles=</varname> is used. This option may be used multiple times to
+        exclude multiple files or directories from host from being copied into the newly formatted file
+        system.</para>
+
+        <para>When
+        <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        is invoked with the <option>--image=</option> or <option>--root=</option> command line switches the
+        paths specified are taken relative to the specified root directory or disk image root.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>MakeDirectories=</varname></term>
 
index 874a8dc3ad14fc9b4bcc4071bfe1d0a64890b656..acede30864179e3804429d7de5befd7195a65a6b 100644 (file)
@@ -226,6 +226,7 @@ typedef struct Partition {
 
         char *format;
         char **copy_files;
+        char **exclude_files;
         char **make_directories;
         EncryptMode encrypt;
         VerityMode verity;
@@ -369,6 +370,7 @@ static Partition* partition_free(Partition *p) {
 
         free(p->format);
         strv_free(p->copy_files);
+        strv_free(p->exclude_files);
         strv_free(p->make_directories);
         free(p->verity_match_key);
 
@@ -395,6 +397,7 @@ static void partition_foreignize(Partition *p) {
 
         p->format = mfree(p->format);
         p->copy_files = strv_free(p->copy_files);
+        p->exclude_files = strv_free(p->exclude_files);
         p->make_directories = strv_free(p->make_directories);
         p->verity_match_key = mfree(p->verity_match_key);
 
@@ -1386,6 +1389,43 @@ static int config_parse_copy_files(
         return 0;
 }
 
+static int config_parse_exclude_files(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+        _cleanup_free_ char *resolved = NULL;
+        char ***exclude_files = ASSERT_PTR(data);
+        int r;
+
+        if (isempty(rvalue)) {
+                *exclude_files = strv_free(*exclude_files);
+                return 0;
+        }
+
+        r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to expand specifiers in ExcludeFiles= path, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+        if (r < 0)
+                return 0;
+
+        if (strv_consume(exclude_files, TAKE_PTR(resolved)) < 0)
+                return log_oom();
+
+        return 0;
+}
+
 static int config_parse_copy_blocks(
                 const char *unit,
                 const char *filename,
@@ -1553,30 +1593,31 @@ static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mod
 static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
 
         ConfigTableItem table[] = {
-                { "Partition", "Type",            config_parse_type,        0, &p->type              },
-                { "Partition", "Label",           config_parse_label,       0, &p->new_label         },
-                { "Partition", "UUID",            config_parse_uuid,        0, p                     },
-                { "Partition", "Priority",        config_parse_int32,       0, &p->priority          },
-                { "Partition", "Weight",          config_parse_weight,      0, &p->weight            },
-                { "Partition", "PaddingWeight",   config_parse_weight,      0, &p->padding_weight    },
-                { "Partition", "SizeMinBytes",    config_parse_size4096,    1, &p->size_min          },
-                { "Partition", "SizeMaxBytes",    config_parse_size4096,   -1, &p->size_max          },
-                { "Partition", "PaddingMinBytes", config_parse_size4096,    1, &p->padding_min       },
-                { "Partition", "PaddingMaxBytes", config_parse_size4096,   -1, &p->padding_max       },
-                { "Partition", "FactoryReset",    config_parse_bool,        0, &p->factory_reset     },
-                { "Partition", "CopyBlocks",      config_parse_copy_blocks, 0, p                     },
-                { "Partition", "Format",          config_parse_fstype,      0, &p->format            },
-                { "Partition", "CopyFiles",       config_parse_copy_files,  0, &p->copy_files        },
-                { "Partition", "MakeDirectories", config_parse_make_dirs,   0, p                     },
-                { "Partition", "Encrypt",         config_parse_encrypt,     0, &p->encrypt           },
-                { "Partition", "Verity",          config_parse_verity,      0, &p->verity            },
-                { "Partition", "VerityMatchKey",  config_parse_string,      0, &p->verity_match_key  },
-                { "Partition", "Flags",           config_parse_gpt_flags,   0, &p->gpt_flags         },
-                { "Partition", "ReadOnly",        config_parse_tristate,    0, &p->read_only         },
-                { "Partition", "NoAuto",          config_parse_tristate,    0, &p->no_auto           },
-                { "Partition", "GrowFileSystem",  config_parse_tristate,    0, &p->growfs            },
-                { "Partition", "SplitName",       config_parse_string,      0, &p->split_name_format },
-                { "Partition", "Minimize",        config_parse_minimize,    0, &p->minimize          },
+                { "Partition", "Type",            config_parse_type,          0, &p->type              },
+                { "Partition", "Label",           config_parse_label,         0, &p->new_label         },
+                { "Partition", "UUID",            config_parse_uuid,          0, p                     },
+                { "Partition", "Priority",        config_parse_int32,         0, &p->priority          },
+                { "Partition", "Weight",          config_parse_weight,        0, &p->weight            },
+                { "Partition", "PaddingWeight",   config_parse_weight,        0, &p->padding_weight    },
+                { "Partition", "SizeMinBytes",    config_parse_size4096,      1, &p->size_min          },
+                { "Partition", "SizeMaxBytes",    config_parse_size4096,     -1, &p->size_max          },
+                { "Partition", "PaddingMinBytes", config_parse_size4096,      1, &p->padding_min       },
+                { "Partition", "PaddingMaxBytes", config_parse_size4096,     -1, &p->padding_max       },
+                { "Partition", "FactoryReset",    config_parse_bool,          0, &p->factory_reset     },
+                { "Partition", "CopyBlocks",      config_parse_copy_blocks,   0, p                     },
+                { "Partition", "Format",          config_parse_fstype,        0, &p->format            },
+                { "Partition", "CopyFiles",       config_parse_copy_files,    0, &p->copy_files        },
+                { "Partition", "ExcludeFiles",    config_parse_exclude_files, 0, &p->exclude_files     },
+                { "Partition", "MakeDirectories", config_parse_make_dirs,     0, p                     },
+                { "Partition", "Encrypt",         config_parse_encrypt,       0, &p->encrypt           },
+                { "Partition", "Verity",          config_parse_verity,        0, &p->verity            },
+                { "Partition", "VerityMatchKey",  config_parse_string,        0, &p->verity_match_key  },
+                { "Partition", "Flags",           config_parse_gpt_flags,     0, &p->gpt_flags         },
+                { "Partition", "ReadOnly",        config_parse_tristate,      0, &p->read_only         },
+                { "Partition", "NoAuto",          config_parse_tristate,      0, &p->no_auto           },
+                { "Partition", "GrowFileSystem",  config_parse_tristate,      0, &p->growfs            },
+                { "Partition", "SplitName",       config_parse_string,        0, &p->split_name_format },
+                { "Partition", "Minimize",        config_parse_minimize,      0, &p->minimize          },
                 {}
         };
         int r;
@@ -3969,15 +4010,16 @@ static int partition_populate_filesystem(Partition *p, const char *node, const S
         return 0;
 }
 
-static int make_copy_files_denylist(Context *context, Set **ret) {
+static int make_copy_files_denylist(Context *context, const Partition *p, Set **ret) {
         _cleanup_set_free_ Set *denylist = NULL;
         int r;
 
         assert(context);
+        assert(p);
         assert(ret);
 
-        LIST_FOREACH(partitions, p, context->partitions) {
-                const char *sources = gpt_partition_type_mountpoint_nulstr(p->type);
+        LIST_FOREACH(partitions, q, context->partitions) {
+                const char *sources = gpt_partition_type_mountpoint_nulstr(q->type);
                 if (!sources)
                         continue;
 
@@ -4005,23 +4047,42 @@ static int make_copy_files_denylist(Context *context, Set **ret) {
                 }
         }
 
+        STRV_FOREACH(e, p->exclude_files) {
+                _cleanup_free_ char *d = NULL;
+                struct stat st;
+
+                r = chase_symlinks_and_stat(*e, 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), *e);
+
+                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 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_set_free_ Set *denylist = NULL;
                 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
                 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
 
@@ -4045,6 +4106,10 @@ 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,
@@ -5327,21 +5392,17 @@ static int fd_apparent_size(int fd, uint64_t *ret) {
 }
 
 static int context_minimize(Context *context) {
-        _cleanup_set_free_ Set *denylist = NULL;
         const char *vt;
         int r;
 
         assert(context);
 
-        r = make_copy_files_denylist(context, &denylist);
-        if (r < 0)
-                return r;
-
         r = var_tmp_dir(&vt);
         if (r < 0)
                 return log_error_errno(r, "Could not determine temporary directory: %m");
 
         LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_set_free_ Set *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;
@@ -5366,6 +5427,10 @@ static int context_minimize(Context *context) {
 
                 assert(!p->copy_blocks_path);
 
+                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");
index 0ed55ac0331ec1305ee9850562438ab47286299d..7aef2d723ee2a7d87e81b85fdefd7daeea9a7e22 100755 (executable)
@@ -839,7 +839,7 @@ EOF
     systemd-dissect -U "$imgs/mnt"
 }
 
-test_issue_24786() {
+test_exclude_files() {
     local defs imgs root output
 
     defs="$(runas testuser mktemp --directory "/tmp/test-repart.XXXXXXXXXX")"
@@ -851,6 +851,7 @@ test_issue_24786() {
     runas testuser touch "$root/abc"
     runas testuser mkdir "$root/usr"
     runas testuser touch "$root/usr/def"
+    runas testuser touch "$root/usr/qed"
 
     runas testuser tee "$defs/00-root.conf" <<EOF
 [Partition]
@@ -862,6 +863,7 @@ EOF
 [Partition]
 Type=usr-${architecture}
 CopyFiles=/usr:/
+ExcludeFiles=/usr/qed
 EOF
 
     output=$(runas testuser systemd-repart --definitions="$defs" \
@@ -881,13 +883,17 @@ EOF
     loop=$(losetup -P --show -f "$imgs/zzz")
     udevadm wait --timeout 60 --settle "${loop:?}"
 
+    # Test that the /usr directory did not end up in the root partition but other files did.
     mkdir "$imgs/mnt"
     mount -t ext4 "${loop}p1" "$imgs/mnt"
     assert_rc 0 ls "$imgs/mnt/abc"
     assert_rc 2 ls "$imgs/mnt/usr"
+
+    # Test that the qed file did not end up in the usr partition but other files did.
     mkdir "$imgs/mnt/usr"
     mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
     assert_rc 0 ls "$imgs/mnt/usr/def"
+    assert_rc 2 ls "$imgs/mnt/usr/qed"
 
     umount -R "$imgs/mnt"
     losetup -d "$loop"
@@ -1015,7 +1021,7 @@ test_issue_21817
 test_issue_24553
 test_zero_uuid
 test_verity
-test_issue_24786
+test_exclude_files
 test_minimize
 
 # Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and