repart: add new --copy-source= switch

This specifies a directory to which CopyFiles= is considered relative.
If unset defaults to the --root=/--image= setting, or host / otherwise.

This is very similar to --root= but is much more focussed: it is really
and exclusively about CopyFiles= (and related settings such as
ExcludeFiles=) and does not affect any of the settings, i.e. it doesn't
affect CopyBlocks=, the machine ID/seed handling, or where definitions
are read from.

In fact, --root= and --copy-source= may be combined for example to
use the machine ID and similar from one tree, but the copy the files
from another.
This commit is contained in:
Lennart Poettering 2023-09-29 16:24:48 +02:00
parent 248f0186c1
commit 607343a1ac
3 changed files with 50 additions and 15 deletions

View file

@ -468,10 +468,11 @@
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para> <para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
<para>When <para>When
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
is invoked with the <option>--image=</option> or <option>--root=</option> command line switches the invoked with the <option>--copy-source=</option> command line switch the file paths are taken
source paths specified are taken relative to the specified root directory or disk image root. relative to the specified directory. If <option>--copy-source=</option> is not used, but the
</para> <option>--image=</option> or <option>--root=</option> switches are used, the source paths are taken
relative to the specified root directory or disk image root.</para>
<xi:include href="version-info.xml" xpointer="v247"/></listitem> <xi:include href="version-info.xml" xpointer="v247"/></listitem>
</varlistentry> </varlistentry>

View file

@ -276,6 +276,9 @@
so that the tool operates on the configuration and machine ID stored in the root file system later so that the tool operates on the configuration and machine ID stored in the root file system later
transitioned into itself.</para> transitioned into itself.</para>
<para>See <option>--copy-source=</option> for a more restricted option that only affects
<varname>CopyFiles=</varname>.</para>
<xi:include href="version-info.xml" xpointer="v245"/></listitem> <xi:include href="version-info.xml" xpointer="v245"/></listitem>
</varlistentry> </varlistentry>
@ -500,6 +503,19 @@
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--copy-source=</option><replaceable>PATH</replaceable></term>
<term><option>-s</option> <replaceable>PATH</replaceable></term>
<listitem><para>Specifies a source directory all <varname>CopyFiles=</varname> source paths shall be
considered relative to. This is similar to <option>--root=</option>, but exclusively applies to the
<varname>CopyFiles=</varname> setting. If <option>--root=</option> and
<option>--copy-source=</option> are used in combination the former applies as usual, except for
<varname>CopyFiles=</varname> where the latter takes precedence.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" /> <xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" /> <xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" /> <xi:include href="standard-options.xml" xpointer="no-pager" />

View file

@ -162,6 +162,7 @@ static ImagePolicy *arg_image_policy = NULL;
static Architecture arg_architecture = _ARCHITECTURE_INVALID; static Architecture arg_architecture = _ARCHITECTURE_INVALID;
static int arg_offline = -1; static int arg_offline = -1;
static char **arg_copy_from = NULL; static char **arg_copy_from = NULL;
static char *arg_copy_source = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@ -175,6 +176,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_copy_source, freep);
typedef struct FreeArea FreeArea; typedef struct FreeArea FreeArea;
@ -4274,11 +4276,11 @@ static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type)
if (!st) if (!st)
return log_oom(); return log_oom();
r = chase_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st); r = chase_and_stat(path, arg_copy_source, CHASE_PREFIX_ROOT, NULL, st);
if (r == -ENOENT) if (r == -ENOENT)
return 0; return 0;
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_root), path); return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_copy_source), path);
r = hashmap_ensure_put(denylist, &inode_hash_ops, st, INT_TO_PTR(type)); r = hashmap_ensure_put(denylist, &inode_hash_ops, st, INT_TO_PTR(type));
if (r == -EEXIST) if (r == -EEXIST)
@ -4402,11 +4404,11 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
if (!st) if (!st)
return log_oom(); return log_oom();
r = chase_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st); r = chase_and_stat(path, arg_copy_source, CHASE_PREFIX_ROOT, NULL, st);
if (r == -ENOENT) if (r == -ENOENT)
return 0; return 0;
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_root), path); return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_copy_source), path);
r = set_ensure_consume(subvolumes, &inode_hash_ops, TAKE_PTR(st)); r = set_ensure_consume(subvolumes, &inode_hash_ops, TAKE_PTR(st));
if (r < 0) if (r < 0)
@ -4469,9 +4471,9 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
if (rfd < 0) if (rfd < 0)
return rfd; return rfd;
sfd = chase_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL); sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
if (sfd < 0) if (sfd < 0)
return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source); return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_copy_source), *source);
(void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS); (void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS);
(void) copy_access(sfd, rfd); (void) copy_access(sfd, rfd);
@ -4493,9 +4495,9 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
if (r < 0) if (r < 0)
return r; return r;
sfd = chase_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL); sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL);
if (sfd < 0) if (sfd < 0)
return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source); return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_copy_source), *source);
r = fd_verify_regular(sfd); r = fd_verify_regular(sfd);
if (r < 0) { if (r < 0) {
@ -4541,7 +4543,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
denylist, subvolumes_by_source_inode); denylist, subvolumes_by_source_inode);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m", return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
strempty(arg_root), *source, strempty(root), *target); strempty(arg_copy_source), *source, strempty(root), *target);
} else { } else {
_cleanup_free_ char *dn = NULL, *fn = NULL; _cleanup_free_ char *dn = NULL, *fn = NULL;
@ -4572,7 +4574,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE); r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target); return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_copy_source), *target);
(void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS); (void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS);
(void) copy_access(sfd, tfd); (void) copy_access(sfd, tfd);
@ -6372,6 +6374,7 @@ static int help(void) {
" --sector-size=SIZE Set the logical sector size for the image\n" " --sector-size=SIZE Set the logical sector size for the image\n"
" --architecture=ARCH Set the generic architecture for the image\n" " --architecture=ARCH Set the generic architecture for the image\n"
" --offline=BOOL Whether to build the image offline\n" " --offline=BOOL Whether to build the image offline\n"
" -s --copy-source=PATH Specify the primary source tree to copy files from\n"
" --copy-from=IMAGE Copy partitions from the given image(s)\n" " --copy-from=IMAGE Copy partitions from the given image(s)\n"
"\nSee the %s for details.\n", "\nSee the %s for details.\n",
program_invocation_short_name, program_invocation_short_name,
@ -6417,6 +6420,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ARCHITECTURE, ARG_ARCHITECTURE,
ARG_OFFLINE, ARG_OFFLINE,
ARG_COPY_FROM, ARG_COPY_FROM,
ARG_COPY_SOURCE,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -6452,6 +6456,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "architecture", required_argument, NULL, ARG_ARCHITECTURE }, { "architecture", required_argument, NULL, ARG_ARCHITECTURE },
{ "offline", required_argument, NULL, ARG_OFFLINE }, { "offline", required_argument, NULL, ARG_OFFLINE },
{ "copy-from", required_argument, NULL, ARG_COPY_FROM }, { "copy-from", required_argument, NULL, ARG_COPY_FROM },
{ "copy-source", required_argument, NULL, 's' },
{} {}
}; };
@ -6460,7 +6465,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) while ((c = getopt_long(argc, argv, "hs:", options, NULL)) >= 0)
switch (c) { switch (c) {
@ -6785,6 +6790,12 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
} }
case 's':
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_copy_source);
if (r < 0)
return r;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -7345,6 +7356,13 @@ static int run(int argc, char *argv[]) {
} }
} }
if (!arg_copy_source && arg_root) {
/* If no explicit copy source is specified, then use --root=/--image= */
arg_copy_source = strdup(arg_root);
if (!arg_copy_source)
return log_oom();
}
context = context_new(arg_seed); context = context_new(arg_seed);
if (!context) if (!context)
return log_oom(); return log_oom();