diff options
author | Anita Zhang <the.anitazha@gmail.com> | 2019-12-04 16:07:41 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-04 16:07:41 -0800 |
commit | adae5eb9778870ef59ab512ae1339f59410f1554 (patch) | |
tree | 56bb16922b88beb39c0fc993080990b763d6440f | |
parent | Implement SNI when using DNS-over-TLS (diff) | |
parent | valgrind: temporarily handle that valgrind still doesn't know LOOP_GET_STATUS64 (diff) | |
download | systemd-adae5eb9778870ef59ab512ae1339f59410f1554.tar.xz systemd-adae5eb9778870ef59ab512ae1339f59410f1554.zip |
Merge pull request #14219 from poettering/homed-preparatory-loop
preparatory /dev/loopN support split out of homed PR
-rw-r--r-- | src/core/namespace.c | 2 | ||||
-rw-r--r-- | src/dissect/dissect.c | 5 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 2 | ||||
-rw-r--r-- | src/portable/portable.c | 4 | ||||
-rw-r--r-- | src/shared/dissect-image.c | 16 | ||||
-rw-r--r-- | src/shared/loop-util.c | 298 | ||||
-rw-r--r-- | src/shared/loop-util.h | 13 | ||||
-rw-r--r-- | src/shared/machine-image.c | 5 | ||||
-rw-r--r-- | src/test/test-dissect-image.c | 3 |
9 files changed, 305 insertions, 43 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c index c0ff44c5137..104e96193d7 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <errno.h> +#include <linux/loop.h> #include <sched.h> #include <stdio.h> #include <sys/mount.h> @@ -1220,6 +1221,7 @@ int setup_namespace( r = loop_device_make_by_path(root_image, dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR, + LO_FLAGS_PARTSCAN, &loop_device); if (r < 0) return log_debug_errno(r, "Failed to create loop device for root image: %m"); diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 50de0afce6f..c1be6c034c5 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <fcntl.h> -#include <stdio.h> #include <getopt.h> +#include <linux/loop.h> +#include <stdio.h> #include "architecture.h" #include "dissect-image.h" @@ -171,7 +172,7 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; - r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d); + r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &d); if (r < 0) return log_error_errno(r, "Failed to set up loopback device: %m"); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 873a76596f0..9fac3262196 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5036,7 +5036,7 @@ static int run(int argc, char *argv[]) { goto finish; } - r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop); + r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &loop); if (r < 0) { log_error_errno(r, "Failed to set up loopback block device: %m"); goto finish; diff --git a/src/portable/portable.c b/src/portable/portable.c index 34b123e8469..7a86398a4b5 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <linux/loop.h> + #include "bus-common-errors.h" #include "bus-error.h" #include "conf-files.h" @@ -359,7 +361,7 @@ static int portable_extract_by_path( assert(path); - r = loop_device_make_by_path(path, O_RDONLY, &d); + r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d); if (r == -EISDIR) { /* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory * tree and not a raw device. It's easy then. */ diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 934e0fe830f..11d21c3a4d1 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#if HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif + #include <linux/dm-ioctl.h> #include <linux/loop.h> #include <sys/mount.h> @@ -215,9 +219,15 @@ static int wait_for_partitions_to_appear( * an explicit recognizable error about this, so that callers can generate a * proper message explaining the situation. */ - if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) { - log_debug("Device is a loop device and partition scanning is off!"); - return -EPROTONOSUPPORT; + if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0) { +#if HAVE_VALGRIND_MEMCHECK_H + /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */ + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + + if ((info.lo_flags & LO_FLAGS_PARTSCAN) == 0) + return log_debug_errno(EPROTONOSUPPORT, + "Device is a loop device and partition scanning is off!"); } } if (r != -EBUSY) diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index 559d7f8174e..acf3eae2d72 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -1,26 +1,40 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#if HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif + #include <errno.h> #include <fcntl.h> +#include <linux/blkpg.h> +#include <linux/fs.h> #include <linux/loop.h> +#include <sys/file.h> #include <sys/ioctl.h> #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(int fd, int open_flags, LoopDevice **ret) { - const struct loop_info64 info = { - .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0), - }; +int loop_device_make_full( + int fd, + int open_flags, + uint64_t offset, + uint64_t size, + uint32_t loop_flags, + LoopDevice **ret) { _cleanup_close_ int control = -1, loop = -1; _cleanup_free_ char *loopdev = NULL; unsigned n_attempts = 0; + struct loop_info64 info; + LoopDevice *d = NULL; struct stat st; - LoopDevice *d; - int nr, r; + int nr = -1, r; assert(fd >= 0); assert(ret); @@ -30,31 +44,47 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) { return -errno; if (S_ISBLK(st.st_mode)) { - int copy; + if (ioctl(loop, LOOP_GET_STATUS64, &info) >= 0) { + /* Oh! This is a loopback device? That's interesting! */ - /* If this is already a block device, store a copy of the fd as it is */ +#if HAVE_VALGRIND_MEMCHECK_H + /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */ + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + nr = info.lo_number; - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (copy < 0) - return -errno; + if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) + return -ENOMEM; + } - d = new0(LoopDevice, 1); - if (!d) - return -ENOMEM; + if (offset == 0 && IN_SET(size, 0, UINT64_MAX)) { + int copy; - *d = (LoopDevice) { - .fd = copy, - .nr = -1, - .relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */ - }; + /* If this is already a block device, store a copy of the fd as it is */ - *ret = d; - return d->fd; - } + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; - r = stat_verify_regular(&st); - if (r < 0) - return r; + d = new(LoopDevice, 1); + if (!d) + return -ENOMEM; + + *d = (LoopDevice) { + .fd = copy, + .nr = nr, + .node = TAKE_PTR(loopdev), + .relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */ + }; + + *ret = d; + return d->fd; + } + } else { + r = stat_verify_regular(&st); + if (r < 0) + return r; + } control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); if (control < 0) @@ -86,12 +116,23 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) { loop = safe_close(loop); } - if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) - return -errno; + info = (struct loop_info64) { + /* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */ + .lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((loop_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR, + .lo_offset = offset, + .lo_sizelimit = size == UINT64_MAX ? 0 : size, + }; + + if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) { + r = -errno; + goto fail; + } d = new(LoopDevice, 1); - if (!d) - return -ENOMEM; + if (!d) { + r = -ENOMEM; + goto fail; + } *d = (LoopDevice) { .fd = TAKE_FD(loop), @@ -101,9 +142,17 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) { *ret = d; return d->fd; + +fail: + if (fd >= 0) + (void) ioctl(fd, LOOP_CLR_FD); + if (d && d->fd >= 0) + (void) ioctl(d->fd, LOOP_CLR_FD); + + return r; } -int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) { +int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret) { _cleanup_close_ int fd = -1; assert(path); @@ -114,7 +163,7 @@ int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) if (fd < 0) return -errno; - return loop_device_make(fd, open_flags, ret); + return loop_device_make(fd, open_flags, loop_flags, ret); } LoopDevice* loop_device_unref(LoopDevice *d) { @@ -156,3 +205,190 @@ void loop_device_relinquish(LoopDevice *d) { d->relinquished = true; } + +int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) { + _cleanup_close_ int loop_fd = -1; + _cleanup_free_ char *p = NULL; + struct loop_info64 info; + struct stat st; + LoopDevice *d; + int nr; + + assert(loop_path); + assert(ret); + + loop_fd = open(loop_path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags); + if (loop_fd < 0) + return -errno; + + if (fstat(loop_fd, &st) < 0) + return -errno; + if (!S_ISBLK(st.st_mode)) + return -ENOTBLK; + + if (ioctl(loop_fd, LOOP_GET_STATUS64, &info) >= 0) { +#if HAVE_VALGRIND_MEMCHECK_H + /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */ + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + nr = info.lo_number; + } else + nr = -1; + + p = strdup(loop_path); + if (!p) + return -ENOMEM; + + d = new(LoopDevice, 1); + if (!d) + return -ENOMEM; + + *d = (LoopDevice) { + .fd = TAKE_FD(loop_fd), + .nr = nr, + .node = TAKE_PTR(p), + .relinquished = true, /* It's not ours, don't try to destroy it when this object is freed */ + }; + + *ret = d; + 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; + +#if HAVE_VALGRIND_MEMCHECK_H + /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */ + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + + if (size == UINT64_MAX && offset == UINT64_MAX) + return 0; + if (info.lo_sizelimit == size && info.lo_offset == offset) + return 0; + + if (size != UINT64_MAX) + info.lo_sizelimit = size; + if (offset != UINT64_MAX) + info.lo_offset = offset; + + if (ioctl(d->fd, LOOP_SET_STATUS64, &info) < 0) + return -errno; + + return 0; +} + +int loop_device_flock(LoopDevice *d, int operation) { + assert(d); + + if (d->fd < 0) + return -EBADF; + + if (flock(d->fd, operation) < 0) + return -errno; + + return 0; +} diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h index d78466c5ee6..5156b46ad61 100644 --- a/src/shared/loop-util.h +++ b/src/shared/loop-util.h @@ -14,10 +14,19 @@ struct LoopDevice { bool relinquished; }; -int loop_device_make(int fd, int open_flags, LoopDevice **ret); -int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret); +int loop_device_make_full(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, LoopDevice **ret); +static inline int loop_device_make(int fd, int open_flags, uint32_t loop_flags, LoopDevice **ret) { + return loop_device_make_full(fd, open_flags, 0, 0, loop_flags, ret); +} + +int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret); +int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret); LoopDevice* loop_device_unref(LoopDevice *d); DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref); void loop_device_relinquish(LoopDevice *d); + +int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size); + +int loop_device_flock(LoopDevice *d, int operation); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index a11803e731a..15fd514353a 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -2,13 +2,14 @@ #include <errno.h> #include <fcntl.h> +#include <linux/fs.h> +#include <linux/loop.h> #include <stdio.h> #include <stdlib.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <unistd.h> -#include <linux/fs.h> #include "alloc-util.h" #include "btrfs-util.h" @@ -1166,7 +1167,7 @@ int image_read_metadata(Image *i) { _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; - r = loop_device_make_by_path(i->path, O_RDONLY, &d); + r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, &d); if (r < 0) return r; diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c index 7b32e8373f9..12685dad137 100644 --- a/src/test/test-dissect-image.c +++ b/src/test/test-dissect-image.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include <fcntl.h> +#include <linux/loop.h> #include <stdio.h> #include "dissect-image.h" @@ -21,7 +22,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - r = loop_device_make_by_path(argv[1], O_RDONLY, &d); + r = loop_device_make_by_path(argv[1], O_RDONLY, LO_FLAGS_PARTSCAN, &d); if (r < 0) { log_error_errno(r, "Failed to set up loopback device: %m"); return EXIT_FAILURE; |