From: Lennart Poettering Date: Mon, 6 Mar 2023 10:53:26 +0000 (+0100) Subject: udev-builtin-blkid: pick up info of backing file X-Git-Tag: v254-rc1~1072^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=be86e98350528b458dd5ec17bad84bbe2aea3976;p=thirdparty%2Fsystemd.git udev-builtin-blkid: pick up info of backing file This adds support for retrieving info about the inode backing a loopback file to udev-builtin-blkid. It will pick up the inode number and device of the backing inode, as well as the lo_file_name[] array that the loopback device maintains. A later patch uses this information to create block device symlinks in /dev/ that allow refering block devices by their backing inodes. This is useful when separate tools set up a loopback device from those which ultimately shall mount them, and there shall be a stable reference be passed along. For example, we can add a new kernel option setuploop= or so which allows setting up a block device via a generator, and still have a way to safely reference later. And yes, this doesn't directly have anything to do with the probing libblkid does, but it's close enough, and we have the device open anyway here, so the additional ioctl() here should not hurt. --- diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index d2de03d5f9b..154cf7000f9 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -5,11 +5,17 @@ * Copyright © 2011 Karel Zak */ +#if HAVE_VALGRIND_MEMCHECK_H +#include +#endif + #include #include #include +#include #include #include +#include #include #include "sd-id128.h" @@ -17,6 +23,7 @@ #include "alloc-util.h" #include "blkid-util.h" #include "device-util.h" +#include "devnum-util.h" #include "efi-loader.h" #include "errno-util.h" #include "fd-util.h" @@ -234,11 +241,86 @@ static int probe_superblocks(blkid_probe pr) { return blkid_do_safeprobe(pr); } +static int read_loopback_backing_inode( + sd_device *dev, + int fd, + dev_t *ret_devno, + ino_t *ret_inode, + char **ret_fname) { + + _cleanup_free_ char *fn = NULL; + struct loop_info64 info; + const char *name; + int r; + + assert(dev); + assert(fd >= 0); + assert(ret_devno); + assert(ret_inode); + assert(ret_fname); + + /* Retrieves various fields of the current loopback device backing file, so that we can ultimately + * use it to create stable symlinks to loopback block devices, based on what they are backed by. We + * pick up inode/device as well as file name field. Note that we pick up the "lo_file_name" field + * here, which is an arbitrary free-form string provided by userspace. We do not return the sysfs + * attribute loop/backing_file here, because that is directly accessible from udev rules anyway. And + * sometimes, depending on context, it's a good thing to return the string userspace can freely pick + * over the string automatically generated by the kernel. */ + + r = sd_device_get_sysname(dev, &name); + if (r < 0) + return r; + + if (!startswith(name, "loop")) + goto notloop; + + if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) { + if (ERRNO_IS_NOT_SUPPORTED(errno)) + goto notloop; + + return -errno; + } + +#if HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info)); +#endif + + if (isempty((char*) info.lo_file_name) || + strnlen((char*) info.lo_file_name, sizeof(info.lo_file_name)-1) == sizeof(info.lo_file_name)-1) + /* Don't pick up file name if it is unset or possibly truncated. (Note: we can't really know + * the file file name is truncated if it uses sizeof(info.lo_file_name)-1 as length; it could + * also just mean the string is just that long and wasn't truncated — but the fact is simply + * that we cannot know in that case if it was truncated or not, hence we assume the worst and + * suppress — at least for now. For shorter strings we know for sure it wasn't truncated, + * hence that's always safe.) */ + fn = NULL; + else { + fn = memdup_suffix0(info.lo_file_name, sizeof(info.lo_file_name)); + if (!fn) + return -ENOMEM; + } + + *ret_inode = info.lo_inode; + *ret_devno = info.lo_device; + *ret_fname = TAKE_PTR(fn); + return 1; + + +notloop: + *ret_devno = 0; + *ret_inode = 0; + *ret_fname = NULL; + return 0; +} + static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) { const char *devnode, *root_partition = NULL, *data, *name; _cleanup_(blkid_free_probep) blkid_probe pr = NULL; + _cleanup_free_ char *backing_fname = NULL; bool noraid = false, is_gpt = false; _cleanup_close_ int fd = -EBADF; + ino_t backing_inode = 0; + dev_t backing_devno = 0; int64_t offset = 0; int r; @@ -352,6 +434,32 @@ static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv if (is_gpt) find_gpt_root(dev, pr, test); + r = read_loopback_backing_inode( + dev, + fd, + &backing_devno, + &backing_inode, + &backing_fname); + if (r < 0) + log_device_debug_errno(dev, r, "Failed to read loopback backing inode, ignoring: %m"); + else if (r > 0) { + udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_DEVICE", DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(backing_devno)); + udev_builtin_add_propertyf(dev, test, "ID_LOOP_BACKING_INODE", "%" PRIu64, (uint64_t) backing_inode); + + if (backing_fname) { + /* In the worst case blkid_encode_string() will blow up to 4x the string + * length. Hence size the buffer to 4x of the longest string + * read_loopback_backing_inode() might return */ + char encoded[sizeof_field(struct loop_info64, lo_file_name) * 4 + 1]; + + assert(strlen(backing_fname) < ELEMENTSOF(encoded) / 4); + blkid_encode_string(backing_fname, encoded, ELEMENTSOF(encoded)); + + udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME", backing_fname); + udev_builtin_add_property(dev, test, "ID_LOOP_BACKING_FILENAME_ENC", encoded); + } + } + return 0; }