summaryrefslogtreecommitdiff
path: root/libmount/src
diff options
context:
space:
mode:
Diffstat (limited to 'libmount/src')
-rw-r--r--libmount/src/Makemodule.am137
-rw-r--r--libmount/src/cache.c697
-rw-r--r--libmount/src/context.c2297
-rw-r--r--libmount/src/context_loopdev.c368
-rw-r--r--libmount/src/context_mount.c935
-rw-r--r--libmount/src/context_umount.c856
-rw-r--r--libmount/src/fs.c1501
-rw-r--r--libmount/src/init.c45
-rw-r--r--libmount/src/iter.c78
-rw-r--r--libmount/src/libmount.h.in653
-rw-r--r--libmount/src/libmount.sym250
-rw-r--r--libmount/src/lock.c697
-rw-r--r--libmount/src/mountP.h402
-rw-r--r--libmount/src/optmap.c233
-rw-r--r--libmount/src/optstr.c1303
-rw-r--r--libmount/src/tab.c1195
-rw-r--r--libmount/src/tab_diff.c362
-rw-r--r--libmount/src/tab_parse.c944
-rw-r--r--libmount/src/tab_update.c862
-rw-r--r--libmount/src/test.c59
-rw-r--r--libmount/src/utils.c991
-rw-r--r--libmount/src/version.c127
22 files changed, 14992 insertions, 0 deletions
diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am
new file mode 100644
index 0000000..494e02a
--- /dev/null
+++ b/libmount/src/Makemodule.am
@@ -0,0 +1,137 @@
+
+# libmount.h is generated, so it's store in builddir!
+mountincdir = $(includedir)/libmount
+nodist_mountinc_HEADERS = $(top_builddir)/libmount/src/libmount.h
+
+usrlib_exec_LTLIBRARIES += libmount.la
+libmount_la_SOURCES = \
+ include/list.h \
+ \
+ libmount/src/cache.c \
+ libmount/src/context.c \
+ libmount/src/context_loopdev.c \
+ libmount/src/context_mount.c \
+ libmount/src/context_umount.c \
+ libmount/src/fs.c \
+ libmount/src/init.c \
+ libmount/src/iter.c \
+ libmount/src/lock.c \
+ libmount/src/mountP.h \
+ libmount/src/optmap.c \
+ libmount/src/optstr.c \
+ libmount/src/tab.c \
+ libmount/src/tab_diff.c \
+ libmount/src/tab_parse.c \
+ libmount/src/tab_update.c \
+ libmount/src/test.c \
+ libmount/src/utils.c \
+ libmount/src/version.c \
+ $(mountinc_HEADERS)
+
+nodist_libmount_la_SOURCES = libmount/src/mountP.h
+
+libmount_la_LIBADD = libcommon.la libblkid.la $(SELINUX_LIBS)
+
+libmount_la_CFLAGS = \
+ -I$(ul_libblkid_incdir) \
+ -I$(ul_libmount_incdir) \
+ -I$(top_srcdir)/libmount/src
+
+libmount_la_DEPENDENCIES = \
+ $(libmount_la_LIBADD) \
+ libmount/src/libmount.sym \
+ libmount/src/libmount.h.in
+
+libmount_la_LDFLAGS = \
+ -Wl,--version-script=$(top_srcdir)/libmount/src/libmount.sym \
+ -version-info $(LIBMOUNT_VERSION_INFO)
+
+EXTRA_DIST += \
+ libmount/src/libmount.sym \
+ libmount/src/libmount.h.in
+
+if BUILD_LIBMOUNT_TESTS
+check_PROGRAMS += \
+ test_mount_cache \
+ test_mount_context \
+ test_mount_lock \
+ test_mount_optstr \
+ test_mount_tab \
+ test_mount_tab_diff \
+ test_mount_tab_update \
+ test_mount_utils \
+ test_mount_version
+
+libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS)
+libmount_tests_ldflags = libblkid.la -static
+libmount_tests_ldadd = libmount.la $(UUID_LIBS)
+
+if HAVE_SELINUX
+libmount_tests_ldadd += $(SELINUX_LIBS)
+endif
+
+test_mount_cache_SOURCES = libmount/src/cache.c
+test_mount_cache_CFLAGS = $(libmount_tests_cflags)
+test_mount_cache_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_cache_LDADD = $(libmount_tests_ldadd)
+
+test_mount_context_SOURCES = libmount/src/context.c
+test_mount_context_CFLAGS = $(libmount_tests_cflags)
+test_mount_context_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_context_LDADD = $(libmount_tests_ldadd)
+
+test_mount_lock_SOURCES = libmount/src/lock.c
+test_mount_lock_CFLAGS = $(libmount_tests_cflags)
+test_mount_lock_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_lock_LDADD = $(libmount_tests_ldadd)
+
+test_mount_optstr_SOURCES = libmount/src/optstr.c
+test_mount_optstr_CFLAGS = $(libmount_tests_cflags)
+test_mount_optstr_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_optstr_LDADD = $(libmount_tests_ldadd)
+
+test_mount_tab_SOURCES = libmount/src/tab.c
+test_mount_tab_CFLAGS = $(libmount_tests_cflags)
+test_mount_tab_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_tab_LDADD = $(libmount_tests_ldadd)
+
+test_mount_tab_diff_SOURCES = libmount/src/tab_diff.c
+test_mount_tab_diff_CFLAGS = $(libmount_tests_cflags)
+test_mount_tab_diff_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_tab_diff_LDADD = $(libmount_tests_ldadd)
+
+test_mount_tab_update_SOURCES = libmount/src/tab_update.c
+test_mount_tab_update_CFLAGS = $(libmount_tests_cflags)
+test_mount_tab_update_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_tab_update_LDADD = $(libmount_tests_ldadd)
+
+test_mount_utils_SOURCES = libmount/src/utils.c
+test_mount_utils_CFLAGS = $(libmount_tests_cflags)
+test_mount_utils_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_utils_LDADD = $(libmount_tests_ldadd)
+
+test_mount_version_SOURCES = libmount/src/version.c
+test_mount_version_CFLAGS = $(libmount_tests_cflags)
+test_mount_version_LDFLAGS = $(libmount_tests_ldflags)
+test_mount_version_LDADD = $(libmount_tests_ldadd)
+
+endif # BUILD_LIBMOUNT_TESTS
+
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libmount:
+ if test "$(usrlib_execdir)" != "$(libdir)"; then \
+ mkdir -p $(DESTDIR)$(libdir); \
+ mv $(DESTDIR)$(usrlib_execdir)/libmount.so.* $(DESTDIR)$(libdir); \
+ so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libmount.so); \
+ so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+ (cd $(DESTDIR)$(usrlib_execdir) && \
+ rm -f libmount.so && \
+ $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libmount.so); \
+ fi
+
+uninstall-hook-libmount:
+ rm -f $(DESTDIR)$(libdir)/libmount.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libmount
+UNINSTALL_HOOKS += uninstall-hook-libmount
diff --git a/libmount/src/cache.c b/libmount/src/cache.c
new file mode 100644
index 0000000..34419cb
--- /dev/null
+++ b/libmount/src/cache.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2009-2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: cache
+ * @title: Cache
+ * @short_description: paths and tags (UUID/LABEL) caching
+ *
+ * The cache is a very simple API for work with tags (LABEL, UUID, ...) and
+ * paths. The cache uses libblkid as a backend for TAGs resolution.
+ *
+ * All returned paths are always canonicalized.
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <blkid.h>
+
+#include "canonicalize.h"
+#include "mountP.h"
+#include "loopdev.h"
+
+/*
+ * Canonicalized (resolved) paths & tags cache
+ */
+#define MNT_CACHE_CHUNKSZ 128
+
+#define MNT_CACHE_ISTAG (1 << 1) /* entry is TAG */
+#define MNT_CACHE_ISPATH (1 << 2) /* entry is path */
+#define MNT_CACHE_TAGREAD (1 << 3) /* tag read by mnt_cache_read_tags() */
+
+/* path cache entry */
+struct mnt_cache_entry {
+ char *key; /* search key (e.g. uncanonicalized path) */
+ char *value; /* value (e.g. canonicalized path) */
+ int flag;
+};
+
+struct libmnt_cache {
+ struct mnt_cache_entry *ents;
+ size_t nents;
+ size_t nallocs;
+
+ /* blkid_evaluate_tag() works in two ways:
+ *
+ * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
+ * then the blkid_cache is NULL.
+ *
+ * 2/ all tags are read from blkid.tab and verified by /dev
+ * scanning, then the blkid_cache is not NULL and then it's
+ * better to reuse the blkid_cache.
+ */
+ blkid_cache bc;
+};
+
+/**
+ * mnt_new_cache:
+ *
+ * Returns: new struct libmnt_cache instance or NULL in case of ENOMEM error.
+ */
+struct libmnt_cache *mnt_new_cache(void)
+{
+ struct libmnt_cache *cache = calloc(1, sizeof(*cache));
+ if (!cache)
+ return NULL;
+ DBG(CACHE, mnt_debug_h(cache, "alloc"));
+ return cache;
+}
+
+/**
+ * mnt_free_cache:
+ * @cache: pointer to struct libmnt_cache instance
+ *
+ * Deallocates the cache.
+ */
+void mnt_free_cache(struct libmnt_cache *cache)
+{
+ size_t i;
+
+ if (!cache)
+ return;
+
+ DBG(CACHE, mnt_debug_h(cache, "free"));
+
+ for (i = 0; i < cache->nents; i++) {
+ struct mnt_cache_entry *e = &cache->ents[i];
+ if (e->value != e->key)
+ free(e->value);
+ free(e->key);
+ }
+ free(cache->ents);
+ if (cache->bc)
+ blkid_put_cache(cache->bc);
+ free(cache);
+}
+
+/* note that the @key could be tha same pointer as @value */
+static int cache_add_entry(struct libmnt_cache *cache, char *key,
+ char *value, int flag)
+{
+ struct mnt_cache_entry *e;
+
+ assert(cache);
+ assert(value);
+ assert(key);
+
+ if (cache->nents == cache->nallocs) {
+ size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
+
+ e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
+ if (!e)
+ return -ENOMEM;
+ cache->ents = e;
+ cache->nallocs = sz;
+ }
+
+ e = &cache->ents[cache->nents];
+ e->key = key;
+ e->value = value;
+ e->flag = flag;
+ cache->nents++;
+
+ DBG(CACHE, mnt_debug_h(cache, "add entry [%2zd] (%s): %s: %s",
+ cache->nents,
+ (flag & MNT_CACHE_ISPATH) ? "path" : "tag",
+ value, key));
+ return 0;
+}
+
+/* add tag to the cache, @devname has to be allocated string */
+static int cache_add_tag(struct libmnt_cache *cache, const char *tagname,
+ const char *tagval, char *devname, int flag)
+{
+ size_t tksz, vlsz;
+ char *key;
+ int rc;
+
+ assert(cache);
+ assert(devname);
+ assert(tagname);
+ assert(tagval);
+
+ /* add into cache -- cache format for TAGs is
+ * key = "TAG_NAME\0TAG_VALUE\0"
+ * value = "/dev/foo"
+ */
+ tksz = strlen(tagname);
+ vlsz = strlen(tagval);
+
+ key = malloc(tksz + vlsz + 2);
+ if (!key)
+ return -ENOMEM;
+
+ memcpy(key, tagname, tksz + 1); /* include '\0' */
+ memcpy(key + tksz + 1, tagval, vlsz + 1);
+
+ rc = cache_add_entry(cache, key, devname, flag | MNT_CACHE_ISTAG);
+ if (!rc)
+ return 0;
+
+ free(key);
+ return rc;
+}
+
+
+/*
+ * Returns cached canonicalized path or NULL.
+ */
+static const char *cache_find_path(struct libmnt_cache *cache, const char *path)
+{
+ size_t i;
+
+ assert(cache);
+ assert(path);
+
+ if (!cache || !path)
+ return NULL;
+
+ for (i = 0; i < cache->nents; i++) {
+ struct mnt_cache_entry *e = &cache->ents[i];
+ if (!(e->flag & MNT_CACHE_ISPATH))
+ continue;
+ if (strcmp(path, e->key) == 0)
+ return e->value;
+ }
+ return NULL;
+}
+
+/*
+ * Returns cached path or NULL.
+ */
+static const char *cache_find_tag(struct libmnt_cache *cache,
+ const char *token, const char *value)
+{
+ size_t i;
+ size_t tksz;
+
+ assert(cache);
+ assert(token);
+ assert(value);
+
+ if (!cache || !token || !value)
+ return NULL;
+
+ tksz = strlen(token);
+
+ for (i = 0; i < cache->nents; i++) {
+ struct mnt_cache_entry *e = &cache->ents[i];
+ if (!(e->flag & MNT_CACHE_ISTAG))
+ continue;
+ if (strcmp(token, e->key) == 0 &&
+ strcmp(value, e->key + tksz + 1) == 0)
+ return e->value;
+ }
+ return NULL;
+}
+
+static char *cache_find_tag_value(struct libmnt_cache *cache,
+ const char *devname, const char *token)
+{
+ size_t i;
+
+ assert(cache);
+ assert(devname);
+ assert(token);
+
+ for (i = 0; i < cache->nents; i++) {
+ struct mnt_cache_entry *e = &cache->ents[i];
+ if (!(e->flag & MNT_CACHE_ISTAG))
+ continue;
+ if (strcmp(e->value, devname) == 0 && /* dev name */
+ strcmp(token, e->key) == 0) /* tag name */
+ return e->key + strlen(token) + 1; /* tag value */
+ }
+
+ return NULL;
+}
+
+/**
+ * mnt_cache_read_tags
+ * @cache: pointer to struct libmnt_cache instance
+ * @devname: path device
+ *
+ * Reads @devname LABEL and UUID to the @cache.
+ *
+ * Returns: 0 if at least one tag was added, 1 if no tag was added or
+ * negative number in case of error.
+ */
+int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
+{
+ blkid_probe pr;
+ size_t i, ntags = 0;
+ const char *tags[] = { "LABEL", "UUID", "TYPE", "PARTUUID", "PARTLABEL" };
+ const char *blktags[] = { "LABEL", "UUID", "TYPE", "PART_ENTRY_UUID", "PART_ENTRY_NAME" };
+
+ assert(cache);
+ assert(devname);
+
+ if (!cache || !devname)
+ return -EINVAL;
+
+ DBG(CACHE, mnt_debug_h(cache, "tags for %s requested", devname));
+
+ /* check is device is already cached */
+ for (i = 0; i < cache->nents; i++) {
+ struct mnt_cache_entry *e = &cache->ents[i];
+ if (!(e->flag & MNT_CACHE_TAGREAD))
+ continue;
+ if (strcmp(e->value, devname) == 0)
+ /* tags has been already read */
+ return 0;
+ }
+
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr)
+ return -1;
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr,
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+ BLKID_SUBLKS_TYPE);
+
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+
+ if (blkid_do_safeprobe(pr))
+ goto error;
+
+ DBG(CACHE, mnt_debug_h(cache, "reading tags for: %s", devname));
+
+ for (i = 0; i < ARRAY_SIZE(tags); i++) {
+ const char *data;
+ char *dev;
+
+ if (cache_find_tag_value(cache, devname, tags[i])) {
+ DBG(CACHE, mnt_debug_h(cache,
+ "\ntag %s already cached", tags[i]));
+ continue;
+ }
+ if (blkid_probe_lookup_value(pr, blktags[i], &data, NULL))
+ continue;
+ dev = strdup(devname);
+ if (!dev)
+ goto error;
+ if (cache_add_tag(cache, tags[i], data, dev,
+ MNT_CACHE_TAGREAD)) {
+ free(dev);
+ goto error;
+ }
+ ntags++;
+ }
+
+ DBG(CACHE, mnt_debug_h(cache, "\tread %zd tags", ntags));
+ blkid_free_probe(pr);
+ return ntags ? 0 : 1;
+error:
+ blkid_free_probe(pr);
+ return -1;
+}
+
+/**
+ * mnt_cache_device_has_tag:
+ * @cache: paths cache
+ * @devname: path to the device
+ * @token: tag name (e.g "LABEL")
+ * @value: tag value
+ *
+ * Look up @cache to check it @tag+@value are associated with @devname.
+ *
+ * Returns: 1 on success or 0.
+ */
+int mnt_cache_device_has_tag(struct libmnt_cache *cache, const char *devname,
+ const char *token, const char *value)
+{
+ const char *path = cache_find_tag(cache, token, value);
+
+ if (path && strcmp(path, devname) == 0)
+ return 1;
+ return 0;
+}
+
+/**
+ * mnt_cache_find_tag_value:
+ * @cache: cache for results
+ * @devname: device name
+ * @token: tag name ("LABEL" or "UUID")
+ *
+ * Returns: LABEL or UUID for the @devname or NULL in case of error.
+ */
+char *mnt_cache_find_tag_value(struct libmnt_cache *cache,
+ const char *devname, const char *token)
+{
+ if (!cache || !devname || !token)
+ return NULL;
+
+ if (mnt_cache_read_tags(cache, devname) != 0)
+ return NULL;
+
+ return cache_find_tag_value(cache, devname, token);
+}
+
+/**
+ * mnt_get_fstype:
+ * @devname: device name
+ * @ambi: returns TRUE if probing result is ambivalent (optional argument)
+ * @cache: cache for results or NULL
+ *
+ * Returns: filesystem type or NULL in case of error. The result has to be
+ * deallocated by free() if @cache is NULL.
+ */
+char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache)
+{
+ blkid_probe pr;
+ const char *data;
+ char *type = NULL;
+ int rc;
+
+ DBG(CACHE, mnt_debug_h(cache, "get %s FS type", devname));
+
+ if (cache)
+ return mnt_cache_find_tag_value(cache, devname, "TYPE");
+
+ /*
+ * no cache, probe directly
+ */
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr)
+ return NULL;
+
+ blkid_probe_enable_superblocks(pr, 1);
+
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
+
+ rc = blkid_do_safeprobe(pr);
+
+ if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
+ type = strdup(data);
+
+ if (ambi)
+ *ambi = rc == -2 ? TRUE : FALSE;
+
+ blkid_free_probe(pr);
+ return type;
+}
+
+/**
+ * mnt_resolve_path:
+ * @path: "native" path
+ * @cache: cache for results or NULL
+ *
+ * Converts path:
+ * - to the absolute path
+ * - /dev/dm-N to /dev/mapper/name
+ *
+ * Returns: absolute path or NULL in case of error. The result has to be
+ * deallocated by free() if @cache is NULL.
+ */
+char *mnt_resolve_path(const char *path, struct libmnt_cache *cache)
+{
+ char *p = NULL;
+ char *key = NULL;
+ char *value = NULL;
+
+ /*DBG(CACHE, mnt_debug_h(cache, "resolving path %s", path));*/
+
+ if (!path)
+ return NULL;
+ if (cache)
+ p = (char *) cache_find_path(cache, path);
+
+ if (!p) {
+ p = canonicalize_path(path);
+
+ if (p && cache) {
+ value = p;
+ key = strcmp(path, p) == 0 ? value : strdup(path);
+
+ if (!key || !value)
+ goto error;
+
+ if (cache_add_entry(cache, key, value,
+ MNT_CACHE_ISPATH))
+ goto error;
+ }
+ }
+
+ return p;
+error:
+ if (value != key)
+ free(value);
+ free(key);
+ return NULL;
+}
+
+/**
+ * mnt_pretty_path:
+ * @path: any path
+ * @cache: NULL or pointer to the cache
+ *
+ * Converts path:
+ * - to the absolute path
+ * - /dev/dm-N to /dev/mapper/name
+ * - /dev/loopN to the loop backing filename
+ * - empty path (NULL) to 'none'
+ *
+ * Returns: new allocated string with path, result has to be always deallocated
+ * by free().
+ */
+char *mnt_pretty_path(const char *path, struct libmnt_cache *cache)
+{
+ char *pretty = mnt_resolve_path(path, cache);
+
+ if (!pretty)
+ return strdup("none");
+
+ /* users assume backing file name rather than /dev/loopN in
+ * output if the device has been initialized by mount(8).
+ */
+ if (strncmp(pretty, "/dev/loop", 9) == 0) {
+ struct loopdev_cxt lc;
+
+ if (loopcxt_init(&lc, 0) || loopcxt_set_device(&lc, pretty))
+ goto done;
+
+ if (loopcxt_is_autoclear(&lc)) {
+ char *tmp = loopcxt_get_backing_file(&lc);
+ if (tmp) {
+ if (!cache)
+ free(pretty); /* not cached, deallocate */
+ return tmp; /* return backing file */
+ }
+ }
+ loopcxt_deinit(&lc);
+
+ }
+
+done:
+ /* don't return pointer to the cache, allocate a new string */
+ return cache ? strdup(pretty) : pretty;
+}
+
+/**
+ * mnt_resolve_tag:
+ * @token: tag name
+ * @value: tag value
+ * @cache: for results or NULL
+ *
+ * Returns: device name or NULL in case of error. The result has to be
+ * deallocated by free() if @cache is NULL.
+ */
+char *mnt_resolve_tag(const char *token, const char *value,
+ struct libmnt_cache *cache)
+{
+ char *p = NULL;
+
+ assert(token);
+ assert(value);
+
+ /*DBG(CACHE, mnt_debug_h(cache, "resolving tag token=%s value=%s",
+ token, value));*/
+
+ if (!token || !value)
+ return NULL;
+
+ if (cache)
+ p = (char *) cache_find_tag(cache, token, value);
+
+ if (!p) {
+ /* returns newly allocated string */
+ p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL);
+
+ if (p && cache &&
+ cache_add_tag(cache, token, value, p, 0))
+ goto error;
+ }
+
+ return p;
+error:
+ free(p);
+ return NULL;
+}
+
+
+
+/**
+ * mnt_resolve_spec:
+ * @spec: path or tag
+ * @cache: paths cache
+ *
+ * Returns: canonicalized path or NULL. The result has to be
+ * deallocated by free() if @cache is NULL.
+ */
+char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
+{
+ char *cn = NULL;
+
+ if (!spec)
+ return NULL;
+
+ if (strchr(spec, '=')) {
+ char *tag, *val;
+
+ if (!blkid_parse_tag_string(spec, &tag, &val)) {
+ cn = mnt_resolve_tag(tag, val, cache);
+
+ free(tag);
+ free(val);
+ }
+ } else
+ cn = mnt_resolve_path(spec, cache);
+
+ return cn;
+}
+
+
+#ifdef TEST_PROGRAM
+
+int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char line[BUFSIZ];
+ struct libmnt_cache *cache;
+
+ cache = mnt_new_cache();
+ if (!cache)
+ return -ENOMEM;
+
+ while(fgets(line, sizeof(line), stdin)) {
+ size_t sz = strlen(line);
+ char *p;
+
+ if (sz > 0 && line[sz - 1] == '\n')
+ line[sz - 1] = '\0';
+
+ p = mnt_resolve_path(line, cache);
+ printf("%s : %s\n", line, p);
+ }
+ mnt_free_cache(cache);
+ return 0;
+}
+
+int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char line[BUFSIZ];
+ struct libmnt_cache *cache;
+
+ cache = mnt_new_cache();
+ if (!cache)
+ return -ENOMEM;
+
+ while(fgets(line, sizeof(line), stdin)) {
+ size_t sz = strlen(line);
+ char *p;
+
+ if (sz > 0 && line[sz - 1] == '\n')
+ line[sz - 1] = '\0';
+
+ p = mnt_resolve_spec(line, cache);
+ printf("%s : %s\n", line, p);
+ }
+ mnt_free_cache(cache);
+ return 0;
+}
+
+int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char line[BUFSIZ];
+ struct libmnt_cache *cache;
+ size_t i;
+
+ cache = mnt_new_cache();
+ if (!cache)
+ return -ENOMEM;
+
+ while(fgets(line, sizeof(line), stdin)) {
+ size_t sz = strlen(line);
+
+ if (sz > 0 && line[sz - 1] == '\n')
+ line[sz - 1] = '\0';
+
+ if (!strcmp(line, "quit"))
+ break;
+
+ if (*line == '/') {
+ if (mnt_cache_read_tags(cache, line) < 0)
+ fprintf(stderr, "%s: read tags failed\n", line);
+
+ } else if (strchr(line, '=')) {
+ char *tag, *val;
+ const char *cn = NULL;
+
+ if (!blkid_parse_tag_string(line, &tag, &val)) {
+ cn = cache_find_tag(cache, tag, val);
+
+ free(tag);
+ free(val);
+ }
+ if (cn)
+ printf("%s: %s\n", line, cn);
+ else
+ printf("%s: not cached\n", line);
+ }
+ }
+
+ for (i = 0; i < cache->nents; i++) {
+ struct mnt_cache_entry *e = &cache->ents[i];
+ if (!(e->flag & MNT_CACHE_ISTAG))
+ continue;
+
+ printf("%15s : %5s : %s\n", e->value, e->key,
+ e->key + strlen(e->key) + 1);
+ }
+
+ mnt_free_cache(cache);
+ return 0;
+
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test ts[] = {
+ { "--resolve-path", test_resolve_path, " resolve paths from stdin" },
+ { "--resolve-spec", test_resolve_spec, " evaluate specs from stdin" },
+ { "--read-tags", test_read_tags, " read devname or TAG from stdin (\"quit\" to exit)" },
+ { NULL }
+ };
+
+ return mnt_run_test(ts, argc, argv);
+}
+#endif
diff --git a/libmount/src/context.c b/libmount/src/context.c
new file mode 100644
index 0000000..d6f9050
--- /dev/null
+++ b/libmount/src/context.c
@@ -0,0 +1,2297 @@
+/*
+ * Copyright (C) 2010,2011,2012 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: context
+ * @title: Library high-level context
+ * @short_description: high-level API to mount/umount devices.
+ *
+ * <informalexample>
+ * <programlisting>
+ * struct libmnt_context *cxt = mnt_new_context();
+ *
+ * mnt_context_set_options(cxt, "aaa,bbb,ccc=CCC");
+ * mnt_context_set_mflags(cxt, MS_NOATIME|MS_NOEXEC);
+ * mnt_context_set_target(cxt, "/mnt/foo");
+ *
+ * if (!mnt_context_mount(cxt))
+ * printf("successfully mounted\n");
+ * mnt_free_context(cxt);
+ *
+ * </programlisting>
+ * </informalexample>
+ *
+ * This code is similar to:
+ *
+ * mount -o aaa,bbb,ccc=CCC,noatime,noexec /mnt/foo
+ *
+ */
+
+#include "mountP.h"
+
+#include <sys/wait.h>
+
+/**
+ * mnt_new_context:
+ *
+ * Returns: newly allocated mount context
+ */
+struct libmnt_context *mnt_new_context(void)
+{
+ struct libmnt_context *cxt;
+ uid_t ruid, euid;
+
+ cxt = calloc(1, sizeof(*cxt));
+ if (!cxt)
+ return NULL;
+
+ ruid = getuid();
+ euid = geteuid();
+
+ mnt_context_reset_status(cxt);
+
+ cxt->loopdev_fd = -1;
+
+ /* if we're really root and aren't running setuid */
+ cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
+
+ DBG(CXT, mnt_debug_h(cxt, "----> allocate %s",
+ cxt->restricted ? "[RESTRICTED]" : ""));
+
+ mnt_has_regular_mtab(&cxt->mtab_path, &cxt->mtab_writable);
+
+ if (!cxt->mtab_writable)
+ /* use /run/mount/utab if /etc/mtab is useless */
+ mnt_has_regular_utab(&cxt->utab_path, &cxt->utab_writable);
+
+ return cxt;
+}
+
+/**
+ * mnt_free_context:
+ * @cxt: mount context
+ *
+ * Deallocates context struct.
+ */
+void mnt_free_context(struct libmnt_context *cxt)
+{
+ if (!cxt)
+ return;
+
+ mnt_reset_context(cxt);
+
+ free(cxt->fstype_pattern);
+ free(cxt->optstr_pattern);
+
+ if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
+ mnt_free_table(cxt->fstab);
+ 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);
+
+ free(cxt->children);
+
+ DBG(CXT, mnt_debug_h(cxt, "<---- free"));
+ free(cxt);
+}
+
+/**
+ * mnt_reset_context:
+ * @cxt: mount context
+ *
+ * Resets all information in the context that are directly related to
+ * the latest mount (spec, source, target, mount options, ....)
+ *
+ * The match patters, cached fstab, cached canonicalized paths and tags and
+ * [e]uid are not reseted. You have to use
+ *
+ * mnt_context_set_fstab(cxt, NULL);
+ * mnt_context_set_cache(cxt, NULL);
+ * mnt_context_set_fstype_pattern(cxt, NULL);
+ * mnt_context_set_options_pattern(cxt, NULL);
+ *
+ *
+ * to reset these stuff.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_reset_context(struct libmnt_context *cxt)
+{
+ int fl;
+
+ if (!cxt)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "<---- reset [status=%d] ---->",
+ mnt_context_get_status(cxt)));
+
+ fl = cxt->flags;
+
+ if (!(cxt->flags & MNT_FL_EXTERN_FS))
+ mnt_free_fs(cxt->fs);
+
+ mnt_free_table(cxt->mtab);
+
+ free(cxt->helper);
+ free(cxt->orig_user);
+
+ cxt->fs = NULL;
+ cxt->mtab = NULL;
+ cxt->ambi = 0;
+ cxt->helper = NULL;
+ cxt->orig_user = NULL;
+ cxt->mountflags = 0;
+ cxt->user_mountflags = 0;
+ cxt->mountdata = NULL;
+ cxt->flags = MNT_FL_DEFAULT;
+
+ mnt_context_reset_status(cxt);
+
+ /* restore non-resetable flags */
+ cxt->flags |= (fl & MNT_FL_EXTERN_FSTAB);
+ cxt->flags |= (fl & MNT_FL_EXTERN_CACHE);
+ cxt->flags |= (fl & MNT_FL_NOMTAB);
+ cxt->flags |= (fl & MNT_FL_FAKE);
+ cxt->flags |= (fl & MNT_FL_SLOPPY);
+ cxt->flags |= (fl & MNT_FL_VERBOSE);
+ cxt->flags |= (fl & MNT_FL_NOHELPERS);
+ cxt->flags |= (fl & MNT_FL_LOOPDEL);
+ cxt->flags |= (fl & MNT_FL_LAZY);
+ cxt->flags |= (fl & MNT_FL_FORK);
+ cxt->flags |= (fl & MNT_FL_FORCE);
+ cxt->flags |= (fl & MNT_FL_NOCANONICALIZE);
+ cxt->flags |= (fl & MNT_FL_RDONLY_UMOUNT);
+ return 0;
+}
+
+/**
+ * mnt_context_reset_status:
+ * @cxt: context
+ *
+ * Resets mount(2) and mount.type statuses, so mnt_context_do_mount() or
+ * mnt_context_do_umount() could be again called with the same settings.
+ *
+ * BE CAREFUL -- after this soft reset the libmount will NOT parse mount
+ * options, evaluate permissions or apply stuff from fstab.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_reset_status(struct libmnt_context *cxt)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->syscall_status = 1; /* means not called yet */
+ cxt->helper_exec_status = 1;
+ cxt->helper_status = 0;
+ return 0;
+}
+
+static int set_flag(struct libmnt_context *cxt, int flag, int enable)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (enable) {
+ DBG(CXT, mnt_debug_h(cxt, "enabling flag %04x", flag));
+ cxt->flags |= flag;
+ } else {
+ DBG(CXT, mnt_debug_h(cxt, "disabling flag %04x", flag));
+ cxt->flags &= ~flag;
+ }
+ return 0;
+}
+
+/**
+ * mnt_context_is_restricted:
+ * @cxt: mount context
+ *
+ * Returns: 0 for unrestricted mount (user is root), or 1 for non-root mounts
+ */
+int mnt_context_is_restricted(struct libmnt_context *cxt)
+{
+ assert(cxt);
+ return cxt->restricted;
+}
+
+/**
+ * mnt_context_set_optsmode
+ * @cxt: mount context
+ * @mode: MNT_OMASK_* flags
+ *
+ * Controls how to use mount optionsmsource and target paths from fstab/mtab.
+ *
+ * @MNT_OMODE_IGNORE: ignore mtab/fstab options
+ *
+ * @MNT_OMODE_APPEND: append mtab/fstab options to existing options
+ *
+ * @MNT_OMODE_PREPEND: prepend mtab/fstab options to existing options
+ *
+ * @MNT_OMODE_REPLACE: replace existing options with options from mtab/fstab
+ *
+ * @MNT_OMODE_FORCE: always read mtab/fstab (although source and target is defined)
+ *
+ * @MNT_OMODE_FSTAB: read from fstab
+ *
+ * @MNT_OMODE_MTAB: read from mtab if fstab not enabled or failed
+ *
+ * @MNT_OMODE_NOTAB: do not read fstab/mtab at all
+ *
+ * @MNT_OMODE_AUTO: default mode (MNT_OMODE_PREPEND | MNT_OMODE_FSTAB | MNT_OMODE_MTAB)
+ *
+ * @MNT_OMODE_USER: default for non-root users (MNT_OMODE_REPLACE | MNT_OMODE_FORCE | MNT_OMODE_FSTAB)
+ *
+ * Notes:
+ *
+ * - MNT_OMODE_USER is always used if mount context is in restricted mode
+ * - MNT_OMODE_AUTO is used if nothing other is defined
+ * - the flags are eavaluated in this order: MNT_OMODE_NOTAB, MNT_OMODE_FORCE,
+ * MNT_OMODE_FSTAB, MNT_OMODE_MTAB and then the mount options from fstab/mtab
+ * are set according to MNT_OMODE_{IGNORE,APPEND,PREPAND,REPLACE}
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode)
+{
+ if (!cxt)
+ return -EINVAL;
+ cxt->optsmode = mode;
+ return 0;
+}
+
+/**
+ * mnt_context_get_optsmode
+ * @cxt: mount context
+ *
+ * Returns: MNT_OMASK_* mask or zero.
+ */
+
+int mnt_context_get_optsmode(struct libmnt_context *cxt)
+{
+ return cxt ? cxt->optsmode : 0;
+}
+
+/**
+ * mnt_context_disable_canonicalize:
+ * @cxt: mount context
+ * @disable: TRUE or FALSE
+ *
+ * Enable/disable paths canonicalization and tags evaluation. The libmount context
+ * canonicalies paths when search in fstab and when prepare source and target paths
+ * for mount(2) syscall.
+ *
+ * This fuction has effect to the private (within context) fstab instance only
+ * (see mnt_context_set_fstab()). If you want to use an external fstab then you
+ * need manage your private struct libmnt_cache (see mnt_table_set_cache(fstab,
+ * NULL).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable)
+{
+ return set_flag(cxt, MNT_FL_NOCANONICALIZE, disable);
+}
+
+/**
+ * mnt_context_is_nocanonicalize:
+ * @cxt: mount context
+ *
+ * Returns: 1 if no-canonicalize mode enabled or 0.
+ */
+int mnt_context_is_nocanonicalize(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_NOCANONICALIZE) ? 1 : 0;
+}
+
+/**
+ * mnt_context_enable_lazy:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable lazy umount (see umount(8) man page, option -l).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_LAZY, enable);
+}
+
+/**
+ * mnt_context_is_lazy:
+ * @cxt: mount context
+ *
+ * Returns: 1 if lazy umount is enabled or 0
+ */
+int mnt_context_is_lazy(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_LAZY) ? 1 : 0;
+}
+
+/**
+ * mnt_context_enable_fork:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable fork(2) call in mnt_context_next_mount() (see mount(8) man
+ * page, option -F).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_fork(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_FORK, enable);
+}
+
+/**
+ * mnt_context_is_fork:
+ * @cxt: mount context
+ *
+ * Returns: 1 if fork (mount -F) is enabled or 0
+ */
+int mnt_context_is_fork(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_FORK) ? 1 : 0;
+}
+
+/**
+ * mnt_context_is_parent:
+ * @cxt: mount context
+ *
+ * Return: 1 if mount -F enabled and the current context is parent, or 0
+ */
+int mnt_context_is_parent(struct libmnt_context *cxt)
+{
+ return mnt_context_is_fork(cxt) && cxt->pid == 0;
+}
+
+/**
+ * mnt_context_is_child:
+ * @cxt: mount context
+ *
+ * Return: 1 if mount -F enabled and the current context is child, or 0
+ */
+int mnt_context_is_child(struct libmnt_context *cxt)
+{
+ return !mnt_context_is_fork(cxt) && cxt->pid;
+}
+
+/**
+ * mnt_context_enable_rdonly_umount:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable read-only remount on failed umount(2)
+ * (see umount(8) man page, option -r).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_RDONLY_UMOUNT, enable);
+}
+
+/**
+ * mnt_context_is_rdonly_umount
+ * @cxt: mount context
+ *
+ * See also mnt_context_enable_rdonly_umount() and see umount(8) man page,
+ * option -r.
+ *
+ * Returns: 1 if read-only remount failed umount(2) is enables or 0
+ */
+int mnt_context_is_rdonly_umount(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_RDONLY_UMOUNT) ? 1 : 0;
+}
+
+/**
+ * mnt_context_disable_helpers:
+ * @cxt: mount context
+ * @disable: TRUE or FALSE
+ *
+ * Enable/disable /sbin/[u]mount.* helpers (see mount(8) man page, option -i).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_disable_helpers(struct libmnt_context *cxt, int disable)
+{
+ return set_flag(cxt, MNT_FL_NOHELPERS, disable);
+}
+
+/**
+ * mnt_context_is_nohelpers
+ * @cxt: mount context
+ *
+ * Returns: 1 if helpers are disabled (mount -i) or 0
+ */
+int mnt_context_is_nohelpers(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_NOHELPERS) ? 1 : 0;
+}
+
+
+/**
+ * mnt_context_enable_sloppy:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Set/unset sloppy mounting (see mount(8) man page, option -s).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_SLOPPY, enable);
+}
+
+/**
+ * mnt_context_is_sloppy:
+ * @cxt: mount context
+ *
+ * Returns: 1 if sloppy flag is enabled or 0
+ */
+int mnt_context_is_sloppy(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_SLOPPY) ? 1 : 0;
+}
+
+/**
+ * mnt_context_enable_fake:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable fake mounting (see mount(8) man page, option -f).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_fake(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_FAKE, enable);
+}
+
+/**
+ * mnt_context_is_fake:
+ * @cxt: mount context
+ *
+ * Returns: 1 if fake flag is enabled or 0
+ */
+int mnt_context_is_fake(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_FAKE) ? 1 : 0;
+}
+
+/**
+ * mnt_context_disable_mtab:
+ * @cxt: mount context
+ * @disable: TRUE or FALSE
+ *
+ * Disable/enable mtab update (see mount(8) man page, option -n).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable)
+{
+ return set_flag(cxt, MNT_FL_NOMTAB, disable);
+}
+
+/**
+ * mnt_context_is_nomtab:
+ * @cxt: mount context
+ *
+ * Returns: 1 if no-mtab is enabled or 0
+ */
+int mnt_context_is_nomtab(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_NOMTAB) ? 1 : 0;
+}
+
+/**
+ * mnt_context_disable_swapmatch:
+ * @cxt: mount context
+ * @disable: TRUE or FALSE
+ *
+ * Disable/enable swap between source and target for mount(8) if only one path
+ * is specified.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_disable_swapmatch(struct libmnt_context *cxt, int disable)
+{
+ return set_flag(cxt, MNT_FL_NOSWAPMATCH, disable);
+}
+
+/**
+ * mnt_context_is_swapmatch:
+ * @cxt: mount context
+ *
+ * Returns: 1 if swap between source and target is allowed (default is 1) or 0.
+ */
+int mnt_context_is_swapmatch(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_NOSWAPMATCH) ? 0 : 1;
+}
+
+/**
+ * mnt_context_enable_force:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable force umounting (see umount(8) man page, option -f).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_force(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_FORCE, enable);
+}
+
+/**
+ * mnt_context_is_force
+ * @cxt: mount context
+ *
+ * Returns: 1 if force umounting flag is enabled or 0
+ */
+int mnt_context_is_force(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_FORCE) ? 1 : 0;
+}
+
+/**
+ * mnt_context_enable_verbose:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable verbose output (TODO: not implemented yet)
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_VERBOSE, enable);
+}
+
+/**
+ * mnt_context_is_verbose
+ * @cxt: mount context
+ *
+ * Returns: 1 if verbose flag is enabled or 0
+ */
+int mnt_context_is_verbose(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_VERBOSE) ? 1 : 0;
+}
+
+/**
+ * mnt_context_enable_loopdel:
+ * @cxt: mount context
+ * @enable: TRUE or FALSE
+ *
+ * Enable/disable loop delete (destroy) after umount (see umount(8), option -d)
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable)
+{
+ return set_flag(cxt, MNT_FL_LOOPDEL, enable);
+}
+
+/**
+ * mnt_context_is_loopdel:
+ * @cxt: mount context
+ *
+ * Returns: 1 if loop device should be deleted after umount (umount -d) or 0.
+ */
+int mnt_context_is_loopdel(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_LOOPDEL) ? 1 : 0;
+}
+
+/**
+ * mnt_context_set_fs:
+ * @cxt: mount context
+ * @fs: filesystem description
+ *
+ * The mount context uses private @fs by default. This function allows to
+ * overwrite the private @fs with an external instance. Note that the external
+ * @fs instance is not deallocated by mnt_free_context() or mnt_reset_context().
+ *
+ * The @fs will be modified by mnt_context_set_{source,target,options,fstype}
+ * functions, If the @fs is NULL then all current FS specific setting (source,
+ * target, etc., exclude spec) is reseted.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (!(cxt->flags & MNT_FL_EXTERN_FS))
+ mnt_free_fs(cxt->fs);
+
+ set_flag(cxt, MNT_FL_EXTERN_FS, fs != NULL);
+ cxt->fs = fs;
+ return 0;
+}
+
+/**
+ * mnt_context_get_fs:
+ * @cxt: mount context
+ *
+ * The FS contains the basic description of mountpoint, fs type and so on.
+ * Note that the FS is modified by mnt_context_set_{source,target,options,fstype}
+ * functions.
+ *
+ * Returns: pointer to FS description or NULL in case of calloc() errrr.
+ */
+struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt)
+{
+ if (!cxt)
+ return NULL;
+ if (!cxt->fs) {
+ cxt->fs = mnt_new_fs();
+ cxt->flags &= ~MNT_FL_EXTERN_FS;
+ }
+ return cxt->fs;
+}
+
+/**
+ * mnt_context_set_source:
+ * @cxt: mount context
+ * @source: mount source (device, directory, UUID, LABEL, ...)
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_source(struct libmnt_context *cxt, const char *source)
+{
+ return mnt_fs_set_source(mnt_context_get_fs(cxt), source);
+}
+
+/**
+ * mnt_context_get_source:
+ * @cxt: mount context
+ *
+ * Returns: returns pointer or NULL in case of error pr if not set.
+ */
+const char *mnt_context_get_source(struct libmnt_context *cxt)
+{
+ return mnt_fs_get_source(mnt_context_get_fs(cxt));
+}
+
+/**
+ * mnt_context_set_target:
+ * @cxt: mount context
+ * @target: mountpoint
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_target(struct libmnt_context *cxt, const char *target)
+{
+ return mnt_fs_set_target(mnt_context_get_fs(cxt), target);
+}
+
+/**
+ * mnt_context_get_target:
+ * @cxt: mount context
+ *
+ * Returns: returns pointer or NULL in case of error pr if not set.
+ */
+const char *mnt_context_get_target(struct libmnt_context *cxt)
+{
+ return mnt_fs_get_target(mnt_context_get_fs(cxt));
+}
+
+/**
+ * mnt_context_set_fstype:
+ * @cxt: mount context
+ * @fstype: filesystem type
+ *
+ * Note that the @fstype has to be the real FS type. For comma-separated list of
+ * filesystems or for "nofs" notation use mnt_context_set_fstype_pattern().
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype)
+{
+ if (fstype && strchr(fstype, ','))
+ return -EINVAL;
+ return mnt_fs_set_fstype(mnt_context_get_fs(cxt), fstype);
+}
+
+/**
+ * mnt_context_get_fstype:
+ * @cxt: mount context
+ *
+ * Returns: pointer or NULL in case of error pr if not set.
+ */
+const char *mnt_context_get_fstype(struct libmnt_context *cxt)
+{
+ return mnt_fs_get_fstype(mnt_context_get_fs(cxt));
+}
+
+/**
+ * mnt_context_set_options:
+ * @cxt: mount context
+ * @optstr: comma delimited mount options
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_options(struct libmnt_context *cxt, const char *optstr)
+{
+ return mnt_fs_set_options(mnt_context_get_fs(cxt), optstr);
+}
+
+/**
+ * mnt_context_append_options:
+ * @cxt: mount context
+ * @optstr: comma delimited mount options
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_append_options(struct libmnt_context *cxt, const char *optstr)
+{
+ return mnt_fs_append_options(mnt_context_get_fs(cxt), optstr);
+}
+
+/**
+ * mnt_context_get_options:
+ * @cxt: mount context
+ *
+ * This function returns mount options set by mnt_context_set_options() or
+ * mnt_context_append_options().
+ *
+ * Note that *after* mnt_context_prepare_mount() may the mount options string
+ * also includes options set by mnt_context_set_mflags() or another options
+ * generated by this library.
+ *
+ * Returns: pointer or NULL
+ */
+const char *mnt_context_get_options(struct libmnt_context *cxt)
+{
+ return mnt_fs_get_options(mnt_context_get_fs(cxt));
+}
+
+/**
+ * mnt_context_set_fstype_pattern:
+ * @cxt: mount context
+ * @pattern: FS name pattern (or NULL to reset the current setting)
+ *
+ * See mount(8), option -t.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_fstype_pattern(struct libmnt_context *cxt, const char *pattern)
+{
+ char *p = NULL;
+
+ if (!cxt)
+ return -EINVAL;
+ if (pattern) {
+ p = strdup(pattern);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(cxt->fstype_pattern);
+ cxt->fstype_pattern = p;
+ return 0;
+}
+
+/**
+ * mnt_context_set_options_pattern:
+ * @cxt: mount context
+ * @pattern: options pattern (or NULL to reset the current setting)
+ *
+ * See mount(8), option -O.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_options_pattern(struct libmnt_context *cxt, const char *pattern)
+{
+ char *p = NULL;
+
+ if (!cxt)
+ return -EINVAL;
+ if (pattern) {
+ p = strdup(pattern);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(cxt->optstr_pattern);
+ cxt->optstr_pattern = p;
+ return 0;
+}
+
+/**
+ * mnt_context_set_fstab:
+ * @cxt: mount context
+ * @tb: fstab
+ *
+ * The mount context reads /etc/fstab to the the private struct libmnt_table by default.
+ * This function allows to overwrite the private fstab with an external
+ * instance. Note that the external instance is not deallocated by mnt_free_context().
+ *
+ * The fstab is used read-only and is not modified, it should be possible to
+ * share the fstab between more mount contexts (TODO: tests it.)
+ *
+ * If the @tb argument is NULL then the current private fstab instance is
+ * reseted.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
+ mnt_free_table(cxt->fstab);
+
+ set_flag(cxt, MNT_FL_EXTERN_FSTAB, tb != NULL);
+ cxt->fstab = tb;
+ return 0;
+}
+
+/**
+ * mnt_context_get_fstab:
+ * @cxt: mount context
+ * @tb: returns fstab
+ *
+ * See also mnt_table_parse_fstab() for more details about fstab.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb)
+{
+ struct libmnt_cache *cache;
+
+ if (!cxt)
+ return -EINVAL;
+
+ if (!cxt->fstab) {
+ int rc;
+
+ cxt->fstab = mnt_new_table();
+ if (!cxt->fstab)
+ return -ENOMEM;
+ if (cxt->table_errcb)
+ mnt_table_set_parser_errcb(cxt->fstab, cxt->table_errcb);
+ cxt->flags &= ~MNT_FL_EXTERN_FSTAB;
+ rc = mnt_table_parse_fstab(cxt->fstab, NULL);
+ if (rc)
+ return rc;
+ }
+
+ cache = mnt_context_get_cache(cxt);
+
+ /* never touch an external fstab */
+ if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
+ mnt_table_set_cache(cxt->fstab, cache);
+
+ if (tb)
+ *tb = cxt->fstab;
+ return 0;
+}
+
+/**
+ * mnt_context_get_mtab:
+ * @cxt: mount context
+ * @tb: returns mtab
+ *
+ * See also mnt_table_parse_mtab() for more details about mtab/mountinfo. The
+ * result will deallocated by mnt_free_context(@cxt).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb)
+{
+ struct libmnt_cache *cache;
+
+ if (!cxt)
+ return -EINVAL;
+
+ if (!cxt->mtab) {
+ int rc;
+
+ cxt->mtab = mnt_new_table();
+ if (!cxt->mtab)
+ return -ENOMEM;
+
+ if (cxt->table_errcb)
+ mnt_table_set_parser_errcb(cxt->mtab, cxt->table_errcb);
+
+ rc = mnt_table_parse_mtab(cxt->mtab, cxt->mtab_path);
+ if (rc)
+ return rc;
+ }
+
+ cache = mnt_context_get_cache(cxt);
+ mnt_table_set_cache(cxt->mtab, cache);
+
+ if (tb)
+ *tb = cxt->mtab;
+ return 0;
+}
+
+/**
+ * mnt_context_get_table:
+ * @cxt: mount context
+ * @filename: e.g. /proc/self/mountinfo
+ * @tb: returns the table
+ *
+ * This function allocates a new table and parses the @file. The parser error
+ * callback and cache for tags and paths is set according to the @cxt setting.
+ * See also mnt_table_parse_file().
+ *
+ * It's strongly recommended use mnt_context_get_mtab() and
+ * mnt_context_get_fstab() functions for mtab and fstab files. This function
+ * does not care about LIBMOUNT_* env.variables and does not merge userspace
+ * options.
+ *
+ * The result will NOT be deallocated by mnt_free_context(@cxt).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_get_table(struct libmnt_context *cxt,
+ const char *filename, struct libmnt_table **tb)
+{
+ struct libmnt_cache *cache;
+ int rc;
+
+ if (!cxt || !tb)
+ return -EINVAL;
+
+ *tb = mnt_new_table();
+ if (!*tb)
+ return -ENOMEM;
+
+ if (cxt->table_errcb)
+ mnt_table_set_parser_errcb(*tb, cxt->table_errcb);
+
+ rc = mnt_table_parse_file(*tb, filename);
+ if (rc) {
+ mnt_free_table(*tb);
+ return rc;
+ }
+
+ cache = mnt_context_get_cache(cxt);
+ if (cache)
+ mnt_table_set_cache(*tb, cache);
+
+ return 0;
+}
+
+/**
+ * mnt_context_set_tables_errcb
+ * @cxt: mount context
+ * @cb: pointer to callback function
+ *
+ * The error callback is used for all tab files (e.g. mtab, fstab)
+ * parsed within the context.
+ *
+ * See also mnt_context_get_mtab(),
+ * mnt_context_get_fstab(),
+ * mnt_table_set_parser_errcb().
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_tables_errcb(struct libmnt_context *cxt,
+ int (*cb)(struct libmnt_table *tb, const char *filename, int line))
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->table_errcb = cb;
+ return 0;
+}
+
+/**
+ * mnt_context_set_cache:
+ * @cxt: mount context
+ * @cache: cache instance or nULL
+ *
+ * The mount context maintains a private struct libmnt_cache by default. This function
+ * allows to overwrite the private cache with an external instance. Note that
+ * the external instance is not deallocated by mnt_free_context().
+ *
+ * If the @cache argument is NULL then the current private cache instance is
+ * reseted.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_cache(struct libmnt_context *cxt, struct libmnt_cache *cache)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (!(cxt->flags & MNT_FL_EXTERN_CACHE))
+ mnt_free_cache(cxt->cache);
+
+ set_flag(cxt, MNT_FL_EXTERN_CACHE, cache != NULL);
+ cxt->cache = cache;
+ return 0;
+}
+
+/**
+ * mnt_context_get_cache
+ * @cxt: mount context
+ *
+ * See also mnt_context_set_cache().
+ *
+ * Returns: pointer to cache or NULL if canonicalization is disabled.
+ */
+struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt)
+{
+ if (!cxt || mnt_context_is_nocanonicalize(cxt))
+ return NULL;
+
+ if (!cxt->cache) {
+ cxt->cache = mnt_new_cache();
+ if (!cxt->cache)
+ return NULL;
+ cxt->flags &= ~MNT_FL_EXTERN_CACHE;
+ }
+ return cxt->cache;
+}
+
+/**
+ * mnt_context_set_passwd_cb:
+ * @cxt: mount context
+ * @get: callback to get password
+ * @release: callback to release (delallocate) password
+ *
+ * Sets callbacks for encryption password (e.g encrypted loopdev)
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_passwd_cb(struct libmnt_context *cxt,
+ char *(*get)(struct libmnt_context *),
+ void (*release)(struct libmnt_context *, char *))
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->pwd_get_cb = get;
+ cxt->pwd_release_cb = release;
+ return 0;
+}
+
+/**
+ * mnt_context_get_lock:
+ * @cxt: mount context
+ *
+ * The libmount applications don't have to care about mtab locking, but with a
+ * small exception: the application has to be able to remove the lock file when
+ * interrupted by signal or signals have to be ignored when the lock is locked.
+ *
+ * The default behavior is to ignore all signals (except SIGALRM and
+ * SIGTRAP for mtab udate) when the lock is locked. If this behavior
+ * is unacceptable then use:
+ *
+ * lc = mnt_context_get_lock(cxt);
+ * if (lc)
+ * mnt_lock_block_signals(lc, FALSE);
+ *
+ * and don't forget to call mnt_unlock_file(lc) before exit.
+ *
+ * Returns: pointer to lock struct or NULL.
+ */
+struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt)
+{
+ /*
+ * DON'T call this function within libmount, it will always allocate
+ * the lock. The mnt_update_* functions are able to allocate the lock
+ * only when mtab/utab update is really necessary.
+ */
+ if (!cxt || mnt_context_is_nomtab(cxt))
+ return NULL;
+
+ if (!cxt->lock) {
+ cxt->lock = mnt_new_lock(cxt->mtab_writable ?
+ cxt->mtab_path : cxt->utab_path, 0);
+ if (cxt->lock)
+ mnt_lock_block_signals(cxt->lock, TRUE);
+ }
+ return cxt->lock;
+}
+
+/**
+ * mnt_context_set_mflags:
+ * @cxt: mount context
+ * @flags: mount(2) flags (MS_* flags)
+ *
+ * Sets mount flags (see mount(2) man page).
+ *
+ * Note that mount context allows to define mount options by mount flags. It
+ * means you can for example use
+ *
+ * mnt_context_set_mflags(cxt, MS_NOEXEC | MS_NOSUID);
+ *
+ * rather than
+ *
+ * mnt_context_set_options(cxt, "noexec,nosuid");
+ *
+ * these both calls have the same effect.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->mountflags = flags;
+
+ if ((cxt->flags & MNT_FL_MOUNTOPTS_FIXED) && cxt->fs)
+ /*
+ * the final mount options are already generated, refresh...
+ */
+ return mnt_optstr_apply_flags(
+ &cxt->fs->vfs_optstr,
+ cxt->mountflags,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP));
+
+ return 0;
+}
+
+/**
+ * mnt_context_get_mflags:
+ * @cxt: mount context
+ * @flags: returns MS_* mount flags
+ *
+ * Converts mount options string to MS_* flags and bitewise-OR the result with
+ * already defined flags (see mnt_context_set_mflags()).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags)
+{
+ int rc = 0;
+ if (!cxt || !flags)
+ return -EINVAL;
+
+ *flags = 0;
+ if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) {
+ const char *o = mnt_fs_get_options(cxt->fs);
+ if (o)
+ rc = mnt_optstr_get_flags(o, flags,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP));
+ }
+ if (!rc)
+ *flags |= cxt->mountflags;
+ return rc;
+}
+
+/**
+ * mnt_context_set_user_mflags:
+ * @cxt: mount context
+ * @flags: mount(2) flags (MNT_MS_* flags, e.g. MNT_MS_LOOP)
+ *
+ * Sets userspace mount flags.
+ *
+ * See also notest for mnt_context_set_mflags().
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_user_mflags(struct libmnt_context *cxt, unsigned long flags)
+{
+ if (!cxt)
+ return -EINVAL;
+ cxt->user_mountflags = flags;
+ return 0;
+}
+
+/**
+ * mnt_context_get_user_mflags:
+ * @cxt: mount context
+ * @flags: returns mount flags
+ *
+ * Converts mount options string to MNT_MS_* flags and bitewise-OR the result
+ * with already defined flags (see mnt_context_set_user_mflags()).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags)
+{
+ int rc = 0;
+ if (!cxt || !flags)
+ return -EINVAL;
+
+ *flags = 0;
+ if (!(cxt->flags & MNT_FL_MOUNTFLAGS_MERGED) && cxt->fs) {
+ const char *o = mnt_fs_get_user_options(cxt->fs);
+ if (o)
+ rc = mnt_optstr_get_flags(o, flags,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
+ }
+ if (!rc)
+ *flags |= cxt->user_mountflags;
+ return rc;
+}
+
+/**
+ * mnt_context_set_mountdata:
+ * @cxt: mount context
+ * @data: mount(2) data
+ *
+ * The mount context generates mountdata from mount options by default. This
+ * function allows to overwrite this behavior, and @data will be used instead
+ * of mount options.
+ *
+ * The libmount does not deallocated the data by mnt_free_context(). Note that
+ * NULL is also valid mount data.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_mountdata(struct libmnt_context *cxt, void *data)
+{
+ if (!cxt)
+ return -EINVAL;
+ cxt->mountdata = data;
+ cxt->flags |= MNT_FL_MOUNTDATA;
+ return 0;
+}
+
+/*
+ * Translates LABEL/UUID/path to mountable path
+ */
+int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
+{
+ const char *path = NULL;
+ struct libmnt_cache *cache;
+ const char *t, *v, *src;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt || !cxt->fs)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "preparing source path"));
+
+ src = mnt_fs_get_source(cxt->fs);
+
+ if (!src && (cxt->mountflags & MS_PROPAGATION))
+ /* mount --make-{shared,private,...} */
+ return mnt_fs_set_source(cxt->fs, "none");
+
+ /* ignore filesystems without source or filesystems
+ * where the source is quasi-path (//foo/bar)
+ */
+ if (!src || mnt_fs_is_netfs(cxt->fs))
+ return 0;
+
+ DBG(CXT, mnt_debug_h(cxt, "srcpath '%s'", src));
+
+ cache = mnt_context_get_cache(cxt);
+
+ if (!mnt_fs_get_tag(cxt->fs, &t, &v)) {
+ /*
+ * Source is TAG (evaluate)
+ */
+ if (cache)
+ path = mnt_resolve_tag(t, v, cache);
+
+ rc = path ? mnt_fs_set_source(cxt->fs, path) : -MNT_ERR_NOSOURCE;
+
+ } else if (cache && !mnt_fs_is_pseudofs(cxt->fs)) {
+ /*
+ * Source is PATH (canonicalize)
+ */
+ path = mnt_resolve_path(src, cache);
+ if (path && strcmp(path, src))
+ rc = mnt_fs_set_source(cxt->fs, path);
+ }
+
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "failed to prepare srcpath [rc=%d]", rc));
+ return rc;
+ }
+
+ if (!path)
+ path = src;
+
+ if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) ||
+ mnt_fs_is_pseudofs(cxt->fs)) {
+ DBG(CXT, mnt_debug_h(cxt, "BIND/MOVE/pseudo FS source: %s", path));
+ return rc;
+ }
+
+ /*
+ * Initialize loop device
+ */
+ 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'",
+ mnt_fs_get_source(cxt->fs)));
+ return 0;
+}
+
+int mnt_context_prepare_target(struct libmnt_context *cxt)
+{
+ const char *tgt;
+ struct libmnt_cache *cache;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt || !cxt->fs)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "preparing target path"));
+
+ tgt = mnt_fs_get_target(cxt->fs);
+ if (!tgt)
+ return 0;
+
+ cache = mnt_context_get_cache(cxt);
+ if (cache) {
+ char *path = mnt_resolve_path(tgt, cache);
+ if (path && strcmp(path, tgt) != 0)
+ rc = mnt_fs_set_target(cxt->fs, path);
+ }
+
+ if (rc)
+ DBG(CXT, mnt_debug_h(cxt, "failed to prepare target '%s'", tgt));
+ else
+ DBG(CXT, mnt_debug_h(cxt, "final target '%s'",
+ mnt_fs_get_target(cxt->fs)));
+ return 0;
+}
+
+/*
+ * It's usully no error when we're not able to detect filesystem type -- we
+ * will try to use types from /{etc,proc}/filesystems.
+ */
+int mnt_context_guess_fstype(struct libmnt_context *cxt)
+{
+ char *type;
+ const char *dev;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt || !cxt->fs)
+ return -EINVAL;
+
+ if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION))
+ goto none;
+
+ type = (char *) mnt_fs_get_fstype(cxt->fs);
+ if (type && !strcmp(type, "auto")) {
+ mnt_fs_set_fstype(cxt->fs, NULL);
+ type = NULL;
+ }
+
+ if (type)
+ goto done;
+ if (cxt->flags & MS_REMOUNT)
+ goto none;
+ if (cxt->fstype_pattern)
+ goto done;
+
+ dev = mnt_fs_get_srcpath(cxt->fs);
+ if (!dev)
+ goto done;
+
+ if (access(dev, F_OK) == 0) {
+ struct libmnt_cache *cache = mnt_context_get_cache(cxt);
+
+ type = mnt_get_fstype(dev, &cxt->ambi, cache);
+ if (type) {
+ rc = mnt_fs_set_fstype(cxt->fs, type);
+ if (!cache)
+ free(type); /* type is not cached */
+ }
+ } else {
+ if (strchr(dev, ':') != NULL)
+ rc = mnt_fs_set_fstype(cxt->fs, "nfs");
+ else if (!strncmp(dev, "//", 2))
+ rc = mnt_fs_set_fstype(cxt->fs, "cifs");
+ }
+
+done:
+ DBG(CXT, mnt_debug_h(cxt, "FS type: %s", mnt_fs_get_fstype(cxt->fs)));
+ return rc;
+none:
+ return mnt_fs_set_fstype(cxt->fs, "none");
+}
+
+/*
+ * The default is to use fstype from cxt->fs, this could be overwritten by
+ * @type. The @act is MNT_ACT_{MOUNT,UMOUNT}.
+ *
+ * Returns: 0 on success or negative number in case of error. Note that success
+ * does not mean that there is any usable helper, you have to check cxt->helper.
+ */
+int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name,
+ const char *type)
+{
+ char search_path[] = FS_SEARCH_PATH; /* from config.h */
+ char *p = NULL, *path;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!type)
+ type = mnt_fs_get_fstype(cxt->fs);
+
+ if (mnt_context_is_nohelpers(cxt)
+ || !type
+ || !strcmp(type, "none")
+ || mnt_fs_is_swaparea(cxt->fs))
+ return 0;
+
+ path = strtok_r(search_path, ":", &p);
+ while (path) {
+ char helper[PATH_MAX];
+ struct stat st;
+ int rc;
+
+ rc = snprintf(helper, sizeof(helper), "%s/%s.%s",
+ path, name, type);
+ path = strtok_r(NULL, ":", &p);
+
+ if (rc < 0 || (size_t) rc >= sizeof(helper))
+ continue;
+
+ rc = stat(helper, &st);
+ if (rc == -1 && errno == ENOENT && strchr(type, '.')) {
+ /* If type ends with ".subtype" try without it */
+ char *hs = strrchr(helper, '.');
+ if (hs)
+ *hs = '\0';
+ rc = stat(helper, &st);
+ }
+
+ DBG(CXT, mnt_debug_h(cxt, "%-25s ... %s", helper,
+ rc ? "not found" : "found"));
+ if (rc)
+ continue;
+
+ free(cxt->helper);
+ cxt->helper = strdup(helper);
+ if (!cxt->helper)
+ return -ENOMEM;
+ return 0;
+ }
+
+ return 0;
+}
+
+int mnt_context_merge_mflags(struct libmnt_context *cxt)
+{
+ unsigned long fl = 0;
+ int rc;
+
+ assert(cxt);
+
+ DBG(CXT, mnt_debug_h(cxt, "merging mount flags"));
+
+ rc = mnt_context_get_mflags(cxt, &fl);
+ if (rc)
+ return rc;
+ cxt->mountflags = fl;
+
+ fl = 0;
+ rc = mnt_context_get_user_mflags(cxt, &fl);
+ if (rc)
+ return rc;
+ cxt->user_mountflags = fl;
+
+ DBG(CXT, mnt_debug_h(cxt, "final flags: VFS=%08lx user=%08lx",
+ cxt->mountflags, cxt->user_mountflags));
+
+ cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED;
+ return 0;
+}
+
+/*
+ * Prepare /etc/mtab or /run/mount/utab
+ */
+int mnt_context_prepare_update(struct libmnt_context *cxt)
+{
+ int rc;
+ const char *target;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->action);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ DBG(CXT, mnt_debug_h(cxt, "prepare update"));
+
+ if (cxt->mountflags & MS_PROPAGATION) {
+ DBG(CXT, mnt_debug_h(cxt, "skip update: MS_PROPAGATION"));
+ return 0;
+ }
+
+ target = mnt_fs_get_target(cxt->fs);
+
+ if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/"))
+ /* Don't try to touch mtab if umounting root FS */
+ mnt_context_disable_mtab(cxt, TRUE);
+
+ if (mnt_context_is_nomtab(cxt)) {
+ DBG(CXT, mnt_debug_h(cxt, "skip update: NOMTAB flag"));
+ return 0;
+ }
+ if (cxt->helper) {
+ DBG(CXT, mnt_debug_h(cxt, "skip update: external helper"));
+ return 0;
+ }
+ if (!cxt->mtab_writable && !cxt->utab_writable) {
+ DBG(CXT, mnt_debug_h(cxt, "skip update: no writable destination"));
+ return 0;
+ }
+ /* 0 = success, 1 = not called yet */
+ if (cxt->syscall_status != 1 && cxt->syscall_status != 0) {
+ DBG(CXT, mnt_debug_h(cxt,
+ "skip update: syscall failed [status=%d]",
+ cxt->syscall_status));
+ return 0;
+ }
+ if (!cxt->update) {
+ cxt->update = mnt_new_update();
+ if (!cxt->update)
+ return -ENOMEM;
+
+ mnt_update_set_filename(cxt->update,
+ cxt->mtab_writable ? cxt->mtab_path : cxt->utab_path,
+ !cxt->mtab_writable);
+ }
+
+ if (cxt->action == MNT_ACT_UMOUNT)
+ rc = mnt_update_set_fs(cxt->update, cxt->mountflags,
+ mnt_context_get_target(cxt), NULL);
+ else
+ rc = mnt_update_set_fs(cxt->update, cxt->mountflags,
+ NULL, cxt->fs);
+
+ return rc < 0 ? rc : 0;
+}
+
+int mnt_context_update_tabs(struct libmnt_context *cxt)
+{
+ unsigned long fl;
+
+ assert(cxt);
+
+ if (mnt_context_is_nomtab(cxt)) {
+ DBG(CXT, mnt_debug_h(cxt, "don't update: NOMTAB flag"));
+ return 0;
+ }
+ if (cxt->helper) {
+ DBG(CXT, mnt_debug_h(cxt, "don't update: external helper"));
+ return 0;
+ }
+ if (!cxt->update || !mnt_update_is_ready(cxt->update)) {
+ DBG(CXT, mnt_debug_h(cxt, "don't update: no update prepared"));
+ return 0;
+ }
+ if (cxt->syscall_status) {
+ DBG(CXT, mnt_debug_h(cxt, "don't update: syscall failed/not called"));
+ return 0;
+ }
+
+ fl = mnt_update_get_mflags(cxt->update);
+ if ((cxt->mountflags & MS_RDONLY) != (fl & MS_RDONLY))
+ /*
+ * fix MS_RDONLY in options
+ */
+ mnt_update_force_rdonly(cxt->update,
+ cxt->mountflags & MS_RDONLY);
+
+ return mnt_update_table(cxt->update, cxt->lock);
+}
+
+static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb,
+ int direction)
+{
+ struct libmnt_fs *fs = NULL;
+ const char *src = NULL, *tgt = NULL;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+
+ if (!cxt->fs)
+ return -EINVAL;
+
+ src = mnt_fs_get_source(cxt->fs);
+ tgt = mnt_fs_get_target(cxt->fs);
+
+ if (tgt && src)
+ fs = mnt_table_find_pair(tb, src, tgt, direction);
+ else {
+ if (src)
+ fs = mnt_table_find_source(tb, src, direction);
+ else if (tgt)
+ fs = mnt_table_find_target(tb, tgt, direction);
+
+ if (!fs && mnt_context_is_swapmatch(cxt)) {
+ /* swap source and target (if @src is not LABEL/UUID),
+ * for example in
+ *
+ * mount /foo/bar
+ *
+ * the path could be a mountpoint as well as source (for
+ * example bind mount, symlink to device, ...).
+ */
+ if (src && !mnt_fs_get_tag(cxt->fs, NULL, NULL))
+ fs = mnt_table_find_target(tb, src, direction);
+ if (!fs && tgt)
+ fs = mnt_table_find_source(tb, tgt, direction);
+ }
+ }
+
+ if (!fs)
+ return -MNT_ERR_NOFSTAB; /* not found */
+
+ DBG(CXT, mnt_debug_h(cxt, "apply entry:"));
+ DBG(CXT, mnt_fs_print_debug(fs, stderr));
+
+ /* copy from tab to our FS description
+ */
+ rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs));
+ if (!rc)
+ rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs));
+
+ if (!rc && !mnt_fs_get_fstype(cxt->fs))
+ rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs));
+
+ if (rc)
+ return rc;
+
+ if (cxt->optsmode & MNT_OMODE_IGNORE)
+ ;
+ else if (cxt->optsmode & MNT_OMODE_REPLACE)
+ rc = mnt_fs_set_options(cxt->fs, mnt_fs_get_options(fs));
+
+ else if (cxt->optsmode & MNT_OMODE_APPEND)
+ rc = mnt_fs_append_options(cxt->fs, mnt_fs_get_options(fs));
+
+ else if (cxt->optsmode & MNT_OMODE_PREPEND)
+ rc = mnt_fs_prepend_options(cxt->fs, mnt_fs_get_options(fs));
+
+ if (!rc)
+ cxt->flags |= MNT_FL_TAB_APPLIED;
+ return rc;
+}
+
+/**
+ * mnt_context_apply_fstab:
+ * @cxt: mount context
+ *
+ * This function is optional.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_apply_fstab(struct libmnt_context *cxt)
+{
+ int rc = -1;
+ struct libmnt_table *tab = NULL;
+ const char *src = NULL, *tgt = NULL;
+
+ assert(cxt);
+ assert(cxt->fs);
+
+ if (!cxt)
+ return -EINVAL;
+
+ if (mnt_context_tab_applied(cxt)) /* already applied */
+ return 0;
+
+ if (mnt_context_is_restricted(cxt)) {
+ DBG(CXT, mnt_debug_h(cxt, "force fstab usage for non-root users!"));
+ cxt->optsmode = MNT_OMODE_USER;
+ } else if (cxt->optsmode == 0) {
+ DBG(CXT, mnt_debug_h(cxt, "use default optmode"));
+ cxt->optsmode = MNT_OMODE_AUTO;
+ } else if (cxt->optsmode & MNT_OMODE_NOTAB) {
+ cxt->optsmode &= ~MNT_OMODE_FSTAB;
+ cxt->optsmode &= ~MNT_OMODE_MTAB;
+ cxt->optsmode &= ~MNT_OMODE_FORCE;
+ }
+
+ if (cxt->fs) {
+ src = mnt_fs_get_source(cxt->fs);
+ tgt = mnt_fs_get_target(cxt->fs);
+ }
+
+ DBG(CXT, mnt_debug_h(cxt, "OPTSMODE: ignore=%d, append=%d, prepend=%d, "
+ "replace=%d, force=%d, fstab=%d, mtab=%d",
+ cxt->optsmode & MNT_OMODE_IGNORE ? 1 : 0,
+ cxt->optsmode & MNT_OMODE_APPEND ? 1 : 0,
+ cxt->optsmode & MNT_OMODE_PREPEND ? 1 : 0,
+ cxt->optsmode & MNT_OMODE_REPLACE ? 1 : 0,
+ cxt->optsmode & MNT_OMODE_FORCE ? 1 : 0,
+ cxt->optsmode & MNT_OMODE_FSTAB ? 1 : 0,
+ cxt->optsmode & MNT_OMODE_MTAB ? 1 : 0));
+
+ /* fstab is not required if source and target are specified */
+ if (src && tgt && !(cxt->optsmode & MNT_OMODE_FORCE)) {
+ DBG(CXT, mnt_debug_h(cxt, "fstab not required -- skip"));
+ return 0;
+ }
+
+ if (!src && tgt
+ && !(cxt->optsmode & MNT_OMODE_FSTAB)
+ && !(cxt->optsmode & MNT_OMODE_MTAB)) {
+ DBG(CXT, mnt_debug_h(cxt, "only target; fstab/mtab not required "
+ "-- skip, probably MS_PROPAGATION"));
+ return 0;
+ }
+
+ DBG(CXT, mnt_debug_h(cxt,
+ "trying to apply fstab (src=%s, target=%s)", src, tgt));
+
+ /* let's initialize cxt->fs */
+ mnt_context_get_fs(cxt);
+
+ /* try fstab */
+ if (cxt->optsmode & MNT_OMODE_FSTAB) {
+ rc = mnt_context_get_fstab(cxt, &tab);
+ if (!rc)
+ rc = apply_table(cxt, tab, MNT_ITER_FORWARD);
+ }
+
+ /* try mtab */
+ if (rc < 0 && (cxt->optsmode & MNT_OMODE_MTAB)) {
+ DBG(CXT, mnt_debug_h(cxt, "trying to apply from mtab"));
+ rc = mnt_context_get_mtab(cxt, &tab);
+ if (!rc)
+ rc = apply_table(cxt, tab, MNT_ITER_BACKWARD);
+ }
+ if (rc)
+ DBG(CXT, mnt_debug_h(cxt, "failed to find entry in fstab/mtab"));
+ return rc;
+}
+
+/**
+ * mnt_context_tab_applied:
+ * @cxt: mount context
+ *
+ * Returns: 1 if fstab (or mtab) has been applied to the context, or 0.
+ */
+int mnt_context_tab_applied(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_TAB_APPLIED);
+}
+
+/**
+ * mnt_context_get_status:
+ * @cxt: mount context
+ *
+ * Global libmount status.
+ *
+ * The real exit code of the mount.type helper has to be tested by
+ * mnt_context_get_helper_status(). The mnt_context_get_status() only inform
+ * that exec() has been successful.
+ *
+ * Returns: 1 if mount.type or mount(2) syscall has been successfully called.
+ */
+int mnt_context_get_status(struct libmnt_context *cxt)
+{
+ return cxt && (!cxt->syscall_status || !cxt->helper_exec_status);
+}
+
+/**
+ * mnt_context_helper_executed:
+ * @cxt: mount context
+ *
+ * Returns: 1 if mount.type helper has been executed, or 0.
+ */
+int mnt_context_helper_executed(struct libmnt_context *cxt)
+{
+ return cxt->helper_exec_status != 1;
+}
+
+/**
+ * mnt_context_get_helper_status:
+ * @cxt: mount context
+ *
+ * Return: mount.type helper exit status, result is reliable only if
+ * mnt_context_helper_executed() returns 1.
+ */
+int mnt_context_get_helper_status(struct libmnt_context *cxt)
+{
+ return cxt->helper_status;
+}
+
+/**
+ * mnt_context_syscall_called:
+ * @cxt: mount context
+ *
+ * Returns: 1 if mount(2) syscall has been called, or 0.
+ */
+int mnt_context_syscall_called(struct libmnt_context *cxt)
+{
+ return cxt->syscall_status != 1;
+}
+
+/**
+ * mnt_context_get_syscall_errno:
+ * @cxt: mount context
+ *
+ * The result from this function is reliable only if
+ * mnt_context_syscall_called() returns 1.
+ *
+ * Returns: mount(2) errno if the syscall failed or 0.
+ */
+int mnt_context_get_syscall_errno(struct libmnt_context *cxt)
+{
+ if (cxt->syscall_status < 0)
+ return -cxt->syscall_status;
+
+ return 0;
+}
+
+/**
+ * mnt_context_set_syscall_status:
+ * @cxt: mount context
+ * @status: mount(2) status
+ *
+ * The @status should be 0 on success, or negative number on error (-errno).
+ *
+ * This function should be used only if [u]mount(2) syscall is NOT called by
+ * libmount code.
+ *
+ * Returns: 0 or negative number in case of error.
+ */
+int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "syscall status set to: %d", status));
+ cxt->syscall_status = status;
+ return 0;
+}
+
+/**
+ * mnt_context_strerror
+ * @cxt: context
+ * @buf: buffer
+ * @bufsiz: size of the buffer
+ *
+ * Not implemented yet.
+ *
+ * Returns: 0 or negative number in case of error.
+ */
+int mnt_context_strerror(struct libmnt_context *cxt __attribute__((__unused__)),
+ char *buf __attribute__((__unused__)),
+ size_t bufsiz __attribute__((__unused__)))
+{
+ /* TODO: based on cxt->syscall_errno or cxt->helper_status */
+ return 0;
+}
+
+/**
+ * mnt_context_init_helper
+ * @cxt: mount context
+ * @action: MNT_ACT_{UMOUNT,MOUNT}
+ * @flags: not used now
+ *
+ * This function infors libmount that used from [u]mount.type helper.
+ *
+ * The function also calls mnt_context_disable_helpers() to avoid recursive
+ * mount.type helpers calling. It you really want to call another
+ * mount.type helper from your helper than you have to explicitly enable this
+ * feature by:
+ *
+ * mnt_context_disable_helpers(cxt, FALSE);
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_init_helper(struct libmnt_context *cxt, int action,
+ int flags __attribute__((__unused__)))
+{
+ int rc = mnt_context_disable_helpers(cxt, TRUE);
+
+ if (!rc)
+ rc = set_flag(cxt, MNT_FL_HELPER, 1);
+ if (!rc)
+ cxt->action = action;
+
+ DBG(CXT, mnt_debug_h(cxt, "initialized for [u]mount.<type> helper [rc=%d]", rc));
+ return rc;
+}
+
+/**
+ * mnt_context_helper_setopt:
+ * @cxt: context
+ * @c: getopt() result
+ * @arg: getopt() optarg
+ *
+ * This function applies [u]mount.type command line option (for example parsed
+ * by getopt or getopt_long) to @cxt. All unknown options are ignored and
+ * then 1 is returned.
+ *
+ * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
+ */
+int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg)
+{
+ if (cxt) {
+ switch(cxt->action) {
+ case MNT_ACT_MOUNT:
+ return mnt_context_mount_setopt(cxt, c, arg);
+ case MNT_ACT_UMOUNT:
+ return mnt_context_umount_setopt(cxt, c, arg);
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * mnt_context_is_fs_mounted:
+ * @cxt: context
+ * @fs: filesystem
+ * @mounted: returns 1 for mounted and 0 for non-mounted filesystems
+ *
+ * Please, read mnt_table_is_fs_mounted() description!
+ *
+ * Returns: 0 on success and negative number in case of error.
+ */
+int mnt_context_is_fs_mounted(struct libmnt_context *cxt,
+ struct libmnt_fs *fs, int *mounted)
+{
+ struct libmnt_table *mtab;
+ int rc;
+
+ if (!cxt || !fs || !mounted)
+ return -EINVAL;
+
+ rc = mnt_context_get_mtab(cxt, &mtab);
+ if (rc)
+ return rc;
+
+ *mounted = mnt_table_is_fs_mounted(mtab, fs);
+ return 0;
+}
+
+static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid)
+{
+ pid_t *pids;
+
+ if (!cxt)
+ return -EINVAL;
+
+ pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1);
+ if (!pids)
+ return -ENOMEM;
+
+ DBG(CXT, mnt_debug_h(cxt, "add new child %d", pid));
+ cxt->children = pids;
+ cxt->children[cxt->nchildren++] = pid;
+
+ return 0;
+}
+
+int mnt_fork_context(struct libmnt_context *cxt)
+{
+ int rc = 0;
+ pid_t pid;
+
+ if (!mnt_context_is_parent(cxt))
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "forking context"));
+
+ DBG_FLUSH;
+
+ pid = fork();
+
+ switch (pid) {
+ case -1: /* error */
+ DBG(CXT, mnt_debug_h(cxt, "fork failed %m"));
+ return -errno;
+
+ case 0: /* child */
+ cxt->pid = getpid();
+ mnt_context_enable_fork(cxt, FALSE);
+ DBG(CXT, mnt_debug_h(cxt, "child created"));
+ break;
+
+ default:
+ rc = mnt_context_add_child(cxt, pid);
+ break;
+ }
+
+ return rc;
+}
+
+int mnt_context_wait_for_children(struct libmnt_context *cxt,
+ int *nchildren, int *nerrs)
+{
+ int i;
+
+ if (!cxt)
+ return -EINVAL;
+
+ assert(mnt_context_is_parent(cxt));
+
+ for (i = 0; i < cxt->nchildren; i++) {
+ pid_t pid = cxt->children[i];
+ int rc = 0, ret = 0;
+
+ if (!pid)
+ continue;
+ do {
+ DBG(CXT, mnt_debug_h(cxt,
+ "waiting for child (%d/%d): %d",
+ i + 1, cxt->nchildren, pid));
+ errno = 0;
+ rc = waitpid(pid, &ret, 0);
+
+ } while (rc == -1 && errno == EINTR);
+
+ if (nchildren)
+ (*nchildren)++;
+
+ if (rc != -1 && nerrs) {
+ if (WIFEXITED(ret))
+ (*nerrs) += WEXITSTATUS(ret) == 0 ? 0 : 1;
+ else
+ (*nerrs)++;
+ }
+ cxt->children[i] = 0;
+ }
+
+ cxt->nchildren = 0;
+ free(cxt->children);
+ cxt->children = NULL;
+ return 0;
+}
+
+
+
+#ifdef TEST_PROGRAM
+
+struct libmnt_lock *lock;
+
+static void lock_fallback(void)
+{
+ if (lock)
+ mnt_unlock_file(lock);
+}
+
+int test_mount(struct libmnt_test *ts, int argc, char *argv[])
+{
+ int idx = 1, rc = 0;
+ struct libmnt_context *cxt;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ cxt = mnt_new_context();
+ if (!cxt)
+ return -ENOMEM;
+
+ if (!strcmp(argv[idx], "-o")) {
+ mnt_context_set_options(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+ if (!strcmp(argv[idx], "-t")) {
+ /* TODO: use mnt_context_set_fstype_pattern() */
+ mnt_context_set_fstype(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+
+ if (argc == idx + 1)
+ /* mount <mountpont>|<device> */
+ mnt_context_set_target(cxt, argv[idx++]);
+
+ else if (argc == idx + 2) {
+ /* mount <device> <mountpoint> */
+ mnt_context_set_source(cxt, argv[idx++]);
+ mnt_context_set_target(cxt, argv[idx++]);
+ }
+
+ /* this is unnecessary! -- libmount is able to internaly
+ * create and manage the lock
+ */
+ lock = mnt_context_get_lock(cxt);
+ if (lock)
+ atexit(lock_fallback);
+
+ rc = mnt_context_mount(cxt);
+ if (rc)
+ printf("failed to mount: %m\n");
+ else
+ printf("successfully mounted\n");
+
+ lock = NULL; /* because we use atexit lock_fallback */
+ mnt_free_context(cxt);
+ return rc;
+}
+
+int test_umount(struct libmnt_test *ts, int argc, char *argv[])
+{
+ int idx = 1, rc = 0;
+ struct libmnt_context *cxt;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ cxt = mnt_new_context();
+ if (!cxt)
+ return -ENOMEM;
+
+ if (!strcmp(argv[idx], "-t")) {
+ mnt_context_set_fstype(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+
+ if (!strcmp(argv[idx], "-f")) {
+ mnt_context_enable_force(cxt, TRUE);
+ idx++;
+ }
+
+ if (!strcmp(argv[idx], "-l")) {
+ mnt_context_enable_lazy(cxt, TRUE);
+ idx++;
+ }
+
+ if (!strcmp(argv[idx], "-r")) {
+ mnt_context_enable_rdonly_umount(cxt, TRUE);
+ idx++;
+ }
+
+ if (argc == idx + 1) {
+ /* mount <mountpont>|<device> */
+ mnt_context_set_target(cxt, argv[idx++]);
+ } else {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ lock = mnt_context_get_lock(cxt);
+ if (lock)
+ atexit(lock_fallback);
+
+ rc = mnt_context_umount(cxt);
+ if (rc)
+ printf("failed to umount\n");
+ else
+ printf("successfully umounted\n");
+err:
+ lock = NULL; /* because we use atexit lock_fallback */
+ mnt_free_context(cxt);
+ return rc;
+}
+
+int test_flags(struct libmnt_test *ts, int argc, char *argv[])
+{
+ int idx = 1, rc = 0;
+ struct libmnt_context *cxt;
+ const char *opt = NULL;
+ unsigned long flags = 0;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ cxt = mnt_new_context();
+ if (!cxt)
+ return -ENOMEM;
+
+ if (!strcmp(argv[idx], "-o")) {
+ mnt_context_set_options(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+
+ if (argc == idx + 1)
+ /* mount <mountpont>|<device> */
+ mnt_context_set_target(cxt, argv[idx++]);
+
+ rc = mnt_context_prepare_mount(cxt);
+ if (rc)
+ printf("failed to prepare mount %s\n", strerror(-rc));
+
+ opt = mnt_fs_get_options(cxt->fs);
+ if (opt)
+ fprintf(stdout, "options: %s\n", opt);
+
+ mnt_context_get_mflags(cxt, &flags);
+ fprintf(stdout, "flags: %08lx\n", flags);
+
+ mnt_free_context(cxt);
+ return rc;
+}
+
+int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_context *cxt;
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+ int mntrc, ignored, idx = 1;
+
+ cxt = mnt_new_context();
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+
+ if (!cxt || !itr)
+ return -ENOMEM;
+
+ if (argc > 2) {
+ if (!strcmp(argv[idx], "-O")) {
+ mnt_context_set_options_pattern(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+ if (!strcmp(argv[idx], "-t")) {
+ mnt_context_set_fstype_pattern(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+ }
+
+ while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
+
+ const char *tgt = mnt_fs_get_target(fs);
+
+ if (ignored == 1)
+ printf("%s: ignored: not match\n", tgt);
+ else if (ignored == 2)
+ printf("%s: ignored: already mounted\n", tgt);
+
+ else if (!mnt_context_get_status(cxt)) {
+ if (mntrc > 0) {
+ errno = mntrc;
+ warn("%s: mount failed", tgt);
+ } else
+ warnx("%s: mount failed", tgt);
+ } else
+ printf("%s: successfully mounted\n", tgt);
+ }
+
+ mnt_free_context(cxt);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--mount", test_mount, "[-o <opts>] [-t <type>] <spec>|<src> <target>" },
+ { "--umount", test_umount, "[-t <type>] [-f][-l][-r] <src>|<target>" },
+ { "--mount-all", test_mountall, "[-O <pattern>] [-t <pattern] mount all filesystems from fstab" },
+ { "--flags", test_flags, "[-o <opts>] <spec>" },
+ { NULL }};
+
+ umask(S_IWGRP|S_IWOTH); /* to be compatible with mount(8) */
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c
new file mode 100644
index 0000000..da246e3
--- /dev/null
+++ b/libmount/src/context_loopdev.c
@@ -0,0 +1,368 @@
+/*
+ * 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;
+
+ 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 |
+ MNT_MS_ENCRYPTION)) {
+
+ DBG(CXT, mnt_debug_h(cxt, "loopdev specific options detected"));
+ 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). The file size
+ * should be at least 1KiB otherwise we will create empty loopdev where
+ * is no mountable filesystem...
+ *
+ * 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);
+
+ if (mnt_fs_is_regular(cxt->fs) &&
+ (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) {
+ struct stat st;
+
+ if (stat(src, &st) == 0 && S_ISREG(st.st_mode) &&
+ st.st_size > 1024)
+ return 1;
+ }
+
+ 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 %s",
+ 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;
+ char *val = NULL, *enc = NULL, *pwd = NULL;
+ size_t len;
+ struct loopdev_cxt lc;
+ int rc = 0, lo_flags = 0;
+ uint64_t offset = 0, sizelimit = 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;
+ }
+
+ rc = loopcxt_init(&lc, 0);
+ if (rc)
+ return rc;
+
+ ON_DBG(CXT, loopcxt_enable_debug(&lc, 1));
+
+ optstr = mnt_fs_get_user_options(cxt->fs);
+
+ /*
+ * loop=
+ */
+ if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) &&
+ mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) {
+
+ val = strndup(val, len);
+ rc = val ? loopcxt_set_device(&lc, val) : -ENOMEM;
+ free(val);
+
+ if (rc == 0)
+ loopdev = loopcxt_get_device(&lc);
+ }
+
+ /*
+ * offset=
+ */
+ if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) &&
+ mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) {
+ rc = mnt_parse_offset(val, len, &offset);
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "failed to parse offset="));
+ rc = -MNT_ERR_MOUNTOPT;
+ }
+ }
+
+ /*
+ * sizelimit=
+ */
+ if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) &&
+ mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) {
+ rc = mnt_parse_offset(val, len, &sizelimit);
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "failed to parse sizelimit="));
+ rc = -MNT_ERR_MOUNTOPT;
+ }
+ }
+
+ /*
+ * encryption=
+ */
+ if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) &&
+ mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) {
+ enc = strndup(val, len);
+ if (val && !enc)
+ rc = -ENOMEM;
+ if (enc && cxt->pwd_get_cb) {
+ DBG(CXT, mnt_debug_h(cxt, "asking for pass"));
+ pwd = cxt->pwd_get_cb(cxt);
+ }
+ }
+
+ if (rc == 0 && is_mounted_same_loopfile(cxt,
+ mnt_context_get_target(cxt),
+ backing_file, offset))
+ rc = -EBUSY;
+
+ if (rc)
+ goto done;
+
+ /* 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
+ * -- note that loopcxt_find_unused() resets "lc"
+ */
+ rc = loopcxt_set_backing_file(&lc, backing_file);
+
+ if (!rc && offset)
+ rc = loopcxt_set_offset(&lc, offset);
+ if (!rc && sizelimit)
+ rc = loopcxt_set_sizelimit(&lc, sizelimit);
+ if (!rc && enc && pwd)
+ loopcxt_set_encryption(&lc, enc, pwd);
+ if (!rc)
+ loopcxt_set_flags(&lc, lo_flags);
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "failed to set loopdev attributes"));
+ goto done;
+ }
+
+ /* 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"));
+ rc = -MNT_ERR_LOOPDEV;
+ goto done;
+ }
+ 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;
+ mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop");
+ }
+
+ if (!(cxt->mountflags & MS_RDONLY) &&
+ loopcxt_is_readonly(&lc))
+ /*
+ * mount planned read-write, but loopdev is read-only,
+ * let's fix mount options...
+ */
+ mnt_context_set_mflags(cxt, 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:
+ free(enc);
+ if (pwd && cxt->pwd_release_cb) {
+ DBG(CXT, mnt_debug_h(cxt, "release pass"));
+ cxt->pwd_release_cb(cxt, pwd);
+ }
+ 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/context_mount.c b/libmount/src/context_mount.c
new file mode 100644
index 0000000..f3d8ff1
--- /dev/null
+++ b/libmount/src/context_mount.c
@@ -0,0 +1,935 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: context-mount
+ * @title: Mount context
+ * @short_description: high-level API to mount operation.
+ */
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "linux_version.h"
+#include "mountP.h"
+
+/*
+ * this has to be called after mnt_context_evaluate_permissions()
+ */
+static int fix_optstr(struct libmnt_context *cxt)
+{
+ int rc = 0;
+ char *next;
+ char *name, *val;
+ size_t namesz, valsz;
+ struct libmnt_fs *fs;
+#ifdef HAVE_LIBSELINUX
+ int se_fix = 0, se_rem = 0;
+#endif
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt)
+ return -EINVAL;
+ if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED))
+ return 0;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: fixing optstr"));
+
+ fs = cxt->fs;
+
+ /* The propagation flags should not be used together with any other
+ * flags (except MS_REC and MS_SILENT) */
+ if (cxt->mountflags & MS_PROPAGATION)
+ cxt->mountflags &= (MS_PROPAGATION | MS_REC | MS_SILENT);
+
+ /*
+ * The "user" options is our business (so we can modify the option),
+ * but exception is command line for /sbin/mount.<type> helpers. Let's
+ * save the original user=<name> to call the helpers with unchanged
+ * "user" setting.
+ *
+ * Don't check for MNT_MS_USER in cxt->user_mountflags, the flag maybe
+ * removed by evaluate_permissions().
+ */
+ if (!mnt_optstr_get_option(fs->user_optstr, "user", &val, &valsz)) {
+ if (val) {
+ cxt->orig_user = strndup(val, valsz);
+ if (!cxt->orig_user) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+ cxt->flags |= MNT_FL_SAVED_USER;
+ }
+
+ /*
+ * Sync mount options with mount flags
+ */
+ DBG(CXT, mnt_debug_h(cxt, "mount: fixing vfs optstr"));
+ rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP));
+ if (rc)
+ goto done;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: fixing user optstr"));
+ rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
+ if (rc)
+ goto done;
+
+ if (fs->vfs_optstr && *fs->vfs_optstr == '\0') {
+ free(fs->vfs_optstr);
+ fs->vfs_optstr = NULL;
+ }
+ if (fs->user_optstr && *fs->user_optstr == '\0') {
+ free(fs->user_optstr);
+ fs->user_optstr = NULL;
+ }
+
+ next = fs->fs_optstr;
+
+#ifdef HAVE_LIBSELINUX
+ if (!is_selinux_enabled())
+ /* Always remove SELinux garbage if SELinux disabled */
+ se_rem = 1;
+ else if (cxt->mountflags & MS_REMOUNT)
+ /*
+ * Linux kernel < 2.6.39 does not allow to remount with any
+ * selinux specific mount options.
+ *
+ * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65
+ * 026eb167ae77244458fa4b4b9fc171209c079ba7
+ * fix this odd behavior, so we don't have to care about it in
+ * userspace.
+ */
+ se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39);
+ else
+ /* For normal mount we have translate the contexts */
+ se_fix = 1;
+
+ if (!se_rem) {
+ /* de-duplicate SELinux options */
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "context");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "fscontext");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "defcontext");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "rootcontext");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "seclabel");
+ }
+#endif
+ while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+
+ if (namesz == 3 && !strncmp(name, "uid", 3))
+ rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
+ else if (namesz == 3 && !strncmp(name, "gid", 3))
+ rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
+#ifdef HAVE_LIBSELINUX
+ else if ((se_rem || se_fix) &&
+ namesz >= 7 && (!strncmp(name, "context", 7) ||
+ !strncmp(name, "fscontext", 9) ||
+ !strncmp(name, "defcontext", 10) ||
+ !strncmp(name, "rootcontext", 11) ||
+ !strncmp(name, "seclabel", 8))) {
+ if (se_rem) {
+ /* remove context= option */
+ next = name;
+ rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
+ name,
+ val ? val + valsz :
+ name + namesz);
+ } else if (se_fix && val && valsz)
+ /* translate selinux contexts */
+ rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
+ val, valsz, &next);
+ }
+#endif
+ if (rc)
+ goto done;
+ }
+
+ if (!rc && cxt->user_mountflags & MNT_MS_USER)
+ rc = mnt_optstr_fix_user(&fs->user_optstr);
+
+ /* refresh merged optstr */
+ free(fs->optstr);
+ fs->optstr = NULL;
+ fs->optstr = mnt_fs_strdup_options(fs);
+done:
+ cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;
+
+ DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: "
+ "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc,
+ fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
+
+ if (rc)
+ rc = -MNT_ERR_MOUNTOPT;
+ return rc;
+}
+
+/*
+ * Converts already evaluated and fixed options to the form that is compatible
+ * with /sbin/mount.type helpers.
+ */
+static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
+{
+ struct libmnt_optmap const *maps[1];
+ char *next, *name, *val;
+ size_t namesz, valsz;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(optstr);
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: generate helper mount options"));
+
+ *optstr = mnt_fs_strdup_options(cxt->fs);
+ if (!*optstr)
+ return -ENOMEM;
+
+ if (cxt->flags & MNT_FL_SAVED_USER)
+ rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
+ if (rc)
+ goto err;
+
+ /* remove userspace options with MNT_NOHLPS flag */
+ maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+ next = *optstr;
+
+ while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+ const struct libmnt_optmap *ent;
+
+ mnt_optmap_get_entry(maps, 1, name, namesz, &ent);
+ if (ent && ent->id && (ent->mask & MNT_NOHLPS)) {
+ next = name;
+ rc = mnt_optstr_remove_option_at(optstr, name,
+ val ? val + valsz : name + namesz);
+ if (rc)
+ goto err;
+ }
+ }
+
+ return rc;
+err:
+ free(*optstr);
+ *optstr = NULL;
+ return rc;
+}
+
+/*
+ * this has to be called before fix_optstr()
+ *
+ * Note that user=<name> maybe be used by some filesystems as filesystem
+ * specific option (e.g. cifs). Yes, developers of such filesystems have
+ * allocated pretty hot place in hell...
+ */
+static int evaluate_permissions(struct libmnt_context *cxt)
+{
+ unsigned long u_flags = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt)
+ return -EINVAL;
+ if (!cxt->fs)
+ return 0;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions"));
+
+ mnt_context_get_user_mflags(cxt, &u_flags);
+
+ if (!mnt_context_is_restricted(cxt)) {
+ /*
+ * superuser mount
+ */
+ cxt->user_mountflags &= ~MNT_MS_OWNER;
+ cxt->user_mountflags &= ~MNT_MS_GROUP;
+ cxt->user_mountflags &= ~MNT_MS_USER;
+ cxt->user_mountflags &= ~MNT_MS_USERS;
+ } else {
+ /*
+ * user mount
+ */
+ if (!(cxt->flags & MNT_FL_TAB_APPLIED))
+ {
+ DBG(CXT, mnt_debug_h(cxt, "perms: fstab not applied, ignore user mount"));
+ return -EPERM;
+ }
+
+ /*
+ * MS_OWNERSECURE and MS_SECURE mount options are already
+ * applied by mnt_optstr_get_flags() in mnt_context_merge_mflags()
+ * if "user" (but no user=<name> !) options is set.
+ *
+ * Let's ignore all user=<name> (if <name> is set) requests.
+ */
+ if (cxt->user_mountflags & MNT_MS_USER) {
+ size_t valsz = 0;
+
+ if (!mnt_optstr_get_option(cxt->fs->user_optstr,
+ "user", NULL, &valsz) && valsz) {
+
+ DBG(CXT, mnt_debug_h(cxt, "perms: user=<name> detected, ignore"));
+ cxt->user_mountflags &= ~MNT_MS_USER;
+ }
+ }
+
+ /*
+ * MS_OWNER: Allow owners to mount when fstab contains the
+ * owner option. Note that this should never be used in a high
+ * security environment, but may be useful to give people at
+ * the console the possibility of mounting a floppy. MS_GROUP:
+ * Allow members of device group to mount. (Martin Dickopp)
+ */
+ if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
+ struct stat sb;
+ struct libmnt_cache *cache = NULL;
+ char *xsrc = NULL;
+ const char *srcpath = mnt_fs_get_srcpath(cxt->fs);
+
+ if (!srcpath) { /* Ah... source is TAG */
+ cache = mnt_context_get_cache(cxt);
+ xsrc = mnt_resolve_spec(
+ mnt_context_get_source(cxt),
+ cache);
+ srcpath = xsrc;
+ }
+ if (!srcpath) {
+ DBG(CXT, mnt_debug_h(cxt, "perms: src undefined"));
+ return -EPERM;
+ }
+
+ if (strncmp(srcpath, "/dev/", 5) == 0 &&
+ stat(srcpath, &sb) == 0 &&
+ (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
+ ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))
+
+ cxt->user_mountflags |= MNT_MS_USER;
+
+ if (!cache)
+ free(xsrc);
+ }
+
+ if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
+ DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS"));
+ return -EPERM;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * mnt_context_helper_setopt() backend
+ *
+ * This function applies mount.type command line option (for example parsed
+ * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
+ * then 1 is returned.
+ *
+ * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
+ */
+int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
+{
+ int rc = -EINVAL;
+
+ assert(cxt);
+ assert(cxt->action == MNT_ACT_MOUNT);
+
+ switch(c) {
+ case 'f':
+ rc = mnt_context_enable_fake(cxt, TRUE);
+ break;
+ case 'n':
+ rc = mnt_context_disable_mtab(cxt, TRUE);
+ break;
+ case 'r':
+ rc = mnt_context_append_options(cxt, "ro");
+ break;
+ case 'v':
+ rc = mnt_context_enable_verbose(cxt, TRUE);
+ break;
+ case 'w':
+ rc = mnt_context_append_options(cxt, "rw");
+ break;
+ case 'o':
+ if (arg)
+ rc = mnt_context_append_options(cxt, arg);
+ break;
+ case 's':
+ rc = mnt_context_enable_sloppy(cxt, TRUE);
+ break;
+ case 't':
+ if (arg)
+ rc = mnt_context_set_fstype(cxt, arg);
+ break;
+ default:
+ return 1;
+ break;
+ }
+
+ return rc;
+}
+
+static int exec_helper(struct libmnt_context *cxt)
+{
+ char *o = NULL;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: executing helper %s", cxt->helper));
+
+ rc = generate_helper_optstr(cxt, &o);
+ if (rc)
+ return -EINVAL;
+
+ DBG_FLUSH;
+
+ switch (fork()) {
+ case 0:
+ {
+ const char *args[12], *type;
+ int i = 0;
+
+ if (setgid(getgid()) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setuid(getuid()) < 0)
+ exit(EXIT_FAILURE);
+
+ type = mnt_fs_get_fstype(cxt->fs);
+
+ args[i++] = cxt->helper; /* 1 */
+ args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */
+ args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */
+
+ /*
+ * TODO: remove the exception for "nfs", -s is documented
+ * for years should be usable everywhere.
+ */
+ if (mnt_context_is_sloppy(cxt) &&
+ type && startswith(type, "nfs"))
+ args[i++] = "-s"; /* 4 */
+ if (mnt_context_is_fake(cxt))
+ args[i++] = "-f"; /* 5 */
+ if (mnt_context_is_nomtab(cxt))
+ args[i++] = "-n"; /* 6 */
+ if (mnt_context_is_verbose(cxt))
+ args[i++] = "-v"; /* 7 */
+ if (o) {
+ args[i++] = "-o"; /* 8 */
+ args[i++] = o; /* 9 */
+ }
+ if (type && !endswith(cxt->helper, type)) {
+ args[i++] = "-t"; /* 10 */
+ args[i++] = type; /* 11 */
+ }
+ args[i] = NULL; /* 12 */
+#ifdef CONFIG_LIBMOUNT_DEBUG
+ for (i = 0; args[i]; i++)
+ DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
+ i, args[i]));
+#endif
+ DBG_FLUSH;
+ execv(cxt->helper, (char * const *) args);
+ exit(EXIT_FAILURE);
+ }
+ default:
+ {
+ int st;
+ wait(&st);
+ cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
+
+ DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
+ cxt->helper, cxt->helper_status));
+ cxt->helper_exec_status = rc = 0;
+ break;
+ }
+
+ case -1:
+ cxt->helper_exec_status = rc = -errno;
+ DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
+ break;
+ }
+
+ free(o);
+ return rc;
+}
+
+/*
+ * The default is to use fstype from cxt->fs, this could be overwritten by
+ * @try_type argument.
+ *
+ * Returns: 0 on success,
+ * >0 in case of mount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+static int do_mount(struct libmnt_context *cxt, const char *try_type)
+{
+ int rc = 0;
+ const char *src, *target, *type;
+ unsigned long flags;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (try_type && !cxt->helper) {
+ rc = mnt_context_prepare_helper(cxt, "mount", try_type);
+ if (rc)
+ return rc;
+ }
+ if (cxt->helper)
+ return exec_helper(cxt);
+
+ flags = cxt->mountflags;
+ src = mnt_fs_get_srcpath(cxt->fs);
+ target = mnt_fs_get_target(cxt->fs);
+
+ if (!target)
+ return -EINVAL;
+ if (!src) {
+ /* unnecessary, should be already resolved in
+ * mnt_context_prepare_srcpath(), but for sure... */
+ DBG(CXT, mnt_debug_h(cxt, "WARNING: source is NULL -- using \"none\"!"));
+ src = "none";
+ }
+ type = try_type ? : mnt_fs_get_fstype(cxt->fs);
+
+ if (!(flags & MS_MGC_MSK))
+ flags |= MS_MGC_VAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
+ "[source=%s, target=%s, type=%s, "
+ " mountflags=0x%08lx, mountdata=%s]",
+ mnt_context_is_fake(cxt) ? "(FAKE) " : "",
+ src, target, type,
+ flags, cxt->mountdata ? "yes" : "<none>"));
+
+ if (mnt_context_is_fake(cxt))
+ cxt->syscall_status = 0;
+ else {
+ if (mount(src, target, type, flags, cxt->mountdata)) {
+ cxt->syscall_status = -errno;
+ DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]",
+ -cxt->syscall_status));
+ return -cxt->syscall_status;
+ }
+ DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
+ cxt->syscall_status = 0;
+ }
+
+ if (try_type && cxt->update) {
+ struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
+ if (fs)
+ rc = mnt_fs_set_fstype(fs, try_type);
+ }
+
+ return rc;
+}
+
+static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
+{
+ int neg = pattern && strncmp(pattern, "no", 2) == 0;
+ int rc = -EINVAL;
+ char **filesystems, **fp;
+
+ assert(cxt);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!neg && pattern) {
+ /*
+ * try all types from the list
+ */
+ char *p, *p0;
+
+ DBG(CXT, mnt_debug_h(cxt, "trying to mount by FS pattern list"));
+
+ p0 = p = strdup(pattern);
+ if (!p)
+ return -ENOMEM;
+ do {
+ char *end = strchr(p, ',');
+ if (end)
+ *end = '\0';
+ rc = do_mount(cxt, p);
+ p = end ? end + 1 : NULL;
+
+ } while (!mnt_context_get_status(cxt) && p);
+
+ free(p0);
+
+ if (mnt_context_get_status(cxt))
+ return rc;
+ }
+
+ /*
+ * try /etc/filesystems and /proc/filesystems
+ */
+ DBG(CXT, mnt_debug_h(cxt, "trying to mount by filesystems lists"));
+
+ rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
+ if (rc)
+ return rc;
+
+ for (fp = filesystems; *fp; fp++) {
+ rc = do_mount(cxt, *fp);
+ if (mnt_context_get_status(cxt))
+ break;
+ if (mnt_context_get_syscall_errno(cxt) != EINVAL)
+ break;
+ }
+ mnt_free_filesystems(filesystems);
+ return rc;
+}
+
+/**
+ * mnt_context_prepare_mount:
+ * @cxt: context
+ *
+ * Prepare context for mounting, unnecessary for mnt_context_mount().
+ *
+ * Returns: negative number on error, zero on success
+ */
+int mnt_context_prepare_mount(struct libmnt_context *cxt)
+{
+ int rc = -EINVAL;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+
+ if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
+ return -EINVAL;
+ if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
+ return -EINVAL;
+ if (cxt->flags & MNT_FL_PREPARED)
+ return 0;
+
+ cxt->action = MNT_ACT_MOUNT;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: preparing"));
+
+ /* TODO: fstab is unnecessary for MS_{MOVE,BIND,STARED,...} */
+ rc = mnt_context_apply_fstab(cxt);
+ if (!rc)
+ rc = mnt_context_merge_mflags(cxt);
+ if (!rc)
+ rc = evaluate_permissions(cxt);
+ if (!rc)
+ rc = fix_optstr(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_srcpath(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_target(cxt);
+ if (!rc)
+ rc = mnt_context_guess_fstype(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_helper(cxt, "mount", NULL);
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed"));
+ return rc;
+ }
+ cxt->flags |= MNT_FL_PREPARED;
+ return rc;
+}
+
+/**
+ * mnt_context_do_mount
+ * @cxt: context
+ *
+ * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
+ *
+ * Note that this function could be called only once. If you want to mount
+ * another source or target than you have to call mnt_reset_context().
+ *
+ * If you want to call mount(2) for the same source and target with a different
+ * mount flags or fstype then call mnt_context_reset_status() and then try
+ * again mnt_context_do_mount().
+ *
+ * WARNING: non-zero return code does not mean that mount(2) syscall or
+ * mount.type helper wasn't successfully called.
+ *
+ * Check mnt_context_get_status() after error!
+*
+ * Returns: 0 on success;
+ * >0 in case of mount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+int mnt_context_do_mount(struct libmnt_context *cxt)
+{
+ const char *type;
+ int res;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+ assert((cxt->flags & MNT_FL_PREPARED));
+ assert((cxt->action == MNT_ACT_MOUNT));
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: do mount"));
+
+ if (!(cxt->flags & MNT_FL_MOUNTDATA))
+ cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);
+
+ type = mnt_fs_get_fstype(cxt->fs);
+ if (type)
+ res = do_mount(cxt, NULL);
+ else
+ res = do_mount_by_pattern(cxt, cxt->fstype_pattern);
+
+ if (mnt_context_get_status(cxt)
+ && !mnt_context_is_fake(cxt)
+ && !cxt->helper) {
+ /*
+ * Mounted by mount(2), do some post-mount checks
+ *
+ * Kernel allows to use MS_RDONLY for bind mounts, but the
+ * read-only request could be silently ignored. Check it to
+ * avoid 'ro' in mtab and 'rw' in /proc/mounts.
+ */
+ if ((cxt->mountflags & MS_BIND)
+ && (cxt->mountflags & MS_RDONLY)
+ && !mnt_is_readonly(mnt_context_get_target(cxt)))
+
+ mnt_context_set_mflags(cxt,
+ cxt->mountflags & ~MS_RDONLY);
+
+
+ /* Kernel can silently add MS_RDONLY flag when mounting file
+ * system that does not have write support. Check this to avoid
+ * 'ro' in /proc/mounts and 'rw' in mtab.
+ */
+ if (!(cxt->mountflags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE))
+ && mnt_is_readonly(mnt_context_get_target(cxt)))
+
+ mnt_context_set_mflags(cxt,
+ cxt->mountflags | MS_RDONLY);
+ }
+
+ return res;
+}
+
+/**
+ * mnt_context_finalize_mount:
+ * @cxt: context
+ *
+ * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
+ * after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
+ *
+ * Returns: negative number on error, 0 on success.
+ */
+int mnt_context_finalize_mount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+ assert((cxt->flags & MNT_FL_PREPARED));
+
+ rc = mnt_context_prepare_update(cxt);
+ if (!rc)
+ rc = mnt_context_update_tabs(cxt);;
+ return rc;
+}
+
+/**
+ * mnt_context_mount:
+ * @cxt: mount context
+ *
+ * High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type).
+ *
+ * This is similar to:
+ *
+ * mnt_context_prepare_mount(cxt);
+ * mnt_context_do_mount(cxt);
+ * mnt_context_finalize_mount(cxt);
+ *
+ * See also mnt_context_disable_helpers().
+ *
+ * Note that this function could be called only once. If you want to mount with
+ * different setting than you have to call mnt_reset_context(). It's NOT enough
+ * to call mnt_context_reset_status() if you want call this function more than
+ * once, whole context has to be reseted.
+ *
+ * WARNING: non-zero return code does not mean that mount(2) syscall or
+ * mount.type helper wasn't successfully called.
+ *
+ * Check mnt_context_get_status() after error!
+ *
+ * Returns: 0 on success;
+ * >0 in case of mount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+int mnt_context_mount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+
+ rc = mnt_context_prepare_mount(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_update(cxt);
+ if (!rc)
+ rc = mnt_context_do_mount(cxt);
+
+ /* TODO: if mtab update is expected then check if the
+ * target is really mounted read-write to avoid 'ro' in
+ * mtab and 'rw' in /proc/mounts.
+ */
+ if (!rc)
+ rc = mnt_context_update_tabs(cxt);
+ return rc;
+}
+
+/**
+ * mnt_context_next_mount:
+ * @cxt: context
+ * @itr: iterator
+ * @fs: returns the current filesystem
+ * @mntrc: returns the return code from mnt_context_mount()
+ * @ignored: returns 1 for not matching and 2 for already mounted filesystems
+ *
+ * This function tries to mount the next filesystem from fstab (as returned by
+ * mnt_context_get_fstab()). See also mnt_context_set_fstab().
+ *
+ * You can filter out filesystems by:
+ * mnt_context_set_options_pattern() to simulate mount -a -O pattern
+ * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern
+ *
+ * If the filesystem is already mounted or does not match defined criteria,
+ * then the mnt_context_next_mount() function returns zero, but the @ignored is
+ * non-zero. Note that the root filesystem and filesystems with "noauto" option
+ * are always ignored.
+ *
+ * If mount(2) syscall or mount.type helper failed, then the
+ * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
+ * Use also mnt_context_get_status() to check if the filesystem was
+ * successfully mounted.
+ *
+ * Returns: 0 on success,
+ * <0 in case of error (!= mount(2) errors)
+ * 1 at the end of the list.
+ */
+int mnt_context_next_mount(struct libmnt_context *cxt,
+ struct libmnt_iter *itr,
+ struct libmnt_fs **fs,
+ int *mntrc,
+ int *ignored)
+{
+ struct libmnt_table *fstab, *mtab;
+ const char *o, *tgt;
+ int rc, mounted = 0;
+
+ if (ignored)
+ *ignored = 0;
+ if (mntrc)
+ *mntrc = 0;
+
+ if (!cxt || !fs || !itr)
+ return -EINVAL;
+
+ mtab = cxt->mtab;
+ cxt->mtab = NULL; /* do not reset mtab */
+ mnt_reset_context(cxt);
+ cxt->mtab = mtab;
+
+ rc = mnt_context_get_fstab(cxt, &fstab);
+ if (rc)
+ return rc;
+
+ rc = mnt_table_next_fs(fstab, itr, fs);
+ if (rc != 0)
+ return rc; /* more filesystems (or error) */
+
+ o = mnt_fs_get_user_options(*fs);
+ tgt = mnt_fs_get_target(*fs);
+
+ DBG(CXT, mnt_debug_h(cxt, "next-mount: trying %s", tgt));
+
+ /* ignore swap */
+ if (mnt_fs_is_swaparea(*fs) ||
+
+ /* ignore root filesystem */
+ (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||
+
+ /* ignore noauto filesystems */
+ (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) ||
+
+ /* ignore filesystems not match with options patterns */
+ (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
+ cxt->fstype_pattern)) ||
+
+ /* ignore filesystems not match with type patterns */
+ (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
+ cxt->optstr_pattern))) {
+ if (ignored)
+ *ignored = 1;
+ DBG(CXT, mnt_debug_h(cxt, "next-mount: not-match "
+ "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
+ mnt_fs_get_fstype(*fs),
+ cxt->fstype_pattern,
+ mnt_fs_get_options(*fs),
+ cxt->optstr_pattern));
+ return 0;
+ }
+
+ /* ignore already mounted filesystems */
+ rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted);
+ if (rc)
+ return rc;
+ if (mounted) {
+ if (ignored)
+ *ignored = 2;
+ return 0;
+ }
+
+ if (mnt_context_is_fork(cxt)) {
+ rc = mnt_fork_context(cxt);
+ if (rc)
+ return rc; /* fork error */
+
+ if (mnt_context_is_parent(cxt)) {
+ return 0; /* parent */
+ }
+ }
+
+ /* child or non-forked */
+
+ rc = mnt_context_set_fs(cxt, *fs);
+ if (!rc) {
+ rc = mnt_context_mount(cxt);
+ if (mntrc)
+ *mntrc = rc;
+ }
+
+ if (mnt_context_is_child(cxt)) {
+ DBG(CXT, mnt_debug_h(cxt, "next-mount: child exit [rc=%d]", rc));
+ DBG_FLUSH;
+ exit(rc);
+ }
+ return 0;
+}
+
diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c
new file mode 100644
index 0000000..2c2975e
--- /dev/null
+++ b/libmount/src/context_umount.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: context-umount
+ * @title: Umount context
+ * @short_description: high-level API to umount operation.
+ */
+
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "pathnames.h"
+#include "loopdev.h"
+#include "strutils.h"
+#include "mountP.h"
+
+/*
+ * umount2 flags
+ */
+#ifndef MNT_FORCE
+# define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
+#endif
+
+#ifndef MNT_DETACH
+# define MNT_DETACH 0x00000002 /* Just detach from the tree */
+#endif
+
+#ifndef UMOUNT_NOFOLLOW
+# define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+#ifndef UMOUNT_UNUSED
+# define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
+#endif
+
+
+static int lookup_umount_fs(struct libmnt_context *cxt)
+{
+ int rc, loopdev = 0;
+ const char *tgt;
+ struct libmnt_table *mtab = NULL;
+ struct libmnt_fs *fs;
+
+ assert(cxt);
+ assert(cxt->fs);
+
+ DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS"));
+
+ tgt = mnt_fs_get_target(cxt->fs);
+ if (!tgt) {
+ DBG(CXT, mnt_debug_h(cxt, "umount: undefined target"));
+ return -EINVAL;
+ }
+ rc = mnt_context_get_mtab(cxt, &mtab);
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab"));
+ return rc;
+ }
+
+try_loopdev:
+ fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD);
+ if (!fs && mnt_context_is_swapmatch(cxt)) {
+ /* maybe the option is source rather than target (mountpoint) */
+ fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD);
+
+ if (fs) {
+ struct libmnt_fs *fs1 = mnt_table_find_target(mtab,
+ mnt_fs_get_target(fs),
+ MNT_ITER_BACKWARD);
+ if (!fs1) {
+ DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!"));
+ return -EINVAL;
+ }
+ if (fs != fs1) {
+ /* Something was stacked over `file' on the
+ * same mount point. */
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount: %s: %s is mounted "
+ "over it on the same point",
+ tgt, mnt_fs_get_source(fs1)));
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (!fs && !loopdev) {
+ /*
+ * Maybe target is /path/file.img, try to convert to /dev/loopN
+ */
+ struct stat st;
+
+ if (stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) {
+ char *dev = NULL;
+ int count = loopdev_count_by_backing_file(tgt, &dev);
+
+ if (count == 1) {
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount: %s --> %s (retry)", tgt, dev));
+ mnt_fs_set_source(cxt->fs, tgt);
+ mnt_fs_set_target(cxt->fs, dev);
+ free(dev);
+ tgt = mnt_fs_get_target(cxt->fs);
+
+ loopdev = 1; /* to avoid endless loop */
+ goto try_loopdev;
+
+ } else if (count > 1)
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount: warning: %s is associated "
+ "with more than one loodev", tgt));
+ }
+ }
+
+ if (!fs) {
+ DBG(CXT, mnt_debug_h(cxt, "umount: cannot find %s in mtab", tgt));
+ return 0;
+ }
+
+ if (fs != cxt->fs) {
+ /* copy from mtab to our FS description
+ */
+ mnt_fs_set_source(cxt->fs, NULL);
+ mnt_fs_set_target(cxt->fs, NULL);
+
+ if (!mnt_copy_fs(cxt->fs, fs)) {
+ DBG(CXT, mnt_debug_h(cxt, "umount: failed to copy FS"));
+ return -errno;
+ }
+ DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied"));
+ }
+
+ cxt->flags |= MNT_FL_TAB_APPLIED;
+ return rc;
+}
+
+/* check if @devname is loopdev and if the device is associated
+ * with a source from @fstab_fs
+ */
+static int is_associated_fs(const char *devname, struct libmnt_fs *fs)
+{
+ uintmax_t offset = 0;
+ const char *src;
+ char *val, *optstr;
+ size_t valsz;
+ int flags = 0;
+
+ /* check if it begins with /dev/loop */
+ if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP)))
+ return 0;
+
+ src = mnt_fs_get_srcpath(fs);
+ if (!src)
+ return 0;
+
+ /* check for offset option in @fs */
+ optstr = (char *) mnt_fs_get_user_options(fs);
+
+ if (optstr &&
+ mnt_optstr_get_option(optstr, "offset", &val, &valsz) == 0) {
+ flags |= LOOPDEV_FL_OFFSET;
+
+ if (mnt_parse_offset(val, valsz, &offset) != 0)
+ return 0;
+ }
+
+ return loopdev_is_used(devname, src, offset, flags);
+}
+
+static int prepare_helper_from_options(struct libmnt_context *cxt,
+ const char *name)
+{
+ char *suffix = NULL;
+ const char *opts;
+ size_t valsz;
+
+ if (mnt_context_is_nohelpers(cxt))
+ return 0;
+
+ opts = mnt_fs_get_user_options(cxt->fs);
+ if (!opts)
+ return 0;
+
+ if (mnt_optstr_get_option(opts, name, &suffix, &valsz))
+ return 0;
+
+ suffix = strndup(suffix, valsz);
+ if (!suffix)
+ return -ENOMEM;
+
+ DBG(CXT, mnt_debug_h(cxt, "umount: umount.%s %s requested", suffix, name));
+
+ return mnt_context_prepare_helper(cxt, "umount", suffix);
+}
+
+/*
+ * Note that cxt->fs contains relevant mtab entry!
+ */
+static int evaluate_permissions(struct libmnt_context *cxt)
+{
+ struct libmnt_table *fstab;
+ unsigned long u_flags = 0;
+ const char *tgt, *src, *optstr;
+ int rc, ok = 0;
+ struct libmnt_fs *fs;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt || !cxt->fs)
+ return -EINVAL;
+
+ if (!mnt_context_is_restricted(cxt))
+ return 0; /* superuser mount */
+
+ DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions"));
+
+ if (!(cxt->flags & MNT_FL_TAB_APPLIED)) {
+ DBG(CXT, mnt_debug_h(cxt,
+ "cannot find %s in mtab and you are not root",
+ mnt_fs_get_target(cxt->fs)));
+ goto eperm;
+ }
+
+ if (cxt->user_mountflags & MNT_MS_UHELPER) {
+ /* on uhelper= mount option based helper */
+ rc = prepare_helper_from_options(cxt, "uhelper");
+ if (rc)
+ return rc;
+ if (cxt->helper)
+ return 0; /* we'll call /sbin/umount.<uhelper> */
+ }
+
+ /*
+ * User mounts has to be in /etc/fstab
+ */
+ rc = mnt_context_get_fstab(cxt, &fstab);
+ if (rc)
+ return rc;
+
+ tgt = mnt_fs_get_target(cxt->fs);
+ src = mnt_fs_get_source(cxt->fs);
+
+ if (mnt_fs_get_bindsrc(cxt->fs)) {
+ src = mnt_fs_get_bindsrc(cxt->fs);
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount: using bind source: %s", src));
+ }
+
+ /* If fstab contains the two lines
+ * /dev/sda1 /mnt/zip auto user,noauto 0 0
+ * /dev/sda4 /mnt/zip auto user,noauto 0 0
+ * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail.
+ * So, we must not look for file, but for the pair (dev,file) in fstab.
+ */
+ fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD);
+ if (!fs) {
+ /*
+ * It's possible that there is /path/file.img in fstab and
+ * /dev/loop0 in mtab -- then we have to check releation
+ * between loopdev and the file.
+ */
+ fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD);
+ if (fs) {
+ const char *dev = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */
+
+ if (!dev || !is_associated_fs(dev, fs))
+ fs = NULL;
+ }
+ if (!fs) {
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount %s: mtab disagrees with fstab",
+ tgt));
+ goto eperm;
+ }
+ }
+
+ /*
+ * User mounting and unmounting is allowed only if fstab contains one
+ * of the options `user', `users' or `owner' or `group'.
+ *
+ * The option `users' allows arbitrary users to mount and unmount -
+ * this may be a security risk.
+ *
+ * The options `user', `owner' and `group' only allow unmounting by the
+ * user that mounted (visible in mtab).
+ */
+ optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */
+ if (!optstr)
+ goto eperm;
+
+ if (mnt_optstr_get_flags(optstr, &u_flags,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP)))
+ goto eperm;
+
+ if (u_flags & MNT_MS_USERS) {
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount: promiscuous setting ('users') in fstab"));
+ return 0;
+ }
+ /*
+ * Check user=<username> setting from mtab if there is user, owner or
+ * group option in /etc/fstab
+ */
+ if (u_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) {
+
+ char *curr_user = NULL;
+ char *mtab_user = NULL;
+ size_t sz;
+
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount: checking user=<username> from mtab"));
+
+ curr_user = mnt_get_username(getuid());
+
+ if (!curr_user) {
+ DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot "
+ "convert %d to username", tgt, getuid()));
+ goto eperm;
+ }
+
+ /* get options from mtab */
+ optstr = mnt_fs_get_user_options(cxt->fs);
+ if (optstr && !mnt_optstr_get_option(optstr,
+ "user", &mtab_user, &sz) && sz)
+ ok = !strncmp(curr_user, mtab_user, sz);
+ }
+
+ if (ok) {
+ DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt));
+ return 0;
+ }
+eperm:
+ DBG(CXT, mnt_debug_h(cxt, "umount is not allowed for you"));
+ return -EPERM;
+}
+
+static int exec_helper(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+ assert(cxt->helper_exec_status == 1);
+
+ DBG_FLUSH;
+
+ switch (fork()) {
+ case 0:
+ {
+ const char *args[10], *type;
+ int i = 0;
+
+ if (setgid(getgid()) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setuid(getuid()) < 0)
+ exit(EXIT_FAILURE);
+
+ type = mnt_fs_get_fstype(cxt->fs);
+
+ args[i++] = cxt->helper; /* 1 */
+ args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */
+
+ if (mnt_context_is_nomtab(cxt))
+ args[i++] = "-n"; /* 3 */
+ if (mnt_context_is_lazy(cxt))
+ args[i++] = "-l"; /* 4 */
+ if (mnt_context_is_force(cxt))
+ args[i++] = "-f"; /* 5 */
+ if (mnt_context_is_verbose(cxt))
+ args[i++] = "-v"; /* 6 */
+ if (mnt_context_is_rdonly_umount(cxt))
+ args[i++] = "-r"; /* 7 */
+ if (type && !endswith(cxt->helper, type)) {
+ args[i++] = "-t"; /* 8 */
+ args[i++] = (char *) type; /* 9 */
+ }
+
+ args[i] = NULL; /* 10 */
+#ifdef CONFIG_LIBMOUNT_DEBUG
+ for (i = 0; args[i]; i++)
+ DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
+ i, args[i]));
+#endif
+ DBG_FLUSH;
+ execv(cxt->helper, (char * const *) args);
+ exit(EXIT_FAILURE);
+ }
+ default:
+ {
+ int st;
+ wait(&st);
+ cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
+
+ DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
+ cxt->helper, cxt->helper_status));
+ cxt->helper_exec_status = rc = 0;
+ break;
+ }
+
+ case -1:
+ cxt->helper_exec_status = rc = -errno;
+ DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * mnt_context_helper_setopt() backend.
+ *
+ * This function applies umount.type command line option (for example parsed
+ * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
+ * then 1 is returned.
+ *
+ * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
+ */
+int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg)
+{
+ int rc = -EINVAL;
+
+ assert(cxt);
+ assert(cxt->action == MNT_ACT_UMOUNT);
+
+ switch(c) {
+ case 'n':
+ rc = mnt_context_disable_mtab(cxt, TRUE);
+ break;
+ case 'l':
+ rc = mnt_context_enable_lazy(cxt, TRUE);
+ break;
+ case 'f':
+ rc = mnt_context_enable_force(cxt, TRUE);
+ break;
+ case 'v':
+ rc = mnt_context_enable_verbose(cxt, TRUE);
+ break;
+ case 'r':
+ rc = mnt_context_enable_rdonly_umount(cxt, TRUE);
+ break;
+ case 't':
+ if (arg)
+ rc = mnt_context_set_fstype(cxt, arg);
+ break;
+ default:
+ return 1;
+ break;
+ }
+
+ return rc;
+}
+
+/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
+static int umount_nofollow_support(void)
+{
+ int res = umount2("", UMOUNT_UNUSED);
+ if (res != -1 || errno != EINVAL)
+ return 0;
+
+ res = umount2("", UMOUNT_NOFOLLOW);
+ if (res != -1 || errno != ENOENT)
+ return 0;
+
+ return 1;
+}
+
+static int do_umount(struct libmnt_context *cxt)
+{
+ int rc = 0, flags = 0;
+ const char *src, *target;
+ char *tgtbuf = NULL;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+ assert(cxt->syscall_status == 1);
+
+ if (cxt->helper)
+ return exec_helper(cxt);
+
+ src = mnt_fs_get_srcpath(cxt->fs);
+ target = mnt_fs_get_target(cxt->fs);
+
+ if (!target)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "do umount"));
+
+ if (cxt->restricted && !mnt_context_is_fake(cxt)) {
+ /*
+ * extra paranoa for non-root users
+ * -- chdir to the parent of the mountpoint and use NOFOLLOW
+ * flag to avoid races and symlink attacks.
+ */
+ if (umount_nofollow_support())
+ flags |= UMOUNT_NOFOLLOW;
+
+ rc = mnt_chdir_to_parent(target, &tgtbuf);
+ if (rc)
+ return rc;
+ target = tgtbuf;
+ }
+
+ if (mnt_context_is_lazy(cxt))
+ flags |= MNT_DETACH;
+
+ else if (mnt_context_is_force(cxt))
+ flags |= MNT_FORCE;
+
+ DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]%s",
+ target, flags,
+ mnt_context_is_fake(cxt) ? " (FAKE)" : ""));
+
+ if (mnt_context_is_fake(cxt))
+ rc = 0;
+ else {
+ rc = flags ? umount2(target, flags) : umount(target);
+ if (rc < 0)
+ cxt->syscall_status = -errno;
+ free(tgtbuf);
+ }
+
+ /*
+ * try remount read-only
+ */
+ if (rc < 0
+ && cxt->syscall_status == -EBUSY
+ && mnt_context_is_rdonly_umount(cxt)
+ && src) {
+
+ mnt_context_set_mflags(cxt, (cxt->mountflags |
+ MS_REMOUNT | MS_RDONLY));
+ mnt_context_enable_loopdel(cxt, FALSE);
+
+ DBG(CXT, mnt_debug_h(cxt,
+ "umount(2) failed [errno=%d] -- trying to remount read-only",
+ -cxt->syscall_status));
+
+ rc = mount(src, mnt_fs_get_target(cxt->fs), NULL,
+ MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
+ if (rc < 0) {
+ cxt->syscall_status = -errno;
+ DBG(CXT, mnt_debug_h(cxt,
+ "read-only re-mount(2) failed [errno=%d]",
+ -cxt->syscall_status));
+
+ return -cxt->syscall_status;
+ }
+ cxt->syscall_status = 0;
+ DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success"));
+ return 0;
+ }
+
+ if (rc < 0) {
+ DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]",
+ -cxt->syscall_status));
+ return -cxt->syscall_status;
+ }
+
+ cxt->syscall_status = 0;
+ DBG(CXT, mnt_debug_h(cxt, "umount(2) success"));
+ return 0;
+}
+
+/**
+ * mnt_context_prepare_umount:
+ * @cxt: mount context
+ *
+ * Prepare context for umounting, unnecessary for mnt_context_umount().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_prepare_umount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+
+ if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
+ return -EINVAL;
+ if (!mnt_context_get_source(cxt) && !mnt_context_get_target(cxt))
+ return -EINVAL;
+ if (cxt->flags & MNT_FL_PREPARED)
+ return 0;
+
+ free(cxt->helper); /* be paranoid */
+ cxt->helper = NULL;
+ cxt->action = MNT_ACT_UMOUNT;
+
+ rc = lookup_umount_fs(cxt);
+ if (!rc)
+ rc = mnt_context_merge_mflags(cxt);
+ if (!rc)
+ rc = evaluate_permissions(cxt);
+
+ if (!rc && !cxt->helper) {
+
+ if (cxt->user_mountflags & MNT_MS_HELPER)
+ /* on helper= mount option based helper */
+ rc = prepare_helper_from_options(cxt, "helper");
+
+ if (!rc && !cxt->helper)
+ /* on fstype based helper */
+ rc = mnt_context_prepare_helper(cxt, "umount", NULL);
+ }
+
+ if (!rc && mnt_context_is_loopdel(cxt) && cxt->fs) {
+ const char *src = mnt_fs_get_srcpath(cxt->fs);
+
+ if (src && (!is_loopdev(src) || loopdev_is_autoclear(src)))
+ mnt_context_enable_loopdel(cxt, FALSE);
+ }
+
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "umount: preparing failed"));
+ return rc;
+ }
+ cxt->flags |= MNT_FL_PREPARED;
+ return rc;
+}
+
+/**
+ * mnt_context_do_umount:
+ * @cxt: mount context
+ *
+ * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type).
+ * Unnecessary for mnt_context_umount().
+ *
+ * See also mnt_context_disable_helpers().
+ *
+ * WARNING: non-zero return code does not mean that umount(2) syscall or
+ * umount.type helper wasn't successfully called.
+ *
+ * Check mnt_context_get_status() after error!
+*
+ * Returns: 0 on success;
+ * >0 in case of umount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+int mnt_context_do_umount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+ assert((cxt->flags & MNT_FL_PREPARED));
+ assert((cxt->action == MNT_ACT_UMOUNT));
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ rc = do_umount(cxt);
+ if (rc)
+ return rc;
+
+ if (mnt_context_get_status(cxt) && !mnt_context_is_fake(cxt)) {
+ /*
+ * Umounted, do some post-umount operations
+ * - remove loopdev
+ * - refresh in-memory mtab stuff if remount rather than
+ * umount has been performed
+ */
+ if (mnt_context_is_loopdel(cxt)
+ && !(cxt->mountflags & MS_REMOUNT))
+ rc = mnt_context_delete_loopdev(cxt);
+
+ if (!mnt_context_is_nomtab(cxt)
+ && mnt_context_get_status(cxt)
+ && !cxt->helper
+ && mnt_context_is_rdonly_umount(cxt)
+ && (cxt->mountflags & MS_REMOUNT)) {
+
+ /* use "remount" instead of "umount" in /etc/mtab */
+ if (!rc && cxt->update && cxt->mtab_writable)
+ rc = mnt_update_set_fs(cxt->update,
+ cxt->mountflags, NULL, cxt->fs);
+ }
+ }
+ return rc;
+}
+
+/**
+ * mnt_context_finalize_umount:
+ * @cxt: context
+ *
+ * Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called
+ * after mnt_context_do_umount(). See also mnt_context_set_syscall_status().
+ *
+ * Returns: negative number on error, 0 on success.
+ */
+int mnt_context_finalize_umount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_PREPARED));
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ rc = mnt_context_prepare_update(cxt);
+ if (!rc)
+ rc = mnt_context_update_tabs(cxt);;
+ return rc;
+}
+
+
+/**
+ * mnt_context_umount:
+ * @cxt: umount context
+ *
+ * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type).
+ *
+ * This is similar to:
+ *
+ * mnt_context_prepare_umount(cxt);
+ * mnt_context_do_umount(cxt);
+ * mnt_context_finalize_umount(cxt);
+ *
+ * See also mnt_context_disable_helpers().
+ *
+ * WARNING: non-zero return code does not mean that umount(2) syscall or
+ * umount.type helper wasn't successfully called.
+ *
+ * Check mnt_context_get_status() after error!
+ *
+ * Returns: 0 on success;
+ * >0 in case of umount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+int mnt_context_umount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+
+ DBG(CXT, mnt_debug_h(cxt, "umount: %s", mnt_context_get_target(cxt)));
+
+ rc = mnt_context_prepare_umount(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_update(cxt);
+ if (!rc)
+ rc = mnt_context_do_umount(cxt);
+ if (!rc)
+ rc = mnt_context_update_tabs(cxt);
+ return rc;
+}
+
+
+/**
+ * mnt_context_next_umount:
+ * @cxt: context
+ * @itr: iterator
+ * @fs: returns the current filesystem
+ * @mntrc: returns the return code from mnt_context_umount()
+ * @ignored: returns 1 for not matching
+ *
+ * This function tries to umount the next filesystem from mtab (as returned by
+ * mnt_context_get_mtab()).
+ *
+ * You can filter out filesystems by:
+ * mnt_context_set_options_pattern() to simulate umount -a -O pattern
+ * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern
+ *
+ * If the filesystem is not mounted or does not match defined criteria,
+ * then the function mnt_context_next_umount() returns zero, but the @ignored is
+ * non-zero. Note that the root filesystem is always ignored.
+ *
+ * If umount(2) syscall or umount.type helper failed, then the
+ * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero.
+ * Use also mnt_context_get_status() to check if the filesystem was
+ * successfully umounted.
+ *
+ * Returns: 0 on success,
+ * <0 in case of error (!= umount(2) errors)
+ * 1 at the end of the list.
+ */
+int mnt_context_next_umount(struct libmnt_context *cxt,
+ struct libmnt_iter *itr,
+ struct libmnt_fs **fs,
+ int *mntrc,
+ int *ignored)
+{
+ struct libmnt_table *mtab;
+ const char *tgt;
+ int rc;
+
+ if (ignored)
+ *ignored = 0;
+ if (mntrc)
+ *mntrc = 0;
+
+ if (!cxt || !fs || !itr)
+ return -EINVAL;
+
+ rc = mnt_context_get_mtab(cxt, &mtab);
+ cxt->mtab = NULL; /* do not reset mtab */
+ mnt_reset_context(cxt);
+ cxt->mtab = mtab;
+
+ if (rc)
+ return rc;
+
+ do {
+ rc = mnt_table_next_fs(mtab, itr, fs);
+ if (rc != 0)
+ return rc; /* no more filesystems (or error) */
+
+ tgt = mnt_fs_get_target(*fs);
+ } while (!tgt);
+
+ DBG(CXT, mnt_debug_h(cxt, "next-umount: trying %s", tgt));
+
+ /* ignore root filesystem */
+ if ((tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||
+
+ /* ignore filesystems not match with options patterns */
+ (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
+ cxt->fstype_pattern)) ||
+
+ /* ignore filesystems not match with type patterns */
+ (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
+ cxt->optstr_pattern))) {
+ if (ignored)
+ *ignored = 1;
+ DBG(CXT, mnt_debug_h(cxt, "next-umount: not-match "
+ "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
+ mnt_fs_get_fstype(*fs),
+ cxt->fstype_pattern,
+ mnt_fs_get_options(*fs),
+ cxt->optstr_pattern));
+ return 0;
+ }
+
+ rc = mnt_context_set_fs(cxt, *fs);
+ if (rc)
+ return rc;
+ rc = mnt_context_umount(cxt);
+ if (mntrc)
+ *mntrc = rc;
+ return 0;
+}
diff --git a/libmount/src/fs.c b/libmount/src/fs.c
new file mode 100644
index 0000000..c3b0cb2
--- /dev/null
+++ b/libmount/src/fs.c
@@ -0,0 +1,1501 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: fs
+ * @title: Filesystem
+ * @short_description: represents one entry from fstab, mtab, or mountinfo file
+ *
+ */
+#include <ctype.h>
+#include <blkid.h>
+#include <stddef.h>
+
+#include "mountP.h"
+#include "strutils.h"
+
+/**
+ * mnt_new_fs:
+ *
+ * Returns: newly allocated struct libmnt_fs.
+ */
+struct libmnt_fs *mnt_new_fs(void)
+{
+ struct libmnt_fs *fs = calloc(1, sizeof(*fs));
+ if (!fs)
+ return NULL;
+
+ /*DBG(FS, mnt_debug_h(fs, "alloc"));*/
+ INIT_LIST_HEAD(&fs->ents);
+ return fs;
+}
+
+/**
+ * mnt_free_fs:
+ * @fs: fs pointer
+ *
+ * Deallocates the fs.
+ */
+void mnt_free_fs(struct libmnt_fs *fs)
+{
+ if (!fs)
+ return;
+ list_del(&fs->ents);
+
+ /*DBG(FS, mnt_debug_h(fs, "free"));*/
+
+ free(fs->source);
+ free(fs->bindsrc);
+ free(fs->tagname);
+ free(fs->tagval);
+ free(fs->root);
+ free(fs->swaptype);
+ free(fs->target);
+ free(fs->fstype);
+ free(fs->optstr);
+ free(fs->vfs_optstr);
+ free(fs->fs_optstr);
+ free(fs->user_optstr);
+ free(fs->attrs);
+
+ free(fs);
+}
+
+/**
+ * mnt_reset_fs:
+ * @fs: fs pointer
+ *
+ * Resets (zeroize) @fs.
+ */
+void mnt_reset_fs(struct libmnt_fs *fs)
+{
+ if (fs)
+ memset(fs, 0, sizeof(*fs));
+}
+
+static inline int update_str(char **dest, const char *src)
+{
+ size_t sz;
+ char *x;
+
+ assert(dest);
+
+ if (!src) {
+ free(*dest);
+ *dest = NULL;
+ return 0; /* source (old) is empty */
+ }
+
+ sz = strlen(src) + 1;
+ x = realloc(*dest, sz);
+ if (!x)
+ return -ENOMEM;
+ *dest = x;
+ memcpy(*dest, src, sz);
+ return 0;
+}
+
+static inline int cpy_str_at_offset(void *new, const void *old, size_t offset)
+{
+ char **o = (char **) (old + offset);
+ char **n = (char **) (new + offset);
+
+ if (*n)
+ return 0; /* already set, not overwrite */
+
+ return update_str(n, *o);
+}
+
+/**
+ * mnt_copy_fs:
+ * @dest: destination FS
+ * @src: source FS
+ *
+ * If @dest is NULL, then a new FS is allocated, if any @dest field is already
+ * set then the field is NOT overwrited.
+ *
+ * This function does not copy userdata (se mnt_fs_set_userdata()). A new copy is
+ * not linked with any existing mnt_tab.
+ *
+ * Returns: @dest or NULL in case of error
+ */
+struct libmnt_fs *mnt_copy_fs(struct libmnt_fs *dest,
+ const struct libmnt_fs *src)
+{
+ const struct libmnt_fs *org = dest;
+
+ if (!dest) {
+ dest = mnt_new_fs();
+ if (!dest)
+ return NULL;
+ }
+
+ /*DBG(FS, mnt_debug_h(dest, "copy from %p", src));*/
+
+ dest->id = src->id;
+ dest->parent = src->parent;
+ dest->devno = src->devno;
+ dest->tid = src->tid;
+
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, source)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, tagname)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, tagval)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, root)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, swaptype)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, target)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, fstype)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, optstr)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, vfs_optstr)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, fs_optstr)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, user_optstr)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, attrs)))
+ goto err;
+ if (cpy_str_at_offset(dest, src, offsetof(struct libmnt_fs, bindsrc)))
+ goto err;
+
+ dest->freq = src->freq;
+ dest->passno = src->passno;
+ dest->flags = src->flags;
+ dest->size = src->size;
+ dest->usedsize = src->usedsize;
+ dest->priority = src->priority;
+
+ return dest;
+err:
+ if (!org)
+ mnt_free_fs(dest);
+ return NULL;
+}
+
+/*
+ * This function copies all @fs description except information that does not
+ * belong to /etc/mtab (e.g. VFS and userspace mount options with MNT_NOMTAB
+ * mask).
+ *
+ * Returns: copy of @fs.
+ */
+struct libmnt_fs *mnt_copy_mtab_fs(const struct libmnt_fs *fs)
+{
+ struct libmnt_fs *n = mnt_new_fs();
+
+ if (!n)
+ return NULL;
+
+ if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, source)))
+ goto err;
+ if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, target)))
+ goto err;
+ if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, fstype)))
+ goto err;
+
+ if (fs->vfs_optstr) {
+ char *p = NULL;
+ mnt_optstr_get_options(fs->vfs_optstr, &p,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP),
+ MNT_NOMTAB);
+ n->vfs_optstr = p;
+ }
+
+ if (fs->user_optstr) {
+ char *p = NULL;
+ mnt_optstr_get_options(fs->user_optstr, &p,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
+ MNT_NOMTAB);
+ n->user_optstr = p;
+ }
+
+ if (cpy_str_at_offset(n, fs, offsetof(struct libmnt_fs, fs_optstr)))
+ goto err;
+
+ /* we cannot copy original optstr, the new optstr has to be without
+ * non-mtab options -- so, let's generate a new string */
+ n->optstr = mnt_fs_strdup_options(n);
+
+ n->freq = fs->freq;
+ n->passno = fs->passno;
+ n->flags = fs->flags;
+
+ return n;
+err:
+ mnt_free_fs(n);
+ return NULL;
+
+}
+
+/**
+ * mnt_fs_get_userdata:
+ * @fs: struct libmnt_file instance
+ *
+ * Returns: private data set by mnt_fs_set_userdata() or NULL.
+ */
+void *mnt_fs_get_userdata(struct libmnt_fs *fs)
+{
+ return fs ? fs->userdata : NULL;
+}
+
+/**
+ * mnt_fs_set_userdata:
+ * @fs: struct libmnt_file instance
+ * @data: user data
+ *
+ * The "userdata" are library independent data.
+ *
+ * Returns: 0 or negative number in case of error (if @fs is NULL).
+ */
+int mnt_fs_set_userdata(struct libmnt_fs *fs, void *data)
+{
+ if (!fs)
+ return -EINVAL;
+ fs->userdata = data;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_srcpath:
+ * @fs: struct libmnt_file (fstab/mtab/mountinfo) fs
+ *
+ * The mount "source path" is:
+ * - a directory for 'bind' mounts (in fstab or mtab only)
+ * - a device name for standard mounts
+ *
+ * See also mnt_fs_get_tag() and mnt_fs_get_source().
+ *
+ * Returns: mount source path or NULL in case of error or when the path
+ * is not defined.
+ */
+const char *mnt_fs_get_srcpath(struct libmnt_fs *fs)
+{
+ assert(fs);
+ if (!fs)
+ return NULL;
+
+ /* fstab-like fs */
+ if (fs->tagname)
+ return NULL; /* the source contains a "NAME=value" */
+ return fs->source;
+}
+
+/**
+ * mnt_fs_get_source:
+ * @fs: struct libmnt_file (fstab/mtab/mountinfo) fs
+ *
+ * Returns: mount source. Note that the source could be unparsed TAG
+ * (LABEL/UUID). See also mnt_fs_get_srcpath() and mnt_fs_get_tag().
+ */
+const char *mnt_fs_get_source(struct libmnt_fs *fs)
+{
+ return fs ? fs->source : NULL;
+}
+
+/*
+ * Used by parser ONLY (@source has to be allocated on error)
+ */
+int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source)
+{
+ char *t = NULL, *v = NULL;
+
+ assert(fs);
+
+ if (source && strchr(source, '=')) {
+ if (blkid_parse_tag_string(source, &t, &v) != 0)
+ return -1;
+ }
+
+ if (fs->source != source)
+ free(fs->source);
+
+ free(fs->tagname);
+ free(fs->tagval);
+
+ fs->source = source;
+ fs->tagname = t;
+ fs->tagval = v;
+ return 0;
+}
+
+/**
+ * mnt_fs_set_source:
+ * @fs: fstab/mtab/mountinfo entry
+ * @source: new source
+ *
+ * This function creates a private copy (strdup()) of @source.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_source(struct libmnt_fs *fs, const char *source)
+{
+ char *p = NULL;
+ int rc;
+
+ if (!fs)
+ return -EINVAL;
+
+ if (source) {
+ p = strdup(source);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ rc = __mnt_fs_set_source_ptr(fs, p);
+ if (rc)
+ free(p);
+ return rc;
+}
+
+/**
+ * mnt_fs_streq_srcpath:
+ * @fs: fs
+ * @path: source path
+ *
+ * Compares @fs source path with @path. The tailing slash is ignored.
+ * See also mnt_fs_match_source().
+ *
+ * Returns: 1 if @fs source path equal to @path, otherwise 0.
+ */
+int mnt_fs_streq_srcpath(struct libmnt_fs *fs, const char *path)
+{
+ const char *p;
+
+ if (!fs)
+ return 0;
+
+ p = mnt_fs_get_srcpath(fs);
+
+ if (!mnt_fs_is_pseudofs(fs))
+ return streq_except_trailing_slash(p, path);
+
+ if (!p && !path)
+ return 1;
+
+ return p && path && strcmp(p, path) == 0;
+}
+
+/**
+ * mnt_fs_streq_target:
+ * @fs: fs
+ * @path: mount point
+ *
+ * Compares @fs target path with @path. The tailing slash is ignored.
+ * See also mnt_fs_match_target().
+ *
+ * Returns: 1 if @fs target path equal to @path, otherwise 0.
+ */
+int mnt_fs_streq_target(struct libmnt_fs *fs, const char *path)
+{
+ return fs && streq_except_trailing_slash(mnt_fs_get_target(fs), path);
+}
+
+/**
+ * mnt_fs_get_tag:
+ * @fs: fs
+ * @name: returns pointer to NAME string
+ * @value: returns pointer to VALUE string
+ *
+ * "TAG" is NAME=VALUE (e.g. LABEL=foo)
+ *
+ * The TAG is the first column in the fstab file. The TAG or "srcpath" has to
+ * be always set for all entries.
+ *
+ * See also mnt_fs_get_source().
+ *
+ * <informalexample>
+ * <programlisting>
+ * char *src;
+ * struct libmnt_fs *fs = mnt_table_find_target(tb, "/home", MNT_ITER_FORWARD);
+ *
+ * if (!fs)
+ * goto err;
+ *
+ * src = mnt_fs_get_srcpath(fs);
+ * if (!src) {
+ * char *tag, *val;
+ * if (mnt_fs_get_tag(fs, &tag, &val) == 0)
+ * printf("%s: %s\n", tag, val); // LABEL or UUID
+ * } else
+ * printf("device: %s\n", src); // device or bind path
+ * </programlisting>
+ * </informalexample>
+ *
+ * Returns: 0 on success or negative number in case that a TAG is not defined.
+ */
+int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value)
+{
+ if (fs == NULL || !fs->tagname)
+ return -EINVAL;
+ if (name)
+ *name = fs->tagname;
+ if (value)
+ *value = fs->tagval;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_target:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Returns: pointer to mountpoint path or NULL
+ */
+const char *mnt_fs_get_target(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->target : NULL;
+}
+
+/**
+ * mnt_fs_set_target:
+ * @fs: fstab/mtab/mountinfo entry
+ * @target: mountpoint
+ *
+ * This function creates a private copy (strdup()) of @target.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_target(struct libmnt_fs *fs, const char *target)
+{
+ char *p = NULL;
+
+ assert(fs);
+
+ if (!fs)
+ return -EINVAL;
+ if (target) {
+ p = strdup(target);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(fs->target);
+ fs->target = p;
+
+ return 0;
+}
+
+static int mnt_fs_get_flags(struct libmnt_fs *fs)
+{
+ return fs ? fs->flags : 0;
+}
+
+/**
+ * mnt_fs_is_kernel:
+ * @fs: filesystem
+ *
+ * Returns: 1 if the filesystem description is read from kernel e.g. /proc/mounts.
+ */
+int mnt_fs_is_kernel(struct libmnt_fs *fs)
+{
+ return mnt_fs_get_flags(fs) & MNT_FS_KERNEL;
+}
+
+/**
+ * mnt_fs_is_swaparea:
+ * @fs: filesystem
+ *
+ * Returns: 1 if the filesystem uses "swap" as a type
+ */
+int mnt_fs_is_swaparea(struct libmnt_fs *fs)
+{
+ return mnt_fs_get_flags(fs) & MNT_FS_SWAP;
+}
+
+/**
+ * mnt_fs_is_pseudofs:
+ * @fs: filesystem
+ *
+ * Returns: 1 if the filesystem is a pseudo fs type (proc, cgroups)
+ */
+int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
+{
+ return mnt_fs_get_flags(fs) & MNT_FS_PSEUDO;
+}
+
+/**
+ * mnt_fs_is_netfs:
+ * @fs: filesystem
+ *
+ * Returns: 1 if the filesystem is a network filesystem
+ */
+int mnt_fs_is_netfs(struct libmnt_fs *fs)
+{
+ return mnt_fs_get_flags(fs) & MNT_FS_NET;
+}
+
+/**
+ * mnt_fs_get_fstype:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Returns: pointer to filesystem type.
+ */
+const char *mnt_fs_get_fstype(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->fstype : NULL;
+}
+
+/* Used by struct libmnt_file parser only */
+int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype)
+{
+ assert(fs);
+
+ if (fstype != fs->fstype)
+ free(fs->fstype);
+
+ fs->fstype = fstype;
+ fs->flags &= ~MNT_FS_PSEUDO;
+ fs->flags &= ~MNT_FS_NET;
+ fs->flags &= ~MNT_FS_SWAP;
+
+ /* save info about pseudo filesystems */
+ if (fs->fstype) {
+ if (mnt_fstype_is_pseudofs(fs->fstype))
+ fs->flags |= MNT_FS_PSEUDO;
+ else if (mnt_fstype_is_netfs(fs->fstype))
+ fs->flags |= MNT_FS_NET;
+ else if (!strcmp(fs->fstype, "swap"))
+ fs->flags |= MNT_FS_SWAP;
+ }
+ return 0;
+}
+
+/**
+ * mnt_fs_set_fstype:
+ * @fs: fstab/mtab/mountinfo entry
+ * @fstype: filesystem type
+ *
+ * This function creates a private copy (strdup()) of @fstype.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_fstype(struct libmnt_fs *fs, const char *fstype)
+{
+ char *p = NULL;
+
+ if (!fs)
+ return -EINVAL;
+ if (fstype) {
+ p = strdup(fstype);
+ if (!p)
+ return -ENOMEM;
+ }
+ return __mnt_fs_set_fstype_ptr(fs, p);
+}
+
+/*
+ * Merges @vfs and @fs options strings into a new string.
+ * This function cares about 'ro/rw' options. The 'ro' is
+ * always used if @vfs or @fs is read-only.
+ * For example:
+ *
+ * mnt_merge_optstr("rw,noexec", "ro,journal=update")
+ *
+ * returns: "ro,noexec,journal=update"
+ *
+ * mnt_merge_optstr("rw,noexec", "rw,journal=update")
+ *
+ * returns: "rw,noexec,journal=update"
+ */
+static char *merge_optstr(const char *vfs, const char *fs)
+{
+ char *res, *p;
+ size_t sz;
+ int ro = 0, rw = 0;
+
+ if (!vfs && !fs)
+ return NULL;
+ if (!vfs || !fs)
+ return strdup(fs ? fs : vfs);
+ if (!strcmp(vfs, fs))
+ return strdup(vfs); /* e.g. "aaa" and "aaa" */
+
+ /* leave space for leading "r[ow],", "," and trailing zero */
+ sz = strlen(vfs) + strlen(fs) + 5;
+ res = malloc(sz);
+ if (!res)
+ return NULL;
+ p = res + 3; /* make a room for rw/ro flag */
+
+ snprintf(p, sz - 3, "%s,%s", vfs, fs);
+
+ /* remove 'rw' flags */
+ rw += !mnt_optstr_remove_option(&p, "rw"); /* from vfs */
+ rw += !mnt_optstr_remove_option(&p, "rw"); /* from fs */
+
+ /* remove 'ro' flags if necessary */
+ if (rw != 2) {
+ ro += !mnt_optstr_remove_option(&p, "ro");
+ if (ro + rw < 2)
+ ro += !mnt_optstr_remove_option(&p, "ro");
+ }
+
+ if (!strlen(p))
+ memcpy(res, ro ? "ro" : "rw", 3);
+ else
+ memcpy(res, ro ? "ro," : "rw,", 3);
+ return res;
+}
+
+/**
+ * mnt_fs_strdup_options:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Merges all mount options (VFS, FS and userspace) to the one options string
+ * and returns the result. This function does not modigy @fs.
+ *
+ * Returns: pointer to string (can be freed by free(3)) or NULL in case of error.
+ */
+char *mnt_fs_strdup_options(struct libmnt_fs *fs)
+{
+ char *res;
+
+ assert(fs);
+
+ errno = 0;
+
+ if (fs->optstr)
+ return strdup(fs->optstr);
+
+ res = merge_optstr(fs->vfs_optstr, fs->fs_optstr);
+ if (!res && errno)
+ return NULL;
+ if (fs->user_optstr) {
+ if (mnt_optstr_append_option(&res, fs->user_optstr, NULL)) {
+ free(res);
+ res = NULL;
+ }
+ }
+ return res;
+}
+
+/**
+ * mnt_fs_get_options:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Returns: pointer to string or NULL in case of error.
+ */
+const char *mnt_fs_get_options(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->optstr : NULL;
+}
+
+
+/**
+ * mnt_fs_set_options:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ * @optstr: options string
+ *
+ * Splits @optstr to VFS, FS and userspace mount options and update relevat
+ * parts of @fs.
+ *
+ * Returns: 0 on success, or negative number icase of error.
+ */
+int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr)
+{
+ char *v = NULL, *f = NULL, *u = NULL, *n = NULL;
+
+ assert(fs);
+
+ if (!fs)
+ return -EINVAL;
+ if (optstr) {
+ int rc = mnt_split_optstr(optstr, &u, &v, &f, 0, 0);
+ if (rc)
+ return rc;
+ n = strdup(optstr);
+ if (!n)
+ return -ENOMEM;
+ }
+
+ free(fs->fs_optstr);
+ free(fs->vfs_optstr);
+ free(fs->user_optstr);
+ free(fs->optstr);
+
+ fs->fs_optstr = f;
+ fs->vfs_optstr = v;
+ fs->user_optstr = u;
+ fs->optstr = n;
+
+ return 0;
+}
+
+/**
+ * mnt_fs_append_options:
+ * @fs: fstab/mtab/mountinfo entry
+ * @optstr: mount options
+ *
+ * Parses (splits) @optstr and appends results to VFS, FS and userspace lists
+ * of options.
+ *
+ * If @optstr is NULL then @fs is not modified and 0 is returned.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr)
+{
+ char *v = NULL, *f = NULL, *u = NULL;
+ int rc;
+
+ assert(fs);
+
+ if (!fs)
+ return -EINVAL;
+ if (!optstr)
+ return 0;
+
+ rc = mnt_split_optstr((char *) optstr, &u, &v, &f, 0, 0);
+ if (!rc && v)
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, v, NULL);
+ if (!rc && f)
+ rc = mnt_optstr_append_option(&fs->fs_optstr, f, NULL);
+ if (!rc && u)
+ rc = mnt_optstr_append_option(&fs->user_optstr, u, NULL);
+ if (!rc)
+ rc = mnt_optstr_append_option(&fs->optstr, optstr, NULL);
+
+ free(v);
+ free(f);
+ free(u);
+
+ return rc;
+}
+
+/**
+ * mnt_fs_prepend_options:
+ * @fs: fstab/mtab/mountinfo entry
+ * @optstr: mount options
+ *
+ * Parses (splits) @optstr and prepands results to VFS, FS and userspace lists
+ * of options.
+ *
+ * If @optstr is NULL then @fs is not modified and 0 is returned.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr)
+{
+ char *v = NULL, *f = NULL, *u = NULL;
+ int rc;
+
+ assert(fs);
+
+ if (!fs)
+ return -EINVAL;
+ if (!optstr)
+ return 0;
+
+ rc = mnt_split_optstr((char *) optstr, &u, &v, &f, 0, 0);
+ if (!rc && v)
+ rc = mnt_optstr_prepend_option(&fs->vfs_optstr, v, NULL);
+ if (!rc && f)
+ rc = mnt_optstr_prepend_option(&fs->fs_optstr, f, NULL);
+ if (!rc && u)
+ rc = mnt_optstr_prepend_option(&fs->user_optstr, u, NULL);
+ if (!rc)
+ rc = mnt_optstr_prepend_option(&fs->optstr, optstr, NULL);
+
+ free(v);
+ free(f);
+ free(u);
+
+ return rc;
+}
+
+/*
+ * mnt_fs_get_fs_options:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Returns: pointer to superblock (fs-depend) mount option string or NULL.
+ */
+const char *mnt_fs_get_fs_options(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->fs_optstr : NULL;
+}
+
+/**
+ * mnt_fs_get_vfs_options:
+ * @fs: fstab/mtab entry pointer
+ *
+ * Returns: pointer to fs-independent (VFS) mount option string or NULL.
+ */
+const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->vfs_optstr : NULL;
+}
+
+/**
+ * mnt_fs_get_user_options:
+ * @fs: fstab/mtab entry pointer
+ *
+ * Returns: pointer to userspace mount option string or NULL.
+ */
+const char *mnt_fs_get_user_options(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->user_optstr : NULL;
+}
+
+/**
+ * mnt_fs_get_attributes:
+ * @fs: fstab/mtab entry pointer
+ *
+ * Returns: pointer to attributes string or NULL.
+ */
+const char *mnt_fs_get_attributes(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->attrs : NULL;
+}
+
+/**
+ * mnt_fs_set_attributes:
+ * @fs: fstab/mtab/mountinfo entry
+ * @optstr: options string
+ *
+ * Sets mount attributes. The attributes are mount(2) and mount(8) independent
+ * options, these options are not send to kernel and are not interpreted by
+ * libmount. The attributes are stored in /run/mount/utab only.
+ *
+ * The atrtributes are managed by libmount in userspace only. It's possible
+ * that information stored in userspace will not be available for libmount
+ * after CLONE_FS unshare. Be careful, and don't use attributes if possible.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr)
+{
+ char *p = NULL;
+
+ if (!fs)
+ return -EINVAL;
+ if (optstr) {
+ p = strdup(optstr);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(fs->attrs);
+ fs->attrs = p;
+
+ return 0;
+}
+
+/**
+ * mnt_fs_append_attributes
+ * @fs: fstab/mtab/mountinfo entry
+ * @optstr: options string
+ *
+ * Appends mount attributes. (See mnt_fs_set_attributes()).
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_append_attributes(struct libmnt_fs *fs, const char *optstr)
+{
+ if (!fs)
+ return -EINVAL;
+ if (!optstr)
+ return 0;
+ return mnt_optstr_append_option(&fs->attrs, optstr, NULL);
+}
+
+/**
+ * mnt_fs_prepend_attributes
+ * @fs: fstab/mtab/mountinfo entry
+ * @optstr: options string
+ *
+ * Prepends mount attributes. (See mnt_fs_set_attributes()).
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_prepend_attributes(struct libmnt_fs *fs, const char *optstr)
+{
+ if (!fs)
+ return -EINVAL;
+ if (!optstr)
+ return 0;
+ return mnt_optstr_prepend_option(&fs->attrs, optstr, NULL);
+}
+
+
+/**
+ * mnt_fs_get_freq:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Returns: dump frequency in days.
+ */
+int mnt_fs_get_freq(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->freq : 0;
+}
+
+/**
+ * mnt_fs_set_freq:
+ * @fs: fstab/mtab entry pointer
+ * @freq: dump frequency in days
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_freq(struct libmnt_fs *fs, int freq)
+{
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+ fs->freq = freq;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_passno:
+ * @fs: fstab/mtab entry pointer
+ *
+ * Returns: "pass number on parallel fsck".
+ */
+int mnt_fs_get_passno(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->passno: 0;
+}
+
+/**
+ * mnt_fs_set_passno:
+ * @fs: fstab/mtab entry pointer
+ * @passno: pass number
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_passno(struct libmnt_fs *fs, int passno)
+{
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+ fs->passno = passno;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_root:
+ * @fs: /proc/self/mountinfo entry
+ *
+ * Returns: root of the mount within the filesystem or NULL
+ */
+const char *mnt_fs_get_root(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->root : NULL;
+}
+
+/**
+ * mnt_fs_set_root:
+ * @fs: mountinfo entry
+ * @root: path
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_root(struct libmnt_fs *fs, const char *root)
+{
+ char *p = NULL;
+
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+ if (root) {
+ p = strdup(root);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(fs->root);
+ fs->root = p;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_swaptype:
+ * @fs: /proc/swaps entry
+ *
+ * Returns: swap type or NULL
+ */
+const char *mnt_fs_get_swaptype(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->swaptype : NULL;
+}
+
+/**
+ * mnt_fs_get_size:
+ * @fs: /proc/swaps entry
+ *
+ * Returns: size
+ */
+off_t mnt_fs_get_size(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->size : 0;
+}
+
+/**
+ * mnt_fs_get_usedsize:
+ * @fs: /proc/swaps entry
+ *
+ * Returns: used size
+ */
+off_t mnt_fs_get_usedsize(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->usedsize : 0;
+}
+
+/**
+ * mnt_fs_get_priority:
+ * @fs: /proc/swaps entry
+ *
+ * Returns: priority
+ */
+int mnt_fs_get_priority(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->priority : 0;
+}
+
+/**
+ * mnt_fs_set_priority:
+ * @fs: /proc/swaps entry
+ *
+ * Returns: 0 or -1 in case of error
+ */
+int mnt_fs_set_priority(struct libmnt_fs *fs, int prio)
+{
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+ fs->priority = prio;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_bindsrc:
+ * @fs: /run/mount/utab entry
+ *
+ * Returns: full path that was used for mount(2) on MS_BIND
+ */
+const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->bindsrc : NULL;
+}
+
+/**
+ * mnt_fs_set_bindsrc:
+ * @fs: filesystem
+ * @src: path
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src)
+{
+ char *p = NULL;
+
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+ if (src) {
+ p = strdup(src);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(fs->bindsrc);
+ fs->bindsrc = p;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_id:
+ * @fs: /proc/self/mountinfo entry
+ *
+ * Returns: mount ID (unique identifier of the mount) or negative number in case of error.
+ */
+int mnt_fs_get_id(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->id : -EINVAL;
+}
+
+/**
+ * mnt_fs_get_parent_id:
+ * @fs: /proc/self/mountinfo entry
+ *
+ * Returns: parent mount ID or negative number in case of error.
+ */
+int mnt_fs_get_parent_id(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->parent : -EINVAL;
+}
+
+/**
+ * mnt_fs_get_devno:
+ * @fs: /proc/self/mountinfo entry
+ *
+ * Returns: value of st_dev for files on filesystem or 0 in case of error.
+ */
+dev_t mnt_fs_get_devno(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->devno : 0;
+}
+
+/**
+ * mnt_fs_get_tid:
+ * @fs: /proc/<tid>/mountinfo entry
+ *
+ * Returns: TID (task ID) for filesystems read from mountinfo file
+ */
+pid_t mnt_fs_get_tid(struct libmnt_fs *fs)
+{
+ assert(fs);
+ return fs ? fs->tid : 0;
+}
+
+/**
+ * mnt_fs_get_option:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ * @name: option name
+ * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL
+ * @valsz: returns size of options value or 0
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case of error.
+ */
+int mnt_fs_get_option(struct libmnt_fs *fs, const char *name,
+ char **value, size_t *valsz)
+{
+ char rc = 1;
+
+ if (fs->fs_optstr)
+ rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz);
+ if (rc == 1 && fs->vfs_optstr)
+ rc = mnt_optstr_get_option(fs->vfs_optstr, name, value, valsz);
+ if (rc == 1 && fs->user_optstr)
+ rc = mnt_optstr_get_option(fs->user_optstr, name, value, valsz);
+ return rc;
+}
+
+/**
+ * mnt_fs_get_attribute:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ * @name: option name
+ * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL
+ * @valsz: returns size of options value or 0
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case of error.
+ */
+int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name,
+ char **value, size_t *valsz)
+{
+ char rc = 1;
+
+ if (fs->attrs)
+ rc = mnt_optstr_get_option(fs->attrs, name, value, valsz);
+ return rc;
+}
+
+/**
+ * mnt_fs_match_target:
+ * @fs: filesystem
+ * @target: mountpoint path
+ * @cache: tags/paths cache or NULL
+ *
+ * Possible are three attempts:
+ * 1) compare @target with @fs->target
+ * 2) realpath(@target) with @fs->target
+ * 3) realpath(@target) with realpath(@fs->target) if @fs is not from
+ * /proc/self/mountinfo.
+ *
+ * The 2nd and 3rd attempts are not performed when @cache is NULL.
+ *
+ * Returns: 1 if @fs target is equal to @target else 0.
+ */
+int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
+ struct libmnt_cache *cache)
+{
+ int rc = 0;
+
+ if (!fs || !target || !fs->target)
+ return 0;
+
+ /* 1) native paths */
+ rc = mnt_fs_streq_target(fs, target);
+
+ if (!rc && cache) {
+ /* 2) - canonicalized and non-canonicalized */
+ char *cn = mnt_resolve_path(target, cache);
+ rc = (cn && strcmp(cn, fs->target) == 0);
+
+ /* 3) - canonicalized and canonicalized */
+ if (!rc && cn && !mnt_fs_is_kernel(fs)) {
+ char *tcn = mnt_resolve_path(fs->target, cache);
+ rc = (tcn && strcmp(cn, tcn) == 0);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * mnt_fs_match_source:
+ * @fs: filesystem
+ * @source: tag or path (device or so) or NULL
+ * @cache: tags/paths cache or NULL
+ *
+ * Possible are four attempts:
+ * 1) compare @source with @fs->source
+ * 2) compare realpath(@source) with @fs->source
+ * 3) compare realpath(@source) with realpath(@fs->source)
+ * 4) compare realpath(@source) with evaluated tag from @fs->source
+ *
+ * The 2nd, 3rd and 4th attempts are not performed when @cache is NULL. The
+ * 2nd and 3rd attempts are not performed if @fs->source is tag.
+ *
+ * Returns: 1 if @fs source is equal to @source else 0.
+ */
+int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
+ struct libmnt_cache *cache)
+{
+ char *cn;
+ const char *src, *t, *v;
+
+ if (!fs)
+ return 0;
+
+ /* 1) native paths... */
+ if (mnt_fs_streq_srcpath(fs, source) == 1)
+ return 1;
+
+ if (!source || !fs->source)
+ return 0;
+
+ /* ... and tags */
+ if (fs->tagname && strcmp(source, fs->source) == 0)
+ return 1;
+
+ if (!cache)
+ return 0;
+ if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO))
+ return 0;
+
+ cn = mnt_resolve_spec(source, cache);
+ if (!cn)
+ return 0;
+
+ /* 2) canonicalized and native */
+ src = mnt_fs_get_srcpath(fs);
+ if (src && mnt_fs_streq_srcpath(fs, cn))
+ return 1;
+
+ /* 3) canonicalized and canonicalized */
+ if (src) {
+ src = mnt_resolve_path(src, cache);
+ if (src && !strcmp(cn, src))
+ return 1;
+ }
+ if (src || mnt_fs_get_tag(fs, &t, &v))
+ /* src path does not match and tag is not defined */
+ return 0;
+
+ /* read @source's tags to the cache */
+ if (mnt_cache_read_tags(cache, cn) < 0) {
+ if (errno == EACCES) {
+ /* we don't have permissions to read TAGs from
+ * @source, but can translate @fs tag to devname.
+ *
+ * (because libblkid uses udev symlinks and this is
+ * accessible for non-root uses)
+ */
+ char *x = mnt_resolve_tag(t, v, cache);
+ if (x && !strcmp(x, cn))
+ return 1;
+ }
+ return 0;
+ }
+
+ /* 4) has the @source a tag that matches with tag from @fs ? */
+ if (mnt_cache_device_has_tag(cache, cn, t, v))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * mnt_fs_match_fstype:
+ * @fs: filesystem
+ * @types: filesystem name or comma delimited list of filesystems
+ *
+ * For more details see mnt_match_fstype().
+ *
+ * Returns: 1 if @fs type is matching to @types else 0. The function returns
+ * 0 when types is NULL.
+ */
+int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types)
+{
+ return mnt_match_fstype(fs->fstype, types);
+}
+
+/**
+ * mnt_fs_match_options:
+ * @fs: filesystem
+ * @options: comma delimited list of options (and nooptions)
+ *
+ * For more details see mnt_match_options().
+ *
+ * Returns: 1 if @fs type is matching to @options else 0. The function returns
+ * 0 when types is NULL.
+ */
+int mnt_fs_match_options(struct libmnt_fs *fs, const char *options)
+{
+ return mnt_match_options(mnt_fs_get_options(fs), options);
+}
+
+/**
+ * mnt_fs_print_debug
+ * @fs: fstab/mtab/mountinfo entry
+ * @file: file stream
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
+{
+ if (!fs)
+ return -EINVAL;
+ fprintf(file, "------ fs: %p\n", fs);
+ fprintf(file, "source: %s\n", mnt_fs_get_source(fs));
+ fprintf(file, "target: %s\n", mnt_fs_get_target(fs));
+ fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs));
+
+ if (mnt_fs_get_options(fs))
+ fprintf(file, "optstr: %s\n", mnt_fs_get_options(fs));
+ if (mnt_fs_get_vfs_options(fs))
+ fprintf(file, "VFS-optstr: %s\n", mnt_fs_get_vfs_options(fs));
+ if (mnt_fs_get_fs_options(fs))
+ fprintf(file, "FS-opstr: %s\n", mnt_fs_get_fs_options(fs));
+ if (mnt_fs_get_user_options(fs))
+ fprintf(file, "user-optstr: %s\n", mnt_fs_get_user_options(fs));
+ if (mnt_fs_get_attributes(fs))
+ fprintf(file, "attributes: %s\n", mnt_fs_get_attributes(fs));
+
+ if (mnt_fs_get_root(fs))
+ fprintf(file, "root: %s\n", mnt_fs_get_root(fs));
+
+ if (mnt_fs_get_swaptype(fs))
+ fprintf(file, "swaptype: %s\n", mnt_fs_get_swaptype(fs));
+ if (mnt_fs_get_size(fs))
+ fprintf(file, "size: %jd\n", mnt_fs_get_size(fs));
+ if (mnt_fs_get_usedsize(fs))
+ fprintf(file, "usedsize: %jd\n", mnt_fs_get_usedsize(fs));
+ if (mnt_fs_get_priority(fs))
+ fprintf(file, "priority: %d\n", mnt_fs_get_priority(fs));
+
+ if (mnt_fs_get_bindsrc(fs))
+ fprintf(file, "bindsrc: %s\n", mnt_fs_get_bindsrc(fs));
+ if (mnt_fs_get_freq(fs))
+ fprintf(file, "freq: %d\n", mnt_fs_get_freq(fs));
+ if (mnt_fs_get_passno(fs))
+ fprintf(file, "pass: %d\n", mnt_fs_get_passno(fs));
+ if (mnt_fs_get_id(fs))
+ fprintf(file, "id: %d\n", mnt_fs_get_id(fs));
+ if (mnt_fs_get_parent_id(fs))
+ fprintf(file, "parent: %d\n", mnt_fs_get_parent_id(fs));
+ if (mnt_fs_get_devno(fs))
+ fprintf(file, "devno: %d:%d\n", major(mnt_fs_get_devno(fs)),
+ minor(mnt_fs_get_devno(fs)));
+ if (mnt_fs_get_tid(fs))
+ fprintf(file, "tid: %d\n", mnt_fs_get_tid(fs));
+
+ return 0;
+}
+
+/**
+ * mnt_free_mntent:
+ * @mnt: mount entry
+ *
+ * Deallocates "mntent.h" mount entry.
+ */
+void mnt_free_mntent(struct mntent *mnt)
+{
+ if (mnt) {
+ free(mnt->mnt_fsname);
+ free(mnt->mnt_dir);
+ free(mnt->mnt_type);
+ free(mnt->mnt_opts);
+ free(mnt);
+ }
+}
+
+/**
+ * mnt_fs_to_mntent:
+ * @fs: filesystem
+ * @mnt: mount description (as described in mntent.h)
+ *
+ * Copies information from @fs to struct mntent @mnt. If @mnt is already set
+ * then the struct mntent items are reallocated and updated. See also
+ * mnt_free_mntent().
+ *
+ * Returns: 0 on success and negative number in case of error.
+ */
+int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt)
+{
+ int rc;
+ struct mntent *m;
+
+ if (!fs || !mnt)
+ return -EINVAL;
+
+ m = *mnt;
+ if (!m) {
+ m = calloc(1, sizeof(*m));
+ if (!m)
+ return -ENOMEM;
+ }
+
+ if ((rc = update_str(&m->mnt_fsname, mnt_fs_get_source(fs))))
+ goto err;
+ if ((rc = update_str(&m->mnt_dir, mnt_fs_get_target(fs))))
+ goto err;
+ if ((rc = update_str(&m->mnt_type, mnt_fs_get_fstype(fs))))
+ goto err;
+
+ errno = 0;
+ m->mnt_opts = mnt_fs_strdup_options(fs);
+ if (!m->mnt_opts && errno) {
+ rc = -errno;
+ goto err;
+ }
+
+ m->mnt_freq = mnt_fs_get_freq(fs);
+ m->mnt_passno = mnt_fs_get_passno(fs);
+
+ if (!m->mnt_fsname) {
+ m->mnt_fsname = strdup("none");
+ if (!m->mnt_fsname)
+ goto err;
+ }
+ *mnt = m;
+
+ return 0;
+err:
+ if (m != *mnt)
+ mnt_free_mntent(m);
+ return rc;
+}
diff --git a/libmount/src/init.c b/libmount/src/init.c
new file mode 100644
index 0000000..58d4c18
--- /dev/null
+++ b/libmount/src/init.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debuging
+ */
+
+#include <stdarg.h>
+
+#include "mountP.h"
+
+int libmount_debug_mask;
+
+/**
+ * mnt_init_debug:
+ * @mask: debug mask (0xffff to enable full debuging)
+ *
+ * If the @mask is not specified then this function reads
+ * LIBMOUNT_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ */
+void mnt_init_debug(int mask)
+{
+ if (libmount_debug_mask & MNT_DEBUG_INIT)
+ return;
+ if (!mask) {
+ char *str = getenv("LIBMOUNT_DEBUG");
+ if (str)
+ libmount_debug_mask = strtoul(str, 0, 0);
+ } else
+ libmount_debug_mask = mask;
+
+ if (libmount_debug_mask)
+ fprintf(stderr, "libmount: debug mask set to 0x%04x.\n",
+ libmount_debug_mask);
+ libmount_debug_mask |= MNT_DEBUG_INIT;
+}
diff --git a/libmount/src/iter.c b/libmount/src/iter.c
new file mode 100644
index 0000000..393c23e
--- /dev/null
+++ b/libmount/src/iter.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: iter
+ * @title: Iterator
+ * @short_description: unified iterator
+ *
+ * The iterator keeps direction and last position for access to the internal
+ * library tables/lists.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mountP.h"
+
+/**
+ * mnt_new_iter:
+ * @direction: MNT_INTER_{FOR,BACK}WARD direction
+ *
+ * Returns: newly allocated generic libmount iterator.
+ */
+struct libmnt_iter *mnt_new_iter(int direction)
+{
+ struct libmnt_iter *itr = calloc(1, sizeof(*itr));
+ if (!itr)
+ return NULL;
+ itr->direction = direction;
+ return itr;
+}
+
+/**
+ * mnt_free_iter:
+ * @itr: iterator pointer
+ *
+ * Deallocates iterator.
+ */
+void mnt_free_iter(struct libmnt_iter *itr)
+{
+ free(itr);
+}
+
+/**
+ * mnt_reset_iter:
+ * @itr: iterator pointer
+ * @direction: MNT_INTER_{FOR,BACK}WARD or -1 to keep the derection unchanged
+ *
+ * Resets iterator.
+ */
+void mnt_reset_iter(struct libmnt_iter *itr, int direction)
+{
+ assert(itr);
+
+ if (direction == -1 && itr)
+ direction = itr->direction;
+
+ if (itr) {
+ memset(itr, 0, sizeof(*itr));
+ itr->direction = direction;
+ }
+}
+
+/**
+ * mnt_iter_get_direction:
+ * @itr: iterator pointer
+ *
+ * Returns: MNT_INTER_{FOR,BACK}WARD or negative number in case of error.
+ */
+int mnt_iter_get_direction(struct libmnt_iter *itr)
+{
+ assert(itr);
+ return itr ? itr->direction : -EINVAL;
+}
diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in
new file mode 100644
index 0000000..43646ac
--- /dev/null
+++ b/libmount/src/libmount.h.in
@@ -0,0 +1,653 @@
+/*
+ * mount.h - libmount API
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LIBMOUNT_MOUNT_H
+#define _LIBMOUNT_MOUNT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <mntent.h>
+#include <sys/types.h>
+
+#define LIBMOUNT_VERSION "@LIBMOUNT_VERSION@"
+
+/**
+ * libmnt_cache:
+ *
+ * Stores canonicalized paths and evaluated tags
+ */
+struct libmnt_cache;
+
+/**
+ * libmnt_lock:
+ *
+ * Stores information about locked file (e.g. /etc/mtab)
+ */
+struct libmnt_lock;
+
+/**
+ * libmnt_iter:
+ *
+ * Generic iterator (stores state about lists)
+ */
+struct libmnt_iter;
+
+/**
+ * libmnt_optmap:
+ *
+ * Mount options description (map)
+ */
+struct libmnt_optmap
+{
+ const char *name; /* option name[=%<type>] (e.g. "loop[=%s]") */
+ int id; /* option ID or MS_* flags (e.g MS_RDONLY) */
+ int mask; /* MNT_{NOMTAB,INVERT,...} mask */
+};
+
+/*
+ * mount options map masks
+ */
+#define MNT_INVERT (1 << 1) /* invert the mountflag */
+#define MNT_NOMTAB (1 << 2) /* skip in the mtab option string */
+#define MNT_PREFIX (1 << 3) /* prefix used for some options (e.g. "x-foo") */
+#define MNT_NOHLPS (1 << 4) /* don't add the option to mount.<type> helpers command line */
+
+/**
+ * libmnt_fs:
+ *
+ * Parsed fstab/mtab/mountinfo entry
+ */
+struct libmnt_fs;
+
+/**
+ * libmnt_table:
+ *
+ * List of struct libmnt_fs entries (parsed fstab/mtab/mountinfo)
+ */
+struct libmnt_table;
+
+/**
+ * libmnt_update
+ *
+ * /etc/mtab or utab update description
+ */
+struct libmnt_update;
+
+/**
+ * libmnt_context
+ *
+ * Mount/umount status
+ */
+struct libmnt_context;
+
+/**
+ * libmnt_tabdiff:
+ *
+ * Stores mountinfo state
+ */
+struct libmnt_tabdiff;
+
+/*
+ * Actions
+ */
+enum {
+ MNT_ACT_MOUNT = 1,
+ MNT_ACT_UMOUNT
+};
+
+/*
+ * Errors -- by default libmount returns -errno for generic errors (ENOMEM,
+ * EINVAL, ...) and for mount(2) errors, but for some specific operations it
+ * returns private error codes. Note that maximum system errno value should be
+ * 4095 on UNIXes.
+ *
+ * See also mnt_context_get_syscall_errno() and mnt_context_get_helper_status().
+ */
+#define MNT_ERR_NOFSTAB 5000 /* not found required entry in fstab */
+#define MNT_ERR_NOFSTYPE 5001 /* failed to detect filesystem type */
+#define MNT_ERR_NOSOURCE 5002 /* required mount source undefined */
+#define MNT_ERR_LOOPDEV 5003 /* loopdev setup failed, errno set by libc */
+#define MNT_ERR_MOUNTOPT 5004 /* failed to parse/use userspace mount options */
+
+/* init.c */
+extern void mnt_init_debug(int mask);
+
+/* version.c */
+extern int mnt_parse_version_string(const char *ver_string);
+extern int mnt_get_library_version(const char **ver_string);
+extern int mnt_get_library_features(const char ***features);
+
+/* utils.c */
+extern char *mnt_mangle(const char *str);
+extern char *mnt_unmangle(const char *str);
+extern int mnt_fstype_is_netfs(const char *type);
+extern int mnt_fstype_is_pseudofs(const char *type);
+extern int mnt_match_fstype(const char *type, const char *pattern);
+extern int mnt_match_options(const char *optstr, const char *pattern);
+extern const char *mnt_get_fstab_path(void);
+extern const char *mnt_get_swaps_path(void);
+extern const char *mnt_get_mtab_path(void);
+extern int mnt_has_regular_mtab(const char **mtab, int *writable);
+extern char *mnt_get_mountpoint(const char *path);
+
+/* cache.c */
+extern struct libmnt_cache *mnt_new_cache(void);
+extern void mnt_free_cache(struct libmnt_cache *cache);
+extern int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname);
+extern int mnt_cache_device_has_tag(struct libmnt_cache *cache,
+ const char *devname,
+ const char *token,
+ const char *value);
+
+extern char *mnt_cache_find_tag_value(struct libmnt_cache *cache,
+ const char *devname, const char *token);
+
+extern char *mnt_get_fstype(const char *devname, int *ambi,
+ struct libmnt_cache *cache);
+extern char *mnt_resolve_path(const char *path, struct libmnt_cache *cache);
+extern char *mnt_resolve_tag(const char *token, const char *value,
+ struct libmnt_cache *cache);
+extern char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache);
+
+extern char *mnt_pretty_path(const char *path, struct libmnt_cache *cache);
+
+/* optstr.c */
+extern int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz,
+ char **value, size_t *valuesz);
+extern int mnt_optstr_append_option(char **optstr, const char *name,
+ const char *value);
+extern int mnt_optstr_prepend_option(char **optstr, const char *name,
+ const char *value);
+extern int mnt_optstr_get_option(const char *optstr, const char *name,
+ char **value, size_t *valsz);
+extern int mnt_optstr_set_option(char **optstr, const char *name,
+ const char *value);
+extern int mnt_optstr_remove_option(char **optstr, const char *name);
+extern int mnt_optstr_deduplicate_option(char **optstr, const char *name);
+
+extern int mnt_split_optstr(const char *optstr,
+ char **user, char **vfs, char **fs,
+ int ignore_user, int ignore_vfs);
+
+extern int mnt_optstr_get_options(const char *optstr, char **subset,
+ const struct libmnt_optmap *map, int ignore);
+
+extern int mnt_optstr_get_flags(const char *optstr, unsigned long *flags,
+ const struct libmnt_optmap *map);
+extern int mnt_optstr_apply_flags(char **optstr, unsigned long flags,
+ const struct libmnt_optmap *map);
+
+
+/* iter.c */
+enum {
+
+ MNT_ITER_FORWARD = 0,
+ MNT_ITER_BACKWARD
+};
+extern struct libmnt_iter *mnt_new_iter(int direction);
+extern void mnt_free_iter(struct libmnt_iter *itr);
+extern void mnt_reset_iter(struct libmnt_iter *itr, int direction);
+extern int mnt_iter_get_direction(struct libmnt_iter *itr);
+
+/* optmap.c */
+enum {
+ MNT_LINUX_MAP = 1,
+ MNT_USERSPACE_MAP
+};
+extern const struct libmnt_optmap *mnt_get_builtin_optmap(int id);
+
+/* lock.c */
+extern struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id);
+extern void mnt_free_lock(struct libmnt_lock *ml);
+extern void mnt_unlock_file(struct libmnt_lock *ml);
+extern int mnt_lock_file(struct libmnt_lock *ml);
+extern int mnt_lock_block_signals(struct libmnt_lock *ml, int enable);
+
+/* fs.c */
+extern struct libmnt_fs *mnt_new_fs(void);
+extern void mnt_free_fs(struct libmnt_fs *fs);
+extern void mnt_reset_fs(struct libmnt_fs *fs);
+extern struct libmnt_fs *mnt_copy_fs(struct libmnt_fs *dest,
+ const struct libmnt_fs *src);
+extern void *mnt_fs_get_userdata(struct libmnt_fs *fs);
+extern int mnt_fs_set_userdata(struct libmnt_fs *fs, void *data);
+extern const char *mnt_fs_get_source(struct libmnt_fs *fs);
+extern int mnt_fs_set_source(struct libmnt_fs *fs, const char *source);
+extern const char *mnt_fs_get_srcpath(struct libmnt_fs *fs);
+extern int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name,
+ const char **value);
+extern const char *mnt_fs_get_target(struct libmnt_fs *fs);
+extern int mnt_fs_set_target(struct libmnt_fs *fs, const char *target);
+extern const char *mnt_fs_get_fstype(struct libmnt_fs *fs);
+extern int mnt_fs_set_fstype(struct libmnt_fs *fs, const char *fstype);
+
+extern int mnt_fs_streq_srcpath(struct libmnt_fs *fs, const char *path);
+extern int mnt_fs_streq_target(struct libmnt_fs *fs, const char *path);
+
+extern char *mnt_fs_strdup_options(struct libmnt_fs *fs);
+extern const char *mnt_fs_get_options(struct libmnt_fs *fs);
+extern int mnt_fs_set_options(struct libmnt_fs *fs, const char *optstr);
+extern int mnt_fs_append_options(struct libmnt_fs *fs, const char *optstr);
+extern int mnt_fs_prepend_options(struct libmnt_fs *fs, const char *optstr);
+
+extern int mnt_fs_get_option(struct libmnt_fs *fs, const char *name,
+ char **value, size_t *valsz);
+
+
+extern const char *mnt_fs_get_fs_options(struct libmnt_fs *fs);
+extern const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs);
+extern const char *mnt_fs_get_user_options(struct libmnt_fs *fs);
+
+extern const char *mnt_fs_get_attributes(struct libmnt_fs *fs);
+extern int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr);
+extern int mnt_fs_get_attribute(struct libmnt_fs *fs, const char *name,
+ char **value, size_t *valsz);
+extern int mnt_fs_append_attributes(struct libmnt_fs *fs, const char *optstr);
+extern int mnt_fs_prepend_attributes(struct libmnt_fs *fs, const char *optstr);
+
+extern int mnt_fs_get_freq(struct libmnt_fs *fs);
+extern int mnt_fs_set_freq(struct libmnt_fs *fs, int freq);
+extern int mnt_fs_get_passno(struct libmnt_fs *fs);
+extern int mnt_fs_set_passno(struct libmnt_fs *fs, int passno);
+extern const char *mnt_fs_get_root(struct libmnt_fs *fs);
+extern int mnt_fs_set_root(struct libmnt_fs *fs, const char *root);
+extern const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs);
+extern int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src);
+extern int mnt_fs_get_id(struct libmnt_fs *fs);
+extern int mnt_fs_get_parent_id(struct libmnt_fs *fs);
+extern dev_t mnt_fs_get_devno(struct libmnt_fs *fs);
+extern pid_t mnt_fs_get_tid(struct libmnt_fs *fs);
+
+extern const char *mnt_fs_get_swaptype(struct libmnt_fs *fs);
+extern off_t mnt_fs_get_size(struct libmnt_fs *fs);
+extern off_t mnt_fs_get_usedsize(struct libmnt_fs *fs);
+extern int mnt_fs_get_priority(struct libmnt_fs *fs);
+
+extern int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
+ struct libmnt_cache *cache);
+extern int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
+ struct libmnt_cache *cache);
+extern int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types);
+extern int mnt_fs_match_options(struct libmnt_fs *fs, const char *options);
+extern int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file);
+
+extern int mnt_fs_is_kernel(struct libmnt_fs *fs);
+extern int mnt_fs_is_swaparea(struct libmnt_fs *fs);
+extern int mnt_fs_is_netfs(struct libmnt_fs *fs);
+extern int mnt_fs_is_pseudofs(struct libmnt_fs *fs);
+
+extern void mnt_free_mntent(struct mntent *mnt);
+extern int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt);
+
+/* tab-parse.c */
+extern struct libmnt_table *mnt_new_table_from_file(const char *filename);
+extern struct libmnt_table *mnt_new_table_from_dir(const char *dirname);
+extern int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f,
+ const char *filename);
+extern int mnt_table_parse_file(struct libmnt_table *tb, const char *filename);
+extern int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname);
+
+extern int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename);
+extern int mnt_table_parse_swaps(struct libmnt_table *tb, const char *filename);
+extern int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename);
+extern int mnt_table_set_parser_errcb(struct libmnt_table *tb,
+ int (*cb)(struct libmnt_table *tb, const char *filename, int line));
+
+/* tab.c */
+extern struct libmnt_table *mnt_new_table(void);
+extern void mnt_free_table(struct libmnt_table *tb);
+extern int mnt_reset_table(struct libmnt_table *tb);
+extern int mnt_table_get_nents(struct libmnt_table *tb);
+extern int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc);
+extern struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb);
+extern int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs);
+extern int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs);
+extern int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
+ struct libmnt_fs **fs);
+extern int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
+ struct libmnt_fs *parent, struct libmnt_fs **chld);
+extern int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root);
+extern int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr,
+ struct libmnt_fs *fs);
+
+extern struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb,
+ const char *path, int direction);
+extern struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb,
+ const char *path, int direction);
+extern struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
+ const char *val, int direction);
+extern struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
+ const char *source, int direction);
+extern struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb,
+ const char *source,
+ const char *target, int direction);
+extern struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
+ dev_t devno, int direction);
+
+extern int mnt_table_find_next_fs(struct libmnt_table *tb,
+ struct libmnt_iter *itr,
+ int (*match_func)(struct libmnt_fs *, void *), void *userdata,
+ struct libmnt_fs **fs);
+
+extern int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs);
+
+
+/* tab_update.c */
+extern struct libmnt_update *mnt_new_update(void);
+extern void mnt_free_update(struct libmnt_update *upd);
+extern int mnt_update_is_ready(struct libmnt_update *upd);
+extern int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
+ const char *target, struct libmnt_fs *fs);
+extern int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc);
+extern unsigned long mnt_update_get_mflags(struct libmnt_update *upd);
+extern int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly);
+extern const char *mnt_update_get_filename(struct libmnt_update *upd);
+extern struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd);
+
+/* tab_diff.c */
+enum {
+ MNT_TABDIFF_MOUNT = 1,
+ MNT_TABDIFF_UMOUNT,
+ MNT_TABDIFF_MOVE,
+ MNT_TABDIFF_REMOUNT,
+ MNT_TABDIFF_PROPAGATION, /* not implemented yet (TODO) */
+};
+
+extern struct libmnt_tabdiff *mnt_new_tabdiff(void);
+extern void mnt_free_tabdiff(struct libmnt_tabdiff *df);
+
+extern int mnt_diff_tables(struct libmnt_tabdiff *df,
+ struct libmnt_table *old_tab,
+ struct libmnt_table *new_tab);
+
+extern int mnt_tabdiff_next_change(struct libmnt_tabdiff *df,
+ struct libmnt_iter *itr,
+ struct libmnt_fs **old_fs,
+ struct libmnt_fs **new_fs,
+ int *oper);
+
+/* context.c */
+
+/*
+ * Mode for mount options from fstab (or mtab), see mnt_context_set_optsmode().
+ */
+enum {
+ MNT_OMODE_IGNORE = (1 << 1), /* ignore mtab/fstab options */
+ MNT_OMODE_APPEND = (1 << 2), /* append mtab/fstab options to existing options */
+ MNT_OMODE_PREPEND = (1 << 3), /* prepend mtab/fstab options to existing options */
+ MNT_OMODE_REPLACE = (1 << 4), /* replace existing options with options from mtab/fstab */
+
+ MNT_OMODE_FORCE = (1 << 5), /* always read mtab/fstab options */
+
+ MNT_OMODE_FSTAB = (1 << 10), /* read from fstab */
+ MNT_OMODE_MTAB = (1 << 11), /* read from mtab if fstab not enabled or failed */
+ MNT_OMODE_NOTAB = (1 << 12), /* do not read fstab/mtab at all */
+
+ /* default */
+ MNT_OMODE_AUTO = (MNT_OMODE_PREPEND | MNT_OMODE_FSTAB | MNT_OMODE_MTAB),
+ /* non-root users */
+ MNT_OMODE_USER = (MNT_OMODE_REPLACE | MNT_OMODE_FORCE | MNT_OMODE_FSTAB)
+};
+
+extern struct libmnt_context *mnt_new_context(void);
+extern void mnt_free_context(struct libmnt_context *cxt);
+extern int mnt_reset_context(struct libmnt_context *cxt);
+extern int mnt_context_is_restricted(struct libmnt_context *cxt);
+
+extern int mnt_context_init_helper(struct libmnt_context *cxt,
+ int action, int flags);
+extern int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg);
+
+extern int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode);
+extern int mnt_context_disable_canonicalize(struct libmnt_context *cxt, int disable);
+extern int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable);
+extern int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable);
+extern int mnt_context_disable_helpers(struct libmnt_context *cxt, int disable);
+extern int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable);
+extern int mnt_context_enable_fake(struct libmnt_context *cxt, int enable);
+extern int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable);
+extern int mnt_context_enable_force(struct libmnt_context *cxt, int enable);
+extern int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable);
+extern int mnt_context_enable_loopdel(struct libmnt_context *cxt, int enable);
+extern int mnt_context_enable_fork(struct libmnt_context *cxt, int enable);
+extern int mnt_context_disable_swapmatch(struct libmnt_context *cxt, int disable);
+
+extern int mnt_context_get_optsmode(struct libmnt_context *cxt);
+extern int mnt_context_is_lazy(struct libmnt_context *cxt);
+extern int mnt_context_is_rdonly_umount(struct libmnt_context *cxt);
+extern int mnt_context_is_sloppy(struct libmnt_context *cxt);
+extern int mnt_context_is_fake(struct libmnt_context *cxt);
+extern int mnt_context_is_nomtab(struct libmnt_context *cxt);
+extern int mnt_context_is_force(struct libmnt_context *cxt);
+extern int mnt_context_is_verbose(struct libmnt_context *cxt);
+extern int mnt_context_is_loopdel(struct libmnt_context *cxt);
+extern int mnt_context_is_nohelpers(struct libmnt_context *cxt);
+extern int mnt_context_is_nocanonicalize(struct libmnt_context *cxt);
+extern int mnt_context_is_swapmatch(struct libmnt_context *cxt);
+
+extern int mnt_context_is_fork(struct libmnt_context *cxt);
+extern int mnt_context_is_parent(struct libmnt_context *cxt);
+extern int mnt_context_is_child(struct libmnt_context *cxt);
+
+extern int mnt_context_wait_for_children(struct libmnt_context *cxt,
+ int *nchildren, int *nerrs);
+
+extern int mnt_context_is_fs_mounted(struct libmnt_context *cxt,
+ struct libmnt_fs *fs, int *mounted);
+
+extern int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs);
+extern struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt);
+extern int mnt_context_set_source(struct libmnt_context *cxt, const char *source);
+extern int mnt_context_set_target(struct libmnt_context *cxt, const char *target);
+extern int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype);
+extern const char *mnt_context_get_source(struct libmnt_context *cxt);
+extern const char *mnt_context_get_target(struct libmnt_context *cxt);
+extern const char *mnt_context_get_fstype(struct libmnt_context *cxt);
+
+extern int mnt_context_set_options(struct libmnt_context *cxt, const char *optstr);
+extern int mnt_context_append_options(struct libmnt_context *cxt,
+ const char *optstr);
+extern const char *mnt_context_get_options(struct libmnt_context *cxt);
+
+extern int mnt_context_set_fstype_pattern(struct libmnt_context *cxt,
+ const char *pattern);
+extern int mnt_context_set_options_pattern(struct libmnt_context *cxt,
+ const char *pattern);
+
+extern int mnt_context_set_passwd_cb(struct libmnt_context *cxt,
+ char *(*get)(struct libmnt_context *),
+ void (*release)(struct libmnt_context *, char *));
+
+extern int mnt_context_set_tables_errcb(struct libmnt_context *cxt,
+ int (*cb)(struct libmnt_table *tb, const char *filename, int line));
+extern int mnt_context_set_fstab(struct libmnt_context *cxt,
+ struct libmnt_table *tb);
+extern int mnt_context_get_fstab(struct libmnt_context *cxt,
+ struct libmnt_table **tb);
+extern int mnt_context_get_mtab(struct libmnt_context *cxt,
+ struct libmnt_table **tb);
+extern int mnt_context_get_table(struct libmnt_context *cxt,
+ const char *filename,
+ struct libmnt_table **tb);
+extern int mnt_context_set_cache(struct libmnt_context *cxt,
+ struct libmnt_cache *cache);
+extern struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt);
+extern struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt);
+
+extern int mnt_context_set_mflags(struct libmnt_context *cxt,
+ unsigned long flags);
+extern int mnt_context_get_mflags(struct libmnt_context *cxt,
+ unsigned long *flags);
+extern int mnt_context_set_user_mflags(struct libmnt_context *cxt,
+ unsigned long flags);
+extern int mnt_context_get_user_mflags(struct libmnt_context *cxt,
+ unsigned long *flags);
+
+extern int mnt_context_set_mountdata(struct libmnt_context *cxt, void *data);
+extern int mnt_context_apply_fstab(struct libmnt_context *cxt);
+
+extern int mnt_context_reset_status(struct libmnt_context *cxt);
+extern int mnt_context_get_status(struct libmnt_context *cxt);
+
+extern int mnt_context_helper_executed(struct libmnt_context *cxt);
+extern int mnt_context_get_helper_status(struct libmnt_context *cxt);
+
+extern int mnt_context_syscall_called(struct libmnt_context *cxt);
+extern int mnt_context_get_syscall_errno(struct libmnt_context *cxt);
+
+extern int mnt_context_strerror(struct libmnt_context *cxt, char *buf,
+ size_t bufsiz);
+
+extern int mnt_context_mount(struct libmnt_context *cxt);
+extern int mnt_context_umount(struct libmnt_context *cxt);
+extern int mnt_context_next_mount(struct libmnt_context *cxt,
+ struct libmnt_iter *itr, struct libmnt_fs **fs,
+ int *mntrc, int *ignored);
+extern int mnt_context_next_umount(struct libmnt_context *cxt,
+ struct libmnt_iter *itr, struct libmnt_fs **fs,
+ int *mntrc, int *ignored);
+
+extern int mnt_context_prepare_mount(struct libmnt_context *cxt);
+extern int mnt_context_do_mount(struct libmnt_context *cxt);
+extern int mnt_context_finalize_mount(struct libmnt_context *cxt);
+
+extern int mnt_context_prepare_umount(struct libmnt_context *cxt);
+extern int mnt_context_do_umount(struct libmnt_context *cxt);
+extern int mnt_context_finalize_umount(struct libmnt_context *cxt);
+
+
+extern int mnt_context_tab_applied(struct libmnt_context *cxt);
+extern int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status);
+
+/*
+ * mount(8) userspace options masks (MNT_MAP_USERSPACE map)
+ */
+#define MNT_MS_NOAUTO (1 << 2)
+#define MNT_MS_USER (1 << 3)
+#define MNT_MS_USERS (1 << 4)
+#define MNT_MS_OWNER (1 << 5)
+#define MNT_MS_GROUP (1 << 6)
+#define MNT_MS_NETDEV (1 << 7)
+#define MNT_MS_COMMENT (1 << 8)
+#define MNT_MS_LOOP (1 << 9)
+#define MNT_MS_NOFAIL (1 << 10)
+#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)
+#define MNT_MS_ENCRYPTION (1 << 16)
+
+/*
+ * mount(2) MS_* masks (MNT_MAP_LINUX map)
+ */
+#ifndef MS_RDONLY
+#define MS_RDONLY 1 /* Mount read-only */
+#endif
+#ifndef MS_NOSUID
+#define MS_NOSUID 2 /* Ignore suid and sgid bits */
+#endif
+#ifndef MS_NODEV
+#define MS_NODEV 4 /* Disallow access to device special files */
+#endif
+#ifndef MS_NOEXEC
+#define MS_NOEXEC 8 /* Disallow program execution */
+#endif
+#ifndef MS_SYNCHRONOUS
+#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
+#endif
+#ifndef MS_REMOUNT
+#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
+#endif
+#ifndef MS_MANDLOCK
+#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
+#endif
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
+#endif
+#ifndef MS_NOATIME
+#define MS_NOATIME 0x400 /* 1024: Do not update access times. */
+#endif
+#ifndef MS_NODIRATIME
+#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */
+#endif
+#ifndef MS_BIND
+#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE 0x2000 /* 8192: Atomically move tree */
+#endif
+#ifndef MS_REC
+#define MS_REC 0x4000 /* 16384: Recursive loopback */
+#endif
+#ifndef MS_SILENT
+#define MS_SILENT 0x8000 /* 32768 */
+#endif
+#ifndef MS_RELATIME
+#define MS_RELATIME 0x200000 /* 200000: Update access times relative
+ to mtime/ctime */
+#endif
+#ifndef MS_UNBINDABLE
+#define MS_UNBINDABLE (1<<17) /* 131072 unbindable*/
+#endif
+#ifndef MS_PRIVATE
+#define MS_PRIVATE (1<<18) /* 262144 Private*/
+#endif
+#ifndef MS_SLAVE
+#define MS_SLAVE (1<<19) /* 524288 Slave*/
+#endif
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20) /* 1048576 Shared*/
+#endif
+#ifndef MS_I_VERSION
+#define MS_I_VERSION (1<<23) /* update inode I_version field */
+#endif
+#ifndef MS_STRICTATIME
+#define MS_STRICTATIME (1<<24) /* strict atime semantics */
+#endif
+
+/*
+ * Magic mount flag number. Had to be or-ed to the flag values.
+ */
+#ifndef MS_MGC_VAL
+#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
+#endif
+#ifndef MS_MGC_MSK
+#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+#endif
+
+
+/* Shared-subtree options */
+#define MS_PROPAGATION (MS_SHARED|MS_SLAVE|MS_UNBINDABLE|MS_PRIVATE)
+
+/* Options that we make ordinary users have by default. */
+#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV)
+
+/* Options that we make owner-mounted devices have by default */
+#define MS_OWNERSECURE (MS_NOSUID|MS_NODEV)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBMOUNT_MOUNT_H */
diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym
new file mode 100644
index 0000000..9e0f2c4
--- /dev/null
+++ b/libmount/src/libmount.sym
@@ -0,0 +1,250 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol foo;
+ * can't run with old libblkid.so not providing foo;
+ * version info can't enforce this since we never change the SONAME.
+ */
+MOUNT_2.19 {
+global:
+ mnt_cache_device_has_tag;
+ mnt_cache_find_tag_value;
+ mnt_cache_read_tags;
+ mnt_context_append_options;
+ mnt_context_apply_fstab;
+ mnt_context_disable_canonicalize;
+ mnt_context_disable_helpers;
+ mnt_context_disable_mtab;
+ mnt_context_do_mount;
+ mnt_context_do_umount;
+ mnt_context_enable_fake;
+ mnt_context_enable_force;
+ mnt_context_enable_lazy;
+ mnt_context_enable_loopdel;
+ mnt_context_enable_rdonly_umount;
+ mnt_context_enable_sloppy;
+ mnt_context_enable_verbose;
+ mnt_context_finalize_mount;
+ mnt_context_finalize_umount;
+ mnt_context_get_cache;
+ mnt_context_get_fs;
+ mnt_context_get_fstab;
+ mnt_context_get_fstype;
+ mnt_context_get_lock;
+ mnt_context_get_mflags;
+ mnt_context_get_mtab;
+ mnt_context_get_optsmode;
+ mnt_context_get_source;
+ mnt_context_get_status;
+ mnt_context_get_target;
+ mnt_context_get_user_mflags;
+ mnt_context_helper_setopt;
+ mnt_context_init_helper;
+ mnt_context_is_fake;
+ mnt_context_is_force;
+ mnt_context_is_lazy;
+ mnt_context_is_nomtab;
+ mnt_context_is_rdonly_umount;
+ mnt_context_is_restricted;
+ mnt_context_is_sloppy;
+ mnt_context_is_verbose;
+ mnt_context_mount;
+ mnt_context_prepare_mount;
+ mnt_context_prepare_umount;
+ mnt_context_set_cache;
+ mnt_context_set_fs;
+ mnt_context_set_fstab;
+ mnt_context_set_fstype;
+ mnt_context_set_fstype_pattern;
+ mnt_context_set_mflags;
+ mnt_context_set_mountdata;
+ mnt_context_set_options;
+ mnt_context_set_options_pattern;
+ mnt_context_set_optsmode;
+ mnt_context_set_source;
+ mnt_context_set_syscall_status;
+ mnt_context_set_target;
+ mnt_context_set_user_mflags;
+ mnt_context_strerror;
+ mnt_context_umount;
+ mnt_copy_fs;
+ mnt_free_cache;
+ mnt_free_context;
+ mnt_free_fs;
+ mnt_free_iter;
+ mnt_free_lock;
+ mnt_free_mntent;
+ mnt_free_table;
+ mnt_free_update;
+ mnt_fs_append_attributes;
+ mnt_fs_append_options;
+ mnt_fs_get_attribute;
+ mnt_fs_get_attributes;
+ mnt_fs_get_bindsrc;
+ mnt_fs_get_devno;
+ mnt_fs_get_freq;
+ mnt_fs_get_fs_options;
+ mnt_fs_get_fstype;
+ mnt_fs_get_id;
+ mnt_fs_get_option;
+ mnt_fs_get_parent_id;
+ mnt_fs_get_passno;
+ mnt_fs_get_root;
+ mnt_fs_get_source;
+ mnt_fs_get_srcpath;
+ mnt_fs_get_tag;
+ mnt_fs_get_target;
+ mnt_fs_get_userdata;
+ mnt_fs_get_user_options;
+ mnt_fs_get_vfs_options;
+ mnt_fs_is_kernel;
+ mnt_fs_match_fstype;
+ mnt_fs_match_options;
+ mnt_fs_match_source;
+ mnt_fs_match_target;
+ mnt_fs_prepend_attributes;
+ mnt_fs_prepend_options;
+ mnt_fs_print_debug;
+ mnt_fs_set_attributes;
+ mnt_fs_set_bindsrc;
+ mnt_fs_set_freq;
+ mnt_fs_set_fstype;
+ mnt_fs_set_options;
+ mnt_fs_set_passno;
+ mnt_fs_set_root;
+ mnt_fs_set_source;
+ mnt_fs_set_target;
+ mnt_fs_set_userdata;
+ mnt_fs_strdup_options;
+ mnt_fs_to_mntent;
+ mnt_fstype_is_netfs;
+ mnt_fstype_is_pseudofs;
+ mnt_get_builtin_optmap;
+ mnt_get_fstab_path;
+ mnt_get_fstype;
+ mnt_get_library_version;
+ mnt_get_mtab_path;
+ mnt_has_regular_mtab;
+ mnt_init_debug;
+ mnt_iter_get_direction;
+ mnt_lock_file;
+ mnt_mangle;
+ mnt_match_fstype;
+ mnt_match_options;
+ mnt_new_cache;
+ mnt_new_context;
+ mnt_new_fs;
+ mnt_new_iter;
+ mnt_new_lock;
+ mnt_new_table;
+ mnt_new_table_from_dir;
+ mnt_new_table_from_file;
+ mnt_new_update;
+ mnt_optstr_append_option;
+ mnt_optstr_apply_flags;
+ mnt_optstr_get_flags;
+ mnt_optstr_get_option;
+ mnt_optstr_get_options;
+ mnt_optstr_next_option;
+ mnt_optstr_prepend_option;
+ mnt_optstr_remove_option;
+ mnt_optstr_set_option;
+ mnt_parse_version_string;
+ mnt_reset_context;
+ mnt_reset_fs;
+ mnt_reset_iter;
+ mnt_resolve_path;
+ mnt_resolve_spec;
+ mnt_resolve_tag;
+ mnt_split_optstr;
+ mnt_table_add_fs;
+ mnt_table_find_next_fs;
+ mnt_table_find_pair;
+ mnt_table_find_source;
+ mnt_table_find_srcpath;
+ mnt_table_find_tag;
+ mnt_table_find_target;
+ mnt_table_get_cache;
+ mnt_table_get_nents;
+ mnt_table_get_root_fs;
+ mnt_table_next_child_fs;
+ mnt_table_next_fs;
+ mnt_table_parse_file;
+ mnt_table_parse_fstab;
+ mnt_table_parse_mtab;
+ mnt_table_parse_stream;
+ mnt_table_remove_fs;
+ mnt_table_set_cache;
+ mnt_table_set_iter;
+ mnt_table_set_parser_errcb;
+ mnt_unlock_file;
+ mnt_unmangle;
+ mnt_update_force_rdonly;
+ mnt_update_get_filename;
+ mnt_update_get_fs;
+ mnt_update_get_mflags;
+ mnt_update_is_ready;
+ mnt_update_set_fs;
+ mnt_update_table;
+local:
+ *;
+};
+
+MOUNT_2.20 {
+global:
+ mnt_context_get_table;
+ mnt_context_is_fs_mounted;
+ mnt_context_next_mount;
+ mnt_context_set_tables_errcb;
+ mnt_diff_tables;
+ mnt_free_tabdiff;
+ mnt_fs_get_options;
+ mnt_lock_block_signals;
+ mnt_new_tabdiff;
+ mnt_pretty_path;
+ mnt_reset_table;
+ mnt_tabdiff_next_change;
+ mnt_table_is_fs_mounted;
+} MOUNT_2.19;
+
+MOUNT_2.21 {
+global:
+ mnt_context_enable_fork;
+ mnt_context_get_helper_status;
+ mnt_context_get_syscall_errno;
+ mnt_context_helper_executed;
+ mnt_context_is_child;
+ mnt_context_is_fork;
+ mnt_context_is_parent;
+ mnt_context_next_umount;
+ mnt_context_reset_status;
+ mnt_context_set_passwd_cb;
+ mnt_context_syscall_called;
+ mnt_context_wait_for_children;
+ mnt_fs_is_netfs;
+ mnt_fs_is_pseudofs;
+ mnt_fs_is_swaparea;
+ mnt_get_library_features;
+ mnt_table_parse_dir;
+} MOUNT_2.20;
+
+MOUNT_2.22 {
+global:
+ mnt_context_disable_swapmatch;
+ mnt_context_get_options;
+ mnt_context_is_loopdel;
+ mnt_context_is_nocanonicalize;
+ mnt_context_is_nohelpers;
+ mnt_context_is_swapmatch;
+ mnt_context_tab_applied;
+ mnt_fs_get_priority;
+ mnt_fs_get_size;
+ mnt_fs_get_swaptype;
+ mnt_fs_get_tid;
+ mnt_fs_get_usedsize;
+ mnt_fs_streq_srcpath;
+ mnt_fs_streq_target;
+ mnt_get_mountpoint;
+ mnt_get_swaps_path;
+ mnt_optstr_deduplicate_option;
+ mnt_table_find_devno;
+ mnt_table_parse_swaps;
+} MOUNT_2.21;
diff --git a/libmount/src/lock.c b/libmount/src/lock.c
new file mode 100644
index 0000000..9f04725
--- /dev/null
+++ b/libmount/src/lock.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: lock
+ * @title: Locking
+ * @short_description: locking methods for /etc/mtab or another libmount files
+ *
+ * The mtab lock is backwardly compatible with the standard linux /etc/mtab
+ * locking. Note, it's necessary to use the same locking schema in all
+ * application that access the file.
+ */
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/file.h>
+
+#include "pathnames.h"
+#include "mountP.h"
+
+/*
+ * lock handler
+ */
+struct libmnt_lock {
+ char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
+ char *linkfile; /* path to link file (e.g. /etc/mtab~.<id>) */
+ int lockfile_fd; /* lock file descriptor */
+
+ unsigned int locked :1, /* do we own the lock? */
+ sigblock :1, /* block signals when locked */
+ simplelock :1; /* use flock rather than normal mtab lock */
+
+ sigset_t oldsigmask;
+};
+
+
+/**
+ * mnt_new_lock:
+ * @datafile: the file that should be covered by the lock
+ * @id: unique linkfile identifier or 0 (default is getpid())
+ *
+ * Returns: newly allocated lock handler or NULL on case of error.
+ */
+struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
+{
+ struct libmnt_lock *ml = NULL;
+ char *lo = NULL, *ln = NULL;
+ size_t losz;
+
+ if (!datafile)
+ return NULL;
+
+ /* for flock we use "foo.lock, for mtab "foo~"
+ */
+ losz = strlen(datafile) + sizeof(".lock");
+ lo = malloc(losz);
+ if (!lo)
+ goto err;
+
+ /* default is mtab~ lock */
+ snprintf(lo, losz, "%s~", datafile);
+
+ if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
+ ln = NULL;
+ goto err;
+ }
+ ml = calloc(1, sizeof(*ml) );
+ if (!ml)
+ goto err;
+
+ ml->lockfile_fd = -1;
+ ml->linkfile = ln;
+ ml->lockfile = lo;
+
+ DBG(LOCKS, mnt_debug_h(ml, "alloc: default linkfile=%s, lockfile=%s", ln, lo));
+ return ml;
+err:
+ free(lo);
+ free(ln);
+ free(ml);
+ return NULL;
+}
+
+
+/**
+ * mnt_free_lock:
+ * @ml: struct libmnt_lock handler
+ *
+ * Deallocates mnt_lock.
+ */
+void mnt_free_lock(struct libmnt_lock *ml)
+{
+ if (!ml)
+ return;
+ DBG(LOCKS, mnt_debug_h(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
+ free(ml->lockfile);
+ free(ml->linkfile);
+ free(ml);
+}
+
+/**
+ * mnt_lock_block_signals:
+ * @ml: struct libmnt_lock handler
+ * @enable: TRUE/FALSE
+ *
+ * Block/unblock signals when the lock is locked, the signals are not blocked
+ * by default.
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int mnt_lock_block_signals(struct libmnt_lock *ml, int enable)
+{
+ if (!ml)
+ return -EINVAL;
+ DBG(LOCKS, mnt_debug_h(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED"));
+ ml->sigblock = enable ? 1 : 0;
+ return 0;
+}
+
+/* don't export this to API
+ */
+int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable)
+{
+ size_t sz;
+
+ if (!ml)
+ return -EINVAL;
+
+ assert(ml->lockfile);
+
+ DBG(LOCKS, mnt_debug_h(ml, "flock: %s", enable ? "ENABLED" : "DISABLED"));
+ ml->simplelock = enable ? 1 : 0;
+
+ sz = strlen(ml->lockfile);
+ assert(sz);
+
+ if (sz < 1)
+ return -EINVAL;
+
+ /* Change lock name:
+ *
+ * flock: "<name>.lock"
+ * mtab lock: "<name>~"
+ */
+ if (ml->simplelock && endswith(ml->lockfile, "~"))
+ memcpy(ml->lockfile + sz - 1, ".lock", 6);
+
+ else if (!ml->simplelock && endswith(ml->lockfile, ".lock"))
+ memcpy(ml->lockfile + sz - 5, "~", 2);
+
+ DBG(LOCKS, mnt_debug_h(ml, "new lock filename: '%s'", ml->lockfile));
+ return 0;
+}
+
+/*
+ * Returns path to lockfile.
+ */
+static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
+{
+ return ml ? ml->lockfile : NULL;
+}
+
+/*
+ * Note that the filename is generated by mnt_new_lock() and depends on
+ * getpid() or 'id' argument of the mnt_new_lock() function.
+ *
+ * Returns: unique (per process/thread) path to linkfile.
+ */
+static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
+{
+ return ml ? ml->linkfile : NULL;
+}
+
+/*
+ * Simple flocking
+ */
+static void unlock_simplelock(struct libmnt_lock *ml)
+{
+ assert(ml);
+ assert(ml->simplelock);
+
+ if (ml->lockfile_fd >= 0) {
+ DBG(LOCKS, mnt_debug_h(ml, "%s: unflocking",
+ mnt_lock_get_lockfile(ml)));
+ close(ml->lockfile_fd);
+ }
+}
+
+static int lock_simplelock(struct libmnt_lock *ml)
+{
+ const char *lfile;
+ int rc;
+
+ assert(ml);
+ assert(ml->simplelock);
+
+ lfile = mnt_lock_get_lockfile(ml);
+
+ DBG(LOCKS, mnt_debug_h(ml, "%s: locking", lfile));
+
+ if (ml->sigblock) {
+ sigset_t sigs;
+ sigemptyset(&ml->oldsigmask);
+ sigfillset(&sigs);
+ sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
+ }
+
+ ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC,
+ S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ if (ml->lockfile_fd < 0) {
+ rc = -errno;
+ goto err;
+ }
+
+ while (flock(ml->lockfile_fd, LOCK_EX) < 0) {
+ int errsv;
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ errsv = errno;
+ close(ml->lockfile_fd);
+ ml->lockfile_fd = -1;
+ rc = -errsv;
+ goto err;
+ }
+ ml->locked = 1;
+ return 0;
+err:
+ if (ml->sigblock)
+ sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
+ return rc;
+}
+
+/*
+ * traditional mtab locking
+ */
+
+static void mnt_lockalrm_handler(int sig __attribute__((__unused__)))
+{
+ /* do nothing, say nothing, be nothing */
+}
+
+/*
+ * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
+ * fcntl() to avoid never ending waiting.
+ *
+ * Returns: 0 on success, 1 on timeout, -errno on error.
+ */
+static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
+{
+ struct timeval now;
+ struct sigaction sa, osa;
+ int ret = 0;
+
+ gettimeofday(&now, NULL);
+
+ if (now.tv_sec >= maxtime)
+ return 1; /* timeout */
+
+ /* setup ALARM handler -- we don't want to wait forever */
+ sa.sa_flags = 0;
+ sa.sa_handler = mnt_lockalrm_handler;
+ sigfillset (&sa.sa_mask);
+
+ sigaction(SIGALRM, &sa, &osa);
+
+ DBG(LOCKS, mnt_debug_h(ml, "(%d) waiting for F_SETLKW", getpid()));
+
+ alarm(maxtime - now.tv_sec);
+ if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1)
+ ret = errno == EINTR ? 1 : -errno;
+ alarm(0);
+
+ /* restore old sigaction */
+ sigaction(SIGALRM, &osa, NULL);
+
+ DBG(LOCKS, mnt_debug_h(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d",
+ getpid(), ret));
+ return ret;
+}
+
+/*
+ * Create the mtab lock file.
+ *
+ * The old code here used flock on a lock file /etc/mtab~ and deleted
+ * this lock file afterwards. However, as rgooch remarks, that has a
+ * race: a second mount may be waiting on the lock and proceed as
+ * soon as the lock file is deleted by the first mount, and immediately
+ * afterwards a third mount comes, creates a new /etc/mtab~, applies
+ * flock to that, and also proceeds, so that the second and third mount
+ * now both are scribbling in /etc/mtab.
+ *
+ * The new code uses a link() instead of a creat(), where we proceed
+ * only if it was us that created the lock, and hence we always have
+ * to delete the lock afterwards. Now the use of flock() is in principle
+ * superfluous, but avoids an arbitrary sleep().
+ *
+ * Where does the link point to? Obvious choices are mtab and mtab~~.
+ * HJLu points out that the latter leads to races. Right now we use
+ * mtab~.pid instead.
+ *
+ *
+ * The original mount locking code has used sleep(1) between attempts and
+ * maximal number of attempts has been 5.
+ *
+ * There was very small number of attempts and extremely long waiting (1s)
+ * that is useless on machines with large number of mount processes.
+ *
+ * Now we wait few thousand microseconds between attempts and we have a global
+ * time limit (30s) rather than limit for number of attempts. The advantage
+ * is that this method also counts time which we spend in fcntl(F_SETLKW) and
+ * number of attempts is not restricted.
+ * -- kzak@redhat.com [Mar-2007]
+ *
+ *
+ * This mtab locking code has been refactored and moved to libmount. The mtab
+ * locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and
+ * backwardly compatible code.
+ *
+ * Don't forget that this code has to be compatible with 3rd party mounts
+ * (/sbin/mount.foo) and has to work with NFS.
+ * -- kzak@redhat.com [May-2009]
+ */
+
+/* maximum seconds between first and last attempt */
+#define MOUNTLOCK_MAXTIME 30
+
+/* sleep time (in microseconds, max=999999) between attempts */
+#define MOUNTLOCK_WAITTIME 5000
+
+static void unlock_mtab(struct libmnt_lock *ml)
+{
+ if (!ml)
+ return;
+
+ if (!ml->locked && ml->lockfile && ml->linkfile)
+ {
+ /* We have (probably) all files, but we don't own the lock,
+ * Really? Check it! Maybe ml->locked wasn't set properly
+ * because code was interrupted by signal. Paranoia? Yes.
+ *
+ * We own the lock when linkfile == lockfile.
+ */
+ struct stat lo, li;
+
+ if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) &&
+ lo.st_dev == li.st_dev && lo.st_ino == li.st_ino)
+ ml->locked = 1;
+ }
+
+ if (ml->linkfile)
+ unlink(ml->linkfile);
+ if (ml->lockfile_fd >= 0)
+ close(ml->lockfile_fd);
+ if (ml->locked && ml->lockfile) {
+ unlink(ml->lockfile);
+ DBG(LOCKS, mnt_debug_h(ml, "unlink %s", ml->lockfile));
+ }
+}
+
+static int lock_mtab(struct libmnt_lock *ml)
+{
+ int i, rc = -1;
+ struct timespec waittime;
+ struct timeval maxtime;
+ const char *lockfile, *linkfile;
+
+ if (!ml)
+ return -EINVAL;
+ if (ml->locked)
+ return 0;
+
+ lockfile = mnt_lock_get_lockfile(ml);
+ if (!lockfile)
+ return -EINVAL;
+ linkfile = mnt_lock_get_linkfile(ml);
+ if (!linkfile)
+ return -EINVAL;
+
+ if (ml->sigblock) {
+ /*
+ * Block all signals when locked, mnt_unlock_file() will
+ * restore the old mask.
+ */
+ sigset_t sigs;
+
+ sigemptyset(&ml->oldsigmask);
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGTRAP);
+ sigdelset(&sigs, SIGALRM);
+ sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
+ }
+
+ i = open(linkfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
+ if (i < 0) {
+ /* linkfile does not exist (as a file) and we cannot create it.
+ * Read-only or full filesystem? Too many files open in the system?
+ */
+ if (errno > 0)
+ rc = -errno;
+ goto failed;
+ }
+ close(i);
+
+ gettimeofday(&maxtime, NULL);
+ maxtime.tv_sec += MOUNTLOCK_MAXTIME;
+
+ waittime.tv_sec = 0;
+ waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
+
+ /* Repeat until it was us who made the link */
+ while (!ml->locked) {
+ struct timeval now;
+ struct flock flock;
+ int j;
+
+ j = link(linkfile, lockfile);
+ if (j == 0)
+ ml->locked = 1;
+
+ if (j < 0 && errno != EEXIST) {
+ if (errno > 0)
+ rc = -errno;
+ goto failed;
+ }
+ ml->lockfile_fd = open(lockfile, O_WRONLY);
+
+ if (ml->lockfile_fd < 0) {
+ /* Strange... Maybe the file was just deleted? */
+ int errsv = errno;
+ gettimeofday(&now, NULL);
+ if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) {
+ ml->locked = 0;
+ continue;
+ }
+ if (errsv > 0)
+ rc = -errsv;
+ goto failed;
+ }
+
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ flock.l_len = 0;
+
+ if (ml->locked) {
+ /* We made the link. Now claim the lock. */
+ if (fcntl (ml->lockfile_fd, F_SETLK, &flock) == -1) {
+ DBG(LOCKS, mnt_debug_h(ml,
+ "%s: can't F_SETLK lockfile, errno=%d\n",
+ lockfile, errno));
+ /* proceed, since it was us who created the lockfile anyway */
+ }
+ break;
+ } else {
+ /* Someone else made the link. Wait. */
+ int err = mnt_wait_mtab_lock(ml, &flock, maxtime.tv_sec);
+
+ if (err == 1) {
+ DBG(LOCKS, mnt_debug_h(ml,
+ "%s: can't create link: time out (perhaps "
+ "there is a stale lock file?)", lockfile));
+ rc = -ETIMEDOUT;
+ goto failed;
+
+ } else if (err < 0) {
+ rc = err;
+ goto failed;
+ }
+ nanosleep(&waittime, NULL);
+ close(ml->lockfile_fd);
+ ml->lockfile_fd = -1;
+ }
+ }
+ DBG(LOCKS, mnt_debug_h(ml, "%s: (%d) successfully locked",
+ lockfile, getpid()));
+ unlink(linkfile);
+ return 0;
+
+failed:
+ mnt_unlock_file(ml);
+ return rc;
+}
+
+
+/**
+ * mnt_lock_file
+ * @ml: pointer to struct libmnt_lock instance
+ *
+ * Creates lock file (e.g. /etc/mtab~). Note that this function may
+ * use alarm().
+ *
+ * Your application has to always call mnt_unlock_file() before exit.
+ *
+ * Traditional mtab locking scheme:
+ *
+ * 1. create linkfile (e.g. /etc/mtab~.$PID)
+ * 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~)
+ * 3. a) link() success: setups F_SETLK lock (see fcnlt(2))
+ * b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2.
+ *
+ * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
+ * of stale lock file).
+ */
+int mnt_lock_file(struct libmnt_lock *ml)
+{
+ if (!ml)
+ return -EINVAL;
+
+ if (ml->simplelock)
+ return lock_simplelock(ml);
+
+ return lock_mtab(ml);
+}
+
+/**
+ * mnt_unlock_file:
+ * @ml: lock struct
+ *
+ * Unlocks the file. The function could be called independently on the
+ * lock status (for example from exit(3)).
+ */
+void mnt_unlock_file(struct libmnt_lock *ml)
+{
+ if (!ml)
+ return;
+
+ DBG(LOCKS, mnt_debug_h(ml, "(%d) %s", getpid(),
+ ml->locked ? "unlocking" : "cleaning"));
+
+ if (ml->simplelock)
+ unlock_simplelock(ml);
+ else
+ unlock_mtab(ml);
+
+ ml->locked = 0;
+ ml->lockfile_fd = -1;
+
+ if (ml->sigblock) {
+ DBG(LOCKS, mnt_debug_h(ml, "restoring sigmask"));
+ sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
+ }
+}
+
+#ifdef TEST_PROGRAM
+
+struct libmnt_lock *lock;
+
+/*
+ * read number from @filename, increment the number and
+ * write the number back to the file
+ */
+void increment_data(const char *filename, int verbose, int loopno)
+{
+ long num;
+ FILE *f;
+ char buf[256];
+
+ if (!(f = fopen(filename, "r")))
+ err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
+
+ if (!fgets(buf, sizeof(buf), f))
+ err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename);
+
+ fclose(f);
+ num = atol(buf) + 1;
+
+ if (!(f = fopen(filename, "w")))
+ err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
+
+ fprintf(f, "%ld", num);
+ fclose(f);
+
+ if (verbose)
+ fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
+ filename, num - 1, num, loopno);
+}
+
+void clean_lock(void)
+{
+ if (!lock)
+ return;
+ mnt_unlock_file(lock);
+ mnt_free_lock(lock);
+}
+
+void __attribute__((__noreturn__)) sig_handler(int sig)
+{
+ errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
+}
+
+int test_lock(struct libmnt_test *ts, int argc, char *argv[])
+{
+ time_t synctime = 0;
+ unsigned int usecs;
+ struct timeval tv;
+ const char *datafile = NULL;
+ int verbose = 0, loops = 0, l, idx = 1;
+
+ if (argc < 3)
+ return -EINVAL;
+
+ if (strcmp(argv[idx], "--synctime") == 0) {
+ synctime = (time_t) atol(argv[idx + 1]);
+ idx += 2;
+ }
+ if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
+ verbose = 1;
+ idx++;
+ }
+
+ if (idx < argc)
+ datafile = argv[idx++];
+ if (idx < argc)
+ loops = atoi(argv[idx++]);
+
+ if (!datafile || !loops)
+ return -EINVAL;
+
+ if (verbose)
+ fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
+ getpid(), (int) synctime, datafile, loops);
+
+ atexit(clean_lock);
+
+ /* be paranoid and call exit() (=clean_lock()) for all signals */
+ {
+ int sig = 0;
+ struct sigaction sa;
+
+ sa.sa_handler = sig_handler;
+ sa.sa_flags = 0;
+ sigfillset(&sa.sa_mask);
+
+ while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
+ sigaction (sig, &sa, (struct sigaction *) 0);
+ }
+
+ /* start the test in exactly defined time */
+ if (synctime) {
+ gettimeofday(&tv, NULL);
+ if (synctime && synctime - tv.tv_sec > 1) {
+ usecs = ((synctime - tv.tv_sec) * 1000000UL) -
+ (1000000UL - tv.tv_usec);
+ usleep(usecs);
+ }
+ }
+
+ for (l = 0; l < loops; l++) {
+ lock = mnt_new_lock(datafile, 0);
+ if (!lock)
+ return -1;
+
+ if (mnt_lock_file(lock) != 0) {
+ fprintf(stderr, "%d: failed to lock %s file\n",
+ getpid(), datafile);
+ return -1;
+ }
+
+ increment_data(datafile, verbose, l);
+
+ mnt_unlock_file(lock);
+ mnt_free_lock(lock);
+ lock = NULL;
+
+ /* The mount command usually finish after mtab update. We
+ * simulate this via short sleep -- it's also enough to make
+ * concurrent processes happy.
+ */
+ if (synctime)
+ usleep(25000);
+ }
+
+ return 0;
+}
+
+/*
+ * Note that this test should be executed from a script that creates many
+ * parallel processes, otherwise this test does not make sense.
+ */
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
+ "increment a number in datafile" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
new file mode 100644
index 0000000..0ef073a
--- /dev/null
+++ b/libmount/src/mountP.h
@@ -0,0 +1,402 @@
+/*
+ * mountP.h - private library header file
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifndef _LIBMOUNT_PRIVATE_H
+#define _LIBMOUNT_PRIVATE_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "list.h"
+#include "libmount.h"
+
+/* features */
+#define CONFIG_LIBMOUNT_ASSERT
+#define CONFIG_LIBMOUNT_DEBUG
+
+#ifdef CONFIG_LIBMOUNT_ASSERT
+#include <assert.h>
+#endif
+
+/*
+ * Debug
+ */
+#if defined(TEST_PROGRAM) && !defined(LIBMOUNT_DEBUG)
+#define CONFIG_LIBMOUNT_DEBUG
+#endif
+
+#define MNT_DEBUG_INIT (1 << 1)
+#define MNT_DEBUG_CACHE (1 << 2)
+#define MNT_DEBUG_OPTIONS (1 << 3)
+#define MNT_DEBUG_LOCKS (1 << 4)
+#define MNT_DEBUG_TAB (1 << 5)
+#define MNT_DEBUG_FS (1 << 6)
+#define MNT_DEBUG_OPTS (1 << 7)
+#define MNT_DEBUG_UPDATE (1 << 8)
+#define MNT_DEBUG_UTILS (1 << 9)
+#define MNT_DEBUG_CXT (1 << 10)
+#define MNT_DEBUG_DIFF (1 << 11)
+#define MNT_DEBUG_ALL 0xFFFF
+
+#ifdef CONFIG_LIBMOUNT_DEBUG
+# include <stdio.h>
+# include <stdarg.h>
+
+# define ON_DBG(m, x) do { \
+ if ((MNT_DEBUG_ ## m) & libmount_debug_mask) { \
+ x; \
+ } \
+ } while (0)
+
+# define DBG(m, x) do { \
+ if ((MNT_DEBUG_ ## m) & libmount_debug_mask) { \
+ fprintf(stderr, "%d: libmount: %8s: ", getpid(), # m); \
+ x; \
+ } \
+ } while (0)
+
+# define DBG_FLUSH do { \
+ if (libmount_debug_mask && \
+ libmount_debug_mask != MNT_DEBUG_INIT) \
+ fflush(stderr); \
+ } while(0)
+
+extern int libmount_debug_mask;
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+mnt_debug(const char *mesg, ...)
+{
+ va_list ap;
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+mnt_debug_h(void *handler, const char *mesg, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "[%p]: ", handler);
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+#else /* !CONFIG_LIBMOUNT_DEBUG */
+# define ON_DBG(m,x) do { ; } while (0)
+# define DBG(m,x) do { ; } while (0)
+# define DBG_FLUSH do { ; } while(0)
+#endif
+
+/* extension for files in the directory */
+#define MNT_MNTTABDIR_EXT ".fstab"
+
+/* library private paths */
+#define MNT_RUNTIME_TOPDIR "/run"
+#define MNT_RUNTIME_TOPDIR_OLD "/dev"
+
+#define MNT_PATH_UTAB MNT_RUNTIME_TOPDIR "/mount/utab"
+#define MNT_PATH_UTAB_OLD MNT_RUNTIME_TOPDIR_OLD "/.mount/utab"
+
+#define MNT_UTAB_HEADER "# libmount utab file\n"
+
+#ifdef TEST_PROGRAM
+struct libmnt_test {
+ const char *name;
+ int (*body)(struct libmnt_test *ts, int argc, char *argv[]);
+ const char *usage;
+};
+
+/* test.c */
+extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]);
+#endif
+
+/* utils.c */
+extern int endswith(const char *s, const char *sx);
+extern int startswith(const char *s, const char *sx);
+
+extern int mnt_is_readonly(const char *path);
+
+extern int mnt_parse_offset(const char *str, size_t len, uintmax_t *res);
+
+extern int mnt_chdir_to_parent(const char *target, char **filename);
+extern char *mnt_get_username(const uid_t uid);
+extern int mnt_get_uid(const char *username, uid_t *uid);
+extern int mnt_get_gid(const char *groupname, gid_t *gid);
+extern int mnt_in_group(gid_t gid);
+
+extern char *mnt_get_fs_root(const char *path, const char *mountpoint);
+extern int mnt_open_uniq_filename(const char *filename, char **name);
+extern int mnt_has_regular_utab(const char **utab, int *writable);
+extern const char *mnt_get_utab_path(void);
+
+extern int mnt_get_filesystems(char ***filesystems, const char *pattern);
+extern void mnt_free_filesystems(char **filesystems);
+
+/* tab.c */
+extern struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
+ struct libmnt_fs *fs,
+ unsigned long mountflags,
+ char **fsroot);
+
+/*
+ * Generic iterator
+ */
+struct libmnt_iter {
+ struct list_head *p; /* current position */
+ struct list_head *head; /* start position */
+ int direction; /* MNT_ITER_{FOR,BACK}WARD */
+};
+
+#define IS_ITER_FORWARD(_i) ((_i)->direction == MNT_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i) ((_i)->direction == MNT_ITER_BACKWARD)
+
+#define MNT_ITER_INIT(itr, list) \
+ do { \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (list)->next : (list)->prev; \
+ (itr)->head = (list); \
+ } while(0)
+
+#define MNT_ITER_ITERATE(itr, res, restype, member) \
+ do { \
+ res = list_entry((itr)->p, restype, member); \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (itr)->p->next : (itr)->p->prev; \
+ } while(0)
+
+
+/*
+ * This struct represents one entry in mtab/fstab/mountinfo file.
+ * (note that fstab[1] means the first column from fstab, and so on...)
+ */
+struct libmnt_fs {
+ struct list_head ents;
+
+ int id; /* mountinfo[1]: ID */
+ int parent; /* mountinfo[2]: parent */
+ dev_t devno; /* mountinfo[3]: st_dev */
+
+ char *bindsrc; /* utab, full path from fstab[1] for bind mounts */
+
+ char *source; /* fstab[1], mountinfo[10], swaps[1]:
+ * source dev, file, dir or TAG */
+ char *tagname; /* fstab[1]: tag name - "LABEL", "UUID", ..*/
+ char *tagval; /* tag value */
+
+ char *root; /* mountinfo[4]: root of the mount within the FS */
+ char *target; /* mountinfo[5], fstab[2]: mountpoint */
+ char *fstype; /* mountinfo[9], fstab[3]: filesystem type */
+
+ char *optstr; /* fstab[4], merged options */
+ char *vfs_optstr; /* mountinfo[6]: fs-independent (VFS) options */
+ char *fs_optstr; /* mountinfo[11]: fs-dependent options */
+ char *user_optstr; /* userspace mount options */
+ char *attrs; /* mount attributes */
+
+ int freq; /* fstab[5]: dump frequency in days */
+ int passno; /* fstab[6]: pass number on parallel fsck */
+
+ /* /proc/swaps */
+ char *swaptype; /* swaps[2]: device type (partition, file, ...) */
+ off_t size; /* swaps[3]: swaparea size */
+ off_t usedsize; /* swaps[4]: used size */
+ int priority; /* swaps[5]: swap priority */
+
+ int flags; /* MNT_FS_* flags */
+ pid_t tid; /* /proc/<tid>/mountinfo otherwise zero */
+
+ void *userdata; /* library independent data */
+};
+
+/*
+ * fs flags
+ */
+#define MNT_FS_PSEUDO (1 << 1) /* pseudo filesystem */
+#define MNT_FS_NET (1 << 2) /* network filesystem */
+#define MNT_FS_SWAP (1 << 3) /* swap device */
+#define MNT_FS_KERNEL (1 << 4) /* data from /proc/{mounts,self/mountinfo} */
+#define MNT_FS_MERGED (1 << 5) /* already merged data from /run/mount/utab */
+
+#define mnt_fs_is_regular(_f) (!(mnt_fs_is_pseudofs(_f) \
+ || mnt_fs_is_netfs(_f) \
+ || mnt_fs_is_swaparea(_f)))
+
+/*
+ * mtab/fstab/mountinfo file
+ */
+struct libmnt_table {
+ int fmt; /* MNT_FMT_* file format */
+ int nents; /* number of valid entries */
+
+ struct libmnt_cache *cache; /* canonicalized paths/tags cache */
+
+ int (*errcb)(struct libmnt_table *tb,
+ const char *filename, int line);
+
+ struct list_head ents; /* list of entries (libmnt_fs) */
+};
+
+extern struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt);
+
+/*
+ * Tab file format
+ */
+enum {
+ MNT_FMT_GUESS,
+ MNT_FMT_FSTAB, /* /etc/{fs,m}tab */
+ MNT_FMT_MTAB = MNT_FMT_FSTAB, /* alias */
+ MNT_FMT_MOUNTINFO, /* /proc/#/mountinfo */
+ MNT_FMT_UTAB, /* /run/mount/utab */
+ MNT_FMT_SWAPS /* /proc/swaps */
+};
+
+
+/*
+ * Mount context -- high-level API
+ */
+struct libmnt_context
+{
+ int action; /* MNT_ACT_{MOUNT,UMOUNT} */
+ int restricted; /* root or not? */
+
+ char *fstype_pattern; /* for mnt_match_fstype() */
+ char *optstr_pattern; /* for mnt_match_options() */
+
+ struct libmnt_fs *fs; /* filesystem description (type, mountpoint, device, ...) */
+
+ struct libmnt_table *fstab; /* fstab (or mtab for some remounts) entries */
+ struct libmnt_table *mtab; /* mtab entries */
+
+ int (*table_errcb)(struct libmnt_table *tb, /* callback for libmnt_table structs */
+ const char *filename, int line);
+
+ char *(*pwd_get_cb)(struct libmnt_context *); /* get encryption password */
+ void (*pwd_release_cb)(struct libmnt_context *, char *); /* release password */
+
+ 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 */
+
+ unsigned long user_mountflags; /* MNT_MS_* (loop=, user=, ...) */
+
+ struct libmnt_cache *cache; /* paths cache */
+ struct libmnt_lock *lock; /* mtab lock */
+ struct libmnt_update *update;/* mtab/utab update */
+
+ const char *mtab_path; /* path to mtab */
+ int mtab_writable; /* is mtab writable */
+
+ const char *utab_path; /* path to utab */
+ int utab_writable; /* is utab writable */
+
+ int flags; /* private context flags */
+ int ambi; /* libblkid returns ambivalent result */
+
+ char *helper; /* name of the used /sbin/[u]mount.<type> helper */
+ int helper_status; /* helper wait(2) status */
+ int helper_exec_status; /* 1: not called yet, 0: success, <0: -errno */
+
+ char *orig_user; /* original (non-fixed) user= option */
+
+ pid_t *children; /* "mount -a --fork" PIDs */
+ int nchildren; /* number of children */
+ pid_t pid; /* 0=parent; PID=child */
+
+
+ int syscall_status; /* 1: not called yet, 0: success, <0: -errno */
+};
+
+/* flags */
+#define MNT_FL_NOMTAB (1 << 1)
+#define MNT_FL_FAKE (1 << 2)
+#define MNT_FL_SLOPPY (1 << 3)
+#define MNT_FL_VERBOSE (1 << 4)
+#define MNT_FL_NOHELPERS (1 << 5)
+#define MNT_FL_LOOPDEL (1 << 6)
+#define MNT_FL_LAZY (1 << 7)
+#define MNT_FL_FORCE (1 << 8)
+#define MNT_FL_NOCANONICALIZE (1 << 9)
+#define MNT_FL_RDONLY_UMOUNT (1 << 11) /* remount,ro after EBUSY umount(2) */
+#define MNT_FL_FORK (1 << 12)
+#define MNT_FL_NOSWAPMATCH (1 << 13)
+
+#define MNT_FL_EXTERN_FS (1 << 15) /* cxt->fs is not private */
+#define MNT_FL_EXTERN_FSTAB (1 << 16) /* cxt->fstab is not private */
+#define MNT_FL_EXTERN_CACHE (1 << 17) /* cxt->cache is not private */
+
+#define MNT_FL_MOUNTDATA (1 << 20)
+#define MNT_FL_TAB_APPLIED (1 << 21) /* mtab/fstab merged to cxt->fs */
+#define MNT_FL_MOUNTFLAGS_MERGED (1 << 22) /* MS_* flags was read from optstr */
+#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 */
+#define MNT_FL_MOUNTOPTS_FIXED (1 << 27)
+
+/* default flags */
+#define MNT_FL_DEFAULT 0
+
+/* lock.c */
+extern int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable);
+
+/* optmap.c */
+extern const struct libmnt_optmap *mnt_optmap_get_entry(
+ struct libmnt_optmap const **maps,
+ int nmaps, const char *name,
+ size_t namelen,
+ const struct libmnt_optmap **mapent);
+
+/* optstr.c */
+extern int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end);
+extern int mnt_optstr_fix_gid(char **optstr, char *value, size_t valsz, char **next);
+extern int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next);
+extern int mnt_optstr_fix_secontext(char **optstr, char *value, size_t valsz, char **next);
+extern int mnt_optstr_fix_user(char **optstr);
+
+/* fs.c */
+extern struct libmnt_fs *mnt_copy_mtab_fs(const struct libmnt_fs *fs);
+extern int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source);
+extern int __mnt_fs_set_fstype_ptr(struct libmnt_fs *fs, char *fstype);
+
+/* context.c */
+extern int mnt_context_prepare_srcpath(struct libmnt_context *cxt);
+extern int mnt_context_prepare_target(struct libmnt_context *cxt);
+extern int mnt_context_guess_fstype(struct libmnt_context *cxt);
+extern int mnt_context_prepare_helper(struct libmnt_context *cxt,
+ const char *name, const char *type);
+extern int mnt_context_prepare_update(struct libmnt_context *cxt);
+extern int mnt_context_merge_mflags(struct libmnt_context *cxt);
+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);
+
+extern int mnt_fork_context(struct libmnt_context *cxt);
+
+/* tab_update.c */
+extern int mnt_update_set_filename(struct libmnt_update *upd,
+ const char *filename, int userspace_only);
+
+#endif /* _LIBMOUNT_PRIVATE_H */
diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c
new file mode 100644
index 0000000..68217ce
--- /dev/null
+++ b/libmount/src/optmap.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: optmap
+ * @title: Option maps
+ * @short_description: description for mount options
+ *
+ * The mount(2) linux syscall uses two arguments for mount options:
+ *
+ * @mountflags: (see MS_* macros in linux/fs.h)
+ *
+ * @mountdata: (usully a comma separated string of options)
+ *
+ * The libmount uses options-map(s) to describe mount options.
+ *
+ * The option description (map entry) includes:
+ *
+ * @name: and argument name
+ *
+ * @id: (in the map unique identifier or a mountflags, e.g MS_RDONLY)
+ *
+ * @mask: (MNT_INVERT, MNT_NOMTAB)
+ *
+ * The option argument value is defined by:
+ *
+ * "=" -- required argument, e.g "comment="
+ *
+ * "[=]" -- optional argument, e.g. "loop[=]"
+ *
+ * Example:
+ *
+ * <informalexample>
+ * <programlisting>
+ * #define MY_MS_FOO (1 << 1)
+ * #define MY_MS_BAR (1 << 2)
+ *
+ * libmnt_optmap myoptions[] = {
+ * { "foo", MY_MS_FOO },
+ * { "nofoo", MY_MS_FOO | MNT_INVERT },
+ * { "bar=", MY_MS_BAR },
+ * { NULL }
+ * };
+ * </programlisting>
+ * </informalexample>
+ *
+ * The libmount defines two basic built-in options maps:
+ *
+ * @MNT_LINUX_MAP: fs-independent kernel mount options (usually MS_* flags)
+ *
+ * @MNT_USERSPACE_MAP: userspace specific mount options (e.g. "user", "loop")
+ *
+ * For more details about option map struct see "struct mnt_optmap" in
+ * mount/mount.h.
+ */
+#include "mountP.h"
+
+/*
+ * fs-independent mount flags (built-in MNT_LINUX_MAP)
+ */
+static const struct libmnt_optmap linux_flags_map[] =
+{
+ { "ro", MS_RDONLY }, /* read-only */
+ { "rw", MS_RDONLY, MNT_INVERT }, /* read-write */
+ { "exec", MS_NOEXEC, MNT_INVERT }, /* permit execution of binaries */
+ { "noexec", MS_NOEXEC }, /* don't execute binaries */
+ { "suid", MS_NOSUID, MNT_INVERT }, /* honor suid executables */
+ { "nosuid", MS_NOSUID }, /* don't honor suid executables */
+ { "dev", MS_NODEV, MNT_INVERT }, /* interpret device files */
+ { "nodev", MS_NODEV }, /* don't interpret devices */
+
+ { "sync", MS_SYNCHRONOUS }, /* synchronous I/O */
+ { "async", MS_SYNCHRONOUS, MNT_INVERT },/* asynchronous I/O */
+
+ { "dirsync", MS_DIRSYNC }, /* synchronous directory modifications */
+ { "remount", MS_REMOUNT, MNT_NOMTAB }, /* alter flags of mounted FS */
+ { "bind", MS_BIND }, /* Remount part of tree elsewhere */
+ { "rbind", MS_BIND | MS_REC }, /* Idem, plus mounted subtrees */
+#ifdef MS_NOSUB
+ { "sub", MS_NOSUB, MNT_INVERT }, /* allow submounts */
+ { "nosub", MS_NOSUB }, /* don't allow submounts */
+#endif
+#ifdef MS_SILENT
+ { "silent", MS_SILENT }, /* be quiet */
+ { "loud", MS_SILENT, MNT_INVERT }, /* print out messages. */
+#endif
+#ifdef MS_MANDLOCK
+ { "mand", MS_MANDLOCK }, /* Allow mandatory locks on this FS */
+ { "nomand", MS_MANDLOCK, MNT_INVERT }, /* Forbid mandatory locks on this FS */
+#endif
+#ifdef MS_NOATIME
+ { "atime", MS_NOATIME, MNT_INVERT }, /* Update access time */
+ { "noatime", MS_NOATIME }, /* Do not update access time */
+#endif
+#ifdef MS_I_VERSION
+ { "iversion", MS_I_VERSION }, /* Update inode I_version time */
+ { "noiversion", MS_I_VERSION, MNT_INVERT},/* Don't update inode I_version time */
+#endif
+#ifdef MS_NODIRATIME
+ { "diratime", MS_NODIRATIME, MNT_INVERT }, /* Update dir access times */
+ { "nodiratime", MS_NODIRATIME }, /* Do not update dir access times */
+#endif
+#ifdef MS_RELATIME
+ { "relatime", MS_RELATIME }, /* Update access times relative to mtime/ctime */
+ { "norelatime", MS_RELATIME, MNT_INVERT }, /* Update access time without regard to mtime/ctime */
+#endif
+#ifdef MS_STRICTATIME
+ { "strictatime", MS_STRICTATIME }, /* Strict atime semantics */
+ { "nostrictatime", MS_STRICTATIME, MNT_INVERT }, /* kernel default atime */
+#endif
+ { NULL, 0, 0 }
+};
+
+/*
+ * userspace mount option (built-in MNT_USERSPACE_MAP)
+ */
+static const struct libmnt_optmap userspace_opts_map[] =
+{
+ { "defaults", 0, 0 }, /* default options */
+
+ { "auto", MNT_MS_NOAUTO, MNT_NOHLPS | MNT_INVERT | MNT_NOMTAB }, /* Can be mounted using -a */
+ { "noauto", MNT_MS_NOAUTO, MNT_NOHLPS | MNT_NOMTAB }, /* Can only be mounted explicitly */
+
+ { "user[=]", MNT_MS_USER }, /* Allow ordinary user to mount (mtab) */
+ { "nouser", MNT_MS_USER, MNT_INVERT | MNT_NOMTAB }, /* Forbid ordinary user to mount */
+
+ { "users", MNT_MS_USERS, MNT_NOMTAB }, /* Allow ordinary users to mount */
+ { "nousers", MNT_MS_USERS, MNT_INVERT | MNT_NOMTAB }, /* Forbid ordinary users to mount */
+
+ { "owner", MNT_MS_OWNER, MNT_NOMTAB }, /* Let the owner of the device mount */
+ { "noowner", MNT_MS_OWNER, MNT_INVERT | MNT_NOMTAB }, /* Device owner has no special privs */
+
+ { "group", MNT_MS_GROUP, MNT_NOMTAB }, /* Let the group of the device mount */
+ { "nogroup", MNT_MS_GROUP, MNT_INVERT | MNT_NOMTAB }, /* Device group has no special privs */
+
+ /*
+ * Note that traditional init scripts assume _netdev option in /etc/mtab to
+ * umount network block devices on shutdown.
+ */
+ { "_netdev", MNT_MS_NETDEV }, /* Device requires network */
+
+ { "comment=", MNT_MS_COMMENT, MNT_NOHLPS | MNT_NOMTAB },/* fstab comment only */
+ { "x-", MNT_MS_XCOMMENT, MNT_NOHLPS | MNT_NOMTAB | MNT_PREFIX }, /* x- options */
+
+ { "loop[=]", MNT_MS_LOOP, MNT_NOHLPS }, /* use the loop device */
+ { "offset=", MNT_MS_OFFSET, MNT_NOHLPS | MNT_NOMTAB }, /* loop device offset */
+ { "sizelimit=", MNT_MS_SIZELIMIT, MNT_NOHLPS | MNT_NOMTAB }, /* loop device size limit */
+ { "encryption=", MNT_MS_ENCRYPTION, MNT_NOHLPS | MNT_NOMTAB }, /* loop device encryption */
+
+ { "nofail", MNT_MS_NOFAIL, MNT_NOMTAB }, /* Do not fail if ENOENT on dev */
+
+ { "uhelper=", MNT_MS_UHELPER }, /* /sbin/umount.<helper> */
+
+ { "helper=", MNT_MS_HELPER }, /* /sbin/mount.<helper> */
+
+ { NULL, 0, 0 }
+};
+
+/**
+ * mnt_get_builtin_map:
+ * @id: map id -- MNT_LINUX_MAP or MNT_USERSPACE_MAP
+ *
+ * MNT_LINUX_MAP - Linux kernel fs-independent mount options
+ * (usually MS_* flags, see linux/fs.h)
+ *
+ * MNT_USERSPACE_MAP - userpace mount(8) specific mount options
+ * (e.g user=, _netdev, ...)
+ *
+ * Returns: static built-in libmount map.
+ */
+const struct libmnt_optmap *mnt_get_builtin_optmap(int id)
+{
+ assert(id);
+
+ if (id == MNT_LINUX_MAP)
+ return linux_flags_map;
+ else if (id == MNT_USERSPACE_MAP)
+ return userspace_opts_map;
+ return NULL;
+}
+
+/*
+ * Lookups for the @name in @maps and returns a map and in @mapent
+ * returns the map entry
+ */
+const struct libmnt_optmap *mnt_optmap_get_entry(
+ struct libmnt_optmap const **maps,
+ int nmaps,
+ const char *name,
+ size_t namelen,
+ const struct libmnt_optmap **mapent)
+{
+ int i;
+
+ assert(maps);
+ assert(nmaps);
+ assert(name);
+ assert(namelen);
+
+ if (mapent)
+ *mapent = NULL;
+
+ for (i = 0; i < nmaps; i++) {
+ const struct libmnt_optmap *map = maps[i];
+ const struct libmnt_optmap *ent;
+ const char *p;
+
+ for (ent = map; ent && ent->name; ent++) {
+ if (ent->mask & MNT_PREFIX) {
+ if (startswith(name, ent->name)) {
+ if (mapent)
+ *mapent = ent;
+ return map;
+ }
+ continue;
+ }
+ if (strncmp(ent->name, name, namelen))
+ continue;
+ p = ent->name + namelen;
+ if (*p == '\0' || *p == '=' || *p == '[') {
+ if (mapent)
+ *mapent = ent;
+ return map;
+ }
+ }
+ }
+ return NULL;
+}
+
diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c
new file mode 100644
index 0000000..16d6d13
--- /dev/null
+++ b/libmount/src/optstr.c
@@ -0,0 +1,1303 @@
+/*
+ * Copyright (C) 2008,2009,2012 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: optstr
+ * @title: Options string
+ * @short_description: low-level API for work with mount options
+ *
+ * This is simple and low-level API to work with mount options that are stored
+ * in string.
+ */
+#include <ctype.h>
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#include "mountP.h"
+
+/*
+ * Option location
+ */
+struct libmnt_optloc {
+ char *begin;
+ char *end;
+ char *value;
+ size_t valsz;
+ size_t namesz;
+};
+
+#define mnt_init_optloc(_ol) (memset((_ol), 0, sizeof(struct libmnt_optloc)))
+
+/*
+ * Parses the first option from @optstr. The @optstr pointer is set to begin of
+ * the next option.
+ *
+ * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.
+ */
+static int mnt_optstr_parse_next(char **optstr, char **name, size_t *namesz,
+ char **value, size_t *valsz)
+{
+ int open_quote = 0;
+ char *start = NULL, *stop = NULL, *p, *sep = NULL;
+ char *optstr0;
+
+ assert(optstr);
+ assert(*optstr);
+
+ optstr0 = *optstr;
+
+ if (name)
+ *name = NULL;
+ if (namesz)
+ *namesz = 0;
+ if (value)
+ *value = NULL;
+ if (valsz)
+ *valsz = 0;
+
+ /* trim leading commas as to not invalidate option
+ * strings with multiple consecutive commas */
+ while (optstr0 && *optstr0 == ',')
+ optstr0++;
+
+ for (p = optstr0; p && *p; p++) {
+ if (!start)
+ start = p; /* begin of the option item */
+ if (*p == '"')
+ open_quote ^= 1; /* reverse the status */
+ if (open_quote)
+ continue; /* still in quoted block */
+ if (!sep && *p == '=')
+ sep = p; /* name and value separator */
+ if (*p == ',')
+ stop = p; /* terminate the option item */
+ else if (*(p + 1) == '\0')
+ stop = p + 1; /* end of optstr */
+ if (!start || !stop)
+ continue;
+ if (stop <= start)
+ goto error;
+
+ if (name)
+ *name = start;
+ if (namesz)
+ *namesz = sep ? sep - start : stop - start;
+ *optstr = *stop ? stop + 1 : stop;
+
+ if (sep) {
+ if (value)
+ *value = sep + 1;
+ if (valsz)
+ *valsz = stop - sep - 1;
+ }
+ return 0;
+ }
+
+ return 1; /* end of optstr */
+
+error:
+ DBG(OPTIONS, mnt_debug("parse error: \"%s\"", optstr0));
+ return -EINVAL;
+}
+
+/*
+ * Locates the first option that match with @name. The @end is set to
+ * char behind the option (it means ',' or \0).
+ *
+ * Returns negative number on parse error, 1 when not found and 0 on success.
+ */
+static int mnt_optstr_locate_option(char *optstr, const char *name,
+ struct libmnt_optloc *ol)
+{
+ char *n;
+ size_t namesz, nsz;
+ int rc;
+
+ if (!optstr)
+ return 1;
+
+ assert(name);
+ assert(optstr);
+
+ namesz = strlen(name);
+
+ do {
+ rc = mnt_optstr_parse_next(&optstr, &n, &nsz,
+ &ol->value, &ol->valsz);
+ if (rc)
+ break;
+
+ if (namesz == nsz && strncmp(n, name, nsz) == 0) {
+ ol->begin = n;
+ ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;
+ ol->namesz = nsz;
+ return 0;
+ }
+ } while(1);
+
+ return rc;
+}
+
+/**
+ * mnt_optstr_next_option:
+ * @optstr: option string, returns position to next option
+ * @name: returns option name
+ * @namesz: returns option name length
+ * @value: returns option value or NULL
+ * @valuesz: returns option value length or zero
+ *
+ * Parses the first option in @optstr.
+ *
+ * Returns: 0 on success, 1 at the end of @optstr or negative number in case of
+ * error.
+ */
+int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz,
+ char **value, size_t *valuesz)
+{
+ if (!optstr || !*optstr)
+ return -EINVAL;
+ return mnt_optstr_parse_next(optstr, name, namesz, value, valuesz);
+}
+
+static int __mnt_optstr_append_option(char **optstr,
+ const char *name, size_t nsz,
+ const char *value, size_t vsz)
+{
+ char *p;
+ size_t sz, osz;
+
+ assert(name);
+ assert(*name);
+ assert(nsz);
+
+ osz = *optstr ? strlen(*optstr) : 0;
+
+ sz = osz + nsz + 1; /* 1: '\0' */
+ if (osz)
+ sz++; /* ',' options separator */
+ if (vsz)
+ sz += vsz + 1; /* 1: '=' */
+
+ p = realloc(*optstr, sz);
+ if (!p)
+ return -ENOMEM;
+ *optstr = p;
+
+ if (osz) {
+ p += osz;
+ *p++ = ',';
+ }
+
+ memcpy(p, name, nsz);
+ p += nsz;
+
+ if (vsz) {
+ *p++ = '=';
+ memcpy(p, value, vsz);
+ p += vsz;
+ }
+ *p = '\0';
+
+ return 0;
+}
+
+/**
+ * mnt_optstr_append_option:
+ * @optstr: option string or NULL, returns reallocated string
+ * @name: value name
+ * @value: value
+ *
+ * Returns: 0 on success or -1 in case of error. After error the @optstr should
+ * be unmodified.
+ */
+int mnt_optstr_append_option(char **optstr, const char *name, const char *value)
+{
+ size_t vsz, nsz;
+
+ if (!name || !*name)
+ return 0;
+
+ nsz = strlen(name);
+ vsz = value ? strlen(value) : 0;
+
+ return __mnt_optstr_append_option(optstr, name, nsz, value, vsz);
+}
+
+/**
+ * mnt_optstr_prepend_option:
+ * @optstr: option string or NULL, returns reallocated string
+ * @name: value name
+ * @value: value
+ *
+ * Returns: 0 on success or -1 in case of error. After error the @optstr should
+ * be unmodified.
+ */
+int mnt_optstr_prepend_option(char **optstr, const char *name, const char *value)
+{
+ int rc = 0;
+ char *tmp = *optstr;
+
+ *optstr = NULL;
+
+ rc = mnt_optstr_append_option(optstr, name, value);
+ if (!rc && tmp && *tmp)
+ rc = mnt_optstr_append_option(optstr, tmp, NULL);
+ if (!rc) {
+ free(tmp);
+ return 0;
+ }
+
+ free(*optstr);
+ *optstr = tmp;
+
+ DBG(OPTIONS, mnt_debug("failed to prepend '%s[=%s]' to '%s'",
+ name, value, *optstr));
+ return rc;
+}
+
+/**
+ * mnt_optstr_get_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option name
+ * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL
+ * @valsz: returns size of the value or 0
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case
+ * of error.
+ */
+int mnt_optstr_get_option(const char *optstr, const char *name,
+ char **value, size_t *valsz)
+{
+ struct libmnt_optloc ol;
+ int rc;
+
+ mnt_init_optloc(&ol);
+
+ rc = mnt_optstr_locate_option((char *) optstr, name, &ol);
+ if (!rc) {
+ if (value)
+ *value = ol.value;
+ if (valsz)
+ *valsz = ol.valsz;
+ }
+ return rc;
+}
+
+/**
+ * mnt_optstr_deduplicate_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option name
+ *
+ * Removes all instances of @name except the last one.
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case
+ * of error.
+ */
+int mnt_optstr_deduplicate_option(char **optstr, const char *name)
+{
+ int rc;
+ char *begin = NULL, *end = NULL, *opt = *optstr;
+
+ do {
+ struct libmnt_optloc ol;
+
+ mnt_init_optloc(&ol);
+
+ rc = mnt_optstr_locate_option(opt, name, &ol);
+ if (!rc) {
+ if (begin) {
+ /* remove previous instance */
+ size_t shift = strlen(*optstr);
+
+ mnt_optstr_remove_option_at(optstr, begin, end);
+
+ /* now all offset are not valied anymore - recount */
+ shift -= strlen(*optstr);
+ ol.begin -= shift;
+ ol.end -= shift;
+ }
+ begin = ol.begin;
+ end = ol.end;
+ opt = end && *end ? end + 1 : NULL;
+ }
+ } while (rc == 0 && opt && *opt);
+
+ return rc < 0 ? rc : begin ? 0 : 1;
+}
+
+/*
+ * The result never starts or ends with comma or contains two commas
+ * (e.g. ",aaa,bbb" or "aaa,,bbb" or "aaa,")
+ */
+int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end)
+{
+ size_t sz;
+
+ if (!optstr || !begin || !end)
+ return -EINVAL;
+
+ if ((begin == *optstr || *(begin - 1) == ',') && *end == ',')
+ end++;
+
+ sz = strlen(end);
+
+ memmove(begin, end, sz + 1);
+ if (!*begin && (begin > *optstr) && *(begin - 1) == ',')
+ *(begin - 1) = '\0';
+
+ return 0;
+}
+
+/* insert 'substr' or '=substr' to @str on position @pos */
+static int insert_value(char **str, char *pos, const char *substr, char **next)
+{
+ size_t subsz = strlen(substr); /* substring size */
+ size_t strsz = strlen(*str);
+ size_t possz = strlen(pos);
+ size_t posoff;
+ char *p = NULL;
+ int sep;
+
+ /* is it necessary to prepend '=' before the substring ? */
+ sep = !(pos > *str && *(pos - 1) == '=');
+
+ /* save an offset of the place where we need add substr */
+ posoff = pos - *str;
+
+ p = realloc(*str, strsz + sep + subsz + 1);
+ if (!p)
+ return -ENOMEM;
+
+ /* zeroize new allocated memory -- valgind loves is... */
+ memset(p + strsz, 0, sep + subsz + 1);
+
+ /* set pointers to the reallocated string */
+ *str = p;
+ pos = p + posoff;
+
+ if (possz)
+ /* create a room for new substring */
+ memmove(pos + subsz + sep, pos, possz + 1);
+ if (sep)
+ *pos++ = '=';
+
+ memcpy(pos, substr, subsz);
+
+ if (next) {
+ /* set pointer to the next option */
+ *next = pos + subsz;
+ if (**next == ',')
+ (*next)++;
+ }
+ return 0;
+}
+
+/**
+ * mnt_optstr_set_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option
+ * @value: new value or NULL
+ *
+ * Set or unset option @value.
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case
+ * of error.
+ */
+int mnt_optstr_set_option(char **optstr, const char *name, const char *value)
+{
+ struct libmnt_optloc ol;
+ char *nameend;
+ int rc = 1;
+
+ if (!optstr)
+ return -EINVAL;
+
+ mnt_init_optloc(&ol);
+
+ if (*optstr)
+ rc = mnt_optstr_locate_option(*optstr, name, &ol);
+ if (rc < 0)
+ return rc; /* parse error */
+ if (rc == 1)
+ return mnt_optstr_append_option(optstr, name, value); /* not found */
+
+ nameend = ol.begin + ol.namesz;
+
+ if (value == NULL && ol.value && ol.valsz)
+ /* remove unwanted "=value" */
+ mnt_optstr_remove_option_at(optstr, nameend, ol.end);
+
+ else if (value && ol.value == NULL)
+ /* insert "=value" */
+ rc = insert_value(optstr, nameend, value, NULL);
+
+ else if (value && ol.value && strlen(value) == ol.valsz)
+ /* simply replace =value */
+ memcpy(ol.value, value, ol.valsz);
+
+ else if (value && ol.value) {
+ mnt_optstr_remove_option_at(optstr, nameend, ol.end);
+ rc = insert_value(optstr, nameend, value, NULL);
+ }
+ return rc;
+}
+
+/**
+ * mnt_optstr_remove_option:
+ * @optstr: string with comma separated list of options
+ * @name: requested option name
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case
+ * of error.
+ */
+int mnt_optstr_remove_option(char **optstr, const char *name)
+{
+ struct libmnt_optloc ol;
+ int rc;
+
+ mnt_init_optloc(&ol);
+
+ rc = mnt_optstr_locate_option(*optstr, name, &ol);
+ if (rc != 0)
+ return rc;
+
+ mnt_optstr_remove_option_at(optstr, ol.begin, ol.end);
+ return 0;
+}
+
+/**
+ * mnt_split_optstr:
+ * @optstr: string with comma separated list of options
+ * @user: returns newly allocated string with userspace options
+ * @vfs: returns newly allocated string with VFS options
+ * @fs: returns newly allocated string with FS options
+ * @ignore_user: option mask for options that should be ignored
+ * @ignore_vfs: option mask for options that should be ignored
+ *
+ * For example:
+ *
+ * mnt_split_optstr(optstr, &u, NULL, NULL, MNT_NOMTAB, 0);
+ *
+ * returns all userspace options, the options that does not belong to
+ * mtab are ignored.
+ *
+ * Note that FS options are all options that are undefined in MNT_USERSPACE_MAP
+ * or MNT_LINUX_MAP.
+ *
+ * Returns: 0 on success, or negative number in case of error.
+ */
+int mnt_split_optstr(const char *optstr, char **user, char **vfs,
+ char **fs, int ignore_user, int ignore_vfs)
+{
+ char *name, *val, *str = (char *) optstr;
+ size_t namesz, valsz;
+ struct libmnt_optmap const *maps[2];
+
+ assert(optstr);
+
+ if (!optstr)
+ return -EINVAL;
+
+ maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
+ maps[1] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+
+ if (vfs)
+ *vfs = NULL;
+ if (fs)
+ *fs = NULL;
+ if (user)
+ *user = NULL;
+
+ while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
+ int rc = 0;
+ const struct libmnt_optmap *ent = NULL;
+ const struct libmnt_optmap *m =
+ mnt_optmap_get_entry(maps, 2, name, namesz, &ent);
+
+ if (ent && !ent->id)
+ continue; /* ignore undefined options (comments) */
+
+ if (ent && m && m == maps[0] && vfs) {
+ if (ignore_vfs && (ent->mask & ignore_vfs))
+ continue;
+ rc = __mnt_optstr_append_option(vfs, name, namesz,
+ val, valsz);
+ } else if (ent && m && m == maps[1] && user) {
+ if (ignore_user && (ent->mask & ignore_user))
+ continue;
+ rc = __mnt_optstr_append_option(user, name, namesz,
+ val, valsz);
+ } else if (!m && fs)
+ rc = __mnt_optstr_append_option(fs, name, namesz,
+ val, valsz);
+ if (rc) {
+ if (vfs)
+ free(*vfs);
+ if (fs)
+ free(*fs);
+ if (user)
+ free(*user);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * mnt_optstr_get_options
+ * @optstr: string with comma separated list of options
+ * @subset: returns newly allocated string with options
+ * @map: options map
+ * @ignore: mask of the options that should be ignored
+ *
+ * Extracts options from @optstr that belongs to the @map, for example:
+ *
+ * mnt_optstr_get_options(optstr, &p,
+ * mnt_get_builtin_optmap(MNT_LINUX_MAP),
+ * MNT_NOMTAB);
+ *
+ * the 'p' returns all VFS options, the options that does not belong to mtab
+ * are ignored.
+ *
+ * Returns: 0 on success, or negative number in case of error.
+ */
+int mnt_optstr_get_options(const char *optstr, char **subset,
+ const struct libmnt_optmap *map, int ignore)
+{
+ struct libmnt_optmap const *maps[1];
+ char *name, *val, *str = (char *) optstr;
+ size_t namesz, valsz;
+
+ if (!optstr || !subset)
+ return -EINVAL;
+
+ maps[0] = map;
+ *subset = NULL;
+
+ while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
+ int rc = 0;
+ const struct libmnt_optmap *ent;
+
+ mnt_optmap_get_entry(maps, 1, name, namesz, &ent);
+
+ if (!ent || !ent->id)
+ continue; /* ignore undefined options (comments) */
+
+ if (ignore && (ent->mask & ignore))
+ continue;
+ rc = __mnt_optstr_append_option(subset, name, namesz, val, valsz);
+ if (rc) {
+ free(*subset);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * mnt_optstr_get_flags:
+ * @optstr: string with comma separated list of options
+ * @flags: returns mount flags
+ * @map: options map
+ *
+ * Returns in @flags IDs of options from @optstr as defined in the @map.
+ *
+ * For example:
+ *
+ * "bind,exec,foo,bar" --returns-> MS_BIND
+ *
+ * "bind,noexec,foo,bar" --returns-> MS_BIND|MS_NOEXEC
+ *
+ * Note that @flags are not zeroized by this function! This function set/unset
+ * bites in the @flags only.
+ *
+ * Returns: 0 on success or negative number in case of error
+ */
+int mnt_optstr_get_flags(const char *optstr, unsigned long *flags,
+ const struct libmnt_optmap *map)
+{
+ struct libmnt_optmap const *maps[2];
+ char *name, *str = (char *) optstr;
+ size_t namesz = 0, valsz = 0;
+ int nmaps = 0;
+
+ assert(optstr);
+
+ if (!optstr || !flags || !map)
+ return -EINVAL;
+
+ maps[nmaps++] = map;
+
+ if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP))
+ /*
+ * Add userspace map -- the "user" is interpreted as
+ * MS_NO{EXEC,SUID,DEV}.
+ */
+ maps[nmaps++] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+
+ while(!mnt_optstr_next_option(&str, &name, &namesz, NULL, &valsz)) {
+ const struct libmnt_optmap *ent;
+ const struct libmnt_optmap *m;
+
+ m = mnt_optmap_get_entry(maps, nmaps, name, namesz, &ent);
+ if (!m || !ent || !ent->id)
+ continue;
+
+ if (m == map) { /* requested map */
+ if (ent->mask & MNT_INVERT)
+ *flags &= ~ent->id;
+ else
+ *flags |= ent->id;
+
+ } else if (nmaps == 2 && m == maps[1] && valsz == 0) {
+ /*
+ * Special case -- translate "user" (but no user=) to
+ * MS_ options
+ */
+ if (ent->mask & MNT_INVERT)
+ continue;
+ if (ent->id & (MNT_MS_OWNER | MNT_MS_GROUP))
+ *flags |= MS_OWNERSECURE;
+ else if (ent->id & (MNT_MS_USER | MNT_MS_USERS))
+ *flags |= MS_SECURE;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * mnt_optstr_apply_flags:
+ * @optstr: string with comma separated list of options
+ * @flags: returns mount flags
+ * @map: options map
+ *
+ * Removes/adds options to the @optstr according to flags. For example:
+ *
+ * MS_NOATIME and "foo,bar,noexec" --returns-> "foo,bar,noatime"
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_optstr_apply_flags(char **optstr, unsigned long flags,
+ const struct libmnt_optmap *map)
+{
+ struct libmnt_optmap const *maps[1];
+ char *name, *next, *val;
+ size_t namesz = 0, valsz = 0;
+ unsigned long fl;
+ int rc = 0;
+
+ assert(optstr);
+
+ if (!optstr || !map)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug("applying 0x%08lu flags to '%s'", flags, *optstr));
+
+ maps[0] = map;
+ next = *optstr;
+ fl = flags;
+
+ /*
+ * There is a convetion that 'rw/ro' flags is always at the begin of
+ * the string (athough the 'rw' is unnecessary).
+ */
+ if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) {
+ const char *o = (fl & MS_RDONLY) ? "ro" : "rw";
+
+ if (next &&
+ (!strncmp(next, "rw", 2) || !strncmp(next, "ro", 2)) &&
+ (*(next + 2) == '\0' || *(next + 2) == ',')) {
+
+ /* already set, be paranoid and fix it */
+ memcpy(next, o, 2);
+ } else {
+ rc = mnt_optstr_prepend_option(optstr, o, NULL);
+ if (rc)
+ goto err;
+ next = *optstr; /* because realloc() */
+ }
+ fl &= ~MS_RDONLY;
+ next += 2;
+ if (*next == ',')
+ next++;
+ }
+
+ if (next && *next) {
+ /*
+ * scan @optstr and remove options that are missing in
+ * the @flags
+ */
+ while(!mnt_optstr_next_option(&next, &name, &namesz,
+ &val, &valsz)) {
+ const struct libmnt_optmap *ent;
+
+ if (mnt_optmap_get_entry(maps, 1, name, namesz, &ent)) {
+ /*
+ * remove unwanted option (rw/ro is already set)
+ */
+ if (!ent->id)
+ continue;
+ if (ent->id == MS_RDONLY ||
+ (ent->mask & MNT_INVERT) ||
+ (fl & ent->id) != (unsigned long) ent->id) {
+
+ char *end = val ? val + valsz :
+ name + namesz;
+ next = name;
+ rc = mnt_optstr_remove_option_at(
+ optstr, name, end);
+ if (rc)
+ goto err;
+ }
+ if (!(ent->mask & MNT_INVERT))
+ fl &= ~ent->id;
+ }
+ }
+ }
+
+ /* add missing options */
+ if (fl) {
+ const struct libmnt_optmap *ent;
+ char *p;
+
+ for (ent = map; ent && ent->name; ent++) {
+ if ((ent->mask & MNT_INVERT)
+ || ent->id == 0
+ || (fl & ent->id) != (unsigned long) ent->id)
+ continue;
+
+ /* don't add options which require values (e.g. offset=%d) */
+ p = strchr(ent->name, '=');
+ if (p) {
+ if (p > ent->name && *(p - 1) == '[')
+ p--; /* name[=] */
+ else
+ continue; /* name= */
+
+ p = strndup(ent->name, p - ent->name);
+ if (!p) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ mnt_optstr_append_option(optstr, p, NULL);
+ free(p);
+ } else
+ mnt_optstr_append_option(optstr, ent->name, NULL);
+ }
+ }
+
+ DBG(CXT, mnt_debug("new optstr '%s'", *optstr));
+ return rc;
+err:
+ DBG(CXT, mnt_debug("failed to apply flags [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @optstr: string with comma separated list of options
+ * @value: pointer to the begin of the context value
+ * @valsz: size of the value
+ * @next: returns pointer to the next option (optional argument)
+ *
+ * Translates SELinux context from human to raw format. The function does not
+ * modify @optstr and returns zero if libmount is compiled without SELinux
+ * support.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+#ifndef HAVE_LIBSELINUX
+int mnt_optstr_fix_secontext(char **optstr __attribute__ ((__unused__)),
+ char *value __attribute__ ((__unused__)),
+ size_t valsz __attribute__ ((__unused__)),
+ char **next __attribute__ ((__unused__)))
+{
+ return 0;
+}
+#else
+int mnt_optstr_fix_secontext(char **optstr,
+ char *value,
+ size_t valsz,
+ char **next)
+{
+ int rc = 0;
+
+ security_context_t raw = NULL;
+ char *p, *val, *begin, *end;
+ size_t sz;
+
+ if (!optstr || !*optstr || !value || !valsz)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug("fixing SELinux context"));
+
+ begin = value;
+ end = value + valsz;
+
+ /* the selinux contexts are quoted */
+ if (*value == '"') {
+ if (valsz <= 2 || *(value + valsz - 1) != '"')
+ return -EINVAL; /* improperly quoted option string */
+ value++;
+ valsz -= 2;
+ }
+
+ p = strndup(value, valsz);
+ if (!p)
+ return -ENOMEM;
+
+
+ /* translate the context */
+ rc = selinux_trans_to_raw_context((security_context_t) p, &raw);
+
+ DBG(CXT, mnt_debug("SELinux context '%s' translated to '%s'",
+ p, rc == -1 ? "FAILED" : (char *) raw));
+
+ free(p);
+ if (rc == -1 || !raw)
+ return -EINVAL;
+
+
+ /* create quoted string from the raw context */
+ sz = strlen((char *) raw);
+ if (!sz)
+ return -EINVAL;
+
+ p = val = malloc(valsz + 3);
+ if (!val)
+ return -ENOMEM;
+
+ *p++ = '"';
+ memcpy(p, raw, sz);
+ p += sz;
+ *p++ = '"';
+ *p = '\0';
+
+ freecon(raw);
+
+ /* set new context */
+ mnt_optstr_remove_option_at(optstr, begin, end);
+ rc = insert_value(optstr, begin, val, next);
+ free(val);
+
+ return rc;
+}
+#endif
+
+static int set_uint_value(char **optstr, unsigned int num,
+ char *begin, char *end, char **next)
+{
+ char buf[40];
+ snprintf(buf, sizeof(buf), "%u", num);
+
+ mnt_optstr_remove_option_at(optstr, begin, end);
+ return insert_value(optstr, begin, buf, next);
+}
+
+/*
+ * @optstr: string with comma separated list of options
+ * @value: pointer to the begin of the uid value
+ * @valsz: size of the value
+ * @next: returns pointer to the next option (optional argument)
+
+ * Translates "username" or "useruid" to the real UID.
+ *
+ * For example:
+ * if (!mnt_optstr_get_option(optstr, "uid", &val, &valsz))
+ * mnt_optstr_fix_uid(&optstr, val, valsz, NULL);
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_optstr_fix_uid(char **optstr, char *value, size_t valsz, char **next)
+{
+ int rc = 0;
+ char *end;
+
+ if (!optstr || !*optstr || !value || !valsz)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug("fixing uid"));
+
+ end = value + valsz;
+
+ if (valsz == 7 && !strncmp(value, "useruid", 7) &&
+ (*(value + 7) == ',' || !*(value + 7)))
+ rc = set_uint_value(optstr, getuid(), value, end, next);
+
+ else if (!isdigit(*value)) {
+ uid_t id;
+ char *p = strndup(value, valsz);
+ if (!p)
+ return -ENOMEM;
+ rc = mnt_get_uid(p, &id);
+ free(p);
+
+ if (!rc)
+ rc = set_uint_value(optstr, id, value, end, next);
+
+ } else if (next) {
+ /* nothing */
+ *next = value + valsz;
+ if (**next == ',')
+ (*next)++;
+ }
+
+ return rc;
+}
+
+/*
+ * @optstr: string with comma separated list of options
+ * @value: pointer to the begin of the uid value
+ * @valsz: size of the value
+ * @next: returns pointer to the next option (optional argument)
+
+ * Translates "groupname" or "usergid" to the real GID.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_optstr_fix_gid(char **optstr, char *value, size_t valsz, char **next)
+{
+ int rc = 0;
+ char *end;
+
+ if (!optstr || !*optstr || !value || !valsz)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug("fixing gid"));
+
+ end = value + valsz;
+
+ if (valsz == 7 && !strncmp(value, "usergid", 7) &&
+ (*(value + 7) == ',' || !*(value + 7)))
+ rc = set_uint_value(optstr, getgid(), value, end, next);
+
+ else if (!isdigit(*value)) {
+ gid_t id;
+ char *p = strndup(value, valsz);
+ if (!p)
+ return -ENOMEM;
+ rc = mnt_get_gid(p, &id);
+ free(p);
+
+ if (!rc)
+ rc = set_uint_value(optstr, id, value, end, next);
+
+ } else if (next) {
+ /* nothing */
+ *next = value + valsz;
+ if (**next == ',')
+ (*next)++;
+ }
+ return rc;
+}
+
+/*
+ * Converts "user" to "user=<username>".
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_optstr_fix_user(char **optstr)
+{
+ char *username;
+ struct libmnt_optloc ol;
+ int rc = 0;
+
+ DBG(CXT, mnt_debug("fixing user"));
+
+ mnt_init_optloc(&ol);
+
+ rc = mnt_optstr_locate_option(*optstr, "user", &ol);
+ if (rc)
+ return rc == 1 ? 0 : rc; /* 1: user= not found */
+
+ username = mnt_get_username(getuid());
+ if (!username)
+ return -ENOMEM;
+
+ if (!ol.valsz || (ol.value && strncmp(ol.value, username, ol.valsz))) {
+ if (ol.valsz)
+ /* remove old value */
+ mnt_optstr_remove_option_at(optstr, ol.value, ol.end);
+
+ rc = insert_value(optstr, ol.value ? ol.value : ol.end,
+ username, NULL);
+ }
+
+ free(username);
+ return rc;
+}
+
+#ifdef TEST_PROGRAM
+
+int test_append(struct libmnt_test *ts, int argc, char *argv[])
+{
+ const char *value = NULL, *name;
+ char *optstr;
+ int rc;
+
+ if (argc < 3)
+ return -EINVAL;
+ optstr = strdup(argv[1]);
+ name = argv[2];
+
+ if (argc == 4)
+ value = argv[3];
+
+ rc = mnt_optstr_append_option(&optstr, name, value);
+ if (!rc)
+ printf("result: >%s<\n", optstr);
+ free(optstr);
+ return rc;
+}
+
+int test_prepend(struct libmnt_test *ts, int argc, char *argv[])
+{
+ const char *value = NULL, *name;
+ char *optstr;
+ int rc;
+
+ if (argc < 3)
+ return -EINVAL;
+ optstr = strdup(argv[1]);
+ name = argv[2];
+
+ if (argc == 4)
+ value = argv[3];
+
+ rc = mnt_optstr_prepend_option(&optstr, name, value);
+ if (!rc)
+ printf("result: >%s<\n", optstr);
+ free(optstr);
+ return rc;
+}
+
+int test_split(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr, *user = NULL, *fs = NULL, *vfs = NULL;
+ int rc;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ optstr = strdup(argv[1]);
+
+ rc = mnt_split_optstr(optstr, &user, &vfs, &fs, 0, 0);
+ if (!rc) {
+ printf("user : %s\n", user);
+ printf("vfs : %s\n", vfs);
+ printf("fs : %s\n", fs);
+ }
+
+ free(user);
+ free(vfs);
+ free(fs);
+ free(optstr);
+ return rc;
+}
+
+int test_flags(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr;
+ int rc;
+ unsigned long fl = 0;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ optstr = strdup(argv[1]);
+
+ rc = mnt_optstr_get_flags(optstr, &fl, mnt_get_builtin_optmap(MNT_LINUX_MAP));
+ if (rc)
+ return rc;
+ printf("mountflags: 0x%08lx\n", fl);
+
+ fl = 0;
+ rc = mnt_optstr_get_flags(optstr, &fl, mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
+ if (rc)
+ return rc;
+ printf("userspace-mountflags: 0x%08lx\n", fl);
+
+ free(optstr);
+ return rc;
+}
+
+int test_apply(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr;
+ int rc, map;
+ unsigned long flags;
+
+ if (argc < 4)
+ return -EINVAL;
+
+ if (!strcmp(argv[1], "--user"))
+ map = MNT_USERSPACE_MAP;
+ else if (!strcmp(argv[1], "--linux"))
+ map = MNT_LINUX_MAP;
+ else {
+ fprintf(stderr, "unknown option '%s'\n", argv[1]);
+ return -EINVAL;
+ }
+
+ optstr = strdup(argv[2]);
+ flags = strtoul(argv[3], NULL, 16);
+
+ printf("flags: 0x%08lx\n", flags);
+
+ rc = mnt_optstr_apply_flags(&optstr, flags, mnt_get_builtin_optmap(map));
+ printf("optstr: %s\n", optstr);
+
+ free(optstr);
+ return rc;
+}
+
+int test_set(struct libmnt_test *ts, int argc, char *argv[])
+{
+ const char *value = NULL, *name;
+ char *optstr;
+ int rc;
+
+ if (argc < 3)
+ return -EINVAL;
+ optstr = strdup(argv[1]);
+ name = argv[2];
+
+ if (argc == 4)
+ value = argv[3];
+
+ rc = mnt_optstr_set_option(&optstr, name, value);
+ if (!rc)
+ printf("result: >%s<\n", optstr);
+ free(optstr);
+ return rc;
+}
+
+int test_get(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr;
+ const char *name;
+ char *val = NULL;
+ size_t sz = 0;
+ int rc;
+
+ if (argc < 2)
+ return -EINVAL;
+ optstr = argv[1];
+ name = argv[2];
+
+ rc = mnt_optstr_get_option(optstr, name, &val, &sz);
+ if (rc == 0) {
+ printf("found; name: %s", name);
+ if (sz) {
+ printf(", argument: size=%zd data=", sz);
+ if (fwrite(val, 1, sz, stdout) != sz)
+ return -1;
+ }
+ printf("\n");
+ } else if (rc == 1)
+ printf("%s: not found\n", name);
+ else
+ printf("parse error: %s\n", optstr);
+ return rc;
+}
+
+int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+{
+ const char *name;
+ char *optstr;
+ int rc;
+
+ if (argc < 3)
+ return -EINVAL;
+ optstr = strdup(argv[1]);
+ name = argv[2];
+
+ rc = mnt_optstr_remove_option(&optstr, name);
+ if (!rc)
+ printf("result: >%s<\n", optstr);
+ free(optstr);
+ return rc;
+}
+
+int test_dedup(struct libmnt_test *ts, int argc, char *argv[])
+{
+ const char *name;
+ char *optstr;
+ int rc;
+
+ if (argc < 3)
+ return -EINVAL;
+ optstr = strdup(argv[1]);
+ name = argv[2];
+
+ rc = mnt_optstr_deduplicate_option(&optstr, name);
+ if (!rc)
+ printf("result: >%s<\n", optstr);
+ free(optstr);
+ return rc;
+}
+
+int test_fix(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr;
+ int rc = 0;
+ char *name, *val, *next;
+ size_t valsz, namesz;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ next = optstr = strdup(argv[1]);
+
+ printf("optstr: %s\n", optstr);
+
+ while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+
+ if (!strncmp(name, "uid", 3))
+ rc = mnt_optstr_fix_uid(&optstr, val, valsz, &next);
+ else if (!strncmp(name, "gid", 3))
+ rc = mnt_optstr_fix_gid(&optstr, val, valsz, &next);
+ else if (!strncmp(name, "context", 7))
+ rc = mnt_optstr_fix_secontext(&optstr, val, valsz, &next);
+ if (rc)
+ break;
+ }
+ if (rc)
+ rc = mnt_optstr_fix_user(&optstr);
+
+ printf("fixed: %s\n", optstr);
+
+ free(optstr);
+ return rc;
+
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--append", test_append, "<optstr> <name> [<value>] append value to optstr" },
+ { "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" },
+ { "--set", test_set, "<optstr> <name> [<value>] (un)set value" },
+ { "--get", test_get, "<optstr> <name> search name in optstr" },
+ { "--remove", test_remove, "<optstr> <name> remove name in optstr" },
+ { "--dedup", test_dedup, "<optstr> <name> deduplicate name in optstr" },
+ { "--split", test_split, "<optstr> split into FS, VFS and userspace" },
+ { "--flags", test_flags, "<optstr> convert options to MS_* flags" },
+ { "--apply", test_apply, "--{linux,user} <optstr> <mask> apply mask to optstr" },
+ { "--fix", test_fix, "<optstr> fix uid=, gid=, user, and context=" },
+
+ { NULL }
+ };
+ return mnt_run_test(tss, argc, argv);
+}
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/tab.c b/libmount/src/tab.c
new file mode 100644
index 0000000..1c159b5
--- /dev/null
+++ b/libmount/src/tab.c
@@ -0,0 +1,1195 @@
+/*
+ * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: table
+ * @title: Table of filesystems
+ * @short_description: container for entries from fstab, mtab or mountinfo
+ *
+ * Note that mnt_table_find_* functions are mount(8) compatible. These functions
+ * try to find an entry in more iterations where the first attempt is always
+ * based on comparison with unmodified (non-canonicalized or un-evaluated)
+ * paths or tags. For example fstab with two entries:
+ * <informalexample>
+ * <programlisting>
+ * LABEL=foo /foo auto rw
+ * /dev/foo /foo auto rw
+ * </programlisting>
+ * </informalexample>
+ *
+ * where both lines are used for the *same* device, then
+ * <informalexample>
+ * <programlisting>
+ * mnt_table_find_source(tb, "/dev/foo", &fs);
+ * </programlisting>
+ * </informalexample>
+ * will returns the second line, and
+ * <informalexample>
+ * <programlisting>
+ * mnt_table_find_source(tb, "LABEL=foo", &fs);
+ * </programlisting>
+ * </informalexample>
+ * will returns the first entry, and
+ * <informalexample>
+ * <programlisting>
+ * mnt_table_find_source(tb, "UUID=anyuuid", &fs);
+ * </programlisting>
+ * </informalexample>
+ * will returns the first entry (if UUID matches with the device).
+ */
+#include <blkid.h>
+
+#include "mountP.h"
+#include "strutils.h"
+
+/**
+ * mnt_new_table:
+ *
+ * The tab is a container for struct libmnt_fs entries that usually represents a fstab,
+ * mtab or mountinfo file from your system.
+ *
+ * See also mnt_table_parse_file().
+ *
+ * Returns: newly allocated tab struct.
+ */
+struct libmnt_table *mnt_new_table(void)
+{
+ struct libmnt_table *tb = NULL;
+
+ tb = calloc(1, sizeof(*tb));
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "alloc"));
+
+ INIT_LIST_HEAD(&tb->ents);
+ return tb;
+}
+
+/**
+ * mnt_reset_table:
+ * @tb: tab pointer
+ *
+ * Dealocates all entries (filesystems) from the table
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_reset_table(struct libmnt_table *tb)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, mnt_debug_h(tb, "reset"));
+
+ while (!list_empty(&tb->ents)) {
+ struct libmnt_fs *fs = list_entry(tb->ents.next,
+ struct libmnt_fs, ents);
+ mnt_free_fs(fs);
+ }
+
+ tb->nents = 0;
+ return 0;
+}
+
+/**
+ * mnt_free_table:
+ * @tb: tab pointer
+ *
+ * Deallocates tab struct and all entries.
+ */
+void mnt_free_table(struct libmnt_table *tb)
+{
+ if (!tb)
+ return;
+
+ mnt_reset_table(tb);
+
+ DBG(TAB, mnt_debug_h(tb, "free"));
+ free(tb);
+}
+
+/**
+ * mnt_table_get_nents:
+ * @tb: pointer to tab
+ *
+ * Returns: number of valid entries in tab.
+ */
+int mnt_table_get_nents(struct libmnt_table *tb)
+{
+ assert(tb);
+ return tb ? tb->nents : 0;
+}
+
+/**
+ * mnt_table_set_cache:
+ * @tb: pointer to tab
+ * @mpc: pointer to struct libmnt_cache instance
+ *
+ * Setups a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
+ * cache is recommended for mnt_table_find_*() functions.
+ *
+ * The cache could be shared between more tabs. Be careful when you share the
+ * same cache between more threads -- currently the cache does not provide any
+ * locking method.
+ *
+ * See also mnt_new_cache().
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc)
+{
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+ tb->cache = mpc;
+ return 0;
+}
+
+/**
+ * mnt_table_get_cache:
+ * @tb: pointer to tab
+ *
+ * Returns: pointer to struct libmnt_cache instance or NULL.
+ */
+struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
+{
+ assert(tb);
+ return tb ? tb->cache : NULL;
+}
+
+/**
+ * mnt_table_add_fs:
+ * @tb: tab pointer
+ * @fs: new entry
+ *
+ * Adds a new entry to tab.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
+{
+ assert(tb);
+ assert(fs);
+
+ if (!tb || !fs)
+ return -EINVAL;
+
+ list_add_tail(&fs->ents, &tb->ents);
+
+ DBG(TAB, mnt_debug_h(tb, "add entry: %s %s",
+ mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
+ tb->nents++;
+ return 0;
+}
+
+/**
+ * mnt_table_remove_fs:
+ * @tb: tab pointer
+ * @fs: new entry
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
+{
+ assert(tb);
+ assert(fs);
+
+ if (!tb || !fs)
+ return -EINVAL;
+ list_del(&fs->ents);
+ tb->nents--;
+ return 0;
+}
+
+/**
+ * mnt_table_get_root_fs:
+ * @tb: mountinfo file (/proc/self/mountinfo)
+ * @root: returns pointer to the root filesystem (/)
+ *
+ * Returns: 0 on success or -1 case of error.
+ */
+int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
+{
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs;
+ int root_id = 0;
+
+ assert(tb);
+ assert(root);
+
+ if (!tb || !root)
+ return -EINVAL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup root fs"));
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ int id = mnt_fs_get_parent_id(fs);
+ if (!id)
+ break; /* @tab is not mountinfo file? */
+
+ if (!*root || id < root_id) {
+ *root = fs;
+ root_id = id;
+ }
+ }
+
+ return root_id ? 0 : -EINVAL;
+}
+
+/**
+ * mnt_table_next_child_fs:
+ * @tb: mountinfo file (/proc/self/mountinfo)
+ * @itr: iterator
+ * @parent: parental FS
+ * @chld: returns the next child filesystem
+ *
+ * Note that filesystems are returned in the order how was mounted (according to
+ * IDs in /proc/self/mountinfo).
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at end of list.
+ */
+int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
+ struct libmnt_fs *parent, struct libmnt_fs **chld)
+{
+ struct libmnt_fs *fs;
+ int parent_id, lastchld_id = 0, chld_id = 0;
+
+ if (!tb || !itr || !parent)
+ return -EINVAL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup next child of %s",
+ mnt_fs_get_target(parent)));
+
+ parent_id = mnt_fs_get_id(parent);
+ if (!parent_id)
+ return -EINVAL;
+
+ /* get ID of the previously returned child */
+ if (itr->head && itr->p != itr->head) {
+ MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents);
+ lastchld_id = mnt_fs_get_id(fs);
+ }
+
+ *chld = NULL;
+
+ mnt_reset_iter(itr, MNT_ITER_FORWARD);
+ while(mnt_table_next_fs(tb, itr, &fs) == 0) {
+ int id;
+
+ if (mnt_fs_get_parent_id(fs) != parent_id)
+ continue;
+
+ id = mnt_fs_get_id(fs);
+
+ /* avoid infinite loop. This only happens in rare cases
+ * such as in early userspace when the rootfs is its own parent */
+ if (id == parent_id)
+ continue;
+
+ if ((!lastchld_id || id > lastchld_id) &&
+ (!*chld || id < chld_id)) {
+ *chld = fs;
+ chld_id = id;
+ }
+ }
+
+ if (!chld_id)
+ return 1; /* end of iterator */
+
+ /* set the iterator to the @chld for the next call */
+ mnt_table_set_iter(tb, itr, *chld);
+
+ return 0;
+}
+
+/**
+ * mnt_table_next_fs:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @fs: returns the next tab entry
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at end of list.
+ *
+ * Example:
+ * <informalexample>
+ * <programlisting>
+ * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
+ * const char *dir = mnt_fs_get_target(fs);
+ * printf("mount point: %s\n", dir);
+ * }
+ * mnt_free_table(fi);
+ * </programlisting>
+ * </informalexample>
+ *
+ * lists all mountpoints from fstab in backward order.
+ */
+int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs)
+{
+ int rc = 1;
+
+ assert(tb);
+ assert(itr);
+ assert(fs);
+
+ if (!tb || !itr || !fs)
+ return -EINVAL;
+ *fs = NULL;
+
+ if (!itr->head)
+ MNT_ITER_INIT(itr, &tb->ents);
+ if (itr->p != itr->head) {
+ MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * mnt_table_find_next_fs:
+ * @tb: table
+ * @itr: iterator
+ * @match_func: function returns 1 or 0
+ * @userdata: extra data for match_func
+ * @fs: returns pointer to the next matching table entry
+ *
+ * This function allows search in @tb.
+ *
+ * Returns: negative number in case of error, 1 at end of table or 0 o success.
+ */
+int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
+ int (*match_func)(struct libmnt_fs *, void *), void *userdata,
+ struct libmnt_fs **fs)
+{
+ if (!tb || !itr || !fs || !match_func)
+ return -EINVAL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup next fs"));
+
+ if (!itr->head)
+ MNT_ITER_INIT(itr, &tb->ents);
+
+ do {
+ if (itr->p != itr->head)
+ MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
+ else
+ break; /* end */
+
+ if (match_func(*fs, userdata))
+ return 0;
+ } while(1);
+
+ *fs = NULL;
+ return 1;
+}
+
+/**
+ * mnt_table_set_iter:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @fs: tab entry
+ *
+ * Sets @iter to the position of @fs in the file @tb.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs)
+{
+ assert(tb);
+ assert(itr);
+ assert(fs);
+
+ if (!tb || !itr || !fs)
+ return -EINVAL;
+
+ MNT_ITER_INIT(itr, &tb->ents);
+ itr->p = &fs->ents;
+
+ return 0;
+}
+
+/**
+ * mnt_table_find_target:
+ * @tb: tab pointer
+ * @path: mountpoint directory
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * Try to lookup an entry in given tab, possible are three iterations, first
+ * with @path, second with realpath(@path) and third with realpath(@path)
+ * against realpath(fs->target). The 2nd and 3rd iterations are not performed
+ * when @tb cache is not set (see mnt_table_set_cache()).
+ *
+ * Returns: a tab entry or NULL.
+ */
+struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction)
+{
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs = NULL;
+ char *cn;
+
+ assert(tb);
+ assert(path);
+
+ if (!tb || !path)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup TARGET: %s", path));
+
+ /* native @target */
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_streq_target(fs, path))
+ return fs;
+ }
+ if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
+ return NULL;
+
+ /* canonicalized paths in struct libmnt_table */
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_streq_target(fs, cn))
+ return fs;
+ }
+
+ /* non-canonicaled path in struct libmnt_table
+ * -- note that mountpoint in /proc/self/mountinfo is already
+ * canonicalized by kernel
+ */
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ char *p;
+
+ if (!fs->target
+ || mnt_fs_is_swaparea(fs)
+ || mnt_fs_is_kernel(fs)
+ || (*fs->target == '/' && *(fs->target + 1) == '\0'))
+ continue;
+
+ p = mnt_resolve_path(fs->target, tb->cache);
+ /* both canonicalized, strcmp() is fine here */
+ if (p && strcmp(cn, p) == 0)
+ return fs;
+ }
+ return NULL;
+}
+
+/**
+ * mnt_table_find_srcpath:
+ * @tb: tab pointer
+ * @path: source path (devname or dirname) or NULL
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * Try to lookup an entry in given tab, possible are four iterations, first
+ * with @path, second with realpath(@path), third with tags (LABEL, UUID, ..)
+ * from @path and fourth with realpath(@path) against realpath(entry->srcpath).
+ *
+ * The 2nd, 3rd and 4th iterations are not performed when @tb cache is not
+ * set (see mnt_table_set_cache()).
+ *
+ * Note that NULL is a valid source path; it will be replaced with "none". The
+ * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
+ *
+ * Returns: a tab entry or NULL.
+ */
+struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction)
+{
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs = NULL;
+ int ntags = 0;
+ char *cn;
+ const char *p;
+
+ assert(tb);
+
+ DBG(TAB, mnt_debug_h(tb, "lookup srcpath: %s", path));
+
+ /* native paths */
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_streq_srcpath(fs, path))
+ return fs;
+ if (mnt_fs_get_tag(fs, NULL, NULL) == 0)
+ ntags++;
+ }
+
+ if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
+ return NULL;
+
+ /* canonicalized paths in struct libmnt_table */
+ if (ntags < mnt_table_get_nents(tb)) {
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_streq_srcpath(fs, cn))
+ return fs;
+ }
+ }
+
+ /* evaluated tag */
+ if (ntags) {
+ int rc = mnt_cache_read_tags(tb->cache, cn);
+
+ mnt_reset_iter(&itr, direction);
+
+ if (rc == 0) {
+ /* @path's TAGs are in the cache */
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ const char *t, *v;
+
+ if (mnt_fs_get_tag(fs, &t, &v))
+ continue;
+
+ if (mnt_cache_device_has_tag(tb->cache, cn, t, v))
+ return fs;
+ }
+ } else if (rc < 0 && errno == EACCES) {
+ /* @path is unaccessible, try evaluate all TAGs in @tb
+ * by udev symlinks -- this could be expensive on systems
+ * with huge fstab/mtab */
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ const char *t, *v, *x;
+ if (mnt_fs_get_tag(fs, &t, &v))
+ continue;
+ x = mnt_resolve_tag(t, v, tb->cache);
+
+ /* both canonicalized, strcmp() is fine here */
+ if (x && strcmp(x, cn) == 0)
+ return fs;
+ }
+ }
+ }
+
+ /* non-canonicalized paths in struct libmnt_table */
+ if (ntags <= mnt_table_get_nents(tb)) {
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs))
+ continue;
+ p = mnt_fs_get_srcpath(fs);
+ if (p)
+ p = mnt_resolve_path(p, tb->cache);
+
+ /* both canonicalized, strcmp() is fine here */
+ if (p && strcmp(p, cn) == 0)
+ return fs;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * mnt_table_find_tag:
+ * @tb: tab pointer
+ * @tag: tag name (e.g "LABEL", "UUID", ...)
+ * @val: tag value
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * Try to lookup an entry in given tab, first attempt is lookup by @tag and
+ * @val, for the second attempt the tag is evaluated (converted to the device
+ * name) and mnt_table_find_srcpath() is preformed. The second attempt is not
+ * performed when @tb cache is not set (see mnt_table_set_cache()).
+
+ * Returns: a tab entry or NULL.
+ */
+struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
+ const char *val, int direction)
+{
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs = NULL;
+
+ assert(tb);
+ assert(tag);
+ assert(val);
+
+ if (!tb || !tag || !val)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup by TAG: %s %s", tag, val));
+
+ /* look up by TAG */
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (fs->tagname && fs->tagval &&
+ strcmp(fs->tagname, tag) == 0 &&
+ strcmp(fs->tagval, val) == 0)
+ return fs;
+ }
+
+ if (tb->cache) {
+ /* look up by device name */
+ char *cn = mnt_resolve_tag(tag, val, tb->cache);
+ if (cn)
+ return mnt_table_find_srcpath(tb, cn, direction);
+ }
+ return NULL;
+}
+
+/**
+ * mnt_table_find_source:
+ * @tb: tab pointer
+ * @source: TAG or path
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * This is high-level API for mnt_table_find_{srcpath,tag}. You needn't to care
+ * about @source format (device, LABEL, UUID, ...). This function parses @source
+ * and calls mnt_table_find_tag() or mnt_table_find_srcpath().
+ *
+ * Returns: a tab entry or NULL.
+ */
+struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
+ const char *source, int direction)
+{
+ struct libmnt_fs *fs = NULL;
+
+ assert(tb);
+
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s", source));
+
+ if (source && strchr(source, '=')) {
+ char *tag, *val;
+
+ if (blkid_parse_tag_string(source, &tag, &val) == 0) {
+
+ fs = mnt_table_find_tag(tb, tag, val, direction);
+
+ free(tag);
+ free(val);
+ }
+ } else
+ fs = mnt_table_find_srcpath(tb, source, direction);
+
+ return fs;
+}
+
+/**
+ * mnt_table_find_pair
+ * @tb: tab pointer
+ * @source: TAG or path
+ * @target: mountpoint
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * This function is implemented by mnt_fs_match_source() and
+ * mnt_fs_match_target() functions. It means that this is more expensive that
+ * others mnt_table_find_* function, because every @tab entry is fully evaluated.
+ *
+ * Returns: a tab entry or NULL.
+ */
+struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source,
+ const char *target, int direction)
+{
+ struct libmnt_fs *fs = NULL;
+ struct libmnt_iter itr;
+
+ assert(tb);
+ assert(target);
+
+ if (!tb || !target)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s TARGET: %s", source, target));
+
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+
+ if (mnt_fs_match_target(fs, target, tb->cache) &&
+ mnt_fs_match_source(fs, source, tb->cache))
+ return fs;
+ }
+
+ return NULL;
+}
+
+/**
+ * mnt_table_find_devno
+ * @tb: /proc/self/mountinfo
+ * @devno: device number
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * Note that zero could be valid device number for root pseudo filesystem (e.g.
+ * tmpfs).
+ *
+ * Returns: a tab entry or NULL.
+ */
+struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
+ dev_t devno, int direction)
+{
+ struct libmnt_fs *fs = NULL;
+ struct libmnt_iter itr;
+
+ assert(tb);
+
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "lookup DEVNO: %d", (int) devno));
+
+ mnt_reset_iter(&itr, direction);
+
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_get_devno(fs) == devno)
+ return fs;
+ }
+
+ return NULL;
+}
+
+/*
+ * tb: /proc/self/mountinfo
+ * fs: filesystem
+ * mountflags: MS_BIND or 0
+ * fsroot: fs-root that will be probably used in the mountinfo file
+ * for @fs after mount(2)
+ *
+ * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
+ *
+ * Returns: entry from @tb that will be used as a source for @fs if the @fs is
+ * bindmount.
+ *
+ * Don't export to library API!
+ */
+struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
+ struct libmnt_fs *fs,
+ unsigned long mountflags,
+ char **fsroot)
+{
+ char *root = NULL, *mnt = NULL;
+ const char *fstype;
+ struct libmnt_fs *src_fs = NULL;
+
+ assert(fs);
+ assert(fsroot);
+
+ DBG(TAB, mnt_debug("lookup fs-root for %s", mnt_fs_get_source(fs)));
+
+ fstype = mnt_fs_get_fstype(fs);
+
+ if (tb && (mountflags & MS_BIND)) {
+ const char *src, *src_root;
+ char *xsrc = NULL;
+
+ DBG(TAB, mnt_debug("fs-root for bind"));
+
+ src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache);
+ if (src)
+ mnt = mnt_get_mountpoint(src);
+ if (mnt)
+ root = mnt_get_fs_root(src, mnt);
+
+ if (xsrc && !tb->cache) {
+ free(xsrc);
+ src = NULL;
+ }
+ if (!mnt)
+ goto err;
+
+ src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD);
+ if (!src_fs) {
+ DBG(TAB, mnt_debug("not found '%s' in mountinfo -- using default", mnt));
+ goto dflt;
+ }
+
+ /* on btrfs the subvolume is used as fs-root in
+ * /proc/self/mountinfo, so we have to get the original subvolume
+ * name from src_fs and prepend the subvolume name to the
+ * fs-root path
+ */
+ src_root = mnt_fs_get_root(src_fs);
+ if (src_root && !startswith(root, src_root)) {
+ size_t sz = strlen(root) + strlen(src_root) + 1;
+ char *tmp = malloc(sz);
+
+ if (!tmp)
+ goto err;
+ snprintf(tmp, sz, "%s%s", src_root, root);
+ free(root);
+ root = tmp;
+ }
+ }
+
+ /*
+ * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
+ */
+ else if (fstype && !strcmp(fstype, "btrfs")) {
+ char *vol = NULL, *p;
+ size_t sz, volsz = 0;
+
+ if (mnt_fs_get_option(fs, "subvol", &vol, &volsz))
+ goto dflt;
+
+ DBG(TAB, mnt_debug("setting FS root: btrfs subvol"));
+
+ sz = volsz;
+ if (*vol != '/')
+ sz++;
+ root = malloc(sz + 1);
+ if (!root)
+ goto err;
+ p = root;
+ if (*vol != '/')
+ *p++ = '/';
+ memcpy(p, vol, volsz);
+ *(root + sz) = '\0';
+ }
+dflt:
+ if (!root) {
+ root = strdup("/");
+ if (!root)
+ goto err;
+ }
+ *fsroot = root;
+
+ DBG(TAB, mnt_debug("FS root result: %s", root));
+
+ free(mnt);
+ return src_fs;
+err:
+ free(root);
+ free(mnt);
+ return NULL;
+}
+
+static int is_mountinfo(struct libmnt_table *tb)
+{
+ struct libmnt_fs *fs;
+
+ if (!tb)
+ return 0;
+
+ fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
+ if (fs && mnt_fs_is_kernel(fs) && mnt_fs_get_root(fs))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * mnt_table_is_mounted:
+ * @tb: /proc/self/mountinfo file
+ * @fstab_fs: /etc/fstab entry
+ *
+ * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
+ * ignored. This function explicitly compares source, target and root of the
+ * filesystems.
+ *
+ * Note that source and target are canonicalized only if a cache for @tb is
+ * defined (see mnt_table_set_cache()). The target canonicalization may
+ * triggers automount on autofs mountpoints!
+ *
+ * Don't use it if you want to know if a device is mounted, just use
+ * mnt_table_find_source() for the device.
+ *
+ * This function is designed mostly for "mount -a".
+ *
+ * TODO: check for loopdev (see mount/mount.c is_fstab_entry_mounted().
+ *
+ * Returns: 0 or 1
+ */
+int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
+{
+ char *root = NULL;
+ const char *src = NULL, *tgt = NULL;
+ char *xtgt = NULL;
+ int rc = 0;
+
+ assert(tb);
+ assert(fstab_fs);
+
+ if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_get_nents(tb) == 0)
+ return 0;
+
+ if (is_mountinfo(tb)) {
+ /* @tb is mountinfo, so we can try to use fs-roots */
+ struct libmnt_fs *fs;
+ int flags = 0;
+
+ if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0)
+ flags = MS_BIND;
+
+ fs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
+ if (fs)
+ src = mnt_fs_get_srcpath(fs);
+ }
+
+ if (!src)
+ src = mnt_fs_get_source(fstab_fs);
+
+ if (src && tb->cache && !mnt_fs_is_pseudofs(fstab_fs))
+ src = mnt_resolve_spec(src, tb->cache);
+
+ tgt = mnt_fs_get_target(fstab_fs);
+
+ if (tgt && src) {
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
+
+ if (!mnt_fs_streq_srcpath(fs, src))
+ continue;
+
+ if (root) {
+ const char *r = mnt_fs_get_root(fs);
+ if (!r || strcmp(r, root) != 0)
+ continue;
+ }
+
+ /*
+ * Compare target, try to minimize number of situations
+ * when we need to canonicalize the path to avoid
+ * readlink() on mountpoints.
+ */
+ if (!xtgt) {
+ if (mnt_fs_streq_target(fs, tgt))
+ break;
+ if (tb->cache)
+ xtgt = mnt_resolve_path(tgt, tb->cache);
+ }
+ if (xtgt && mnt_fs_streq_target(fs, xtgt))
+ break;
+
+ }
+ if (fs)
+ rc = 1; /* success */
+ }
+
+ free(root);
+ return rc;
+}
+
+#ifdef TEST_PROGRAM
+
+static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
+{
+ fprintf(stderr, "%s:%d: parse error\n", filename, line);
+
+ return 1; /* all errors are recoverable -- this is default */
+}
+
+struct libmnt_table *create_table(const char *file)
+{
+ struct libmnt_table *tb;
+
+ if (!file)
+ return NULL;
+ tb = mnt_new_table();
+ if (!tb)
+ goto err;
+
+ mnt_table_set_parser_errcb(tb, parser_errcb);
+
+ if (mnt_table_parse_file(tb, file) != 0)
+ goto err;
+ return tb;
+err:
+ fprintf(stderr, "%s: parsing failed\n", file);
+ mnt_free_table(tb);
+ return NULL;
+}
+
+int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_table *tb;
+ struct libmnt_fs *fs;
+ int rc = -1;
+
+ tb = create_table(argv[1]);
+ if (!tb)
+ return -1;
+
+ fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD);
+ if (!fs)
+ goto done;
+
+ printf("ORIGINAL:\n");
+ mnt_fs_print_debug(fs, stdout);
+
+ fs = mnt_copy_fs(NULL, fs);
+ if (!fs)
+ goto done;
+
+ printf("COPY:\n");
+ mnt_fs_print_debug(fs, stdout);
+ mnt_free_fs(fs);
+ rc = 0;
+done:
+ mnt_free_table(tb);
+ return rc;
+}
+
+int test_parse(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_table *tb = NULL;
+ struct libmnt_iter *itr = NULL;
+ struct libmnt_fs *fs;
+ int rc = -1;
+
+ tb = create_table(argv[1]);
+ if (!tb)
+ return -1;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ goto done;
+
+ while(mnt_table_next_fs(tb, itr, &fs) == 0)
+ mnt_fs_print_debug(fs, stdout);
+ rc = 0;
+done:
+ mnt_free_iter(itr);
+ mnt_free_table(tb);
+ return rc;
+}
+
+int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
+{
+ struct libmnt_table *tb;
+ struct libmnt_fs *fs = NULL;
+ struct libmnt_cache *mpc = NULL;
+ const char *file, *find, *what;
+ int rc = -1;
+
+ if (argc != 4) {
+ fprintf(stderr, "try --help\n");
+ return -EINVAL;
+ }
+
+ file = argv[1], find = argv[2], what = argv[3];
+
+ tb = create_table(file);
+ if (!tb)
+ goto done;
+
+ /* create a cache for canonicalized paths */
+ mpc = mnt_new_cache();
+ if (!mpc)
+ goto done;
+ mnt_table_set_cache(tb, mpc);
+
+ if (strcasecmp(find, "source") == 0)
+ fs = mnt_table_find_source(tb, what, dr);
+ else if (strcasecmp(find, "target") == 0)
+ fs = mnt_table_find_target(tb, what, dr);
+
+ if (!fs)
+ fprintf(stderr, "%s: not found %s '%s'\n", file, find, what);
+ else {
+ mnt_fs_print_debug(fs, stdout);
+ rc = 0;
+ }
+done:
+ mnt_free_table(tb);
+ mnt_free_cache(mpc);
+ return rc;
+}
+
+int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
+{
+ return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
+}
+
+int test_find_fw(struct libmnt_test *ts, int argc, char *argv[])
+{
+ return test_find(ts, argc, argv, MNT_ITER_FORWARD);
+}
+
+int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_table *tb;
+ struct libmnt_fs *fs;
+ struct libmnt_cache *mpc = NULL;
+ int rc = -1;
+
+ tb = create_table(argv[1]);
+ if (!tb)
+ return -1;
+ mpc = mnt_new_cache();
+ if (!mpc)
+ goto done;
+ mnt_table_set_cache(tb, mpc);
+
+ fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD);
+ if (!fs)
+ goto done;
+
+ mnt_fs_print_debug(fs, stdout);
+ rc = 0;
+done:
+ mnt_free_table(tb);
+ mnt_free_cache(mpc);
+ return rc;
+}
+
+static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_table *tb = NULL, *fstab = NULL;
+ struct libmnt_fs *fs;
+ struct libmnt_iter *itr = NULL;
+ struct libmnt_cache *mpc = NULL;
+ int rc;
+
+ tb = mnt_new_table_from_file("/proc/self/mountinfo");
+ if (!tb) {
+ fprintf(stderr, "failed to parse mountinfo\n");
+ return -1;
+ }
+
+ fstab = create_table(argv[1]);
+ if (!fstab)
+ goto done;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ goto done;
+
+ mpc = mnt_new_cache();
+ if (!mpc)
+ goto done;
+ mnt_table_set_cache(tb, mpc);
+
+ while(mnt_table_next_fs(fstab, itr, &fs) == 0) {
+ if (mnt_table_is_fs_mounted(tb, fs))
+ printf("%s already mounted on %s\n",
+ mnt_fs_get_source(fs),
+ mnt_fs_get_target(fs));
+ else
+ printf("%s not mounted on %s\n",
+ mnt_fs_get_source(fs),
+ mnt_fs_get_target(fs));
+ }
+
+ rc = 0;
+done:
+ mnt_free_table(tb);
+ mnt_free_table(fstab);
+ mnt_free_cache(mpc);
+ mnt_free_iter(itr);
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--parse", test_parse, "<file> parse and print tab" },
+ { "--find-forward", test_find_fw, "<file> <source|target> <string>" },
+ { "--find-backward", test_find_bw, "<file> <source|target> <string>" },
+ { "--find-pair", test_find_pair, "<file> <source> <target>" },
+ { "--copy-fs", test_copy_fs, "<file> copy root FS from the file" },
+ { "--is-mounted", test_is_mounted, "<fstab> check what from <file> are already mounted" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c
new file mode 100644
index 0000000..f01f889
--- /dev/null
+++ b/libmount/src/tab_diff.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: tabdiff
+ * @title: Monitor mountinfo changes
+ * @short_description: monitor changes in the list of the mounted filesystems
+ */
+#include "mountP.h"
+
+struct tabdiff_entry {
+ int oper; /* MNT_TABDIFF_* flags; */
+
+ struct libmnt_fs *old_fs; /* pointer to the old FS */
+ struct libmnt_fs *new_fs; /* pointer to the new FS */
+
+ struct list_head changes;
+};
+
+struct libmnt_tabdiff {
+ int nchanges; /* number of changes */
+
+ struct list_head changes; /* list with modified entries */
+ struct list_head unused; /* list with unuused entries */
+};
+
+/**
+ * mnt_new_tabdiff:
+ *
+ * Allocates a new table diff struct.
+ *
+ * Returns: new diff handler or NULL.
+ */
+struct libmnt_tabdiff *mnt_new_tabdiff(void)
+{
+ struct libmnt_tabdiff *df = calloc(1, sizeof(*df));
+
+ if (!df)
+ return NULL;
+
+ DBG(DIFF, mnt_debug_h(df, "alloc"));
+
+ INIT_LIST_HEAD(&df->changes);
+ INIT_LIST_HEAD(&df->unused);
+ return df;
+}
+
+static void free_tabdiff_entry(struct tabdiff_entry *de)
+{
+ if (!de)
+ return;
+ list_del(&de->changes);
+ free(de);
+}
+
+/**
+ * mnt_free_tabdiff:
+ * @df: tab diff
+ *
+ * Deallocates tab diff struct and all entries.
+ */
+void mnt_free_tabdiff(struct libmnt_tabdiff *df)
+{
+ if (!df)
+ return;
+
+ DBG(DIFF, mnt_debug_h(df, "free"));
+
+ while (!list_empty(&df->changes)) {
+ struct tabdiff_entry *de = list_entry(df->changes.next,
+ struct tabdiff_entry, changes);
+ free_tabdiff_entry(de);
+ }
+
+ free(df);
+}
+
+/**
+ * mnt_tabdiff_next_change:
+ * @df: tabdiff pointer
+ * @itr: iterator
+ * @old_fs: returns the old entry or NULL if new entry added
+ * @new_fs: returns the new entry or NULL if old entry removed
+ * @oper: MNT_TABDIFF_{MOVE,UMOUNT,REMOUNT,MOUNT} flags
+ *
+ * The options @old_fs, @new_fs and @oper are optional.
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at end of list.
+ */
+int mnt_tabdiff_next_change(struct libmnt_tabdiff *df, struct libmnt_iter *itr,
+ struct libmnt_fs **old_fs, struct libmnt_fs **new_fs, int *oper)
+{
+ int rc = 1;
+ struct tabdiff_entry *de = NULL;
+
+ assert(df);
+ assert(df);
+
+ if (!df || !itr)
+ return -EINVAL;
+
+ if (!itr->head)
+ MNT_ITER_INIT(itr, &df->changes);
+ if (itr->p != itr->head) {
+ MNT_ITER_ITERATE(itr, de, struct tabdiff_entry, changes);
+ rc = 0;
+ }
+
+ if (old_fs)
+ *old_fs = de ? de->old_fs : NULL;
+ if (new_fs)
+ *new_fs = de ? de->new_fs : NULL;
+ if (oper)
+ *oper = de ? de->oper : 0;
+
+ return rc;
+}
+
+static int tabdiff_reset(struct libmnt_tabdiff *df)
+{
+ assert(df);
+
+ DBG(DIFF, mnt_debug_h(df, "reseting"));
+
+ /* zeroize all entries and move them to the list of unuused
+ */
+ while (!list_empty(&df->changes)) {
+ struct tabdiff_entry *de = list_entry(df->changes.next,
+ struct tabdiff_entry, changes);
+
+ list_del(&de->changes);
+ list_add_tail(&de->changes, &df->unused);
+
+ de->new_fs = de->old_fs = NULL;
+ de->oper = 0;
+ }
+
+ df->nchanges = 0;
+ return 0;
+}
+
+static int tabdiff_add_entry(struct libmnt_tabdiff *df, struct libmnt_fs *old,
+ struct libmnt_fs *new, int oper)
+{
+ struct tabdiff_entry *de;
+
+ assert(df);
+
+ DBG(DIFF, mnt_debug_h(df, "add change on %s",
+ mnt_fs_get_target(new ? new : old)));
+
+ if (!list_empty(&df->unused)) {
+ de = list_entry(df->unused.next, struct tabdiff_entry, changes);
+ list_del(&de->changes);
+ } else {
+ de = calloc(1, sizeof(*de));
+ if (!de)
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&de->changes);
+
+ de->old_fs = old;
+ de->new_fs = new;
+ de->oper = oper;
+
+ list_add_tail(&de->changes, &df->changes);
+ df->nchanges++;
+ return 0;
+}
+
+static struct tabdiff_entry *tabdiff_get_mount(struct libmnt_tabdiff *df,
+ const char *src,
+ int id)
+{
+ struct list_head *p;
+
+ assert(df);
+
+ list_for_each(p, &df->changes) {
+ struct tabdiff_entry *de;
+
+ de = list_entry(p, struct tabdiff_entry, changes);
+
+ if (de->oper == MNT_TABDIFF_MOUNT && de->new_fs &&
+ mnt_fs_get_id(de->new_fs) == id) {
+
+ const char *s = mnt_fs_get_source(de->new_fs);
+
+ if (s == NULL && src == NULL)
+ return de;
+ if (s && src && strcmp(s, src) == 0)
+ return de;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * mnt_diff_tables:
+ * @df: diff handler
+ * @old_tab: old table
+ * @new_tab: new table
+ *
+ * Compares @old_tab and @new_tab, the result is stored in @df and accessible by
+ * mnt_tabdiff_next_change().
+ *
+ * Returns: number of changes, negative number in case of error.
+ */
+int mnt_diff_tables(struct libmnt_tabdiff *df, struct libmnt_table *old_tab,
+ struct libmnt_table *new_tab)
+{
+ struct libmnt_fs *fs;
+ struct libmnt_iter itr;
+ int no, nn;
+
+ if (!df || !old_tab || !new_tab)
+ return -EINVAL;
+
+ tabdiff_reset(df);
+
+ no = mnt_table_get_nents(old_tab);
+ nn = mnt_table_get_nents(new_tab);
+
+ if (!no && !nn) /* both tables are empty */
+ return 0;
+
+ DBG(DIFF, mnt_debug_h(df, "analyze new=%p (%d entries), "
+ "old=%p (%d entries)",
+ new_tab, nn, old_tab, no));
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ /* all mounted or umounted */
+ if (!no && nn) {
+ while(mnt_table_next_fs(new_tab, &itr, &fs) == 0)
+ tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT);
+ goto done;
+
+ } else if (no && !nn) {
+ while(mnt_table_next_fs(old_tab, &itr, &fs) == 0)
+ tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT);
+ goto done;
+ }
+
+ /* search newly mounted or modified */
+ while(mnt_table_next_fs(new_tab, &itr, &fs) == 0) {
+ struct libmnt_fs *o_fs;
+ const char *src = mnt_fs_get_source(fs),
+ *tgt = mnt_fs_get_target(fs);
+
+ o_fs = mnt_table_find_pair(old_tab, src, tgt, MNT_ITER_FORWARD);
+ if (!o_fs)
+ /* 'fs' is not in the old table -- so newly mounted */
+ tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT);
+ else {
+ /* is modified? */
+ const char *v1 = mnt_fs_get_vfs_options(o_fs),
+ *v2 = mnt_fs_get_vfs_options(fs),
+ *f1 = mnt_fs_get_fs_options(o_fs),
+ *f2 = mnt_fs_get_fs_options(fs);
+
+ if ((v1 && v2 && strcmp(v1, v2)) || (f1 && f2 && strcmp(f1, f2)))
+ tabdiff_add_entry(df, o_fs, fs, MNT_TABDIFF_REMOUNT);
+ }
+ }
+
+ /* search umounted or moved */
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+ while(mnt_table_next_fs(old_tab, &itr, &fs) == 0) {
+ const char *src = mnt_fs_get_source(fs),
+ *tgt = mnt_fs_get_target(fs);
+
+ if (!mnt_table_find_pair(new_tab, src, tgt, MNT_ITER_FORWARD)) {
+ struct tabdiff_entry *de;
+
+ de = tabdiff_get_mount(df, src, mnt_fs_get_id(fs));
+ if (de) {
+ de->oper = MNT_TABDIFF_MOVE;
+ de->old_fs = fs;
+ } else
+ tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT);
+ }
+ }
+done:
+ DBG(DIFF, mnt_debug_h(df, "%d changes detected", df->nchanges));
+ return df->nchanges;
+}
+
+#ifdef TEST_PROGRAM
+
+int test_diff(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_table *tb_old = NULL, *tb_new = NULL;
+ struct libmnt_tabdiff *diff = NULL;
+ struct libmnt_iter *itr;
+ struct libmnt_fs *old, *new;
+ int rc = -1, change;
+
+ tb_old = mnt_new_table_from_file(argv[1]);
+ tb_new = mnt_new_table_from_file(argv[2]);
+ diff = mnt_new_tabdiff();
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+
+ if (!tb_old || !tb_new || !diff || !itr) {
+ warnx("failed to allocate resources");
+ goto done;
+ }
+
+ rc = mnt_diff_tables(diff, tb_old, tb_new);
+ if (rc < 0)
+ goto done;
+
+ while(mnt_tabdiff_next_change(diff, itr, &old, &new, &change) == 0) {
+
+ printf("%s on %s: ", mnt_fs_get_source(new ? new : old),
+ mnt_fs_get_target(new ? new : old));
+
+ switch(change) {
+ case MNT_TABDIFF_MOVE:
+ printf("MOVED to %s\n", mnt_fs_get_target(new));
+ break;
+ case MNT_TABDIFF_UMOUNT:
+ printf("UMOUNTED\n");
+ break;
+ case MNT_TABDIFF_REMOUNT:
+ printf("REMOUNTED from '%s' to '%s'\n",
+ mnt_fs_get_options(old),
+ mnt_fs_get_options(new));
+ break;
+ case MNT_TABDIFF_MOUNT:
+ printf("MOUNTED\n");
+ break;
+ default:
+ printf("unknown change!\n");
+ }
+ }
+
+ rc = 0;
+done:
+ mnt_free_table(tb_old);
+ mnt_free_table(tb_new);
+ mnt_free_tabdiff(diff);
+ mnt_free_iter(itr);
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--diff", test_diff, "<old> <new> prints change" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c
new file mode 100644
index 0000000..ab5d51a
--- /dev/null
+++ b/libmount/src/tab_parse.c
@@ -0,0 +1,944 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifdef HAVE_SCANDIRAT
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif /* !__USE_GNU */
+#endif /* HAVE_SCANDIRAT */
+
+#include <ctype.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "at.h"
+#include "mangle.h"
+#include "mountP.h"
+#include "pathnames.h"
+#include "strutils.h"
+
+static inline char *skip_spaces(char *s)
+{
+ assert(s);
+
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return s;
+}
+
+static int next_number(char **s, int *num)
+{
+ char *end = NULL;
+
+ assert(num);
+ assert(s);
+
+ *s = skip_spaces(*s);
+ if (!**s)
+ return -1;
+ *num = strtol(*s, &end, 10);
+ if (end == NULL || *s == end)
+ return -1;
+
+ *s = end;
+
+ /* valid end of number is space or terminator */
+ if (*end == ' ' || *end == '\t' || *end == '\0')
+ return 0;
+ return -1;
+}
+
+/*
+ * Parses one line from {fs,m}tab
+ */
+static int mnt_parse_table_line(struct libmnt_fs *fs, char *s)
+{
+ int rc, n = 0, xrc;
+ char *src = NULL, *fstype = NULL, *optstr = NULL;
+
+ rc = sscanf(s, UL_SCNsA" " /* (1) source */
+ UL_SCNsA" " /* (2) target */
+ UL_SCNsA" " /* (3) FS type */
+ UL_SCNsA" " /* (4) options */
+ "%n", /* byte count */
+
+ &src,
+ &fs->target,
+ &fstype,
+ &optstr,
+ &n);
+ xrc = rc;
+
+ if (rc == 3 || rc == 4) { /* options are optional */
+ unmangle_string(src);
+ unmangle_string(fs->target);
+ unmangle_string(fstype);
+
+ if (optstr && *optstr)
+ unmangle_string(optstr);
+
+ /* note that __foo functions does not reallocate the string
+ */
+ rc = __mnt_fs_set_source_ptr(fs, src);
+ if (!rc) {
+ src = NULL;
+ rc = __mnt_fs_set_fstype_ptr(fs, fstype);
+ if (!rc)
+ fstype = NULL;
+ }
+ if (!rc && optstr)
+ rc = mnt_fs_set_options(fs, optstr);
+ free(optstr);
+ optstr = NULL;
+ } else {
+ DBG(TAB, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s));
+ rc = -EINVAL;
+ }
+
+ if (rc) {
+ free(src);
+ free(fstype);
+ free(optstr);
+ DBG(TAB, mnt_debug("tab parse error: [set vars, rc=%d]\n", rc));
+ return rc; /* error */
+ }
+
+ fs->passno = fs->freq = 0;
+
+ if (xrc == 4 && n)
+ s = skip_spaces(s + n);
+ if (xrc == 4 && *s) {
+ if (next_number(&s, &fs->freq) != 0) {
+ if (*s) {
+ DBG(TAB, mnt_debug("tab parse error: [freq]"));
+ rc = -EINVAL;
+ }
+ } else if (next_number(&s, &fs->passno) != 0 && *s) {
+ DBG(TAB, mnt_debug("tab parse error: [passno]"));
+ rc = -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Parses one line from mountinfo file
+ */
+static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s)
+{
+ int rc, end = 0;
+ unsigned int maj, min;
+ char *fstype = NULL, *src = NULL, *p;
+
+ rc = sscanf(s, "%u " /* (1) id */
+ "%u " /* (2) parent */
+ "%u:%u " /* (3) maj:min */
+ UL_SCNsA" " /* (4) mountroot */
+ UL_SCNsA" " /* (5) target */
+ UL_SCNsA /* (6) vfs options (fs-independent) */
+ "%n", /* number of read bytes */
+
+ &fs->id,
+ &fs->parent,
+ &maj, &min,
+ &fs->root,
+ &fs->target,
+ &fs->vfs_optstr,
+ &end);
+
+ if (rc >= 7 && end > 0)
+ s += end;
+
+ /* (7) optional fields, terminated by " - " */
+ p = strstr(s, " - ");
+ if (!p) {
+ DBG(TAB, mnt_debug("mountinfo parse error: not found separator"));
+ return -EINVAL;
+ }
+ s = p + 3;
+
+ rc += sscanf(s, UL_SCNsA" " /* (8) FS type */
+ UL_SCNsA" " /* (9) source */
+ UL_SCNsA, /* (10) fs options (fs specific) */
+
+ &fstype,
+ &src,
+ &fs->fs_optstr);
+
+ if (rc >= 10) {
+ fs->flags |= MNT_FS_KERNEL;
+ fs->devno = makedev(maj, min);
+
+ unmangle_string(fs->root);
+ unmangle_string(fs->target);
+ unmangle_string(fs->vfs_optstr);
+ unmangle_string(fstype);
+ unmangle_string(src);
+ unmangle_string(fs->fs_optstr);
+
+ rc = __mnt_fs_set_fstype_ptr(fs, fstype);
+ if (!rc) {
+ fstype = NULL;
+ rc = __mnt_fs_set_source_ptr(fs, src);
+ if (!rc)
+ src = NULL;
+ }
+
+ /* merge VFS and FS options to the one string */
+ fs->optstr = mnt_fs_strdup_options(fs);
+ if (!fs->optstr)
+ rc = -ENOMEM;
+ } else {
+ free(fstype);
+ free(src);
+ DBG(TAB, mnt_debug(
+ "mountinfo parse error [sscanf rc=%d]: '%s'", rc, s));
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/*
+ * Parses one line from utab file
+ */
+static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s)
+{
+ const char *p = s;
+
+ assert(fs);
+ assert(s);
+ assert(!fs->source);
+ assert(!fs->target);
+
+ while (p && *p) {
+ char *end = NULL;
+
+ while (*p == ' ') p++;
+ if (!*p)
+ break;
+
+ if (!fs->source && !strncmp(p, "SRC=", 4)) {
+ char *v = unmangle(p + 4, &end);
+ if (!v)
+ goto enomem;
+ __mnt_fs_set_source_ptr(fs, v);
+
+ } else if (!fs->target && !strncmp(p, "TARGET=", 7)) {
+ fs->target = unmangle(p + 7, &end);
+ if (!fs->target)
+ goto enomem;
+
+ } else if (!fs->root && !strncmp(p, "ROOT=", 5)) {
+ fs->root = unmangle(p + 5, &end);
+ if (!fs->root)
+ goto enomem;
+
+ } else if (!fs->bindsrc && !strncmp(p, "BINDSRC=", 8)) {
+ fs->bindsrc = unmangle(p + 8, &end);
+ if (!fs->bindsrc)
+ goto enomem;
+
+ } else if (!fs->user_optstr && !strncmp(p, "OPTS=", 5)) {
+ fs->user_optstr = unmangle(p + 5, &end);
+ if (!fs->user_optstr)
+ goto enomem;
+
+ } else if (!fs->attrs && !strncmp(p, "ATTRS=", 6)) {
+ fs->attrs = unmangle(p + 6, &end);
+ if (!fs->attrs)
+ goto enomem;
+
+ } else {
+ /* unknown variable */
+ while (*p && *p != ' ') p++;
+ }
+ if (end)
+ p = end;
+ }
+
+ return 0;
+enomem:
+ DBG(TAB, mnt_debug("utab parse error: ENOMEM"));
+ return -ENOMEM;
+}
+
+/*
+ * Parses one line from /proc/swaps
+ */
+static int mnt_parse_swaps_line(struct libmnt_fs *fs, char *s)
+{
+ uintmax_t fsz, usz;
+ int rc;
+ char *src = NULL;
+
+ rc = sscanf(s, UL_SCNsA" " /* (1) source */
+ UL_SCNsA" " /* (2) type */
+ "%jd" /* (3) size */
+ "%jd" /* (4) used */
+ "%d", /* priority */
+
+ &src,
+ &fs->swaptype,
+ &fsz,
+ &usz,
+ &fs->priority);
+
+ if (rc == 5) {
+ size_t sz;
+
+ fs->size = fsz;
+ fs->usedsize = usz;
+
+ unmangle_string(src);
+
+ /* remove "(deleted)" suffix */
+ sz = strlen(src);
+ if (sz > PATH_DELETED_SUFFIX_SZ) {
+ char *p = src + (sz - PATH_DELETED_SUFFIX_SZ);
+ if (strcmp(p, PATH_DELETED_SUFFIX) == 0)
+ *p = '\0';
+ }
+
+ rc = mnt_fs_set_source(fs, src);
+ if (!rc)
+ mnt_fs_set_fstype(fs, "swap");
+ free(src);
+ } else {
+ DBG(TAB, mnt_debug("tab parse error: [sscanf rc=%d]: '%s'", rc, s));
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+
+/*
+ * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*)
+ *
+ * Note that we aren't trying to guess utab file format, because this file has
+ * to be always parsed by private libmount routines with explicitly defined
+ * format.
+ *
+ * mountinfo: "<number> <number> ... "
+ */
+static int guess_table_format(char *line)
+{
+ unsigned int a, b;
+
+ DBG(TAB, mnt_debug("trying to guess table type"));
+
+ if (sscanf(line, "%u %u", &a, &b) == 2)
+ return MNT_FMT_MOUNTINFO;
+
+ if (strncmp(line, "Filename\t", 9) == 0)
+ return MNT_FMT_SWAPS;
+
+ return MNT_FMT_FSTAB; /* fstab, mtab or /proc/mounts */
+}
+
+/*
+ * Read and parse the next line from {fs,m}tab or mountinfo
+ */
+static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f, struct libmnt_fs *fs,
+ const char *filename, int *nlines)
+{
+ char buf[BUFSIZ];
+ char *s;
+ int rc;
+
+ assert(tb);
+ assert(f);
+ assert(fs);
+
+ /* read the next non-blank non-comment line */
+next_line:
+ do {
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ return -EINVAL;
+ ++*nlines;
+ s = index (buf, '\n');
+ if (!s) {
+ /* Missing final newline? Otherwise extremely */
+ /* long line - assume file was corrupted */
+ if (feof(f)) {
+ DBG(TAB, mnt_debug_h(tb,
+ "%s: no final newline", filename));
+ s = index (buf, '\0');
+ } else {
+ DBG(TAB, mnt_debug_h(tb,
+ "%s:%d: missing newline at line",
+ filename, *nlines));
+ goto err;
+ }
+ }
+ *s = '\0';
+ if (--s >= buf && *s == '\r')
+ *s = '\0';
+ s = skip_spaces(buf);
+ } while (*s == '\0' || *s == '#');
+
+ if (tb->fmt == MNT_FMT_GUESS) {
+ tb->fmt = guess_table_format(s);
+ if (tb->fmt == MNT_FMT_SWAPS)
+ goto next_line; /* skip swap header */
+ }
+
+ switch (tb->fmt) {
+ case MNT_FMT_FSTAB:
+ rc = mnt_parse_table_line(fs, s);
+ break;
+ case MNT_FMT_MOUNTINFO:
+ rc = mnt_parse_mountinfo_line(fs, s);
+ break;
+ case MNT_FMT_UTAB:
+ rc = mnt_parse_utab_line(fs, s);
+ break;
+ case MNT_FMT_SWAPS:
+ if (strncmp(s, "Filename\t", 9) == 0)
+ goto next_line; /* skip swap header */
+ rc = mnt_parse_swaps_line(fs, s);
+ break;
+ default:
+ rc = -1; /* unknown format */
+ break;
+ }
+
+ if (rc == 0)
+ return 0;
+err:
+ DBG(TAB, mnt_debug_h(tb, "%s:%d: %s parse error", filename, *nlines,
+ tb->fmt == MNT_FMT_MOUNTINFO ? "mountinfo" :
+ tb->fmt == MNT_FMT_SWAPS ? "swaps" :
+ tb->fmt == MNT_FMT_FSTAB ? "tab" : "utab"));
+
+ /* by default all errors are recoverable, otherwise behavior depends on
+ * errcb() function. See mnt_table_set_parser_errcb().
+ */
+ return tb->errcb ? tb->errcb(tb, filename, *nlines) : 1;
+}
+
+static pid_t path_to_tid(const char *filename)
+{
+ char *path = mnt_resolve_path(filename, NULL);
+ char *p, *end = NULL;
+ pid_t tid = 0;
+
+ if (!path)
+ goto done;
+ p = strrchr(path, '/');
+ if (!p)
+ goto done;
+ *p = '\0';
+ p = strrchr(path, '/');
+ if (!p)
+ goto done;
+ p++;
+
+ errno = 0;
+ tid = strtol(p, &end, 10);
+ if (errno || p == end || (end && *end)) {
+ tid = 0;
+ goto done;
+ }
+ DBG(TAB, mnt_debug("TID for %s is %d", filename, tid));
+done:
+ free(path);
+ return tid;
+}
+
+/**
+ * mnt_table_parse_stream:
+ * @tb: tab pointer
+ * @f: file stream
+ * @filename: filename used for debug and error messages
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
+{
+ int nlines = 0;
+ int rc = -1;
+ int flags = 0;
+ pid_t tid = -1;
+
+ assert(tb);
+ assert(f);
+ assert(filename);
+
+ DBG(TAB, mnt_debug_h(tb, "%s: start parsing (%d entries)",
+ filename, mnt_table_get_nents(tb)));
+
+ /* necessary for /proc/mounts only, the /proc/self/mountinfo
+ * parser sets the flag properly
+ */
+ if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0)
+ flags = MNT_FS_KERNEL;
+
+ while (!feof(f)) {
+ struct libmnt_fs *fs = mnt_new_fs();
+
+ if (!fs)
+ goto err;
+
+ rc = mnt_table_parse_next(tb, f, fs, filename, &nlines);
+ if (!rc) {
+ rc = mnt_table_add_fs(tb, fs);
+ fs->flags |= flags;
+ if (tb->fmt == MNT_FMT_MOUNTINFO && filename) {
+ if (tid == -1)
+ tid = path_to_tid(filename);
+ fs->tid = tid;
+ }
+ }
+ if (rc) {
+ mnt_free_fs(fs);
+ if (rc == 1)
+ continue; /* recoverable error */
+ if (feof(f))
+ break;
+ goto err; /* fatal error */
+ }
+ }
+
+ DBG(TAB, mnt_debug_h(tb, "%s: stop parsing (%d entries)",
+ filename, mnt_table_get_nents(tb)));
+ return 0;
+err:
+ DBG(TAB, mnt_debug_h(tb, "%s: parse error (rc=%d)", filename, rc));
+ return rc;
+}
+
+/**
+ * mnt_table_parse_file:
+ * @tb: tab pointer
+ * @filename: file
+ *
+ * Parses whole table (e.g. /etc/mtab) and appends new records to the @tab.
+ *
+ * The libmount parser ignores broken (syntax error) lines, these lines are
+ * reported to caller by errcb() function (see mnt_table_set_parser_errcb()).
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_table_parse_file(struct libmnt_table *tb, const char *filename)
+{
+ FILE *f;
+ int rc;
+
+ assert(tb);
+ assert(filename);
+
+ if (!filename || !tb)
+ return -EINVAL;
+
+ f = fopen(filename, "r");
+ if (f) {
+ rc = mnt_table_parse_stream(tb, f, filename);
+ fclose(f);
+ } else
+ return -errno;
+
+ return rc;
+}
+
+static int mnt_table_parse_dir_filter(const struct dirent *d)
+{
+ size_t namesz;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG &&
+ d->d_type != DT_LNK)
+ return 0;
+#endif
+ if (*d->d_name == '.')
+ return 0;
+
+#define MNT_MNTTABDIR_EXTSIZ (sizeof(MNT_MNTTABDIR_EXT) - 1)
+
+ namesz = strlen(d->d_name);
+ if (!namesz || namesz < MNT_MNTTABDIR_EXTSIZ + 1 ||
+ strcmp(d->d_name + (namesz - MNT_MNTTABDIR_EXTSIZ),
+ MNT_MNTTABDIR_EXT))
+ return 0;
+
+ /* Accept this */
+ return 1;
+}
+
+#ifdef HAVE_SCANDIRAT
+static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname)
+{
+ int n = 0, i;
+ int dd;
+ struct dirent **namelist = NULL;
+
+ dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dd < 0)
+ return -errno;
+
+ n = scandirat(dd, ".", &namelist, mnt_table_parse_dir_filter, versionsort);
+ if (n <= 0) {
+ close(dd);
+ return 0;
+ }
+
+ for (i = 0; i < n; i++) {
+ struct dirent *d = namelist[i];
+ struct stat st;
+ FILE *f;
+
+ if (fstat_at(dd, ".", d->d_name, &st, 0) ||
+ !S_ISREG(st.st_mode))
+ continue;
+
+ f = fopen_at(dd, ".", d->d_name, O_RDONLY, "r");
+ if (f) {
+ mnt_table_parse_stream(tb, f, d->d_name);
+ fclose(f);
+ }
+ }
+
+ for (i = 0; i < n; i++)
+ free(namelist[i]);
+ free(namelist);
+ close(dd);
+ return 0;
+}
+#else
+static int __mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname)
+{
+ int n = 0, i, r = 0;
+ DIR *dir = NULL;
+ struct dirent **namelist = NULL;
+
+ n = scandir(dirname, &namelist, mnt_table_parse_dir_filter, versionsort);
+ if (n <= 0)
+ return 0;
+
+ /* let use "at" functions rather than play crazy games with paths... */
+ dir = opendir(dirname);
+ if (!dir) {
+ r = -errno;
+ goto out;
+ }
+
+ for (i = 0; i < n; i++) {
+ struct dirent *d = namelist[i];
+ struct stat st;
+ FILE *f;
+
+ if (fstat_at(dirfd(dir), _PATH_MNTTAB_DIR, d->d_name, &st, 0) ||
+ !S_ISREG(st.st_mode))
+ continue;
+
+ f = fopen_at(dirfd(dir), _PATH_MNTTAB_DIR,
+ d->d_name, O_RDONLY, "r");
+ if (f) {
+ mnt_table_parse_stream(tb, f, d->d_name);
+ fclose(f);
+ }
+ }
+
+out:
+ for (i = 0; i < n; i++)
+ free(namelist[i]);
+ free(namelist);
+ if (dir)
+ closedir(dir);
+ return r;
+}
+#endif
+
+/**
+ * mnt_table_parse_dir:
+ * @tb: mount table
+ * @dirname: directory
+ *
+ * The directory:
+ * - files are sorted by strverscmp(3)
+ * - files that starts with "." are ignored (e.g. ".10foo.fstab")
+ * - files without the ".fstab" extension are ignored
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_parse_dir(struct libmnt_table *tb, const char *dirname)
+{
+ return __mnt_table_parse_dir(tb, dirname);
+}
+
+struct libmnt_table *__mnt_new_table_from_file(const char *filename, int fmt)
+{
+ struct libmnt_table *tb;
+ struct stat st;
+
+ assert(filename);
+
+ if (!filename)
+ return NULL;
+ if (stat(filename, &st))
+ return NULL;
+ tb = mnt_new_table();
+ if (tb) {
+ tb->fmt = fmt;
+ if (mnt_table_parse_file(tb, filename) != 0) {
+ mnt_free_table(tb);
+ tb = NULL;
+ }
+ }
+ return tb;
+}
+
+/**
+ * mnt_new_table_from_file:
+ * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path
+ *
+ * Same as mnt_new_table() + mnt_table_parse_file(). Use this function for private
+ * files only. This function does not allow to use error callback, so you
+ * cannot provide any feedback to end-users about broken records in files (e.g.
+ * fstab).
+ *
+ * Returns: newly allocated tab on success and NULL in case of error.
+ */
+struct libmnt_table *mnt_new_table_from_file(const char *filename)
+{
+ return __mnt_new_table_from_file(filename, MNT_FMT_GUESS);
+}
+
+/**
+ * mnt_new_table_from_dir
+ * @dirname: directory with *.fstab files
+ *
+ * Returns: newly allocated tab on success and NULL in case of error.
+ */
+struct libmnt_table *mnt_new_table_from_dir(const char *dirname)
+{
+ struct libmnt_table *tb;
+
+ assert(dirname);
+
+ if (!dirname)
+ return NULL;
+ tb = mnt_new_table();
+ if (tb && mnt_table_parse_dir(tb, dirname) != 0) {
+ mnt_free_table(tb);
+ tb = NULL;
+ }
+ return tb;
+}
+
+/**
+ * mnt_table_set_parser_errcb:
+ * @tb: pointer to table
+ * @cb: pointer to callback function
+ *
+ * The error callback function is called by table parser (mnt_table_parse_file())
+ * in case of syntax error. The callback function could be used for errors
+ * evaluation, libmount will continue/stop parsing according to callback return
+ * codes:
+ *
+ * <0 : fatal error (abort parsing)
+ * 0 : success (parsing continue)
+ * >0 : recoverable error (the line is ignored, parsing continue).
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_set_parser_errcb(struct libmnt_table *tb,
+ int (*cb)(struct libmnt_table *tb, const char *filename, int line))
+{
+ assert(tb);
+ tb->errcb = cb;
+ return 0;
+}
+
+/**
+ * mnt_table_parse_swaps:
+ * @tb: table
+ * @filename: overwrites default (/proc/swaps or $LIBMOUNT_SWAPS) or NULL
+ *
+ * This function parses /proc/swaps and appends new lines to the @tab.
+ *
+ * See also mnt_table_set_parser_errcb().
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_parse_swaps(struct libmnt_table *tb, const char *filename)
+{
+ assert(tb);
+
+ if (!tb)
+ return -EINVAL;
+ if (!filename) {
+ filename = mnt_get_swaps_path();
+ if (!filename)
+ return -EINVAL;
+ }
+
+ tb->fmt = MNT_FMT_SWAPS;
+
+ return mnt_table_parse_file(tb, filename);
+}
+
+/**
+ * mnt_table_parse_fstab:
+ * @tb: table
+ * @filename: overwrites default (/etc/fstab or $LIBMOUNT_FSTAB) or NULL
+ *
+ * This function parses /etc/fstab and appends new lines to the @tab. If the
+ * @filename is a directory then mnt_table_parse_dir() is called.
+ *
+ * See also mnt_table_set_parser_errcb().
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_parse_fstab(struct libmnt_table *tb, const char *filename)
+{
+ struct stat st;
+ int rc = 0;
+
+ assert(tb);
+
+ if (!tb)
+ return -EINVAL;
+ if (!filename)
+ filename = mnt_get_fstab_path();
+
+ if (!filename || stat(filename, &st))
+ return -EINVAL;
+
+ tb->fmt = MNT_FMT_FSTAB;
+
+ if (S_ISREG(st.st_mode))
+ rc = mnt_table_parse_file(tb, filename);
+ else if (S_ISDIR(st.st_mode))
+ rc = mnt_table_parse_dir(tb, filename);
+ else
+ rc = -EINVAL;
+
+ return rc;
+}
+
+/*
+ * This function uses @uf to found corresponding record in @tb, then the record
+ * from @tb is updated (user specific mount options are added).
+ *
+ * Note that @uf must contain only user specific mount options instead of
+ * VFS options (note that FS options are ignored).
+ *
+ * Returns modified filesystem (from @tb) or NULL.
+ */
+static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf)
+{
+ struct libmnt_fs *fs;
+ struct libmnt_iter itr;
+ const char *optstr, *src, *target, *root, *attrs;
+
+ assert(tb);
+ assert(uf);
+ if (!tb || !uf)
+ return NULL;
+
+ DBG(TAB, mnt_debug_h(tb, "merging user fs"));
+
+ src = mnt_fs_get_srcpath(uf);
+ target = mnt_fs_get_target(uf);
+ optstr = mnt_fs_get_user_options(uf);
+ attrs = mnt_fs_get_attributes(uf);
+ root = mnt_fs_get_root(uf);
+
+ if (!src || !target || !root || (!attrs && !optstr))
+ return NULL;
+
+ mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
+
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ const char *r = mnt_fs_get_root(fs);
+
+ if (fs->flags & MNT_FS_MERGED)
+ continue;
+
+ if (r && strcmp(r, root) == 0
+ && mnt_fs_streq_target(fs, target)
+ && mnt_fs_streq_srcpath(fs, src))
+ break;
+ }
+
+ if (fs) {
+ DBG(TAB, mnt_debug_h(tb, "found fs -- appending user optstr"));
+ mnt_fs_append_options(fs, optstr);
+ mnt_fs_append_attributes(fs, attrs);
+ mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf));
+ fs->flags |= MNT_FS_MERGED;
+
+ DBG(TAB, mnt_debug_h(tb, "found fs:"));
+ DBG(TAB, mnt_fs_print_debug(fs, stderr));
+ }
+ return fs;
+}
+
+/**
+ * mnt_table_parse_mtab:
+ * @tb: table
+ * @filename: overwrites default (/etc/mtab or $LIBMOUNT_MTAB) or NULL
+ *
+ * This function parses /etc/mtab or /proc/self/mountinfo +
+ * /run/mount/utabs or /proc/mounts.
+ *
+ * See also mnt_table_set_parser_errcb().
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_parse_mtab(struct libmnt_table *tb, const char *filename)
+{
+ int rc;
+ const char *utab = NULL;
+
+ if (mnt_has_regular_mtab(&filename, NULL)) {
+
+ DBG(TAB, mnt_debug_h(tb, "force %s usage", filename));
+
+ rc = mnt_table_parse_file(tb, filename);
+ if (!rc)
+ return 0;
+ filename = NULL; /* failed */
+ }
+
+ /*
+ * useless /etc/mtab
+ * -- read kernel information from /proc/self/mountinfo
+ */
+ tb->fmt = MNT_FMT_MOUNTINFO;
+ rc = mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO);
+ if (rc) {
+ /* hmm, old kernel? ...try /proc/mounts */
+ tb->fmt = MNT_FMT_MTAB;
+ return mnt_table_parse_file(tb, _PATH_PROC_MOUNTS);
+ }
+
+ /*
+ * try to read user specific information from /run/mount/utabs
+ */
+ utab = mnt_get_utab_path();
+ if (utab) {
+ struct libmnt_table *u_tb = __mnt_new_table_from_file(utab, MNT_FMT_UTAB);
+
+ if (u_tb) {
+ struct libmnt_fs *u_fs;
+ struct libmnt_iter itr;
+
+ mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
+
+ /* merge user options into mountinfo from kernel */
+ while(mnt_table_next_fs(u_tb, &itr, &u_fs) == 0)
+ mnt_table_merge_user_fs(tb, u_fs);
+
+ mnt_free_table(u_tb);
+ }
+ }
+ return 0;
+}
diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c
new file mode 100644
index 0000000..4f04c8e
--- /dev/null
+++ b/libmount/src/tab_update.c
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: update
+ * @title: Tables update
+ * @short_description: userspace mount information management
+ *
+ * The struct libmnt_update provides abstraction to manage mount options in
+ * userspace independently on system configuration. This low-level API works on
+ * system with and without /etc/mtab. On systems without the regular /etc/mtab
+ * file are userspace mount options (e.g. user=) stored to the /run/mount/utab
+ * file.
+ *
+ * It's recommended to use high-level struct libmnt_context API.
+ */
+#include <sys/file.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "mountP.h"
+#include "mangle.h"
+#include "pathnames.h"
+
+struct libmnt_update {
+ char *target;
+ struct libmnt_fs *fs;
+ char *filename;
+ unsigned long mountflags;
+ int userspace_only;
+ int ready;
+
+ struct libmnt_table *mountinfo;
+};
+
+static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
+static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
+
+/**
+ * mnt_new_update:
+ *
+ * Returns: newly allocated update handler
+ */
+struct libmnt_update *mnt_new_update(void)
+{
+ struct libmnt_update *upd;
+
+ upd = calloc(1, sizeof(*upd));
+ if (!upd)
+ return NULL;
+
+ DBG(UPDATE, mnt_debug_h(upd, "allocate"));
+ return upd;
+}
+
+/**
+ * mnt_free_update:
+ * @upd: update
+ *
+ * Deallocates struct libmnt_update handler.
+ */
+void mnt_free_update(struct libmnt_update *upd)
+{
+ if (!upd)
+ return;
+
+ DBG(UPDATE, mnt_debug_h(upd, "free"));
+
+ mnt_free_fs(upd->fs);
+ mnt_free_table(upd->mountinfo);
+ free(upd->target);
+ free(upd->filename);
+ free(upd);
+}
+
+/*
+ * Returns 0 on success, <0 in case of error.
+ */
+int mnt_update_set_filename(struct libmnt_update *upd, const char *filename,
+ int userspace_only)
+{
+ const char *path = NULL;
+ int rw = 0;
+
+ assert(upd);
+
+ /* filename explicitly defined */
+ if (filename) {
+ char *p = strdup(filename);
+ if (!p)
+ return -ENOMEM;
+
+ upd->userspace_only = userspace_only;
+ free(upd->filename);
+ upd->filename = p;
+ }
+
+ if (upd->filename)
+ return 0;
+
+ /* detect tab filename -- /etc/mtab or /run/mount/utab
+ */
+ mnt_has_regular_mtab(&path, &rw);
+ if (!rw) {
+ path = NULL;
+ mnt_has_regular_utab(&path, &rw);
+ if (!rw)
+ return -EACCES;
+ upd->userspace_only = TRUE;
+ }
+ upd->filename = strdup(path);
+ if (!upd->filename)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * mnt_update_get_filename:
+ * @upd: update
+ *
+ * This function returns file name (e.g. /etc/mtab) for the up-dated file.
+ *
+ * Returns: pointer to filename that will be updated or NULL in case of error.
+ */
+const char *mnt_update_get_filename(struct libmnt_update *upd)
+{
+ return upd ? upd->filename : NULL;
+}
+
+/**
+ * mnt_update_is_ready:
+ * @upd: update handler
+ *
+ * Returns: 1 if entry described by @upd is successfully prepared and will be
+ * written to mtab/utab file.
+ */
+int mnt_update_is_ready(struct libmnt_update *upd)
+{
+ return upd ? upd->ready : FALSE;
+}
+
+/**
+ * mnt_update_set_fs:
+ * @upd: update handler
+ * @mountflags: MS_* flags
+ * @target: umount target, must be NULL for mount
+ * @fs: mount filesystem description, must be NULL for umount
+ *
+ * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary.
+ */
+int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
+ const char *target, struct libmnt_fs *fs)
+{
+ int rc;
+
+ assert(upd);
+ assert(target || fs);
+
+ if (!upd)
+ return -EINVAL;
+ if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs)))
+ return -EINVAL;
+ if (target && fs)
+ return -EINVAL;
+
+ DBG(UPDATE, mnt_debug_h(upd,
+ "resetting FS [fs=0x%p, target=%s, flags=0x%08lx]",
+ fs, target, mountflags));
+ if (fs) {
+ DBG(UPDATE, mnt_debug_h(upd, "FS template:"));
+ DBG(UPDATE, mnt_fs_print_debug(fs, stderr));
+ }
+
+ mnt_free_fs(upd->fs);
+ free(upd->target);
+ upd->ready = FALSE;
+ upd->fs = NULL;
+ upd->target = NULL;
+ upd->mountflags = 0;
+
+ if (mountflags & MS_PROPAGATION)
+ return 1;
+
+ upd->mountflags = mountflags;
+
+ rc = mnt_update_set_filename(upd, NULL, 0);
+ if (rc) {
+ DBG(UPDATE, mnt_debug_h(upd, "no writable file available [rc=%d]", rc));
+ return rc; /* error or no file available (rc = 1) */
+ }
+ if (target) {
+ upd->target = strdup(target);
+ if (!upd->target)
+ return -ENOMEM;
+
+ } else if (fs) {
+ if (upd->userspace_only && !(mountflags & MS_MOVE)) {
+ rc = utab_new_entry(upd, fs, mountflags);
+ if (rc)
+ return rc;
+ } else {
+ upd->fs = mnt_copy_mtab_fs(fs);
+ if (!upd->fs)
+ return -ENOMEM;
+
+ }
+ }
+
+
+ DBG(UPDATE, mnt_debug_h(upd, "ready"));
+ upd->ready = TRUE;
+ return 0;
+}
+
+/**
+ * mnt_update_get_fs:
+ * @upd: update
+ *
+ * Returns: update filesystem entry or NULL
+ */
+struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd)
+{
+ return upd ? upd->fs : NULL;
+}
+
+/**
+ * mnt_update_get_mflags:
+ * @upd: update
+ *
+ * Returns: mount flags as was set by mnt_update_set_fs()
+ */
+unsigned long mnt_update_get_mflags(struct libmnt_update *upd)
+{
+ return upd ? upd->mountflags : 0;
+}
+
+/**
+ * mnt_update_force_rdonly:
+ * @upd: update
+ * @rdonly: is read-only?
+ *
+ * Returns: 0 on success and negative number in case of error.
+ */
+int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly)
+{
+ int rc = 0;
+
+ if (!upd || !upd->fs)
+ return -EINVAL;
+
+ if (rdonly && (upd->mountflags & MS_RDONLY))
+ return 0;
+ if (!rdonly && !(upd->mountflags & MS_RDONLY))
+ return 0;
+
+ if (!upd->userspace_only) {
+ /* /etc/mtab -- we care about VFS options there */
+ const char *o = mnt_fs_get_options(upd->fs);
+ char *n = o ? strdup(o) : NULL;
+
+ if (n)
+ mnt_optstr_remove_option(&n, rdonly ? "rw" : "ro");
+ if (!mnt_optstr_prepend_option(&n, rdonly ? "ro" : "rw", NULL))
+ rc = mnt_fs_set_options(upd->fs, n);
+
+ free(n);
+ }
+
+ if (rdonly)
+ upd->mountflags &= ~MS_RDONLY;
+ else
+ upd->mountflags |= MS_RDONLY;
+
+ return rc;
+}
+
+/*
+ * Allocates utab entry (upd->fs) for mount/remount. This function should be
+ * called *before* mount(2) syscall. The @fs is used as a read-only template.
+ *
+ * Returns: 0 on success, negative number on error, 1 if utabs update is
+ * unnecessary.
+ */
+static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs,
+ unsigned long mountflags)
+{
+ int rc = 0;
+ const char *o = NULL, *a = NULL;
+ char *u = NULL;
+
+ assert(fs);
+ assert(upd);
+ assert(upd->fs == NULL);
+ assert(!(mountflags & MS_MOVE));
+
+ DBG(UPDATE, mnt_debug("prepare utab entry"));
+
+ o = mnt_fs_get_user_options(fs);
+ a = mnt_fs_get_attributes(fs);
+ upd->fs = NULL;
+
+ if (o) {
+ /* remove non-mtab options */
+ rc = mnt_optstr_get_options(o, &u,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
+ MNT_NOMTAB);
+ if (rc)
+ goto err;
+ }
+
+ if (!u && !a) {
+ DBG(UPDATE, mnt_debug("utab entry unnecessary (no options)"));
+ return 1;
+ }
+
+ /* allocate the entry */
+ upd->fs = mnt_copy_fs(NULL, fs);
+ if (!upd->fs) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = mnt_fs_set_options(upd->fs, u);
+ if (rc)
+ goto err;
+ rc = mnt_fs_set_attributes(upd->fs, a);
+ if (rc)
+ goto err;
+
+ if (!(mountflags & MS_REMOUNT)) {
+ rc = set_fs_root(upd, fs, mountflags);
+ if (rc)
+ goto err;
+ }
+
+ free(u);
+ DBG(UPDATE, mnt_debug("utab entry OK"));
+ return 0;
+err:
+ free(u);
+ mnt_free_fs(upd->fs);
+ upd->fs = NULL;
+ return rc;
+}
+
+/*
+ * Sets fs-root and fs-type to @upd->fs according to the @fs template and
+ * @mountfalgs. For MS_BIND mountflag it reads information about source
+ * filesystem from /proc/self/mountinfo.
+ */
+static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs,
+ unsigned long mountflags)
+{
+ struct libmnt_fs *src_fs;
+ char *fsroot = NULL;
+ const char *src;
+ int rc = 0;
+
+ DBG(UPDATE, mnt_debug("setting FS root"));
+
+ assert(upd);
+ assert(upd->fs);
+ assert(fs);
+
+ if (mountflags & MS_BIND) {
+ if (!upd->mountinfo)
+ upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
+
+ src = mnt_fs_get_srcpath(fs);
+ if (src) {
+ rc = mnt_fs_set_bindsrc(upd->fs, src);
+ if (rc)
+ goto err;
+ }
+ }
+
+ src_fs = mnt_table_get_fs_root(upd->mountinfo, fs,
+ mountflags, &fsroot);
+ if (src_fs) {
+ src = mnt_fs_get_srcpath(src_fs);
+ rc = mnt_fs_set_source(upd->fs, src);
+ if (rc)
+ goto err;
+
+ mnt_fs_set_fstype(upd->fs, mnt_fs_get_fstype(src_fs));
+ }
+
+ upd->fs->root = fsroot;
+ return 0;
+err:
+ free(fsroot);
+ return rc;
+}
+
+/* mtab and fstab update -- returns zero on success
+ */
+static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs)
+{
+ const char *o, *src, *fstype;
+ char *m1, *m2, *m3, *m4;
+ int rc;
+
+ assert(fs);
+ assert(f);
+
+ src = mnt_fs_get_source(fs);
+ fstype = mnt_fs_get_fstype(fs);
+ o = mnt_fs_get_options(fs);
+
+ m1 = src ? mangle(src) : "none";
+ m2 = mangle(mnt_fs_get_target(fs));
+ m3 = fstype ? mangle(fstype) : "none";
+ m4 = o ? mangle(o) : "rw";
+
+ if (m1 && m2 && m3 && m4) {
+ rc = fprintf(f, "%s %s %s %s %d %d\n",
+ m1, m2, m3, m4,
+ mnt_fs_get_freq(fs),
+ mnt_fs_get_passno(fs));
+ if (rc > 0)
+ rc = 0;
+ } else
+ rc = -ENOMEM;
+
+ if (src)
+ free(m1);
+ free(m2);
+ if (fstype)
+ free(m3);
+ if (o)
+ free(m4);
+
+ return rc;
+}
+
+static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs)
+{
+ char *p;
+ int rc = 0;
+
+ assert(fs);
+ assert(f);
+
+ if (!fs || !f)
+ return -EINVAL;
+
+ p = mangle(mnt_fs_get_source(fs));
+ if (p) {
+ rc = fprintf(f, "SRC=%s ", p);
+ free(p);
+ }
+ if (rc >= 0) {
+ p = mangle(mnt_fs_get_target(fs));
+ if (p) {
+ rc = fprintf(f, "TARGET=%s ", p);
+ free(p);
+ }
+ }
+ if (rc >= 0) {
+ p = mangle(mnt_fs_get_root(fs));
+ if (p) {
+ rc = fprintf(f, "ROOT=%s ", p);
+ free(p);
+ }
+ }
+ if (rc >= 0) {
+ p = mangle(mnt_fs_get_bindsrc(fs));
+ if (p) {
+ rc = fprintf(f, "BINDSRC=%s ", p);
+ free(p);
+ }
+ }
+ if (rc >= 0) {
+ p = mangle(mnt_fs_get_attributes(fs));
+ if (p) {
+ rc = fprintf(f, "ATTRS=%s ", p);
+ free(p);
+ }
+ }
+ if (rc >= 0) {
+ p = mangle(mnt_fs_get_user_options(fs));
+ if (p) {
+ rc = fprintf(f, "OPTS=%s", p);
+ free(p);
+ }
+ }
+ if (rc >= 0)
+ rc = fprintf(f, "\n");
+
+ if (rc > 0)
+ rc = 0; /* success */
+ return rc;
+}
+
+static int update_table(struct libmnt_update *upd, struct libmnt_table *tb)
+{
+ FILE *f;
+ int rc, fd;
+ char *uq = NULL;
+
+ if (!tb || !upd->filename)
+ return -EINVAL;
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: updating", upd->filename));
+
+ fd = mnt_open_uniq_filename(upd->filename, &uq);
+ if (fd < 0)
+ return fd; /* error */
+
+ f = fdopen(fd, "w");
+ if (f) {
+ struct stat st;
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (upd->userspace_only)
+ rc = fprintf_utab_fs(f, fs);
+ else
+ rc = fprintf_mtab_fs(f, fs);
+ if (rc) {
+ DBG(UPDATE, mnt_debug_h(upd,
+ "%s: write entry failed: %m", uq));
+ goto leave;
+ }
+ }
+
+ if (fflush(f) != 0) {
+ rc = -errno;
+ DBG(UPDATE, mnt_debug_h(upd, "%s: fflush failed: %m", uq));
+ goto leave;
+ }
+
+ rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
+
+ if (!rc && stat(upd->filename, &st) == 0)
+ /* Copy uid/gid from the present file before renaming. */
+ rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0;
+
+ fclose(f);
+ f = NULL;
+
+ if (!rc)
+ rc = rename(uq, upd->filename) ? -errno : 0;
+ } else {
+ rc = -errno;
+ close(fd);
+ }
+
+leave:
+ if (f)
+ fclose(f);
+
+ unlink(uq); /* be paranoid */
+ free(uq);
+ return rc;
+}
+
+static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
+{
+ struct libmnt_fs *fs = mnt_copy_fs(NULL, upd->fs);
+
+ if (!fs)
+ return -ENOMEM;
+
+ mnt_table_add_fs(tb, fs);
+ return update_table(upd, tb);
+}
+
+static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ struct libmnt_table *tb;
+ int rc = 0;
+
+ assert(upd);
+ assert(upd->fs);
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: add entry", upd->filename));
+
+ if (lc)
+ rc = mnt_lock_file(lc);
+ if (rc)
+ return rc;
+
+ tb = __mnt_new_table_from_file(upd->filename,
+ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
+ if (tb)
+ rc = add_file_entry(tb, upd);
+ if (lc)
+ mnt_unlock_file(lc);
+
+ mnt_free_table(tb);
+ return rc;
+}
+
+static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ struct libmnt_table *tb;
+ int rc = 0;
+
+ assert(upd);
+ assert(upd->target);
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: remove entry", upd->filename));
+
+ if (lc)
+ rc = mnt_lock_file(lc);
+ if (rc)
+ return rc;
+
+ tb = __mnt_new_table_from_file(upd->filename,
+ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
+ if (tb) {
+ struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD);
+ if (rem) {
+ mnt_table_remove_fs(tb, rem);
+ rc = update_table(upd, tb);
+ mnt_free_fs(rem);
+ }
+ }
+ if (lc)
+ mnt_unlock_file(lc);
+
+ mnt_free_table(tb);
+ return rc;
+}
+
+static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ struct libmnt_table *tb = NULL;
+ int rc = 0;
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", upd->filename));
+
+ if (lc)
+ rc = mnt_lock_file(lc);
+ if (rc)
+ return rc;
+
+ tb = __mnt_new_table_from_file(upd->filename,
+ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
+ if (tb) {
+ struct libmnt_fs *cur = mnt_table_find_target(tb,
+ mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD);
+ if (cur) {
+ rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs));
+ if (!rc)
+ rc = update_table(upd, tb);
+ }
+ }
+
+ if (lc)
+ mnt_unlock_file(lc);
+
+ mnt_free_table(tb);
+ return rc;
+}
+
+static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ struct libmnt_table *tb = NULL;
+ int rc = 0;
+ struct libmnt_fs *fs;
+
+ assert(upd);
+ assert(upd->fs);
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", upd->filename));
+
+ fs = upd->fs;
+
+ if (lc)
+ rc = mnt_lock_file(lc);
+ if (rc)
+ return rc;
+
+ tb = __mnt_new_table_from_file(upd->filename,
+ upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
+ if (tb) {
+ struct libmnt_fs *cur = mnt_table_find_target(tb,
+ mnt_fs_get_target(fs),
+ MNT_ITER_BACKWARD);
+ if (cur) {
+ if (upd->userspace_only)
+ rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs));
+ if (!rc)
+ rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs));
+ if (!rc)
+ rc = update_table(upd, tb);
+ } else
+ rc = add_file_entry(tb, upd); /* not found, add new */
+ }
+
+ if (lc)
+ mnt_unlock_file(lc);
+
+ mnt_free_table(tb);
+ return rc;
+}
+
+/**
+ * mnt_update_table:
+ * @upd: update
+ * @lc: lock or NULL
+ *
+ * High-level API to update /etc/mtab (or private /run/mount/utab file).
+ *
+ * The @lc lock is optional and will be created if necessary. Note that
+ * the automatically created lock blocks all signals.
+ *
+ * See also mnt_lock_block_signals() and mnt_context_get_lock().
+ *
+ * Returns: 0 on success, negative number on error.
+ */
+int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ struct libmnt_lock *lc0 = lc;
+ int rc = -EINVAL;
+
+ assert(upd);
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+ if (!upd->ready)
+ return 0;
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: update tab", upd->filename));
+ if (upd->fs) {
+ DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
+ }
+ if (!lc) {
+ lc = mnt_new_lock(upd->filename, 0);
+ if (lc)
+ mnt_lock_block_signals(lc, TRUE);
+ }
+ if (lc && upd->userspace_only)
+ mnt_lock_use_simplelock(lc, TRUE); /* use flock */
+
+ if (!upd->fs && upd->target)
+ rc = update_remove_entry(upd, lc); /* umount */
+ else if (upd->mountflags & MS_MOVE)
+ rc = update_modify_target(upd, lc); /* move */
+ else if (upd->mountflags & MS_REMOUNT)
+ rc = update_modify_options(upd, lc); /* remount */
+ else if (upd->fs)
+ rc = update_add_entry(upd, lc); /* mount */
+
+ upd->ready = FALSE;
+ DBG(UPDATE, mnt_debug_h(upd, "%s: update tab: done [rc=%d]",
+ upd->filename, rc));
+ if (lc != lc0)
+ mnt_free_lock(lc);
+ return rc;
+}
+
+#ifdef TEST_PROGRAM
+
+static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags)
+{
+ int rc;
+ struct libmnt_update *upd;
+
+ DBG(UPDATE, mnt_debug("update test"));
+
+ upd = mnt_new_update();
+ if (!upd)
+ return -ENOMEM;
+
+ rc = mnt_update_set_fs(upd, mountflags, target, fs);
+ if (rc == 1) {
+ /* update is unnecessary */
+ rc = 0;
+ goto done;
+ }
+ if (rc) {
+ fprintf(stderr, "failed to set FS\n");
+ goto done;
+ }
+
+ /* [... here should be mount(2) call ...] */
+
+ rc = mnt_update_table(upd, NULL);
+done:
+ mnt_free_update(upd);
+ return rc;
+}
+
+static int test_add(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_fs *fs = mnt_new_fs();
+ int rc;
+
+ if (argc < 5 || !fs)
+ return -1;
+ mnt_fs_set_source(fs, argv[1]);
+ mnt_fs_set_target(fs, argv[2]);
+ mnt_fs_set_fstype(fs, argv[3]);
+ mnt_fs_set_options(fs, argv[4]);
+
+ rc = update(NULL, fs, 0);
+ mnt_free_fs(fs);
+ return rc;
+}
+
+static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+{
+ if (argc < 2)
+ return -1;
+ return update(argv[1], NULL, 0);
+}
+
+static int test_move(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_fs *fs = mnt_new_fs();
+ int rc;
+
+ if (argc < 3)
+ return -1;
+ mnt_fs_set_source(fs, argv[1]);
+ mnt_fs_set_target(fs, argv[2]);
+
+ rc = update(NULL, fs, MS_MOVE);
+
+ mnt_free_fs(fs);
+ return rc;
+}
+
+static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_fs *fs = mnt_new_fs();
+ int rc;
+
+ if (argc < 3)
+ return -1;
+ mnt_fs_set_target(fs, argv[1]);
+ mnt_fs_set_options(fs, argv[2]);
+
+ rc = update(NULL, fs, MS_REMOUNT);
+ mnt_free_fs(fs);
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--add", test_add, "<src> <target> <type> <options> add line to mtab" },
+ { "--remove", test_remove, "<target> MS_REMOUNT mtab change" },
+ { "--move", test_move, "<old_target> <target> MS_MOVE mtab change" },
+ { "--remount",test_remount, "<target> <options> MS_REMOUNT mtab change" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/test.c b/libmount/src/test.c
new file mode 100644
index 0000000..2da00b4
--- /dev/null
+++ b/libmount/src/test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Routines for TEST_PROGRAMs
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef TEST_PROGRAM
+#define TEST_PROGRAM
+#endif
+
+#include "mountP.h"
+
+int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[])
+{
+ int rc = -1;
+ struct libmnt_test *ts;
+
+ assert(tests);
+ assert(argc);
+ assert(argv);
+
+ if (argc < 2 ||
+ strcmp(argv[1], "--help") == 0 ||
+ strcmp(argv[1], "-h") == 0)
+ goto usage;
+
+ mnt_init_debug(0);
+
+ for (ts = tests; ts->name; ts++) {
+ if (strcmp(ts->name, argv[1]) == 0) {
+ rc = ts->body(ts, argc - 1, argv + 1);
+ if (rc)
+ printf("FAILED [rc=%d]", rc);
+ break;
+ }
+ }
+
+ if (rc < 0 && ts->name == NULL)
+ goto usage;
+
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+usage:
+ printf("\nUsage:\n\t%s <test> [testoptions]\nTests:\n",
+ program_invocation_short_name);
+ for (ts = tests; ts->name; ts++) {
+ printf("\t%-15s", ts->name);
+ if (ts->usage)
+ printf(" %s\n", ts->usage);
+ }
+ printf("\n");
+ return EXIT_FAILURE;
+}
diff --git a/libmount/src/utils.c b/libmount/src/utils.c
new file mode 100644
index 0000000..63d1079
--- /dev/null
+++ b/libmount/src/utils.c
@@ -0,0 +1,991 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: utils
+ * @title: Utils
+ * @short_description: misc utils.
+ */
+#include <ctype.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "strutils.h"
+#include "pathnames.h"
+#include "mountP.h"
+#include "mangle.h"
+#include "canonicalize.h"
+#include "env.h"
+#include "match.h"
+
+int endswith(const char *s, const char *sx)
+{
+ ssize_t off;
+
+ assert(s);
+ assert(sx);
+
+ off = strlen(s);
+ if (!off)
+ return 0;
+ off -= strlen(sx);
+ if (off < 0)
+ return 0;
+
+ return !strcmp(s + off, sx);
+}
+
+int startswith(const char *s, const char *sx)
+{
+ size_t off;
+
+ assert(s);
+ assert(sx);
+
+ off = strlen(sx);
+ if (!off)
+ return 0;
+
+ return !strncmp(s, sx, off);
+}
+
+int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
+{
+ char *p;
+ int rc = 0;
+
+ if (!str || !*str)
+ return -EINVAL;
+
+ p = strndup(str, len);
+ if (!p)
+ return -errno;
+
+ if (strtosize(p, res))
+ rc = -EINVAL;
+ free(p);
+ return rc;
+}
+
+/* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
+static int fstype_cmp(const void *v1, const void *v2)
+{
+ const char *s1 = *(const char **)v1;
+ const char *s2 = *(const char **)v2;
+
+ return strcmp(s1, s2);
+}
+
+/* returns basename and keeps dirname in the @path, if @path is "/" (root)
+ * then returns empty string */
+static char *stripoff_last_component(char *path)
+{
+ char *p = path ? strrchr(path, '/') : NULL;
+
+ if (!p)
+ return NULL;
+ *p = '\0';
+ return p + 1;
+}
+
+/*
+ * Note that the @target has to be absolute path (so at least "/"). The
+ * @filename returns allocated buffer with last path component, for example:
+ *
+ * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
+ */
+int mnt_chdir_to_parent(const char *target, char **filename)
+{
+ char *buf, *parent, *last = NULL;
+ char cwd[PATH_MAX];
+ int rc = -EINVAL;
+
+ if (!target || *target != '/')
+ return -EINVAL;
+
+ DBG(UTILS, mnt_debug("moving to %s parent", target));
+
+ buf = strdup(target);
+ if (!buf)
+ return -ENOMEM;
+
+ if (*(buf + 1) != '\0') {
+ last = stripoff_last_component(buf);
+ if (!last)
+ goto err;
+ }
+
+ parent = buf && *buf ? buf : "/";
+
+ if (chdir(parent) == -1) {
+ DBG(UTILS, mnt_debug("failed to chdir to %s: %m", parent));
+ rc = -errno;
+ goto err;
+ }
+ if (!getcwd(cwd, sizeof(cwd))) {
+ DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
+ rc = -errno;
+ goto err;
+ }
+ if (strcmp(cwd, parent) != 0) {
+ DBG(UTILS, mnt_debug(
+ "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
+ goto err;
+ }
+
+ DBG(CXT, mnt_debug(
+ "current directory moved to %s [last_component='%s']",
+ parent, last));
+
+ *filename = buf;
+
+ if (!last || !*last)
+ memcpy(*filename, ".", 2);
+ else
+ memcpy(*filename, last, strlen(last) + 1);
+ return 0;
+err:
+ free(buf);
+ return rc;
+}
+
+/*
+ * Check if @path is on read-only filesystem independently on file permissions.
+ */
+int mnt_is_readonly(const char *path)
+{
+ if (access(path, W_OK) == 0)
+ return 0;
+ if (errno == EROFS)
+ return 1;
+ if (errno != EACCES)
+ return 0;
+
+#ifdef HAVE_FUTIMENS
+ /*
+ * access(2) returns EACCES on read-only FS:
+ *
+ * - for set-uid application if one component of the path is not
+ * accessible for the current rUID. (Note that euidaccess(2) does not
+ * check for EROFS at all).
+ *
+ * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind)
+ */
+ {
+ struct timespec times[2];
+
+ times[0].tv_nsec = UTIME_NOW; /* atime */
+ times[1].tv_nsec = UTIME_OMIT; /* mtime */
+
+ if (utimensat(AT_FDCWD, path, times, 0) == -1)
+ return errno == EROFS;
+ }
+#endif
+ return 0;
+}
+
+/**
+ * mnt_mangle:
+ * @str: string
+ *
+ * Encode @str to be compatible with fstab/mtab
+ *
+ * Returns: new allocated string or NULL in case of error.
+ */
+char *mnt_mangle(const char *str)
+{
+ return mangle(str);
+}
+
+/**
+ * mnt_unmangle:
+ * @str: string
+ *
+ * Decode @str from fstab/mtab
+ *
+ * Returns: new allocated string or NULL in case of error.
+ */
+char *mnt_unmangle(const char *str)
+{
+ return unmangle(str, NULL);
+}
+
+/**
+ * mnt_fstype_is_pseudofs:
+ * @type: filesystem name
+ *
+ * Returns: 1 for filesystems like proc, sysfs, ... or 0.
+ */
+int mnt_fstype_is_pseudofs(const char *type)
+{
+ /* This array must remain sorted when adding new fstypes */
+ static const char *pseudofs[] = {
+ "anon_inodefs",
+ "autofs",
+ "bdev",
+ "binfmt_misc",
+ "cgroup",
+ "configfs",
+ "cpuset",
+ "debugfs",
+ "devfs",
+ "devpts",
+ "devtmpfs",
+ "dlmfs",
+ "fuse.gvfs-fuse-daemon",
+ "fusectl",
+ "hugetlbfs",
+ "mqueue",
+ "nfsd",
+ "none",
+ "pipefs",
+ "proc",
+ "pstore",
+ "ramfs",
+ "rootfs",
+ "rpc_pipefs",
+ "securityfs",
+ "sockfs",
+ "spufs",
+ "sysfs",
+ "tmpfs"
+ };
+
+ return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
+ sizeof(char*), fstype_cmp) == NULL);
+}
+
+/**
+ * mnt_fstype_is_netfs:
+ * @type: filesystem name
+ *
+ * Returns: 1 for filesystems like cifs, nfs, ... or 0.
+ */
+int mnt_fstype_is_netfs(const char *type)
+{
+ if (!type)
+ return 0;
+ if (strcmp(type, "cifs") == 0 ||
+ strcmp(type, "smbfs") == 0 ||
+ strncmp(type,"nfs", 3) == 0 ||
+ strcmp(type, "afs") == 0 ||
+ strcmp(type, "ncpfs") == 0 ||
+ strncmp(type,"9p", 2) == 0)
+ return 1;
+ return 0;
+}
+
+/**
+ * mnt_match_fstype:
+ * @type: filesystem type
+ * @pattern: filesystem name or comma delimited list of names
+ *
+ * The @pattern list of filesystem can be prefixed with a global
+ * "no" prefix to invert matching of the whole list. The "no" could
+ * also be used for individual items in the @pattern list. So,
+ * "nofoo,bar" has the same meaning as "nofoo,nobar".
+ *
+ * "bar" : "nofoo,bar" -> False (global "no" prefix)
+ *
+ * "bar" : "foo,bar" -> True
+ *
+ * "bar" : "foo,nobar" -> False
+ *
+ * Returns: 1 if type is matching, else 0. This function also returns
+ * 0 if @pattern is NULL and @type is non-NULL.
+ */
+int mnt_match_fstype(const char *type, const char *pattern)
+{
+ return match_fstype(type, pattern);
+}
+
+
+/* Returns 1 if needle found or noneedle not found in haystack
+ * Otherwise returns 0
+ */
+static int check_option(const char *haystack, size_t len,
+ const char *needle, size_t needle_len)
+{
+ const char *p;
+ int no = 0;
+
+ if (needle_len >= 1 && *needle == '+') {
+ needle++;
+ needle_len--;
+ } else if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
+ no = 1;
+ needle += 2;
+ needle_len -= 2;
+ }
+
+ for (p = haystack; p && p < haystack + len; p++) {
+ char *sep = strchr(p, ',');
+ size_t plen = sep ? (size_t) (sep - p) :
+ len - (p - haystack);
+
+ if (plen == needle_len) {
+ if (!strncmp(p, needle, plen))
+ return !no; /* foo or nofoo was found */
+ }
+ p += plen;
+ }
+
+ return no; /* foo or nofoo was not found */
+}
+
+/**
+ * mnt_match_options:
+ * @optstr: options string
+ * @pattern: comma delimited list of options
+ *
+ * The "no" could used for individual items in the @options list. The "no"
+ * prefix does not have a global meaning.
+ *
+ * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
+ * DIFFERENT meanings; each option is matched explicitly as specified.
+ *
+ * The "no" prefix interpretation could be disable by "+" prefix, for example
+ * "+noauto" matches if @optstr literally contains "noauto" string.
+ *
+ * "xxx,yyy,zzz" : "nozzz" -> False
+ *
+ * "xxx,yyy,zzz" : "xxx,noeee" -> True
+ *
+ * "bar,zzz" : "nofoo" -> True
+ *
+ * "nofoo,bar" : "+nofoo" -> True
+ *
+ * "bar,zzz" : "+nofoo" -> False
+ *
+ *
+ * Returns: 1 if pattern is matching, else 0. This function also returns 0
+ * if @pattern is NULL and @optstr is non-NULL.
+ */
+int mnt_match_options(const char *optstr, const char *pattern)
+{
+ const char *p;
+ size_t len, optstr_len = 0;
+
+ if (!pattern && !optstr)
+ return 1;
+ if (!pattern)
+ return 0;
+
+ len = strlen(pattern);
+ if (optstr)
+ optstr_len = strlen(optstr);
+
+ for (p = pattern; p < pattern + len; p++) {
+ char *sep = strchr(p, ',');
+ size_t plen = sep ? (size_t) (sep - p) :
+ len - (p - pattern);
+
+ if (!plen)
+ continue; /* if two ',' appear in a row */
+
+ if (!check_option(optstr, optstr_len, p, plen))
+ return 0; /* any match failure means failure */
+
+ p += plen;
+ }
+
+ /* no match failures in list means success */
+ return 1;
+}
+
+void mnt_free_filesystems(char **filesystems)
+{
+ char **p;
+
+ if (!filesystems)
+ return;
+ for (p = filesystems; *p; p++)
+ free(*p);
+ free(filesystems);
+}
+
+static int add_filesystem(char ***filesystems, char *name)
+{
+ int n = 0;
+
+ assert(filesystems);
+ assert(name);
+
+ if (*filesystems) {
+ char **p;
+ for (n = 0, p = *filesystems; *p; p++, n++) {
+ if (strcmp(*p, name) == 0)
+ return 0;
+ }
+ }
+
+ #define MYCHUNK 16
+
+ if (n == 0 || !((n + 1) % MYCHUNK)) {
+ size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
+ char **x = realloc(*filesystems, items * sizeof(char *));
+
+ if (!x)
+ goto err;
+ *filesystems = x;
+ }
+ name = strdup(name);
+ if (!name)
+ goto err;
+ (*filesystems)[n] = name;
+ (*filesystems)[n + 1] = NULL;
+ return 0;
+err:
+ mnt_free_filesystems(*filesystems);
+ return -ENOMEM;
+}
+
+static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
+{
+ int rc = 0;
+ FILE *f;
+ char line[128];
+
+ f = fopen(filename, "r");
+ if (!f)
+ return 0;
+
+ while (fgets(line, sizeof(line), f)) {
+ char name[sizeof(line)];
+
+ if (*line == '#' || strncmp(line, "nodev", 5) == 0)
+ continue;
+ if (sscanf(line, " %128[^\n ]\n", name) != 1)
+ continue;
+ if (pattern && !mnt_match_fstype(name, pattern))
+ continue;
+ rc = add_filesystem(filesystems, name);
+ if (rc)
+ break;
+ }
+
+ fclose(f);
+ return rc;
+}
+
+int mnt_get_filesystems(char ***filesystems, const char *pattern)
+{
+ int rc;
+
+ if (!filesystems)
+ return -EINVAL;
+ *filesystems = NULL;
+
+ rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
+ if (rc)
+ return rc;
+ return get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
+}
+
+static size_t get_pw_record_size(void)
+{
+#ifdef _SC_GETPW_R_SIZE_MAX
+ long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (sz > 0)
+ return sz;
+#endif
+ return 16384;
+}
+
+/*
+ * Returns allocated string with username or NULL.
+ */
+char *mnt_get_username(const uid_t uid)
+{
+ struct passwd pwd;
+ struct passwd *res;
+ size_t sz = get_pw_record_size();
+ char *buf, *username = NULL;
+
+ buf = malloc(sz);
+ if (!buf)
+ return NULL;
+
+ if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
+ username = strdup(pwd.pw_name);
+
+ free(buf);
+ return username;
+}
+
+int mnt_get_uid(const char *username, uid_t *uid)
+{
+ int rc = -1;
+ struct passwd pwd;
+ struct passwd *pw;
+ size_t sz = get_pw_record_size();
+ char *buf;
+
+ if (!username || !uid)
+ return -EINVAL;
+
+ buf = malloc(sz);
+ if (!buf)
+ return -ENOMEM;
+
+ if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
+ *uid= pw->pw_uid;
+ rc = 0;
+ } else {
+ DBG(UTILS, mnt_debug(
+ "cannot convert '%s' username to UID", username));
+ rc = errno ? -errno : -EINVAL;
+ }
+
+ free(buf);
+ return rc;
+}
+
+int mnt_get_gid(const char *groupname, gid_t *gid)
+{
+ int rc = -1;
+ struct group grp;
+ struct group *gr;
+ size_t sz = get_pw_record_size();
+ char *buf;
+
+ if (!groupname || !gid)
+ return -EINVAL;
+
+ buf = malloc(sz);
+ if (!buf)
+ return -ENOMEM;
+
+ if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
+ *gid= gr->gr_gid;
+ rc = 0;
+ } else {
+ DBG(UTILS, mnt_debug(
+ "cannot convert '%s' groupname to GID", groupname));
+ rc = errno ? -errno : -EINVAL;
+ }
+
+ free(buf);
+ return rc;
+}
+
+int mnt_in_group(gid_t gid)
+{
+ int rc = 0, n, i;
+ gid_t *grps = NULL;
+
+ if (getgid() == gid)
+ return 1;
+
+ n = getgroups(0, NULL);
+ if (n <= 0)
+ goto done;
+
+ grps = malloc(n * sizeof(*grps));
+ if (!grps)
+ goto done;
+
+ if (getgroups(n, grps) == n) {
+ for (i = 0; i < n; i++) {
+ if (grps[i] == gid) {
+ rc = 1;
+ break;
+ }
+ }
+ }
+done:
+ free(grps);
+ return rc;
+}
+
+static int try_write(const char *filename)
+{
+ int fd;
+
+ if (!filename)
+ return -EINVAL;
+
+ fd = open(filename, O_RDWR|O_CREAT, S_IWUSR| \
+ S_IRUSR|S_IRGRP|S_IROTH);
+ if (fd >= 0) {
+ close(fd);
+ return 0;
+ }
+ return -errno;
+}
+
+/**
+ * mnt_has_regular_mtab:
+ * @mtab: returns path to mtab
+ * @writable: returns 1 if the file is writable
+ *
+ * If the file does not exist and @writable argument is not NULL then it will
+ * try to create the file
+ *
+ * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
+ * errno for more details).
+ */
+int mnt_has_regular_mtab(const char **mtab, int *writable)
+{
+ struct stat st;
+ int rc;
+ const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
+
+ if (writable)
+ *writable = 0;
+ if (mtab && !*mtab)
+ *mtab = filename;
+
+ DBG(UTILS, mnt_debug("mtab: %s", filename));
+
+ rc = lstat(filename, &st);
+
+ if (rc == 0) {
+ /* file exist */
+ if (S_ISREG(st.st_mode)) {
+ if (writable)
+ *writable = !try_write(filename);
+ return 1;
+ }
+ goto done;
+ }
+
+ /* try to create the file */
+ if (writable) {
+ *writable = !try_write(filename);
+ if (*writable)
+ return 1;
+ }
+
+done:
+ DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
+ return 0;
+}
+
+/*
+ * Don't export this to libmount API -- utab is private library stuff.
+ *
+ * If the file does not exist and @writable argument is not NULL then it will
+ * try to create the directory (e.g. /run/mount) and the file.
+ *
+ * Returns: 1 if utab is a regular file, and 0 in case of
+ * error (check errno for more details).
+ */
+int mnt_has_regular_utab(const char **utab, int *writable)
+{
+ struct stat st;
+ int rc;
+ const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
+
+ if (writable)
+ *writable = 0;
+ if (utab && !*utab)
+ *utab = filename;
+
+ DBG(UTILS, mnt_debug("utab: %s", filename));
+
+ rc = lstat(filename, &st);
+
+ if (rc == 0) {
+ /* file exist */
+ if (S_ISREG(st.st_mode)) {
+ if (writable)
+ *writable = !try_write(filename);
+ return 1;
+ }
+ goto done; /* it's not regular file */
+ }
+
+ if (writable) {
+ char *dirname = strdup(filename);
+
+ if (!dirname)
+ goto done;
+
+ stripoff_last_component(dirname); /* remove filename */
+
+ rc = mkdir(dirname, S_IWUSR|
+ S_IRUSR|S_IRGRP|S_IROTH|
+ S_IXUSR|S_IXGRP|S_IXOTH);
+ free(dirname);
+ if (rc && errno != EEXIST)
+ goto done; /* probably EACCES */
+
+ *writable = !try_write(filename);
+ if (*writable)
+ return 1;
+ }
+done:
+ DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
+ return 0;
+}
+
+/**
+ * mnt_get_swaps_path:
+ *
+ * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
+ */
+const char *mnt_get_swaps_path(void)
+{
+ const char *p = safe_getenv("LIBMOUNT_SWAPS");
+ return p ? : _PATH_PROC_SWAPS;
+}
+
+/**
+ * mnt_get_fstab_path:
+ *
+ * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
+ */
+const char *mnt_get_fstab_path(void)
+{
+ const char *p = safe_getenv("LIBMOUNT_FSTAB");
+ return p ? : _PATH_MNTTAB;
+}
+
+/**
+ * mnt_get_mtab_path:
+ *
+ * This function returns *default* location of the mtab file. The result does
+ * not have to be writable. See also mnt_has_regular_mtab().
+ *
+ * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
+ */
+const char *mnt_get_mtab_path(void)
+{
+ const char *p = safe_getenv("LIBMOUNT_MTAB");
+ return p ? : _PATH_MOUNTED;
+}
+
+/*
+ * Don't export this to libmount API -- utab is private library stuff.
+ *
+ * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
+ */
+const char *mnt_get_utab_path(void)
+{
+ struct stat st;
+ const char *p = safe_getenv("LIBMOUNT_UTAB");
+
+ if (p)
+ return p;
+
+ if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
+ return MNT_PATH_UTAB;
+
+ return MNT_PATH_UTAB_OLD;
+}
+
+
+/* returns file descriptor or -errno, @name returns uniques filename
+ */
+int mnt_open_uniq_filename(const char *filename, char **name)
+{
+ int rc, fd;
+ char *n;
+
+ assert(filename);
+
+ if (name)
+ *name = NULL;
+
+ rc = asprintf(&n, "%s.XXXXXX", filename);
+ if (rc <= 0)
+ return -errno;
+
+ fd = mkstemp(n);
+ if (fd >= 0 && name)
+ *name = n;
+ else
+ free(n);
+
+ return fd < 0 ? -errno : fd;
+}
+
+/**
+ * mnt_get_mountpoint:
+ * @path: pathname
+ *
+ * This function finds the mountpoint that a given path resides in. @path
+ * should be canonicalized. The returned pointer should be freed by the caller.
+ *
+ * Returns: target of mounted device or NULL on error
+ */
+char *mnt_get_mountpoint(const char *path)
+{
+ char *mnt = strdup(path);
+ struct stat st;
+ dev_t dir, base;
+
+ if (!mnt)
+ return NULL;
+ if (*mnt == '/' && *(mnt + 1) == '\0')
+ goto done;
+
+ if (stat(mnt, &st))
+ goto err;
+ base = st.st_dev;
+
+ do {
+ char *p = stripoff_last_component(mnt);
+
+ if (!p)
+ break;
+ if (stat(*mnt ? mnt : "/", &st))
+ goto err;
+ dir = st.st_dev;
+ if (dir != base) {
+ if (p > mnt)
+ *(p - 1) = '/';
+ goto done;
+ }
+ base = dir;
+ } while (mnt && *(mnt + 1) != '\0');
+
+ memcpy(mnt, "/", 2);
+done:
+ DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt));
+ return mnt;
+err:
+ free(mnt);
+ return NULL;
+}
+
+char *mnt_get_fs_root(const char *path, const char *mnt)
+{
+ char *m = (char *) mnt, *res;
+ const char *p;
+ size_t sz;
+
+ if (!m)
+ m = mnt_get_mountpoint(path);
+ if (!m)
+ return NULL;
+
+ sz = strlen(m);
+ p = sz > 1 ? path + sz : path;
+
+ if (m != mnt)
+ free(m);
+
+ res = *p ? strdup(p) : strdup("/");
+ DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
+ return res;
+}
+
+#ifdef TEST_PROGRAM
+int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *type = argv[1];
+ char *pattern = argv[2];
+
+ printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
+ return 0;
+}
+
+int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr = argv[1];
+ char *pattern = argv[2];
+
+ printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
+ return 0;
+}
+
+int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr = argv[1];
+ char *pattern = argv[2];
+
+ printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
+ return 0;
+}
+
+int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *optstr = argv[1];
+ char *pattern = argv[2];
+
+ printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
+ return 0;
+}
+
+int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *path = canonicalize_path(argv[1]),
+ *mnt = path ? mnt_get_mountpoint(path) : NULL;
+
+ printf("%s: %s\n", argv[1], mnt ? : "unknown");
+ free(mnt);
+ free(path);
+ return 0;
+}
+
+int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char *path = canonicalize_path(argv[1]),
+ *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
+
+ printf("%s: %s\n", argv[1], mnt ? : "unknown");
+ free(mnt);
+ free(path);
+ return 0;
+}
+
+int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
+{
+ char **filesystems = NULL;
+ int rc;
+
+ rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
+ if (!rc) {
+ char **p;
+ for (p = filesystems; *p; p++)
+ printf("%s\n", *p);
+ mnt_free_filesystems(filesystems);
+ }
+ return rc;
+}
+
+int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
+{
+ int rc;
+ char *path = canonicalize_path(argv[1]),
+ *last = NULL;
+
+ if (!path)
+ return -errno;
+
+ rc = mnt_chdir_to_parent(path, &last);
+ if (!rc) {
+ printf("path='%s', abs='%s', last='%s'\n",
+ argv[1], path, last);
+ }
+ free(path);
+ free(last);
+ return rc;
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
+ { "--match-options", test_match_options, "<options> <pattern> options matching" },
+ { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
+ { "--starts-with", test_startswith, "<string> <prefix>" },
+ { "--ends-with", test_endswith, "<string> <prefix>" },
+ { "--mountpoint", test_mountpoint, "<path>" },
+ { "--fs-root", test_fsroot, "<path>" },
+ { "--cd-parent", test_chdir, "<path>" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libmount/src/version.c b/libmount/src/version.c
new file mode 100644
index 0000000..00e4f99
--- /dev/null
+++ b/libmount/src/version.c
@@ -0,0 +1,127 @@
+/*
+ * version.c - Return the version of the blkid library
+ *
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * [Based on libblkid/version.c by Theodore Ts'o]
+ *
+ * See COPYING.libmount for the License of this software.
+ */
+
+/**
+ * SECTION: version
+ * @title: Version functions
+ * @short_description: functions to get library version.
+ */
+
+#include <ctype.h>
+
+#include "mountP.h"
+
+static const char *lib_version = LIBMOUNT_VERSION;
+static const char *lib_features[] = {
+#ifdef HAVE_LIBSELINUX
+ "selinux",
+#endif
+#ifdef CONFIG_LIBMOUNT_DEBUG
+ "debug",
+#endif
+ NULL
+};
+
+/**
+ * mnt_parse_version_string:
+ * @ver_string: version string (e.g "2.18.0")
+ *
+ * Returns: release version code.
+ */
+int mnt_parse_version_string(const char *ver_string)
+{
+ const char *cp;
+ int version = 0;
+
+ for (cp = ver_string; *cp; cp++) {
+ if (*cp == '.')
+ continue;
+ if (!isdigit(*cp))
+ break;
+ version = (version * 10) + (*cp - '0');
+ }
+ return version;
+}
+
+/**
+ * mnt_get_library_version:
+ * @ver_string: return pointer to the static library version string if not NULL
+ *
+ * Returns: release version number.
+ */
+int mnt_get_library_version(const char **ver_string)
+{
+ if (ver_string)
+ *ver_string = lib_version;
+
+ return mnt_parse_version_string(lib_version);
+}
+
+/**
+ * mnt_get_library_features:
+ * @features: returns pointer to the static array of strings, the array is
+ * terminated by NULL.
+ *
+ * Returns: number of items in the features array not including the last NULL,
+ * or less then zero in case of error
+ *
+ * Example:
+ * <informalexample>
+ * <programlisting>
+ * const char *features;
+ *
+ * mnt_get_library_features(&features);
+ * while (features && *features)
+ * printf("%s\n", *features++);
+ * </programlisting>
+ * </informalexample>
+ *
+ */
+int mnt_get_library_features(const char ***features)
+{
+ if (!features)
+ return -EINVAL;
+
+ *features = lib_features;
+ return ARRAY_SIZE(lib_features) - 1;
+}
+
+#ifdef TEST_PROGRAM
+int test_version(struct libmnt_test *ts, int argc, char *argv[])
+{
+ const char *ver;
+ const char **features;
+
+ mnt_get_library_version(&ver);
+
+ printf("Library version: %s\n", ver);
+ printf("Library API version: " LIBMOUNT_VERSION "\n");
+ printf("Library features:");
+
+ mnt_get_library_features(&features);
+ while (features && *features)
+ printf(" %s", *features++);
+
+ if (mnt_get_library_version(NULL) ==
+ mnt_parse_version_string(LIBMOUNT_VERSION))
+ return 0;
+
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test ts[] = {
+ { "--print", test_version, "print versions" },
+ { NULL }
+ };
+
+ return mnt_run_test(ts, argc, argv);
+}
+#endif