From: Luca Boccassi Date: Thu, 27 Feb 2025 16:58:55 +0000 (+0000) Subject: core: reuse existing dm-verity device for single filesystem images pinned by policy X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0bd766553cbf7636ad236d0f9137b542c0aeaf69;p=thirdparty%2Fsystemd.git core: reuse existing dm-verity device for single filesystem images pinned by policy Loading images is, generally speaking, the slowest part of sd-executor when spawning a service. This is due to multiple factors. dm-verity is obviously a big part of the cost, but dissecting in general via libblkid also can take a lot of time, due to probing the images and their filesystems. A performance test doing service restarts in a row shows these results, ran on a production system (low power and slow ARM64 SOC) with a real production service, show the following service interruption intervals: Count | P50 (ms) | P90 (ms) | P95 (ms) | P99 (ms) | P99.9 (ms) 507 | 1,532 | 1,726 | 2,548 | 4,112 | 4,592 One iteration is 507 restarts in this case, but this has ran hundreds of times and the results are always in line within margin of error. This also holds true for metrics from live systems, same numbers. Between 1.0s and 1.2s can be attributed by profiling to the time needed for the service code itself to start up and sd_notify, the rest is spent inside systemd's code. This means there is currently a tradeoff for services - either use secure images, or make restarting fast. Downtime of services is a very important metric, as for many cases this directly translates to outages, total or partial (blackouot or greyout). In order to facilitate using secure images without downsides, skip the slow dissect steps (probing, loop devices, etc) when the configured image is a single filesystem dm-verity image with a policy that pins it to a single filesystem, and an already existing and open dm-verity device can be found and reused. This allows orchestrators to pre-open images on download, before restarting services, to minimize downtimes. --- diff --git a/src/core/namespace.c b/src/core/namespace.c index 19bf2903363..7a8facce97c 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 */ - - 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 = 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_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 = mountfsd_mount_image( - p->root_image, - userns_fd, - p->root_image_policy, - p->verity, - dissect_image_flags, - &dissected_image); - if (r < 0) - return r; + /* 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 = 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_guess_verity_roothash( + dissected_image, + p->verity); + if (r < 0) + return r; + + 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; + } } } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 0fe3a5b3e01..e155970e313 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; - - 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; - - (void) sym_blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + if (!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); + /* 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; - 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"); - - 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) + /* 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 | 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 = 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 = 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; + } } if (dest) { diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 951ae660556..9650dce2a49 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 47ea82c3b77..f16c6856cec 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