<varlistentry>
<term><varname>CopyFiles=</varname></term>
- <listitem><para>Takes a pair of colon separated absolute file system paths. The first path refers to
- a source file or directory on the host, the second path refers to a target in the file system of the
- newly created partition and formatted file system. This setting may be used to copy files or
- directories from the host into the file system that is created due to the <varname>Format=</varname>
- option. If <varname>CopyFiles=</varname> is used without <varname>Format=</varname> specified
- explicitly, <literal>Format=</literal> with a suitable default is implied (currently
- <literal>vfat</literal> for <literal>ESP</literal> and <literal>XBOOTLDR</literal> partitions, and
- <literal>ext4</literal> otherwise, but this may change in the future). This option may be used
- multiple times to copy multiple files or directories from host into the newly formatted file system.
- The colon and second path may be omitted in which case the source path is also used as the target
- path (relative to the root of the newly created file system). If the source path refers to a
- directory it is copied recursively.</para>
+ <listitem><para>Takes a colon-separated triplet in the form
+ <literal><varname>source</varname>[:<varname>target</varname>[:<varname>options</varname>]]</literal>.
+ <varname>source</varname> is an absolute path which refers to a source file or directory on the host.
+ <varname>target</varname> is an absolute path in the file system of the newly created partition and
+ formatted file system. <varname>options</varname> is a comma-separated list of options where each
+ option is in the form <literal><varname>key</varname>[=<varname>value</varname>]</literal>.</para>
+
+ <para>This setting may be used to copy files or directories from the host into the file system that
+ is created due to the <varname>Format=</varname> option. If <varname>CopyFiles=</varname> is used
+ without <varname>Format=</varname> specified explicitly, <literal>Format=</literal> with a suitable
+ default is implied (currently <literal>vfat</literal> for <literal>ESP</literal> and
+ <literal>XBOOTLDR</literal> partitions, and <literal>ext4</literal> otherwise, but this may change in
+ the future). This option may be used multiple times to copy multiple files or directories from host
+ into the newly formatted file system.</para>
+
+ <para>The <varname>target</varname> path may be omitted in which case the <varname>source</varname>
+ path is also used as the target path (relative to the root of the newly created file system). If
+ the source path refers to a directory it is copied recursively.</para>
+
+ <para>The <varname>options</varname> may contain the following values:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>fsverity=</varname></term>
+ <listitem><para>May be set to the value <literal>off</literal> (the default if the option is not
+ present) or <literal>copy</literal>. If set to <literal>off</literal> then no files copied into
+ the filesystem from this source will have fs-verity enabled. If set to <literal>copy</literal>
+ then the fs-verity information for each file will be copied from the corresponding source
+ file.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+
+ </varlistentry>
+ </variablelist>
<para>This option has no effect if the partition already exists: it cannot be used to copy additional
files into an existing partition, it may only be used to populate a file system created anew.</para>
typedef struct CopyFiles {
char *source;
char *target;
+ CopyFlags flags;
} CopyFiles;
static void copy_files_free_many(CopyFiles *f, size_t n) {
void *data,
void *userdata) {
- _cleanup_free_ char *source = NULL, *buffer = NULL, *resolved_source = NULL, *resolved_target = NULL;
+ _cleanup_free_ char *source = NULL, *buffer = NULL, *resolved_source = NULL, *resolved_target = NULL, *options = NULL;
Partition *partition = ASSERT_PTR(data);
const char *p = rvalue, *target;
int r;
else
target = buffer;
+ r = extract_first_word(&p, &options, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract options: %s", rvalue);
+
if (!isempty(p))
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Too many arguments: %s", rvalue);
+ CopyFlags flags = COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS;
+ for (const char *opts = options;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *val;
+
+ r = extract_first_word(&opts, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CopyFile options: %s", options);
+ if (r == 0)
+ break;
+
+ if (isempty(word))
+ continue;
+
+ if ((val = startswith(word, "fsverity="))) {
+ if (streq(val, "copy"))
+ flags |= COPY_PRESERVE_FS_VERITY;
+ else if (streq(val, "off"))
+ flags &= ~COPY_PRESERVE_FS_VERITY;
+ else
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "fsverity= expects either 'off' or 'copy'.");
+ } else
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Encountered unknown option '%s', ignoring.", word);
+ }
+
r = specifier_printf(source, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_source);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
partition->copy_files[partition->n_copy_files++] = (CopyFiles) {
.source = TAKE_PTR(resolved_source),
.target = TAKE_PTR(resolved_target),
+ .flags = flags,
};
return 0;
if (streq(p->format, "erofs") && !DEBUG_LOGGING)
flags |= MKFS_QUIET;
+ FOREACH_ARRAY(cf, p->copy_files, p->n_copy_files)
+ if (cf->flags & COPY_PRESERVE_FS_VERITY) {
+ flags |= MKFS_FS_VERITY;
+ break;
+ }
+
return flags;
}
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
+ line->flags,
denylist, subvolumes_by_source_inode);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
- COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
+ line->flags,
denylist, subvolumes_by_source_inode);
if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",