]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Allow target directory excludes
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 22 May 2023 21:17:49 +0000 (23:17 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 30 May 2023 11:45:49 +0000 (13:45 +0200)
Currently, ExcludeFiles= supports excluding directories on the host
from being copied. Let's extend this to also support preventing files
from being copied into specific directories in the partition by adding
a new option ExcludeFilesTarget=. An example where this is useful is
when setting up btrfs subvolumes in the top level that are intended to
be mounted into specific locations, so /usr would be stored in @usr,
/home in @home, .... To accomplish this, we need to copy /usr to @usr
and prevent any files from being copied into /usr in the partition,
which with this commit, we'd be able to do as follows:

```
[Partition]
CopyFiles=/usr:@usr
ExcludeFilesTarget=/usr
```

man/repart.d.xml
src/partition/repart.c
test/units/testsuite-58.sh

index d272d112da6140b6311f0ecb14c4b73da51e650e..8fe2fc3199fd0b3bb1f3f0a72901dc6ff1372bda 100644 (file)
 
       <varlistentry>
         <term><varname>ExcludeFiles=</varname></term>
+        <term><varname>ExcludeFilesTarget=</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
         contents are excluded but not the directory itself. If the path is a directory and does not end with
         <literal>/</literal>, both the directory and its contents are excluded.</para>
 
+        <para><varname>ExcludeFilesTarget=</varname> is like <varname>ExcludeFiles=</varname> except that
+        instead of excluding the path on the host from being copied into the partition, we exclude any files
+        and directories from being copied into the given path in the partition.</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
index 5a2f992e2dc40a03319890c4a4577e66b30a544d..4fde0a58a5e956e21cd6034e8cc2ad7ab8856c4f 100644 (file)
@@ -230,7 +230,8 @@ typedef struct Partition {
 
         char *format;
         char **copy_files;
-        char **exclude_files;
+        char **exclude_files_source;
+        char **exclude_files_target;
         char **make_directories;
         EncryptMode encrypt;
         VerityMode verity;
@@ -374,7 +375,8 @@ static Partition* partition_free(Partition *p) {
 
         free(p->format);
         strv_free(p->copy_files);
-        strv_free(p->exclude_files);
+        strv_free(p->exclude_files_source);
+        strv_free(p->exclude_files_target);
         strv_free(p->make_directories);
         free(p->verity_match_key);
 
@@ -401,7 +403,8 @@ 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->exclude_files_source = strv_free(p->exclude_files_source);
+        p->exclude_files_target = strv_free(p->exclude_files_target);
         p->make_directories = strv_free(p->make_directories);
         p->verity_match_key = mfree(p->verity_match_key);
 
@@ -1597,31 +1600,32 @@ 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", "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          },
+                { "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_source },
+                { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
+                { "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;
@@ -3913,12 +3917,28 @@ static int make_copy_files_denylist(
 
         /* Add the user configured excludes. */
 
-        STRV_FOREACH(e, p->exclude_files) {
+        STRV_FOREACH(e, p->exclude_files_source) {
                 r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
                 if (r < 0)
                         return r;
         }
 
+        STRV_FOREACH(e, p->exclude_files_target) {
+                _cleanup_free_ char *path = NULL;
+
+                const char *s = path_startswith(*e, target);
+                if (!s)
+                        continue;
+
+                path = path_join(source, s);
+                if (!path)
+                        return log_oom();
+
+                r = add_exclude_path(path, &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
index b1b40851dba7b7cd29c62c62301090c6f3dd0a6f..acd03d2fdf96e744e6e0d085dc7225f37401e4e7 100755 (executable)
@@ -868,6 +868,8 @@ testcase_exclude_files() {
 Type=root-${architecture}
 CopyFiles=/
 CopyFiles=/zzz:/
+CopyFiles=/:/oiu
+ExcludeFilesTarget=/oiu/usr
 EOF
 
     runas testuser tee "$defs/10-usr.conf" <<EOF
@@ -921,6 +923,11 @@ EOF
     # Test that /zzz/usr/prs did not end up in the usr partition.
     assert_rc 2 ls "$imgs/mnt/usr/prs"
 
+    # Test that /oiu/ and /oiu/zzz ended up in the root partition but /oiu/usr did not.
+    assert_rc 0 ls "$imgs/mnt/oiu"
+    assert_rc 0 ls "$imgs/mnt/oiu/zzz"
+    assert_rc 2 ls "$imgs/mnt/oiu/usr"
+
     umount -R "$imgs/mnt"
     losetup -d "$loop"
 }