]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - libmount/src/context.c
libmount: do not call umount helper on --fake
[thirdparty/util-linux.git] / libmount / src / context.c
index 70c7b33ad57d3b8603a20ecd6b16f5b958feb446..ec04191fccb20f56f36b382ebe4ef0d525a9da46 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ * 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.
@@ -32,6 +32,9 @@
  */
 
 #include "mountP.h"
+#include "fileutils.h"
+
+#include <sys/wait.h>
 
 /**
  * mnt_new_context:
@@ -47,24 +50,21 @@ struct libmnt_context *mnt_new_context(void)
        if (!cxt)
                return NULL;
 
+       INIT_LIST_HEAD(&cxt->addmounts);
+
        ruid = getuid();
        euid = geteuid();
 
-       cxt->syscall_status = 1;        /* not called yet */
-       cxt->helper_exec_status = 1;
+       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",
+       DBG(CXT, ul_debugobj(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;
 }
@@ -85,16 +85,16 @@ void mnt_free_context(struct libmnt_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_unref_table(cxt->fstab);
+       mnt_unref_cache(cxt->cache);
 
        mnt_context_clear_loopdev(cxt);
        mnt_free_lock(cxt->lock);
        mnt_free_update(cxt->update);
 
-       DBG(CXT, mnt_debug_h(cxt, "<---- free"));
+       free(cxt->children);
+
+       DBG(CXT, ul_debugobj(cxt, "<---- free"));
        free(cxt);
 }
 
@@ -102,11 +102,11 @@ void mnt_free_context(struct libmnt_context *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, ....)
+ * Resets all information in the context that is 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
+ * The match patterns, cached fstab, cached canonicalized paths and tags and
+ * [e]uid are not reset. You have to use
  *
  *     mnt_context_set_fstab(cxt, NULL);
  *     mnt_context_set_cache(cxt, NULL);
@@ -114,7 +114,7 @@ void mnt_free_context(struct libmnt_context *cxt)
  *     mnt_context_set_options_pattern(cxt, NULL);
  *
  *
- * to reset these stuff.
+ * to reset this stuff.
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -125,35 +125,42 @@ int mnt_reset_context(struct libmnt_context *cxt)
        if (!cxt)
                return -EINVAL;
 
-       DBG(CXT, mnt_debug_h(cxt, "<---- reset [status=%d] ---->",
+       DBG(CXT, ul_debugobj(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);
+       mnt_unref_fs(cxt->fs);
+       mnt_unref_table(cxt->mtab);
+       mnt_unref_table(cxt->utab);
 
        free(cxt->helper);
        free(cxt->orig_user);
 
        cxt->fs = NULL;
        cxt->mtab = NULL;
-       cxt->ambi = 0;
+       cxt->utab = NULL;
        cxt->helper = NULL;
        cxt->orig_user = NULL;
        cxt->mountflags = 0;
        cxt->user_mountflags = 0;
        cxt->mountdata = NULL;
        cxt->flags = MNT_FL_DEFAULT;
-       cxt->syscall_status = 1;
-       cxt->helper_exec_status = 1;
-       cxt->helper_status = 0;
 
-       /* restore non-resetable flags */
-       cxt->flags |= (fl & MNT_FL_EXTERN_FSTAB);
-       cxt->flags |= (fl & MNT_FL_EXTERN_CACHE);
+       /* free additional mounts list */
+       while (!list_empty(&cxt->addmounts)) {
+               struct libmnt_addmount *ad = list_entry(cxt->addmounts.next,
+                                                 struct libmnt_addmount,
+                                                 mounts);
+               mnt_free_addmount(ad);
+       }
+
+       mnt_context_reset_status(cxt);
+
+       if (cxt->table_fltrcb)
+               mnt_context_set_tabfilter(cxt, NULL, NULL);
+
+       /* restore non-resettable flags */
        cxt->flags |= (fl & MNT_FL_NOMTAB);
        cxt->flags |= (fl & MNT_FL_FAKE);
        cxt->flags |= (fl & MNT_FL_SLOPPY);
@@ -161,20 +168,107 @@ int mnt_reset_context(struct libmnt_context *cxt)
        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);
+       cxt->flags |= (fl & MNT_FL_NOSWAPMATCH);
+       cxt->flags |= (fl & MNT_FL_TABPATHS_CHECKED);
        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 context_init_paths(struct libmnt_context *cxt, int writable)
+{
+       assert(cxt);
+
+       if (!cxt->mtab_path)
+               cxt->mtab_path = mnt_get_mtab_path();
+       if (!cxt->utab_path)
+               cxt->utab_path = mnt_get_utab_path();
+
+       if (!writable)
+               return 0;               /* only paths wanted */
+       if (mnt_context_is_nomtab(cxt))
+               return 0;               /* write mode overrided by mount -n */
+       if (cxt->flags & MNT_FL_TABPATHS_CHECKED)
+               return 0;
+
+       DBG(CXT, ul_debugobj(cxt, "checking for writable tab files"));
+
+#ifdef USE_LIBMOUNT_FORCE_MOUNTINFO
+       cxt->mtab_writable = 0;
+#else
+       mnt_has_regular_mtab(&cxt->mtab_path, &cxt->mtab_writable);
+#endif
+
+       if (!cxt->mtab_writable)
+               /* use /run/mount/utab if /etc/mtab is useless */
+               mnt_has_regular_utab(&cxt->utab_path, &cxt->utab_writable);
+
+       cxt->flags |= MNT_FL_TABPATHS_CHECKED;
+       return 0;
+}
+
+int mnt_context_mtab_writable(struct libmnt_context *cxt)
+{
+       assert(cxt);
+
+       context_init_paths(cxt, 1);
+       return cxt->mtab_writable == 1;
+}
+
+int mnt_context_utab_writable(struct libmnt_context *cxt)
+{
+       assert(cxt);
+
+       context_init_paths(cxt, 1);
+       return cxt->utab_writable == 1;
+}
+
+const char *mnt_context_get_writable_tabpath(struct libmnt_context *cxt)
+{
+       assert(cxt);
+
+       context_init_paths(cxt, 1);
+       return cxt->mtab_writable ? cxt->mtab_path : cxt->utab_path;
+}
+
+
 static int set_flag(struct libmnt_context *cxt, int flag, int enable)
 {
+       assert(cxt);
        if (!cxt)
                return -EINVAL;
-       if (enable)
+       if (enable) {
+               DBG(CXT, ul_debugobj(cxt, "enabling flag %04x", flag));
                cxt->flags |= flag;
-       else
+       } else {
+               DBG(CXT, ul_debugobj(cxt, "disabling flag %04x", flag));
                cxt->flags &= ~flag;
+       }
        return 0;
 }
 
@@ -182,20 +276,47 @@ static int set_flag(struct libmnt_context *cxt, int flag, int enable)
  * mnt_context_is_restricted:
  * @cxt: mount context
  *
- * Returns: 0 for unrestricted mount (user is root), or 1 for non-root mounts
+ * Returns: 0 for an 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: mask, see MNT_OMASK_* flags in libmount mount.h
+ * @mode: MNT_OMODE_* flags
+ *
+ * Controls how to use mount optionssource 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
  *
- * Controls how to use mount options from fstab/mtab.
+ * @MNT_OMODE_REPLACE: replace existing options with options from mtab/fstab
+ *
+ * @MNT_OMODE_FORCE: always read mtab/fstab (although source and target are 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 else is defined
+ * - the flags are evaluated 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.
  */
@@ -211,12 +332,12 @@ int mnt_context_set_optsmode(struct libmnt_context *cxt, int mode)
  * mnt_context_get_optsmode
  * @cxt: mount context
  *
- * Returns: MNT_OMASK_* mask or zero.
+ * Returns: MNT_OMODE_* mask or zero.
  */
 
 int mnt_context_get_optsmode(struct libmnt_context *cxt)
 {
-       return cxt ? cxt->optsmode : 0;
+       return cxt->optsmode;
 }
 
 /**
@@ -225,12 +346,13 @@ int mnt_context_get_optsmode(struct libmnt_context *cxt)
  * @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
+ * canonicalizes paths when searching in fstab and when preparing source and target paths
  * for mount(2) syscall.
  *
- * This fuction has effect to the private 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).
+ * This fuction has an effect on the private (within context) fstab instance only
+ * (see mnt_context_set_fstab()). If you want to use an external fstab then you
+ * need to manage your private struct libmnt_cache (see mnt_table_set_cache(fstab,
+ * NULL).
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -239,6 +361,17 @@ 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 is 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
@@ -261,9 +394,59 @@ int mnt_context_enable_lazy(struct libmnt_context *cxt, int enable)
  */
 int mnt_context_is_lazy(struct libmnt_context *cxt)
 {
-       return cxt && (cxt->flags & MNT_FL_LAZY) ? 1 : 0;
+       return 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->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 f the current context is child, or 0
+ */
+int mnt_context_is_child(struct libmnt_context *cxt)
+{
+       /* See mnt_fork_context(), the for fork flag is always disabled
+        * for children to avoid recursive forking.
+        */
+       return !mnt_context_is_fork(cxt) && cxt->pid;
+}
 
 /**
  * mnt_context_enable_rdonly_umount:
@@ -284,14 +467,14 @@ int mnt_context_enable_rdonly_umount(struct libmnt_context *cxt, int enable)
  * mnt_context_is_rdonly_umount
  * @cxt: mount context
  *
- * See also mnt_context_enable_rdonly_umount() and see umount(8) man page,
+ * See also mnt_context_enable_rdonly_umount() and 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;
+       return cxt->flags & MNT_FL_RDONLY_UMOUNT ? 1 : 0;
 }
 
 /**
@@ -308,6 +491,18 @@ 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->flags & MNT_FL_NOHELPERS ? 1 : 0;
+}
+
+
 /**
  * mnt_context_enable_sloppy:
  * @cxt: mount context
@@ -330,7 +525,7 @@ int mnt_context_enable_sloppy(struct libmnt_context *cxt, int enable)
  */
 int mnt_context_is_sloppy(struct libmnt_context *cxt)
 {
-       return cxt && (cxt->flags & MNT_FL_SLOPPY) ? 1 : 0;
+       return cxt->flags & MNT_FL_SLOPPY ? 1 : 0;
 }
 
 /**
@@ -355,7 +550,7 @@ int mnt_context_enable_fake(struct libmnt_context *cxt, int enable)
  */
 int mnt_context_is_fake(struct libmnt_context *cxt)
 {
-       return cxt && (cxt->flags & MNT_FL_FAKE) ? 1 : 0;
+       return cxt->flags & MNT_FL_FAKE ? 1 : 0;
 }
 
 /**
@@ -373,14 +568,40 @@ int mnt_context_disable_mtab(struct libmnt_context *cxt, int disable)
 }
 
 /**
- * mnt_context_is_nomtab
+ * 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;
+       return 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->flags & MNT_FL_NOSWAPMATCH ? 0 : 1;
 }
 
 /**
@@ -405,7 +626,7 @@ int mnt_context_enable_force(struct libmnt_context *cxt, int enable)
  */
 int mnt_context_is_force(struct libmnt_context *cxt)
 {
-       return cxt && (cxt->flags & MNT_FL_FORCE) ? 1 : 0;
+       return cxt->flags & MNT_FL_FORCE ? 1 : 0;
 }
 
 /**
@@ -430,7 +651,7 @@ int mnt_context_enable_verbose(struct libmnt_context *cxt, int enable)
  */
 int mnt_context_is_verbose(struct libmnt_context *cxt)
 {
-       return cxt && (cxt->flags & MNT_FL_VERBOSE) ? 1 : 0;
+       return cxt->flags & MNT_FL_VERBOSE ? 1 : 0;
 }
 
 /**
@@ -438,7 +659,7 @@ int mnt_context_is_verbose(struct libmnt_context *cxt)
  * @cxt: mount context
  * @enable: TRUE or FALSE
  *
- * Enable/disable loop delete (destroy) after umount (see umount(8), option -d)
+ * Enable/disable the loop delete (destroy) after umount (see umount(8), option -d)
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -447,18 +668,30 @@ 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->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().
+ * overwrite the private @fs with an external instance. This function
+ * increments @fs reference counter (and deincrement reference counter of the
+ * old fs).
  *
  * 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.
+ * functions, If the @fs is NULL, then all current FS specific settings (source,
+ * target, etc., exclude spec) are reset.
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -466,10 +699,9 @@ 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);
+       mnt_ref_fs(fs);                 /* new */
+       mnt_unref_fs(cxt->fs);          /* old */
        cxt->fs = fs;
        return 0;
 }
@@ -482,24 +714,60 @@ int mnt_context_set_fs(struct libmnt_context *cxt, struct libmnt_fs *fs)
  * 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.
+ * Returns: pointer to FS description or NULL in case of a calloc() error.
  */
 struct libmnt_fs *mnt_context_get_fs(struct libmnt_context *cxt)
 {
        if (!cxt)
                return NULL;
-       if (!cxt->fs) {
+       if (!cxt->fs)
                cxt->fs = mnt_new_fs();
-               cxt->flags &= ~MNT_FL_EXTERN_FS;
-       }
        return cxt->fs;
 }
 
+/**
+ * mnt_context_get_fs_userdata:
+ * @cxt: mount context
+ *
+ * Returns: pointer to userdata or NULL.
+ */
+void *mnt_context_get_fs_userdata(struct libmnt_context *cxt)
+{
+       return cxt->fs ? mnt_fs_get_userdata(cxt->fs) : NULL;
+}
+
+/**
+ * mnt_context_get_fstab_userdata:
+ * @cxt: mount context
+ *
+ * Returns: pointer to userdata or NULL.
+ */
+void *mnt_context_get_fstab_userdata(struct libmnt_context *cxt)
+{
+       return cxt->fstab ? mnt_table_get_userdata(cxt->fstab) : NULL;
+}
+
+/**
+ * mnt_context_get_mtab_userdata:
+ * @cxt: mount context
+ *
+ * Returns: pointer to userdata or NULL.
+ */
+void *mnt_context_get_mtab_userdata(struct libmnt_context *cxt)
+{
+       return cxt->mtab ? mnt_table_get_userdata(cxt->mtab) : NULL;
+}
+
 /**
  * mnt_context_set_source:
  * @cxt: mount context
  * @source: mount source (device, directory, UUID, LABEL, ...)
  *
+ * Note that libmount does not interpret "nofail" (MNT_MS_NOFAIL)
+ * mount option. The real return code is always returned, when
+ * the device does not exist then it's usually MNT_ERR_NOSOURCE
+ * from libmount or ENOENT, ENOTDIR, ENOTBLK, ENXIO from moun(2).
+ *
  * Returns: 0 on success, negative number in case of error.
  */
 int mnt_context_set_source(struct libmnt_context *cxt, const char *source)
@@ -511,7 +779,7 @@ int mnt_context_set_source(struct libmnt_context *cxt, const char *source)
  * mnt_context_get_source:
  * @cxt: mount context
  *
- * Returns: returns pointer or NULL in case of error pr if not set.
+ * Returns: returns pointer or NULL in case of error or if not set.
  */
 const char *mnt_context_get_source(struct libmnt_context *cxt)
 {
@@ -534,7 +802,7 @@ int mnt_context_set_target(struct libmnt_context *cxt, const char *target)
  * mnt_context_get_target:
  * @cxt: mount context
  *
- * Returns: returns pointer or NULL in case of error pr if not set.
+ * Returns: returns pointer or NULL in case of error or if not set.
  */
 const char *mnt_context_get_target(struct libmnt_context *cxt)
 {
@@ -546,15 +814,14 @@ const char *mnt_context_get_target(struct libmnt_context *cxt)
  * @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().
+ * Note that the @fstype has to be a FS type. For patterns with
+ * comma-separated list of filesystems or for the "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);
 }
 
@@ -562,7 +829,7 @@ int mnt_context_set_fstype(struct libmnt_context *cxt, const char *fstype)
  * mnt_context_get_fstype:
  * @cxt: mount context
  *
- * Returns: returns pointer or NULL in case of error pr if not set.
+ * Returns: pointer or NULL in case of error or if not set.
  */
 const char *mnt_context_get_fstype(struct libmnt_context *cxt)
 {
@@ -593,6 +860,24 @@ 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(), the mount options string
+ * may also include options set by mnt_context_set_mflags() or other 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
@@ -648,15 +933,19 @@ int mnt_context_set_options_pattern(struct libmnt_context *cxt, const char *patt
  * @cxt: mount context
  * @tb: fstab
  *
- * The mount context reads /etc/fstab to the the private struct libmnt_table by default.
+ * The mount context reads /etc/fstab to 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().
+ * instance.
+ *
+ * This function modify the @tb reference counter. This function does not set
+ * the cache for the @tb. You have to explicitly call mnt_table_set_cache(tb,
+ * mnt_context_get_cache(cxt));
  *
  * 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.)
+ * share the fstab between more mount contexts (TODO: test it.)
  *
- * If the @tb argument is NULL then the current private fstab instance is
- * reseted.
+ * If the @tb argument is NULL, then the current private fstab instance is
+ * reset.
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -664,10 +953,10 @@ 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);
+       mnt_ref_table(tb);              /* new */
+       mnt_unref_table(cxt->fstab);    /* old */
+
        cxt->fstab = tb;
        return 0;
 }
@@ -683,11 +972,8 @@ int mnt_context_set_fstab(struct libmnt_context *cxt, struct libmnt_table *tb)
  */
 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;
 
@@ -696,18 +982,12 @@ int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb)
                        return -ENOMEM;
                if (cxt->table_errcb)
                        mnt_table_set_parser_errcb(cxt->fstab, cxt->table_errcb);
-               cxt->flags &= ~MNT_FL_EXTERN_FSTAB;
+               mnt_table_set_cache(cxt->fstab, mnt_context_get_cache(cxt));
                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;
@@ -719,37 +999,115 @@ int mnt_context_get_fstab(struct libmnt_context *cxt, struct libmnt_table **tb)
  * @tb: returns mtab
  *
  * See also mnt_table_parse_mtab() for more details about mtab/mountinfo. The
- * result will deallocated by mnt_free_context(@cxt).
+ * result will be 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;
 
+               context_init_paths(cxt, 0);
+
                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 (cxt->table_fltrcb)
+                       mnt_table_set_parser_fltrcb(cxt->mtab,
+                                       cxt->table_fltrcb,
+                                       cxt->table_fltrcb_data);
+
+               mnt_table_set_cache(cxt->mtab, mnt_context_get_cache(cxt));
+               if (cxt->utab)
+                       /* utab already parsed, don't parse it again */
+                       rc = __mnt_table_parse_mtab(cxt->mtab,
+                                                   cxt->mtab_path, cxt->utab);
+               else
+                       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;
+
+       DBG(CXT, ul_debugobj(cxt, "mtab requested [nents=%d]",
+                               mnt_table_get_nents(cxt->mtab)));
+       return 0;
+}
+
+/*
+ * Called by mtab parser to filter out entries, non-zero means that
+ * an entry has to be filtered out.
+ */
+static int mtab_filter(struct libmnt_fs *fs, void *data)
+{
+       if (!fs || !data)
+               return 0;
+       if (mnt_fs_streq_target(fs, data))
+               return 0;
+       if (mnt_fs_streq_srcpath(fs, data))
+               return 0;
+       return 1;
+}
+
+/*
+ * The same like mnt_context_get_mtab(), but does not read all mountinfo/mtab
+ * file, but only entries relevant for @tgt.
+ */
+int mnt_context_get_mtab_for_target(struct libmnt_context *cxt,
+                                   struct libmnt_table **mtab,
+                                   const char *tgt)
+{
+       struct stat st;
+       struct libmnt_cache *cache = NULL;
+       char *cn_tgt = NULL;
+       int rc;
+
+       if (stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) {
+               cache = mnt_context_get_cache(cxt);
+               cn_tgt = mnt_resolve_path(tgt, cache);
+               if (cn_tgt)
+                       mnt_context_set_tabfilter(cxt, mtab_filter, cn_tgt);
+       }
+
+       rc = mnt_context_get_mtab(cxt, mtab);
+
+       if (cn_tgt) {
+               mnt_context_set_tabfilter(cxt, NULL, NULL);
+               if (!cache)
+                       free(cn_tgt);
+       }
+
+       return rc;
+}
+
+/*
+ * Allows to specify a filter for tab file entries. The filter is called by
+ * the table parser. Currently used for mtab and utab only.
+ */
+int mnt_context_set_tabfilter(struct libmnt_context *cxt,
+                             int (*fltr)(struct libmnt_fs *, void *),
+                             void *data)
+{
+       if (!cxt)
+               return -EINVAL;
+
+       cxt->table_fltrcb = fltr;
+       cxt->table_fltrcb_data = data;
+
+       if (cxt->mtab)
+               mnt_table_set_parser_fltrcb(cxt->mtab,
+                               cxt->table_fltrcb,
+                               cxt->table_fltrcb_data);
+
+       DBG(CXT, ul_debugobj(cxt, "tabfilter %s", fltr ? "ENABLED!" : "disabled"));
        return 0;
 }
 
@@ -763,7 +1121,7 @@ int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb)
  * 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
+ * It's strongly recommended to use the 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.
@@ -775,7 +1133,6 @@ int mnt_context_get_mtab(struct libmnt_context *cxt, struct libmnt_table **tb)
 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)
@@ -790,14 +1147,11 @@ int mnt_context_get_table(struct libmnt_context *cxt,
 
        rc = mnt_table_parse_file(*tb, filename);
        if (rc) {
-               mnt_free_table(*tb);
+               mnt_unref_table(*tb);
                return rc;
        }
 
-       cache = mnt_context_get_cache(cxt);
-       if (cache)
-               mnt_table_set_cache(*tb, cache);
-
+       mnt_table_set_cache(*tb, mnt_context_get_cache(cxt));
        return 0;
 }
 
@@ -821,6 +1175,11 @@ int mnt_context_set_tables_errcb(struct libmnt_context *cxt,
        if (!cxt)
                return -EINVAL;
 
+       if (cxt->mtab)
+               mnt_table_set_parser_errcb(cxt->mtab, cb);
+       if (cxt->fstab)
+               mnt_table_set_parser_errcb(cxt->fstab, cb);
+
        cxt->table_errcb = cb;
        return 0;
 }
@@ -830,12 +1189,15 @@ int mnt_context_set_tables_errcb(struct libmnt_context *cxt,
  * @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().
+ * The mount context maintains a private struct libmnt_cache by default. This
+ * function allows to overwrite the private cache with an external instance.
+ * This function increments cache reference counter.
  *
- * If the @cache argument is NULL then the current private cache instance is
- * reseted.
+ * If the @cache argument is NULL, then the current cache instance is reset.
+ * This function apply the cache to fstab and mtab instances (if already
+ * exists).
+ *
+ * The old cache instance reference counter is de-incremented.
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -843,11 +1205,17 @@ 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);
+       mnt_ref_cache(cache);                   /* new */
+       mnt_unref_cache(cxt->cache);            /* old */
+
        cxt->cache = cache;
+
+       if (cxt->mtab)
+               mnt_table_set_cache(cxt->mtab, cache);
+       if (cxt->fstab)
+               mnt_table_set_cache(cxt->fstab, cache);
+
        return 0;
 }
 
@@ -861,18 +1229,39 @@ int mnt_context_set_cache(struct libmnt_context *cxt, struct libmnt_cache *cache
  */
 struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt)
 {
-       if (!cxt || (cxt->flags & MNT_FL_NOCANONICALIZE))
+       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;
+               struct libmnt_cache *cache = mnt_new_cache();
+               mnt_context_set_cache(cxt, cache);
+               mnt_unref_cache(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). This
+ * function is deprecated (encrypted loops are no longer supported).
+ *
+ * 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
@@ -883,7 +1272,7 @@ struct libmnt_cache *mnt_context_get_cache(struct libmnt_context *cxt)
  *
  * 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:
+ * is unacceptable, then use:
  *
  *     lc = mnt_context_get_lock(cxt);
  *     if (lc)
@@ -900,12 +1289,12 @@ struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt)
         * the lock. The mnt_update_* functions are able to allocate the lock
         * only when mtab/utab update is really necessary.
         */
-       if (!cxt || (cxt->flags & MNT_FL_NOMTAB))
+       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);
+               cxt->lock = mnt_new_lock(
+                               mnt_context_get_writable_tabpath(cxt), 0);
                if (cxt->lock)
                        mnt_lock_block_signals(cxt->lock, TRUE);
        }
@@ -928,7 +1317,7 @@ struct libmnt_lock *mnt_context_get_lock(struct libmnt_context *cxt)
  *
  *     mnt_context_set_options(cxt, "noexec,nosuid");
  *
- * these both calls have the same effect.
+ * both of these calls have the same effect.
  *
  * Returns: 0 on success, negative number in case of error.
  */
@@ -936,7 +1325,18 @@ 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;
 }
 
@@ -946,13 +1346,15 @@ int mnt_context_set_mflags(struct libmnt_context *cxt, unsigned long flags)
  * @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()).
+ * the 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;
+       struct list_head *p;
+
        if (!cxt || !flags)
                return -EINVAL;
 
@@ -963,6 +1365,14 @@ int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags)
                        rc = mnt_optstr_get_flags(o, flags,
                                    mnt_get_builtin_optmap(MNT_LINUX_MAP));
        }
+
+       list_for_each(p, &cxt->addmounts) {
+               struct libmnt_addmount *ad =
+                               list_entry(p, struct libmnt_addmount, mounts);
+
+               *flags |= ad->mountflags;
+       }
+
        if (!rc)
                *flags |= cxt->mountflags;
        return rc;
@@ -975,12 +1385,13 @@ int mnt_context_get_mflags(struct libmnt_context *cxt, unsigned long *flags)
  *
  * Sets userspace mount flags.
  *
- * See also notest for mnt_context_set_mflags().
+ * See also notes 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)
 {
+       assert(cxt);
        if (!cxt)
                return -EINVAL;
        cxt->user_mountflags = flags;
@@ -993,13 +1404,14 @@ int mnt_context_set_user_mflags(struct libmnt_context *cxt, unsigned long flags)
  * @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()).
+ * with the 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;
 
@@ -1024,7 +1436,7 @@ int mnt_context_get_user_mflags(struct libmnt_context *cxt, unsigned long *flags
  * 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
+ * The libmount does not deallocate the data by mnt_free_context(). Note that
  * NULL is also valid mount data.
  *
  * Returns: 0 on success, negative number in case of error.
@@ -1055,17 +1467,21 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
        if (!cxt || !cxt->fs)
                return -EINVAL;
 
-       DBG(CXT, mnt_debug_h(cxt, "preparing source path"));
+       DBG(CXT, ul_debugobj(cxt, "preparing source path"));
 
        src = mnt_fs_get_source(cxt->fs);
 
+       if (!src && mnt_context_propagation_only(cxt))
+               /* 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)
+        * where the source is quasi-path (//foo/bar)
         */
-       if (!src || (cxt->fs->flags & MNT_FS_NET))
+       if (!src || mnt_fs_is_netfs(cxt->fs))
                return 0;
 
-       DBG(CXT, mnt_debug_h(cxt, "srcpath '%s'", src));
+       DBG(CXT, ul_debugobj(cxt, "srcpath '%s'", src));
 
        cache = mnt_context_get_cache(cxt);
 
@@ -1076,9 +1492,9 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
                if (cache)
                        path = mnt_resolve_tag(t, v, cache);
 
-               rc = path ? mnt_fs_set_source(cxt->fs, path) : -EINVAL;
+               rc = path ? mnt_fs_set_source(cxt->fs, path) : -MNT_ERR_NOSOURCE;
 
-       } else if (cache) {
+       } else if (cache && !mnt_fs_is_pseudofs(cxt->fs)) {
                /*
                 * Source is PATH (canonicalize)
                 */
@@ -1088,16 +1504,16 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
         }
 
        if (rc) {
-               DBG(CXT, mnt_debug_h(cxt, "failed to prepare srcpath [rc=%d]", rc));
+               DBG(CXT, ul_debugobj(cxt, "failed to prepare srcpath [rc=%d]", rc));
                return rc;
        }
 
        if (!path)
                path = src;
 
-       if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) ||
-           (cxt->fs->flags & MNT_FS_PSEUDO)) {
-               DBG(CXT, mnt_debug_h(cxt, "PROPAGATION/pseudo FS source: %s", path));
+       if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_REMOUNT))
+           || mnt_fs_is_pseudofs(cxt->fs)) {
+               DBG(CXT, ul_debugobj(cxt, "REMOUNT/BIND/MOVE/pseudo FS source: %s", path));
                return rc;
        }
 
@@ -1110,11 +1526,52 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
                        return rc;
        }
 
-       DBG(CXT, mnt_debug_h(cxt, "final srcpath '%s'",
+       DBG(CXT, ul_debugobj(cxt, "final srcpath '%s'",
                                mnt_fs_get_source(cxt->fs)));
        return 0;
 }
 
+/* create a mountpoint if x-mount.mkdir[=<mode>] specified */
+static int mkdir_target(const char *tgt, struct libmnt_fs *fs)
+{
+       char *mstr = NULL;
+       size_t mstr_sz = 0;
+       mode_t mode = 0;
+       struct stat st;
+       int rc;
+
+       assert(tgt);
+       assert(fs);
+
+       if (mnt_optstr_get_option(fs->user_optstr, "x-mount.mkdir", &mstr, &mstr_sz) != 0)
+               return 0;
+       if (stat(tgt, &st) == 0)
+               return 0;
+
+       if (mstr && mstr_sz) {
+               char *end = NULL;
+
+               errno = 0;
+               mode = strtol(mstr, &end, 8);
+
+               if (errno || !end || mstr + mstr_sz != end) {
+                       DBG(CXT, ul_debug("failed to parse mkdir mode '%s'", mstr));
+                       return -MNT_ERR_MOUNTOPT;
+               }
+       }
+
+       if (!mode)
+               mode = S_IRWXU |                        /* 0755 */
+                      S_IRGRP | S_IXGRP |
+                      S_IROTH | S_IXOTH;
+
+       rc = mkdir_p(tgt, mode);
+       if (rc)
+               DBG(CXT, ul_debug("mkdir %s failed: %m", tgt));
+
+       return rc;
+}
+
 int mnt_context_prepare_target(struct libmnt_context *cxt)
 {
        const char *tgt;
@@ -1128,41 +1585,93 @@ int mnt_context_prepare_target(struct libmnt_context *cxt)
        if (!cxt || !cxt->fs)
                return -EINVAL;
 
-       DBG(CXT, mnt_debug_h(cxt, "preparing target path"));
+       DBG(CXT, ul_debugobj(cxt, "preparing target path"));
 
        tgt = mnt_fs_get_target(cxt->fs);
        if (!tgt)
                return 0;
 
+       /* mkdir target */
+       if (cxt->action == MNT_ACT_MOUNT
+           && !mnt_context_is_restricted(cxt)
+           && cxt->user_mountflags & MNT_MS_XCOMMENT) {
+
+               rc = mkdir_target(tgt, cxt->fs);
+               if (rc)
+                       return rc;      /* mkdir or parse error */
+       }
+
+       /* canonicalize the path */
        cache = mnt_context_get_cache(cxt);
        if (cache) {
                char *path = mnt_resolve_path(tgt, cache);
-               if (strcmp(path, tgt))
+               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"));
+               DBG(CXT, ul_debugobj(cxt, "failed to prepare target '%s'", tgt));
        else
-               DBG(CXT, mnt_debug_h(cxt, "final target '%s'",
+               DBG(CXT, ul_debugobj(cxt, "final target '%s'",
                                        mnt_fs_get_target(cxt->fs)));
        return 0;
 }
 
+/* Guess type, but not set to cxt->fs, always use free() for the result. It's
+ * no error when we're not able to guess a filesystem type. Note that error
+ * does not mean that result in @type is NULL.
+ */
+int mnt_context_guess_srcpath_fstype(struct libmnt_context *cxt, char **type)
+{
+       int rc = 0;
+       const char *dev = mnt_fs_get_srcpath(cxt->fs);
+
+       *type = NULL;
+
+       if (!dev)
+               goto done;
+
+       if (access(dev, F_OK) == 0) {
+               struct libmnt_cache *cache = mnt_context_get_cache(cxt);
+               int ambi = 0;
+
+               *type = mnt_get_fstype(dev, &ambi, cache);
+               if (cache && *type)
+                       *type = strdup(*type);
+               if (ambi)
+                       rc = -MNT_ERR_AMBIFS;
+       } else {
+               DBG(CXT, ul_debugobj(cxt, "access(%s) failed [%m]", dev));
+               if (strchr(dev, ':') != NULL)
+                       *type = strdup("nfs");
+               else if (!strncmp(dev, "//", 2))
+                       *type = strdup("cifs");
+       }
+
+done:
+       return rc;
+}
+
+/*
+ * It's usually no error when we're not able to detect the filesystem type -- we
+ * will try to use the types from /{etc,proc}/filesystems.
+ */
 int mnt_context_guess_fstype(struct libmnt_context *cxt)
 {
        char *type;
-       const char *dev;
-       int rc = -EINVAL;
+       int rc = 0;
 
        assert(cxt);
        assert(cxt->fs);
        assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
 
+       DBG(CXT, ul_debugobj(cxt, "preparing fstype"));
+
        if (!cxt || !cxt->fs)
                return -EINVAL;
 
-       if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION))
+       if ((cxt->mountflags & (MS_BIND | MS_MOVE))
+           || mnt_context_propagation_only(cxt))
                goto none;
 
        type = (char *) mnt_fs_get_fstype(cxt->fs);
@@ -1173,41 +1682,22 @@ int mnt_context_guess_fstype(struct libmnt_context *cxt)
 
        if (type)
                goto done;
-       if (cxt->flags & MS_REMOUNT)
+       if (cxt->mountflags & MS_REMOUNT)
                goto none;
        if (cxt->fstype_pattern)
                goto done;
 
-       dev = mnt_fs_get_srcpath(cxt->fs);
-       if (!dev)
-               goto err;
-
-       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");
-       }
-       if (rc)
-               goto err;
+       rc = mnt_context_guess_srcpath_fstype(cxt, &type);
+       if (rc == 0 && type)
+               __mnt_fs_set_fstype_ptr(cxt->fs, type);
+       else
+               free(type);
 done:
-       DBG(CXT, mnt_debug_h(cxt, "FS type: %s",
-                               mnt_fs_get_fstype(cxt->fs)));
-       return 0;
+       DBG(CXT, ul_debugobj(cxt, "FS type: %s [rc=%d]",
+                               mnt_fs_get_fstype(cxt->fs), rc));
+       return rc;
 none:
        return mnt_fs_set_fstype(cxt->fs, "none");
-err:
-       DBG(CXT, mnt_debug_h(cxt, "failed to detect FS type"));
-       return rc;
 }
 
 /*
@@ -1230,8 +1720,14 @@ int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name,
        if (!type)
                type = mnt_fs_get_fstype(cxt->fs);
 
-       if ((cxt->flags & MNT_FL_NOHELPERS) || !type ||
-           !strcmp(type, "none") || (cxt->fs->flags & MNT_FS_SWAP))
+       if (type && strchr(type, ','))
+               return 0;                       /* type is fstype pattern */
+
+       if (mnt_context_is_nohelpers(cxt)
+           || !type
+           || !strcmp(type, "none")
+           || strstr(type, "/..")              /* don't try to smuggle path */
+           || mnt_fs_is_swaparea(cxt->fs))
                return 0;
 
        path = strtok_r(search_path, ":", &p);
@@ -1244,23 +1740,24 @@ int mnt_context_prepare_helper(struct libmnt_context *cxt, const char *name,
                                                path, name, type);
                path = strtok_r(NULL, ":", &p);
 
-               if (rc >= sizeof(helper) || rc < 0)
+               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 */
-                       *strrchr(helper, '.') = '\0';
+                       char *hs = strrchr(helper, '.');
+                       if (hs)
+                               *hs = '\0';
                        rc = stat(helper, &st);
                }
 
-               DBG(CXT, mnt_debug_h(cxt, "%-25s ... %s", helper,
+               DBG(CXT, ul_debugobj(cxt, "%-25s ... %s", helper,
                                        rc ? "not found" : "found"));
                if (rc)
                        continue;
 
-               if (cxt->helper)
-                       free(cxt->helper);
+               free(cxt->helper);
                cxt->helper = strdup(helper);
                if (!cxt->helper)
                        return -ENOMEM;
@@ -1277,25 +1774,20 @@ int mnt_context_merge_mflags(struct libmnt_context *cxt)
 
        assert(cxt);
 
-       DBG(CXT, mnt_debug_h(cxt, "merging mount flags"));
+       DBG(CXT, ul_debugobj(cxt, "merging mount flags"));
 
        rc = mnt_context_get_mflags(cxt, &fl);
        if (rc)
                return rc;
        cxt->mountflags = fl;
 
-       /* TODO: if cxt->fs->fs_optstr contains 'ro' then set the MS_RDONLY to
-        * mount flags, it's possible that superblock is read-only, but VFS is
-        * read-write.
-        */
-
        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",
+       DBG(CXT, ul_debugobj(cxt, "final flags: VFS=%08lx user=%08lx",
                        cxt->mountflags, cxt->user_mountflags));
 
        cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED;
@@ -1315,51 +1807,55 @@ int mnt_context_prepare_update(struct libmnt_context *cxt)
        assert(cxt->action);
        assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
 
-       DBG(CXT, mnt_debug_h(cxt, "prepare update"));
+       DBG(CXT, ul_debugobj(cxt, "prepare update"));
 
-       if (cxt->mountflags & MS_PROPAGATION) {
-               DBG(CXT, mnt_debug_h(cxt, "skip update: MS_PROPAGATION"));
+       if (mnt_context_propagation_only(cxt)) {
+               DBG(CXT, ul_debugobj(cxt, "skip update: only 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 */
-               cxt->flags |= MNT_FL_NOMTAB;
-
-       if (cxt->flags & MNT_FL_NOMTAB) {
-               DBG(CXT, mnt_debug_h(cxt, "skip update: NOMTAB flag"));
-               return 0;
+       if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/")) {
+               DBG(CXT, ul_debugobj(cxt, "root umount: setting NOMTAB"));
+               mnt_context_disable_mtab(cxt, TRUE);
        }
-       if (cxt->helper) {
-               DBG(CXT, mnt_debug_h(cxt, "skip update: external helper"));
+       if (mnt_context_is_nomtab(cxt)) {
+               DBG(CXT, ul_debugobj(cxt, "skip update: NOMTAB flag"));
                return 0;
        }
-       if (!cxt->mtab_writable && !cxt->utab_writable) {
-               DBG(CXT, mnt_debug_h(cxt, "skip update: no writable destination"));
+       if (!mnt_context_get_writable_tabpath(cxt)) {
+               DBG(CXT, ul_debugobj(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,
+               DBG(CXT, ul_debugobj(cxt,
                                "skip update: syscall failed [status=%d]",
                                cxt->syscall_status));
                return 0;
        }
+
        if (!cxt->update) {
+               const char *name = mnt_context_get_writable_tabpath(cxt);
+
+               if (cxt->action == MNT_ACT_UMOUNT && is_file_empty(name)) {
+                       DBG(CXT, ul_debugobj(cxt,
+                               "skip update: umount, no table"));
+                       return 0;
+               }
+
                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);
+               mnt_update_set_filename(cxt->update, name,
+                               !mnt_context_mtab_writable(cxt));
        }
 
        if (cxt->action == MNT_ACT_UMOUNT)
                rc = mnt_update_set_fs(cxt->update, cxt->mountflags,
-                                       mnt_fs_get_target(cxt->fs), NULL);
+                                       mnt_context_get_target(cxt), NULL);
        else
                rc = mnt_update_set_fs(cxt->update, cxt->mountflags,
                                        NULL, cxt->fs);
@@ -1373,20 +1869,34 @@ int mnt_context_update_tabs(struct libmnt_context *cxt)
 
        assert(cxt);
 
-       if (cxt->flags & MNT_FL_NOMTAB) {
-               DBG(CXT, mnt_debug_h(cxt, "don't update: NOMTAB flag"));
+       if (mnt_context_is_nomtab(cxt)) {
+               DBG(CXT, ul_debugobj(cxt, "don't update: NOMTAB flag"));
                return 0;
        }
-       if (cxt->helper) {
-               DBG(CXT, mnt_debug_h(cxt, "don't update: external helper"));
+       if (!cxt->update || !mnt_update_is_ready(cxt->update)) {
+               DBG(CXT, ul_debugobj(cxt, "don't update: no update prepared"));
                return 0;
        }
-       if (!cxt->update || !mnt_update_is_ready(cxt->update)) {
-               DBG(CXT, mnt_debug_h(cxt, "don't update: no update prepared"));
+
+       /* check utab update when external helper executed */
+       if (mnt_context_helper_executed(cxt)
+           && mnt_context_get_helper_status(cxt) == 0
+           && mnt_context_utab_writable(cxt)) {
+
+               if (mnt_update_already_done(cxt->update, cxt->lock)) {
+                       DBG(CXT, ul_debugobj(cxt, "don't update: error evaluate or already updated"));
+                       return 0;
+               }
+       } else if (cxt->helper) {
+               DBG(CXT, ul_debugobj(cxt, "don't update: external helper"));
                return 0;
        }
-       if (cxt->syscall_status) {
-               DBG(CXT, mnt_debug_h(cxt, "don't update: syscall failed/not called"));
+
+       if (cxt->syscall_status != 0
+           && !(mnt_context_helper_executed(cxt) &&
+                mnt_context_get_helper_status(cxt) == 0)) {
+
+               DBG(CXT, ul_debugobj(cxt, "don't update: syscall/helper failed/not called"));
                return 0;
        }
 
@@ -1425,14 +1935,14 @@ static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb,
                else if (tgt)
                        fs = mnt_table_find_target(tb, tgt, direction);
 
-               if (!fs) {
+               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, ...).
+                        * 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);
@@ -1442,9 +1952,9 @@ static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb,
        }
 
        if (!fs)
-               return -EINVAL;
+               return -MNT_ERR_NOFSTAB;        /* not found */
 
-       DBG(CXT, mnt_debug_h(cxt, "apply entry:"));
+       DBG(CXT, ul_debugobj(cxt, "apply entry:"));
        DBG(CXT, mnt_fs_print_debug(fs, stderr));
 
        /* copy from tab to our FS description
@@ -1456,6 +1966,9 @@ static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb,
        if (!rc && !mnt_fs_get_fstype(cxt->fs))
                rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs));
 
+       if (!rc && !mnt_fs_get_root(cxt->fs) && mnt_fs_get_root(fs))
+               rc = mnt_fs_set_root(cxt->fs, mnt_fs_get_root(fs));
+
        if (rc)
                return rc;
 
@@ -1485,70 +1998,198 @@ static int apply_table(struct libmnt_context *cxt, struct libmnt_table *tb,
  */
 int mnt_context_apply_fstab(struct libmnt_context *cxt)
 {
-       int rc = -1;
+       int rc = -1, isremount = 0;
        struct libmnt_table *tab = NULL;
        const char *src = NULL, *tgt = NULL;
+       unsigned long mflags = 0;
 
-       assert(cxt);
-       assert(cxt->fs);
-
-       if (!cxt)
+       if (!cxt || !cxt->fs)
                return -EINVAL;
 
-       if (cxt->flags & MNT_FL_TAB_APPLIED)
+       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"));
+               DBG(CXT, ul_debugobj(cxt, "force fstab usage for non-root users!"));
                cxt->optsmode = MNT_OMODE_USER;
-
-       } else if (cxt->optsmode == 0)
+       } else if (cxt->optsmode == 0) {
+               DBG(CXT, ul_debugobj(cxt, "use default optsmode"));
                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 (mnt_context_get_mflags(cxt, &mflags) == 0 && mflags & MS_REMOUNT)
+               isremount = 1;
 
        if (cxt->fs) {
                src = mnt_fs_get_source(cxt->fs);
                tgt = mnt_fs_get_target(cxt->fs);
        }
 
+       DBG(CXT, ul_debugobj(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"));
+               DBG(CXT, ul_debugobj(cxt, "fstab not required -- skip"));
                return 0;
        }
 
-       DBG(CXT, mnt_debug_h(cxt,
-               "trying to apply fstab (src=%s, target=%s)", src, tgt));
+       if (!src && tgt
+           && !(cxt->optsmode & MNT_OMODE_FSTAB)
+           && !(cxt->optsmode & MNT_OMODE_MTAB)) {
+               DBG(CXT, ul_debugobj(cxt, "only target; fstab/mtab not required "
+                                         "-- skip, probably MS_PROPAGATION"));
+               return 0;
+       }
 
        /* let's initialize cxt->fs */
-       mnt_context_get_fs(cxt);
+       ignore_result( mnt_context_get_fs(cxt) );
 
        /* try fstab */
        if (cxt->optsmode & MNT_OMODE_FSTAB) {
+               DBG(CXT, ul_debugobj(cxt, "trying to apply fstab (src=%s, target=%s)", src, tgt));
                rc = mnt_context_get_fstab(cxt, &tab);
                if (!rc)
                        rc = apply_table(cxt, tab, MNT_ITER_FORWARD);
        }
 
        /* try mtab */
-       if (rc == -1 && (cxt->optsmode & MNT_OMODE_MTAB)) {
-               rc = mnt_context_get_mtab(cxt, &tab);
+       if (rc < 0 && (cxt->optsmode & MNT_OMODE_MTAB)
+           && (isremount || cxt->action == MNT_ACT_UMOUNT)) {
+               DBG(CXT, ul_debugobj(cxt, "trying to apply mtab (src=%s, target=%s)", src, tgt));
+               if (tgt)
+                       rc = mnt_context_get_mtab_for_target(cxt, &tab, tgt);
+               else
+                       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 found entry in fstab/mtab"));
+       if (rc) {
+               if (!mnt_context_is_restricted(cxt)
+                   && tgt && !src
+                   && isremount) {
+                       DBG(CXT, ul_debugobj(cxt, "only target; ignore missing mtab entry on remount"));
+                       return 0;
+               }
+
+               DBG(CXT, ul_debugobj(cxt, "failed to find entry in fstab/mtab [rc=%d]: %m", rc));
+
+               /* force to "not found in fstab/mtab" error, the details why
+                * not found are not so important and may be misinterpreted by
+                * applications... */
+               rc = -MNT_ERR_NOFSTAB;
+       }
        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->flags & MNT_FL_TAB_APPLIED;
+}
+
+/*
+ * This is not a public function!
+ *
+ * Returns 1 if *only propagation flags* change is requested.
+ */
+int mnt_context_propagation_only(struct libmnt_context *cxt)
+{
+       if (cxt->action != MNT_ACT_MOUNT)
+               return 0;
+
+       /* has to be called after context_mount.c: fix_opts() */
+       assert((cxt->flags & MNT_FL_MOUNTOPTS_FIXED));
+
+       /* all propagation mounts are in cxt->addmount */
+       return !list_empty(&cxt->addmounts)
+              && (cxt->mountflags == 0 || cxt->mountflags == MS_SILENT)
+              && cxt->fs
+              && (!cxt->fs->fstype || strcmp(cxt->fs->fstype, "none") == 0)
+              && (!cxt->fs->source || strcmp(cxt->fs->source, "none") == 0);
+}
+
 /**
  * mnt_context_get_status:
  * @cxt: mount context
  *
- * Returns: 1 if /sbin/mount.type or mount(2) syscall was successfull.
+ * 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 informs
+ * 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);
+       return !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;
 }
 
 /**
@@ -1556,11 +2197,10 @@ int mnt_context_get_status(struct libmnt_context *cxt)
  * @cxt: mount context
  * @status: mount(2) status
  *
- * The @status should be 0 on succcess, or negative number on error (-1 or
- * -errno).
+ * The @status should be 0 on success, or negative number on error (-errno).
  *
- * This function should be used if [u]mount(2) syscall was NOT called by
- * libmount (by mnt_context_mount() or mnt_context_do_mount()) only.
+ * This function should only be used if the [u]mount(2) syscall is NOT called by
+ * libmount code.
  *
  * Returns: 0 or negative number in case of error.
  */
@@ -1569,7 +2209,7 @@ 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));
+       DBG(CXT, ul_debugobj(cxt, "syscall status set to: %d", status));
        cxt->syscall_status = status;
        return 0;
 }
@@ -1580,9 +2220,13 @@ int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status)
  * @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, char *buf, size_t bufsiz)
+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;
@@ -1592,29 +2236,34 @@ int mnt_context_strerror(struct libmnt_context *cxt, char *buf, size_t bufsiz)
  * mnt_context_init_helper
  * @cxt: mount context
  * @action: MNT_ACT_{UMOUNT,MOUNT}
- * @flags: not used
+ * @flags: not used now
  *
- * This function infors libmount that used from [u]mount.type helper.
+ * This function informs 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
+ * mount.type helper from your helper, then 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)
+int mnt_context_init_helper(struct libmnt_context *cxt, int action,
+                           int flags __attribute__((__unused__)))
 {
-       int rc = mnt_context_disable_helpers(cxt, TRUE);
+       int rc;
 
+       if (!cxt)
+               return -EINVAL;
+
+       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));
+       DBG(CXT, ul_debugobj(cxt, "initialized for [u]mount.<type> helper [rc=%d]", rc));
        return rc;
 }
 
@@ -1624,7 +2273,7 @@ int mnt_context_init_helper(struct libmnt_context *cxt, int action, int flags)
  * @c: getopt() result
  * @arg: getopt() optarg
  *
- * This function applies [u]mount.type command line option (for example parsed
+ * This function applies the [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.
  *
@@ -1649,6 +2298,8 @@ int mnt_context_helper_setopt(struct libmnt_context *cxt, int c, char *arg)
  * @fs: filesystem
  * @mounted: returns 1 for mounted and 0 for non-mounted filesystems
  *
+ * Please, read the 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,
@@ -1668,6 +2319,103 @@ int mnt_context_is_fs_mounted(struct libmnt_context *cxt,
        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, ul_debugobj(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;
+
+       assert(cxt);
+       if (!mnt_context_is_parent(cxt))
+               return -EINVAL;
+
+       DBG(CXT, ul_debugobj(cxt, "forking context"));
+
+       DBG_FLUSH;
+
+       pid = fork();
+
+       switch (pid) {
+       case -1: /* error */
+               DBG(CXT, ul_debugobj(cxt, "fork failed %m"));
+               return -errno;
+
+       case 0: /* child */
+               cxt->pid = getpid();
+               mnt_context_enable_fork(cxt, FALSE);
+               DBG(CXT, ul_debugobj(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, ul_debugobj(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;
@@ -1710,7 +2458,7 @@ int test_mount(struct libmnt_test *ts, int argc, char *argv[])
                mnt_context_set_target(cxt, argv[idx++]);
        }
 
-       /* this is unnecessary -- libmount is able to internaly
+       /* this is unnecessary! -- libmount is able to internally
         * create and manage the lock
         */
        lock = mnt_context_get_lock(cxt);
@@ -1719,10 +2467,11 @@ int test_mount(struct libmnt_test *ts, int argc, char *argv[])
 
        rc = mnt_context_mount(cxt);
        if (rc)
-               printf("failed to mount %s\n", strerror(errno));
+               warn("failed to mount");
        else
                printf("successfully mounted\n");
 
+       lock = NULL;    /* because we use atexit lock_fallback */
        mnt_free_context(cxt);
        return rc;
 }
@@ -1777,6 +2526,7 @@ int test_umount(struct libmnt_test *ts, int argc, char *argv[])
        else
                printf("successfully umounted\n");
 err:
+       lock = NULL;    /* because we use atexit lock_fallback */
        mnt_free_context(cxt);
        return rc;
 }
@@ -1833,11 +2583,11 @@ int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
                return -ENOMEM;
 
        if (argc > 2) {
-               if (!strcmp(argv[idx], "-O")) {
+               if (argv[idx] && !strcmp(argv[idx], "-O")) {
                        mnt_context_set_options_pattern(cxt, argv[idx + 1]);
                        idx += 2;
                }
-               if (!strcmp(argv[idx], "-t")) {
+               if (argv[idx] && !strcmp(argv[idx], "-t")) {
                        mnt_context_set_fstype_pattern(cxt, argv[idx + 1]);
                        idx += 2;
                }