summaryrefslogtreecommitdiffstats
path: root/libmount/src/tab.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmount/src/tab.c')
-rw-r--r--libmount/src/tab.c1095
1 files changed, 1095 insertions, 0 deletions
diff --git a/libmount/src/tab.c b/libmount/src/tab.c
new file mode 100644
index 000000000..227c5aa43
--- /dev/null
+++ b/libmount/src/tab.c
@@ -0,0 +1,1095 @@
+/*
+ * 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: tab
+ * @title: Table of filesystems
+ * @short_description: container for entries from fstab/mtab/mountinfo
+ *
+ *
+ * Note that mnt_table_find_* functions are mount(8) compatible. These functions
+ * try to found 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 <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <blkid.h>
+
+#include "nls.h"
+#include "mountP.h"
+#include "c.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);
+
+ 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 (fs->target && strcmp(fs->target, path) == 0)
+ 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 (fs->target && strcmp(fs->target, cn) == 0)
+ return fs;
+ }
+
+ /* non-canonicaled path in struct libmnt_table */
+ mnt_reset_iter(&itr, direction);
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ char *p;
+
+ if (!fs->target || !(fs->flags & MNT_FS_SWAP) ||
+ (*fs->target == '/' && *(fs->target + 1) == '\0'))
+ continue;
+
+ p = mnt_resolve_path(fs->target, tb->cache);
+ if (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 valid source path is NULL; the libmount uses NULL instead of
+ * "none". The "none" is used in /proc/{mounts,self/mountninfo} 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) {
+ const char *src = mnt_fs_get_source(fs);
+
+ p = mnt_fs_get_srcpath(fs);
+
+ if (path == NULL && src == NULL)
+ return fs; /* source is "none" */
+ if (p && strcmp(p, path) == 0)
+ return fs;
+ if (!p && src)
+ ntags++; /* mnt_fs_get_srcpath() returs nothing, it's TAG */
+ }
+
+ 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) {
+ p = mnt_fs_get_srcpath(fs);
+ if (p && strcmp(p, cn) == 0)
+ 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);
+ if (x && !strcmp(x, cn))
+ 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 (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO))
+ continue;
+ p = mnt_fs_get_srcpath(fs);
+ if (p)
+ p = mnt_resolve_path(p, tb->cache);
+ if (p && strcmp(cn, p) == 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;
+}
+
+/*
+ * @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.
+ */
+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;
+
+ DBG(TAB, mnt_debug("fs-root for bind"));
+
+ src = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache);
+ if (!src)
+ goto err;
+
+ mnt = mnt_get_mountpoint(src);
+ if (!mnt)
+ goto err;
+
+ root = mnt_get_fs_root(src, mnt);
+
+ 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;
+}
+
+/**
+ * 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.
+ *
+ * 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;
+ struct libmnt_fs *src_fs;
+ const char *src, *tgt;
+ int flags = 0, rc = 0;
+
+ assert(tb);
+ assert(fstab_fs);
+
+ if (fstab_fs->flags & MNT_FS_SWAP)
+ return 0;
+
+ if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0)
+ flags = MS_BIND;
+
+ src_fs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
+ if (src_fs)
+ src = mnt_fs_get_srcpath(src_fs);
+ else
+ src = mnt_resolve_spec(mnt_fs_get_source(fstab_fs), tb->cache);
+
+ tgt = mnt_fs_get_target(fstab_fs);
+
+ if (tgt || src || root) {
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ const char *s = mnt_fs_get_srcpath(fs),
+ *t = mnt_fs_get_target(fs),
+ *r = mnt_fs_get_root(fs);
+
+ if (s && t && r && !strcmp(t, tgt) &&
+ !strcmp(s, src) && !strcmp(r, root))
+ 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;
+ int rc = -1;
+
+ tb = create_table(argv[1]);
+ if (!tb)
+ return -1;
+
+ 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);
+ 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;
+ 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;
+
+ 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_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 */