In mkosi, I want to add a sysupdate verb to wrap systemd-sysupdate.
The definitions will be picked up from mkosi.sysupdate/ and passed
to systemd-sysupdate. I want users to be able to write transfer
definitions that are independent of the output directory used by
mkosi. To make this possible, it should be possible to specify the
directory that transfer sources should be looked up in on the sysupdate
command line. Let's allow this via a new --transfer-source= option.
Additionally, transfer sources that want to take advantage of this
feature should specify PathRelativeTo=directory to indicate the configured
Path= is interpreted relative to the tranfer source directory specified
on the CLI.
This allows for the following transfer definition to be put in
mkosi.sysupdate:
"""
[Transfer]
ProtectVersion=%A
[Source]
Type=regular-file
Path=/
PathRelativeTo=directory
MatchPattern=ParticleOS_@v.usr-%a.@u.raw
[Target]
Type=partition
Path=auto
MatchPattern=ParticleOS_@v
MatchPartitionType=usr
PartitionFlags=0
ReadOnly=1
"""
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--transfer-source=</option></term>
+
+ <listitem><para>Takes a path as its argument. When specified, all transfer sources configured with
+ <varname>PathRelativeTo=explicit</varname> will be interpreted relative to the specified path.</para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
<varlistentry>
<term><varname>PathRelativeTo=</varname></term>
- <listitem><para>Specifies what partition <varname>Path=</varname> should be relative to. Takes one of
- <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>, or <constant>boot</constant>.
- If unspecified, defaults to <constant>root</constant>.</para>
+ <listitem><para>Specifies what anchor point <varname>Path=</varname> should be relative to. Takes one
+ of <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>,
+ <constant>boot</constant> or <constant>directory</constant>. If unspecified, defaults to
+ <constant>root</constant>.</para>
+
+ <para>If set to <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>,
+ the specified <varname>Path=</varname> will be resolved relative to the mount point of the
+ corresponding partition, as defined by the
+ <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
+ Specification</ulink>.</para>
<para>If set to <constant>boot</constant>, the specified <varname>Path=</varname> will be resolved
relative to the mount point of the $BOOT partition (i.e. the ESP or XBOOTLDR), as defined by the
<ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader
Specification</ulink>.</para>
+ <para>If set to <constant>explicit</constant>, the specified <varname>Path=</varname> will be
+ resolved relative to the directory specified with <option>--transfer-source=</option> when invoking
+ <command>systemd-sysupdate</command>.</para>
+
<para>The values <constant>esp</constant>, <constant>xbootldr</constant>, and
<constant>boot</constant> are only supported when <varname>Type=</varname> is set to
<constant>regular-file</constant> or <constant>directory</constant>.</para>
int resource_resolve_path(
Resource *rr,
const char *root,
+ const char *relative_to_directory,
const char *node) {
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *resolved = NULL, *relative_to = NULL;
ChaseFlags chase_flags = CHASE_PREFIX_ROOT;
- if (rr->path_relative_to == PATH_RELATIVE_TO_ROOT) {
+ if (rr->path_relative_to == PATH_RELATIVE_TO_EXPLICIT) {
+ assert(relative_to_directory);
+
+ relative_to = strdup(relative_to_directory);
+ if (!relative_to)
+ return log_oom();
+ } else if (rr->path_relative_to == PATH_RELATIVE_TO_ROOT) {
relative_to = strdup(empty_to_root(root));
if (!relative_to)
return log_oom();
[PATH_RELATIVE_TO_ESP] = "esp",
[PATH_RELATIVE_TO_XBOOTLDR] = "xbootldr",
[PATH_RELATIVE_TO_BOOT] = "boot",
+ [PATH_RELATIVE_TO_EXPLICIT] = "explicit",
};
DEFINE_STRING_TABLE_LOOKUP(path_relative_to, PathRelativeTo);
PATH_RELATIVE_TO_ESP,
PATH_RELATIVE_TO_XBOOTLDR,
PATH_RELATIVE_TO_BOOT, /* Refers to $BOOT from the BLS. No direct counterpart in PartitionDesignator */
+ PATH_RELATIVE_TO_EXPLICIT,
_PATH_RELATIVE_TO_MAX,
_PATH_RELATIVE_TO_INVALID = -EINVAL,
} PathRelativeTo;
Instance* resource_find_instance(Resource *rr, const char *version);
-int resource_resolve_path(Resource *rr, const char *root, const char *node);
+int resource_resolve_path(Resource *rr, const char *root, const char *relative_to_directory, const char *node);
ResourceType resource_type_from_string(const char *s) _pure_;
const char* resource_type_to_string(ResourceType t) _const_;
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Source specification lacks Path=.");
+ if (t->source.path_relative_to == PATH_RELATIVE_TO_EXPLICIT && !arg_transfer_source)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "PathRelativeTo=explicit requires --transfer-source= to be specified.");
+
+ if (t->target.path_relative_to == PATH_RELATIVE_TO_EXPLICIT)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "PathRelativeTo=explicit can only be used in source specifications.");
+
if (t->source.path) {
if (RESOURCE_IS_FILESYSTEM(t->source.type) || t->source.type == RESOURCE_PARTITION)
if (!path_is_absolute(t->source.path) || !path_is_normalized(t->source.path))
assert(t);
- r = resource_resolve_path(&t->source, root, node);
+ r = resource_resolve_path(&t->source, root, arg_transfer_source, node);
if (r < 0)
return r;
- r = resource_resolve_path(&t->target, root, node);
+ r = resource_resolve_path(&t->target, root, /*relative_to_directory=*/ NULL, node);
if (r < 0)
return r;
static int arg_verify = -1;
static ImagePolicy *arg_image_policy = NULL;
static bool arg_offline = false;
+char *arg_transfer_source = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_component, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_transfer_source, freep);
typedef struct Context {
Transfer **transfers;
" --no-legend Do not show the headers and footers\n"
" --json=pretty|short|off\n"
" Generate JSON output\n"
+ " --transfer-source=PATH\n"
+ " Specify the directory to transfer sources from\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
ARG_REBOOT,
ARG_VERIFY,
ARG_OFFLINE,
+ ARG_TRANSFER_SOURCE,
};
static const struct option options[] = {
{ "component", required_argument, NULL, 'C' },
{ "verify", required_argument, NULL, ARG_VERIFY },
{ "offline", no_argument, NULL, ARG_OFFLINE },
+ { "transfer-source", required_argument, NULL, ARG_TRANSFER_SOURCE },
{}
};
arg_offline = true;
break;
+ case ARG_TRANSFER_SOURCE:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_transfer_source);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;
extern bool arg_sync;
extern uint64_t arg_instances_max;
extern char *arg_root;
+extern char *arg_transfer_source;