From 4917d842ea6feb1e815f701826dadbef3feb066f Mon Sep 17 00:00:00 2001 From: Vaclav Dolezal Date: Mon, 19 Feb 2018 18:45:55 +0100 Subject: libmount: add support for switching namespaces [kzak@redhat.com: - cosmetic changes, add some comments] Signed-off-by: Vaclav Dolezal Signed-off-by: Karel Zak --- libmount/src/context.c | 202 +++++++++++++++++++++++++++++++++++++++++++++ libmount/src/libmount.h.in | 14 ++++ libmount/src/libmount.sym | 9 ++ libmount/src/mountP.h | 9 ++ 4 files changed, 234 insertions(+) (limited to 'libmount/src') diff --git a/libmount/src/context.c b/libmount/src/context.c index 7add0e39e..8928a7d96 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -34,6 +34,7 @@ #include "mountP.h" #include "fileutils.h" #include "strutils.h" +#include "namespace.h" #include @@ -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: + * + * + * 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); + * + * + * + * 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 + * + * + * mnt_context_switch_ns(cxt, mnt_context_get_origin_ns(cxt)); + * + * + * + * 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 + * + * + * mnt_context_switch_ns(cxt, mnt_context_get_target_ns(cxt)); + * + * + * + * 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 diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index e12a90f07..885729dfb 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index ca16cafa1..1db14b2b7 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -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; diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 4ce891cda..0c795121a 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -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 */ }; -- cgit v1.2.3-55-g7522