]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: add support for switching namespaces
authorVaclav Dolezal <vdolezal@redhat.com>
Mon, 19 Feb 2018 17:45:55 +0000 (18:45 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 11 Jun 2018 13:49:48 +0000 (15:49 +0200)
[kzak@redhat.com: - cosmetic changes, add some comments]

Signed-off-by: Vaclav Dolezal <vdolezal@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/docs/libmount-sections.txt
libmount/src/context.c
libmount/src/libmount.h.in
libmount/src/libmount.sym
libmount/src/mountP.h

index dea724b2f356c46f333f3a574c4be558312cae74..811726bb298cb481b8b02efbd45530d5075975ed 100644 (file)
@@ -20,6 +20,7 @@ mnt_resolve_target
 <SECTION>
 <FILE>context</FILE>
 libmnt_context
+libmnt_ns
 mnt_free_context
 mnt_new_context
 mnt_reset_context
@@ -53,11 +54,13 @@ mnt_context_get_mtab
 mnt_context_get_mtab_userdata
 mnt_context_get_options
 mnt_context_get_optsmode
+mnt_context_get_origin_ns
 mnt_context_get_source
 mnt_context_get_status
 mnt_context_get_syscall_errno
 mnt_context_get_table
 mnt_context_get_target
+mnt_context_get_target_ns
 mnt_context_get_user_mflags
 mnt_context_helper_executed
 mnt_context_helper_setopt
@@ -95,8 +98,12 @@ mnt_context_set_source
 mnt_context_set_syscall_status
 mnt_context_set_tables_errcb
 mnt_context_set_target
+mnt_context_set_target_ns
 mnt_context_set_user_mflags
 mnt_context_strerror
+mnt_context_switch_ns
+mnt_context_switch_origin_ns
+mnt_context_switch_target_ns
 mnt_context_syscall_called
 mnt_context_tab_applied
 mnt_context_wait_for_children
@@ -110,6 +117,7 @@ MNT_ERR_NOFSTYPE
 MNT_ERR_NOSOURCE
 MNT_ERR_LOOPOVERLAP
 MNT_ERR_LOCK
+MNT_ERR_NAMESPACE
 <SUBSECTION>
 MNT_EX_SUCCESS
 MNT_EX_USAGE
index 7add0e39e9c3ae3ae75ccc726f705e2930a9c26b..8928a7d96052b3dc63844b4514b2b074f0577385 100644 (file)
@@ -34,6 +34,7 @@
 #include "mountP.h"
 #include "fileutils.h"
 #include "strutils.h"
+#include "namespace.h"
 
 #include <sys/wait.h>
 
@@ -60,6 +61,10 @@ struct libmnt_context *mnt_new_context(void)
 
        cxt->loopdev_fd = -1;
 
+       cxt->ns_orig.fd = -1;
+       cxt->ns_tgt.fd = -1;
+       cxt->ns_cur = &cxt->ns_orig;
+
        /* if we're really root and aren't running setuid */
        cxt->restricted = (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
 
@@ -93,6 +98,8 @@ void mnt_free_context(struct libmnt_context *cxt)
        mnt_free_lock(cxt->lock);
        mnt_free_update(cxt->update);
 
+       mnt_context_set_target_ns(cxt, NULL);
+
        free(cxt->children);
 
        DBG(CXT, ul_debugobj(cxt, "<---- free"));
@@ -2585,6 +2592,201 @@ int mnt_context_wait_for_children(struct libmnt_context *cxt,
        return 0;
 }
 
+static void close_ns(struct libmnt_ns *ns)
+{
+       if (ns->fd == -1)
+               return;
+
+       close(ns->fd);
+       ns->fd = -1;
+
+       mnt_unref_cache(ns->cache);
+       ns->cache = NULL;
+}
+
+/**
+ * mnt_context_set_target_ns:
+ * @cxt: mount context
+ * @path: path to target namespace or NULL
+ *
+ * Sets target namespace to namespace represented by @path. If @path is NULL,
+ * target namespace is cleared.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ */
+int mnt_context_set_target_ns(struct libmnt_context *cxt, const char *path)
+{
+       int rc = 0;
+
+       int tmp;
+       if (!cxt)
+               return -EINVAL;
+
+       DBG(CXT, ul_debugobj(cxt, "Setting %s as target namespace", path));
+
+       /* cleanup only */
+       if (!path) {
+               close_ns(&cxt->ns_orig);
+               close_ns(&cxt->ns_tgt);
+               return 0;
+       }
+
+       /* open original namespace */
+       if (cxt->ns_orig.fd == -1) {
+               cxt->ns_orig.fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+               if (cxt->ns_orig.fd == -1)
+                       return -errno;
+               cxt->ns_orig.cache = NULL;
+       }
+
+       /* open target (wanted) namespace */
+       tmp = open(path, O_RDONLY | O_CLOEXEC);
+       if (tmp == -1)
+               return -errno;
+
+       /* test whether namespace switching works */
+       DBG(CXT, ul_debugobj(cxt, "Trying whether namespace is valid"));
+       if (setns(tmp, CLONE_NEWNS)
+           || setns(cxt->ns_orig.fd, CLONE_NEWNS)) {
+               rc = -errno;
+               DBG(CXT, ul_debugobj(cxt, "setns(2) failed [errno=%d %m]", errno));
+               goto err;
+       }
+
+       close_ns(&cxt->ns_tgt);
+
+       cxt->ns_tgt.fd = tmp;
+       cxt->ns_tgt.cache = NULL;
+
+       return 0;
+err:
+       close(tmp);
+       return rc;
+}
+
+/**
+ * mnt_context_get_target_ns:
+ * @cxt: mount context
+ *
+ * Returns: pointer to target namespace
+ */
+struct libmnt_ns *mnt_context_get_target_ns(struct libmnt_context *cxt)
+{
+       return &cxt->ns_tgt;
+}
+
+/**
+ * mnt_context_get_origin_ns:
+ * @cxt: mount context
+ *
+ * Returns: pointer to original namespace
+ */
+struct libmnt_ns *mnt_context_get_origin_ns(struct libmnt_context *cxt)
+{
+       return &cxt->ns_orig;
+}
+
+
+/**
+ * mnt_context_switch_ns:
+ * @cxt: mount context
+ * @ns: namespace to switch to
+ *
+ * Switch to namespace specified by ns
+ *
+ * Typical usage:
+ * <informalexample>
+ *   <programlisting>
+ *     struct libmnt_ns *ns_old;
+ *     ns_old = mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt));
+ *     ... code ...
+ *     mnt_context_switch_ns(cxt, ns_old);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Returns: pointer to previous namespace or NULL on error
+ */
+struct libmnt_ns *mnt_context_switch_ns(struct libmnt_context *cxt, struct libmnt_ns *ns)
+{
+       struct libmnt_ns *old;
+
+       if (!cxt || !ns)
+               return NULL;
+
+       old = cxt->ns_cur;
+       if (ns == old || ns->fd == -1)
+               return old;
+
+       /* remember the curremt cache */
+       if (old->cache != cxt->cache) {
+               mnt_unref_cache(old->cache);
+               old->cache = cxt->cache;
+               mnt_ref_cache(old->cache);
+       }
+
+       /* switch */
+       DBG(CXT, ul_debugobj(cxt, "Switching to %s namespace",
+               ns == mnt_context_get_target_ns(cxt) ? "target" :
+               ns == mnt_context_get_origin_ns(cxt) ? "original" : "other"));
+
+       if (setns(ns->fd, CLONE_NEWNS)) {
+               int errsv = errno;
+
+               DBG(CXT, ul_debugobj(cxt, "setns(2) failed [errno=%d %m]", errno));
+               errno = errsv;
+               return NULL;
+       }
+
+       /* update pointer to the current namespace */
+       cxt->ns_cur = ns;
+
+       /* update pointer to the cache */
+       mnt_unref_cache(cxt->cache);
+       cxt->cache = ns->cache;
+       mnt_ref_cache(cxt->cache);
+
+       return old;
+}
+
+/**
+ * mnt_context_switch_origin_ns:
+ * @cxt: mount context
+ *
+ * Switch to original namespace
+ *
+ * This is shorthand for
+ * <informalexample>
+ *   <programlisting>
+ *     mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt));
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Returns: pointer to previous namespace or NULL on error
+ */
+struct libmnt_ns *mnt_context_switch_origin_ns(struct libmnt_context *cxt)
+{
+       return mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt));
+}
+
+/**
+ * mnt_context_switch_target_ns:
+ * @cxt: mount context
+ *
+ * Switch to target namespace
+ *
+ * This is shorthand for
+ * <informalexample>
+ *   <programlisting>
+ *     mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt));
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Returns: pointer to previous namespace or NULL on error
+ */
+struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt)
+{
+       return mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt));
+}
 
 
 #ifdef TEST_PROGRAM
index e12a90f079847d9c96110ce482180ceda149a9aa..885729dfb4f4b83eed6b9e5d59ce41aac2a3ada4 100644 (file)
@@ -128,6 +128,13 @@ struct libmnt_monitor;
  */
 struct libmnt_tabdiff;
 
+/**
+ * libmnt_ns:
+ *
+ * Describes mount namespace
+ */
+struct libmnt_ns;
+
 /*
  * Actions
  */
@@ -815,6 +822,13 @@ extern int mnt_context_strerror(struct libmnt_context *cxt, char *buf,
 extern int mnt_context_get_excode(struct libmnt_context *cxt,
                         int rc, char *buf, size_t bufsz);
 
+extern int mnt_context_set_target_ns(struct libmnt_context *cxt, const char *path);
+extern struct libmnt_ns *mnt_context_get_target_ns(struct libmnt_context *cxt);
+extern struct libmnt_ns *mnt_context_get_origin_ns(struct libmnt_context *cxt);
+extern struct libmnt_ns *mnt_context_switch_ns(struct libmnt_context *cxt, struct libmnt_ns *ns);
+extern struct libmnt_ns *mnt_context_switch_origin_ns(struct libmnt_context *cxt);
+extern struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt);
+
 
 /* context_mount.c */
 extern int mnt_context_mount(struct libmnt_context *cxt);
index ca16cafa19209d7d1e7feb0926840fa1988f146f..1db14b2b796454a14d014c2904c2789d75ad4c2a 100644 (file)
@@ -322,3 +322,12 @@ MOUNT_2.30 {
        mnt_context_enable_rwonly_mount;
        mnt_context_get_excode;
 } MOUNT_2.28;
+
+MOUNT_2.33 {
+       mnt_context_get_origin_ns;
+       mnt_context_get_target_ns;
+       mnt_context_set_target_ns;
+       mnt_context_switch_ns;
+       mnt_context_switch_origin_ns;
+       mnt_context_switch_target_ns;
+} MOUNT_2.30;
index 4ce891cda20c6f175baaebee80951b3a712f8f04..0c795121afe467ce7b1b58189ef2d59b2cccbf8a 100644 (file)
@@ -268,6 +268,11 @@ struct libmnt_addmount {
        struct list_head        mounts;
 };
 
+struct libmnt_ns {
+       int fd;                         /* file descriptor of namespace, -1 when inactive */
+       struct libmnt_cache *cache;     /* paths cache associated with NS */
+};
+
 /*
  * Mount context -- high-level API
  */
@@ -329,6 +334,10 @@ struct libmnt_context
 
        int     syscall_status; /* 1: not called yet, 0: success, <0: -errno */
 
+       struct libmnt_ns        ns_orig;        /* original namespace */
+       struct libmnt_ns        ns_tgt;         /* target namespace */
+       struct libmnt_ns        *ns_cur;        /* pointer to current namespace */
+
        unsigned int    enabled_textdomain : 1;         /* bindtextdomain() called */
 };