]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - libmount/src/tab.c
libmount: add mnt_table_{find,insert,move}_fs()
[thirdparty/util-linux.git] / libmount / src / tab.c
index b2a8d7fd47ead9ca413cd03be2c09f0c21943b51..674dc67e5b17796f293b3dadb7a97d7ba85f1788 100644 (file)
@@ -1,8 +1,14 @@
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
- * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com>
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
  *
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
+ * libmount 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.
  */
 
 /**
@@ -150,9 +156,9 @@ void mnt_unref_table(struct libmnt_table *tb)
  * @tb: tab pointer
  *
  * Deallocates the table. This function does not care about reference count. Don't
- * use this function directly -- it's better to use use mnt_unref_table().
+ * use this function directly -- it's better to use mnt_unref_table().
  *
- * The table entries (filesystems) are unrefrenced by mnt_reset_table() and
+ * The table entries (filesystems) are unreferenced by mnt_reset_table() and
  * cache by mnt_unref_cache().
  */
 void mnt_free_table(struct libmnt_table *tb)
@@ -287,18 +293,7 @@ const char *mnt_table_get_intro_comment(struct libmnt_table *tb)
  */
 int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm)
 {
-       char *p = NULL;
-
-       if (!tb)
-               return -EINVAL;
-       if (comm) {
-               p = strdup(comm);
-               if (!p)
-                       return -ENOMEM;
-       }
-       free(tb->comm_intro);
-       tb->comm_intro = p;
-       return 0;
+       return strdup_to_struct_member(tb, comm_intro, comm);
 }
 
 /**
@@ -339,18 +334,7 @@ const char *mnt_table_get_trailing_comment(struct libmnt_table *tb)
  */
 int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm)
 {
-       char *p = NULL;
-
-       if (!tb)
-               return -EINVAL;
-       if (comm) {
-               p = strdup(comm);
-               if (!p)
-                       return -ENOMEM;
-       }
-       free(tb->comm_tail);
-       tb->comm_tail = p;
-       return 0;
+       return strdup_to_struct_member(tb, comm_tail, comm);
 }
 
 /**
@@ -381,7 +365,7 @@ int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm)
  * same cache between more threads -- currently the cache does not provide any
  * locking method.
  *
- * This function increments cache reference counter. It's recomented to use
+ * This function increments cache reference counter. It's recommended to use
  * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
  * referenced by @tb only.
  *
@@ -411,6 +395,38 @@ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
        return tb ? tb->cache : NULL;
 }
 
+/**
+ * mnt_table_find_fs:
+ * @tb: tab pointer
+ * @fs: entry to look for
+ *
+ * Checks if @fs is part of table @tb.
+ *
+ * Returns: index of @fs in table, 0 if not found or negative number in case of error.
+ */
+int mnt_table_find_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
+{
+       struct list_head *p;
+       int i = 0;
+
+       if (!tb || !fs)
+               return -EINVAL;
+
+       if (list_empty(&fs->ents))
+               return 0;
+
+       /* Let's use directly list rather than mnt_table_next_fs() as we
+        * compare list entry with fs only.
+        */
+       list_for_each(p, &tb->ents) {
+               ++i;
+               if (list_entry(p, struct libmnt_fs, ents) == fs)
+                       return i;
+       }
+
+       return 0;
+}
+
 /**
  * mnt_table_add_fs:
  * @tb: tab pointer
@@ -427,6 +443,9 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
        if (!tb || !fs)
                return -EINVAL;
 
+       if (!list_empty(&fs->ents))
+               return -EBUSY;
+
        mnt_ref_fs(fs);
        list_add_tail(&fs->ents, &tb->ents);
        tb->nents++;
@@ -436,6 +455,95 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
        return 0;
 }
 
+static int __table_insert_fs(
+                       struct libmnt_table *tb, int before,
+                       struct libmnt_fs *pos, struct libmnt_fs *fs)
+{
+       struct list_head *head = pos ? &pos->ents : &tb->ents;
+
+       if (before)
+               list_add(&fs->ents, head);
+       else
+               list_add_tail(&fs->ents, head);
+
+       tb->nents++;
+
+       DBG(TAB, ul_debugobj(tb, "insert entry: %s %s",
+                       mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
+       return 0;
+}
+
+/**
+ * mnt_table_insert_fs:
+ * @tb: tab pointer
+ * @before: 1 to insert before pos, 0 to insert after pos
+ * @pos: entry to specify position or NULL
+ * @fs: new entry
+ *
+ * Adds a new entry to @tb before or after a specific table entry @pos. If the
+ * @pos is NULL than add the begin of the @tab if @before is 1; or to the tail
+ * of the @tb if @before is 0.
+ *
+ * This function inncrements reference to @fs. Don't forget to use
+ * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs
+ * referenced by the table only.
+
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_insert_fs(struct libmnt_table *tb, int before,
+                       struct libmnt_fs *pos, struct libmnt_fs *fs)
+{
+       if (!tb || !fs)
+               return -EINVAL;
+
+       if (!list_empty(&fs->ents))
+               return -EBUSY;
+
+       if (pos && mnt_table_find_fs(tb, pos) < 1)
+               return -ENOENT;
+
+       mnt_ref_fs(fs);
+       return __table_insert_fs(tb, before, pos, fs);
+}
+
+/**
+ * mnt_table_move_fs:
+ * @src: tab pointer of source table
+ * @dst: tab pointer of destination table
+ * @before: 1 to move before position, 0 to move after position
+ * @pos: entry to specify position or NULL
+ * @fs: entry to move
+ *
+ * Removes @fs from @src table and adds it before/after a specific entry @pos
+ * of @dst table. If the @pos is NULL than add the begin of the @dst if @before
+ * is 1; or to the tail of the @dst if @before is 0.
+ *
+ * The reference counter of @fs is not modified.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_move_fs(struct libmnt_table *src, struct libmnt_table *dst,
+                      int before, struct libmnt_fs *pos, struct libmnt_fs *fs)
+{
+       if (!src || !dst || !fs)
+               return -EINVAL;
+
+       if (mnt_table_find_fs(src, fs) < 1)
+               return -ENOENT;
+
+       if (pos && mnt_table_find_fs(dst, pos) < 1)
+               return -ENOENT;
+
+       /* remove from source */
+       list_del_init(&fs->ents);
+       src->nents--;
+
+       /* insert to the destination */
+       return __table_insert_fs(dst, before, pos, fs);
+}
+
+
 /**
  * mnt_table_remove_fs:
  * @tb: tab pointer
@@ -449,26 +557,41 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
  */
 int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
 {
-       if (!tb || !fs)
+       if (!tb || !fs || mnt_table_find_fs(tb, fs) < 1)
                return -EINVAL;
 
-       list_del(&fs->ents);
-       INIT_LIST_HEAD(&fs->ents);      /* otherwise FS still points to the list */
+       list_del_init(&fs->ents);
 
        mnt_unref_fs(fs);
        tb->nents--;
        return 0;
 }
 
+static inline struct libmnt_fs *get_parent_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
+{
+       struct libmnt_iter itr;
+       struct libmnt_fs *x;
+       int parent_id = mnt_fs_get_parent_id(fs);
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+       while (mnt_table_next_fs(tb, &itr, &x) == 0) {
+               if (mnt_fs_get_id(x) == parent_id)
+                       return x;
+       }
+
+       return NULL;
+}
+
 /**
  * mnt_table_get_root_fs:
  * @tb: mountinfo file (/proc/self/mountinfo)
  * @root: returns pointer to the root filesystem (/)
  *
- * The function uses the parent ID from the mountinfo file to determine the root filesystem
- * (the filesystem with the smallest ID). The function is designed mostly for
- * applications where it is necessary to sort mountpoints by IDs to get the tree
- * of the mountpoints (e.g. findmnt default output).
+ * The function uses the parent ID from the mountinfo file to determine the
+ * root filesystem (the filesystem with the smallest ID with parent ID missing
+ * in the table). The function is designed mostly for applications where it is
+ * necessary to sort mountpoints by IDs to get the tree of the mountpoints
+ * (e.g. findmnt default output).
  *
  * If you're not sure, then use
  *
@@ -491,6 +614,7 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
 
        *root = NULL;
 
+       /* get smallest possible ID from the table */
        mnt_reset_iter(&itr, MNT_ITER_FORWARD);
        while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
                int id = mnt_fs_get_parent_id(fs);
@@ -501,6 +625,15 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
                }
        }
 
+       /* go to the root node by "parent_id -> id" relation */
+       while (*root) {
+               struct libmnt_fs *x = get_parent_fs(tb, *root);
+               if (!x || x == *root)
+                       break;
+               DBG(TAB, ul_debugobj(tb, " messy mountinfo, walk to %s", mnt_fs_get_target(x)));
+               *root = x;
+       }
+
        return *root ? 0 : -EINVAL;
 }
 
@@ -712,7 +845,7 @@ static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid)
  * backward mode iterator).
  *
  * @MNT_UNIQ_FORWARD:  remove later mounted filesystems
- * @MNT_UNIQ_KEEPTREE: keep parent->id relation ship stil valid
+ * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
  *
  * Returns: negative number in case of error, or 0 o success.
  */
@@ -802,6 +935,7 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb,
                                            int direction)
 {
        char *mnt;
+       struct stat st;
 
        if (!tb || !path || !*path)
                return NULL;
@@ -810,6 +944,9 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb,
 
        DBG(TAB, ul_debugobj(tb, "lookup MOUNTPOINT: '%s'", path));
 
+       if (mnt_stat_mountpoint(path, &st))
+               return NULL;
+
        mnt = strdup(path);
        if (!mnt)
                return NULL;
@@ -867,6 +1004,20 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat
                if (mnt_fs_streq_target(fs, path))
                        return fs;
        }
+
+       /* try absolute path */
+       if (is_relative_path(path) && (cn = absolute_path(path))) {
+               DBG(TAB, ul_debugobj(tb, "lookup absolute TARGET: '%s'", cn));
+               mnt_reset_iter(&itr, direction);
+               while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
+                       if (mnt_fs_streq_target(fs, cn)) {
+                               free(cn);
+                               return fs;
+                       }
+               }
+               free(cn);
+       }
+
        if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
                return NULL;
 
@@ -879,7 +1030,7 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat
                        return fs;
        }
 
-       /* non-canonicaled path in struct libmnt_table
+       /* non-canonical path in struct libmnt_table
         * -- note that mountpoint in /proc/self/mountinfo is already
         *    canonicalized by the kernel
         */
@@ -914,6 +1065,8 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat
  * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
  * set (see mnt_table_set_cache()).
  *
+ * For btrfs returns tab entry for default id.
+ *
  * 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.
  *
@@ -936,9 +1089,33 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa
 
        /* native paths */
        mnt_reset_iter(&itr, direction);
+
        while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
-               if (mnt_fs_streq_srcpath(fs, path))
+
+               if (mnt_fs_streq_srcpath(fs, path)) {
+#ifdef HAVE_BTRFS_SUPPORT
+                       if (fs->fstype && !strcmp(fs->fstype, "btrfs")) {
+                               uint64_t default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
+                               char *val;
+                               size_t len;
+
+                               if (default_id == UINT64_MAX)
+                                       DBG(TAB, ul_debug("not found btrfs volume setting"));
+
+                               else if (mnt_fs_get_option(fs, "subvolid", &val, &len) == 0) {
+                                       uint64_t subvol_id;
+
+                                       if (mnt_parse_offset(val, len, &subvol_id)) {
+                                               DBG(TAB, ul_debugobj(tb, "failed to parse subvolid="));
+                                               continue;
+                                       }
+                                       if (subvol_id != default_id)
+                                               continue;
+                               }
+                       }
+#endif /* HAVE_BTRFS_SUPPORT */
                        return fs;
+               }
                if (mnt_fs_get_tag(fs, NULL, NULL) == 0)
                        ntags++;
        }
@@ -1223,6 +1400,134 @@ static char *remove_mountpoint_from_path(const char *path, const char *mnt)
        return res;
 }
 
+#ifdef HAVE_BTRFS_SUPPORT
+static int get_btrfs_fs_root(struct libmnt_table *tb, struct libmnt_fs *fs, char **root)
+{
+       char *vol = NULL, *p;
+       size_t sz, volsz = 0;
+
+       DBG(BTRFS, ul_debug("lookup for btrfs FS root"));
+       *root = NULL;
+
+       if (mnt_fs_get_option(fs, "subvolid", &vol, &volsz) == 0) {
+               char *target;
+               struct libmnt_fs *f;
+               char subvolidstr[sizeof(stringify_value(UINT64_MAX))];
+
+               DBG(BTRFS, ul_debug(" found subvolid=%s, checking", vol));
+
+               assert (volsz + 1 < sizeof(stringify_value(UINT64_MAX)));
+               memcpy(subvolidstr, vol, volsz);
+               subvolidstr[volsz] = '\0';
+
+               target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
+               if (!target)
+                       goto err;
+
+               DBG(BTRFS, ul_debug(" trying target=%s subvolid=%s", target, subvolidstr));
+               f = mnt_table_find_target_with_option(tb, target,
+                                       "subvolid", subvolidstr,
+                                       MNT_ITER_BACKWARD);
+               if (!tb->cache)
+                       free(target);
+               if (!f)
+                       goto not_found;
+
+               /* Instead of set of BACKREF queries constructing subvol path
+                * corresponding to a particular subvolid, use the one in
+                * mountinfo. Kernel keeps subvol path up to date.
+                */
+               if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0)
+                       goto not_found;
+
+       } else if (mnt_fs_get_option(fs, "subvol", &vol, &volsz) != 0) {
+               /* If fstab entry does not contain "subvol", we have to
+                * check, whether btrfs has default subvolume defined.
+                */
+               uint64_t default_id;
+               char *target;
+               struct libmnt_fs *f;
+               char default_id_str[sizeof(stringify_value(UINT64_MAX))];
+
+               DBG(BTRFS, ul_debug(" subvolid/subvol not found, checking default"));
+
+               default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
+               if (default_id == UINT64_MAX)
+                       goto not_found;
+
+               /* Volume has default subvolume. Check if it matches to
+                * the one in mountinfo.
+                *
+                * Only kernel >= 4.2 reports subvolid. On older
+                * kernels, there is no reasonable way to detect which
+                * subvolume was mounted.
+                */
+               target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
+               if (!target)
+                       goto err;
+
+               snprintf(default_id_str, sizeof(default_id_str), "%llu",
+                               (unsigned long long int) default_id);
+
+               DBG(BTRFS, ul_debug(" trying target=%s default subvolid=%s",
+                                       target, default_id_str));
+
+               f = mnt_table_find_target_with_option(tb, target,
+                                       "subvolid", default_id_str,
+                                       MNT_ITER_BACKWARD);
+               if (!tb->cache)
+                       free(target);
+               if (!f)
+                       goto not_found;
+
+               /* Instead of set of BACKREF queries constructing
+                * subvol path, use the one in mountinfo. Kernel does
+                * the evaluation for us.
+                */
+               DBG(BTRFS, ul_debug("setting FS root: btrfs default subvolid = %s",
+                                       default_id_str));
+
+               if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0)
+                       goto not_found;
+       }
+
+       DBG(BTRFS, ul_debug(" using subvol=%s", vol));
+       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';
+       return 0;
+
+not_found:
+       DBG(BTRFS, ul_debug(" not found btrfs volume setting"));
+       return 1;
+err:
+       DBG(BTRFS, ul_debug(" error on btrfs volume setting evaluation"));
+       return errno ? -errno : -1;
+}
+#endif /* HAVE_BTRFS_SUPPORT */
+
+static const char *get_cifs_unc_subdir_path (const char *unc)
+{
+       /*
+        *  1 or more slash:     %*[/]
+        *  1 or more non-slash: %*[^/]
+        *  number of byte read: %n
+        */
+       int share_end = 0;
+       int r = sscanf(unc, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end);
+       if (r == EOF || share_end == 0)
+               return NULL;
+       return unc + share_end;
+}
+
 /*
  * tb: /proc/self/mountinfo
  * fs: filesystem
@@ -1232,6 +1537,8 @@ static char *remove_mountpoint_from_path(const char *path, const char *mnt)
  *
  * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
  *
+ * If @tb is NULL then defaults to '/'.
+ *
  * Returns: entry from @tb that will be used as a source for @fs if the @fs is
  *          bindmount.
  *
@@ -1244,7 +1551,6 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
 {
        char *root = NULL;
        const char *mnt = NULL;
-       const char *fstype;
        struct libmnt_fs *src_fs = NULL;
 
        assert(fs);
@@ -1252,8 +1558,6 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
 
        DBG(TAB, ul_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;
@@ -1318,107 +1622,10 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
        /*
         * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
         */
-       else if (fstype && (!strcmp(fstype, "btrfs") || !strcmp(fstype, "auto"))) {
-               char *vol = NULL, *p;
-               size_t sz, volsz = 0;
-
-               DBG(BTRFS, ul_debug("lookup for btrfs FS root"));
-
-               if (mnt_fs_get_option(fs, "subvolid", &vol, &volsz) != 0) {
-                       if (mnt_fs_get_option(fs, "subvol", &vol, &volsz) != 0) {
-                               /* If fstab entry does not contain "subvol", we
-                                * have to check, whether btrfs has default
-                                * subvolume defined.
-                                */
-                               uint64_t default_id;
-                               char *target;
-                               struct libmnt_fs *f;
-                               char default_id_str[sizeof(stringify_value(UINT64_MAX))];
-
-                               DBG(BTRFS, ul_debug(" subvolid/subvol not set, checking default"));
-
-                               default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
-                               if (default_id == UINT64_MAX)
-                                       goto dflt;
-
-                               /* Volume has default subvolume. Check if it
-                                * matches to the one in mountinfo.
-                                *
-                                * Only kernel >= 4.2 reports subvolid. On older
-                                * kernels, there is no reasonable way to detect
-                                * which subvolume was mounted.
-                                */
-                               target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
-                               if (!target)
-                                       goto err;
-
-                               snprintf(default_id_str, sizeof(default_id_str), "%llu",
-                                               (unsigned long long int) default_id);
-
-                               DBG(BTRFS, ul_debug(" tring target=%s default subvolid=%s", target, default_id_str));
-                               f = mnt_table_find_target_with_option(tb, target,
-                                                       "subvolid", default_id_str,
-                                                       MNT_ITER_BACKWARD);
-                               if (!tb->cache)
-                                       free(target);
-                               if (!f)
-                                       goto dflt;
-
-                               /* Instead of set of BACKREF queries constructing
-                                * subvol path, use the one in mountinfo. Kernel
-                                * does the evaluation for us. */
-                               DBG(BTRFS, ul_debug("setting FS root: btrfs default subvolid = %s", default_id_str));
-                               if (mnt_fs_get_option(f, "subvol", &vol, &volsz))
-                                       goto dflt;
-                       } else
-                               DBG(BTRFS, ul_debug(" found subvol=%s", vol));
-               } else {
-                       char *target;
-                       struct libmnt_fs *f;
-                       char subvolidstr[sizeof(stringify_value(UINT64_MAX))];
-
-                       DBG(BTRFS, ul_debug(" found subvolid=%s, checking", vol));
-
-                       assert (volsz + 1 < sizeof(stringify_value(UINT64_MAX)));
-                       memcpy(subvolidstr, vol, volsz);
-                       subvolidstr[volsz] = '\0';
-
-                       target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
-                       if (!target)
-                               goto err;
-
-                       DBG(BTRFS, ul_debug(" tring target=%s subvolid=%s", target, subvolidstr));
-                       f = mnt_table_find_target_with_option(tb, target,
-                                               "subvolid", subvolidstr,
-                                               MNT_ITER_BACKWARD);
-                       if (!tb->cache)
-                               free(target);
-
-                       if (!f)
-                               goto dflt;
-
-                       /* Instead of set of BACKREF queries constructing
-                        * subvol path corresponding to a particular subvolid,
-                        * use the one in mountinfo. Kernel keeps subvol path
-                        * up to date.
-                        */
-                       if (mnt_fs_get_option(f, "subvol", &vol, &volsz))
-                               goto dflt;
-
-                       DBG(BTRFS, ul_debug(" found subvol=%s", vol));
-               }
-
-               sz = volsz;
-               if (*vol != '/')
-                       sz++;
-               root = malloc(sz + 1);
-               if (!root)
+       else if (tb && fs->fstype &&
+                (!strcmp(fs->fstype, "btrfs") || !strcmp(fs->fstype, "auto"))) {
+               if (get_btrfs_fs_root(tb, fs, &root) < 0)
                        goto err;
-               p = root;
-               if (*vol != '/')
-                       *p++ = '/';
-               memcpy(p, vol, volsz);
-               *(root + sz) = '\0';
        }
 #endif /* HAVE_BTRFS_SUPPORT */
 
@@ -1464,13 +1671,15 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
        struct libmnt_fs *fs;
 
        char *root = NULL;
+       char *src2 = NULL;
        const char *src = NULL, *tgt = NULL;
        char *xtgt = NULL;
        int rc = 0;
        dev_t devno = 0;
 
-       DBG(FS, ul_debugobj(fstab_fs, "is FS mounted? [target=%s]",
-                               mnt_fs_get_target(fstab_fs)));
+       DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: target=%s, source=%s",
+                               mnt_fs_get_target(fstab_fs),
+                               mnt_fs_get_source(fstab_fs)));
 
        if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_is_empty(tb)) {
                DBG(FS, ul_debugobj(fstab_fs, "- ignore (swap or no data)"));
@@ -1482,12 +1691,22 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
                struct libmnt_fs *rootfs;
                int flags = 0;
 
-               if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0)
+               if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0 ||
+                   mnt_fs_get_option(fstab_fs, "rbind", NULL, NULL) == 0)
                        flags = MS_BIND;
 
                rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
-               if (rootfs)
+               if (rootfs) {
+                       const char *fstype = mnt_fs_get_fstype(rootfs);
+
                        src = mnt_fs_get_srcpath(rootfs);
+                       if (fstype && strncmp(fstype, "nfs", 3) == 0 && root) {
+                               /* NFS stores the root at the end of the source */
+                               src = src2 = strappend(src, root);
+                               free(root);
+                               root = NULL;
+                       }
+               }
        }
 
        if (!src)
@@ -1512,7 +1731,7 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
        }
        mnt_reset_iter(&itr, MNT_ITER_FORWARD);
 
-       DBG(FS, ul_debugobj(fstab_fs, "is mounted: src=%s, tgt=%s, root=%s", src, tgt, root));
+       DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src, tgt, root));
 
        while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
 
@@ -1528,30 +1747,42 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
                        uint64_t offset = 0;
                        char *val;
                        size_t len;
-                       int flags;
+                       int flags = 0;
 
-                       if (!mnt_fs_is_kernel(fs) ||
-                           !mnt_fs_get_srcpath(fs) ||
+                       if (!mnt_fs_get_srcpath(fs) ||
                            !startswith(mnt_fs_get_srcpath(fs), "/dev/loop"))
                                continue;       /* does not look like loopdev */
 
-                       if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0 &&
-                           mnt_parse_offset(val, len, &offset)) {
-                               DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset="));
-                               continue;
-                       } else
+                       if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0) {
+                               if (mnt_parse_offset(val, len, &offset)) {
+                                       DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset="));
+                                       continue;
+                               }
                                flags = LOOPDEV_FL_OFFSET;
+                       }
 
+                       DBG(FS, ul_debugobj(fs, "checking for loop: src=%s", mnt_fs_get_srcpath(fs)));
 #if __linux__
-                       if (loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, flags))
-                               break;
+                       if (!loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, 0, flags))
+                               continue;
+
+                       DBG(FS, ul_debugobj(fs, "used loop"));
 #endif
                }
 
                if (root) {
-                       const char *r = mnt_fs_get_root(fs);
-                       if (!r || strcmp(r, root) != 0)
-                               continue;
+                       const char *fstype = mnt_fs_get_fstype(fs);
+
+                       if (fstype && strcmp(fstype, "cifs") == 0) {
+                               const char *unc_subdir = get_cifs_unc_subdir_path(src);
+                               const char *path_on_fs = mnt_fs_get_root(fs);
+                               if (!unc_subdir || !path_on_fs || !streq_paths(unc_subdir, path_on_fs))
+                                       continue;
+                       } else {
+                               const char *r = mnt_fs_get_root(fs);
+                               if (!r || strcmp(r, root) != 0)
+                                       continue;
+                       }
                }
 
                /*
@@ -1575,6 +1806,7 @@ done:
        free(root);
 
        DBG(TAB, ul_debugobj(tb, "mnt_table_is_fs_mounted: %s [rc=%d]", src, rc));
+       free(src2);
        return rc;
 }
 
@@ -1588,7 +1820,7 @@ static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
        return 1;       /* all errors are recoverable -- this is the default */
 }
 
-struct libmnt_table *create_table(const char *file, int comments)
+static struct libmnt_table *create_table(const char *file, int comments)
 {
        struct libmnt_table *tb;
 
@@ -1610,7 +1842,7 @@ err:
        return NULL;
 }
 
-int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
+static int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
 {
        struct libmnt_table *tb;
        struct libmnt_fs *fs;
@@ -1640,7 +1872,7 @@ done:
        return rc;
 }
 
-int test_parse(struct libmnt_test *ts, int argc, char *argv[])
+static int test_parse(struct libmnt_test *ts, int argc, char *argv[])
 {
        struct libmnt_table *tb = NULL;
        struct libmnt_iter *itr = NULL;
@@ -1676,7 +1908,52 @@ done:
        return rc;
 }
 
-int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
+static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[])
+{
+       struct libmnt_table *tb;
+       struct libmnt_fs *fs = NULL;
+       struct libmnt_cache *mpc = NULL;
+       const char *file, *what;
+       int rc = -1;
+
+       if (argc != 3) {
+               fprintf(stderr, "try --help\n");
+               return -EINVAL;
+       }
+
+       file = argv[1], what = argv[2];
+
+       tb = create_table(file, FALSE);
+       if (!tb)
+               goto done;
+
+       /* create a cache for canonicalized paths */
+       mpc = mnt_new_cache();
+       if (!mpc)
+               goto done;
+       mnt_table_set_cache(tb, mpc);
+       mnt_unref_cache(mpc);
+
+       fs = mnt_table_find_target(tb, what, MNT_ITER_BACKWARD);
+
+       if (!fs)
+               fprintf(stderr, "%s: not found '%s'\n", file, what);
+       else {
+               int idx = mnt_table_find_fs(tb, fs);
+
+               if (idx < 1)
+                       fprintf(stderr, "%s: not found '%s' fs pointer", file, what);
+               else {
+                       printf("%s index is %d\n", what, idx);
+                       rc = 0;
+               }
+       }
+done:
+       mnt_unref_table(tb);
+       return rc;
+}
+
+static int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
 {
        struct libmnt_table *tb;
        struct libmnt_fs *fs = NULL;
@@ -1718,17 +1995,17 @@ done:
        return rc;
 }
 
-int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
+static 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[])
+static 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[])
+static int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
 {
        struct libmnt_table *tb;
        struct libmnt_fs *fs;
@@ -1755,7 +2032,7 @@ done:
        return rc;
 }
 
-int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
 {
        struct libmnt_table *tb;
        struct libmnt_fs *fs;
@@ -1788,9 +2065,14 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
        struct libmnt_fs *fs;
        struct libmnt_iter *itr = NULL;
        struct libmnt_cache *mpc = NULL;
-       int rc;
+       int writable = 0;
+       const char *path = NULL;
+
+       if (mnt_has_regular_mtab(&path, &writable) == 1 && writable == 0)
+               tb = mnt_new_table_from_file(path);
+       else
+               tb = mnt_new_table_from_file("/proc/self/mountinfo");
 
-       tb = mnt_new_table_from_file("/proc/self/mountinfo");
        if (!tb) {
                fprintf(stderr, "failed to parse mountinfo\n");
                return -1;
@@ -1810,7 +2092,7 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
        mnt_table_set_cache(tb, mpc);
        mnt_unref_cache(mpc);
 
-       while(mnt_table_next_fs(fstab, itr, &fs) == 0) {
+       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),
@@ -1821,12 +2103,11 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
                                        mnt_fs_get_target(fs));
        }
 
-       rc = 0;
 done:
        mnt_unref_table(tb);
        mnt_unref_table(fstab);
        mnt_free_iter(itr);
-       return rc;
+       return 0;
 }
 
 /* returns 0 if @a and @b targets are the same */
@@ -1878,9 +2159,10 @@ int main(int argc, char *argv[])
        { "--find-backward", test_find_bw, "<file> <source|target> <string>" },
        { "--uniq-target",   test_uniq,    "<file>" },
        { "--find-pair",     test_find_pair, "<file> <source> <target>" },
+       { "--find-fs",       test_find_idx, "<file> <target>" },
        { "--find-mountpoint", test_find_mountpoint, "<path>" },
        { "--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" },
+       { "--is-mounted",    test_is_mounted, "<fstab> check what from fstab is already mounted" },
        { NULL }
        };