Quoting Andrei Vagin (avagin@xxxxxxxxxx): > From: Andrey Vagin <avagin@xxxxxxxxxx> > > 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 > > Link: https://lkml.org/lkml/2016/7/6/158 > Signed-off-by: Andrei Vagin <avagin@xxxxxxxxxx> Acked-by: Serge Hallyn <serge@xxxxxxxxxx> > --- > fs/nsfs.c | 95 ++++++++++++++++++++++++++++++++++++++++------- > include/uapi/linux/nsfs.h | 11 ++++++ > 2 files changed, 93 insertions(+), 13 deletions(-) > create mode 100644 include/uapi/linux/nsfs.h > > diff --git a/fs/nsfs.c b/fs/nsfs.c > index 8f20d60..be7d193 100644 > --- a/fs/nsfs.c > +++ b/fs/nsfs.c > @@ -5,11 +5,16 @@ > #include <linux/magic.h> > #include <linux/ktime.h> > #include <linux/seq_file.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) > @@ -44,22 +49,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) > @@ -68,7 +65,7 @@ again: > 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; > @@ -77,7 +74,7 @@ slow: > 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); > } > @@ -95,17 +92,89 @@ slow: > 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); > 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 0000000..5cacd5c > --- /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 */ > -- > 2.5.5 > > _______________________________________________ > Containers mailing list > Containers@xxxxxxxxxxxxxxxxxxxxxxxxxx > https://lists.linuxfoundation.org/mailman/listinfo/containers -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html