From 02c15120a17b18c4af49d62086374622431d8cf4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Dec 2022 17:15:10 +0100 Subject: [PATCH 1/4] test-loop-block: tighten tests a bit - check fstype, too Let's verify that we properly created the file systems once we did so. And tets this way that our dissector works correctly. --- src/test/test-loop-block.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index af2a9683a4..200cf2ba03 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -44,6 +44,15 @@ static void verify_dissected_image(DissectedImage *dissected) { assert_se(dissected->partitions[PARTITION_HOME].node); } +static void verify_dissected_image_harder(DissectedImage *dissected) { + verify_dissected_image(dissected); + + assert_se(streq(dissected->partitions[PARTITION_ESP].fstype, "vfat")); + assert_se(streq(dissected->partitions[PARTITION_XBOOTLDR].fstype, "vfat")); + assert_se(streq(dissected->partitions[PARTITION_ROOT].fstype, "ext4")); + assert_se(streq(dissected->partitions[PARTITION_HOME].fstype, "ext4")); +} + static void* thread_func(void *ptr) { int fd = PTR_TO_FD(ptr); int r; @@ -247,7 +256,7 @@ static int run(int argc, char *argv[]) { dissected = dissected_image_unref(dissected); assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0); - verify_dissected_image(dissected); + verify_dissected_image_harder(dissected); assert_se(mkdtemp_malloc(NULL, &mounted) >= 0); From 9f2d9a4aaba63d3b93d72e776413c8ed5c86e3fb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Dec 2022 17:16:18 +0100 Subject: [PATCH 2/4] test-loop-block: also test dissection without ADD/PIN of partition block devices Let's extend the test further, and try the codepaths where we do not pin/add the partition block devices (i.e. which is the codepaths we use when running without privs) --- src/test/test-loop-block.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index 200cf2ba03..01db8b092e 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -255,6 +255,14 @@ static int run(int argc, char *argv[]) { assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true) >= 0); dissected = dissected_image_unref(dissected); + + /* Try to read once, without pinning or adding partitions, i.e. by only accessing the whole block + * device. */ + assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0); + verify_dissected_image_harder(dissected); + dissected = dissected_image_unref(dissected); + + /* Now go via the loopback device after all, but this time add/pin, because now we want to mount it. */ assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0); verify_dissected_image_harder(dissected); From 59a4c0d7e362269c708ab8359f7e47e952ab245e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Dec 2022 17:17:46 +0100 Subject: [PATCH 3/4] test-loop-block: let's explicitly flush buffer cache on whole block device Let's explicitly flush the kernel's buffer cache on the whole block device once we ran "mkfs". This is necessary, because partition and whole block devices maintain separate buffer caches, and thus writing to one will not be visible on the other if cached there already, until the latter's cache is explicitly flushed. This is preparation for later adding support for probing file sytems also if we have no open partition block devices, and hence want to use the whole block device instead. --- src/test/test-loop-block.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index 01db8b092e..b06ab0d172 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "alloc-util.h" #include "capability-util.h" @@ -256,6 +258,13 @@ static int run(int argc, char *argv[]) { dissected = dissected_image_unref(dissected); + /* We created the file systems now via the per-partition block devices. But the dissection code might + * probe them via the whole block device. These block devices have separate buffer caches though, + * hence what was written via the partition device might not appear on the whole block device + * yet. Let's hence explicitly flush the whole block device, so that the read-back definitely + * works. */ + assert_se(ioctl(loop->fd, BLKFLSBUF, 0) >= 0); + /* Try to read once, without pinning or adding partitions, i.e. by only accessing the whole block * device. */ assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0); From c80c9079c895f8e0285682a99fb3f91b892b8734 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 30 Nov 2022 23:00:37 +0100 Subject: [PATCH 4/4] dissect-image: probe file system via main block device fd/image file fd let's make sure we can probe file systems also when unprivileged: instead of probing the partition block devices for file system signatures, let's go via the original "whole" fd. libblkid makes this easy actually, as it allows us to specify the offset/size of the area to probe. And we have the partition offsets/sizes anyway, so it's trivial for us to make use of. This thus enables fs probing also when lacking privs and operating on naked regular files without loopback devices or anything like this. --- src/shared/dissect-image.c | 42 +++++++++++++++++++++++++++----------- src/shared/dissect-image.h | 4 ++-- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index cf7fae76d1..ddd6c5430a 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -74,9 +74,15 @@ /* how many times to wait for the device nodes to appear */ #define N_DEVICE_NODE_LIST_ATTEMPTS 10 -int probe_filesystem_full(int fd, const char *path, char **ret_fstype) { +int probe_filesystem_full( + int fd, + const char *path, + uint64_t offset, + uint64_t size, + char **ret_fstype) { + /* Try to find device content type and return it in *ret_fstype. If nothing is found, - * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an + * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and a * different error otherwise. */ #if HAVE_BLKID @@ -105,12 +111,19 @@ int probe_filesystem_full(int fd, const char *path, char **ret_fstype) { path = path_by_fd; } + if (size == 0) /* empty size? nothing found! */ + goto not_found; + b = blkid_new_probe(); if (!b) return -ENOMEM; errno = 0; - r = blkid_probe_set_device(b, fd, 0, 0); + r = blkid_probe_set_device( + b, + fd, + offset, + size == UINT64_MAX ? 0 : size); /* when blkid sees size=0 it understands "everything". We prefer using UINT64_MAX for that */ if (r != 0) return errno_or_else(ENOMEM); @@ -154,7 +167,7 @@ not_found: } #if HAVE_BLKID -static int dissected_image_probe_filesystem(DissectedImage *m) { +static int dissected_image_probe_filesystems(DissectedImage *m, int fd) { int r; assert(m); @@ -167,9 +180,14 @@ static int dissected_image_probe_filesystem(DissectedImage *m) { if (!p->found) continue; - if (!p->fstype && p->mount_node_fd >= 0 && !p->decrypted_node) { - r = probe_filesystem_full(p->mount_node_fd, p->node, &p->fstype); - if (r < 0 && r != -EUCLEAN) + if (!p->fstype) { + /* If we have an fd referring to the partition block device, use that. Otherwise go + * via the whole block device or backing regular file, and read via offset. */ + if (p->mount_node_fd >= 0) + r = probe_filesystem_full(p->mount_node_fd, p->node, 0, UINT64_MAX, &p->fstype); + else + r = probe_filesystem_full(fd, p->node, p->offset, p->size, &p->fstype); + if (r < 0) return r; } @@ -1117,6 +1135,10 @@ static int dissect_image( } } + r = dissected_image_probe_filesystems(m, fd); + if (r < 0) + return r; + return 0; } #endif @@ -2240,7 +2262,7 @@ int dissected_image_decrypt( } if (!p->decrypted_fstype && p->mount_node_fd >= 0 && p->decrypted_node) { - r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, &p->decrypted_fstype); + r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, 0, UINT64_MAX, &p->decrypted_fstype); if (r < 0 && r != -EUCLEAN) return r; } @@ -2952,10 +2974,6 @@ int dissect_loop_device( if (r < 0) return r; - r = dissected_image_probe_filesystem(m); - if (r < 0) - return r; - *ret = TAKE_PTR(m); return 0; #else diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 52d8a2e569..2f77228a1d 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -133,9 +133,9 @@ MountOptions* mount_options_free_all(MountOptions *options); DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all); const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator); -int probe_filesystem_full(int fd, const char *path, char **ret_fstype); +int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t size, char **ret_fstype); static inline int probe_filesystem(const char *path, char **ret_fstype) { - return probe_filesystem_full(-1, path, ret_fstype); + return probe_filesystem_full(-1, path, 0, UINT64_MAX, ret_fstype); } int dissect_image_file( const char *path,