[Devel] [PATCH RHEL7 COMMIT] nsfs: add ioctl to get an owning user namespace for ns file descriptor
Konstantin Khorenko
khorenko at virtuozzo.com
Thu Jun 11 19:20:11 MSK 2020
The commit is pushed to "branch-rh7-3.10.0-1127.10.1.vz7.162.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1127.10.1.vz7.162.2
------>
commit ef1f58de40d39ffbdac68ae7dd92afadf2450400
Author: Andrey Vagin <avagin at openvz.org>
Date: Thu Jun 11 19:20:11 2020 +0300
nsfs: add ioctl to get an owning user namespace for ns file descriptor
Each namespace has an owning user namespace and now there is not way
to discover these relationships.
Understending namespaces relationships allows to answer the question:
what capability does process X have to perform operations on a resource
governed by namespace Y?
After a long discussion, Eric W. Biederman proposed to use ioctl-s for
this purpose.
The NS_GET_USERNS ioctl returns a file descriptor to an owning user
namespace.
It returns EPERM if a target namespace is outside of a current user
namespace.
v2: rename parent to relative
v3: Add a missing mntput when returning -EAGAIN --EWB
Acked-by: Serge Hallyn <serge at hallyn.com>
Link: https://lkml.org/lkml/2016/7/6/158
Signed-off-by: Andrei Vagin <avagin at openvz.org>
Signed-off-by: Eric W. Biederman <ebiederm at xmission.com>
(cherry picked from VZ8 commit 6786741dbf99e44fb0c0ed85a37582b8a26f1c3b)
https://jira.sw.ru/browse/PSBM-102357
Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
=====================
Patchset description:
port nsfs from vz8
We have problems with /proc/pid/ns/name bind-mounts in CRIU
1) Currently (without nsfs) such a bind mount have same superblock with
/proc mount, but in case of nested pid-namespaces container can have
multiple different /proc mounts and for ns-bind-mount we need to bind it
from the right pidns. So we will need to enter proper pid-namespace to
reopen ns-file fd from proper proc, it looks too complex.
If we port nsfs ns-bind-mounts will be all on the same superblock which
does not depend from procfs's we opened the ns-file on.
2) Bigger problem will come then we will wan't to migrate ns-bind-mounts
from non-nsfs to nsfs (vz8) kernel this would bring a lot of crutches,
we will need to workaround the fact that before migration mounts were
with same superblock and after migration they can't be.
To overcome those we can port nsfs to vz7 and do ns-bind-mount support in
a new world of nsfs, looks like it would be easier.
First we need to revert all patches which depend from nsfs:
8782a0069f1b proc: add a proc_show_path method to fix mountinfo
b823f8df2fcb ms/tun: Add ioctl() TUNGETDEVNETNS cmd to allow obtaining real net ns of tun device
302889fa2e3d ms/net: add an ioctl to get a socket network namespace
7cb9e7ae7041 ms/tun: Add ioctl() SIOCGSKNS cmd to allow obtaining net ns of tun device
ac08c64138ac nsfs: add ioctl to get a parent namespace
a8e0dd94d5cd nsfs: add ioctl to get an owning user namespace for ns file descriptor
93dca538d184 kernel: add a helper to get an owning user namespace for a namespace
edaecdb8adac ms/pidns: expose task pid_ns_for_children to userspace
2b151c3f8909 ms/ns: allow ns_entries to have custom symlink content
Cherry-pick nsfs from VZ8:
435d5f4bb2cc common object embedded into various struct ....ns
58be28256d98 make mntns ->get()/->put()/->install()/->inum() work with &mnt_ns->ns
ff24870f46d5 netns: switch ->get()/->put()/->install()/->inum() to working with &net->ns
3c0411846118 switch the rest of proc_ns_operations to working with &...->ns
64964528b24e make proc_ns_operations work with struct ns_common * instead of void *
6344c433a452 new helpers: ns_alloc_inum/ns_free_inum
33c429405a2c copy address of proc_ns_ops into ns_common
f77c80142e1a bury struct proc_ns in fs/proc
292662014509 dcache.c: call ->d_prune() regardless of d_unhashed()
e149ed2b805f take the targets of /proc/*/ns/* symlinks to separate fs
Cherry-pick part of reverted patches back from VZ8:
bcac25a58bfc kernel: add a helper to get an owning user namespace for a namespace
6786741dbf99 nsfs: add ioctl to get an owning user namespace for ns file descriptor
a7306ed8d94a nsfs: add ioctl to get a parent namespace
c62cce2caee5 net: add an ioctl to get a socket network namespace
25b14e92af1a ns: allow ns_entries to have custom symlink content
eaa0d190bfe1 pidns: expose task pid_ns_for_children to userspace
Cherry-pick reverted patches back from MS (we also need them to vz8):
75509fd88fbd nsfs: Add a show_path method to fix mountinfo
24dce0800baa net: Export open_related_ns()
d8d211a2a0c3 net: Make extern and export get_net_ns()
f2780d6d7475 tun: Add ioctl() SIOCGSKNS cmd to allow obtaining net ns of tun device
0c3e0e3bb623 tun: Add ioctl() TUNGETDEVNETNS cmd to allow obtaining real net ns of tun device
073c516ff735 nsfs: mark dentry with DCACHE_RCUACCESS
On this kernel I've runed zdtm, so the change should not break interfaces.
https://jira.sw.ru/browse/PSBM-102357
Al Viro (10):
ms: common object embedded into various struct ....ns
make mntns ->get()/->put()/->install()/->inum() work with &mnt_ns->ns
netns: switch ->get()/->put()/->install()/->inum() to working with
&net->ns
switch the rest of proc_ns_operations to working with &...->ns
make proc_ns_operations work with struct ns_common * instead of void *
new helpers: ns_alloc_inum/ns_free_inum
copy address of proc_ns_ops into ns_common
bury struct proc_ns in fs/proc
dcache.c: call ->d_prune() regardless of d_unhashed()
take the targets of /proc/*/ns/* symlinks to separate fs
Andrey Vagin (4):
kernel: add a helper to get an owning user namespace for a namespace
nsfs: add ioctl to get an owning user namespace for ns file descriptor
nsfs: add ioctl to get a parent namespace
net: add an ioctl to get a socket network namespace
Cong Wang (1):
nsfs: mark dentry with DCACHE_RCUACCESS
Eric W. Biederman (1):
nsfs: Add a show_path method to fix mountinfo
Kirill Tkhai (6):
ns: allow ns_entries to have custom symlink content
pidns: expose task pid_ns_for_children to userspace
net: Export open_related_ns()
net: Make extern and export get_net_ns()
tun: Add ioctl() SIOCGSKNS cmd to allow obtaining net ns of tun device
tun: Add ioctl() TUNGETDEVNETNS cmd to allow obtaining real net ns of
tun device
Pavel Tikhomirov (10):
Revert "proc: add a proc_show_path method to fix mountinfo"
Revert "ms/tun: Add ioctl() TUNGETDEVNETNS cmd to allow obtaining real
net ns of tun device"
Revert "ms/net: add an ioctl to get a socket network namespace"
Revert "ms/tun: Add ioctl() SIOCGSKNS cmd to allow obtaining net ns of
tun device"
Revert "nsfs: add ioctl to get a parent namespace"
Revert "nsfs: add ioctl to get an owning user namespace for ns file
descriptor"
Revert "kernel: add a helper to get an owning user namespace for a
namespace"
Revert "ms/pidns: expose task pid_ns_for_children to userspace"
Revert "ms/ns: allow ns_entries to have custom symlink content"
userns: move EXPORT_SYMBOL closer to current_in_userns
---
fs/nsfs.c | 96 ++++++++++++++++++++++++++++++++++++++++-------
include/uapi/linux/nsfs.h | 11 ++++++
2 files changed, 94 insertions(+), 13 deletions(-)
diff --git a/fs/nsfs.c b/fs/nsfs.c
index cb8323fac27fc..c5ddecea66bc8 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -4,11 +4,16 @@
#include <linux/proc_ns.h>
#include <linux/magic.h>
#include <linux/ktime.h>
+#include <linux/user_namespace.h>
+#include <linux/nsfs.h>
static struct vfsmount *nsfs_mnt;
+static long ns_ioctl(struct file *filp, unsigned int ioctl,
+ unsigned long arg);
static const struct file_operations ns_file_operations = {
.llseek = no_llseek,
+ .unlocked_ioctl = ns_ioctl,
};
static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
@@ -49,22 +54,14 @@ static void nsfs_evict(struct inode *inode)
ns->ops->put(ns);
}
-void *ns_get_path(struct path *path, struct task_struct *task,
- const struct proc_ns_operations *ns_ops)
+static void *__ns_get_path(struct path *path, struct ns_common *ns)
{
struct vfsmount *mnt = mntget(nsfs_mnt);
struct qstr qname = { .name = "", };
struct dentry *dentry;
struct inode *inode;
- struct ns_common *ns;
unsigned long d;
-again:
- ns = ns_ops->get(task);
- if (!ns) {
- mntput(mnt);
- return ERR_PTR(-ENOENT);
- }
rcu_read_lock();
d = atomic_long_read(&ns->stashed);
if (!d)
@@ -73,7 +70,7 @@ void *ns_get_path(struct path *path, struct task_struct *task,
if (!lockref_get_not_dead(&dentry->d_lockref))
goto slow;
rcu_read_unlock();
- ns_ops->put(ns);
+ ns->ops->put(ns);
got_it:
path->mnt = mnt;
path->dentry = dentry;
@@ -82,7 +79,7 @@ void *ns_get_path(struct path *path, struct task_struct *task,
rcu_read_unlock();
inode = new_inode_pseudo(mnt->mnt_sb);
if (!inode) {
- ns_ops->put(ns);
+ ns->ops->put(ns);
mntput(mnt);
return ERR_PTR(-ENOMEM);
}
@@ -100,17 +97,90 @@ void *ns_get_path(struct path *path, struct task_struct *task,
return ERR_PTR(-ENOMEM);
}
d_instantiate(dentry, inode);
- dentry->d_fsdata = (void *)ns_ops;
+ dentry->d_fsdata = (void *)ns->ops;
d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
if (d) {
d_delete(dentry); /* make sure ->d_prune() does nothing */
dput(dentry);
+ mntput(mnt);
cpu_relax();
- goto again;
+ return ERR_PTR(-EAGAIN);
}
goto got_it;
}
+void *ns_get_path(struct path *path, struct task_struct *task,
+ const struct proc_ns_operations *ns_ops)
+{
+ struct ns_common *ns;
+ void *ret;
+
+again:
+ ns = ns_ops->get(task);
+ if (!ns)
+ return ERR_PTR(-ENOENT);
+
+ ret = __ns_get_path(path, ns);
+ if (IS_ERR(ret) && PTR_ERR(ret) == -EAGAIN)
+ goto again;
+ return ret;
+}
+
+static int open_related_ns(struct ns_common *ns,
+ struct ns_common *(*get_ns)(struct ns_common *ns))
+{
+ struct path path = {};
+ struct file *f;
+ void *err;
+ int fd;
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ while (1) {
+ struct ns_common *relative;
+
+ relative = get_ns(ns);
+ if (IS_ERR(relative)) {
+ put_unused_fd(fd);
+ return PTR_ERR(relative);
+ }
+
+ err = __ns_get_path(&path, relative);
+ if (IS_ERR(err) && PTR_ERR(err) == -EAGAIN)
+ continue;
+ break;
+ }
+ if (IS_ERR(err)) {
+ put_unused_fd(fd);
+ return PTR_ERR(err);
+ }
+
+ f = dentry_open(&path, O_RDONLY, current_cred());
+ path_put(&path);
+ if (IS_ERR(f)) {
+ put_unused_fd(fd);
+ fd = PTR_ERR(f);
+ } else
+ fd_install(fd, f);
+
+ return fd;
+}
+
+static long ns_ioctl(struct file *filp, unsigned int ioctl,
+ unsigned long arg)
+{
+ struct ns_common *ns = get_proc_ns(file_inode(filp));
+
+ switch (ioctl) {
+ case NS_GET_USERNS:
+ return open_related_ns(ns, ns_get_owner);
+ default:
+ return -ENOTTY;
+ }
+}
+
int ns_get_name(char *buf, size_t size, struct task_struct *task,
const struct proc_ns_operations *ns_ops)
{
diff --git a/include/uapi/linux/nsfs.h b/include/uapi/linux/nsfs.h
new file mode 100644
index 0000000000000..5cacd5c1b5d78
--- /dev/null
+++ b/include/uapi/linux/nsfs.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_NSFS_H
+#define __LINUX_NSFS_H
+
+#include <linux/ioctl.h>
+
+#define NSIO 0xb7
+
+/* Returns a file descriptor that refers to an owning user namespace */
+#define NS_GET_USERNS _IO(NSIO, 0x1)
+
+#endif /* __LINUX_NSFS_H */
More information about the Devel
mailing list