From f1443709e0c2e6d9172bbe7e73ef63676ffb7667 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 May 2019 18:05:29 +0200 Subject: [PATCH] loop-util: optionally also resize partitions --- src/shared/loop-util.c | 101 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index f9be08bf35..8928e92dfd 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -2,14 +2,19 @@ #include #include +#include +#include #include #include #include #include "alloc-util.h" #include "fd-util.h" +#include "fileio.h" #include "loop-util.h" +#include "parse-util.h" #include "stat-util.h" +#include "stdio-util.h" int loop_device_make_full( int fd, @@ -209,13 +214,109 @@ int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) { return d->fd; } +static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) { + char sysfs[STRLEN("/sys/dev/block/:/partition") + 2*DECIMAL_STR_MAX(dev_t) + 1]; + _cleanup_free_ char *whole = NULL, *buffer = NULL; + uint64_t current_offset, current_size, partno; + _cleanup_close_ int whole_fd = -1; + struct stat st; + dev_t devno; + int r; + + assert(partition_fd >= 0); + + /* Resizes the partition the loopback device refer to (assuming it refers to one instead of an actual + * loopback device), and changes the offset, if needed. This is a fancy wrapper around + * BLKPG_RESIZE_PARTITION. */ + + if (fstat(partition_fd, &st) < 0) + return -errno; + + assert(S_ISBLK(st.st_mode)); + + xsprintf(sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)); + r = read_one_line_file(sysfs, &buffer); + if (r == -ENOENT) /* not a partition, cannot resize */ + return -ENOTTY; + if (r < 0) + return r; + r = safe_atou64(buffer, &partno); + if (r < 0) + return r; + + xsprintf(sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev)); + + buffer = mfree(buffer); + r = read_one_line_file(sysfs, &buffer); + if (r < 0) + return r; + r = safe_atou64(buffer, ¤t_offset); + if (r < 0) + return r; + if (current_offset > UINT64_MAX/512U) + return -EINVAL; + current_offset *= 512U; + + if (ioctl(partition_fd, BLKGETSIZE64, ¤t_size) < 0) + return -EINVAL; + + if (size == UINT64_MAX && offset == UINT64_MAX) + return 0; + if (current_size == size && current_offset == offset) + return 0; + + xsprintf(sysfs, "/sys/dev/block/%u:%u/../dev", major(st.st_rdev), minor(st.st_rdev)); + + buffer = mfree(buffer); + r = read_one_line_file(sysfs, &buffer); + if (r < 0) + return r; + r = parse_dev(buffer, &devno); + if (r < 0) + return r; + + r = device_path_make_major_minor(S_IFBLK, devno, &whole); + if (r < 0) + return r; + + whole_fd = open(whole, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (whole_fd < 0) + return -errno; + + struct blkpg_partition bp = { + .pno = partno, + .start = offset == UINT64_MAX ? current_offset : offset, + .length = size == UINT64_MAX ? current_size : size, + }; + + struct blkpg_ioctl_arg ba = { + .op = BLKPG_RESIZE_PARTITION, + .data = &bp, + .datalen = sizeof(bp), + }; + + if (ioctl(whole_fd, BLKPG, &ba) < 0) + return -errno; + + return 0; +} + int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size) { struct loop_info64 info; assert(d); + /* Changes the offset/start of the loop device relative to the beginning of the underlying file or + * block device. If this loop device actually refers to a partition and not a loopback device, we'll + * try to adjust the partition offsets instead. + * + * If either offset or size is UINT64_MAX we won't change that parameter. */ + if (d->fd < 0) return -EBADF; + if (d->nr < 0) /* not a loopback device */ + return resize_partition(d->fd, offset, size); + if (ioctl(d->fd, LOOP_GET_STATUS64, &info) < 0) return -errno; -- 2.25.1