Skip to content

Commit e149ed2

Browse files
author
Al Viro
committed
take the targets of /proc/*/ns/* symlinks to separate fs
New pseudo-filesystem: nsfs. Targets of /proc/*/ns/* live there now. It's not mountable (not even registered, so it's not in /proc/filesystems, etc.). Files on it *are* bindable - we explicitly permit that in do_loopback(). This stuff lives in fs/nsfs.c now; proc_ns_fget() moved there as well. get_proc_ns() is a macro now (it's simply returning ->i_private; would have been an inline, if not for header ordering headache). proc_ns_inode() is an ex-parrot. The interface used in procfs is ns_get_path(path, task, ops) and ns_get_name(buf, size, task, ops). Dentries and inodes are never hashed; a non-counting reference to dentry is stashed in ns_common (removed by ->d_prune()) and reused by ns_get_path() if present. See ns_get_path()/ns_prune_dentry/nsfs_evict() for details of that mechanism. As the result, proc_ns_follow_link() has stopped poking in nd->path.mnt; it does nd_jump_link() on a consistent <vfsmount,dentry> pair it gets from ns_get_path(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent f77c801 commit e149ed2

10 files changed

Lines changed: 208 additions & 161 deletions

File tree

fs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.o super.o \
1111
attr.o bad_inode.o file.o filesystems.o namespace.o \
1212
seq_file.o xattr.o libfs.o fs-writeback.o \
1313
pnode.o splice.o sync.o utimes.o \
14-
stack.o fs_struct.o statfs.o fs_pin.o
14+
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o
1515

1616
ifeq ($(CONFIG_BLOCK),y)
1717
obj-y += buffer.o block_dev.o direct-io.o mpage.o

fs/internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,8 @@ extern const struct file_operations pipefifo_fops;
147147
*/
148148
extern void sb_pin_kill(struct super_block *sb);
149149
extern void mnt_pin_kill(struct mount *m);
150+
151+
/*
152+
* fs/nsfs.c
153+
*/
154+
extern struct dentry_operations ns_dentry_operations;

fs/namespace.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,8 +1569,8 @@ SYSCALL_DEFINE1(oldumount, char __user *, name)
15691569
static bool is_mnt_ns_file(struct dentry *dentry)
15701570
{
15711571
/* Is this a proxy for a mount namespace? */
1572-
struct inode *inode = dentry->d_inode;
1573-
return proc_ns_inode(inode) && dentry->d_fsdata == &mntns_operations;
1572+
return dentry->d_op == &ns_dentry_operations &&
1573+
dentry->d_fsdata == &mntns_operations;
15741574
}
15751575

15761576
struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
@@ -2016,7 +2016,10 @@ static int do_loopback(struct path *path, const char *old_name,
20162016
if (IS_MNT_UNBINDABLE(old))
20172017
goto out2;
20182018

2019-
if (!check_mnt(parent) || !check_mnt(old))
2019+
if (!check_mnt(parent))
2020+
goto out2;
2021+
2022+
if (!check_mnt(old) && old_path.dentry->d_op != &ns_dentry_operations)
20202023
goto out2;
20212024

20222025
if (!recurse && has_locked_children(old, old_path.dentry))

fs/nsfs.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#include <linux/mount.h>
2+
#include <linux/file.h>
3+
#include <linux/fs.h>
4+
#include <linux/proc_ns.h>
5+
#include <linux/magic.h>
6+
#include <linux/ktime.h>
7+
8+
static struct vfsmount *nsfs_mnt;
9+
10+
static const struct file_operations ns_file_operations = {
11+
.llseek = no_llseek,
12+
};
13+
14+
static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
15+
{
16+
struct inode *inode = dentry->d_inode;
17+
const struct proc_ns_operations *ns_ops = dentry->d_fsdata;
18+
19+
return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]",
20+
ns_ops->name, inode->i_ino);
21+
}
22+
23+
static void ns_prune_dentry(struct dentry *dentry)
24+
{
25+
struct inode *inode = dentry->d_inode;
26+
if (inode) {
27+
struct ns_common *ns = inode->i_private;
28+
atomic_long_set(&ns->stashed, 0);
29+
}
30+
}
31+
32+
const struct dentry_operations ns_dentry_operations =
33+
{
34+
.d_prune = ns_prune_dentry,
35+
.d_delete = always_delete_dentry,
36+
.d_dname = ns_dname,
37+
};
38+
39+
static void nsfs_evict(struct inode *inode)
40+
{
41+
struct ns_common *ns = inode->i_private;
42+
clear_inode(inode);
43+
ns->ops->put(ns);
44+
}
45+
46+
void *ns_get_path(struct path *path, struct task_struct *task,
47+
const struct proc_ns_operations *ns_ops)
48+
{
49+
struct vfsmount *mnt = mntget(nsfs_mnt);
50+
struct qstr qname = { .name = "", };
51+
struct dentry *dentry;
52+
struct inode *inode;
53+
struct ns_common *ns;
54+
unsigned long d;
55+
56+
again:
57+
ns = ns_ops->get(task);
58+
if (!ns) {
59+
mntput(mnt);
60+
return ERR_PTR(-ENOENT);
61+
}
62+
rcu_read_lock();
63+
d = atomic_long_read(&ns->stashed);
64+
if (!d)
65+
goto slow;
66+
dentry = (struct dentry *)d;
67+
if (!lockref_get_not_dead(&dentry->d_lockref))
68+
goto slow;
69+
rcu_read_unlock();
70+
ns_ops->put(ns);
71+
got_it:
72+
path->mnt = mnt;
73+
path->dentry = dentry;
74+
return NULL;
75+
slow:
76+
rcu_read_unlock();
77+
inode = new_inode_pseudo(mnt->mnt_sb);
78+
if (!inode) {
79+
ns_ops->put(ns);
80+
mntput(mnt);
81+
return ERR_PTR(-ENOMEM);
82+
}
83+
inode->i_ino = ns->inum;
84+
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
85+
inode->i_flags |= S_IMMUTABLE;
86+
inode->i_mode = S_IFREG | S_IRUGO;
87+
inode->i_fop = &ns_file_operations;
88+
inode->i_private = ns;
89+
90+
dentry = d_alloc_pseudo(mnt->mnt_sb, &qname);
91+
if (!dentry) {
92+
iput(inode);
93+
mntput(mnt);
94+
return ERR_PTR(-ENOMEM);
95+
}
96+
d_instantiate(dentry, inode);
97+
dentry->d_fsdata = (void *)ns_ops;
98+
d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
99+
if (d) {
100+
d_delete(dentry); /* make sure ->d_prune() does nothing */
101+
dput(dentry);
102+
cpu_relax();
103+
goto again;
104+
}
105+
goto got_it;
106+
}
107+
108+
int ns_get_name(char *buf, size_t size, struct task_struct *task,
109+
const struct proc_ns_operations *ns_ops)
110+
{
111+
struct ns_common *ns;
112+
int res = -ENOENT;
113+
ns = ns_ops->get(task);
114+
if (ns) {
115+
res = snprintf(buf, size, "%s:[%u]", ns_ops->name, ns->inum);
116+
ns_ops->put(ns);
117+
}
118+
return res;
119+
}
120+
121+
struct file *proc_ns_fget(int fd)
122+
{
123+
struct file *file;
124+
125+
file = fget(fd);
126+
if (!file)
127+
return ERR_PTR(-EBADF);
128+
129+
if (file->f_op != &ns_file_operations)
130+
goto out_invalid;
131+
132+
return file;
133+
134+
out_invalid:
135+
fput(file);
136+
return ERR_PTR(-EINVAL);
137+
}
138+
139+
static const struct super_operations nsfs_ops = {
140+
.statfs = simple_statfs,
141+
.evict_inode = nsfs_evict,
142+
};
143+
static struct dentry *nsfs_mount(struct file_system_type *fs_type,
144+
int flags, const char *dev_name, void *data)
145+
{
146+
return mount_pseudo(fs_type, "nsfs:", &nsfs_ops,
147+
&ns_dentry_operations, NSFS_MAGIC);
148+
}
149+
static struct file_system_type nsfs = {
150+
.name = "nsfs",
151+
.mount = nsfs_mount,
152+
.kill_sb = kill_anon_super,
153+
};
154+
155+
void __init nsfs_init(void)
156+
{
157+
nsfs_mnt = kern_mount(&nsfs);
158+
if (IS_ERR(nsfs_mnt))
159+
panic("can't set nsfs up\n");
160+
nsfs_mnt->mnt_sb->s_flags &= ~MS_NOUSER;
161+
}

fs/proc/inode.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ static void proc_evict_inode(struct inode *inode)
3232
{
3333
struct proc_dir_entry *de;
3434
struct ctl_table_header *head;
35-
struct ns_common *ns;
3635

3736
truncate_inode_pages_final(&inode->i_data);
3837
clear_inode(inode);
@@ -49,10 +48,6 @@ static void proc_evict_inode(struct inode *inode)
4948
RCU_INIT_POINTER(PROC_I(inode)->sysctl, NULL);
5049
sysctl_head_put(head);
5150
}
52-
/* Release any associated namespace */
53-
ns = PROC_I(inode)->ns.ns;
54-
if (ns && ns->ops)
55-
ns->ops->put(ns);
5651
}
5752

5853
static struct kmem_cache * proc_inode_cachep;

0 commit comments

Comments
 (0)