diff options
-rw-r--r-- | lib/loopdev.c | 14 | ||||
-rw-r--r-- | sys-utils/losetup.8 | 11 | ||||
-rw-r--r-- | sys-utils/losetup.c | 136 |
3 files changed, 114 insertions, 47 deletions
diff --git a/lib/loopdev.c b/lib/loopdev.c index 21c8f4366..8bbde0425 100644 --- a/lib/loopdev.c +++ b/lib/loopdev.c @@ -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; } diff --git a/sys-utils/losetup.8 b/sys-utils/losetup.8 index b614416df..805051290 100644 --- a/sys-utils/losetup.8 +++ b/sys-utils/losetup.8 @@ -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 . diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c index d9c75b9e0..ba1363de3 100644 --- a/sys-utils/losetup.c +++ b/sys-utils/losetup.c @@ -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) { |