From 2368077223fa5800cf88659c9c57a7f6517f3fad Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 11 Sep 2007 14:35:34 +0200 Subject: [PATCH] mount: prevent loop mounting the same file twice 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 Signed-off-by: Matthias Koenig Signed-off-by: Karel Zak --- mount/lomount.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++- mount/lomount.h | 3 ++ mount/mount.c | 51 ++++++++++++++++++++++- 3 files changed, 159 insertions(+), 3 deletions(-) diff --git a/mount/lomount.c b/mount/lomount.c index b3c16fb6..5bd89540 100644 --- a/mount/lomount.c +++ b/mount/lomount.c @@ -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) diff --git a/mount/lomount.h b/mount/lomount.h index c5c913b5..38b3a483 100644 --- a/mount/lomount.h +++ b/mount/lomount.h @@ -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); diff --git a/mount/mount.c b/mount/mount.c index 8ec3730d..9d43e5ff 100644 --- a/mount/mount.c +++ b/mount/mount.c @@ -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; } -- 2.39.5