diff --git a/lib/loopdev.c b/lib/loopdev.c index 2035d2314..0e22bca1c 100644 --- a/lib/loopdev.c +++ b/lib/loopdev.c @@ -1225,7 +1225,7 @@ int loopdev_is_used(const char *device, const char *filename, struct stat st; int rc = 0; - if (!device) + if (!device || !filename) return 0; loopcxt_init(&lc, 0); @@ -1243,6 +1243,9 @@ int loopdev_delete(const char *device) struct loopdev_cxt lc; int rc; + if (!device) + return -EINVAL; + loopcxt_init(&lc, 0); rc = loopcxt_set_device(&lc, device); if (!rc) diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c index 226b562c4..8e9ed93aa 100644 --- a/libmount/src/context_loopdev.c +++ b/libmount/src/context_loopdev.c @@ -69,6 +69,68 @@ int mnt_context_is_loopdev(struct libmnt_context *cxt) return 0; } + +/* Check, if there already exists a mounted loop device on the mountpoint node + * with the same parameters. + */ +static int is_mounted_same_loopfile(struct libmnt_context *cxt, + const char *target, + const char *backing_file, + uint64_t offset) +{ + struct libmnt_table *tb; + struct libmnt_iter itr; + struct libmnt_fs *fs; + struct libmnt_cache *cache; + + assert(cxt); + assert(cxt->fs); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (!target || !backing_file || mnt_context_get_mtab(cxt, &tb)) + return 0; + + DBG(CXT, mnt_debug_h(cxt, "checking if %s mounted on %d", + backing_file, target)); + + cache = mnt_context_get_cache(cxt); + mnt_reset_iter(&itr, MNT_ITER_BACKWARD); + + /* Search for mountpoint node in mtab, procceed if any of these has the + * loop option set or the device is a loop device + */ + while (mnt_table_next_fs(tb, &itr, &fs) == 0) { + const char *src = mnt_fs_get_source(fs); + const char *opts = mnt_fs_get_user_options(fs); + char *val; + size_t len; + int res = 0; + + if (!src || !mnt_fs_match_target(fs, target, cache)) + continue; + + if (strncmp(src, "/dev/loop", 9) == 0) { + res = loopdev_is_used((char *) src, backing_file, + offset, LOOPDEV_FL_OFFSET); + + } else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) && + mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { + + val = strndup(val, len); + res = loopdev_is_used((char *) val, backing_file, + offset, LOOPDEV_FL_OFFSET); + free(val); + } + + if (res) { + DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file)); + return 1; + } + } + + return 0; +} + int mnt_context_setup_loopdev(struct libmnt_context *cxt) { const char *backing_file, *optstr, *loopdev = NULL; @@ -147,6 +209,11 @@ int mnt_context_setup_loopdev(struct libmnt_context *cxt) } } + if (rc == 0 && is_mounted_same_loopfile(cxt, + mnt_context_get_target(cxt), + backing_file, offset)) + rc = -EBUSY; + if (rc) goto done; diff --git a/sys-utils/mount.c b/sys-utils/mount.c index de972a83d..c81af1af9 100644 --- a/sys-utils/mount.c +++ b/sys-utils/mount.c @@ -286,11 +286,15 @@ try_readonly: if (!mnt_context_syscall_called(cxt)) { /* - * libmount errors + * libmount errors (extra library checks) */ - if (rc == -EPERM) { + switch (rc) { + case -EPERM: warnx(_("only root can mount %s on %s"), src, tgt); return EX_USAGE; + case -EBUSY: + warnx(_("%s is already mounted"), src); + return EX_USAGE; } if (src == NULL || tgt == NULL) {