diff options
author | Karel Zak | 2011-06-10 15:39:56 +0200 |
---|---|---|
committer | Karel Zak | 2011-06-10 15:39:56 +0200 |
commit | 7f8b2bf3be6db30280bd8c5a77584350170abd11 (patch) | |
tree | 9d9513a6a21b99e483e92c9a0dbdbe65a1a6ba2b /libmount | |
parent | lib: [loopdev.c] cleanup flags usage (diff) | |
download | kernel-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.am | 6 | ||||
-rw-r--r-- | libmount/src/cache.c | 7 | ||||
-rw-r--r-- | libmount/src/context.c | 36 | ||||
-rw-r--r-- | libmount/src/context_loopdev.c | 227 | ||||
-rw-r--r-- | libmount/src/libmount.h.in | 2 | ||||
-rw-r--r-- | libmount/src/mountP.h | 7 | ||||
-rw-r--r-- | libmount/src/optmap.c | 2 |
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 */ |