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