]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: add mount support for loopdevs
authorKarel Zak <kzak@redhat.com>
Fri, 10 Jun 2011 13:39:56 +0000 (15:39 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 10 Jun 2011 13:39:56 +0000 (15:39 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/Makefile.am
libmount/src/cache.c
libmount/src/context.c
libmount/src/context_loopdev.c [new file with mode: 0644]
libmount/src/libmount.h.in
libmount/src/mountP.h
libmount/src/optmap.c

index 56b0c207d072acefa6543be4daea67fa8db22ed5..5525ba279e8f8739102b695341764e48650b385d 100644 (file)
@@ -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
 
index 4775069c014a9d010737eed70bc62629d077c74c..907e048d07e428d7f6e2984dcdbc367ee8cd6177 100644 (file)
@@ -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;
 
index 6b0edc55b59662710a080758a845f744f70a6510..d65c8f1528386be208375f1e0787176b53b7d06b 100644 (file)
@@ -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 (file)
index 0000000..6ed0df1
--- /dev/null
@@ -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;
+}
+
index 15222083f67d1534ad2e48e32e09d73ef1a09d0c..18d0739ca90b2eb14cd948dd3d557863ab92428a 100644 (file)
@@ -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)
index 0a8c7522ca1328e6fccee15d1b70f14513ceb89c..8c63fbd4e6886f83f38012538fcb1bf8d8cce7d0 100644 (file)
@@ -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,
index 7db6d031429ab9c94839b2dbad633fc0c0c58f71..1ee55222a0683ded1e3cf27c2043e0ec087c8edf 100644 (file)
@@ -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 */