]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/partition/repart.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / partition / repart.c
index 4e57f977266cbaea66af6d2cc4b3f956dff86df2..1e9284e2e2e855ad71c59e91b7192e6dfae24c9b 100644 (file)
@@ -148,12 +148,13 @@ static size_t arg_key_size = 0;
 static EVP_PKEY *arg_private_key = NULL;
 static X509 *arg_certificate = NULL;
 static char *arg_tpm2_device = NULL;
+static uint32_t arg_tpm2_seal_key_handle = 0;
+static char *arg_tpm2_device_key = NULL;
 static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
 static size_t arg_tpm2_n_hash_pcr_values = 0;
-static bool arg_tpm2_hash_pcr_values_use_default = true;
 static char *arg_tpm2_public_key = NULL;
 static uint32_t arg_tpm2_public_key_pcr_mask = 0;
-static bool arg_tpm2_public_key_pcr_mask_use_default = true;
+static char *arg_tpm2_pcrlock = NULL;
 static bool arg_split = false;
 static GptPartitionType *arg_filter_partitions = NULL;
 static size_t arg_n_filter_partitions = 0;
@@ -175,8 +176,10 @@ STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_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_tpm2_hash_pcr_values, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep);
@@ -1686,10 +1689,10 @@ static int partition_read_definition(Partition *p, const char *path, const char
                 { "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", "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                  },
@@ -2202,8 +2205,8 @@ static int context_read_definitions(Context *context) {
                         if (q) {
                                 if (q->priority != p->priority)
                                         return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
-                                                        "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
-                                                        p->priority, q->priority, p->verity_match_key);
+                                                          "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
+                                                          p->priority, q->priority, p->verity_match_key);
 
                                 p->siblings[mode] = q;
                         }
@@ -2328,7 +2331,7 @@ static int context_load_partition_table(Context *context) {
                         return r;
 
                 if (fstat(context->backing_fd, &st) < 0)
-                        return log_error_errno(r, "Failed to stat %s: %m", context->node);
+                        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);
@@ -3191,13 +3194,13 @@ static int context_wipe_range(Context *context, uint64_t offset, uint64_t size)
                 errno = 0;
                 r = blkid_do_probe(probe);
                 if (r < 0)
-                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems.");
+                        return log_error_errno(errno_or_else(EIO), "Failed to probe for file systems.");
                 if (r > 0)
                         break;
 
                 errno = 0;
                 if (blkid_do_wipe(probe, false) < 0)
-                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature.");
+                        return log_error_errno(errno_or_else(EIO), "Failed to wipe file system signature.");
         }
 
         return 0;
@@ -3527,7 +3530,7 @@ static int prepare_temporary_file(PartitionTarget *t, uint64_t size) {
 
         if (ftruncate(fd, size) < 0)
                 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
-                                        FORMAT_BYTES(size));
+                                       FORMAT_BYTES(size));
 
         t->fd = TAKE_FD(fd);
         t->path = TAKE_PTR(temp);
@@ -3561,7 +3564,7 @@ static int partition_target_prepare(
         };
 
         if (!need_path) {
-                if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+                if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
                         return log_error_errno(errno, "Failed to seek to partition offset: %m");
 
                 t->whole_fd = whole_fd;
@@ -3637,10 +3640,10 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
         } else if (t->fd >= 0) {
                 struct stat st;
 
-                if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+                if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
                         return log_error_errno(errno, "Failed to seek to partition offset: %m");
 
-                if (lseek(t->fd, 0, SEEK_SET) == (off_t) -1)
+                if (lseek(t->fd, 0, SEEK_SET) < 0)
                         return log_error_errno(errno, "Failed to seek to start of temporary file: %m");
 
                 if (fstat(t->fd, &st) < 0)
@@ -3708,9 +3711,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
 
                 /* Weird cryptsetup requirement which requires the header file to be the size of at least one
                  * sector. */
-                r = ftruncate(fileno(h), luks_params.sector_size);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+                if (ftruncate(fileno(h), luks_params.sector_size) < 0)
+                        return log_error_errno(errno, "Failed to grow temporary LUKS header file: %m");
         } else {
                 if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
                         return log_oom();
@@ -3743,14 +3745,15 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                         return log_error_errno(r, "Failed to set data offset: %m");
         }
 
-        r = sym_crypt_format(cd,
-                         CRYPT_LUKS2,
-                         "aes",
-                         "xts-plain64",
-                         SD_ID128_TO_UUID_STRING(p->luks_uuid),
-                         NULL,
-                         VOLUME_KEY_SIZE,
-                         &luks_params);
+        r = sym_crypt_format(
+                        cd,
+                        CRYPT_LUKS2,
+                        "aes",
+                        "xts-plain64",
+                        SD_ID128_TO_UUID_STRING(p->luks_uuid),
+                        NULL,
+                        VOLUME_KEY_SIZE,
+                        &luks_params);
         if (r < 0)
                 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
 
@@ -3778,23 +3781,19 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                 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);
                         if (r < 0) {
                                 if (arg_tpm2_public_key || r != -ENOENT)
-                                        return log_error_errno(r, "Failed read TPM PCR public key: %m");
+                                        return log_error_errno(r, "Failed to read TPM PCR public key: %m");
 
                                 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
                                 arg_tpm2_public_key_pcr_mask = 0;
                         }
                 }
 
-                _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
-                r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to create TPM2 context: %m");
-
                 TPM2B_PUBLIC public;
                 if (pubkey) {
                         r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
@@ -3802,9 +3801,36 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                                 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
                 }
 
-                r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
-                if (r < 0)
-                        return log_error_errno(r, "Could not read pcr values: %m");
+                _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
+                if (arg_tpm2_pcrlock) {
+                        r = tpm2_pcrlock_policy_load(arg_tpm2_pcrlock, &pcrlock_policy);
+                        if (r < 0)
+                                return r;
+
+                        flags |= TPM2_FLAGS_USE_PCRLOCK;
+                }
+
+                _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
+                TPM2B_PUBLIC device_key_public = {};
+                if (arg_tpm2_device_key) {
+                        r = tpm2_load_public_key_file(arg_tpm2_device_key, &device_key_public);
+                        if (r < 0)
+                                return r;
+
+                        if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values))
+                                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);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create TPM2 context: %m");
+
+                        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 (r < 0)
+                                        return log_error_errno(r, "Could not read pcr values: %m");
+                        }
+                }
 
                 uint16_t hash_pcr_bank = 0;
                 uint32_t hash_pcr_mask = 0;
@@ -3824,18 +3850,36 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                 }
 
                 TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
-                r = tpm2_calculate_sealing_policy(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, pubkey ? &public : NULL, /* use_pin= */ false, &policy);
+                r = tpm2_calculate_sealing_policy(
+                                arg_tpm2_hash_pcr_values,
+                                arg_tpm2_n_hash_pcr_values,
+                                pubkey ? &public : NULL,
+                                /* use_pin= */ false,
+                                arg_tpm2_pcrlock ? &pcrlock_policy : NULL,
+                                &policy);
                 if (r < 0)
                         return log_error_errno(r, "Could not calculate sealing policy digest: %m");
 
-                r = tpm2_seal(tpm2_context,
-                              /* seal_key_handle= */ 0,
-                              &policy,
-                              /* pin= */ NULL,
-                              &secret, &secret_size,
-                              &blob, &blob_size,
-                              /* ret_primary_alg= */ NULL,
-                              &srk_buf, &srk_buf_size);
+                if (arg_tpm2_device_key)
+                        r = tpm2_calculate_seal(
+                                        arg_tpm2_seal_key_handle,
+                                        &device_key_public,
+                                        /* attributes= */ NULL,
+                                        /* secret= */ NULL, /* secret_size= */ 0,
+                                        &policy,
+                                        /* pin= */ NULL,
+                                        &secret, &secret_size,
+                                        &blob, &blob_size,
+                                        &srk_buf, &srk_buf_size);
+                else
+                        r = tpm2_seal(tpm2_context,
+                                      arg_tpm2_seal_key_handle,
+                                      &policy,
+                                      /* pin= */ NULL,
+                                      &secret, &secret_size,
+                                      &blob, &blob_size,
+                                      /* ret_primary_alg= */ NULL,
+                                      &srk_buf, &srk_buf_size);
                 if (r < 0)
                         return log_error_errno(r, "Failed to seal to TPM2: %m");
 
@@ -3850,8 +3894,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                 keyslot = sym_crypt_keyslot_add_by_volume_key(
                                 cd,
                                 CRYPT_ANY_SLOT,
-                                NULL,
-                                VOLUME_KEY_SIZE,
+                                /* volume_key= */ NULL,
+                                /* volume_key_size= */ VOLUME_KEY_SIZE,
                                 base64_encoded,
                                 base64_encoded_size);
                 if (keyslot < 0)
@@ -3868,7 +3912,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
                                 policy.buffer, policy.size,
                                 NULL, 0, /* no salt because tpm2_seal has no pin */
                                 srk_buf, srk_buf_size,
-                                0,
+                                flags,
                                 &v);
                 if (r < 0)
                         return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
@@ -4087,7 +4131,7 @@ static int partition_format_verity_hash(
                 p->new_uuid_is_set = true;
         }
 
-        p->roothash = TAKE_IOVEC(rh);
+        p->roothash = TAKE_STRUCT(rh);
 
         return 0;
 #else
@@ -4133,13 +4177,13 @@ static int sign_verity_roothash(
 
         return 0;
 #else
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot setup verity signature: %m");
 #endif
 }
 
 static int partition_format_verity_sig(Context *context, Partition *p) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        _cleanup_(iovec_done) struct iovec sig = IOVEC_NULL;
+        _cleanup_(iovec_done) struct iovec sig = {};
         _cleanup_free_ char *text = NULL, *hint = NULL;
         Partition *hp;
         uint8_t fp[X509_FINGERPRINT_SIZE];
@@ -4194,7 +4238,7 @@ static int partition_format_verity_sig(Context *context, Partition *p) {
         if (r < 0)
                 return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(p->new_size));
 
-        if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+        if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
                 return log_error_errno(errno, "Failed to seek to partition %s offset: %m", strna(hint));
 
         r = loop_write(whole_fd, text, p->new_size);
@@ -4233,6 +4277,8 @@ static int context_copy_blocks(Context *context) {
                 assert(p->copy_blocks_size != UINT64_MAX);
                 assert(p->new_size >= p->copy_blocks_size + (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
 
+                usec_t start_timestamp = now(CLOCK_MONOTONIC);
+
                 r = partition_target_prepare(context, p, p->new_size,
                                              /*need_path=*/ p->encrypt != ENCRYPT_OFF || p->siblings[VERITY_HASH],
                                              &t);
@@ -4245,11 +4291,16 @@ static int context_copy_blocks(Context *context) {
                                 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 (p->copy_blocks_offset == UINT64_MAX)
+                        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);
+                else {
+                        log_info("Copying in '%s' @ %" PRIu64 " (%s) on block level into future partition %" PRIu64 ".",
+                                 p->copy_blocks_path, p->copy_blocks_offset, FORMAT_BYTES(p->copy_blocks_size), p->partno);
 
-                if (p->copy_blocks_offset != UINT64_MAX && lseek(p->copy_blocks_fd, p->copy_blocks_offset, SEEK_SET) < 0)
-                        return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path);
+                        if (lseek(p->copy_blocks_fd, p->copy_blocks_offset, SEEK_SET) < 0)
+                                return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path);
+                }
 
                 r = copy_bytes(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK);
                 if (r < 0)
@@ -4267,6 +4318,14 @@ static int context_copy_blocks(Context *context) {
                 if (r < 0)
                         return r;
 
+                usec_t time_spent = usec_sub_unsigned(now(CLOCK_MONOTONIC), start_timestamp);
+                if (time_spent > 250 * USEC_PER_MSEC) /* Show throughput, but not if we spent too little time on it, since it's just noise then */
+                        log_info("Block level copying and synchronization of partition %" PRIu64 " complete in %s (%s/s).",
+                                 p->partno, FORMAT_TIMESPAN(time_spent, 0), FORMAT_BYTES((uint64_t) ((double) p->copy_blocks_size / time_spent * USEC_PER_SEC)));
+                else
+                        log_info("Block level copying and synchronization of partition %" PRIu64 " complete in %s.",
+                                 p->partno, FORMAT_TIMESPAN(time_spent, 0));
+
                 if (p->siblings[VERITY_HASH] && !partition_type_defer(&p->siblings[VERITY_HASH]->type)) {
                         r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
                                                          /* node = */ NULL, partition_target_path(t));
@@ -4488,7 +4547,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
 
                 rfd = open(root, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
                 if (rfd < 0)
-                        return rfd;
+                        return -errno;
 
                 sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
                 if (sfd < 0)
@@ -4675,7 +4734,7 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
          * appear in the host namespace. Hence we fork a child that has its own file system namespace and
          * detached mount propagation. */
 
-        r = safe_fork("(sd-copy)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
+        r = safe_fork("(sd-copy)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
         if (r < 0)
                 return r;
         if (r == 0) {
@@ -4790,6 +4849,17 @@ static int context_mkfs(Context *context) {
                 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. */
@@ -4859,7 +4929,7 @@ static int parse_x509_certificate(const char *certificate, size_t certificate_si
 
         return 0;
 #else
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse X509 certificate.");
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse X509 certificate.");
 #endif
 }
 
@@ -4886,7 +4956,7 @@ static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) {
 
         return 0;
 #else
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse private key.");
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse private key.");
 #endif
 }
 
@@ -5450,7 +5520,7 @@ static int context_write_partition_table(Context *context) {
                 return 0;
         }
 
-        log_info("Applying changes.");
+        log_info("Applying changes to %s.", context->node);
 
         if (context->from_scratch && arg_empty != EMPTY_CREATE) {
                 /* Erase everything if we operate from scratch, except if the image was just created anyway, and thus is definitely empty. */
@@ -5972,8 +6042,9 @@ static int context_open_copy_block_paths(
                 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);
 
@@ -6205,10 +6276,10 @@ static int context_minimize(Context *context) {
                 d = loop_device_unref(d);
 
                 /* Erase the previous filesystem first. */
-                if (ftruncate(fd, 0))
+                if (ftruncate(fd, 0) < 0)
                         return log_error_errno(errno, "Failed to erase temporary file: %m");
 
-                if (ftruncate(fd, fsz))
+                if (ftruncate(fd, fsz) < 0)
                         return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
 
                 if (arg_offline <= 0) {
@@ -6300,7 +6371,7 @@ static int context_minimize(Context *context) {
                         return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
 
                 if (fstat(fd, &st) < 0)
-                        return log_error_errno(r, "Failed to stat temporary file: %m");
+                        return log_error_errno(errno, "Failed to stat temporary file: %m");
 
                 log_info("Minimal partition size of verity hash partition %s is %s",
                          strna(hint), FORMAT_BYTES(st.st_size));
@@ -6376,12 +6447,18 @@ static int help(void) {
                "     --certificate=PATH   PEM certificate to use when generating verity\n"
                "                          roothash signatures\n"
                "     --tpm2-device=PATH   Path to TPM2 device node to use\n"
+               "     --tpm2-device-key=PATH\n"
+               "                          Enroll a TPM2 device using its public key\n"
+               "     --tpm2-seal-key-handle=HANDLE\n"
+               "                          Specify handle of key to use for sealing\n"
                "     --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
                "                          TPM2 PCR indexes to use for TPM2 enrollment\n"
                "     --tpm2-public-key=PATH\n"
                "                          Enroll signed TPM2 PCR policy against PEM public key\n"
                "     --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
                "                          Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
+               "     --tpm2-pcrlock=PATH\n"
+               "                          Specify pcrlock policy to lock against\n"
                "     --seed=UUID          128-bit seed UUID to derive all UUIDs from\n"
                "     --size=BYTES         Grow loopback file to specified size\n"
                "     --json=pretty|short|off\n"
@@ -6434,9 +6511,12 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_PRIVATE_KEY,
                 ARG_CERTIFICATE,
                 ARG_TPM2_DEVICE,
+                ARG_TPM2_DEVICE_KEY,
+                ARG_TPM2_SEAL_KEY_HANDLE,
                 ARG_TPM2_PCRS,
                 ARG_TPM2_PUBLIC_KEY,
                 ARG_TPM2_PUBLIC_KEY_PCRS,
+                ARG_TPM2_PCRLOCK,
                 ARG_SPLIT,
                 ARG_INCLUDE_PARTITIONS,
                 ARG_EXCLUDE_PARTITIONS,
@@ -6471,9 +6551,12 @@ static int parse_argv(int argc, char *argv[]) {
                 { "private-key",          required_argument, NULL, ARG_PRIVATE_KEY          },
                 { "certificate",          required_argument, NULL, ARG_CERTIFICATE          },
                 { "tpm2-device",          required_argument, NULL, ARG_TPM2_DEVICE          },
+                { "tpm2-device-key",      required_argument, NULL, ARG_TPM2_DEVICE_KEY      },
+                { "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE },
                 { "tpm2-pcrs",            required_argument, NULL, ARG_TPM2_PCRS            },
                 { "tpm2-public-key",      required_argument, NULL, ARG_TPM2_PUBLIC_KEY      },
                 { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
+                { "tpm2-pcrlock",         required_argument, NULL, ARG_TPM2_PCRLOCK         },
                 { "split",                required_argument, NULL, ARG_SPLIT                },
                 { "include-partitions",   required_argument, NULL, ARG_INCLUDE_PARTITIONS   },
                 { "exclude-partitions",   required_argument, NULL, ARG_EXCLUDE_PARTITIONS   },
@@ -6487,6 +6570,7 @@ static int parse_argv(int argc, char *argv[]) {
                 {}
         };
 
+        bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true;
         int c, r;
 
         assert(argc >= 0);
@@ -6706,8 +6790,22 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_TPM2_DEVICE_KEY:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_TPM2_SEAL_KEY_HANDLE:
+                        r = safe_atou32_full(optarg, 16, &arg_tpm2_seal_key_handle);
+                        if (r < 0)
+                                return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", optarg);
+
+                        break;
+
                 case ARG_TPM2_PCRS:
-                        arg_tpm2_hash_pcr_values_use_default = false;
+                        auto_hash_pcr_values = false;
                         r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
                         if (r < 0)
                                 return r;
@@ -6722,13 +6820,21 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_TPM2_PUBLIC_KEY_PCRS:
-                        arg_tpm2_public_key_pcr_mask_use_default = false;
+                        auto_public_key_pcr_mask = false;
                         r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
                         if (r < 0)
                                 return r;
 
                         break;
 
+                case ARG_TPM2_PCRLOCK:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
+                        if (r < 0)
+                                return r;
+
+                        auto_pcrlock = false;
+                        break;
+
                 case ARG_SPLIT:
                         r = parse_boolean_argument("--split=", optarg, NULL);
                         if (r < 0)
@@ -6936,15 +7042,32 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "A path to an image file must be specified when --split is used.");
 
-        if (arg_tpm2_public_key_pcr_mask_use_default && arg_tpm2_public_key)
+        if (auto_pcrlock) {
+                assert(!arg_tpm2_pcrlock);
+
+                r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
+                } else
+                        log_debug("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
+        }
+
+        if (auto_public_key_pcr_mask) {
+                assert(arg_tpm2_public_key_pcr_mask == 0);
                 arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
+        }
 
-        if (arg_tpm2_hash_pcr_values_use_default && !GREEDY_REALLOC_APPEND(
-                        arg_tpm2_hash_pcr_values,
-                        arg_tpm2_n_hash_pcr_values,
-                        &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
-                        1))
-                return log_oom();
+        if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 if no pcr policy is specified. */
+                assert(arg_tpm2_n_hash_pcr_values == 0);
+
+                if (!GREEDY_REALLOC_APPEND(
+                                    arg_tpm2_hash_pcr_values,
+                                    arg_tpm2_n_hash_pcr_values,
+                                    &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
+                                    1))
+                        return log_oom();
+        }
 
         if (arg_pretty < 0 && isatty(STDOUT_FILENO))
                 arg_pretty = true;
@@ -7243,8 +7366,9 @@ static int resize_backing_fd(
 
                 assert(loop_device);
 
-                if (ioctl(*fd, BLKGETSIZE64, &current_size) < 0)
-                        return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
+                r = blockdev_get_device_size(*fd, &current_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)
@@ -7456,9 +7580,9 @@ static int run(int argc, char *argv[]) {
                 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(errno, "DDI type '%s' is not defined: %m", arg_make_ddi);
+                        return log_error_errno(r, "DDI type '%s' is not defined: %m", arg_make_ddi);
 
                 if (strv_consume(&arg_definitions, TAKE_PTR(dp)) < 0)
                         return log_oom();