summaryrefslogtreecommitdiffstats
path: root/libmount
diff options
context:
space:
mode:
authorKarel Zak2011-06-10 15:39:56 +0200
committerKarel Zak2011-06-10 15:39:56 +0200
commit7f8b2bf3be6db30280bd8c5a77584350170abd11 (patch)
tree9d9513a6a21b99e483e92c9a0dbdbe65a1a6ba2b /libmount
parentlib: [loopdev.c] cleanup flags usage (diff)
downloadkernel-qcow2-util-linux-7f8b2bf3be6db30280bd8c5a77584350170abd11.tar.gz
kernel-qcow2-util-linux-7f8b2bf3be6db30280bd8c5a77584350170abd11.tar.xz
kernel-qcow2-util-linux-7f8b2bf3be6db30280bd8c5a77584350170abd11.zip
libmount: add mount support for loopdevs
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'libmount')
-rw-r--r--libmount/src/Makefile.am6
-rw-r--r--libmount/src/cache.c7
-rw-r--r--libmount/src/context.c36
-rw-r--r--libmount/src/context_loopdev.c227
-rw-r--r--libmount/src/libmount.h.in2
-rw-r--r--libmount/src/mountP.h7
-rw-r--r--libmount/src/optmap.c2
7 files changed, 261 insertions, 26 deletions
diff --git a/libmount/src/Makefile.am b/libmount/src/Makefile.am
index 56b0c207d..5525ba279 100644
--- a/libmount/src/Makefile.am
+++ b/libmount/src/Makefile.am
@@ -13,13 +13,17 @@ libmount_la_SOURCES = mountP.h version.c utils.c test.c init.c cache.c \
optstr.c optmap.c iter.c lock.c \
fs.c tab.c tab_parse.c tab_update.c tab_diff.c \
context.c context_mount.c context_umount.c \
+ context_loopdev.c \
$(mountinc_HEADERS) \
$(top_srcdir)/lib/at.c \
$(top_srcdir)/include/list.h \
$(top_srcdir)/lib/mangle.c \
$(top_srcdir)/lib/canonicalize.c \
$(top_srcdir)/lib/strutils.c \
- $(top_srcdir)/lib/env.c
+ $(top_srcdir)/lib/env.c \
+ $(top_srcdir)/lib/loopdev.c \
+ $(top_srcdir)/lib/sysfs.c \
+ $(top_srcdir)/lib/linux_version.c
nodist_libmount_la_SOURCES = mountP.h
diff --git a/libmount/src/cache.c b/libmount/src/cache.c
index 4775069c0..907e048d0 100644
--- a/libmount/src/cache.c
+++ b/libmount/src/cache.c
@@ -237,7 +237,6 @@ static int mnt_cache_get_probe(struct libmnt_cache *cache, const char *devname,
blkid_probe pr = cache ? cache->pr : NULL;
assert(devname);
- assert(res);
if (cache && cache->pr && (!cache->filename ||
strcmp(devname, cache->filename))) {
@@ -260,7 +259,8 @@ static int mnt_cache_get_probe(struct libmnt_cache *cache, const char *devname,
}
- *res = pr;
+ if (res)
+ *res = pr;
return 0;
}
@@ -277,7 +277,6 @@ static int mnt_cache_get_probe(struct libmnt_cache *cache, const char *devname,
int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
{
int i, ntags = 0, rc;
- blkid_probe pr;
const char *tags[] = { "LABEL", "UUID", "TYPE" };
assert(cache);
@@ -298,7 +297,7 @@ int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
return 0;
}
- rc = mnt_cache_get_probe(cache, devname, &pr);
+ rc = mnt_cache_get_probe(cache, devname, NULL);
if (rc)
return rc;
diff --git a/libmount/src/context.c b/libmount/src/context.c
index 6b0edc55b..d65c8f152 100644
--- a/libmount/src/context.c
+++ b/libmount/src/context.c
@@ -61,6 +61,7 @@ struct libmnt_context *mnt_new_context(void)
cxt->syscall_status = 1; /* not called yet */
cxt->helper_exec_status = 1;
+ cxt->loopdev_fd = -1;
/* if we're really root and aren't running setuid */
cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
@@ -90,8 +91,6 @@ void mnt_free_context(struct libmnt_context *cxt)
mnt_reset_context(cxt);
- DBG(CXT, mnt_debug_h(cxt, "free"));
-
free(cxt->fstype_pattern);
free(cxt->optstr_pattern);
@@ -100,9 +99,11 @@ void mnt_free_context(struct libmnt_context *cxt)
if (!(cxt->flags & MNT_FL_EXTERN_CACHE))
mnt_free_cache(cxt->cache);
+ mnt_context_clear_loopdev(cxt);
mnt_free_lock(cxt->lock);
mnt_free_update(cxt->update);
+ DBG(CXT, mnt_debug_h(cxt, "free"));
free(cxt);
}
@@ -133,6 +134,11 @@ int mnt_reset_context(struct libmnt_context *cxt)
if (!cxt)
return -EINVAL;
+ DBG(CXT, mnt_debug_h(cxt,
+ "reset [status=%d %s]",
+ mnt_context_get_status(cxt),
+ mnt_context_get_status(cxt) == 0 ? "FAILED" : "SUCCESS"));
+
fl = cxt->flags;
if (!(cxt->flags & MNT_FL_EXTERN_FS))
@@ -933,21 +939,6 @@ int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags
return rc;
}
-static int is_loop(struct libmnt_context *cxt)
-{
- unsigned long fl = 0;
-
- if (cxt->user_mountflags & MNT_MS_LOOP)
- return 1;
- if (!mnt_context_get_mflags(cxt, &fl) && (fl & MNT_MS_LOOP))
- return 1;
-
- /* TODO:
- * - support MNT_MS_{OFFSET,SIZELIMIT,ENCRYPTION}
- */
- return 0;
-}
-
/**
* mnt_context_set_mountdata:
* @cxt: mount context
@@ -1037,11 +1028,14 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
/*
* Initialize loop device
*/
- if (is_loop(cxt)) {
- ; /* TODO */
+ if (mnt_context_is_loopdev(cxt)) {
+ rc = mnt_context_setup_loopdev(cxt);
+ if (rc)
+ return rc;
}
- DBG(CXT, mnt_debug_h(cxt, "final srcpath '%s'", path));
+ DBG(CXT, mnt_debug_h(cxt, "final srcpath '%s'",
+ mnt_fs_get_source(cxt->fs)));
return 0;
}
@@ -1474,7 +1468,7 @@ int mnt_context_apply_fstab(struct libmnt_context *cxt)
* mnt_context_get_status:
* @cxt: mount context
*
- * Returns: 0 if /sbin/mount.type or mount(2) syscall was successfull.
+ * Returns: 1 if /sbin/mount.type or mount(2) syscall was successfull.
*/
int mnt_context_get_status(struct libmnt_context *cxt)
{
diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c
new file mode 100644
index 000000000..6ed0df13e
--- /dev/null
+++ b/libmount/src/context_loopdev.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/*
+ * DOCS: - "lo@" prefix for fstype is unsupported
+ * - encyption= mount option for loop device is unssuported
+ */
+
+#include <blkid.h>
+
+#include "mountP.h"
+#include "loopdev.h"
+#include "linux_version.h"
+
+
+int mnt_context_is_loopdev(struct libmnt_context *cxt)
+{
+ const char *type, *src;
+ int fl;
+
+ assert(cxt);
+ /* The mount flags have to be merged, otherwise we have to use
+ * expensive mnt_context_get_user_mflags() instead of cxt->user_mountflags. */
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt->fs)
+ return 0;
+ src = mnt_fs_get_srcpath(cxt->fs);
+ if (!src)
+ return 0; /* backing file not set */
+
+ if (cxt->user_mountflags & (MNT_MS_LOOP |
+ MNT_MS_OFFSET |
+ MNT_MS_SIZELIMIT))
+ return 1;
+
+ if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION))
+ return 0;
+
+ /* Automatically create a loop device from a regular file if a filesystem
+ * is not specified or the filesystem is known for libblkid (these
+ * filesystems work with block devices only).
+ *
+ * Note that there is not a restriction (on kernel side) that prevents regular
+ * file as a mount(2) source argument. A filesystem that is able to mount
+ * regular files could be implemented.
+ */
+ type = mnt_fs_get_fstype(cxt->fs);
+ fl = __mnt_fs_get_flags(cxt->fs);
+
+ if (!(fl && (MNT_FS_PSEUDO | MNT_FS_NET | MNT_FS_SWAP)) &&
+ (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) {
+ struct stat st;
+
+ if (stat(src, &st) || !S_ISREG(st.st_mode))
+ return 0;
+ }
+
+ return 1;
+}
+
+int mnt_context_setup_loopdev(struct libmnt_context *cxt)
+{
+ const char *backing_file;
+ char *loopdev = NULL;
+ size_t len;
+ struct loopdev_cxt lc;
+ int rc, lo_flags = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ backing_file = mnt_fs_get_srcpath(cxt->fs);
+ if (!backing_file)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "trying to setup loopdev for %s", backing_file));
+
+ if (cxt->mountflags & MS_RDONLY) {
+ DBG(CXT, mnt_debug_h(cxt, "enabling READ-ONLY flag"));
+ lo_flags |= LO_FLAGS_READ_ONLY;
+ }
+ loopcxt_init(&lc, 0);
+
+
+ if ((cxt->user_mountflags & MNT_MS_LOOP) &&
+ mnt_fs_get_option(cxt->fs, "loop", &loopdev, &len) == 0 && loopdev) {
+
+ char *tmp = strndup(loopdev, len);
+ if (!tmp)
+ rc = -ENOMEM;
+ else {
+ rc = loopcxt_set_device(&lc, tmp);
+ free(tmp);
+ }
+ }
+
+ /* since 2.6.37 we don't have to store backing filename to mtab
+ * because kernel provides the name in /sys.
+ */
+ if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) ||
+ !cxt->mtab_writable) {
+ DBG(CXT, mnt_debug_h(cxt, "enabling AUTOCLEAR flag"));
+ lo_flags |= LO_FLAGS_AUTOCLEAR;
+ }
+
+ do {
+ /* found free device */
+ if (!loopdev) {
+ rc = loopcxt_find_unused(&lc);
+ if (rc)
+ goto done;
+ DBG(CXT, mnt_debug_h(cxt, "trying to use %s",
+ loopcxt_get_device(&lc)));
+ }
+
+ /* set device attributes */
+ rc = loopcxt_set_backing_file(&lc, backing_file);
+ if (rc)
+ goto done;
+
+ loopcxt_set_flags(&lc, lo_flags);
+
+ /* setup the device */
+ rc = loopcxt_setup_device(&lc);
+ if (!rc)
+ break; /* success */
+
+ if (loopdev || rc != -EBUSY) {
+ DBG(CXT, mnt_debug_h(cxt, "failed to setup device"));
+ break;
+ }
+ DBG(CXT, mnt_debug_h(cxt, "loopdev stolen...trying again"));
+ } while (1);
+
+ if (!rc)
+ rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc));
+
+ if (!rc) {
+ /* success */
+ cxt->flags |= MNT_FL_LOOPDEV_READY;
+
+ if ((cxt->user_mountflags & MNT_MS_LOOP) &&
+ loopcxt_is_autoclear(&lc))
+ /*
+ * autoclear flag accepted by kernel, don't store
+ * the "loop=" option to mtab.
+ */
+ cxt->user_mountflags &= ~MNT_MS_LOOP;
+
+ if (!(cxt->mountflags & MS_RDONLY) &&
+ loopcxt_is_readonly(&lc))
+ /*
+ * mount planned read-write, but loopdev is read-only,
+ * let's fix mount options...
+ */
+ cxt->mountflags |= MS_RDONLY;
+
+
+ /* we have to keep the device open until mount(1),
+ * otherwise it will auto-cleared by kernel
+ */
+ cxt->loopdev_fd = loopcxt_get_fd(&lc);
+ loopcxt_set_fd(&lc, -1, 0);
+ }
+done:
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+/*
+ * Deletes loop device
+ */
+int mnt_context_delete_loopdev(struct libmnt_context *cxt)
+{
+ const char *src;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+
+ src = mnt_fs_get_srcpath(cxt->fs);
+ if (!src)
+ return -EINVAL;
+
+ if (cxt->loopdev_fd > -1)
+ close(cxt->loopdev_fd);
+
+ rc = loopdev_delete(src);
+ cxt->flags &= ~MNT_FL_LOOPDEV_READY;
+ cxt->loopdev_fd = -1;
+
+ DBG(CXT, mnt_debug_h(cxt, "loopdev deleted [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * Clears loopdev stuff in context, should be called after
+ * failed or successful mount(2).
+ */
+int mnt_context_clear_loopdev(struct libmnt_context *cxt)
+{
+ assert(cxt);
+
+ if (mnt_context_get_status(cxt) == 0 &&
+ (cxt->flags & MNT_FL_LOOPDEV_READY)) {
+ /*
+ * mount(2) failed, delete loopdev
+ */
+ mnt_context_delete_loopdev(cxt);
+
+ } else if (cxt->loopdev_fd > -1) {
+ /*
+ * mount(2) success, close the device
+ */
+ DBG(CXT, mnt_debug_h(cxt, "closing loopdev FD"));
+ close(cxt->loopdev_fd);
+ }
+ cxt->loopdev_fd = -1;
+ return 0;
+}
+
diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in
index 15222083f..18d0739ca 100644
--- a/libmount/src/libmount.h.in
+++ b/libmount/src/libmount.h.in
@@ -466,6 +466,8 @@ extern int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status
#define MNT_MS_UHELPER (1 << 11)
#define MNT_MS_HELPER (1 << 12)
#define MNT_MS_XCOMMENT (1 << 13)
+#define MNT_MS_OFFSET (1 << 14)
+#define MNT_MS_SIZELIMIT (1 << 15)
/*
* mount(2) MS_* masks (MNT_MAP_LINUX map)
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index 0a8c7522c..8c63fbd4e 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -259,6 +259,7 @@ struct libmnt_context
struct libmnt_table *mtab; /* mtab entries */
int optsmode; /* fstab optstr mode MNT_OPTSMODE_{AUTO,FORCE,IGNORE} */
+ int loopdev_fd; /* open loopdev */
unsigned long mountflags; /* final mount(2) flags */
const void *mountdata; /* final mount(2) data, string or binary data */
@@ -309,6 +310,7 @@ struct libmnt_context
#define MNT_FL_SAVED_USER (1 << 23)
#define MNT_FL_PREPARED (1 << 24)
#define MNT_FL_HELPER (1 << 25) /* [u]mount.<type> */
+#define MNT_FL_LOOPDEV_READY (1 << 26) /* /dev/loop<N> initialized by library */
/* default flags */
#define MNT_FL_DEFAULT 0
@@ -349,6 +351,11 @@ extern int mnt_context_update_tabs(struct libmnt_context *cxt);
extern int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg);
extern int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg);
+extern int mnt_context_is_loopdev(struct libmnt_context *cxt);
+extern int mnt_context_setup_loopdev(struct libmnt_context *cxt);
+extern int mnt_context_delete_loopdev(struct libmnt_context *cxt);
+extern int mnt_context_clear_loopdev(struct libmnt_context *cxt);
+
/* tab_update.c */
extern struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd);
extern int mnt_update_set_filename(struct libmnt_update *upd,
diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c
index 7db6d0314..1ee55222a 100644
--- a/libmount/src/optmap.c
+++ b/libmount/src/optmap.c
@@ -151,6 +151,8 @@ static const struct libmnt_optmap userspace_opts_map[] =
{ "x-", MNT_MS_XCOMMENT, MNT_NOMTAB | MNT_PREFIX }, /* x- options */
{ "loop[=]", MNT_MS_LOOP }, /* use the loop device */
+ { "offset=", MNT_MS_OFFSET, MNT_NOMTAB }, /* loop device offset */
+ { "sizelimit=", MNT_MS_SIZELIMIT, MNT_NOMTAB }, /* loop device size limit */
{ "nofail", MNT_MS_NOFAIL, MNT_NOMTAB }, /* Do not fail if ENOENT on dev */