repart: add --append-fstab parameter

If --generate-fstab=PATH is used, there is the possibility that the
fstab file already exists, making systemd-repart fail.

This commit will add a new --append-fstab= parameter, that will read
the file and merge it with the new generated content.  Using the
comments, the command can separate the automatic-generated section from
the user-provided section, allowing for the next append the replacement
only of the automatic-generated section, keeping the user one.

Signed-off-by: Alberto Planas <aplanas@suse.com>
This commit is contained in:
Alberto Planas
2025-04-24 15:02:27 +02:00
parent ba2d8107e2
commit fc0cc3065c
2 changed files with 95 additions and 3 deletions

View File

@@ -619,6 +619,30 @@
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--append-fstab=</option></term>
<listitem><para>Takes one of <literal>no</literal>, <literal>auto</literal> or
<literal>replace</literal>. Controls how the generated
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
file by <option>--generate-fstab=</option> will behave in case that there is a previously
existing file.</para>
<para>If <literal>no</literal> <command>systemd-repart</command> will complain and abort in
case that there is a file. This is the default behaviour. If <literal>replace</literal>
the file will be silently replaced with the new generated one.</para>
<para>If <literal>auto</literal> <command>systemd-repart</command> will search in the pre-existing file
the section that belong to the automatically generated content and will replace it with the newer
generated content, and keep the user provided section if there is one. The generated section is
identified looking for the automatic content surrounded by
<literal># Start section ↓ of automatically generated fstab by systemd-repart</literal> and
<literal># End section ↑ of automatically generated fstab by systemd-repart</literal>. The content
that is before and after those comments are considered user provided, and kept in the new file.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--generate-fstab=<replaceable>PATH</replaceable></option></term>

View File

@@ -111,6 +111,9 @@
#define APIVFS_TMP_DIRS_NULSTR "proc\0sys\0dev\0tmp\0run\0var/tmp\0"
#define AUTOMATIC_FSTAB_HEADER_START "# Start section ↓ of automatically generated fstab by systemd-repart"
#define AUTOMATIC_FSTAB_HEADER_END "# End section ↑ of automatically generated fstab by systemd-repart"
/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
* are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
* sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
@@ -135,6 +138,14 @@ typedef enum FilterPartitionType {
_FILTER_PARTITIONS_INVALID = -EINVAL,
} FilterPartitionsType;
typedef enum AppendMode {
APPEND_NO,
APPEND_AUTO,
APPEND_REPLACE,
_APPEND_MODE_MAX,
_APPEND_MODE_INVALID = -EINVAL,
} AppendMode;
static EmptyMode arg_empty = EMPTY_UNSET;
static bool arg_dry_run = true;
static char *arg_node = NULL;
@@ -181,6 +192,7 @@ static int arg_offline = -1;
static char **arg_copy_from = NULL;
static char *arg_copy_source = NULL;
static char *arg_make_ddi = NULL;
static AppendMode arg_append_fstab = APPEND_NO;
static char *arg_generate_fstab = NULL;
static char *arg_generate_crypttab = NULL;
static Set *arg_verity_settings = NULL;
@@ -455,6 +467,12 @@ static const char *empty_mode_table[_EMPTY_MODE_MAX] = {
[EMPTY_CREATE] = "create",
};
static const char *append_mode_table[_APPEND_MODE_MAX] = {
[APPEND_NO] = "no",
[APPEND_AUTO] = "auto",
[APPEND_REPLACE] = "replace",
};
static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
[ENCRYPT_OFF] = "off",
[ENCRYPT_KEY_FILE] = "key-file",
@@ -476,6 +494,7 @@ static const char *minimize_mode_table[_MINIMIZE_MODE_MAX] = {
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(empty_mode, EmptyMode);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(append_mode, AppendMode);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST);
@@ -7495,7 +7514,7 @@ static bool need_fstab(const Context *context) {
static int context_fstab(Context *context) {
_cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *path = NULL;
_cleanup_free_ char *path = NULL, *c = NULL;
int r;
assert(context);
@@ -7517,7 +7536,7 @@ static int context_fstab(Context *context) {
if (r < 0)
return log_error_errno(r, "Failed to open temporary file for %s: %m", path);
fprintf(f, "# Automatically generated by systemd-repart\n\n");
fputs(AUTOMATIC_FSTAB_HEADER_START "\n", f);
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_free_ char *what = NULL, *options = NULL;
@@ -7552,7 +7571,38 @@ static int context_fstab(Context *context) {
}
}
r = flink_tmpfile(f, t, path, 0);
fputs(AUTOMATIC_FSTAB_HEADER_END "\n", f);
switch (arg_append_fstab) {
case APPEND_AUTO: {
r = read_full_file(path, &c, NULL);
if (r < 0) {
if (r == -ENOENT) {
log_debug("File fstab not found in %s", path);
break;
}
return log_error_errno(r, "Failed to open %s: %m", path);
}
const char *acs, *ace;
acs = find_line(c, AUTOMATIC_FSTAB_HEADER_START);
if (acs) {
fwrite(c, 1, acs - c, f);
ace = find_line_after(acs, AUTOMATIC_FSTAB_HEADER_END);
if (ace)
fputs(ace, f);
} else
fputs(c, f);
break;
}
case APPEND_NO:
case APPEND_REPLACE:
break;
default:
assert_not_reached();
}
r = flink_tmpfile(f, t, path, IN_SET(arg_append_fstab, APPEND_AUTO, APPEND_REPLACE) ? LINK_TMPFILE_REPLACE : 0);
if (r < 0)
return log_error_errno(r, "Failed to link temporary file to %s: %m", path);
@@ -8174,6 +8224,8 @@ static int help(void) {
" -C --make-ddi=confext Make a configuration extension DDI\n"
" -P --make-ddi=portable Make a portable service DDI\n"
"\n%3$sAuxiliary Resource Generation:%4$s\n"
" --append-fstab=MODE One of no, auto, replace; controls how to join the\n"
" content of a pre-existing fstab with the generated one\n"
" --generate-fstab=PATH\n"
" Write fstab configuration to the given path\n"
" --generate-crypttab=PATH\n"
@@ -8229,6 +8281,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
ARG_OFFLINE,
ARG_COPY_FROM,
ARG_MAKE_DDI,
ARG_APPEND_FSTAB,
ARG_GENERATE_FSTAB,
ARG_GENERATE_CRYPTTAB,
ARG_LIST_DEVICES,
@@ -8275,6 +8328,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
{ "copy-from", required_argument, NULL, ARG_COPY_FROM },
{ "copy-source", required_argument, NULL, 's' },
{ "make-ddi", required_argument, NULL, ARG_MAKE_DDI },
{ "append-fstab", required_argument, NULL, ARG_APPEND_FSTAB },
{ "generate-fstab", required_argument, NULL, ARG_GENERATE_FSTAB },
{ "generate-crypttab", required_argument, NULL, ARG_GENERATE_CRYPTTAB },
{ "list-devices", no_argument, NULL, ARG_LIST_DEVICES },
@@ -8658,6 +8712,17 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
return r;
break;
case ARG_APPEND_FSTAB:
if (isempty(optarg)) {
arg_append_fstab = APPEND_AUTO;
break;
}
arg_append_fstab = append_mode_from_string(optarg);
if (arg_append_fstab < 0)
return log_error_errno(arg_append_fstab, "Failed to parse --append-fstab= parameter: %s", optarg);
break;
case ARG_GENERATE_FSTAB:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_generate_fstab);
if (r < 0)
@@ -8850,6 +8915,9 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
}
if (arg_append_fstab && !arg_generate_fstab)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No --generate-fstab= specified for --append-fstab=%s.", append_mode_to_string(arg_append_fstab));
*ret_certificate = TAKE_PTR(certificate);
*ret_private_key = TAKE_PTR(private_key);
*ret_ui = TAKE_PTR(ui);