* Copyright © 2011 Karel Zak <kzak@redhat.com>
*/
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
+#include <linux/loop.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include "sd-id128.h"
#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"
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;
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;
}