summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak2016-08-17 12:28:33 +0200
committerKarel Zak2016-08-17 12:28:33 +0200
commit9a94b634a343e83bfa2a9d311074e3e520abdddd (patch)
treeb1118ead3294be824b9fdbbf01449bb822aad4cf
parenttests: keep 'hppa' in fdisk/bsd test too (diff)
downloadkernel-qcow2-util-linux-9a94b634a343e83bfa2a9d311074e3e520abdddd.tar.gz
kernel-qcow2-util-linux-9a94b634a343e83bfa2a9d311074e3e520abdddd.tar.xz
kernel-qcow2-util-linux-9a94b634a343e83bfa2a9d311074e3e520abdddd.zip
losetup: add --nooverlap options
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>
-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) {