static EmptyMode arg_empty = EMPTY_UNSET;
static bool arg_dry_run = true;
-static const char *arg_node = NULL;
+static char *arg_node = NULL;
static char *arg_root = NULL;
static char *arg_image = NULL;
static char **arg_definitions = NULL;
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_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_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 *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) {
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;
uint32_t ssz;
struct stat st;
- r = context_open_and_lock_backing_fd(context->node, arg_dry_run ? LOCK_SH : LOCK_EX,
- &context->backing_fd);
+ r = context_open_and_lock_backing_fd(
+ context->node,
+ arg_dry_run ? LOCK_SH : LOCK_EX,
+ &context->backing_fd);
if (r < 0)
return r;
if (fstat(context->backing_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", context->node);
- /* Auto-detect sector size if not specified. */
- r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz);
- if (r < 0)
- return log_error_errno(r, "Failed to probe sector size of '%s': %m", context->node);
+ if (IN_SET(arg_empty, EMPTY_REQUIRE, EMPTY_FORCE, EMPTY_CREATE) && S_ISREG(st.st_mode))
+ /* Don't probe sector size from partition table if we are supposed to start from an empty disk */
+ fs_secsz = ssz = 512;
+ else {
+ /* Auto-detect sector size if not specified. */
+ r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz);
+ if (r < 0)
+ return log_error_errno(r, "Failed to probe sector size of '%s': %m", context->node);
- /* If we found the sector size and we're operating on a block device, use it as the file
- * system sector size as well, as we know its the sector size of the actual block device and
- * not just the offset at which we found the GPT header. */
- if (r > 0 && S_ISBLK(st.st_mode))
- fs_secsz = ssz;
+ /* If we found the sector size and we're operating on a block device, use it as the file
+ * system sector size as well, as we know its the sector size of the actual block device and
+ * not just the offset at which we found the GPT header. */
+ if (r > 0 && S_ISBLK(st.st_mode))
+ fs_secsz = ssz;
+ }
r = fdisk_save_user_sector_size(c, /* phy= */ 0, ssz);
}
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
}
-static void context_bar_char_process_partition(
+static int context_bar_char_process_partition(
Context *context,
Partition *bar[],
size_t n,
Partition *p,
- size_t *ret_start) {
+ size_t **start_array,
+ size_t *n_start_array) {
uint64_t from, to, total;
size_t x, y;
assert(bar);
assert(n > 0);
assert(p);
+ assert(start_array);
+ assert(n_start_array);
if (p->dropped)
- return;
+ return 0;
assert(p->offset != UINT64_MAX);
assert(p->new_size != UINT64_MAX);
for (size_t i = x; i < y; i++)
bar[i] = p;
- *ret_start = x;
+ if (!GREEDY_REALLOC_APPEND(*start_array, *n_start_array, &x, 1))
+ return log_oom();
+
+ return 1;
}
static int partition_hint(const Partition *p, const char *node, char **ret) {
static int context_dump_partition_bar(Context *context) {
_cleanup_free_ Partition **bar = NULL;
_cleanup_free_ size_t *start_array = NULL;
+ size_t n_start_array = 0;
Partition *last = NULL;
bool z = false;
size_t c, j = 0;
+ int r;
assert_se((c = columns()) >= 2);
c -= 2; /* We do not use the leftmost and rightmost character cell */
if (!bar)
return log_oom();
- start_array = new(size_t, context->n_partitions);
- if (!start_array)
- return log_oom();
-
- LIST_FOREACH(partitions, p, context->partitions)
- context_bar_char_process_partition(context, bar, c, p, start_array + j++);
+ LIST_FOREACH(partitions, p, context->partitions) {
+ r = context_bar_char_process_partition(context, bar, c, p, &start_array, &n_start_array);
+ if (r < 0)
+ return r;
+ }
putc(' ', stdout);
fputs(ansi_normal(), stdout);
putc('\n', stdout);
- for (size_t i = 0; i < context->n_partitions; i++) {
+ for (size_t i = 0; i < n_start_array; i++) {
_cleanup_free_ char **line = NULL;
line = new0(char*, c);
j = 0;
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_free_ char *d = NULL;
+
+ if (p->dropped)
+ continue;
+
j++;
- if (i < context->n_partitions - j) {
+ if (i < n_start_array - j) {
if (line[start_array[j-1]]) {
const char *e;
return log_oom();
}
- } else if (i == context->n_partitions - j) {
+ } else if (i == n_start_array - j) {
_cleanup_free_ char *hint = NULL;
(void) partition_hint(p, context->node, &hint);
if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
#if HAVE_TPM2
+ _cleanup_(iovec_done) struct iovec pubkey = {}, blob = {}, srk = {};
+ _cleanup_(iovec_done_erase) struct iovec secret = {};
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- _cleanup_(erase_and_freep) void *secret = NULL;
- _cleanup_free_ void *pubkey = NULL;
- _cleanup_free_ void *blob = NULL, *srk_buf = NULL;
- size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
ssize_t base64_encoded_size;
int keyslot;
TPM2Flags flags = 0;
if (arg_tpm2_public_key_pcr_mask != 0) {
- r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey, &pubkey_size);
+ r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey.iov_base, &pubkey.iov_len);
if (r < 0) {
if (arg_tpm2_public_key || r != -ENOENT)
return log_error_errno(r, "Failed to read TPM PCR public key: %m");
}
TPM2B_PUBLIC public;
- if (pubkey) {
- r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
+ if (iovec_is_set(&pubkey)) {
+ r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
if (r < 0)
return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
}
r = tpm2_calculate_sealing_policy(
arg_tpm2_hash_pcr_values,
arg_tpm2_n_hash_pcr_values,
- pubkey ? &public : NULL,
+ iovec_is_set(&pubkey) ? &public : NULL,
/* use_pin= */ false,
arg_tpm2_pcrlock ? &pcrlock_policy : NULL,
&policy);
arg_tpm2_seal_key_handle,
&device_key_public,
/* attributes= */ NULL,
- /* secret= */ NULL, /* secret_size= */ 0,
+ /* secret= */ NULL,
&policy,
/* pin= */ NULL,
- &secret, &secret_size,
- &blob, &blob_size,
- &srk_buf, &srk_buf_size);
+ &secret,
+ &blob,
+ &srk);
else
r = tpm2_seal(tpm2_context,
arg_tpm2_seal_key_handle,
&policy,
/* pin= */ NULL,
- &secret, &secret_size,
- &blob, &blob_size,
+ &secret,
+ &blob,
/* ret_primary_alg= */ NULL,
- &srk_buf, &srk_buf_size);
+ &srk);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
- base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+ base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
if (base64_encoded_size < 0)
return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
keyslot,
hash_pcr_mask,
hash_pcr_bank,
- pubkey, pubkey_size,
+ &pubkey,
arg_tpm2_public_key_pcr_mask,
/* primary_alg= */ 0,
- blob, blob_size,
- policy.buffer, policy.size,
- NULL, 0, /* no salt because tpm2_seal has no pin */
- srk_buf, srk_buf_size,
+ &blob,
+ &IOVEC_MAKE(policy.buffer, policy.size),
+ /* salt= */ NULL, /* no salt because tpm2_seal has no pin */
+ &srk,
+ &pcrlock_policy.nv_handle,
flags,
&v);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
ERR_error_string(ERR_get_error(), NULL));
- ret_signature->iov_base = TAKE_PTR(sig);
- ret_signature->iov_len = sigsz;
+ *ret_signature = IOVEC_MAKE(TAKE_PTR(sig), sigsz);
return 0;
#else
if (r < 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. */
+
+ if (t->fd >= 0 && t->path && !t->loop) {
+ safe_close(t->fd);
+ t->fd = open(t->path, O_RDWR|O_CLOEXEC);
+ if (t->fd < 0)
+ return log_error_errno(errno, "Failed to reopen temporary file: %m");
+ }
+
log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
/* If we're writing to a loop device, we can now mount the empty filesystem and populate it. */
if (r < 0)
return r;
- r = path_is_mount_point(resolved, NULL, 0);
+ r = path_is_mount_point(resolved);
if (r < 0)
return r;
if (r == 0) /* Not a mount point, then it's not a partition of its own, let's not automatically use it. */
if (S_ISREG(st.st_mode))
size = st.st_size;
else if (S_ISBLK(st.st_mode)) {
- if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
- return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
+ r = blockdev_get_device_size(source_fd, &size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of block device to copy from: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", opened);
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;
" -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, *private_key_uri = NULL;
enum {
ARG_VERSION = 0x100,
ARG_JSON,
ARG_KEY_FILE,
ARG_PRIVATE_KEY,
+ ARG_PRIVATE_KEY_URI,
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-uri", required_argument, NULL, ARG_PRIVATE_KEY_URI },
{ "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_URI: {
+ r = free_and_strdup_warn(&private_key_uri, optarg);
if (r < 0)
return r;
break;
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;
/* By default operate on /sysusr/ or /sysroot/ when invoked in the initrd. We prefer the
* former, if it is mounted, so that we have deterministic behaviour on systems where /usr/
* is vendor-supplied but the root fs formatted on first boot. */
- r = path_is_mount_point("/sysusr/usr", NULL, 0);
+ r = path_is_mount_point("/sysusr/usr");
if (r <= 0) {
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Unable to determine whether /sysusr/usr is a mount point, assuming it is not: %m");
return log_oom();
}
- arg_node = argc > optind ? argv[optind] : NULL;
+ if (argc > optind) {
+ arg_node = strdup(argv[optind]);
+ if (!arg_node)
+ return log_oom();
+ }
if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
*p = gpt_partition_type_override_architecture(*p, arg_architecture);
}
+ if (private_key && private_key_uri)
+ return log_error_errno(
+ SYNTHETIC_ERRNO(EINVAL),
+ "Cannot specify both --private-key= and --private-key-uri=.");
+
+ if (private_key) {
+ _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_uri) {
+ /* 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(private_key_uri, &arg_private_key);
+ if (r < 0)
+ return log_error_errno(
+ r,
+ "Failed to load key '%s' from OpenSSL provider: %m",
+ private_key);
+ }
+
return 1;
}
assert(loop_device);
- if (ioctl(*fd, BLKGETSIZE64, ¤t_size) < 0)
- return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
+ r = blockdev_get_device_size(*fd, ¤t_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of block device %s: %m", node);
} else {
r = stat_verify_regular(&st);
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, arg_root, CONF_PATHS_USR_STRV("systemd/repart/definitions"), &dp);
+ r = search_and_access(d, F_OK, NULL, CONF_PATHS_USR_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;