diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index d2de03d5f9..154cf7000f 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; }