summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/loopdev.c14
-rw-r--r--sys-utils/losetup.811
-rw-r--r--sys-utils/losetup.c136
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) {