]> err.no Git - util-linux/commitdiff
mount: prevent loop mounting the same file twice
authorKarel Zak <kzak@redhat.com>
Tue, 11 Sep 2007 12:35:34 +0000 (14:35 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 25 Oct 2007 23:02:44 +0000 (01:02 +0200)
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.

Co-Author: Matthias Koenig <mkoenig@suse.de>
Signed-off-by: Matthias Koenig <mkoenig@suse.de>
Signed-off-by: Karel Zak <kzak@redhat.com>
mount/lomount.c
mount/lomount.h
mount/mount.c

index b3c16fb6b7159a2b485330d27a0b41bdf6184376..5bd8954076e72867b32524ea1a235f3c80f81914 100644 (file)
@@ -139,7 +139,7 @@ show_used_loop_devices (void) {
 
        for (j = 0; j < SIZE(loop_formats); j++) {
            for(i = 0; i < 256; i++) {
-               sprintf(dev, loop_formats[j], i);
+               snprintf(dev, sizeof(dev), loop_formats[j], i);
                if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
                        fd = open (dev, O_RDONLY);
                        if (fd >= 0) {
@@ -165,6 +165,102 @@ show_used_loop_devices (void) {
 
 #endif
 
+/* check if the loopfile is already associated with the same given
+ * parameters.
+ *
+ * returns: -1 error
+ *           0 unused
+ *           1 loop device already used
+ */
+static int
+is_associated(int dev, struct stat *file, unsigned long long offset)
+{
+       struct loop_info64 linfo64;
+       struct loop_info64 linfo;
+       int ret = 0;
+
+       if (ioctl(dev, LOOP_GET_STATUS64, &linfo64) == 0) {
+               if (file->st_dev == linfo64.lo_device &&
+                   file->st_ino == linfo64.lo_inode &&
+                   offset == linfo64.lo_offset)
+                       ret = 1;
+               return ret;
+       }
+       if (ioctl(dev, LOOP_GET_STATUS, &linfo) == 0) {
+               if (file->st_dev == linfo.lo_device &&
+                   file->st_ino == linfo.lo_inode &&
+                   offset == linfo.lo_offset)
+                       ret = 1;
+               return ret;
+       }
+
+       return errno == ENXIO ? 0 : -1;
+}
+
+/* check if the loop file is already used with the same given
+ * parameters. We check for device no, inode and offset.
+ * returns: associated devname or NULL
+ */
+char *
+loopfile_used (const char *filename, unsigned long long offset) {
+       char dev[20];
+       char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
+       int i, j, fd;
+       struct stat devstat, filestat;
+       struct loop_info loopinfo;
+
+       if (stat(filename, &filestat) == -1) {
+               perror(filename);
+               return NULL;
+       }
+
+       for (j = 0; j < SIZE(loop_formats); j++) {
+           for(i = 0; i < 256; i++) {
+               snprintf(dev, sizeof(dev), loop_formats[j], i);
+               if (stat (dev, &devstat) == 0 && S_ISBLK(devstat.st_mode)) {
+                       fd = open (dev, O_RDONLY);
+                       if (fd >= 0) {
+                               int res = 0;
+
+                               if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
+                                       res = is_associated(fd, &filestat, offset);
+                               close (fd);
+                               if (res == 1)
+                                       return xstrdup(dev);
+                       }
+                       continue; /* continue trying as long as devices exist */
+               }
+               break;
+           }
+       }
+       return NULL;
+}
+
+int
+loopfile_used_with(char *devname, const char *filename, unsigned long long offset)
+{
+       struct stat statbuf;
+       int fd, ret;
+
+       if (!is_loop_device(devname))
+               return 0;
+
+       if (stat(filename, &statbuf) == -1) {
+               perror(filename);
+               return -1;
+       }
+
+       fd = open(devname, O_RDONLY);
+       if (fd == -1) {
+               perror(devname);
+               return -1;
+       }
+       ret = is_associated(fd, &statbuf, offset);
+
+       close(fd);
+       return ret;
+}
+
 int
 is_loop_device (const char *device) {
        struct stat statbuf;
@@ -279,6 +375,16 @@ set_loop(const char *device, const char *file, unsigned long long offset,
        char *pass;
        char *filename;
 
+       if (verbose) {
+               char *xdev = loopfile_used(file, offset);
+
+               if (xdev) {
+                       printf(_("warning: %s is already associated with %s\n"),
+                                       file, xdev);
+                       free(xdev);
+               }
+       }
+
        mode = (*loopro ? O_RDONLY : O_RDWR);
        if ((ffd = open(file, mode)) < 0) {
                if (!*loopro && errno == EROFS)
index c5c913b5ed850978b1ec24761d480c5c6cc954a8..38b3a483ade5e2e6ac173090eb19ebfeaddeaf07 100644 (file)
@@ -3,3 +3,6 @@ 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 loopfile_used_with(char *devname, const char *filename, unsigned long long offset);
+extern char *loopfile_used (const char *filename, unsigned long long offset);
index 8ec3730dafd44f58ca22ab4a0c446cc4bd0243dd..9d43e5ffa968aab748acceae1ffd978bd0b8a29b 100644 (file)
@@ -826,9 +826,51 @@ 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 *node0, const char *loopfile, unsigned long long offset)
+{
+       struct mntentchn *mnt = NULL;
+       char *node;
+       int res = 0;
+
+       node = canonicalize_mountpoint(node0);
+
+       /* 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(node, mnt);
+       if (!mnt) {
+               free(node);
+               return 0;
+       }
+       for(; mnt && res == 0; mnt = getmntdirbackward(node, mnt)) {
+               char *p;
+
+               if (strncmp(mnt->m.mnt_fsname, "/dev/loop", 9) == 0)
+                       res = loopfile_used_with((char *) mnt->m.mnt_fsname,
+                                       loopfile, offset);
+
+               else if ((p = strstr(mnt->m.mnt_opts, "loop="))) {
+                       char *dev = xstrdup(p+5);
+                       if ((p = strchr(dev, ',')))
+                               *p = '\0';
+                       res = loopfile_used_with(dev, loopfile, offset);
+                       free(dev);
+               }
+       }
+
+       free(node);
+       return res;
+}
+
 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;
 
@@ -869,6 +911,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"), *loopfile, node);
+        return EX_FAIL;
+      }
+
       do {
         if (!*loopdev || !**loopdev)
          *loopdev = find_unused_loop_device();
@@ -1054,7 +1101,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;
   }