diff --git a/man/repart.d.xml b/man/repart.d.xml
index f297933ccec..50a2de7c33d 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -734,6 +734,42 @@
+
+
+ MountPoint=
+
+ Specifies where and how the partition should be mounted. Takes at least one and at
+ most two fields separated with a colon (:). The first field specifies where the
+ partition should be mounted. The second field specifies extra mount options to append to the default
+ mount options. These fields correspond to the second and fourth column of the
+ fstab5
+ format. This setting may be specified multiple times to mount the partition multiple times. This can
+ be used to add mounts for different btrfs subvolumes located on the same btrfs partition.
+
+ Note that this setting is only taken into account when is
+ specified on the systemd-repart command line.
+
+
+
+
+
+ EncryptedVolume=
+
+ Specify how the encrypted partition should be set up. Takes at least one and at most
+ three fields separated with a colon (:). The first field specifies the encrypted
+ volume name under /dev/mapper/. If not specified, luks-UUID
+ will be used where UUID is the LUKS UUID. The second field specifies the keyfile
+ to use following the same format as specified in crypttab. The third field specifies a
+ comma-delimited list of crypttab options. These fields correspond to the first, third and fourth
+ column of the
+ crypttab5 format.
+
+
+ Note that this setting is only taken into account when
+ is specified on the systemd-repart command line.
+
+
+
diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml
index da5d5858459..7dfcfc90b31 100644
--- a/man/systemd-repart.xml
+++ b/man/systemd-repart.xml
@@ -588,6 +588,28 @@
+
+ PATH
+
+ Specifies a path where to write fstab entries for the mountpoints configured with
+ in the root directory specified with or
+ or in the host's root directory if neither is specified. Disabled by
+ default.
+
+
+
+
+
+ PATH
+
+ Specifies a path where to write crypttab entries for the encrypted volumes configured
+ with in the root directory specified with
+ or or in the host's root directory if
+ neither is specified. Disabled by default.
+
+
+
+
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 6f347a6e1fa..63e9dcfbd16 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -168,6 +168,8 @@ static int arg_offline = -1;
static char **arg_copy_from = NULL;
static char *arg_copy_source = NULL;
static char *arg_make_ddi = NULL;
+static char *arg_generate_fstab = NULL;
+static char *arg_generate_crypttab = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -186,6 +188,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_copy_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_make_ddi, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_generate_fstab, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_generate_crypttab, freep);
typedef struct FreeArea FreeArea;
@@ -215,6 +219,39 @@ typedef enum MinimizeMode {
_MINIMIZE_MODE_INVALID = -EINVAL,
} MinimizeMode;
+typedef struct PartitionMountPoint {
+ char *where;
+ char *options;
+} PartitionMountPoint;
+
+static void partition_mountpoint_free_many(PartitionMountPoint *f, size_t n) {
+ assert(f || n == 0);
+
+ FOREACH_ARRAY(i, f, n) {
+ free(i->where);
+ free(i->options);
+ }
+
+ free(f);
+}
+
+typedef struct PartitionEncryptedVolume {
+ char *name;
+ char *keyfile;
+ char *options;
+} PartitionEncryptedVolume;
+
+static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) {
+ if (!c)
+ return NULL;
+
+ free(c->name);
+ free(c->keyfile);
+ free(c->options);
+
+ return mfree(c);
+}
+
typedef struct Partition {
char *definition_path;
char **drop_in_files;
@@ -277,6 +314,11 @@ typedef struct Partition {
char *split_name_format;
char *split_path;
+ PartitionMountPoint *mountpoints;
+ size_t n_mountpoints;
+
+ PartitionEncryptedVolume *encrypted_volume;
+
struct Partition *siblings[_VERITY_MODE_MAX];
LIST_FIELDS(struct Partition, partitions);
@@ -426,6 +468,12 @@ static Partition* partition_free(Partition *p) {
free(p->split_name_format);
unlink_and_free(p->split_path);
+ partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
+ p->mountpoints = NULL;
+ p->n_mountpoints = 0;
+
+ partition_encrypted_volume_free(p->encrypted_volume);
+
return mfree(p);
}
@@ -461,6 +509,12 @@ static void partition_foreignize(Partition *p) {
p->read_only = -1;
p->growfs = -1;
p->verity = VERITY_OFF;
+
+ partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
+ p->mountpoints = NULL;
+ p->n_mountpoints = 0;
+
+ p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
}
static bool partition_type_exclude(const GptPartitionType *type) {
@@ -1678,41 +1732,163 @@ static int config_parse_uuid(
return 0;
}
+static int config_parse_mountpoint(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *where = NULL, *options = NULL;
+ Partition *p = ASSERT_PTR(data);
+ int r;
+
+ if (isempty(rvalue)) {
+ partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
+ return 0;
+ }
+
+ const char *q = rvalue;
+ r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
+ &where, &options, NULL);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax in %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+ if (r < 1) {
+ log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "Too few arguments in %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+ if (!isempty(q)) {
+ log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments in %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(where, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ if (!GREEDY_REALLOC(p->mountpoints, p->n_mountpoints + 1))
+ return log_oom();
+
+ p->mountpoints[p->n_mountpoints++] = (PartitionMountPoint) {
+ .where = TAKE_PTR(where),
+ .options = TAKE_PTR(options),
+ };
+
+ return 0;
+}
+
+static int config_parse_encrypted_volume(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL;
+ Partition *p = ASSERT_PTR(data);
+ int r;
+
+ if (isempty(rvalue)) {
+ p->encrypted_volume = mfree(p->encrypted_volume);
+ return 0;
+ }
+
+ const char *q = rvalue;
+ r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
+ &volume, &keyfile, &options, NULL);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax in %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+ if (r < 1) {
+ log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "Too few arguments in %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+ if (!isempty(q)) {
+ log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "Too many arguments in %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (!filename_is_valid(volume)) {
+ log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "Volume name %s is not valid, ignoring", volume);
+ return 0;
+ }
+
+ partition_encrypted_volume_free(p->encrypted_volume);
+
+ p->encrypted_volume = new(PartitionEncryptedVolume, 1);
+ if (!p->encrypted_volume)
+ return log_oom();
+
+ *p->encrypted_volume = (PartitionEncryptedVolume) {
+ .name = TAKE_PTR(volume),
+ .keyfile = TAKE_PTR(keyfile),
+ .options = TAKE_PTR(options),
+ };
+
+ return 0;
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF, "Invalid minimize mode");
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
ConfigTableItem table[] = {
- { "Partition", "Type", config_parse_type, 0, &p->type },
- { "Partition", "Label", config_parse_label, 0, &p->new_label },
- { "Partition", "UUID", config_parse_uuid, 0, p },
- { "Partition", "Priority", config_parse_int32, 0, &p->priority },
- { "Partition", "Weight", config_parse_weight, 0, &p->weight },
- { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
- { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min },
- { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max },
- { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min },
- { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max },
- { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
- { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
- { "Partition", "Format", config_parse_fstype, 0, &p->format },
- { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files },
- { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source },
- { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
- { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories },
- { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
- { "Partition", "Verity", config_parse_verity, 0, &p->verity },
- { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
- { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
- { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
- { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
- { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
- { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
- { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
- { "Partition", "Subvolumes", config_parse_make_dirs, 0, &p->subvolumes },
- { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size },
- { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
+ { "Partition", "Type", config_parse_type, 0, &p->type },
+ { "Partition", "Label", config_parse_label, 0, &p->new_label },
+ { "Partition", "UUID", config_parse_uuid, 0, p },
+ { "Partition", "Priority", config_parse_int32, 0, &p->priority },
+ { "Partition", "Weight", config_parse_weight, 0, &p->weight },
+ { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
+ { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min },
+ { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max },
+ { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min },
+ { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max },
+ { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
+ { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
+ { "Partition", "Format", config_parse_fstype, 0, &p->format },
+ { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files },
+ { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source },
+ { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
+ { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories },
+ { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
+ { "Partition", "Verity", config_parse_verity, 0, &p->verity },
+ { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
+ { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
+ { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
+ { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
+ { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
+ { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
+ { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
+ { "Partition", "Subvolumes", config_parse_make_dirs, 0, &p->subvolumes },
+ { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size },
+ { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
+ { "Partition", "MountPoint", config_parse_mountpoint, 0, p },
+ { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
{}
};
int r;
@@ -6130,6 +6306,177 @@ static int fd_apparent_size(int fd, uint64_t *ret) {
return 0;
}
+static bool need_fstab_one(const Partition *p) {
+ assert(p);
+
+ if (p->dropped)
+ return false;
+
+ if (!p->format)
+ return false;
+
+ if (p->n_mountpoints == 0)
+ return false;
+
+ return true;
+}
+
+static bool need_fstab(const Context *context) {
+ assert(context);
+
+ LIST_FOREACH(partitions, p, context->partitions)
+ if (need_fstab_one(p))
+ return true;
+
+ return false;
+}
+
+static int context_fstab(Context *context) {
+ _cleanup_(unlink_and_freep) char *t = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(context);
+
+ if (!arg_generate_fstab)
+ return false;
+
+ if (!need_fstab(context)) {
+ log_notice("MountPoint= is not specified for any elligible partitions, not generating %s",
+ arg_generate_fstab);
+ return 0;
+ }
+
+ path = path_join(arg_copy_source, arg_generate_fstab);
+ if (!path)
+ return log_oom();
+
+ r = fopen_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &t, &f);
+ 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");
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ _cleanup_free_ char *what = NULL, *options = NULL;
+
+ if (!need_fstab_one(p))
+ continue;
+
+ what = strjoin("UUID=", SD_ID128_TO_UUID_STRING(p->fs_uuid));
+ if (!what)
+ return log_oom();
+
+ FOREACH_ARRAY(mountpoint, p->mountpoints, p->n_mountpoints) {
+ r = partition_pick_mount_options(
+ p->type.designator,
+ p->format,
+ /* rw= */ true,
+ /* discard= */ !IN_SET(p->type.designator, PARTITION_ESP, PARTITION_XBOOTLDR),
+ &options,
+ NULL);
+ if (r < 0)
+ return r;
+
+ if (!strextend_with_separator(&options, ",", mountpoint->options))
+ return log_oom();
+
+ fprintf(f, "%s %s %s %s 0 %i\n",
+ what,
+ mountpoint->where,
+ p->format,
+ options,
+ p->type.designator == PARTITION_ROOT ? 1 : 2);
+ }
+ }
+
+ r = flink_tmpfile(f, t, path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to link temporary file to %s: %m", path);
+
+ log_info("%s written.", path);
+
+ return 0;
+}
+
+static bool need_crypttab_one(const Partition *p) {
+ assert(p);
+
+ if (p->dropped)
+ return false;
+
+ if (p->encrypt == ENCRYPT_OFF)
+ return false;
+
+ if (!p->encrypted_volume)
+ return false;
+
+ return true;
+}
+
+static bool need_crypttab(Context *context) {
+ assert(context);
+
+ LIST_FOREACH(partitions, p, context->partitions)
+ if (need_crypttab_one(p))
+ return true;
+
+ return false;
+}
+
+static int context_crypttab(Context *context) {
+ _cleanup_(unlink_and_freep) char *t = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(context);
+
+ if (!arg_generate_crypttab)
+ return false;
+
+ if (!need_crypttab(context)) {
+ log_notice("EncryptedVolume= is not specified for any elligible partitions, not generating %s",
+ arg_generate_crypttab);
+ return 0;
+ }
+
+ path = path_join(arg_copy_source, arg_generate_crypttab);
+ if (!path)
+ return log_oom();
+
+ r = fopen_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &t, &f);
+ 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");
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ _cleanup_free_ char *volume = NULL;
+
+ if (!need_crypttab_one(p))
+ continue;
+
+ if (!p->encrypted_volume->name && asprintf(&volume, "luks-%s", SD_ID128_TO_UUID_STRING(p->luks_uuid)) < 0)
+ return log_oom();
+
+ fprintf(f, "%s UUID=%s %s %s\n",
+ p->encrypted_volume->name ?: volume,
+ SD_ID128_TO_UUID_STRING(p->luks_uuid),
+ isempty(p->encrypted_volume->keyfile) ? "-" : p->encrypted_volume->keyfile,
+ strempty(p->encrypted_volume->options));
+ }
+
+ r = flink_tmpfile(f, t, path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to link temporary file to %s: %m", path);
+
+ log_info("%s written.", path);
+
+ return 0;
+}
+
static int context_minimize(Context *context) {
const char *vt = NULL;
int r;
@@ -6496,6 +6843,10 @@ static int help(void) {
" -S --make-ddi=sysext Make a system extension DDI\n"
" -C --make-ddi=confext Make a configuration extension DDI\n"
" -P --make-ddi=portable Make a portable service DDI\n"
+ " --generate-fstab=PATH\n"
+ " Write fstab configuration to the given path\n"
+ " --generate-crypttab=PATH\n"
+ " Write crypttab configuration to the given path\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -6546,6 +6897,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_OFFLINE,
ARG_COPY_FROM,
ARG_MAKE_DDI,
+ ARG_GENERATE_FSTAB,
+ ARG_GENERATE_CRYPTTAB,
};
static const struct option options[] = {
@@ -6587,6 +6940,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "copy-from", required_argument, NULL, ARG_COPY_FROM },
{ "copy-source", required_argument, NULL, 's' },
{ "make-ddi", required_argument, NULL, ARG_MAKE_DDI },
+ { "generate-fstab", required_argument, NULL, ARG_GENERATE_FSTAB },
+ { "generate-crypttab", required_argument, NULL, ARG_GENERATE_CRYPTTAB },
{}
};
@@ -6964,6 +7319,18 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
+ case ARG_GENERATE_FSTAB:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_generate_fstab);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_GENERATE_CRYPTTAB:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_generate_crypttab);
+ if (r < 0)
+ return r;
+ break;
+
case '?':
return -EINVAL;
@@ -7713,6 +8080,14 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ r = context_fstab(context);
+ if (r < 0)
+ return r;
+
+ r = context_crypttab(context);
+ if (r < 0)
+ return r;
+
r = context_minimize(context);
if (r < 0)
return r;