return 0;
}
+typedef struct DecryptedPartitionTarget {
+ int fd;
+ char *volume;
+ struct crypt_device *device;
+} DecryptedPartitionTarget;
+
+static DecryptedPartitionTarget* decrypted_partition_target_free(DecryptedPartitionTarget *t) {
+#ifdef HAVE_LIBCRYPTSETUP
+ _cleanup_free_ char *name = NULL;
+ int r;
+
+ if (!t)
+ return NULL;
+
+ r = path_extract_filename(t->volume, &name);
+ if (r < 0) {
+ assert(r == -ENOMEM);
+ log_oom();
+ }
+
+ safe_close(t->fd);
+
+ if (name) {
+ /* udev or so might access out block device in the background while we are done. Let's hence
+ * force detach the volume. We sync'ed before, hence this should be safe. */
+ r = sym_crypt_deactivate_by_name(t->device, name, CRYPT_DEACTIVATE_FORCE);
+ if (r < 0)
+ log_error_errno(r, "Failed to deactivate LUKS device: %m");
+ }
+
+ sym_crypt_free(t->device);
+ free(t->volume);
+ free(t);
+#endif
+ return NULL;
+}
+
typedef struct {
LoopDevice *loop;
int fd;
char *path;
int whole_fd;
+ DecryptedPartitionTarget *decrypted;
} PartitionTarget;
static int partition_target_fd(PartitionTarget *t) {
assert(t);
assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
- return t->loop ? t->loop->fd : t->fd >= 0 ? t->fd : t->whole_fd;
+
+ if (t->decrypted)
+ return t->decrypted->fd;
+
+ if (t->loop)
+ return t->loop->fd;
+
+ if (t->fd >= 0)
+ return t->fd;
+
+ return t->whole_fd;
}
static const char* partition_target_path(PartitionTarget *t) {
assert(t);
assert(t->loop || t->path);
- return t->loop ? t->loop->node : t->path;
+
+ if (t->decrypted)
+ return t->decrypted->volume;
+
+ if (t->loop)
+ return t->loop->node;
+
+ return t->path;
}
static PartitionTarget *partition_target_free(PartitionTarget *t) {
if (!t)
return NULL;
+ decrypted_partition_target_free(t->decrypted);
loop_device_unref(t->loop);
safe_close(t->fd);
unlink_and_free(t->path);
int r;
assert(t);
+ assert(!t->decrypted);
if (t->loop) {
r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+ if (t->decrypted && fsync(t->decrypted->fd) < 0)
+ return log_error_errno(errno, "Failed to sync changes to '%s': %m", t->decrypted->volume);
+
if (t->loop) {
r = loop_device_sync(t->loop);
if (r < 0)
return 0;
}
-static int partition_encrypt(Context *context, Partition *p, const char *node) {
+static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
+ const char *node = partition_target_path(target);
struct crypt_params_luks2 luks_params = {
.label = strempty(ASSERT_PTR(p)->new_label),
.sector_size = ASSERT_PTR(context)->sector_size,
- .data_device = node,
+ .data_device = offline ? node : NULL,
};
struct crypt_params_reencrypt reencrypt_params = {
.mode = CRYPT_REENCRYPT_ENCRYPT,
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_fclose_ FILE *h = NULL;
- _cleanup_free_ char *hp = NULL;
+ _cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
const char *passphrase = NULL;
size_t passphrase_size = 0;
const char *vt;
log_info("Encrypting future partition %" PRIu64 "...", p->partno);
- r = var_tmp_dir(&vt);
- if (r < 0)
- return log_error_errno(r, "Failed to determine temporary files directory: %m");
+ if (offline) {
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine temporary files directory: %m");
- r = fopen_temporary_child(vt, &h, &hp);
- if (r < 0)
- return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
+ r = fopen_temporary_child(vt, &h, &hp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
- /* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
- r = ftruncate(fileno(h), context->sector_size);
- if (r < 0)
- return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ /* Weird cryptsetup requirement which requires the header file to be the size of at least one
+ * sector. */
+ r = ftruncate(fileno(h), context->sector_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ } else {
+ if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
+ return log_oom();
+
+ vol = path_join("/dev/mapper/", dm_name);
+ if (!vol)
+ return log_oom();
+ }
- r = sym_crypt_init(&cd, hp);
+ r = sym_crypt_init(&cd, offline ? hp : node);
if (r < 0)
return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
cryptsetup_enable_logging(cd);
- /* Disable kernel keyring usage by libcryptsetup as a workaround for
- * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
- * offline encryption even when repart is running in a container. */
- r = sym_crypt_volume_key_keyring(cd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to disable kernel keyring: %m");
+ if (offline) {
+ /* Disable kernel keyring usage by libcryptsetup as a workaround for
+ * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can
+ * do offline encryption even when repart is running in a container. */
+ r = sym_crypt_volume_key_keyring(cd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable kernel keyring: %m");
- r = sym_crypt_metadata_locking(cd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to disable metadata locking: %m");
+ r = sym_crypt_metadata_locking(cd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable metadata locking: %m");
- r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
- if (r < 0)
- return log_error_errno(r, "Failed to set data offset: %m");
+ r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set data offset: %m");
+ }
r = sym_crypt_format(cd,
CRYPT_LUKS2,
#endif
}
- r = sym_crypt_reencrypt_init_by_passphrase(
- cd,
- NULL,
- passphrase,
- passphrase_size,
- CRYPT_ANY_SLOT,
- 0,
- sym_crypt_get_cipher(cd),
- sym_crypt_get_cipher_mode(cd),
- &reencrypt_params);
- if (r < 0)
- return log_error_errno(r, "Failed to prepare for reencryption: %m");
+ if (offline) {
+ r = sym_crypt_reencrypt_init_by_passphrase(
+ cd,
+ NULL,
+ passphrase,
+ passphrase_size,
+ CRYPT_ANY_SLOT,
+ 0,
+ sym_crypt_get_cipher(cd),
+ sym_crypt_get_cipher_mode(cd),
+ &reencrypt_params);
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare for reencryption: %m");
- /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
- * to do that ourselves. */
+ /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we
+ * have to do that ourselves. */
- sym_crypt_free(cd);
- cd = NULL;
+ sym_crypt_free(cd);
+ cd = NULL;
- r = sym_crypt_init(&cd, node);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
+ r = sym_crypt_init(&cd, node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
- r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
- if (r < 0)
- return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
+ r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
- reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
+ reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
- r = sym_crypt_reencrypt_init_by_passphrase(
- cd,
- NULL,
- passphrase,
- passphrase_size,
- CRYPT_ANY_SLOT,
- 0,
- NULL,
- NULL,
- &reencrypt_params);
- if (r < 0)
- return log_error_errno(r, "Failed to load reencryption context: %m");
+ r = sym_crypt_reencrypt_init_by_passphrase(
+ cd,
+ NULL,
+ passphrase,
+ passphrase_size,
+ CRYPT_ANY_SLOT,
+ 0,
+ NULL,
+ NULL,
+ &reencrypt_params);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load reencryption context: %m");
- r = sym_crypt_reencrypt(cd, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to encrypt %s: %m", node);
+ r = sym_crypt_reencrypt(cd, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt %s: %m", node);
+ } else {
+ _cleanup_free_ DecryptedPartitionTarget *t = NULL;
+ _cleanup_close_ int dev_fd = -1;
+
+ r = sym_crypt_activate_by_volume_key(
+ cd,
+ dm_name,
+ NULL,
+ VOLUME_KEY_SIZE,
+ arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to activate LUKS superblock: %m");
+
+ dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (dev_fd < 0)
+ return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
+
+ if (flock(dev_fd, LOCK_EX) < 0)
+ return log_error_errno(errno, "Failed to lock '%s': %m", vol);
+
+ t = new(DecryptedPartitionTarget, 1);
+ if (!t)
+ return log_oom();
+
+ *t = (DecryptedPartitionTarget) {
+ .fd = TAKE_FD(dev_fd),
+ .volume = TAKE_PTR(vol),
+ .device = TAKE_PTR(cd),
+ };
+
+ target->decrypted = TAKE_PTR(t);
+ }
log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
if (r < 0)
return r;
+ if (p->encrypt != ENCRYPT_OFF && t->loop) {
+ r = partition_encrypt(context, p, t, /* offline = */ false);
+ if (r < 0)
+ return r;
+ }
+
log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
if (r < 0)
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
- if (p->encrypt != ENCRYPT_OFF) {
- r = partition_encrypt(context, p, partition_target_path(t));
+ log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+
+ if (p->encrypt != ENCRYPT_OFF && !t->loop) {
+ r = partition_encrypt(context, p, t, /* offline = */ true);
if (r < 0)
return r;
}
if (r < 0)
return r;
- log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
-
if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) {
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
/* node = */ NULL, partition_target_path(t));
if (r < 0)
return r;
+ if (p->encrypt != ENCRYPT_OFF && t->loop) {
+ r = partition_target_grow(t, p->new_size);
+ if (r < 0)
+ return r;
+
+ r = partition_encrypt(context, p, t, /* offline = */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt device: %m");
+ }
+
log_info("Formatting future partition %" PRIu64 ".", p->partno);
/* If we're not writing to a loop device or if we're populating a read-only filesystem, we
if (partition_needs_populate(p) && !root) {
assert(t->loop);
- r = partition_populate_filesystem(context, p, t->loop->node);
+ r = partition_populate_filesystem(context, p, partition_target_path(t));
if (r < 0)
return r;
}
- if (p->encrypt != ENCRYPT_OFF) {
+ if (p->encrypt != ENCRYPT_OFF && !t->loop) {
r = partition_target_grow(t, p->new_size);
if (r < 0)
return r;
- r = partition_encrypt(context, p, partition_target_path(t));
+ r = partition_encrypt(context, p, t, /* offline = */ true);
if (r < 0)
return log_error_errno(r, "Failed to encrypt device: %m");
}