repart: Allow target directory excludes

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
```
This commit is contained in:
Daan De Meyer
2023-05-22 23:17:49 +02:00
committed by Zbigniew Jędrzejewski-Szmek
parent afbe20b7d4
commit 600bf76c17
3 changed files with 61 additions and 29 deletions

View File

@@ -450,6 +450,7 @@
<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
@@ -461,6 +462,10 @@
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

View 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

View File

@@ -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"
}