diff --git a/src/core/namespace.c b/src/core/namespace.c index 19bf290336..7a8facce97 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -2550,65 +2550,82 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) { SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, p->verity && p->verity->data_path); - if (p->runtime_scope == RUNTIME_SCOPE_SYSTEM) { - /* In system mode we mount directly */ + /* First check if we have a verity device already open and with a fstype pinned by policy. If it + * cannot be found, then fallback to the slow path (full dissect). */ + r = dissected_image_new_from_existing_verity( + p->root_image, + p->verity, + p->root_image_options, + p->root_image_policy, + /* image_filter= */ NULL, + p->runtime_scope, + dissect_image_flags, + &dissected_image); + if (r < 0 && !ERRNO_IS_NEG_DEVICE_ABSENT(r) && r != -ENOPKG) + return r; + if (r >= 0) + log_debug("Reusing pre-existing verity-protected root image %s", p->root_image); + else { + if (p->runtime_scope == RUNTIME_SCOPE_SYSTEM) { + /* In system mode we mount directly */ - r = loop_device_make_by_path( - p->root_image, - FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */, - /* sector_size= */ UINT32_MAX, - FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN, - LOCK_SH, - &loop_device); - if (r < 0) - return log_debug_errno(r, "Failed to create loop device for root image: %m"); + r = loop_device_make_by_path( + p->root_image, + FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : -1 /* < 0 means writable if possible, read-only as fallback */, + /* sector_size= */ UINT32_MAX, + FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN, + LOCK_SH, + &loop_device); + if (r < 0) + return log_debug_errno(r, "Failed to create loop device for root image: %m"); - r = dissect_loop_device( - loop_device, - p->verity, - p->root_image_options, - p->root_image_policy, - /* image_filter= */ NULL, - dissect_image_flags, - &dissected_image); - if (r < 0) - return log_debug_errno(r, "Failed to dissect image: %m"); + r = dissect_loop_device( + loop_device, + p->verity, + p->root_image_options, + p->root_image_policy, + /* image_filter= */ NULL, + dissect_image_flags, + &dissected_image); + if (r < 0) + return log_debug_errno(r, "Failed to dissect image: %m"); - r = dissected_image_load_verity_sig_partition( - dissected_image, - loop_device->fd, - p->verity); - if (r < 0) - return r; + r = dissected_image_load_verity_sig_partition( + dissected_image, + loop_device->fd, + p->verity); + if (r < 0) + return r; - r = dissected_image_guess_verity_roothash( - dissected_image, - p->verity); - if (r < 0) - return r; + r = dissected_image_guess_verity_roothash( + dissected_image, + p->verity); + if (r < 0) + return r; - r = dissected_image_decrypt( - dissected_image, - NULL, - p->verity, - p->root_image_policy, - dissect_image_flags); - if (r < 0) - return log_debug_errno(r, "Failed to decrypt dissected image: %m"); - } else { - userns_fd = namespace_open_by_type(NAMESPACE_USER); - if (userns_fd < 0) - return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m"); + r = dissected_image_decrypt( + dissected_image, + /* passphrase= */ NULL, + p->verity, + p->root_image_policy, + dissect_image_flags); + if (r < 0) + return log_debug_errno(r, "Failed to decrypt dissected image: %m"); + } else { + userns_fd = namespace_open_by_type(NAMESPACE_USER); + if (userns_fd < 0) + return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m"); - r = mountfsd_mount_image( - p->root_image, - userns_fd, - p->root_image_policy, - p->verity, - dissect_image_flags, - &dissected_image); - if (r < 0) - return r; + r = mountfsd_mount_image( + p->root_image, + userns_fd, + p->root_image_policy, + p->verity, + dissect_image_flags, + &dissected_image); + if (r < 0) + return r; + } } } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 0fe3a5b3e0..e155970e31 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -843,12 +843,15 @@ static int open_partition( int r; assert(node); - assert(loop); + assert(loop || !is_partition); fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); if (fd < 0) return -errno; + if (!loop) + return TAKE_FD(fd); + /* Check if the block device is a child of (or equivalent to) the originally provided one. */ r = block_device_new_from_fd(fd, is_partition ? BLOCK_DEVICE_LOOKUP_WHOLE_DISK : 0, &dev); if (r < 0) @@ -911,6 +914,85 @@ static bool image_filter_test(const ImageFilter *filter, PartitionDesignator d, return fnmatch(filter->pattern[d], strempty(label), FNM_NOESCAPE) == 0; } +static int dissect_image_from_unpartitioned( + const char *devname, + uint64_t diskseq, + const sd_id128_t *uuid, + bool encrypted, + const VeritySettings *verity, + const MountOptions *mount_options, + const ImagePolicy *policy, + const ImageFilter *filter, + int *mount_node_fd, /* taken over on success */ + char **fstype, /* taken over on success */ + DissectedImage *m, + DissectImageFlags flags, + PartitionPolicyFlags found_flags) { + + _cleanup_free_ char *n = NULL, *o = NULL; + const char *options = NULL; + int r; + + assert(devname); + assert(m); + assert(fstype); + + if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL)) /* do a filter check with an empty partition label */ + return -ECOMM; + + r = image_policy_may_use(policy, PARTITION_ROOT); + if (r < 0) + return r; + if (r == 0) /* policy says ignore this, so we ignore it */ + return -ENOPKG; + + r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags); + if (r < 0) + return r; + + r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */ + if (r < 0) + return r; + + r = make_partition_devname(devname, diskseq, /* nr= */ -1, flags, &n); + if (r < 0) + return r; + + m->single_file_system = true; + m->encrypted = encrypted; + + m->has_verity = verity && verity->data_path; + m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT); + + m->has_verity_sig = false; /* signature not embedded, must be specified */ + m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash); + + if (uuid) + m->image_uuid = *uuid; + + options = mount_options_from_designator(mount_options, PARTITION_ROOT); + if (options) { + o = strdup(options); + if (!o) + return -ENOMEM; + } + + m->partitions[PARTITION_ROOT] = (DissectedPartition) { + .found = true, + .rw = !m->verity_ready && !fstype_is_ro(*fstype), + .partno = -1, + .architecture = _ARCHITECTURE_INVALID, + .fstype = TAKE_PTR(*fstype), + .node = TAKE_PTR(n), + .mount_options = TAKE_PTR(o), + .mount_node_fd = mount_node_fd ? TAKE_FD(*mount_node_fd) : -EBADF, + .size = UINT64_MAX, + .fsmount_fd = -EBADF, + }; + + return 0; +} + static int dissect_image( DissectedImage *m, int fd, @@ -1030,38 +1112,41 @@ static int dissect_image( if ((!(flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_GENERIC_ROOT)) || (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) { + _cleanup_free_ char *root_fstype_string = NULL; const char *usage = NULL; + bool encrypted; + + r = partition_policy_determine_fstype(policy, PARTITION_ROOT, &encrypted, &root_fstype_string); + if (r < 0) + return r; /* If flags permit this, also allow using non-partitioned single-filesystem images */ - (void) sym_blkid_probe_lookup_value(b, "USAGE", &usage, NULL); + if (root_fstype_string) + usage = encrypted ? "crypto" : "filesystem"; + else + (void) sym_blkid_probe_lookup_value(b, "USAGE", &usage, NULL); if (STRPTR_IN_SET(usage, "filesystem", "crypto")) { - _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL; - const char *fstype = NULL, *options = NULL; + _cleanup_free_ char *t = NULL; + const char *fstype = NULL; _cleanup_close_ int mount_node_fd = -EBADF; sd_id128_t uuid = SD_ID128_NULL; PartitionPolicyFlags found_flags; - bool encrypted; /* OK, we have found a file system, that's our root partition then. */ - if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL)) /* do a filter check with an empty partition label */ - return -ECOMM; + if (!root_fstype_string) { + (void) sym_blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - r = image_policy_may_use(policy, PARTITION_ROOT); - if (r < 0) - return r; - if (r == 0) /* policy says ignore this, so we ignore it */ - return -ENOPKG; + /* blkid will return FAT's serial number as UUID, hence it is quite possible that + * parsing this will fail. We'll ignore the ID, since it's just too short to be + * useful as true identifier. */ + (void) blkid_probe_lookup_value_id128(b, "UUID", &uuid); + } else + /* The policy fstype flags translate to the literal fstype name of each filesystem. */ + fstype = root_fstype_string; - (void) sym_blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - - /* blkid will return FAT's serial number as UUID, hence it is quite possible that - * parsing this will fail. We'll ignore the ID, since it's just too short to be - * useful as true identifier. */ - (void) blkid_probe_lookup_value_id128(b, "UUID", &uuid); - - encrypted = streq_ptr(fstype, "crypto_LUKS"); + encrypted = encrypted || streq_ptr(fstype, "crypto_LUKS"); if (verity_settings_data_covers(verity, PARTITION_ROOT)) found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY; @@ -1074,14 +1159,6 @@ static int dissect_image( } else found_flags = PARTITION_POLICY_UNPROTECTED; - r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags); - if (r < 0) - return r; - - r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */ - if (r < 0) - return r; - if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) { mount_node_fd = open_partition(devname, /* is_partition= */ false, m->loop); if (mount_node_fd < 0) @@ -1094,43 +1171,20 @@ static int dissect_image( return -ENOMEM; } - r = make_partition_devname(devname, diskseq, -1, flags, &n); - if (r < 0) - return r; - - m->single_file_system = true; - m->encrypted = encrypted; - - m->has_verity = verity && verity->data_path; - m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT); - - m->has_verity_sig = false; /* signature not embedded, must be specified */ - m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash); - - m->image_uuid = uuid; - - options = mount_options_from_designator(mount_options, PARTITION_ROOT); - if (options) { - o = strdup(options); - if (!o) - return -ENOMEM; - } - - m->partitions[PARTITION_ROOT] = (DissectedPartition) { - .found = true, - .rw = !m->verity_ready && !fstype_is_ro(fstype), - .partno = -1, - .architecture = _ARCHITECTURE_INVALID, - .fstype = TAKE_PTR(t), - .node = TAKE_PTR(n), - .mount_options = TAKE_PTR(o), - .mount_node_fd = TAKE_FD(mount_node_fd), - .offset = 0, - .size = UINT64_MAX, - .fsmount_fd = -EBADF, - }; - - return 0; + return dissect_image_from_unpartitioned( + devname, + diskseq, + &uuid, + encrypted, + verity, + mount_options, + policy, + filter, + &mount_node_fd, + &t, + m, + flags, + found_flags); } } @@ -1555,10 +1609,15 @@ static int dissect_image( if (r < 0) return r; + /* Local override via env var or designator type wins */ if (fstype) { t = strdup(fstype); if (!t) return -ENOMEM; + } else { + r = partition_policy_determine_fstype(policy, type.designator, /* ret_encrypted= */ NULL, &t); + if (r < 0) + return r; } if (label) { @@ -1896,6 +1955,97 @@ static int dissect_image( } #endif +int dissected_image_new_from_existing_verity( + const char *src, + const VeritySettings *verity, + const MountOptions *options, + const ImagePolicy *image_policy, + const ImageFilter *image_filter, + RuntimeScope runtime_scope, + DissectImageFlags dissect_image_flags, + DissectedImage **ret) { + + /* Look for an already set up dm-verity device with a single filesystem, according to our naming + * scheme and image policy, and if it is pinned by filesystem type set up the image directly. */ + +#if HAVE_BLKID + _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; + _cleanup_free_ char *node = NULL, *root_hash_encoded = NULL, *root_fstype_string = NULL; + _cleanup_close_ int mount_node_fd = -EBADF; + PartitionPolicyFlags found_flags; + bool encrypted = false; + int r; + + assert(!verity || verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR)); + assert(!verity || iovec_is_valid(&verity->root_hash)); + assert(!verity || iovec_is_valid(&verity->root_hash_sig)); + assert(!verity || iovec_is_set(&verity->root_hash) || !iovec_is_set(&verity->root_hash_sig)); + assert(ret); + + /* Shortcut: this deals only with verity images and requires a policy, and only for system services */ + if (runtime_scope != RUNTIME_SCOPE_SYSTEM || + !FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_VERITY_SHARE) || + !image_policy || + !verity || + !verity->data_path || + (verity->designator >= 0 && verity->designator != PARTITION_ROOT) || + !iovec_is_set(&verity->root_hash)) + return -ENOPKG; + + /* The policy fstype flags translate to the literal fstype name of each filesystem. + * Input must be a single filesystem image, if the policy specifies more than one, we need to dissect */ + r = partition_policy_determine_fstype(image_policy, PARTITION_ROOT, &encrypted, &root_fstype_string); + if (r < 0) + return r; + if (!root_fstype_string) + return -ENOPKG; + + if (verity_settings_data_covers(verity, PARTITION_ROOT)) + found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY; + else + found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED; + + root_hash_encoded = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len); + if (!root_hash_encoded) + return -ENOMEM; + + node = strjoin("/dev/mapper/", root_hash_encoded, "-verity"); + if (!node) + return -ENOMEM; + + r = dissected_image_new(src, &dissected_image); + if (r < 0) + return r; + + mount_node_fd = open_partition(node, /* is_partition= */ false, /* loop= */ NULL); + if (mount_node_fd < 0) + return mount_node_fd; + + r = dissect_image_from_unpartitioned( + node, + /* diskseq= */ 0, + /* uuid= */ NULL, + encrypted, + verity, + options, + image_policy, + image_filter, + &mount_node_fd, + &root_fstype_string, + dissected_image, + dissect_image_flags, + found_flags); + if (r < 0) + return r; + + *ret = TAKE_PTR(dissected_image); + + return 0; +#else + return -EOPNOTSUPP; +#endif +} + int dissect_image_file( const char *path, const VeritySettings *verity, @@ -4636,70 +4786,87 @@ int verity_dissect_and_mount( DISSECT_IMAGE_ALLOW_USERSPACE_VERITY | DISSECT_IMAGE_VERITY_SHARE; - if (runtime_scope == RUNTIME_SCOPE_SYSTEM) { - /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be - * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */ - r = loop_device_make_by_path( - src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src, - /* open_flags= */ -1, - /* sector_size= */ UINT32_MAX, - verity->data_path ? 0 : LO_FLAGS_PARTSCAN, - LOCK_SH, - &loop_device); - if (r < 0) - return log_debug_errno(r, "Failed to create loop device for image: %m"); + /* First check if we have a verity device already open and with a fstype pinned by policy. If it + * cannot be found, then fallback to the slow path (full dissect). */ + r = dissected_image_new_from_existing_verity( + src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src, + verity, + options, + image_policy, + image_filter, + runtime_scope, + dissect_image_flags, + &dissected_image); + if (r < 0 && !ERRNO_IS_NEG_DEVICE_ABSENT(r) && r != -ENOPKG) + return r; + if (r >= 0) + log_debug("Reusing pre-existing verity-protected image %s", src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src); + else { + if (runtime_scope == RUNTIME_SCOPE_SYSTEM) { + /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be + * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */ + r = loop_device_make_by_path( + src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src, + /* open_flags= */ -1, + /* sector_size= */ UINT32_MAX, + verity->data_path ? 0 : LO_FLAGS_PARTSCAN, + LOCK_SH, + &loop_device); + if (r < 0) + return log_debug_errno(r, "Failed to create loop device for image: %m"); - r = dissect_loop_device( - loop_device, - verity, - options, - image_policy, - image_filter, - dissect_image_flags, - &dissected_image); - /* No partition table? Might be a single-filesystem image, try again */ - if (!verity->data_path && r == -ENOPKG) r = dissect_loop_device( loop_device, verity, options, image_policy, image_filter, - dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE, + dissect_image_flags, &dissected_image); - if (r < 0) - return log_debug_errno(r, "Failed to dissect image: %m"); + /* No partition table? Might be a single-filesystem image, try again */ + if (!verity->data_path && r == -ENOPKG) + r = dissect_loop_device( + loop_device, + verity, + options, + image_policy, + image_filter, + dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE, + &dissected_image); + if (r < 0) + return log_debug_errno(r, "Failed to dissect image: %m"); - r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity); - if (r < 0) - return r; + r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity); + if (r < 0) + return r; - r = dissected_image_guess_verity_roothash(dissected_image, verity); - if (r < 0) - return r; + r = dissected_image_guess_verity_roothash(dissected_image, verity); + if (r < 0) + return r; - r = dissected_image_decrypt( - dissected_image, - NULL, - verity, - image_policy, - dissect_image_flags); - if (r < 0) - return log_debug_errno(r, "Failed to decrypt dissected image: %m"); - } else { - userns_fd = namespace_open_by_type(NAMESPACE_USER); - if (userns_fd < 0) - return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m"); + r = dissected_image_decrypt( + dissected_image, + NULL, + verity, + image_policy, + dissect_image_flags); + if (r < 0) + return log_debug_errno(r, "Failed to decrypt dissected image: %m"); + } else { + userns_fd = namespace_open_by_type(NAMESPACE_USER); + if (userns_fd < 0) + return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m"); - r = mountfsd_mount_image( - src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src, - userns_fd, - image_policy, - verity, - dissect_image_flags, - &dissected_image); - if (r < 0) - return r; + r = mountfsd_mount_image( + src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src, + userns_fd, + image_policy, + verity, + dissect_image_flags, + &dissected_image); + if (r < 0) + return r; + } } if (dest) { diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 951ae66055..9650dce2a4 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -165,6 +165,7 @@ int dissect_image_file(const char *path, const VeritySettings *verity, const Mou int dissect_image_file_and_warn(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret); int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *image_filter, DissectImageFlags flags, DissectedImage **ret); int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *image_filter, DissectImageFlags flags, DissectedImage **ret); +int dissected_image_new_from_existing_verity(const char *src, const VeritySettings *verity, const MountOptions *options, const ImagePolicy *image_policy, const ImageFilter *image_filter, RuntimeScope runtime_scope, DissectImageFlags dissect_image_flags, DissectedImage **ret); void dissected_image_close(DissectedImage *m); DissectedImage* dissected_image_unref(DissectedImage *m); diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh index 47ea82c3b7..f16c6856ce 100755 --- a/test/units/TEST-50-DISSECT.dissect.sh +++ b/test/units/TEST-50-DISSECT.dissect.sh @@ -507,10 +507,54 @@ NONEXISTENT_VDIR="/tmp/$VBASE-nonexistent.v" mkdir "$VDIR" "$EMPTY_VDIR" ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw" +ln -s /tmp/app0.verity "$VDIR/${VBASE}_0.verity" +ln -s /tmp/app0.roothash "$VDIR/${VBASE}_0.roothash" ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw" systemd-run -P -p ExtensionImages="$VDIR -$EMPTY_VDIR -$NONEXISTENT_VDIR" bash -o pipefail -c '/opt/script1.sh | grep ID' +# Check dissect shortcut for verity images +# No verity for the second img (previous test benefits from variations), remove it for the next one +rm -f "$VDIR/${VBASE}_1.raw" +cat >/run/systemd/system/testservice-50e-vpick.service < makes the devices disappear on Ubuntu 24.04/C9S, so set them +# up by hand. Don't fail if it's already set up. +veritysetup open "$MINIMAL_IMAGE.raw" "$(cat "$MINIMAL_IMAGE.roothash")-verity" "$MINIMAL_IMAGE.verity" --root-hash-file "$MINIMAL_IMAGE.roothash" ||: +veritysetup open "$VDIR/${VBASE}_0.raw" "$(cat "$VDIR/${VBASE}_0.roothash")-verity" "$VDIR/${VBASE}_0.verity" --root-hash-file "$VDIR/${VBASE}_0.roothash" ||: +mkdir -p /tmp/img /tmp/ext +mount -o ro "/dev/mapper/$(cat "$MINIMAL_IMAGE.roothash")-verity" /tmp/img +mount -o ro "/dev/mapper/$(cat "$VDIR/${VBASE}_0.roothash")-verity" /tmp/ext +journalctl --sync +since="$(date '+%H:%M:%S')" +systemctl restart testservice-50e-vpick.service +systemctl is-active testservice-50e-vpick.service +journalctl --sync +timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 2 -F 'Reusing pre-existing verity-protected root image' +timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 2 -F 'Reusing pre-existing verity-protected image' +systemctl stop testservice-50e-vpick.service +umount -R /tmp/img +umount -R /tmp/ext +veritysetup close "$(cat "$MINIMAL_IMAGE.roothash")-verity" ||: +veritysetup close "$(cat "$VDIR/${VBASE}_0.roothash")-verity" ||: + rm -rf "$VDIR" "$EMPTY_VDIR" # ExtensionDirectories will set up an overlay @@ -787,6 +831,49 @@ rm -f /run/systemd/system/testservice-50k.service systemctl daemon-reload rm -rf "$VDIR" "$VDIR2" /tmp/vpickminimg /tmp/markers/ +# Check dissect shortcut for verity images +cat >/run/systemd/system/testservice-50m.service < makes the devices disappear on Ubuntu 24.04/C9S, so set them +# up by hand. Don't fail if it's already set up. +veritysetup open "$MINIMAL_IMAGE.raw" "$(cat "$MINIMAL_IMAGE.roothash")-verity" "$MINIMAL_IMAGE.verity" --root-hash-file "$MINIMAL_IMAGE.roothash" ||: +veritysetup open /tmp/app0.raw "$(cat /tmp/app0.roothash)-verity" /tmp/app0.verity --root-hash-file /tmp/app0.roothash ||: +mkdir -p /tmp/img /tmp/ext +mount -o ro "/dev/mapper/$(cat "$MINIMAL_IMAGE.roothash")-verity" /tmp/img +mount -o ro "/dev/mapper/$(cat /tmp/app0.roothash)-verity" /tmp/ext +journalctl --sync +since="$(date '+%H:%M:%S')" +systemctl restart testservice-50m.service +systemctl is-active testservice-50m.service +journalctl --sync +timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 4 -F 'Reusing pre-existing verity-protected root image' +timeout -v 30 journalctl --since "$since" -n all --follow | grep -m 8 -F 'Reusing pre-existing verity-protected image' +systemctl stop testservice-50m.service +umount /tmp/img +umount /tmp/ext +veritysetup close "$(cat "$MINIMAL_IMAGE.roothash")-verity" ||: +veritysetup close "$(cat /tmp/app0.roothash)-verity" ||: + # Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence mkdir -p /var/lib/extensions/ ln -s /tmp/app-nodistro.raw /var/lib/extensions/app-nodistro.raw