]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
losetup: add --nooverlap options
authorKarel Zak <kzak@redhat.com>
Wed, 17 Aug 2016 10:28:33 +0000 (12:28 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 17 Aug 2016 10:28:33 +0000 (12:28 +0200)
This patch introduces overlap detections and loop devices
re-use for losetup(8). We already support this feature for mount(8)
where it's enabled by default (because we mount filesystems and it's
always mistake to share the same filesystem between more loop
devices).

Stanislav has suggested to enable this feature also for losetup by
default. I'm not sure about it, IMHO it's better to keep losetup(8)
simple and stupid by default, and inform users about possible problems
and solutions in the man page.

The feature forces losetup to scan all loop devices always when new
one is requested. This maybe disadvantage (especially when we use
control-loop  to avoid /sys or /dev scans) on system with huge number
of loop devices.

Co-Author: Stanislav Brabec <sbrabec@suse.cz>
Signed-off-by: Karel Zak <kzak@redhat.com>
lib/loopdev.c
sys-utils/losetup.8
sys-utils/losetup.c

index 21c8f4366ae4473845b06329992c9c894a4c634c..8bbde042592cefe75e31bacf230511c17817b531 100644 (file)
@@ -353,10 +353,8 @@ int loopcxt_deinit_iterator(struct loopdev_cxt *lc)
                fclose(iter->proc);
        if (iter->sysblock)
                closedir(iter->sysblock);
-       iter->minors = NULL;
-       iter->proc = NULL;
-       iter->sysblock = NULL;
-       iter->done = 1;
+
+       memset(iter, 0, sizeof(*iter));
        return 0;
 }
 
@@ -1426,8 +1424,11 @@ int loopcxt_find_unused(struct loopdev_cxt *lc)
        DBG(CXT, ul_debugobj(lc, "find_unused requested"));
 
        if (lc->flags & LOOPDEV_FL_CONTROL) {
-               int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+               int ctl;
+
+               DBG(CXT, ul_debugobj(lc, "using loop-control"));
 
+               ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
                if (ctl >= 0)
                        rc = ioctl(ctl, LOOP_CTL_GET_FREE);
                if (rc >= 0) {
@@ -1443,6 +1444,7 @@ int loopcxt_find_unused(struct loopdev_cxt *lc)
        }
 
        if (rc < 0) {
+               DBG(CXT, ul_debugobj(lc, "using loop scan"));
                rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE);
                if (rc)
                        return rc;
@@ -1577,6 +1579,7 @@ int loopcxt_find_overlap(struct loopdev_cxt *lc, const char *filename,
        if (!filename)
                return -EINVAL;
 
+       DBG(CXT, ul_debugobj(lc, "find_overlap requested"));
        hasst = !stat(filename, &st);
 
        rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
@@ -1633,6 +1636,7 @@ int loopcxt_find_overlap(struct loopdev_cxt *lc, const char *filename,
                rc = 0; /* not found */
 found:
        loopcxt_deinit_iterator(lc);
+       DBG(CXT, ul_debugobj(lc, "find_overlap done [rc=%d]", rc));
        return rc;
 }
 
index b614416df3b3592e2011aa0531e6bdfc241ddf7b..805051290e8b1dc3e5cab943673ee7a36de364b2 100644 (file)
@@ -67,6 +67,11 @@ device is shown.  If no option is given, all loop devices are shown.
 .sp
 Note that the old output format (i.e. \fBlosetup -a\fR) with comma-delimited
 strings is deprecated in favour of the \fB--list\fR output format.
+.sp
+It's possible to create more independent loop devices for the same backing
+file.
+.B This setup may be dangerous, can cause data loss, corruption and overwrites.
+Use \fB\-\-nooverlap\fR to avoid this problem.
 
 .SH OPTIONS
 The \fIsize\fR and \fIoffset\fR
@@ -102,6 +107,12 @@ Find the first unused loop device.  If a
 argument is present, use the found device as loop device.
 Otherwise, just print its name.
 .TP
+.BR \-L , " \-\-nooverlap"
+Check for conflicts between loop devices to avoid situation when the same
+backing file is shared between more loop devices. If the file is already used
+by another device then re-use the device rather than a new one. The option
+makes sense only with \fB\-\-find\fP.
+.TP
 .BR \-j , " \-\-associated " \fIfile
 Show the status of all loop devices associated with the given
 .IR file .
index d9c75b9e0b80455f818f5ef48966cb26137a2429..ba1363de3b9399af35215d19255f905220217fbe 100644 (file)
@@ -392,6 +392,7 @@ static void usage(FILE *out)
        fputs(_(" -f, --find                    find first unused device\n"), out);
        fputs(_(" -c, --set-capacity <loopdev>  resize the device\n"), out);
        fputs(_(" -j, --associated <file>       list all devices associated with <file>\n"), out);
+       fputs(_(" -L, --nooverlap               avoid possible conflict between devices\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
 
@@ -444,10 +445,96 @@ static void warn_size(const char *filename, uint64_t size)
                        filename);
 }
 
+static int create_loop(struct loopdev_cxt *lc,
+                      int nooverlap, int lo_flags, int flags,
+                      const char *file, uint64_t offset, uint64_t sizelimit)
+{
+       int hasdev = loopcxt_has_device(lc);
+       int rc = 0;
+
+       /* Check for conflicts and re-user loop device if possible */
+       if (!hasdev && nooverlap) {
+               rc = loopcxt_find_overlap(lc, file, offset, sizelimit);
+               switch (rc) {
+               case 0: /* not found */
+                       break;
+
+               case 1: /* overlap */
+                       loopcxt_deinit(lc);
+                       errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file);
+
+               case 2: /* overlap -- full size and offset match (reuse) */
+               {
+                       uint32_t lc_encrypt_type;
+
+                       /* Once a loop is initialized RO, there is no
+                        * way to change its parameters. */
+                       if (loopcxt_is_readonly(lc)
+                           && !(lo_flags & LO_FLAGS_READ_ONLY)) {
+                               loopcxt_deinit(lc);
+                               errx(EXIT_FAILURE, _("%s: overlapping read-only loop device exists"), file);
+                       }
+
+                       /* This is no more supported, but check to be safe. */
+                       if (loopcxt_get_encrypt_type(lc, &lc_encrypt_type) == 0
+                           && lc_encrypt_type != LO_CRYPT_NONE) {
+                               loopcxt_deinit(lc);
+                               errx(EXIT_FAILURE, _("%s: overlapping encrypted loop device exists"), file);
+                       }
+                       return 0;       /* success, re-use */
+               }
+               default: /* error */
+                       loopcxt_deinit(lc);
+                       errx(EXIT_FAILURE, _("failed to inspect loop devices"));
+                       return -errno;
+               }
+       }
+
+       if (hasdev && !is_loopdev(loopcxt_get_device(lc)))
+               loopcxt_add_device(lc);
+
+       /* Create a new device */
+       do {
+               const char *errpre;
+
+               /* Note that loopcxt_{find_unused,set_device}() resets
+                * loopcxt struct.
+                */
+               if (!hasdev && (rc = loopcxt_find_unused(lc))) {
+                       warnx(_("cannot find an unused loop device"));
+                       break;
+               }
+               if (flags & LOOPDEV_FL_OFFSET)
+                       loopcxt_set_offset(lc, offset);
+               if (flags & LOOPDEV_FL_SIZELIMIT)
+                       loopcxt_set_sizelimit(lc, sizelimit);
+               if (lo_flags)
+                       loopcxt_set_flags(lc, lo_flags);
+               if ((rc = loopcxt_set_backing_file(lc, file))) {
+                       warn(_("%s: failed to use backing file"), file);
+                       break;
+               }
+               errno = 0;
+               rc = loopcxt_setup_device(lc);
+               if (rc == 0)
+                       break;                  /* success */
+               if (errno == EBUSY && !hasdev)
+                       continue;
+
+               /* errors */
+               errpre = hasdev && loopcxt_get_fd(lc) < 0 ?
+                                loopcxt_get_device(lc) : file;
+               warn(_("%s: failed to set up loop device"), errpre);
+               break;
+       } while (hasdev == 0);
+
+       return rc;
+}
+
 int main(int argc, char **argv)
 {
        struct loopdev_cxt lc;
-       int act = 0, flags = 0, c;
+       int act = 0, flags = 0, no_overlap = 0, c;
        char *file = NULL;
        uint64_t offset = 0, sizelimit = 0;
        int res = 0, showdev = 0, lo_flags = 0;
@@ -467,6 +554,7 @@ int main(int argc, char **argv)
                { "detach", 1, 0, 'd' },
                { "detach-all", 0, 0, 'D' },
                { "find", 0, 0, 'f' },
+               { "nooverlaps", 0, 0, 'L' },
                { "help", 0, 0, 'h' },
                { "associated", 1, 0, 'j' },
                { "json", 0, 0, 'J' },
@@ -502,7 +590,7 @@ int main(int argc, char **argv)
        if (loopcxt_init(&lc, 0))
                err(EXIT_FAILURE, _("failed to initialize loopcxt"));
 
-       while ((c = getopt_long(argc, argv, "ac:d:Dfhj:Jlno:O:PrvV",
+       while ((c = getopt_long(argc, argv, "ac:d:Dfhj:JlLno:O:PrvV",
                                longopts, NULL)) != -1) {
 
                err_exclusive_options(c, longopts, excl, excl_st);
@@ -547,6 +635,9 @@ int main(int argc, char **argv)
                case 'l':
                        list = 1;
                        break;
+               case 'L':
+                       no_overlap = 1;
+                       break;
                case 'n':
                        no_headings = 1;
                        break;
@@ -673,45 +764,7 @@ int main(int argc, char **argv)
 
        switch (act) {
        case A_CREATE:
-       {
-               int hasdev = loopcxt_has_device(&lc);
-
-               if (hasdev && !is_loopdev(loopcxt_get_device(&lc)))
-                       loopcxt_add_device(&lc);
-               do {
-                       const char *errpre;
-
-                       /* Note that loopcxt_{find_unused,set_device}() resets
-                        * loopcxt struct.
-                        */
-                       if (!hasdev && (res = loopcxt_find_unused(&lc))) {
-                               warnx(_("cannot find an unused loop device"));
-                               break;
-                       }
-                       if (flags & LOOPDEV_FL_OFFSET)
-                               loopcxt_set_offset(&lc, offset);
-                       if (flags & LOOPDEV_FL_SIZELIMIT)
-                               loopcxt_set_sizelimit(&lc, sizelimit);
-                       if (lo_flags)
-                               loopcxt_set_flags(&lc, lo_flags);
-                       if ((res = loopcxt_set_backing_file(&lc, file))) {
-                               warn(_("%s: failed to use backing file"), file);
-                               break;
-                       }
-                       errno = 0;
-                       res = loopcxt_setup_device(&lc);
-                       if (res == 0)
-                               break;                  /* success */
-                       if (errno == EBUSY && !hasdev)
-                               continue;
-
-                       /* errors */
-                       errpre = hasdev && loopcxt_get_fd(&lc) < 0 ?
-                                        loopcxt_get_device(&lc) : file;
-                       warn(_("%s: failed to set up loop device"), errpre);
-                       break;
-               } while (hasdev == 0);
-
+               res = create_loop(&lc, no_overlap, lo_flags, flags, file, offset, sizelimit);
                if (res == 0) {
                        if (showdev)
                                printf("%s\n", loopcxt_get_device(&lc));
@@ -720,7 +773,6 @@ int main(int argc, char **argv)
                                goto lo_set_dio;
                }
                break;
-       }
        case A_DELETE:
                res = delete_loop(&lc);
                while (optind < argc) {