From 01db9c85cf928de2ab4cfa07acbe35d0f574de44 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2023 17:58:33 +0100 Subject: [PATCH 1/6] blockdev-util: add new helper blockdev_get_device_size() This function is just a wrapper around the BLKGETSIZE64. Which is a pretty simple ioctl. The only reason to wrap it, is that the headers we need to call it are a bit messy (as "linux/fs.h" is incompatible with certain glibc headers). Hence add the simple helper that wraps it and allows us to do the header mess needed in one file only. It's also nicely symmetric to blockdev_get_sector_size(). --- src/home/homework-luks.c | 12 +++++++----- src/partition/growfs.c | 16 +++++++++------- src/partition/repart.c | 10 ++++++---- src/shared/blockdev-util.c | 15 +++++++++++++++ src/shared/blockdev-util.h | 1 + src/shared/discover-image.c | 6 ++++-- src/shared/dissect-image.c | 5 +++-- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 5bd78a03ed..32978eb37a 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -199,7 +199,7 @@ static int block_get_size_by_fd(int fd, uint64_t *ret) { if (!S_ISBLK(st.st_mode)) return -ENOTBLK; - return RET_NERRNO(ioctl(fd, BLKGETSIZE64, ret)); + return blockdev_get_device_size(fd, ret); } static int block_get_size_by_path(const char *path, uint64_t *ret) { @@ -2227,8 +2227,9 @@ int home_create_luks( if (flock(setup->image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */ return log_error_errno(errno, "Failed to lock block device %s: %m", ip); - if (ioctl(setup->image_fd, BLKGETSIZE64, &block_device_size) < 0) - return log_error_errno(errno, "Failed to read block device size: %m"); + r = blockdev_get_device_size(setup->image_fd, &block_device_size); + if (r < 0) + return log_error_errno(r, "Failed to read block device size: %m"); if (h->disk_size == UINT64_MAX) { @@ -3183,8 +3184,9 @@ int home_resize_luks( } else log_info("Operating on whole block device %s.", ip); - if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0) - return log_error_errno(errno, "Failed to determine size of original block device: %m"); + r = blockdev_get_device_size(image_fd, &old_image_size); + if (r < 0) + return log_error_errno(r, "Failed to determine size of original block device: %m"); if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */ return log_error_errno(errno, "Failed to lock block device %s: %m", ip); diff --git a/src/partition/growfs.c b/src/partition/growfs.c index 62f3ee6744..491040ffb0 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -55,8 +55,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_ return log_error_errno(r, "Failed to open main block device " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(main_devno)); - if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0) - return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m", + r = blockdev_get_device_size(main_devfd, &size); + if (r < 0) + return log_error_errno(r, "Failed to query size of \"%s\" (before resize): %m", main_devpath); log_debug("%s is %"PRIu64" bytes", main_devpath, size); @@ -83,9 +84,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_ if (r < 0) return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath); - if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0) - log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m", - devpath); + r = blockdev_get_device_size(main_devfd, &size); + if (r < 0) + log_warning_errno(r, "Failed to query size of \"%s\" (after resize): %m", devpath); else log_debug("%s is now %"PRIu64" bytes", main_devpath, size); @@ -250,8 +251,9 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(devno)); - if (ioctl(devfd, BLKGETSIZE64, &size) != 0) - return log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath); + r = blockdev_get_device_size(devfd, &size); + if (r < 0) + return log_error_errno(r, "Failed to query size of \"%s\": %m", devpath); log_debug("Resizing \"%s\" to %"PRIu64" bytes...", arg_target, size); diff --git a/src/partition/repart.c b/src/partition/repart.c index 3a92d1be1c..1e9284e2e2 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -6042,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); @@ -7365,8 +7366,9 @@ static int resize_backing_fd( assert(loop_device); - if (ioctl(*fd, BLKGETSIZE64, ¤t_size) < 0) - return log_error_errno(errno, "Failed to determine size of block device %s: %m", node); + r = blockdev_get_device_size(*fd, ¤t_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) diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c index c906aec109..347cd787c3 100644 --- a/src/shared/blockdev-util.c +++ b/src/shared/blockdev-util.c @@ -777,6 +777,21 @@ int blockdev_get_sector_size(int fd, uint32_t *ret) { return 0; } +int blockdev_get_device_size(int fd, uint64_t *ret) { + uint64_t sz = 0; + + assert(fd >= 0); + assert(ret); + + /* This is just a type-safe wrapper around BLKGETSIZE64 that gets us around having to include messy linux/fs.h in various clients */ + + if (ioctl(fd, BLKGETSIZE64, &sz) < 0) + return -errno; + + *ret = sz; + return 0; +} + int blockdev_get_root(int level, dev_t *ret) { _cleanup_free_ char *p = NULL; dev_t devno; diff --git a/src/shared/blockdev-util.h b/src/shared/blockdev-util.h index 954a23df3e..28aede289a 100644 --- a/src/shared/blockdev-util.h +++ b/src/shared/blockdev-util.h @@ -57,5 +57,6 @@ int block_device_has_partitions(sd_device *dev); int blockdev_reread_partition_table(sd_device *dev); int blockdev_get_sector_size(int fd, uint32_t *ret); +int blockdev_get_device_size(int fd, uint64_t *ret); int blockdev_get_root(int level, dev_t *ret); diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 348880cac8..b3f4c9ab76 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -13,6 +13,7 @@ #include #include "alloc-util.h" +#include "blockdev-util.h" #include "btrfs-util.h" #include "chase.h" #include "chattr-util.h" @@ -446,8 +447,9 @@ static int image_make( read_only = true; } - if (ioctl(block_fd, BLKGETSIZE64, &size) < 0) - log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename); + r = blockdev_get_device_size(block_fd, &size); + if (r < 0) + log_debug_errno(r, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename); block_fd = safe_close(block_fd); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 62aca296f6..5869843c42 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1759,8 +1759,9 @@ static int fs_grow(const char *node_path, int mount_fd, const char *mount_path) if (node_fd < 0) return log_debug_errno(errno, "Failed to open node device %s: %m", node_path); - if (ioctl(node_fd, BLKGETSIZE64, &size) != 0) - return log_debug_errno(errno, "Failed to get block device size of %s: %m", node_path); + r = blockdev_get_device_size(node_fd, &size); + if (r < 0) + return log_debug_errno(r, "Failed to get block device size of %s: %m", node_path); if (mount_fd < 0) { assert(mount_path); From c961a8c605a36bb8035380db3385be9ba2e45460 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2023 18:01:39 +0100 Subject: [PATCH 2/6] loop-util: also store the device size in LoopDevice That makes the field easily accessible, just as the sector size. --- src/home/homework-luks.c | 5 ++--- src/shared/loop-util.c | 22 ++++++++++++++++++---- src/shared/loop-util.h | 1 + 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 32978eb37a..57016fb14b 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -1295,9 +1295,6 @@ int home_setup_luks( if (!IN_SET(errno, ENOTTY, EINVAL)) return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n); - if (ioctl(setup->loop->fd, BLKGETSIZE64, &size) < 0) - return log_error_errno(r, "Failed to read block device size of %s: %m", n); - if (fstat(setup->loop->fd, &st) < 0) return log_error_errno(r, "Failed to stat block device %s: %m", n); assert(S_ISBLK(st.st_mode)); @@ -1329,6 +1326,8 @@ int home_setup_luks( offset *= 512U; } + + size = setup->loop->device_size; } else { #if HAVE_VALGRIND_MEMCHECK_H VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index b11e9268af..c337e881fe 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -149,8 +149,9 @@ static int loop_configure_verify(int fd, const struct loop_config *c) { * effect hence. And if not use classic LOOP_SET_STATUS64. */ uint64_t z; - if (ioctl(fd, BLKGETSIZE64, &z) < 0) - return -errno; + r = blockdev_get_device_size(fd, &z); + if (r < 0) + return r; if (z != c->info.lo_sizelimit) { log_debug("LOOP_CONFIGURE is broken, doesn't honour .info.lo_sizelimit. Falling back to LOOP_SET_STATUS64."); @@ -404,6 +405,11 @@ static int loop_configure( assert_not_reached(); } + uint64_t device_size; + r = blockdev_get_device_size(loop_with_fd, &device_size); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get loopback device size: %m"); + LoopDevice *d = new(LoopDevice, 1); if (!d) return log_oom_debug(); @@ -420,6 +426,7 @@ static int loop_configure( .uevent_seqnum_not_before = seqnum, .timestamp_not_before = timestamp, .sector_size = c->block_size, + .device_size = device_size, }; *ret = TAKE_PTR(d); @@ -944,6 +951,11 @@ int loop_device_open( if (r < 0) return r; + uint64_t device_size; + r = blockdev_get_device_size(fd, &device_size); + if (r < 0) + return r; + r = sd_device_get_devnum(dev, &devnum); if (r < 0) return r; @@ -976,6 +988,7 @@ int loop_device_open( .uevent_seqnum_not_before = UINT64_MAX, .timestamp_not_before = USEC_INFINITY, .sector_size = sector_size, + .device_size = device_size, }; *ret = d; @@ -1057,8 +1070,9 @@ static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) { return -EINVAL; current_offset *= 512U; - if (ioctl(partition_fd, BLKGETSIZE64, ¤t_size) < 0) - return -EINVAL; + r = blockdev_get_device_size(partition_fd, ¤t_size); + if (r < 0) + return r; if (size == UINT64_MAX && offset == UINT64_MAX) return 0; diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h index d77c314af8..194e7f78d4 100644 --- a/src/shared/loop-util.h +++ b/src/shared/loop-util.h @@ -28,6 +28,7 @@ struct LoopDevice { uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */ usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */ uint32_t sector_size; + uint64_t device_size; }; /* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */ From 6bc201343fea0d01b96eac37c46dd12b2a3dfab4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2023 18:02:45 +0100 Subject: [PATCH 3/6] loop-util: remember if we created the LoopDevice Let's store in a bool whether a LoopDevice object was created via loop_device_open() or loop_device_make(). --- src/shared/loop-util.c | 2 ++ src/shared/loop-util.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index c337e881fe..8434587286 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -427,6 +427,7 @@ static int loop_configure( .timestamp_not_before = timestamp, .sector_size = c->block_size, .device_size = device_size, + .created = true, }; *ret = TAKE_PTR(d); @@ -989,6 +990,7 @@ int loop_device_open( .timestamp_not_before = USEC_INFINITY, .sector_size = sector_size, .device_size = device_size, + .created = false, }; *ret = d; diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h index 194e7f78d4..94357bba71 100644 --- a/src/shared/loop-util.h +++ b/src/shared/loop-util.h @@ -22,6 +22,7 @@ struct LoopDevice { sd_device *dev; char *backing_file; bool relinquished; + bool created; /* If we created the device */ dev_t backing_devno; /* The backing file's dev_t */ ino_t backing_inode; /* The backing file's ino_t */ uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach, or 0 if we don't know */ From 51778dea0dd84f0564f77cce4a69983da12a9ce5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2023 18:04:44 +0100 Subject: [PATCH 4/6] dissect-image: also store the image size in DissectedImage That way we can easily access it the same way regardless if we operate on a block device or on a regular file. --- src/shared/dissect-image.c | 9 ++++++++- src/shared/dissect-image.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 5869843c42..98690367a1 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1547,6 +1547,7 @@ int dissect_image_file( #if HAVE_BLKID _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_close_ int fd = -EBADF; + struct stat st; int r; assert(path); @@ -1555,7 +1556,10 @@ int dissect_image_file( if (fd < 0) return -errno; - r = fd_verify_regular(fd); + if (fstat(fd, &st) < 0) + return -errno; + + r = stat_verify_regular(&st); if (r < 0) return r; @@ -1563,6 +1567,8 @@ int dissect_image_file( if (r < 0) return r; + m->image_size = st.st_size; + r = probe_sector_size(fd, &m->sector_size); if (r < 0) return r; @@ -3699,6 +3705,7 @@ int dissect_loop_device( return r; m->loop = loop_device_ref(loop); + m->image_size = m->loop->device_size; m->sector_size = m->loop->sector_size; r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags); diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 15c0bf7219..ed02049ed0 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -102,6 +102,7 @@ struct DissectedImage { DecryptedImage *decrypted_image; uint32_t sector_size; + uint64_t image_size; char *image_name; sd_id128_t image_uuid; From 5b1b37c86aa7bfebcb80c4ef98db9eecc5dff2e3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2023 18:25:54 +0100 Subject: [PATCH 5/6] dissect-tool: show sector/image size from DissectedImage object The information is provided to us already in the structure now, hence use it. --- src/dissect/dissect.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 92432b6fed..aea3d6234e 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -827,16 +827,15 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) pager_open(arg_pager_flags); - if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal()); - - if (ioctl(d->fd, BLKGETSIZE64, &size) < 0) - log_debug_errno(errno, "Failed to query size of loopback device: %m"); - else if (arg_json_format_flags & JSON_FORMAT_OFF) - printf(" Size: %s\n", FORMAT_BYTES(size)); - if (arg_json_format_flags & JSON_FORMAT_OFF) { - printf(" Sec. Size: %" PRIu32 "\n", m->sector_size); + printf(" Name: %s%s%s\n", + ansi_highlight(), bn, ansi_normal()); + + printf(" Size: %s\n", + FORMAT_BYTES(m->image_size)); + + printf(" Sec. Size: %" PRIu32 "\n", + m->sector_size); printf(" Arch.: %s\n", strna(architecture_to_string(dissected_image_architecture(m)))); From 64cd3d13eab7e16252d392ea65dcd1893e0e295c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Dec 2023 18:27:19 +0100 Subject: [PATCH 6/6] dissect-tool: hide device column if it's a short-lived loopback device It's pointless showing info that isn#t going to survive the current invocation, hence hide it. The "partition number" column is more useful since it kinda shows the same information, but without the device node name prefixed that is local to the currentl invocation. --- src/dissect/dissect.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index aea3d6234e..f9f47cf7aa 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -961,6 +961,11 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { table_set_ersatz_string(t, TABLE_ERSATZ_DASH); (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100); + /* Hide the device path if this is a loopback device that is not relinquished, since that means the + * device node is not going to be useful the instant our command exits */ + if ((!d || d->created) && (arg_json_format_flags & JSON_FORMAT_OFF)) + table_hide_column_from_display(t, 8); + for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) { DissectedPartition *p = m->partitions + i;