summaryrefslogtreecommitdiffstats
path: root/sys-utils/losetup.c
diff options
context:
space:
mode:
authorKarel Zak2016-08-17 12:28:33 +0200
committerKarel Zak2016-08-17 12:28:33 +0200
commit9a94b634a343e83bfa2a9d311074e3e520abdddd (patch)
treeb1118ead3294be824b9fdbbf01449bb822aad4cf /sys-utils/losetup.c
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>
Diffstat (limited to 'sys-utils/losetup.c')
-rw-r--r--sys-utils/losetup.c136
1 files changed, 94 insertions, 42 deletions
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) {