This backport removes short option -L for --nooverlap, as it conflicts
with --logical-blocksize introduced in
util-linux-losetup-Add-support-for-setting-logical-blocksize.patch.

From 9a94b634a343e83bfa2a9d311074e3e520abdddd Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Wed, 17 Aug 2016 12:28:33 +0200
Subject: [PATCH 16/20] 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>
---
 lib/loopdev.c       |  14 ++++--
 sys-utils/losetup.8 |  11 +++++
 sys-utils/losetup.c | 136 ++++++++++++++++++++++++++++++++++++----------------
 3 files changed, 114 insertions(+), 47 deletions(-)

Index: util-linux-2.25/lib/loopdev.c
===================================================================
--- util-linux-2.25.orig/lib/loopdev.c
+++ util-linux-2.25/lib/loopdev.c
@@ -371,10 +371,8 @@ int loopcxt_deinit_iterator(struct loopd
 		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;
 }
 
@@ -1446,8 +1444,11 @@ int loopcxt_find_unused(struct loopdev_c
 	DBG(lc, loopdev_debug("find_unused requested"));
 
 	if (lc->flags & LOOPDEV_FL_CONTROL) {
-		int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+		int ctl;
+
+		DBG(lc, loopdev_debug("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) {
@@ -1463,6 +1464,7 @@ int loopcxt_find_unused(struct loopdev_c
 	}
 
 	if (rc < 0) {
+		DBG(lc, loopdev_debug("using loop scan"));
 		rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE);
 		if (rc)
 			return rc;
@@ -1597,6 +1599,7 @@ int loopcxt_find_overlap(struct loopdev_
 	if (!filename)
 		return -EINVAL;
 
+	DBG(lc, loopdev_debug("find_overlap requested"));
 	hasst = !stat(filename, &st);
 
 	rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
@@ -1653,6 +1656,7 @@ int loopcxt_find_overlap(struct loopdev_
 		rc = 0;	/* not found */
 found:
 	loopcxt_deinit_iterator(lc);
+	DBG(lc, loopdev_debug("find_overlap done [rc=%d]", rc));
 	return rc;
 }
 
Index: util-linux-2.25/sys-utils/losetup.8
===================================================================
--- util-linux-2.25.orig/sys-utils/losetup.8
+++ util-linux-2.25/sys-utils/losetup.8
@@ -67,6 +67,11 @@ device is shown.  If no option is given,
 .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
@@ -96,6 +101,12 @@ Find the first unused loop device.  If a
 .I file
 argument is present, use the found device as loop device.
 Otherwise, just print its name.
+.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
 .IP "\fB\-j, \-\-associated \fIfile\fP"
 Show the status of all loop devices associated with the given
 .IR file .
Index: util-linux-2.25/sys-utils/losetup.c
===================================================================
--- util-linux-2.25.orig/sys-utils/losetup.c
+++ util-linux-2.25/sys-utils/losetup.c
@@ -375,6 +375,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(_("     --nooverlap               avoid possible conflict between devices\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 
@@ -426,10 +427,96 @@ static void warn_size(const char *filena
 			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, blocksize = 0;
 	int res = 0, showdev = 0, lo_flags = 0;
@@ -439,7 +526,8 @@ int main(int argc, char **argv)
 	enum {
 		OPT_SIZELIMIT = CHAR_MAX + 1,
 		OPT_SHOW,
-		OPT_RAW
+		OPT_RAW,
+		OPT_NOOVERLAP
 	};
 	static const struct option longopts[] = {
 		{ "all", 0, 0, 'a' },
@@ -447,6 +535,7 @@ int main(int argc, char **argv)
 		{ "detach", 1, 0, 'd' },
 		{ "detach-all", 0, 0, 'D' },
 		{ "find", 0, 0, 'f' },
+		{ "nooverlaps", 0, 0, OPT_NOOVERLAP },
 		{ "help", 0, 0, 'h' },
 		{ "associated", 1, 0, 'j' },
 		{ "list", 0, 0, 'l' },
@@ -526,6 +615,9 @@ int main(int argc, char **argv)
 		case 'l':
 			list = 1;
 			break;
+		case OPT_NOOVERLAP:
+			no_overlap = 1;
+			break;
 		case 'n':
 			no_headings = 1;
 			break;
@@ -636,54 +728,13 @@ 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 (flags & LOOPDEV_FL_BLOCKSIZE)
-				loopcxt_set_blocksize(&lc, blocksize);
-			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));
 			warn_size(file, sizelimit);
 		}
 		break;
-	}
 	case A_DELETE:
 		res = delete_loop(&lc);
 		while (optind < argc) {
