Compare commits
34 Commits
d7beed864b
...
cbff6ec2d4
Author | SHA1 | Date |
---|---|---|
Mike Yuan | cbff6ec2d4 | |
Daan De Meyer | 099b16c3e7 | |
Daan De Meyer | 7a7f306b6c | |
Yu Watanabe | 4f2975385f | |
Daan De Meyer | 0432e28394 | |
Yu Watanabe | fc956a3973 | |
Yu Watanabe | b0dbb4aa3a | |
Michael Ferrari | 91ea3dcf35 | |
Yu Watanabe | a95ae2d36a | |
Yu Watanabe | be8e4b1a87 | |
Adrian Vovk | cf612c5fd5 | |
Adrian Vovk | 2cb9c68c3a | |
Adrian Vovk | 78e9059208 | |
Adrian Vovk | e671bdc5c3 | |
Yu Watanabe | 572d031eca | |
Yu Watanabe | 25da422bd1 | |
Yu Watanabe | 5872ea7008 | |
Lennart Poettering | a2369d0224 | |
Lennart Poettering | a37640653c | |
Mike Yuan | c77389e0c1 | |
Mike Yuan | 48cd3fe288 | |
Mike Yuan | 4b03d67466 | |
Mike Yuan | e417906b35 | |
Mike Yuan | 381d68ed39 | |
Mike Yuan | 4e534c9e62 | |
Mike Yuan | 452b13c3d4 | |
Mike Yuan | b47ab3f35b | |
Mike Yuan | 51d32424e8 | |
Mike Yuan | 25b99eb47d | |
Mike Yuan | f8e666759d | |
Mike Yuan | ee0c4f94d2 | |
Mike Yuan | 8300fa4dcf | |
Mike Yuan | 1c4c387071 | |
Mike Yuan | 3e40d55f09 |
2
TODO
2
TODO
|
@ -189,6 +189,8 @@ Features:
|
||||||
* go through our codebase, and convert "vertical tables" (i.e. things such as
|
* go through our codebase, and convert "vertical tables" (i.e. things such as
|
||||||
"systemctl status") to use table_new_vertical() for output
|
"systemctl status") to use table_new_vertical() for output
|
||||||
|
|
||||||
|
* pcrlock: add support for multi-profile UKIs
|
||||||
|
|
||||||
* logind: when logging in use new tmpfs quota support to configure quota on
|
* logind: when logging in use new tmpfs quota support to configure quota on
|
||||||
/tmp/ + /dev/shm/. But do so only in case of tmpfs, because otherwise quota
|
/tmp/ + /dev/shm/. But do so only in case of tmpfs, because otherwise quota
|
||||||
is persistent and any persistent settings mean we don#t have to reapply them.
|
is persistent and any persistent settings mean we don#t have to reapply them.
|
||||||
|
|
|
@ -76,16 +76,7 @@
|
||||||
<term><varname>Type=</varname></term>
|
<term><varname>Type=</varname></term>
|
||||||
|
|
||||||
<listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
|
<listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
|
||||||
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.
|
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.</para>
|
||||||
Architecture specific partition types can use one of these architecture identifiers:
|
|
||||||
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
|
|
||||||
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
|
|
||||||
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
|
|
||||||
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
|
|
||||||
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
|
|
||||||
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
|
|
||||||
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The supported identifiers are:</para>
|
<para>The supported identifiers are:</para>
|
||||||
|
|
||||||
|
@ -237,7 +228,14 @@
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<para>This setting defaults to <constant>linux-generic</constant>.</para>
|
<para>Architecture specific partition types can use one of these architecture identifiers:
|
||||||
|
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
|
||||||
|
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
|
||||||
|
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
|
||||||
|
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
|
||||||
|
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
|
||||||
|
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
|
||||||
|
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).</para>
|
||||||
|
|
||||||
<para>Most of the partition type UUIDs listed above are defined in the <ulink
|
<para>Most of the partition type UUIDs listed above are defined in the <ulink
|
||||||
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions
|
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions
|
||||||
|
@ -897,6 +895,59 @@
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>SupplementFor=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a partition definition name, such as <literal>10-esp</literal>. If specified,
|
||||||
|
<command>systemd-repart</command> will avoid creating this partition and instead prefer to partially
|
||||||
|
merge the two definitions. However, depending on the existing layout of partitions on disk,
|
||||||
|
<command>systemd-repart</command> may be forced to fall back onto un-merging the definitions and
|
||||||
|
using them as originally written, potentially creating this partition. Specifically,
|
||||||
|
<command>systemd-repart</command> will fall back if this partition is found to already exist on disk,
|
||||||
|
or if the target partition already exists on disk but is too small, or if it cannot allocate space
|
||||||
|
for the merged partition for some other reason.</para>
|
||||||
|
|
||||||
|
<para>The following fields are merged into the target definition in the specified ways:
|
||||||
|
<varname>Weight=</varname> and <varname>PaddingWeight=</varname> are simply overwritten;
|
||||||
|
<varname>SizeMinBytes=</varname> and <varname>PaddingMinBytes=</varname> use the larger of the two
|
||||||
|
values; <varname>SizeMaxBytes=</varname> and <varname>PaddingMaxBytes=</varname> use the smaller
|
||||||
|
value; and <varname>CopyFiles=</varname>, <varname>ExcludeFiles=</varname>,
|
||||||
|
<varname>ExcludeFilesTarget=</varname>, <varname>MakeDirectories=</varname>, and
|
||||||
|
<varname>Subvolumes=</varname> are concatenated.</para>
|
||||||
|
|
||||||
|
<para>Usage of this option in combination with <varname>CopyBlocks=</varname>,
|
||||||
|
<varname>Encrypt=</varname>, or <varname>Verity=</varname> is not supported. The target definition
|
||||||
|
cannot set these settings either. A definition cannot simultaneously be a supplement and act as a
|
||||||
|
target for some other supplement definition. A target cannot have more than one supplement partition
|
||||||
|
associated with it.</para>
|
||||||
|
|
||||||
|
<para>For example, distributions can use this to implement <variable>$BOOT</variable> as defined in
|
||||||
|
the <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/">Boot Loader
|
||||||
|
Specification</ulink>. Distributions may prefer to use the ESP as <variable>$BOOT</variable> whenever
|
||||||
|
possible, but to adhere to the spec XBOOTLDR must sometimes be used instead. So, they should create
|
||||||
|
two definitions: the first defining an ESP big enough to hold just the bootloader, and a second for
|
||||||
|
the XBOOTLDR that's sufficiently large to hold kernels and configured as a supplement for the ESP.
|
||||||
|
Whenever possible, <command>systemd-repart</command> will try to merge the two definitions to create
|
||||||
|
one large ESP, but if that's not allowable due to the existing conditions on disk a small ESP and a
|
||||||
|
large XBOOTLDR will be created instead.</para>
|
||||||
|
|
||||||
|
<para>As another example, distributions can also use this to seamlessly share a single
|
||||||
|
<filename>/home</filename> partition in a multi-boot scenario, while preferring to keep
|
||||||
|
<filename>/home</filename> on the root partition by default. Having a <filename>/home</filename>
|
||||||
|
partition separated from the root partition entails some extra complexity: someone has to decide how
|
||||||
|
to split the space between the two partitions. On the other hand, it allows a user to share their
|
||||||
|
home area between multiple installed OSs (i.e. via <citerefentry><refentrytitle>systemd-homed.service
|
||||||
|
</refentrytitle><manvolnum>8</manvolnum></citerefentry>). Distributions should create two definitions:
|
||||||
|
the first for a root partition that takes up some relatively small percentage of the disk, and the
|
||||||
|
second as a supplement for the first to create a <filename>/home</filename> partition that takes up
|
||||||
|
all the remaining free space. On first boot, if <command>systemd-repart</command> finds an existing
|
||||||
|
<filename>/home</filename> partition on disk, it'll un-merge the definitions and create just a small
|
||||||
|
root partition. Otherwise, the definitions will be merged and a single large root partition will be
|
||||||
|
created.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
|
|
@ -469,3 +469,30 @@ int conf_file_read(
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int find_portable_profile(const char *name, const char *unit, char **ret_path) {
|
||||||
|
const char *dot;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
assert(ret_path);
|
||||||
|
|
||||||
|
assert_se(dot = strrchr(unit, '.'));
|
||||||
|
|
||||||
|
NULSTR_FOREACH(p, PORTABLE_PROFILE_DIRS) {
|
||||||
|
_cleanup_free_ char *joined = NULL;
|
||||||
|
|
||||||
|
joined = strjoin(p, "/", name, "/", dot + 1, ".conf");
|
||||||
|
if (!joined)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (laccess(joined, F_OK) >= 0) {
|
||||||
|
*ret_path = TAKE_PTR(joined);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != ENOENT)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
|
@ -47,3 +47,9 @@ int conf_file_read(
|
||||||
void *userdata,
|
void *userdata,
|
||||||
bool ignore_enoent,
|
bool ignore_enoent,
|
||||||
bool *invalid_config);
|
bool *invalid_config);
|
||||||
|
|
||||||
|
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
|
||||||
|
#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
|
||||||
|
|
||||||
|
#define PORTABLE_PROFILE_DIRS CONF_PATHS_NULSTR("systemd/portable/profile")
|
||||||
|
int find_portable_profile(const char *name, const char *unit, char **ret_path);
|
||||||
|
|
|
@ -300,9 +300,10 @@ int log_emergency_level(void);
|
||||||
#define log_dump(level, buffer) \
|
#define log_dump(level, buffer) \
|
||||||
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
|
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
|
||||||
|
|
||||||
#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__)
|
#define log_oom_full(level) log_oom_internal(level, PROJECT_FILE, __LINE__, __func__)
|
||||||
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__)
|
#define log_oom() log_oom_full(LOG_ERR)
|
||||||
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__)
|
#define log_oom_debug() log_oom_full(LOG_DEBUG)
|
||||||
|
#define log_oom_warning() log_oom_full(LOG_WARNING)
|
||||||
|
|
||||||
bool log_on_console(void) _pure_;
|
bool log_on_console(void) _pure_;
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,6 @@ basic_sources = files(
|
||||||
'ordered-set.c',
|
'ordered-set.c',
|
||||||
'os-util.c',
|
'os-util.c',
|
||||||
'parse-util.c',
|
'parse-util.c',
|
||||||
'path-lookup.c',
|
|
||||||
'path-util.c',
|
'path-util.c',
|
||||||
'percent-util.c',
|
'percent-util.c',
|
||||||
'pidref.c',
|
'pidref.c',
|
||||||
|
|
|
@ -153,7 +153,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
|
||||||
_STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
|
_STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
|
||||||
|
|
||||||
#define _STRV_FOREACH_PAIR(x, y, l, i) \
|
#define _STRV_FOREACH_PAIR(x, y, l, i) \
|
||||||
for (typeof(*l) *x, *y, *i = (l); \
|
for (typeof(*(l)) *x, *y, *i = (l); \
|
||||||
i && *(x = i) && *(y = i + 1); \
|
i && *(x = i) && *(y = i + 1); \
|
||||||
i += 2)
|
i += 2)
|
||||||
|
|
||||||
|
|
|
@ -255,6 +255,25 @@ int ask_string(char **ret, const char *text, ...) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool any_key_to_proceed(void) {
|
||||||
|
char key = 0;
|
||||||
|
bool need_nl = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a new line here as well as to when the user inputs, as this is also used during the
|
||||||
|
* boot up sequence when status messages may be interleaved with the current program output.
|
||||||
|
* This ensures that the status messages aren't appended on the same line as this message.
|
||||||
|
*/
|
||||||
|
puts("-- Press any key to proceed --");
|
||||||
|
|
||||||
|
(void) read_one_char(stdin, &key, USEC_INFINITY, &need_nl);
|
||||||
|
|
||||||
|
if (need_nl)
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
return key != 'q';
|
||||||
|
}
|
||||||
|
|
||||||
int open_terminal(const char *name, int mode) {
|
int open_terminal(const char *name, int mode) {
|
||||||
_cleanup_close_ int fd = -EBADF;
|
_cleanup_close_ int fd = -EBADF;
|
||||||
unsigned c = 0;
|
unsigned c = 0;
|
||||||
|
|
|
@ -78,6 +78,7 @@ int chvt(int vt);
|
||||||
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
|
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
|
||||||
int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
|
int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
|
||||||
int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
|
int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
|
||||||
|
bool any_key_to_proceed(void);
|
||||||
|
|
||||||
int vt_disallocate(const char *name);
|
int vt_disallocate(const char *name);
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) {
|
||||||
_cleanup_strv_free_ char **files = NULL, **dirs = NULL;
|
_cleanup_strv_free_ char **files = NULL, **dirs = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = xdg_user_config_dir(&base, "/systemd");
|
r = xdg_user_config_dir("/systemd", &base);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -2478,7 +2478,7 @@ static int initialize_runtime(
|
||||||
/* Create the runtime directory and place the inaccessible device nodes there, if we run in
|
/* Create the runtime directory and place the inaccessible device nodes there, if we run in
|
||||||
* user mode. In system mode mount_setup() already did that. */
|
* user mode. In system mode mount_setup() already did that. */
|
||||||
|
|
||||||
r = xdg_user_runtime_dir(&p, "/systemd");
|
r = xdg_user_runtime_dir("/systemd", &base);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*ret_error_message = "$XDG_RUNTIME_DIR is not set";
|
*ret_error_message = "$XDG_RUNTIME_DIR is not set";
|
||||||
return log_struct_errno(LOG_EMERG, r,
|
return log_struct_errno(LOG_EMERG, r,
|
||||||
|
|
|
@ -1031,7 +1031,7 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags,
|
||||||
r = mkdir_label("/run/systemd/units", 0755);
|
r = mkdir_label("/run/systemd/units", 0755);
|
||||||
else {
|
else {
|
||||||
_cleanup_free_ char *units_path = NULL;
|
_cleanup_free_ char *units_path = NULL;
|
||||||
r = xdg_user_runtime_dir(&units_path, "/systemd/units");
|
r = xdg_user_runtime_dir("/systemd/units", &units_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -5576,12 +5576,13 @@ static int unit_get_invocation_path(Unit *u, char **ret) {
|
||||||
p = strjoin("/run/systemd/units/invocation:", u->id);
|
p = strjoin("/run/systemd/units/invocation:", u->id);
|
||||||
else {
|
else {
|
||||||
_cleanup_free_ char *user_path = NULL;
|
_cleanup_free_ char *user_path = NULL;
|
||||||
r = xdg_user_runtime_dir(&user_path, "/systemd/units/invocation:");
|
|
||||||
|
r = xdg_user_runtime_dir("/systemd/units/invocation:", &user_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
p = strjoin(user_path, u->id);
|
p = strjoin(user_path, u->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p)
|
if (!p)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -93,20 +93,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||||
|
|
||||||
static bool press_any_key(void) {
|
|
||||||
char k = 0;
|
|
||||||
bool need_nl = true;
|
|
||||||
|
|
||||||
puts("-- Press any key to proceed --");
|
|
||||||
|
|
||||||
(void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
|
|
||||||
|
|
||||||
if (need_nl)
|
|
||||||
putchar('\n');
|
|
||||||
|
|
||||||
return k != 'q';
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_welcome(int rfd) {
|
static void print_welcome(int rfd) {
|
||||||
_cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL;
|
_cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL;
|
||||||
static bool done = false;
|
static bool done = false;
|
||||||
|
@ -141,7 +127,7 @@ static void print_welcome(int rfd) {
|
||||||
|
|
||||||
printf("\nPlease configure your system!\n\n");
|
printf("\nPlease configure your system!\n\n");
|
||||||
|
|
||||||
press_any_key();
|
any_key_to_proceed();
|
||||||
|
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
@ -184,7 +170,7 @@ static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned perc
|
||||||
|
|
||||||
/* on the first screen we reserve 2 extra lines for the title */
|
/* on the first screen we reserve 2 extra lines for the title */
|
||||||
if (i % break_lines == break_modulo) {
|
if (i % break_lines == break_modulo) {
|
||||||
if (!press_any_key())
|
if (!any_key_to_proceed())
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2434,6 +2434,8 @@ static int create_interactively(void) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
any_key_to_proceed();
|
||||||
|
|
||||||
r = acquire_bus(&bus);
|
r = acquire_bus(&bus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -44,7 +44,7 @@ sd_journal_sources += [audit_type_to_name]
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
id128_sources = files(
|
sd_id128_sources = files(
|
||||||
'sd-id128/id128-util.c',
|
'sd-id128/id128-util.c',
|
||||||
'sd-id128/sd-id128.c',
|
'sd-id128/sd-id128.c',
|
||||||
)
|
)
|
||||||
|
@ -62,6 +62,40 @@ sd_event_sources = files(
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
|
sd_bus_sources = files(
|
||||||
|
'sd-bus/bus-common-errors.c',
|
||||||
|
'sd-bus/bus-container.c',
|
||||||
|
'sd-bus/bus-control.c',
|
||||||
|
'sd-bus/bus-convenience.c',
|
||||||
|
'sd-bus/bus-creds.c',
|
||||||
|
'sd-bus/bus-dump.c',
|
||||||
|
'sd-bus/bus-error.c',
|
||||||
|
'sd-bus/bus-internal.c',
|
||||||
|
'sd-bus/bus-introspect.c',
|
||||||
|
'sd-bus/bus-kernel.c',
|
||||||
|
'sd-bus/bus-match.c',
|
||||||
|
'sd-bus/bus-message.c',
|
||||||
|
'sd-bus/bus-objects.c',
|
||||||
|
'sd-bus/bus-signature.c',
|
||||||
|
'sd-bus/bus-slot.c',
|
||||||
|
'sd-bus/bus-socket.c',
|
||||||
|
'sd-bus/bus-track.c',
|
||||||
|
'sd-bus/bus-type.c',
|
||||||
|
'sd-bus/sd-bus.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sd_device_sources = files(
|
||||||
|
'sd-device/device-enumerator.c',
|
||||||
|
'sd-device/device-filter.c',
|
||||||
|
'sd-device/device-monitor.c',
|
||||||
|
'sd-device/device-private.c',
|
||||||
|
'sd-device/device-util.c',
|
||||||
|
'sd-device/sd-device.c',
|
||||||
|
)
|
||||||
|
############################################################
|
||||||
|
|
||||||
sd_login_sources = files('sd-login/sd-login.c')
|
sd_login_sources = files('sd-login/sd-login.c')
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
@ -83,33 +117,14 @@ sd_varlink_sources = files(
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
libsystemd_sources = files(
|
sd_path_sources = files(
|
||||||
'sd-bus/bus-common-errors.c',
|
'sd-path/path-lookup.c',
|
||||||
'sd-bus/bus-container.c',
|
'sd-path/sd-path.c',
|
||||||
'sd-bus/bus-control.c',
|
)
|
||||||
'sd-bus/bus-convenience.c',
|
|
||||||
'sd-bus/bus-creds.c',
|
############################################################
|
||||||
'sd-bus/bus-dump.c',
|
|
||||||
'sd-bus/bus-error.c',
|
sd_netlink_sources = files(
|
||||||
'sd-bus/bus-internal.c',
|
|
||||||
'sd-bus/bus-introspect.c',
|
|
||||||
'sd-bus/bus-kernel.c',
|
|
||||||
'sd-bus/bus-match.c',
|
|
||||||
'sd-bus/bus-message.c',
|
|
||||||
'sd-bus/bus-objects.c',
|
|
||||||
'sd-bus/bus-signature.c',
|
|
||||||
'sd-bus/bus-slot.c',
|
|
||||||
'sd-bus/bus-socket.c',
|
|
||||||
'sd-bus/bus-track.c',
|
|
||||||
'sd-bus/bus-type.c',
|
|
||||||
'sd-bus/sd-bus.c',
|
|
||||||
'sd-device/device-enumerator.c',
|
|
||||||
'sd-device/device-filter.c',
|
|
||||||
'sd-device/device-monitor.c',
|
|
||||||
'sd-device/device-private.c',
|
|
||||||
'sd-device/device-util.c',
|
|
||||||
'sd-device/sd-device.c',
|
|
||||||
'sd-hwdb/sd-hwdb.c',
|
|
||||||
'sd-netlink/netlink-genl.c',
|
'sd-netlink/netlink-genl.c',
|
||||||
'sd-netlink/netlink-message-nfnl.c',
|
'sd-netlink/netlink-message-nfnl.c',
|
||||||
'sd-netlink/netlink-message-rtnl.c',
|
'sd-netlink/netlink-message-rtnl.c',
|
||||||
|
@ -122,11 +137,24 @@ libsystemd_sources = files(
|
||||||
'sd-netlink/netlink-types.c',
|
'sd-netlink/netlink-types.c',
|
||||||
'sd-netlink/netlink-util.c',
|
'sd-netlink/netlink-util.c',
|
||||||
'sd-netlink/sd-netlink.c',
|
'sd-netlink/sd-netlink.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sd_network_sources = files(
|
||||||
'sd-network/network-util.c',
|
'sd-network/network-util.c',
|
||||||
'sd-network/sd-network.c',
|
'sd-network/sd-network.c',
|
||||||
'sd-path/sd-path.c',
|
)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
libsystemd_sources = files(
|
||||||
|
'sd-hwdb/sd-hwdb.c',
|
||||||
'sd-resolve/sd-resolve.c',
|
'sd-resolve/sd-resolve.c',
|
||||||
) + sd_journal_sources + id128_sources + sd_daemon_sources + sd_event_sources + sd_login_sources + sd_json_sources + sd_varlink_sources
|
) + sd_journal_sources + sd_id128_sources + sd_daemon_sources \
|
||||||
|
+ sd_event_sources + sd_bus_sources + sd_device_sources \
|
||||||
|
+ sd_login_sources + sd_json_sources + sd_varlink_sources \
|
||||||
|
+ sd_path_sources + sd_netlink_sources + sd_network_sources
|
||||||
|
|
||||||
libsystemd_c_args = ['-fvisibility=default']
|
libsystemd_c_args = ['-fvisibility=default']
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "constants.h"
|
#include "sd-path.h"
|
||||||
#include "macro.h"
|
|
||||||
#include "runtime-scope.h"
|
#include "runtime-scope.h"
|
||||||
|
|
||||||
typedef enum LookupPathsFlags {
|
typedef enum LookupPathsFlags {
|
||||||
|
@ -55,11 +55,21 @@ typedef struct LookupPaths {
|
||||||
int lookup_paths_init(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
int lookup_paths_init(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||||
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
int lookup_paths_init_or_warn(LookupPaths *lp, RuntimeScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||||
|
|
||||||
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
|
int runtime_directory(RuntimeScope scope, const char *suffix, char **ret);
|
||||||
int xdg_user_runtime_dir(char **ret, const char *suffix);
|
|
||||||
int xdg_user_config_dir(char **ret, const char *suffix);
|
/* We don't treat /etc/xdg/systemd in these functions as the xdg base dir spec suggests because we assume
|
||||||
int xdg_user_data_dir(char **ret, const char *suffix);
|
* that is a link to /etc/systemd/ anyway. */
|
||||||
int runtime_directory(char **ret, RuntimeScope scope, const char *suffix);
|
|
||||||
|
int user_search_dirs(const char *suffix, char ***ret_config_dirs, char ***ret_data_dirs);
|
||||||
|
static inline int xdg_user_runtime_dir(const char *suffix, char **ret) {
|
||||||
|
return sd_path_lookup(SD_PATH_USER_RUNTIME, suffix, ret);
|
||||||
|
}
|
||||||
|
static inline int xdg_user_config_dir(const char *suffix, char **ret) {
|
||||||
|
return sd_path_lookup(SD_PATH_USER_CONFIGURATION, suffix, ret);
|
||||||
|
}
|
||||||
|
static inline int xdg_user_data_dir(const char *suffix, char **ret) {
|
||||||
|
return sd_path_lookup(SD_PATH_USER_SHARED, suffix, ret);
|
||||||
|
}
|
||||||
|
|
||||||
bool path_is_user_data_dir(const char *path);
|
bool path_is_user_data_dir(const char *path);
|
||||||
bool path_is_user_config_dir(const char *path);
|
bool path_is_user_config_dir(const char *path);
|
||||||
|
@ -67,11 +77,10 @@ bool path_is_user_config_dir(const char *path);
|
||||||
void lookup_paths_log(LookupPaths *p);
|
void lookup_paths_log(LookupPaths *p);
|
||||||
void lookup_paths_done(LookupPaths *p);
|
void lookup_paths_done(LookupPaths *p);
|
||||||
|
|
||||||
char **generator_binary_paths(RuntimeScope scope);
|
char** generator_binary_paths_internal(RuntimeScope scope, bool env_generator);
|
||||||
char **env_generator_binary_paths(RuntimeScope scope);
|
static inline char** generator_binary_paths(RuntimeScope runtime_scope) {
|
||||||
|
return generator_binary_paths_internal(runtime_scope, false);
|
||||||
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
|
}
|
||||||
#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
|
static inline char** env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||||
|
return generator_binary_paths_internal(runtime_scope, true);
|
||||||
#define PORTABLE_PROFILE_DIRS CONF_PATHS_NULSTR("systemd/portable/profile")
|
}
|
||||||
int find_portable_profile(const char *name, const char *unit, char **ret_path);
|
|
|
@ -65,26 +65,20 @@ static int from_home_dir(const char *envname, const char *suffix, char **buffer,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
static int from_xdg_user_dir(const char *field, char **buffer, const char **ret) {
|
||||||
|
_cleanup_free_ char *user_dirs = NULL;
|
||||||
_cleanup_fclose_ FILE *f = NULL;
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
_cleanup_free_ char *b = NULL;
|
|
||||||
_cleanup_free_ const char *fn = NULL;
|
|
||||||
const char *c = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(field);
|
assert(field);
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
|
r = sd_path_lookup(SD_PATH_USER_CONFIGURATION, "user-dirs.dirs", &user_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
fn = path_join(c, "user-dirs.dirs");
|
f = fopen(user_dirs, "re");
|
||||||
if (!fn)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
f = fopen(fn, "re");
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
goto fallback;
|
goto fallback;
|
||||||
|
@ -107,14 +101,12 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
||||||
if (!p)
|
if (!p)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
p += strspn(p, WHITESPACE);
|
p = skip_leading_chars(p, WHITESPACE);
|
||||||
|
|
||||||
if (*p != '=')
|
if (*p != '=')
|
||||||
continue;
|
continue;
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
p += strspn(p, WHITESPACE);
|
p = skip_leading_chars(p, WHITESPACE);
|
||||||
|
|
||||||
if (*p != '"')
|
if (*p != '"')
|
||||||
continue;
|
continue;
|
||||||
p++;
|
p++;
|
||||||
|
@ -125,62 +117,34 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
|
||||||
*e = 0;
|
*e = 0;
|
||||||
|
|
||||||
/* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
|
/* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
|
||||||
if (startswith(p, "$HOME/")) {
|
if (streq(p, "$HOME"))
|
||||||
_cleanup_free_ char *h = NULL;
|
goto home;
|
||||||
|
|
||||||
r = get_home_dir(&h);
|
const char *s = startswith(p, "$HOME/");
|
||||||
if (r < 0)
|
if (s)
|
||||||
return r;
|
return from_home_dir(/* envname = */ NULL, s, buffer, ret);
|
||||||
|
|
||||||
if (!path_extend(&h, p+5))
|
if (path_is_absolute(p)) {
|
||||||
|
char *c = strdup(p);
|
||||||
|
if (!c)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*buffer = h;
|
*buffer = *ret = c;
|
||||||
*ret = TAKE_PTR(h);
|
|
||||||
return 0;
|
|
||||||
} else if (streq(p, "$HOME")) {
|
|
||||||
|
|
||||||
r = get_home_dir(buffer);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = *buffer;
|
|
||||||
return 0;
|
|
||||||
} else if (path_is_absolute(p)) {
|
|
||||||
char *copy;
|
|
||||||
|
|
||||||
copy = strdup(p);
|
|
||||||
if (!copy)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*buffer = copy;
|
|
||||||
*ret = copy;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fallback:
|
fallback:
|
||||||
/* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
|
/* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
|
||||||
if (streq(field, "XDG_DESKTOP_DIR")) {
|
if (streq(field, "XDG_DESKTOP_DIR"))
|
||||||
_cleanup_free_ char *h = NULL;
|
return from_home_dir(/* envname = */ NULL, "Desktop", buffer, ret);
|
||||||
|
|
||||||
r = get_home_dir(&h);
|
home:
|
||||||
if (r < 0)
|
r = get_home_dir(buffer);
|
||||||
return r;
|
if (r < 0)
|
||||||
|
return r;
|
||||||
if (!path_extend(&h, "Desktop"))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*buffer = h;
|
|
||||||
*ret = TAKE_PTR(h);
|
|
||||||
} else {
|
|
||||||
r = get_home_dir(buffer);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = *buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
*ret = *buffer;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,28 +251,28 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case SD_PATH_USER_DOCUMENTS:
|
case SD_PATH_USER_DOCUMENTS:
|
||||||
return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_MUSIC:
|
case SD_PATH_USER_MUSIC:
|
||||||
return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_MUSIC_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_PICTURES:
|
case SD_PATH_USER_PICTURES:
|
||||||
return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_PICTURES_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_VIDEOS:
|
case SD_PATH_USER_VIDEOS:
|
||||||
return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_VIDEOS_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_DOWNLOAD:
|
case SD_PATH_USER_DOWNLOAD:
|
||||||
return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_PUBLIC:
|
case SD_PATH_USER_PUBLIC:
|
||||||
return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_TEMPLATES:
|
case SD_PATH_USER_TEMPLATES:
|
||||||
return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_USER_DESKTOP:
|
case SD_PATH_USER_DESKTOP:
|
||||||
return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
|
return from_xdg_user_dir("XDG_DESKTOP_DIR", buffer, ret);
|
||||||
|
|
||||||
case SD_PATH_SYSTEMD_UTIL:
|
case SD_PATH_SYSTEMD_UTIL:
|
||||||
*ret = PREFIX_NOSLASH "/lib/systemd";
|
*ret = PREFIX_NOSLASH "/lib/systemd";
|
||||||
|
@ -390,55 +354,56 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
|
static int get_path_alloc(uint64_t type, const char *suffix, char **ret) {
|
||||||
_cleanup_free_ char *buffer = NULL;
|
_cleanup_free_ char *buffer = NULL;
|
||||||
char *buffer2 = NULL;
|
const char *p;
|
||||||
const char *ret;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(path);
|
assert(ret);
|
||||||
|
|
||||||
r = get_path(type, &buffer, &ret);
|
r = get_path(type, &buffer, &p);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (suffix) {
|
if (suffix) {
|
||||||
suffix += strspn(suffix, "/");
|
char *suffixed = path_join(p, suffix);
|
||||||
buffer2 = path_join(ret, suffix);
|
if (!suffixed)
|
||||||
if (!buffer2)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
path_simplify(suffixed);
|
||||||
|
|
||||||
|
free_and_replace(buffer, suffixed);
|
||||||
} else if (!buffer) {
|
} else if (!buffer) {
|
||||||
buffer = strdup(ret);
|
buffer = strdup(p);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
*path = buffer2 ?: TAKE_PTR(buffer);
|
*ret = TAKE_PTR(buffer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
|
_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **ret) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_return(path, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
r = get_path_alloc(type, suffix, path);
|
r = get_path_alloc(type, suffix, ret);
|
||||||
if (r != -EOPNOTSUPP)
|
if (r != -EOPNOTSUPP)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* Fall back to sd_path_lookup_strv */
|
/* Fall back to sd_path_lookup_strv */
|
||||||
_cleanup_strv_free_ char **l = NULL;
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
char *buffer;
|
|
||||||
|
|
||||||
r = sd_path_lookup_strv(type, suffix, &l);
|
r = sd_path_lookup_strv(type, suffix, &l);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
buffer = strv_join(l, ":");
|
char *joined = strv_join(l, ":");
|
||||||
if (!buffer)
|
if (!joined)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*path = buffer;
|
*ret = joined;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,25 +580,16 @@ static int get_search(uint64_t type, char ***ret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
|
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
|
||||||
case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
|
case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR:
|
||||||
RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
|
|
||||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
|
||||||
char **t;
|
|
||||||
|
|
||||||
t = generator_binary_paths(scope);
|
|
||||||
if (!t)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = t;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
|
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
|
||||||
case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
|
case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
|
||||||
char **t;
|
RuntimeScope scope = IN_SET(type, SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR,
|
||||||
|
SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR) ?
|
||||||
|
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
||||||
|
bool env_generator = IN_SET(type, SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR,
|
||||||
|
SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR);
|
||||||
|
|
||||||
t = env_generator_binary_paths(type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR ?
|
char **t = generator_binary_paths_internal(scope, env_generator);
|
||||||
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER);
|
|
||||||
if (!t)
|
if (!t)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -649,11 +605,11 @@ static int get_search(uint64_t type, char ***ret) {
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
|
_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret) {
|
||||||
_cleanup_strv_free_ char **l = NULL, **n = NULL;
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_return(paths, -EINVAL);
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
r = get_search(type, &l);
|
r = get_search(type, &l);
|
||||||
if (r == -EOPNOTSUPP) {
|
if (r == -EOPNOTSUPP) {
|
||||||
|
@ -669,31 +625,20 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***path
|
||||||
l[0] = TAKE_PTR(t);
|
l[0] = TAKE_PTR(t);
|
||||||
l[1] = NULL;
|
l[1] = NULL;
|
||||||
|
|
||||||
*paths = TAKE_PTR(l);
|
*ret = TAKE_PTR(l);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
} else if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!suffix) {
|
if (suffix)
|
||||||
*paths = TAKE_PTR(l);
|
STRV_FOREACH(i, l) {
|
||||||
return 0;
|
if (!path_extend(i, suffix))
|
||||||
}
|
return -ENOMEM;
|
||||||
|
|
||||||
n = new(char*, strv_length(l)+1);
|
path_simplify(*i);
|
||||||
if (!n)
|
}
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
char **j = n;
|
*ret = TAKE_PTR(l);
|
||||||
STRV_FOREACH(i, l) {
|
|
||||||
*j = path_join(*i, suffix);
|
|
||||||
if (!*j)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
*j = NULL;
|
|
||||||
|
|
||||||
*paths = TAKE_PTR(n);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -973,7 +973,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
log_netdev_debug(netdev, "loaded \"%s\"", netdev_kind_to_string(netdev->kind));
|
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
|
||||||
|
|
||||||
r = netdev_request_to_create(netdev);
|
r = netdev_request_to_create(netdev);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "device-private.h"
|
||||||
#include "dhcp-client-internal.h"
|
#include "dhcp-client-internal.h"
|
||||||
#include "hostname-setup.h"
|
#include "hostname-setup.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
|
@ -1428,27 +1429,33 @@ static int dhcp4_set_request_address(Link *link) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool link_needs_dhcp_broadcast(Link *link) {
|
static bool link_needs_dhcp_broadcast(Link *link) {
|
||||||
const char *val;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(link);
|
assert(link);
|
||||||
assert(link->network);
|
assert(link->network);
|
||||||
|
|
||||||
/* Return the setting in DHCP[4].RequestBroadcast if specified. Otherwise return the device property
|
/* Return the setting in DHCP[4].RequestBroadcast if specified. Otherwise return the device property
|
||||||
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER message
|
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER
|
||||||
* is being broadcast because they can't handle unicast messages while not fully configured.
|
* message is being broadcast because they can't handle unicast messages while not fully configured.
|
||||||
* If neither is set or a failure occurs, return false, which is the default for this flag.
|
* If neither is set or a failure occurs, return false, which is the default for this flag. */
|
||||||
*/
|
|
||||||
r = link->network->dhcp_broadcast;
|
|
||||||
if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
|
|
||||||
r = parse_boolean(val);
|
|
||||||
if (r < 0)
|
|
||||||
log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
|
|
||||||
else
|
|
||||||
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
|
|
||||||
|
|
||||||
|
r = link->network->dhcp_broadcast;
|
||||||
|
if (r >= 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!link->dev)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
r = device_get_property_bool(link->dev, "ID_NET_DHCP_BROADCAST");
|
||||||
|
if (r < 0) {
|
||||||
|
if (r != -ENOENT)
|
||||||
|
log_link_warning_errno(link, r, "DHCPv4 CLIENT: Failed to get or parse ID_NET_DHCP_BROADCAST, ignoring: %m");
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return r == true;
|
|
||||||
|
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool link_dhcp4_ipv6_only_mode(Link *link) {
|
static bool link_dhcp4_ipv6_only_mode(Link *link) {
|
||||||
|
|
|
@ -1293,9 +1293,9 @@ static int link_get_network(Link *link, Network **ret) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log_link_full(link, warn ? LOG_WARNING : LOG_DEBUG,
|
log_link_full(link, warn ? LOG_WARNING : LOG_DEBUG,
|
||||||
"found matching network '%s'%s.",
|
"Found matching .network file%s: %s",
|
||||||
network->filename,
|
warn ? ", based on potentially unpredictable interface name" : "",
|
||||||
warn ? ", based on potentially unpredictable interface name" : "");
|
network->filename);
|
||||||
|
|
||||||
if (network->unmanaged)
|
if (network->unmanaged)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
@ -1304,7 +1304,7 @@ static int link_get_network(Link *link, Network **ret) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENOENT;
|
return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int link_reconfigure_impl(Link *link, bool force) {
|
int link_reconfigure_impl(Link *link, bool force) {
|
||||||
|
|
|
@ -590,6 +590,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||||
return log_warning_errno(r, "%s: Failed to store configuration into hashmap: %m", filename);
|
return log_warning_errno(r, "%s: Failed to store configuration into hashmap: %m", filename);
|
||||||
|
|
||||||
TAKE_PTR(network);
|
TAKE_PTR(network);
|
||||||
|
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -404,6 +404,10 @@ typedef struct Partition {
|
||||||
|
|
||||||
PartitionEncryptedVolume *encrypted_volume;
|
PartitionEncryptedVolume *encrypted_volume;
|
||||||
|
|
||||||
|
char *supplement_for_name;
|
||||||
|
struct Partition *supplement_for, *supplement_target_for;
|
||||||
|
struct Partition *suppressing;
|
||||||
|
|
||||||
struct Partition *siblings[_VERITY_MODE_MAX];
|
struct Partition *siblings[_VERITY_MODE_MAX];
|
||||||
|
|
||||||
LIST_FIELDS(struct Partition, partitions);
|
LIST_FIELDS(struct Partition, partitions);
|
||||||
|
@ -411,6 +415,7 @@ typedef struct Partition {
|
||||||
|
|
||||||
#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
|
#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
|
||||||
#define PARTITION_EXISTS(p) (!!(p)->current_partition)
|
#define PARTITION_EXISTS(p) (!!(p)->current_partition)
|
||||||
|
#define PARTITION_SUPPRESSED(p) ((p)->supplement_for && (p)->supplement_for->suppressing == (p))
|
||||||
|
|
||||||
struct FreeArea {
|
struct FreeArea {
|
||||||
Partition *after;
|
Partition *after;
|
||||||
|
@ -520,6 +525,28 @@ static Partition *partition_new(void) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void partition_unlink_supplement(Partition *p) {
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
assert(!p->supplement_for || !p->supplement_target_for); /* Can't be both */
|
||||||
|
|
||||||
|
if (p->supplement_target_for) {
|
||||||
|
assert(p->supplement_target_for->supplement_for == p);
|
||||||
|
|
||||||
|
p->supplement_target_for->supplement_for = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->supplement_for) {
|
||||||
|
assert(p->supplement_for->supplement_target_for == p);
|
||||||
|
assert(!p->supplement_for->suppressing || p->supplement_for->suppressing == p);
|
||||||
|
|
||||||
|
p->supplement_for->supplement_target_for = p->supplement_for->suppressing = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->supplement_for_name = mfree(p->supplement_for_name);
|
||||||
|
p->supplement_target_for = p->supplement_for = p->suppressing = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static Partition* partition_free(Partition *p) {
|
static Partition* partition_free(Partition *p) {
|
||||||
if (!p)
|
if (!p)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -563,6 +590,8 @@ static Partition* partition_free(Partition *p) {
|
||||||
|
|
||||||
partition_encrypted_volume_free(p->encrypted_volume);
|
partition_encrypted_volume_free(p->encrypted_volume);
|
||||||
|
|
||||||
|
partition_unlink_supplement(p);
|
||||||
|
|
||||||
return mfree(p);
|
return mfree(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,6 +637,8 @@ static void partition_foreignize(Partition *p) {
|
||||||
p->n_mountpoints = 0;
|
p->n_mountpoints = 0;
|
||||||
|
|
||||||
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
|
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
|
||||||
|
|
||||||
|
partition_unlink_supplement(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool partition_type_exclude(const GptPartitionType *type) {
|
static bool partition_type_exclude(const GptPartitionType *type) {
|
||||||
|
@ -740,6 +771,10 @@ static void partition_drop_or_foreignize(Partition *p) {
|
||||||
|
|
||||||
p->dropped = true;
|
p->dropped = true;
|
||||||
p->allocated_to_area = NULL;
|
p->allocated_to_area = NULL;
|
||||||
|
|
||||||
|
/* If a supplement partition is dropped, we don't want to merge in its settings. */
|
||||||
|
if (PARTITION_SUPPRESSED(p))
|
||||||
|
p->supplement_for->suppressing = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -775,7 +810,7 @@ static bool context_drop_or_foreignize_one_priority(Context *context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t partition_min_size(const Context *context, const Partition *p) {
|
static uint64_t partition_min_size(const Context *context, const Partition *p) {
|
||||||
uint64_t sz;
|
uint64_t sz, override_min;
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(p);
|
assert(p);
|
||||||
|
@ -817,11 +852,13 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) {
|
||||||
sz = d;
|
sz = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
|
override_min = p->suppressing ? MAX(p->size_min, p->suppressing->size_min) : p->size_min;
|
||||||
|
|
||||||
|
return MAX(round_up_size(override_min != UINT64_MAX ? override_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
||||||
uint64_t sm;
|
uint64_t sm, override_max;
|
||||||
|
|
||||||
/* Calculate how large the partition may become at max. This is generally the configured maximum
|
/* Calculate how large the partition may become at max. This is generally the configured maximum
|
||||||
* size, except when it already exists and is larger than that. In that case it's the existing size,
|
* size, except when it already exists and is larger than that. In that case it's the existing size,
|
||||||
|
@ -839,10 +876,11 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
||||||
if (p->verity == VERITY_SIG)
|
if (p->verity == VERITY_SIG)
|
||||||
return VERITY_SIG_SIZE;
|
return VERITY_SIG_SIZE;
|
||||||
|
|
||||||
if (p->size_max == UINT64_MAX)
|
override_max = p->suppressing ? MIN(p->size_max, p->suppressing->size_max) : p->size_max;
|
||||||
|
if (override_max == UINT64_MAX)
|
||||||
return UINT64_MAX;
|
return UINT64_MAX;
|
||||||
|
|
||||||
sm = round_down_size(p->size_max, context->grain_size);
|
sm = round_down_size(override_max, context->grain_size);
|
||||||
|
|
||||||
if (p->current_size != UINT64_MAX)
|
if (p->current_size != UINT64_MAX)
|
||||||
sm = MAX(p->current_size, sm);
|
sm = MAX(p->current_size, sm);
|
||||||
|
@ -851,13 +889,17 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t partition_min_padding(const Partition *p) {
|
static uint64_t partition_min_padding(const Partition *p) {
|
||||||
|
uint64_t override_min;
|
||||||
|
|
||||||
assert(p);
|
assert(p);
|
||||||
return p->padding_min != UINT64_MAX ? p->padding_min : 0;
|
|
||||||
|
override_min = p->suppressing ? MAX(p->padding_min, p->suppressing->padding_min) : p->padding_min;
|
||||||
|
return override_min != UINT64_MAX ? override_min : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t partition_max_padding(const Partition *p) {
|
static uint64_t partition_max_padding(const Partition *p) {
|
||||||
assert(p);
|
assert(p);
|
||||||
return p->padding_max;
|
return p->suppressing ? MIN(p->padding_max, p->suppressing->padding_max) : p->padding_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
|
static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
|
||||||
|
@ -977,14 +1019,22 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
|
||||||
uint64_t required;
|
uint64_t required;
|
||||||
FreeArea *a = NULL;
|
FreeArea *a = NULL;
|
||||||
|
|
||||||
/* Skip partitions we already dropped or that already exist */
|
if (p->dropped || PARTITION_IS_FOREIGN(p) || PARTITION_SUPPRESSED(p))
|
||||||
if (p->dropped || PARTITION_EXISTS(p))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* How much do we need to fit? */
|
/* How much do we need to fit? */
|
||||||
required = partition_min_size_with_padding(context, p);
|
required = partition_min_size_with_padding(context, p);
|
||||||
assert(required % context->grain_size == 0);
|
assert(required % context->grain_size == 0);
|
||||||
|
|
||||||
|
/* For existing partitions, we should verify that they'll actually fit */
|
||||||
|
if (PARTITION_EXISTS(p)) {
|
||||||
|
if (p->current_size + p->current_padding < required)
|
||||||
|
return false; /* 😢 We won't be able to grow to the required min size! */
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For new partitions, see if there's a free area big enough */
|
||||||
for (size_t i = 0; i < context->n_free_areas; i++) {
|
for (size_t i = 0; i < context->n_free_areas; i++) {
|
||||||
a = context->free_areas[i];
|
a = context->free_areas[i];
|
||||||
|
|
||||||
|
@ -1007,6 +1057,57 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool context_unmerge_and_allocate_partitions(Context *context) {
|
||||||
|
assert(context);
|
||||||
|
|
||||||
|
/* This should only be called after plain context_allocate_partitions fails. This algorithm will
|
||||||
|
* try, in the order that minimizes the number of created supplement partitions, all combinations of
|
||||||
|
* un-suppressing supplement partitions until it finds one that works. */
|
||||||
|
|
||||||
|
/* First, let's try to un-suppress just one supplement partition and see if that gets us anywhere */
|
||||||
|
LIST_FOREACH(partitions, p, context->partitions) {
|
||||||
|
Partition *unsuppressed;
|
||||||
|
|
||||||
|
if (!p->suppressing)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsuppressed = TAKE_PTR(p->suppressing);
|
||||||
|
|
||||||
|
if (context_allocate_partitions(context, NULL))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
p->suppressing = unsuppressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Looks like not. So we have to un-suppress at least two partitions. We can do this recursively */
|
||||||
|
LIST_FOREACH(partitions, p, context->partitions) {
|
||||||
|
Partition *unsuppressed;
|
||||||
|
|
||||||
|
if (!p->suppressing)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsuppressed = TAKE_PTR(p->suppressing);
|
||||||
|
|
||||||
|
if (context_unmerge_and_allocate_partitions(context))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
p->suppressing = unsuppressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No combination of un-suppressed supplements made it possible to fit the partitions */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t partition_weight(const Partition *p) {
|
||||||
|
assert(p);
|
||||||
|
return p->suppressing ? p->suppressing->weight : p->weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t partition_padding_weight(const Partition *p) {
|
||||||
|
assert(p);
|
||||||
|
return p->suppressing ? p->suppressing->padding_weight : p->padding_weight;
|
||||||
|
}
|
||||||
|
|
||||||
static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
|
static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
|
||||||
uint64_t weight_sum = 0;
|
uint64_t weight_sum = 0;
|
||||||
|
|
||||||
|
@ -1020,13 +1121,11 @@ static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
|
||||||
if (p->padding_area != a && p->allocated_to_area != a)
|
if (p->padding_area != a && p->allocated_to_area != a)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (p->weight > UINT64_MAX - weight_sum)
|
if (!INC_SAFE(&weight_sum, partition_weight(p)))
|
||||||
goto overflow_sum;
|
goto overflow_sum;
|
||||||
weight_sum += p->weight;
|
|
||||||
|
|
||||||
if (p->padding_weight > UINT64_MAX - weight_sum)
|
if (!INC_SAFE(&weight_sum, partition_padding_weight(p)))
|
||||||
goto overflow_sum;
|
goto overflow_sum;
|
||||||
weight_sum += p->padding_weight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*ret = weight_sum;
|
*ret = weight_sum;
|
||||||
|
@ -1091,7 +1190,6 @@ static bool context_grow_partitions_phase(
|
||||||
* get any additional room from the left-overs. Similar, if two partitions have the same weight they
|
* get any additional room from the left-overs. Similar, if two partitions have the same weight they
|
||||||
* should get the same space if possible, even if one has a smaller minimum size than the other. */
|
* should get the same space if possible, even if one has a smaller minimum size than the other. */
|
||||||
LIST_FOREACH(partitions, p, context->partitions) {
|
LIST_FOREACH(partitions, p, context->partitions) {
|
||||||
|
|
||||||
/* Look only at partitions associated with this free area, i.e. immediately
|
/* Look only at partitions associated with this free area, i.e. immediately
|
||||||
* preceding it, or allocated into it */
|
* preceding it, or allocated into it */
|
||||||
if (p->allocated_to_area != a && p->padding_area != a)
|
if (p->allocated_to_area != a && p->padding_area != a)
|
||||||
|
@ -1099,11 +1197,14 @@ static bool context_grow_partitions_phase(
|
||||||
|
|
||||||
if (p->new_size == UINT64_MAX) {
|
if (p->new_size == UINT64_MAX) {
|
||||||
uint64_t share, rsz, xsz;
|
uint64_t share, rsz, xsz;
|
||||||
|
uint32_t weight;
|
||||||
bool charge = false;
|
bool charge = false;
|
||||||
|
|
||||||
|
weight = partition_weight(p);
|
||||||
|
|
||||||
/* Calculate how much this space this partition needs if everyone would get
|
/* Calculate how much this space this partition needs if everyone would get
|
||||||
* the weight based share */
|
* the weight based share */
|
||||||
share = scale_by_weight(*span, p->weight, *weight_sum);
|
share = scale_by_weight(*span, weight, *weight_sum);
|
||||||
|
|
||||||
rsz = partition_min_size(context, p);
|
rsz = partition_min_size(context, p);
|
||||||
xsz = partition_max_size(context, p);
|
xsz = partition_max_size(context, p);
|
||||||
|
@ -1143,15 +1244,18 @@ static bool context_grow_partitions_phase(
|
||||||
|
|
||||||
if (charge) {
|
if (charge) {
|
||||||
*span = charge_size(context, *span, p->new_size);
|
*span = charge_size(context, *span, p->new_size);
|
||||||
*weight_sum = charge_weight(*weight_sum, p->weight);
|
*weight_sum = charge_weight(*weight_sum, weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->new_padding == UINT64_MAX) {
|
if (p->new_padding == UINT64_MAX) {
|
||||||
uint64_t share, rsz, xsz;
|
uint64_t share, rsz, xsz;
|
||||||
|
uint32_t padding_weight;
|
||||||
bool charge = false;
|
bool charge = false;
|
||||||
|
|
||||||
share = scale_by_weight(*span, p->padding_weight, *weight_sum);
|
padding_weight = partition_padding_weight(p);
|
||||||
|
|
||||||
|
share = scale_by_weight(*span, padding_weight, *weight_sum);
|
||||||
|
|
||||||
rsz = partition_min_padding(p);
|
rsz = partition_min_padding(p);
|
||||||
xsz = partition_max_padding(p);
|
xsz = partition_max_padding(p);
|
||||||
|
@ -1170,7 +1274,7 @@ static bool context_grow_partitions_phase(
|
||||||
|
|
||||||
if (charge) {
|
if (charge) {
|
||||||
*span = charge_size(context, *span, p->new_padding);
|
*span = charge_size(context, *span, p->new_padding);
|
||||||
*weight_sum = charge_weight(*weight_sum, p->padding_weight);
|
*weight_sum = charge_weight(*weight_sum, padding_weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2155,7 +2259,9 @@ static int partition_finalize_fstype(Partition *p, const char *path) {
|
||||||
|
|
||||||
static bool partition_needs_populate(const Partition *p) {
|
static bool partition_needs_populate(const Partition *p) {
|
||||||
assert(p);
|
assert(p);
|
||||||
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks);
|
assert(!p->supplement_for || !p->suppressing); /* Avoid infinite recursion */
|
||||||
|
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks) ||
|
||||||
|
(p->suppressing && partition_needs_populate(p->suppressing));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
|
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
|
||||||
|
@ -2196,6 +2302,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
||||||
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
||||||
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
|
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
|
||||||
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
|
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
|
||||||
|
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
_cleanup_free_ char *filename = NULL;
|
_cleanup_free_ char *filename = NULL;
|
||||||
|
@ -2320,6 +2427,18 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
||||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
|
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
|
||||||
|
|
||||||
|
if (p->supplement_for_name) {
|
||||||
|
if (!filename_part_is_valid(p->supplement_for_name))
|
||||||
|
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"SupplementFor= is an invalid filename: %s",
|
||||||
|
p->supplement_for_name);
|
||||||
|
|
||||||
|
if (p->copy_blocks_path || p->copy_blocks_auto || p->encrypt != ENCRYPT_OFF ||
|
||||||
|
p->verity != VERITY_OFF)
|
||||||
|
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"SupplementFor= cannot be combined with CopyBlocks=/Encrypt=/Verity=");
|
||||||
|
}
|
||||||
|
|
||||||
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
|
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
|
||||||
if ((IN_SET(p->type.designator,
|
if ((IN_SET(p->type.designator,
|
||||||
PARTITION_ROOT_VERITY,
|
PARTITION_ROOT_VERITY,
|
||||||
|
@ -2626,6 +2745,58 @@ static int context_copy_from(Context *context) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool check_cross_def_ranges_valid(uint64_t a_min, uint64_t a_max, uint64_t b_min, uint64_t b_max) {
|
||||||
|
if (a_min == UINT64_MAX && b_min == UINT64_MAX)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (a_max == UINT64_MAX && b_max == UINT64_MAX)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return MAX(a_min != UINT64_MAX ? a_min : 0, b_min != UINT64_MAX ? b_min : 0) <= MIN(a_max, b_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int supplement_find_target(const Context *context, const Partition *supplement, Partition **ret) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(context);
|
||||||
|
assert(supplement);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
LIST_FOREACH(partitions, p, context->partitions) {
|
||||||
|
_cleanup_free_ char *filename = NULL;
|
||||||
|
|
||||||
|
if (p == supplement)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = path_extract_filename(p->definition_path, &filename);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r,
|
||||||
|
"Failed to extract filename from path '%s': %m",
|
||||||
|
p->definition_path);
|
||||||
|
|
||||||
|
*ASSERT_PTR(endswith(filename, ".conf")) = 0; /* Remove the file extension */
|
||||||
|
|
||||||
|
if (!streq(supplement->supplement_for_name, filename))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (p->supplement_for_name)
|
||||||
|
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"SupplementFor= target is itself configured as a supplement.");
|
||||||
|
|
||||||
|
if (p->suppressing)
|
||||||
|
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"SupplementFor= target already has a supplement defined: %s",
|
||||||
|
p->suppressing->definition_path);
|
||||||
|
|
||||||
|
*ret = p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"Couldn't find target partition for SupplementFor=%s",
|
||||||
|
supplement->supplement_for_name);
|
||||||
|
}
|
||||||
|
|
||||||
static int context_read_definitions(Context *context) {
|
static int context_read_definitions(Context *context) {
|
||||||
_cleanup_strv_free_ char **files = NULL;
|
_cleanup_strv_free_ char **files = NULL;
|
||||||
Partition *last = LIST_FIND_TAIL(partitions, context->partitions);
|
Partition *last = LIST_FIND_TAIL(partitions, context->partitions);
|
||||||
|
@ -2717,7 +2888,33 @@ static int context_read_definitions(Context *context) {
|
||||||
if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
|
if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
|
||||||
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
|
"Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(partitions, p, context->partitions) {
|
||||||
|
Partition *tgt = NULL;
|
||||||
|
|
||||||
|
if (!p->supplement_for_name)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = supplement_find_target(context, p, &tgt);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (tgt->copy_blocks_path || tgt->copy_blocks_auto || tgt->encrypt != ENCRYPT_OFF ||
|
||||||
|
tgt->verity != VERITY_OFF)
|
||||||
|
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"SupplementFor= target uses CopyBlocks=/Encrypt=/Verity=");
|
||||||
|
|
||||||
|
if (!check_cross_def_ranges_valid(p->size_min, p->size_max, tgt->size_min, tgt->size_max))
|
||||||
|
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"SizeMinBytes= larger than SizeMaxBytes= when merged with SupplementFor= target.");
|
||||||
|
|
||||||
|
if (!check_cross_def_ranges_valid(p->padding_min, p->padding_max, tgt->padding_min, tgt->padding_max))
|
||||||
|
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"PaddingMinBytes= larger than PaddingMaxBytes= when merged with SupplementFor= target.");
|
||||||
|
|
||||||
|
p->supplement_for = tgt;
|
||||||
|
tgt->suppressing = tgt->supplement_target_for = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -3101,6 +3298,10 @@ static int context_load_partition_table(Context *context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(partitions, p, context->partitions)
|
||||||
|
if (PARTITION_SUPPRESSED(p) && PARTITION_EXISTS(p))
|
||||||
|
p->supplement_for->suppressing = NULL;
|
||||||
|
|
||||||
add_initial_free_area:
|
add_initial_free_area:
|
||||||
nsectors = fdisk_get_nsectors(c);
|
nsectors = fdisk_get_nsectors(c);
|
||||||
assert(nsectors <= UINT64_MAX/secsz);
|
assert(nsectors <= UINT64_MAX/secsz);
|
||||||
|
@ -3192,6 +3393,11 @@ static void context_unload_partition_table(Context *context) {
|
||||||
|
|
||||||
p->current_uuid = SD_ID128_NULL;
|
p->current_uuid = SD_ID128_NULL;
|
||||||
p->current_label = mfree(p->current_label);
|
p->current_label = mfree(p->current_label);
|
||||||
|
|
||||||
|
/* A supplement partition is only ever un-suppressed if the existing partition table prevented
|
||||||
|
* us from suppressing it. So when unloading the partition table, we must re-suppress. */
|
||||||
|
if (p->supplement_for)
|
||||||
|
p->supplement_for->suppressing = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->start = UINT64_MAX;
|
context->start = UINT64_MAX;
|
||||||
|
@ -4969,6 +5175,31 @@ static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int shallow_join_strv(char ***ret, char **a, char **b) {
|
||||||
|
_cleanup_free_ char **joined = NULL;
|
||||||
|
char **iter;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
joined = new(char*, strv_length(a) + strv_length(b) + 1);
|
||||||
|
if (!joined)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
iter = joined;
|
||||||
|
|
||||||
|
STRV_FOREACH(i, a)
|
||||||
|
*(iter++) = *i;
|
||||||
|
|
||||||
|
STRV_FOREACH(i, b)
|
||||||
|
if (!strv_contains(joined, *i))
|
||||||
|
*(iter++) = *i;
|
||||||
|
|
||||||
|
*iter = NULL;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(joined);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int make_copy_files_denylist(
|
static int make_copy_files_denylist(
|
||||||
Context *context,
|
Context *context,
|
||||||
const Partition *p,
|
const Partition *p,
|
||||||
|
@ -4977,6 +5208,7 @@ static int make_copy_files_denylist(
|
||||||
Hashmap **ret) {
|
Hashmap **ret) {
|
||||||
|
|
||||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||||
|
_cleanup_free_ char **override_exclude_src = NULL, **override_exclude_tgt = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
|
@ -4996,13 +5228,26 @@ static int make_copy_files_denylist(
|
||||||
|
|
||||||
/* Add the user configured excludes. */
|
/* Add the user configured excludes. */
|
||||||
|
|
||||||
STRV_FOREACH(e, p->exclude_files_source) {
|
if (p->suppressing) {
|
||||||
|
r = shallow_join_strv(&override_exclude_src,
|
||||||
|
p->exclude_files_source,
|
||||||
|
p->suppressing->exclude_files_source);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = shallow_join_strv(&override_exclude_tgt,
|
||||||
|
p->exclude_files_target,
|
||||||
|
p->suppressing->exclude_files_target);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
STRV_FOREACH(e, override_exclude_src ?: p->exclude_files_source) {
|
||||||
r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
|
r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRV_FOREACH(e, p->exclude_files_target) {
|
STRV_FOREACH(e, override_exclude_tgt ?: p->exclude_files_target) {
|
||||||
_cleanup_free_ char *path = NULL;
|
_cleanup_free_ char *path = NULL;
|
||||||
|
|
||||||
const char *s = path_startswith(*e, target);
|
const char *s = path_startswith(*e, target);
|
||||||
|
@ -5096,6 +5341,7 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
|
||||||
static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
||||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||||
Subvolume *subvolume;
|
Subvolume *subvolume;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(p);
|
assert(p);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
@ -5104,6 +5350,18 @@ static int make_subvolumes_strv(const Partition *p, char ***ret) {
|
||||||
if (strv_extend(&subvolumes, subvolume->path) < 0)
|
if (strv_extend(&subvolumes, subvolume->path) < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
if (p->suppressing) {
|
||||||
|
_cleanup_strv_free_ char **suppressing = NULL;
|
||||||
|
|
||||||
|
r = make_subvolumes_strv(p->suppressing, &suppressing);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = strv_extend_strv(&subvolumes, suppressing, /* filter_duplicates= */ true);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
*ret = TAKE_PTR(subvolumes);
|
*ret = TAKE_PTR(subvolumes);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -5114,18 +5372,22 @@ static int make_subvolumes_set(
|
||||||
const char *target,
|
const char *target,
|
||||||
Set **ret) {
|
Set **ret) {
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **paths = NULL;
|
||||||
_cleanup_set_free_ Set *subvolumes = NULL;
|
_cleanup_set_free_ Set *subvolumes = NULL;
|
||||||
Subvolume *subvolume;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(p);
|
assert(p);
|
||||||
assert(target);
|
assert(target);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
r = make_subvolumes_strv(p, &paths);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
STRV_FOREACH(subvolume, paths) {
|
||||||
_cleanup_free_ char *path = NULL;
|
_cleanup_free_ char *path = NULL;
|
||||||
|
|
||||||
const char *s = path_startswith(subvolume->path, target);
|
const char *s = path_startswith(*subvolume, target);
|
||||||
if (!s)
|
if (!s)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -5168,6 +5430,7 @@ static usec_t epoch_or_infinity(void) {
|
||||||
|
|
||||||
static int do_copy_files(Context *context, Partition *p, const char *root) {
|
static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||||
|
_cleanup_free_ char **override_copy_files = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(p);
|
assert(p);
|
||||||
|
@ -5177,11 +5440,17 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
if (p->suppressing) {
|
||||||
|
r = shallow_join_strv(&override_copy_files, p->copy_files, p->suppressing->copy_files);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
|
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
|
||||||
* it created them. However, the root directory is created by us, so we have to manually take care
|
* it created them. However, the root directory is created by us, so we have to manually take care
|
||||||
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
|
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
|
||||||
* the root directory. */
|
* the root directory. */
|
||||||
STRV_FOREACH_PAIR(source, target, p->copy_files) {
|
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
|
||||||
_cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
|
_cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
|
||||||
|
|
||||||
if (!path_equal(*target, "/"))
|
if (!path_equal(*target, "/"))
|
||||||
|
@ -5202,7 +5471,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRV_FOREACH_PAIR(source, target, p->copy_files) {
|
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
|
||||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||||
_cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
|
_cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
|
||||||
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
|
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
|
||||||
|
@ -5320,6 +5589,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
|
||||||
|
|
||||||
static int do_make_directories(Partition *p, const char *root) {
|
static int do_make_directories(Partition *p, const char *root) {
|
||||||
_cleanup_strv_free_ char **subvolumes = NULL;
|
_cleanup_strv_free_ char **subvolumes = NULL;
|
||||||
|
_cleanup_free_ char **override_dirs = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(p);
|
assert(p);
|
||||||
|
@ -5329,7 +5599,13 @@ static int do_make_directories(Partition *p, const char *root) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
STRV_FOREACH(d, p->make_directories) {
|
if (p->suppressing) {
|
||||||
|
r = shallow_join_strv(&override_dirs, p->make_directories, p->suppressing->make_directories);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
STRV_FOREACH(d, override_dirs ?: p->make_directories) {
|
||||||
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
|
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
|
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
|
||||||
|
@ -5377,6 +5653,12 @@ static int make_subvolumes_read_only(Partition *p, const char *root) {
|
||||||
return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
|
return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p->suppressing) {
|
||||||
|
r = make_subvolumes_read_only(p->suppressing, root);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5496,6 +5778,38 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int append_btrfs_subvols(char ***l, OrderedHashmap *subvolumes, const char *default_subvolume) {
|
||||||
|
Subvolume *subvolume;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(l);
|
||||||
|
|
||||||
|
ORDERED_HASHMAP_FOREACH(subvolume, subvolumes) {
|
||||||
|
_cleanup_free_ char *s = NULL, *f = NULL;
|
||||||
|
|
||||||
|
s = strdup(subvolume->path);
|
||||||
|
if (!s)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
f = subvolume_flags_to_string(subvolume->flags);
|
||||||
|
if (!f)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (streq_ptr(subvolume->path, default_subvolume) &&
|
||||||
|
!strextend_with_separator(&f, ",", "default"))
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = strv_extend_many(l, "--subvol", s);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
|
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
|
||||||
_cleanup_strv_free_ char **sv = NULL;
|
_cleanup_strv_free_ char **sv = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -5510,28 +5824,14 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
|
||||||
p->format);
|
p->format);
|
||||||
|
|
||||||
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
|
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
|
||||||
Subvolume *subvolume;
|
r = append_btrfs_subvols(&sv, p->subvolumes, p->default_subvolume);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
|
if (p->suppressing) {
|
||||||
_cleanup_free_ char *s = NULL, *f = NULL;
|
r = append_btrfs_subvols(&sv, p->suppressing->subvolumes, NULL);
|
||||||
|
|
||||||
s = strdup(subvolume->path);
|
|
||||||
if (!s)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
f = subvolume_flags_to_string(subvolume->flags);
|
|
||||||
if (!f)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (streq_ptr(subvolume->path, p->default_subvolume) && !strextend_with_separator(&f, ",", "default"))
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = strv_extend_many(&sv, "--subvol", s);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8524,7 +8824,7 @@ static int determine_auto_size(Context *c) {
|
||||||
LIST_FOREACH(partitions, p, c->partitions) {
|
LIST_FOREACH(partitions, p, c->partitions) {
|
||||||
uint64_t m;
|
uint64_t m;
|
||||||
|
|
||||||
if (p->dropped)
|
if (p->dropped || PARTITION_SUPPRESSED(p))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m = partition_min_size_with_padding(c, p);
|
m = partition_min_size_with_padding(c, p);
|
||||||
|
@ -8756,13 +9056,36 @@ static int run(int argc, char *argv[]) {
|
||||||
if (context_allocate_partitions(context, &largest_free_area))
|
if (context_allocate_partitions(context, &largest_free_area))
|
||||||
break; /* Success! */
|
break; /* Success! */
|
||||||
|
|
||||||
if (!context_drop_or_foreignize_one_priority(context)) {
|
if (context_unmerge_and_allocate_partitions(context))
|
||||||
r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
|
break; /* We had to un-suppress a supplement or few, but still success! */
|
||||||
"Can't fit requested partitions into available free space (%s), refusing.",
|
|
||||||
FORMAT_BYTES(largest_free_area));
|
if (context_drop_or_foreignize_one_priority(context))
|
||||||
determine_auto_size(context);
|
continue; /* Still no luck. Let's drop a priority and try again. */
|
||||||
return r;
|
|
||||||
}
|
/* No more priorities left to drop. This configuration just doesn't fit on this disk... */
|
||||||
|
r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
|
||||||
|
"Can't fit requested partitions into available free space (%s), refusing.",
|
||||||
|
FORMAT_BYTES(largest_free_area));
|
||||||
|
determine_auto_size(context);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(partitions, p, context->partitions) {
|
||||||
|
if (!p->supplement_for)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (PARTITION_SUPPRESSED(p)) {
|
||||||
|
assert(!p->allocated_to_area);
|
||||||
|
p->dropped = true;
|
||||||
|
|
||||||
|
log_debug("Partition %s can be merged into %s, suppressing supplement.",
|
||||||
|
p->definition_path, p->supplement_for->definition_path);
|
||||||
|
} else if (PARTITION_EXISTS(p))
|
||||||
|
log_info("Partition %s already exists on disk, using supplement verbatim.",
|
||||||
|
p->definition_path);
|
||||||
|
else
|
||||||
|
log_info("Couldn't allocate partitions with %s merged into %s, using supplement verbatim.",
|
||||||
|
p->definition_path, p->supplement_for->definition_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now assign free space according to the weight logic */
|
/* Now assign free space according to the weight logic */
|
||||||
|
|
|
@ -465,10 +465,6 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
|
||||||
assert(path);
|
assert(path);
|
||||||
assert(st);
|
assert(st);
|
||||||
|
|
||||||
r = hashmap_ensure_allocated(stats_by_path, &path_hash_ops_free_free);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
st_copy = newdup(struct stat, st, 1);
|
st_copy = newdup(struct stat, st, 1);
|
||||||
if (!st_copy)
|
if (!st_copy)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -477,7 +473,7 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
|
||||||
if (!path_copy)
|
if (!path_copy)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
r = hashmap_put(*stats_by_path, path_copy, st_copy);
|
r = hashmap_ensure_put(stats_by_path, &path_hash_ops_free_free, path_copy, st_copy);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -502,12 +498,12 @@ static int config_parse_many_files(
|
||||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *dropins = NULL;
|
_cleanup_ordered_hashmap_free_ OrderedHashmap *dropins = NULL;
|
||||||
_cleanup_set_free_ Set *inodes = NULL;
|
_cleanup_set_free_ Set *inodes = NULL;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int r;
|
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
|
||||||
|
|
||||||
if (ret_stats_by_path) {
|
if (ret_stats_by_path) {
|
||||||
stats_by_path = hashmap_new(&path_hash_ops_free_free);
|
stats_by_path = hashmap_new(&path_hash_ops_free_free);
|
||||||
if (!stats_by_path)
|
if (!stats_by_path)
|
||||||
return -ENOMEM;
|
return log_oom_full(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
STRV_FOREACH(fn, files) {
|
STRV_FOREACH(fn, files) {
|
||||||
|
@ -518,14 +514,14 @@ static int config_parse_many_files(
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
continue;
|
continue;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
|
||||||
|
|
||||||
int fd = fileno(f);
|
int fd = fileno(f);
|
||||||
|
|
||||||
r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f);
|
r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
assert(r != -EEXIST);
|
assert(r == -ENOMEM);
|
||||||
return r;
|
return log_oom_full(level);
|
||||||
}
|
}
|
||||||
assert(r > 0);
|
assert(r > 0);
|
||||||
TAKE_PTR(f);
|
TAKE_PTR(f);
|
||||||
|
@ -535,14 +531,14 @@ static int config_parse_many_files(
|
||||||
|
|
||||||
_cleanup_free_ struct stat *st_dropin = new(struct stat, 1);
|
_cleanup_free_ struct stat *st_dropin = new(struct stat, 1);
|
||||||
if (!st_dropin)
|
if (!st_dropin)
|
||||||
return -ENOMEM;
|
return log_oom_full(level);
|
||||||
|
|
||||||
if (fstat(fd, st_dropin) < 0)
|
if (fstat(fd, st_dropin) < 0)
|
||||||
return -errno;
|
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
|
||||||
|
|
||||||
r = set_ensure_consume(&inodes, &inode_hash_ops, TAKE_PTR(st_dropin));
|
r = set_ensure_consume(&inodes, &inode_hash_ops, TAKE_PTR(st_dropin));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_oom_full(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First read the first found main config file. */
|
/* First read the first found main config file. */
|
||||||
|
@ -553,11 +549,11 @@ static int config_parse_many_files(
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
continue;
|
continue;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
|
||||||
|
|
||||||
if (inodes) {
|
if (inodes) {
|
||||||
if (fstat(fileno(f), &st) < 0)
|
if (fstat(fileno(f), &st) < 0)
|
||||||
return -errno;
|
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
|
||||||
|
|
||||||
if (set_contains(inodes, &st)) {
|
if (set_contains(inodes, &st)) {
|
||||||
log_debug("%s: symlink to/symlinked as drop-in, will be read later.", *fn);
|
log_debug("%s: symlink to/symlinked as drop-in, will be read later.", *fn);
|
||||||
|
@ -567,13 +563,13 @@ static int config_parse_many_files(
|
||||||
|
|
||||||
r = config_parse(/* unit= */ NULL, *fn, f, sections, lookup, table, flags, userdata, &st);
|
r = config_parse(/* unit= */ NULL, *fn, f, sections, lookup, table, flags, userdata, &st);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r; /* config_parse() logs internally. */
|
||||||
assert(r > 0);
|
assert(r > 0);
|
||||||
|
|
||||||
if (ret_stats_by_path) {
|
if (ret_stats_by_path) {
|
||||||
r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
|
r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_full_errno(level, r, "Failed to save stats of %s: %m", *fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -586,13 +582,13 @@ static int config_parse_many_files(
|
||||||
ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) {
|
ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) {
|
||||||
r = config_parse(/* unit= */ NULL, path_dropin, f_dropin, sections, lookup, table, flags, userdata, &st);
|
r = config_parse(/* unit= */ NULL, path_dropin, f_dropin, sections, lookup, table, flags, userdata, &st);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r; /* config_parse() logs internally. */
|
||||||
assert(r > 0);
|
assert(r > 0);
|
||||||
|
|
||||||
if (ret_stats_by_path) {
|
if (ret_stats_by_path) {
|
||||||
r = hashmap_put_stats_by_path(&stats_by_path, path_dropin, &st);
|
r = hashmap_put_stats_by_path(&stats_by_path, path_dropin, &st);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_full_errno(level, r, "Failed to save stats of %s: %m", path_dropin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,11 +621,12 @@ int config_parse_many(
|
||||||
|
|
||||||
r = conf_files_list_dropins(&files, dropin_dirname, root, conf_file_dirs);
|
r = conf_files_list_dropins(&files, dropin_dirname, root, conf_file_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG, r,
|
||||||
|
"Failed to list up drop-in configs in %s: %m", dropin_dirname);
|
||||||
|
|
||||||
r = config_parse_many_files(root, conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
|
r = config_parse_many_files(root, conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r; /* config_parse_many_files() logs internally. */
|
||||||
|
|
||||||
if (ret_dropin_files)
|
if (ret_dropin_files)
|
||||||
*ret_dropin_files = TAKE_PTR(files);
|
*ret_dropin_files = TAKE_PTR(files);
|
||||||
|
@ -650,22 +647,16 @@ int config_parse_standard_file_with_dropins_full(
|
||||||
|
|
||||||
const char* const *conf_paths = (const char* const*) CONF_PATHS_STRV("");
|
const char* const *conf_paths = (const char* const*) CONF_PATHS_STRV("");
|
||||||
_cleanup_strv_free_ char **configs = NULL;
|
_cleanup_strv_free_ char **configs = NULL;
|
||||||
int r;
|
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
|
||||||
|
|
||||||
/* Build the list of main config files */
|
/* Build the list of main config files */
|
||||||
r = strv_extend_strv_biconcat(&configs, root, conf_paths, main_file);
|
r = strv_extend_strv_biconcat(&configs, root, conf_paths, main_file);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
if (flags & CONFIG_PARSE_WARN)
|
return log_oom_full(level);
|
||||||
log_oom();
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cleanup_free_ char *dropin_dirname = strjoin(main_file, ".d");
|
_cleanup_free_ char *dropin_dirname = strjoin(main_file, ".d");
|
||||||
if (!dropin_dirname) {
|
if (!dropin_dirname)
|
||||||
if (flags & CONFIG_PARSE_WARN)
|
return log_oom_full(level);
|
||||||
log_oom();
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config_parse_many(
|
return config_parse_many(
|
||||||
(const char* const*) configs,
|
(const char* const*) configs,
|
||||||
|
|
|
@ -123,8 +123,8 @@ enum {
|
||||||
_SD_PATH_MAX
|
_SD_PATH_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
int sd_path_lookup(uint64_t type, const char *suffix, char **path);
|
int sd_path_lookup(uint64_t type, const char *suffix, char **ret);
|
||||||
int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths);
|
int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret);
|
||||||
|
|
||||||
_SD_END_DECLARATIONS;
|
_SD_END_DECLARATIONS;
|
||||||
|
|
||||||
|
|
|
@ -349,49 +349,35 @@ static int log_unresolvable_specifier(const char *filename, unsigned line) {
|
||||||
arg_dry_run ? (would) : (doing), \
|
arg_dry_run ? (would) : (doing), \
|
||||||
__VA_ARGS__)
|
__VA_ARGS__)
|
||||||
|
|
||||||
static int user_config_paths(char*** ret) {
|
static int user_config_paths(char ***ret) {
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||||
_cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
|
_cleanup_free_ char *runtime_config = NULL;
|
||||||
_cleanup_strv_free_ char **res = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
assert(ret);
|
||||||
|
|
||||||
|
/* Combined user-specific and global dirs */
|
||||||
|
r = user_search_dirs("/user-tmpfiles.d", &config_dirs, &data_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
|
r = xdg_user_runtime_dir("/user-tmpfiles.d", &runtime_config);
|
||||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
if (r < 0 && !ERRNO_IS_NEG_NOINFO(r))
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
|
r = strv_consume(&config_dirs, TAKE_PTR(runtime_config));
|
||||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
|
|
||||||
if (r < 0 && !ERRNO_IS_NOINFO(r))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/user-tmpfiles.d");
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = strv_extend_many(
|
r = strv_extend_strv(&config_dirs, data_dirs, /* filter_duplicates = */ true);
|
||||||
&res,
|
|
||||||
persistent_config,
|
|
||||||
runtime_config,
|
|
||||||
data_home);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/user-tmpfiles.d");
|
r = path_strv_make_absolute_cwd(config_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = path_strv_make_absolute_cwd(res);
|
*ret = TAKE_PTR(config_dirs);
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(res);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -526,9 +526,6 @@ def call_systemd_measure(uki, linux, opts):
|
||||||
|
|
||||||
# First, pick up the sections we shall measure now */
|
# First, pick up the sections we shall measure now */
|
||||||
for s in uki.sections:
|
for s in uki.sections:
|
||||||
|
|
||||||
print(s)
|
|
||||||
|
|
||||||
if not s.measure:
|
if not s.measure:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
|
@ -212,7 +212,7 @@ static int get_firmware_search_dirs(char ***ret) {
|
||||||
* Prioritising entries in "more specific" directories */
|
* Prioritising entries in "more specific" directories */
|
||||||
|
|
||||||
_cleanup_free_ char *user_firmware_dir = NULL;
|
_cleanup_free_ char *user_firmware_dir = NULL;
|
||||||
r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
|
r = xdg_user_config_dir("/qemu/firmware", &user_firmware_dir);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -1488,11 +1488,11 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||||
|
|
||||||
/* if we are going to be starting any units with state then create our runtime dir */
|
/* if we are going to be starting any units with state then create our runtime dir */
|
||||||
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||||
r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
|
r = runtime_directory(arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn",
|
||||||
|
&arg_runtime_directory);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to lookup runtime directory: %m");
|
return log_error_errno(r, "Failed to lookup runtime directory: %m");
|
||||||
if (r) {
|
if (r > 0) { /* We need to create our own runtime dir */
|
||||||
/* r > 0 means we need to create our own runtime dir */
|
|
||||||
r = mkdir_p(arg_runtime_directory, 0755);
|
r = mkdir_p(arg_runtime_directory, 0755);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to create runtime directory: %m");
|
return log_error_errno(r, "Failed to create runtime directory: %m");
|
||||||
|
|
|
@ -20,6 +20,39 @@
|
||||||
|
|
||||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(xdgautostartservice_hash_ops, char, string_hash_func, string_compare_func, XdgAutostartService, xdg_autostart_service_free);
|
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(xdgautostartservice_hash_ops, char, string_hash_func, string_compare_func, XdgAutostartService, xdg_autostart_service_free);
|
||||||
|
|
||||||
|
static int xdg_base_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
|
||||||
|
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||||
|
const char *e;
|
||||||
|
|
||||||
|
/* Implement the mechanisms defined in
|
||||||
|
* https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html */
|
||||||
|
|
||||||
|
assert(ret_config_dirs);
|
||||||
|
assert(ret_data_dirs);
|
||||||
|
|
||||||
|
e = getenv("XDG_CONFIG_DIRS");
|
||||||
|
if (e)
|
||||||
|
config_dirs = strv_split(e, ":");
|
||||||
|
else
|
||||||
|
config_dirs = strv_new("/etc/xdg");
|
||||||
|
if (!config_dirs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
e = getenv("XDG_DATA_DIRS");
|
||||||
|
if (e)
|
||||||
|
data_dirs = strv_split(e, ":");
|
||||||
|
else
|
||||||
|
data_dirs = strv_new("/usr/local/share",
|
||||||
|
"/usr/share");
|
||||||
|
if (!data_dirs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret_config_dirs = TAKE_PTR(config_dirs);
|
||||||
|
*ret_data_dirs = TAKE_PTR(data_dirs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int enumerate_xdg_autostart(Hashmap *all_services) {
|
static int enumerate_xdg_autostart(Hashmap *all_services) {
|
||||||
_cleanup_strv_free_ char **autostart_dirs = NULL;
|
_cleanup_strv_free_ char **autostart_dirs = NULL;
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL;
|
_cleanup_strv_free_ char **config_dirs = NULL;
|
||||||
|
@ -27,14 +60,14 @@ static int enumerate_xdg_autostart(Hashmap *all_services) {
|
||||||
_cleanup_free_ char *user_config_autostart_dir = NULL;
|
_cleanup_free_ char *user_config_autostart_dir = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = xdg_user_config_dir(&user_config_autostart_dir, "/autostart");
|
r = xdg_user_config_dir("/autostart", &user_config_autostart_dir);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
r = strv_extend(&autostart_dirs, user_config_autostart_dir);
|
r = strv_extend(&autostart_dirs, user_config_autostart_dir);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = xdg_user_dirs(&config_dirs, &data_dirs);
|
r = xdg_base_dirs(&config_dirs, &data_dirs);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
r = strv_extend_strv_concat(&autostart_dirs, (const char* const*) config_dirs, "/autostart");
|
r = strv_extend_strv_concat(&autostart_dirs, (const char* const*) config_dirs, "/autostart");
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
all setup run clean clean-again:
|
||||||
|
true
|
||||||
|
|
||||||
|
.PHONY: all setup run clean clean-again
|
|
@ -0,0 +1,10 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
integration_tests += [
|
||||||
|
integration_test_template + {
|
||||||
|
'name' : fs.name(meson.current_source_dir()),
|
||||||
|
'storage' : 'persistent',
|
||||||
|
'vm' : true,
|
||||||
|
'firmware' : 'auto',
|
||||||
|
},
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -e
|
||||||
|
|
||||||
|
TEST_DESCRIPTION="Test Multi-Profile UKI Boots"
|
||||||
|
|
||||||
|
# shellcheck source=test/test-functions
|
||||||
|
. "${TEST_BASE_DIR:?}/test-functions"
|
||||||
|
|
||||||
|
do_test "$@"
|
|
@ -376,6 +376,7 @@ foreach dirname : [
|
||||||
'TEST-83-BTRFS',
|
'TEST-83-BTRFS',
|
||||||
'TEST-84-STORAGETM',
|
'TEST-84-STORAGETM',
|
||||||
'TEST-85-NETWORK',
|
'TEST-85-NETWORK',
|
||||||
|
'TEST-86-MULTI-PROFILE-UKI',
|
||||||
]
|
]
|
||||||
subdir(dirname)
|
subdir(dirname)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
|
@ -29,6 +29,9 @@ if ! systemd-detect-virt --quiet --container; then
|
||||||
udevadm control --log-level debug
|
udevadm control --log-level debug
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
esp_guid=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
||||||
|
xbootldr_guid=BC13C2FF-59E6-4262-A352-B275FD6F7172
|
||||||
|
|
||||||
machine="$(uname -m)"
|
machine="$(uname -m)"
|
||||||
if [ "${machine}" = "x86_64" ]; then
|
if [ "${machine}" = "x86_64" ]; then
|
||||||
root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
|
root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
|
||||||
|
@ -1432,6 +1435,82 @@ EOF
|
||||||
systemd-dissect -U "$imgs/mnt"
|
systemd-dissect -U "$imgs/mnt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testcase_fallback_partitions() {
|
||||||
|
local workdir image defs
|
||||||
|
|
||||||
|
workdir="$(mktemp --directory "/tmp/test-repart.fallback.XXXXXXXXXX")"
|
||||||
|
# shellcheck disable=SC2064
|
||||||
|
trap "rm -rf '${workdir:?}'" RETURN
|
||||||
|
|
||||||
|
image="$workdir/image.img"
|
||||||
|
defs="$workdir/defs"
|
||||||
|
mkdir "$defs"
|
||||||
|
|
||||||
|
tee "$defs/10-esp.conf" <<EOF
|
||||||
|
[Partition]
|
||||||
|
Type=esp
|
||||||
|
Format=vfat
|
||||||
|
SizeMinBytes=10M
|
||||||
|
EOF
|
||||||
|
|
||||||
|
tee "$defs/20-xbootldr.conf" <<EOF
|
||||||
|
[Partition]
|
||||||
|
Type=xbootldr
|
||||||
|
Format=vfat
|
||||||
|
SizeMinBytes=100M
|
||||||
|
SupplementFor=10-esp
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Blank disk => big ESP should be created
|
||||||
|
|
||||||
|
systemd-repart --empty=create --size=auto --dry-run=no --definitions="$defs" "$image"
|
||||||
|
|
||||||
|
output=$(sfdisk -d "$image")
|
||||||
|
assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output"
|
||||||
|
assert_not_in "${image}2" "$output"
|
||||||
|
|
||||||
|
# Disk with small ESP => ESP grows
|
||||||
|
|
||||||
|
sfdisk "$image" <<EOF
|
||||||
|
label: gpt
|
||||||
|
size=10M, type=${esp_guid}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemd-repart --dry-run=no --definitions="$defs" "$image"
|
||||||
|
|
||||||
|
output=$(sfdisk -d "$image")
|
||||||
|
assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output"
|
||||||
|
assert_not_in "${image}2" "$output"
|
||||||
|
|
||||||
|
# Disk with small ESP that can't grow => XBOOTLDR created
|
||||||
|
|
||||||
|
truncate -s 150M "$image"
|
||||||
|
sfdisk "$image" <<EOF
|
||||||
|
label: gpt
|
||||||
|
size=10M, type=${esp_guid},
|
||||||
|
size=10M, type=${root_guid},
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemd-repart --dry-run=no --definitions="$defs" "$image"
|
||||||
|
|
||||||
|
output=$(sfdisk -d "$image")
|
||||||
|
assert_in "${image}1 : start= 2048, size= 20480, type=${esp_guid}" "$output"
|
||||||
|
assert_in "${image}3 : start= 43008, size= 264152, type=${xbootldr_guid}" "$output"
|
||||||
|
|
||||||
|
# Disk with existing XBOOTLDR partition => XBOOTLDR grows, small ESP created
|
||||||
|
|
||||||
|
sfdisk "$image" <<EOF
|
||||||
|
label: gpt
|
||||||
|
size=10M, type=${xbootldr_guid},
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemd-repart --dry-run=no --definitions="$defs" "$image"
|
||||||
|
|
||||||
|
output=$(sfdisk -d "$image")
|
||||||
|
assert_in "${image}1 : start= 2048, size= 204800, type=${xbootldr_guid}" "$output"
|
||||||
|
assert_in "${image}2 : start= 206848, size= 100312, type=${esp_guid}" "$output"
|
||||||
|
}
|
||||||
|
|
||||||
OFFLINE="yes"
|
OFFLINE="yes"
|
||||||
run_testcases
|
run_testcases
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -eux
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
export SYSTEMD_LOG_LEVEL=debug
|
||||||
|
|
||||||
|
bootctl
|
||||||
|
|
||||||
|
CURRENT_UKI=$(bootctl --print-stub-path)
|
||||||
|
|
||||||
|
echo "CURRENT UKI ($CURRENT_UKI):"
|
||||||
|
ukify inspect "$CURRENT_UKI"
|
||||||
|
if test -f /run/systemd/stub/profile; then
|
||||||
|
echo "CURRENT PROFILE:"
|
||||||
|
cat /run/systemd/stub/profile
|
||||||
|
fi
|
||||||
|
echo "CURRENT MEASUREMENT:"
|
||||||
|
/usr/lib/systemd/systemd-measure --current
|
||||||
|
if test -f /run/systemd/tpm2-pcr-signature.json ; then
|
||||||
|
echo "CURRENT SIGNATURE:"
|
||||||
|
jq < /run/systemd/tpm2-pcr-signature.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "CURRENT EVENT LOG + PCRS:"
|
||||||
|
/usr/lib/systemd/systemd-pcrlock
|
||||||
|
|
||||||
|
if test ! -f /run/systemd/stub/profile; then
|
||||||
|
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out /root/pcrsign.private.pem
|
||||||
|
openssl rsa -pubout -in /root/pcrsign.private.pem -out /root/pcrsign.public.pem
|
||||||
|
|
||||||
|
ukify build --extend="$CURRENT_UKI" --output=/tmp/extended0.efi --profile='ID=profile0
|
||||||
|
TITLE="Profile Zero"' --measure-base="$CURRENT_UKI" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
|
||||||
|
|
||||||
|
ukify build --extend=/tmp/extended0.efi --output=/tmp/extended1.efi --profile='ID=profile1
|
||||||
|
TITLE="Profile One"' --measure-base=/tmp/extended0.efi --cmdline="testprofile1=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
|
||||||
|
|
||||||
|
ukify build --extend=/tmp/extended1.efi --output=/tmp/extended2.efi --profile='ID=profile2
|
||||||
|
TITLE="Profile Two"' --measure-base=/tmp/extended1.efi --cmdline="testprofile2=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
|
||||||
|
|
||||||
|
echo "EXTENDED UKI:"
|
||||||
|
ukify inspect /tmp/extended2.efi
|
||||||
|
rm /tmp/extended0.efi /tmp/extended1.efi
|
||||||
|
mv /tmp/extended2.efi "$CURRENT_UKI"
|
||||||
|
|
||||||
|
# Prepare a disk image, locked to the PCR measurements of the UKI we just generated
|
||||||
|
truncate -s 32M /root/encrypted.raw
|
||||||
|
echo -n "geheim" > /root/encrypted.secret
|
||||||
|
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom /root/encrypted.raw --key-file=/root/encrypted.secret
|
||||||
|
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs= --tpm2-public-key=/root/pcrsign.public.pem --unlock-key-file=/root/encrypted.secret /root/encrypted.raw
|
||||||
|
rm -f /root/encrypted.secret
|
||||||
|
|
||||||
|
reboot
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. /run/systemd/stub/profile
|
||||||
|
|
||||||
|
# Validate that with the current profile we can fulfill the PCR 11 policy
|
||||||
|
systemd-cryptsetup attach multiprof /root/encrypted.raw - tpm2-device=auto,headless=1
|
||||||
|
systemd-cryptsetup detach multiprof
|
||||||
|
|
||||||
|
if [ "$ID" = "profile0" ]; then
|
||||||
|
grep -v testprofile /proc/cmdline
|
||||||
|
echo "default $(basename "$CURRENT_UKI")@profile1" > "$(bootctl -p)/loader/loader.conf"
|
||||||
|
reboot
|
||||||
|
exit 0
|
||||||
|
elif [ "$ID" = "profile1" ]; then
|
||||||
|
grep testprofile1=1 /proc/cmdline
|
||||||
|
echo "default $(basename "$CURRENT_UKI")@profile2" > "$(bootctl -p)/loader/loader.conf"
|
||||||
|
reboot
|
||||||
|
exit 0
|
||||||
|
elif [ "$ID" = "profile2" ]; then
|
||||||
|
grep testprofile2=1 /proc/cmdline
|
||||||
|
rm /root/encrypted.raw
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch /testok
|
|
@ -19,5 +19,5 @@ Q /var/lib/machines 0700 - - -
|
||||||
# systemd-nspawn --ephemeral places snapshots) we are more strict, to
|
# systemd-nspawn --ephemeral places snapshots) we are more strict, to
|
||||||
# avoid removing unrelated temporary files.
|
# avoid removing unrelated temporary files.
|
||||||
|
|
||||||
R!$ /var/lib/machines/.#*
|
R! /var/lib/machines/.#*
|
||||||
R!$ /.#machine.*
|
R! /.#machine.*
|
||||||
|
|
|
@ -14,10 +14,10 @@ x /var/tmp/systemd-private-%b-*
|
||||||
X /var/tmp/systemd-private-%b-*/tmp
|
X /var/tmp/systemd-private-%b-*/tmp
|
||||||
|
|
||||||
# Remove top-level private temporary directories on each boot
|
# Remove top-level private temporary directories on each boot
|
||||||
R!$ /tmp/systemd-private-*
|
R! /tmp/systemd-private-*
|
||||||
R!$ /var/tmp/systemd-private-*
|
R! /var/tmp/systemd-private-*
|
||||||
|
|
||||||
# Handle lost systemd-coredump temp files. They could be lost on old filesystems,
|
# Handle lost systemd-coredump temp files. They could be lost on old filesystems,
|
||||||
# for example, after hard reboot.
|
# for example, after hard reboot.
|
||||||
x /var/lib/systemd/coredump/.#core*.%b*
|
x /var/lib/systemd/coredump/.#core*.%b*
|
||||||
r!$ /var/lib/systemd/coredump/.#*
|
r! /var/lib/systemd/coredump/.#*
|
||||||
|
|
Loading…
Reference in New Issue