]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
losetup: add --loop-ref and REF column
authorKarel Zak <kzak@redhat.com>
Thu, 16 Mar 2023 12:41:36 +0000 (13:41 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 20 Jul 2023 21:08:53 +0000 (23:08 +0200)
The lo_file_name is nowhere used (kernel uses backing file descriptor,
no path) and it was used to store limited info about the backing file path
(64 bytes only!). For backward compatibility, we still fill lo_file_name
with the path, but it's nowhere in the userspace used as the complete
backing file path in sysfs.

This commit introduces a new option to overwrite the default path in
lo_file_name. The idea is to use the reference string by udevd in
/dev/loop/by-ref to address loop devices independently on paths.

Addresses: https://github.com/util-linux/util-linux/issues/2106
Suggested-by: Lennart Poettering <lennart@poettering.net>
Signed-off-by: Karel Zak <kzak@redhat.com>
include/loopdev.h
lib/loopdev.c
sys-utils/losetup.8.adoc
sys-utils/losetup.c

index 903adc4912ac2c28dcf200dfc8b4c538d56d14bd..49e38f2115dbe651a4c1993d1968b59d87791cc0 100644 (file)
@@ -122,7 +122,7 @@ struct loopdev_cxt {
        unsigned int    control_ok:1;   /* /dev/loop-control success */
 
        struct path_cxt         *sysfs; /* pointer to /sys/dev/block/<maj:min>/ */
-       struct loop_config      config; /* for GET/SET ioctl */
+       struct loop_config      config; /* for GET/SET ioctl */
        struct loopdev_iter     iter;   /* scans /sys or /dev for used/free devices */
 };
 
@@ -195,8 +195,10 @@ int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
 int loopcxt_set_blocksize(struct loopdev_cxt *lc, uint64_t blocksize);
 int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
 int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+int loopcxt_set_refname(struct loopdev_cxt *lc, const char *refname);
 
 extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
+extern char *loopcxt_get_refname(struct loopdev_cxt *lc);
 extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
 extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
 extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
index dd9ead3ee3ec093e85d5af6667c14042e67b3942..b3c537cd583b3df28a73191132576d6de2d2bd84 100644 (file)
@@ -747,6 +747,26 @@ char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
        return res;
 }
 
+/*
+ * @lc: context
+ *
+ * Returns (allocated) string with loop reference. The same as backing file by
+ * default.
+ */
+char *loopcxt_get_refname(struct loopdev_cxt *lc)
+{
+       char *res = NULL;
+       struct loop_info64 *lo = loopcxt_get_info(lc);
+
+       if (lo) {
+               lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+               res = strdup((char *) lo->lo_file_name);
+       }
+
+       DBG(CXT, ul_debugobj(lc, "get_refname [%s]", res));
+       return res;
+}
+
 /*
  * @lc: context
  * @offset: returns offset number for the given device
@@ -1180,6 +1200,28 @@ int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
        return 0;
 }
 
+/*
+ * @lc: context
+ * @refname: reference name (used to overwrite lo_file_name where is backing
+ *           file by default)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_refname(struct loopdev_cxt *lc, const char *refname)
+{
+       if (!lc)
+               return -EINVAL;
+
+       memset(lc->config.info.lo_file_name, 0, sizeof(lc->config.info.lo_file_name));
+       if (refname)
+               xstrncpy((char *)lc->config.info.lo_file_name, refname, LO_NAME_SIZE);
+
+       DBG(CXT, ul_debugobj(lc, "set refname=%s", (char *)lc->config.info.lo_file_name));
+       return 0;
+}
+
 /*
  * @lc: context
  * @filename: backing file path (the path will be canonicalized)
@@ -1197,9 +1239,10 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
        if (!lc->filename)
                return -errno;
 
-       xstrncpy((char *)lc->config.info.lo_file_name, lc->filename, LO_NAME_SIZE);
+       if (!lc->config.info.lo_file_name[0])
+               loopcxt_set_refname(lc, lc->filename);
 
-       DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->config.info.lo_file_name));
+       DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->filename));
        return 0;
 }
 
index 94e8e7b94b609551e0b61dbd605286a25c04f00b..0d4ad74a985d699d5ce9b617e8e3eeb73628bf0f 100644 (file)
@@ -30,7 +30,7 @@ Detach all associated loop devices:
 
 Set up a loop device:
 
-*losetup* [*-o* _offset_] [*--sizelimit* _size_] [*--sector-size* _size_] [*-Pr*] [*--show*] *-f*|_loopdev file_
+*losetup* [*-o* _offset_] [*--sizelimit* _size_] [*--sector-size* _size_] [*--loop-ref* _name_] [*-Pr*] [*--show*] *-f*|_loopdev file_
 
 Resize a loop device:
 
@@ -74,6 +74,9 @@ Show the status of all loop devices associated with the given _file_.
 *-o*, *--offset* _offset_::
 The data start is moved _offset_ bytes into the specified file or device. The _offset_ may be followed by the multiplicative suffixes; see above.
 
+*--loop-ref* _string_::
+Set reference string. The backwardly compatible default is to use the backing filename as a reference in loop setup ioctl (aka lo_file_name). This option can overwrite this default behavior and set the reference to the _string_. The reference may be used by udevd in /dev/loop/by-ref. Linux kernel does not use the reference at all, but it could be used by some old utils that cannot read the backing file from sysfs. The reference is readable only for the root user (see *--output* +REF) and it is restricted to 64 bytes.
+
 *--sizelimit* _size_::
 The data end is set to no more than _size_ bytes after the data start. The _size_ may be followed by the multiplicative suffixes; see above.
 
index 9e8bd509ac5c190fd00c75564da1df59ec3c1d87..dfdfc58721404831f00c506222dc4d1fe85dfc50 100644 (file)
@@ -48,6 +48,7 @@ enum {
        COL_MAJMIN,
        COL_OFFSET,
        COL_PARTSCAN,
+       COL_REF,
        COL_RO,
        COL_SIZELIMIT,
        COL_DIO,
@@ -76,6 +77,7 @@ static const struct colinfo infos[] = {
        [COL_NAME]        = { "NAME",      0.25, 0, N_("loop device name")},
        [COL_OFFSET]      = { "OFFSET",       5, SCOLS_FL_RIGHT, N_("offset from the beginning"), SCOLS_JSON_NUMBER},
        [COL_PARTSCAN]    = { "PARTSCAN",     1, SCOLS_FL_RIGHT, N_("partscan flag set"), SCOLS_JSON_BOOLEAN},
+       [COL_REF]         = { "REF",        0.1, 0, N_("loop device reference string")},
        [COL_RO]          = { "RO",           1, SCOLS_FL_RIGHT, N_("read-only device"), SCOLS_JSON_BOOLEAN},
        [COL_SIZELIMIT]   = { "SIZELIMIT",    5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes"), SCOLS_JSON_NUMBER},
        [COL_MAJMIN]      = { "MAJ:MIN",      3, 0, N_("loop device major:minor number")},
@@ -291,6 +293,9 @@ static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln)
                        if (loopcxt_get_blocksize(lc, &x) == 0)
                                xasprintf(&np, "%jd", x);
                        break;
+               case COL_REF:
+                       np = loopcxt_get_refname(lc);
+                       break;
                default:
                        return -EINVAL;
                }
@@ -423,6 +428,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -P, --partscan                create a partitioned loop device\n"), out);
        fputs(_(" -r, --read-only               set up a read-only loop device\n"), out);
        fputs(_("     --direct-io[=<on|off>]    open backing file with O_DIRECT\n"), out);
+       fputs(_("     --loop-ref <string>       loop device reference\n"), out);
        fputs(_("     --show                    print device name after setup (with -f)\n"), out);
        fputs(_(" -v, --verbose                 verbose mode\n"), out);
 
@@ -491,7 +497,8 @@ static int find_unused(struct loopdev_cxt *lc)
 
 static int create_loop(struct loopdev_cxt *lc,
                       int nooverlap, int lo_flags, int flags,
-                      const char *file, uint64_t offset, uint64_t sizelimit,
+                      const char *file, const char *refname,
+                      uint64_t offset, uint64_t sizelimit,
                       uint64_t blocksize)
 {
        int hasdev = loopcxt_has_device(lc);
@@ -580,7 +587,10 @@ static int create_loop(struct loopdev_cxt *lc,
                        loopcxt_set_flags(lc, lo_flags);
                if (blocksize > 0)
                        loopcxt_set_blocksize(lc, blocksize);
-
+               if (refname && (rc = loopcxt_set_refname(lc, refname))) {
+                       warnx(_("cannot set loop reference string"));
+                       break;
+               }
                if ((rc = loopcxt_set_backing_file(lc, file))) {
                        warn(_("%s: failed to use backing file"), file);
                        break;
@@ -610,7 +620,7 @@ int main(int argc, char **argv)
 {
        struct loopdev_cxt lc;
        int act = 0, flags = 0, no_overlap = 0, c;
-       char *file = NULL;
+       char *file = NULL, *refname = NULL;
        uint64_t offset = 0, sizelimit = 0, blocksize = 0;
        int res = 0, showdev = 0, lo_flags = 0;
        char *outarg = NULL;
@@ -621,6 +631,7 @@ int main(int argc, char **argv)
                OPT_SIZELIMIT = CHAR_MAX + 1,
                OPT_SHOW,
                OPT_RAW,
+               OPT_REF,
                OPT_DIO,
                OPT_OUTPUT_ALL
        };
@@ -645,6 +656,7 @@ int main(int argc, char **argv)
                { "read-only",    no_argument,       NULL, 'r'           },
                { "direct-io",    optional_argument, NULL, OPT_DIO       },
                { "raw",          no_argument,       NULL, OPT_RAW       },
+               { "loop-ref",     required_argument, NULL, OPT_REF,      },
                { "show",         no_argument,       NULL, OPT_SHOW      },
                { "verbose",      no_argument,       NULL, 'v'           },
                { "version",      no_argument,       NULL, 'V'           },
@@ -691,6 +703,9 @@ int main(int argc, char **argv)
                case 'r':
                        lo_flags |= LO_FLAGS_READ_ONLY;
                        break;
+               case OPT_REF:
+                       refname = optarg;
+                       break;
                case 'd':
                        act = A_DELETE;
                        if (!is_loopdev(optarg) ||
@@ -862,7 +877,7 @@ int main(int argc, char **argv)
 
        switch (act) {
        case A_CREATE:
-               res = create_loop(&lc, no_overlap, lo_flags, flags, file,
+               res = create_loop(&lc, no_overlap, lo_flags, flags, file, refname,
                                  offset, sizelimit, blocksize);
                if (res == 0) {
                        if (showdev)