From dff7e1604680ee0118a04233ef365aebb869c9db Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Thu, 4 Aug 2016 11:48:26 +0200
Subject: [PATCH 12/20] libmount: one iteration to detect overlap and reuse loopdev

The current code scans loopdevs to detect already used loop device and
another scan to detect overlap.

Let's use one scan only, for this purpose loopcxt_find_overlap() has
been modified to return info (rc==2) about full size and offset match.

Signed-off-by: Karel Zak <kzak@redhat.com>
---
 lib/loopdev.c                  | 22 ++++++++++++---
 libmount/src/context_loopdev.c | 63 +++++++++++++++++++++---------------------
 2 files changed, 49 insertions(+), 36 deletions(-)

Index: util-linux-2.25/lib/loopdev.c
===================================================================
--- util-linux-2.25.orig/lib/loopdev.c
+++ util-linux-2.25/lib/loopdev.c
@@ -1566,7 +1566,7 @@ int loopcxt_find_by_backing_file(struct
 }
 
 /*
- * Returns: 0 = success, < 0 error, 1 not found
+ * Returns: 0 = not found, < 0 error, 1 found, 2 found full size and offset match
  */
 int loopcxt_find_overlap(struct loopdev_cxt *lc, const char *filename,
 			   uint64_t offset, uint64_t sizelimit)
@@ -1590,7 +1590,7 @@ int loopcxt_find_overlap(struct loopdev_
 				     filename, offset, sizelimit, 0);
 		if (!rc)
 			continue;	/* unused */
-		if (rc != 1)
+		if (rc < 0)
 			break;		/* error */
 
 		DBG(lc, loopdev_debug("found %s backed by %s",
@@ -1609,15 +1609,29 @@ int loopcxt_find_overlap(struct loopdev_
 			break;
 		}
 
+		/* full match */
+		if (lc_sizelimit == sizelimit && lc_offset == offset) {
+			DBG(lc, loopdev_debug("overlapping loop device %s (full match)",
+						loopcxt_get_device(lc)));
+			rc = 2;
+			goto found;
+		}
+
+		/* overlap */
 		if (lc_sizelimit != 0 && offset >= lc_offset + lc_sizelimit)
 			continue;
 		if (sizelimit != 0 && offset + sizelimit <= lc_offset)
 			continue;
+
 		DBG(lc, loopdev_debug("overlapping loop device %s",
 			loopcxt_get_device(lc)));
-			rc = 0;
-			break;
+			rc = 1;
+			goto found;
 	}
+
+	if (rc == 1)
+		rc = 0;	/* not found */
+found:
 	loopcxt_deinit_iterator(lc);
 	return rc;
 }
Index: util-linux-2.25/libmount/src/context_loopdev.c
===================================================================
--- util-linux-2.25.orig/libmount/src/context_loopdev.c
+++ util-linux-2.25/libmount/src/context_loopdev.c
@@ -228,58 +228,54 @@ int mnt_context_setup_loopdev(struct lib
 		if (rc)
 			goto done_no_deinit;
 
-		rc = loopcxt_find_by_backing_file(&lc,
-				backing_file, offset, sizelimit,
-				LOOPDEV_FL_OFFSET | LOOPDEV_FL_SIZELIMIT);
-		if (rc < 0)
+		rc = loopcxt_find_overlap(&lc, backing_file, offset, sizelimit);
+		switch (rc) {
+		case 0: /* not found */
+			DBG(CXT, ul_debugobj(cxt, "not found overlaping loopdev"));
+			loopcxt_deinit(&lc);
+			break;
+
+		case 1:	/* overlap */
+			DBG(CXT, ul_debugobj(cxt, "overlaping %s detected",
+						loopcxt_get_device(&lc)));
+			rc = -MNT_ERR_LOOPOVERLAP;
 			goto done;
-		if (rc == 0) {
+
+		case 2: /* overlap -- full size and offset match (reuse) */
+		{
 			uint32_t lc_encrypt_type;
 
-			DBG(CXT, ul_debugobj(cxt, "using existing loop device %s",
+			DBG(CXT, ul_debugobj(cxt, "re-using existing loop device %s",
 				loopcxt_get_device(&lc)));
 
 			/* 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)) {
+			 * way to change its parameters. */
+			if (loopcxt_is_readonly(&lc)
+			    && !(lo_flags & LO_FLAGS_READ_ONLY)) {
+				DBG(CXT, ul_debugobj(cxt, "%s is read-only",
+						loopcxt_get_device(&lc)));
 				rc = -EROFS;
 				goto done;
 			}
 
-			/* This is no more supported, but check to be
-			 * safe. */
-			if (loopcxt_get_encrypt_type(&lc, &lc_encrypt_type)) {
-				DBG(CXT, ul_debugobj(cxt, "failed to get crypt type for device %s",
-					loopcxt_get_device(&lc)));
-				rc = -MNT_ERR_LOOPDEV;
-				goto done;
-			}
-			if (lc_encrypt_type != LO_CRYPT_NONE) {
+			/* 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) {
 				DBG(CXT, ul_debugobj(cxt, "encryption no longer supported for device %s",
 					loopcxt_get_device(&lc)));
 				rc = -MNT_ERR_LOOPOVERLAP;
 				goto done;
 			}
+			rc = 0;
 			goto success;
 		}
-		loopcxt_deinit(&lc);
-		/* No existing loop device matches. Now we need to
-		 * check that no loop device overlaps our target range
-		 * inside the backing file. */
-		rc = loopcxt_init(&lc, 0);
-		if (rc)
-			goto done_no_deinit;
-
-		rc = loopcxt_find_overlap(&lc, backing_file, offset, sizelimit);
-		if (rc < 0)
-			goto done;
-		if (rc == 0) {
-			rc = -MNT_ERR_LOOPOVERLAP;
+		default: /* error */
 			goto done;
 		}
 		loopcxt_deinit(&lc);
 	}
 
+	DBG(CXT, ul_debugobj(cxt, "not found; create a new loop device"));
 	rc = loopcxt_init(&lc, 0);
 	if (rc)
 		goto done_no_deinit;
@@ -369,7 +365,11 @@ success:
 		 * otherwise it will be auto-cleared by kernel
 		 */
 		cxt->loopdev_fd = loopcxt_get_fd(&lc);
-		loopcxt_set_fd(&lc, -1, 0);
+		if (cxt->loopdev_fd < 0) {
+			DBG(CXT, ul_debugobj(cxt, "failed to get loopdev FD"));
+			rc = -errno;
+		} else
+			loopcxt_set_fd(&lc, -1, 0);
 	}
 done:
 	loopcxt_deinit(&lc);
