[PATCH] mount: prevent loop mounting the same file twice

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The mount syscall prevents mounting the same device twice
to the same mountpoint. When loop mounting a file, for each
file a new loop device gets allocated, which prevents the detection
of loop mounting the same file to the same mountpoint twice.
The patch adds a check to prevent double mounts, if the same loopfile
is going to be mounted with the same offset to the same mountpoint.

Signed-off-by: Matthias Koenig <mkoenig@xxxxxxx>
---
 mount/lomount.c |   45 ++++++++++++++++++++++++++++++++++++++
 mount/lomount.h |    2 +
 mount/mount.c   |   65 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/mount/lomount.c b/mount/lomount.c
index e039e68..b0c9c6a 100644
--- a/mount/lomount.c
+++ b/mount/lomount.c
@@ -168,6 +168,51 @@ show_used_loop_devices (void) {
 
 #endif
 
+/* check if the loop device dev is already used with the same given
+ * parameters. We check for device no, inode and offset.
+ * returns: -1 error
+ *           0 unused
+ *           1 loop device already used
+ */
+int
+loop_device_used(const char *dev, const char *name, unsigned long long offset)
+{
+	struct stat statbuf;
+	struct loop_info64 loopinfo;
+	int fd, ret;
+
+	if (!is_loop_device(dev))
+		return 0;
+
+	if (stat(name, &statbuf) == -1) {
+		perror("stat");
+		return -1;
+	}
+
+	fd = open(dev, O_RDONLY);
+	if (fd == -1) {
+		perror("open");
+		return -1;
+	}
+
+	if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo) == -1) {
+		if (errno == ENXIO)
+			ret = 0;
+		else {
+			perror("ioctl");
+			ret = -1;
+		}
+	} else if (statbuf.st_dev == loopinfo.lo_device &&
+	           statbuf.st_ino == loopinfo.lo_inode &&
+		   offset == loopinfo.lo_offset)
+			ret = 1;
+		else
+			ret = 0;
+
+	close(fd);
+	return ret;
+}
+
 int
 is_loop_device (const char *device) {
 	struct stat statbuf;
diff --git a/mount/lomount.h b/mount/lomount.h
index 89695cd..e1cf8c3 100644
--- a/mount/lomount.h
+++ b/mount/lomount.h
@@ -4,3 +4,5 @@ extern int set_loop(const char *, const char *, unsigned long long,
 extern int del_loop(const char *);
 extern int is_loop_device(const char *);
 extern char * find_unused_loop_device(void);
+extern int loop_device_used(const char *, const char *, unsigned long long);
+
diff --git a/mount/mount.c b/mount/mount.c
index 40699f3..25d9166 100644
--- a/mount/mount.c
+++ b/mount/mount.c
@@ -42,6 +42,7 @@
 #include "mount_paths.h"
 #include "env.h"
 #include "nls.h"
+#include "realpath.h"
 
 #define DO_PS_FIDDLING
 
@@ -824,9 +825,64 @@ suid_check(const char *spec, const char *node, int *flags, char **user) {
   *flags &= ~(MS_OWNER | MS_GROUP);
 }
 
+/* Check, if there already exists a mounted loop device on the mountpoint node
+ * with the same parameters.
+ */
+static int
+is_mounted_same_loopfile(const char *node, const char *loopfile, unsigned long long offset)
+{
+	struct mntentchn *mnt = NULL;
+	char res_node[PATH_MAX+1];
+	int loop_found = 0;
+	char loop_prefix[] = "/dev/loop";
+	char *p, *loop_device = NULL;
+
+	myrealpath(node, res_node, PATH_MAX);
+
+	/* Search for mountpoint node in mtab,
+	 * procceed if any of these has the loop option set or
+	 * the device is a loop device
+	 */
+	mnt = getmntdirbackward(res_node, mnt);
+	if (!mnt)
+		return 0;
+
+	for(; mnt; mnt = getmntdirbackward(res_node, mnt)) {
+		if (p = strstr(mnt->m.mnt_opts, "loop=")) {
+			/* 1. parse loop device from options */
+			loop_device = xstrdup(p+5);
+			p = strchr(loop_device, ',');
+			if (p)
+				p = '\0';
+		} else if (strncmp(mnt->m.mnt_fsname, loop_prefix,
+			   strlen(loop_prefix)) == 0) {
+			/* 2. loop device is fsname */
+			loop_device = xstrdup(mnt->m.mnt_fsname);
+		} else {
+			loop_device = NULL;
+			continue;
+		}
+
+		/* check if there is already a loop device with the *same*
+		 * parameters (device, inode, offset)
+		 */
+		if (loop_device_used(loop_device, loopfile, offset)) {
+			if (loop_device)
+				free(loop_device);
+			return 1;
+		}
+	}
+
+	if (loop_device)
+		free(loop_device);
+
+	return 0;
+}
+
 static int
 loop_check(const char **spec, const char **type, int *flags,
-	   int *loop, const char **loopdev, const char **loopfile) {
+	   int *loop, const char **loopdev, const char **loopfile,
+	   const char *node) {
   int looptype;
   unsigned long long offset;
 
@@ -867,6 +923,11 @@ loop_check(const char **spec, const char **type, int *flags,
 
       offset = opt_offset ? strtoull(opt_offset, NULL, 0) : 0;
 
+      if (is_mounted_same_loopfile(node, *loopfile, offset)) {
+        error(_("mount: according to mtab %s is already mounted on %s as loop\n"), *loopfile, node);
+        return EX_FAIL;
+      }
+
       do {
         if (!*loopdev || !**loopdev)
 	  *loopdev = find_unused_loop_device();
@@ -1052,7 +1113,7 @@ try_mount_one (const char *spec0, const char *node0, const char *types0,
        * stale assignments of files to loop devices. Nasty when used for
        * encryption.
        */
-      res = loop_check(&spec, &types, &flags, &loop, &loopdev, &loopfile);
+      res = loop_check(&spec, &types, &flags, &loop, &loopdev, &loopfile, node);
       if (res)
 	  goto out;
   }
-- 
1.5.2.4

-
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux