static void *arg_key = NULL;
static size_t arg_key_size = 0;
static EVP_PKEY *arg_private_key = NULL;
+static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
+static char *arg_private_key_source = NULL;
static X509 *arg_certificate = NULL;
static char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_seal_key_handle = 0;
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);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, 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;
_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;
char **exclude_files_target;
char **make_directories;
char **subvolumes;
+ char *default_subvolume;
EncryptMode encrypt;
VerityMode verity;
char *verity_match_key;
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);
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);
}
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) {
}
}
+static int config_parse_default_subvolume(
+ 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) {
+
+ char **subvol = ASSERT_PTR(data);
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ if (isempty(rvalue)) {
+ *subvol = mfree(*subvol);
+ return 0;
+ }
+
+ r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &p);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to expand specifiers in DefaultSubvolume= parameter, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ return free_and_replace(*subvol, p);
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
static int config_parse_gpt_flags(
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);
+ 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);
+ 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", "DefaultSubvolume", config_parse_default_subvolume, 0, &p->default_subvolume },
+ { "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;
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
"Subvolumes= cannot be used with --offline=yes");
+ if (p->default_subvolume && arg_offline > 0)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "DefaultSubvolume= cannot be used with --offline=yes");
+
+ if (p->default_subvolume && !path_strv_contains(p->subvolumes, p->default_subvolume))
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "DefaultSubvolume= must be one of the paths in Subvolumes=");
+
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((IN_SET(p->type.designator,
PARTITION_ROOT_VERITY,
if (r < 0)
return r;
- /* Make sure we only write the partition bar once, even if we're writing the partition table twice to
- * communicate roothashes. */
+ /* Only write the partition bar once, even if we're writing the partition table twice to communicate
+ * roothashes. */
if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !late) {
putc('\n', stdout);
return 0;
}
+static bool loop_device_error_is_fatal(const Partition *p, int r) {
+ assert(p);
+ return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes) || p->default_subvolume;
+}
+
static int partition_target_prepare(
Context *context,
Partition *p,
if (arg_offline <= 0) {
r = loop_device_make(whole_fd, O_RDWR, p->offset, size, context->sector_size, 0, LOCK_EX, &d);
- if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes)))
+ if (r < 0 && loop_device_error_is_fatal(p, r))
return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
if (r >= 0) {
t->loop = TAKE_PTR(d);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Must provide all PCR values when using TPM2 device key.");
} else {
- r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
+ r = tpm2_context_new_or_warn(arg_tpm2_device, &tpm2_context);
if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
+ return r;
if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) {
r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
if (PARTITION_EXISTS(p)) /* Never format existing partitions */
return 0;
- /* Minimized partitions will use the copy blocks logic so let's make sure to skip those here. */
+ /* Minimized partitions will use the copy blocks logic so skip those here. */
if (p->copy_blocks_fd >= 0)
return 0;
return 0;
}
+static int set_default_subvolume(Partition *p, const char *root) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(p);
+ assert(root);
+
+ if (!p->default_subvolume)
+ return 0;
+
+ path = path_join(root, p->default_subvolume);
+ if (!path)
+ return log_oom();
+
+ r = btrfs_subvol_make_default(path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make '%s' the default subvolume: %m", p->default_subvolume);
+
+ return 0;
+}
+
static bool partition_needs_populate(Partition *p) {
assert(p);
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
if (do_make_directories(p, fs) < 0)
_exit(EXIT_FAILURE);
+ if (set_default_subvolume(p, fs) < 0)
+ _exit(EXIT_FAILURE);
+
r = syncfs_path(AT_FDCWD, fs);
if (r < 0) {
log_error_errno(r, "Failed to synchronize written files: %m");
if (!p->format)
continue;
- /* Minimized partitions will use the copy blocks logic so let's make sure to skip those here. */
+ /* Minimized partitions will use the copy blocks logic so skip those here. */
if (p->copy_blocks_fd >= 0)
continue;
assert(p->new_size != UINT64_MAX);
assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
- /* If we're doing encryption, we make sure we keep free space at the end which is required
+ /* If we're doing encryption, keep free space at the end which is required
* for cryptsetup's offline encryption. */
r = partition_target_prepare(context, p,
p->new_size - (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0),
return r;
/* The mkfs binary we invoked might have removed our temporary file when we're not operating
- * on a loop device, so let's make sure we open the file again to make sure our file
- * descriptor points to any potential new file. */
+ * on a loop device, so open the file again to make sure our file descriptor points to actual
+ * new file. */
if (t->fd >= 0 && t->path && !t->loop) {
safe_close(t->fd);
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 eligible 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 eligible 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;
if (arg_offline <= 0) {
r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, context->sector_size, 0, LOCK_EX, &d);
- if (r < 0 && (arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !strv_isempty(p->subvolumes)))
+ if (r < 0 && loop_device_error_is_fatal(p, r))
return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
}
" Specify disk image dissection policy\n"
" --definitions=DIR Find partition definitions in specified directory\n"
" --key-file=PATH Key to use when encrypting partitions\n"
- " --private-key=PATH Private key to use when generating verity roothash\n"
- " signatures\n"
+ " --private-key=PATH|URI\n"
+ " Private key to use when generating verity roothash\n"
+ " signatures, or an engine or provider specific\n"
+ " designation if --private-key-source= is used.\n"
+ " --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
+ " Specify how to use the --private-key=. Allows to use\n"
+ " an OpenSSL engine/provider when generating verity\n"
+ " roothash signatures\n"
" --certificate=PATH PEM certificate to use when generating verity\n"
" roothash signatures\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n"
" -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(),
}
static int parse_argv(int argc, char *argv[]) {
+ _cleanup_free_ char *private_key = NULL;
enum {
ARG_VERSION = 0x100,
ARG_JSON,
ARG_KEY_FILE,
ARG_PRIVATE_KEY,
+ ARG_PRIVATE_KEY_SOURCE,
ARG_CERTIFICATE,
ARG_TPM2_DEVICE,
ARG_TPM2_DEVICE_KEY,
ARG_OFFLINE,
ARG_COPY_FROM,
ARG_MAKE_DDI,
+ ARG_GENERATE_FSTAB,
+ ARG_GENERATE_CRYPTTAB,
};
static const struct option options[] = {
{ "json", required_argument, NULL, ARG_JSON },
{ "key-file", required_argument, NULL, ARG_KEY_FILE },
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
+ { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
{ "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 },
{}
};
}
case ARG_PRIVATE_KEY: {
- _cleanup_(erase_and_freep) char *k = NULL;
- size_t n = 0;
-
- r = read_full_file_full(
- AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
- READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
- NULL,
- &k, &n);
+ r = free_and_strdup_warn(&private_key, optarg);
if (r < 0)
- return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
+ return r;
+ break;
+ }
- EVP_PKEY_free(arg_private_key);
- arg_private_key = NULL;
- r = parse_private_key(k, n, &arg_private_key);
+ case ARG_PRIVATE_KEY_SOURCE:
+ r = parse_openssl_key_source_argument(
+ optarg,
+ &arg_private_key_source,
+ &arg_private_key_source_type);
if (r < 0)
return r;
break;
- }
case ARG_CERTIFICATE: {
_cleanup_free_ char *cert = NULL;
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;
*p = gpt_partition_type_override_architecture(*p, arg_architecture);
}
+ if (private_key && arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+ _cleanup_(erase_and_freep) char *k = NULL;
+ size_t n = 0;
+
+ r = read_full_file_full(
+ AT_FDCWD, private_key, UINT64_MAX, SIZE_MAX,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
+ NULL,
+ &k, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read key file '%s': %m", private_key);
+
+ r = parse_private_key(k, n, &arg_private_key);
+ if (r < 0)
+ return r;
+ } else if (private_key &&
+ IN_SET(arg_private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)) {
+ /* This must happen after parse_x509_certificate() is called above, otherwise
+ * signing later will get stuck as the parsed private key won't have the
+ * certificate, so this block cannot be inline in ARG_PRIVATE_KEY. */
+ r = openssl_load_key_from_token(
+ arg_private_key_source_type,
+ arg_private_key_source,
+ private_key,
+ &arg_private_key);
+ if (r < 0)
+ return log_error_errno(
+ r,
+ "Failed to load key '%s' from OpenSSL private key source %s: %m",
+ private_key,
+ arg_private_key_source);
+ }
+
return 1;
}
bool node_is_our_loop = false;
int r;
- log_show_color(true);
- log_parse_environment();
- log_open();
+ log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
DISSECT_IMAGE_GPT_ONLY |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_USR_NO_ROOT |
- DISSECT_IMAGE_REQUIRE_ROOT,
+ DISSECT_IMAGE_REQUIRE_ROOT |
+ DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);
if (!d)
return log_oom();
- r = search_and_access(d, F_OK, NULL, CONF_PATHS_USR_STRV("systemd/repart/definitions"), &dp);
+ r = search_and_access(d, F_OK, NULL, CONF_PATHS_STRV("systemd/repart/definitions"), &dp);
if (r < 0)
return log_error_errno(r, "DDI type '%s' is not defined: %m", arg_make_ddi);
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;