diff options
Diffstat (limited to 'fs/proc/namespaces.c')
-rw-r--r-- | fs/proc/namespaces.c | 185 |
1 files changed, 164 insertions, 21 deletions
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index b178ed733c36..b7a47196c8c3 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <net/net_namespace.h> | 11 | #include <net/net_namespace.h> |
12 | #include <linux/ipc_namespace.h> | 12 | #include <linux/ipc_namespace.h> |
13 | #include <linux/pid_namespace.h> | 13 | #include <linux/pid_namespace.h> |
14 | #include <linux/user_namespace.h> | ||
14 | #include "internal.h" | 15 | #include "internal.h" |
15 | 16 | ||
16 | 17 | ||
@@ -24,12 +25,168 @@ static const struct proc_ns_operations *ns_entries[] = { | |||
24 | #ifdef CONFIG_IPC_NS | 25 | #ifdef CONFIG_IPC_NS |
25 | &ipcns_operations, | 26 | &ipcns_operations, |
26 | #endif | 27 | #endif |
28 | #ifdef CONFIG_PID_NS | ||
29 | &pidns_operations, | ||
30 | #endif | ||
31 | #ifdef CONFIG_USER_NS | ||
32 | &userns_operations, | ||
33 | #endif | ||
34 | &mntns_operations, | ||
27 | }; | 35 | }; |
28 | 36 | ||
29 | static const struct file_operations ns_file_operations = { | 37 | static const struct file_operations ns_file_operations = { |
30 | .llseek = no_llseek, | 38 | .llseek = no_llseek, |
31 | }; | 39 | }; |
32 | 40 | ||
41 | static const struct inode_operations ns_inode_operations = { | ||
42 | .setattr = proc_setattr, | ||
43 | }; | ||
44 | |||
45 | static int ns_delete_dentry(const struct dentry *dentry) | ||
46 | { | ||
47 | /* Don't cache namespace inodes when not in use */ | ||
48 | return 1; | ||
49 | } | ||
50 | |||
51 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | ||
52 | { | ||
53 | struct inode *inode = dentry->d_inode; | ||
54 | const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; | ||
55 | |||
56 | return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]", | ||
57 | ns_ops->name, inode->i_ino); | ||
58 | } | ||
59 | |||
60 | const struct dentry_operations ns_dentry_operations = | ||
61 | { | ||
62 | .d_delete = ns_delete_dentry, | ||
63 | .d_dname = ns_dname, | ||
64 | }; | ||
65 | |||
66 | static struct dentry *proc_ns_get_dentry(struct super_block *sb, | ||
67 | struct task_struct *task, const struct proc_ns_operations *ns_ops) | ||
68 | { | ||
69 | struct dentry *dentry, *result; | ||
70 | struct inode *inode; | ||
71 | struct proc_inode *ei; | ||
72 | struct qstr qname = { .name = "", }; | ||
73 | void *ns; | ||
74 | |||
75 | ns = ns_ops->get(task); | ||
76 | if (!ns) | ||
77 | return ERR_PTR(-ENOENT); | ||
78 | |||
79 | dentry = d_alloc_pseudo(sb, &qname); | ||
80 | if (!dentry) { | ||
81 | ns_ops->put(ns); | ||
82 | return ERR_PTR(-ENOMEM); | ||
83 | } | ||
84 | |||
85 | inode = iget_locked(sb, ns_ops->inum(ns)); | ||
86 | if (!inode) { | ||
87 | dput(dentry); | ||
88 | ns_ops->put(ns); | ||
89 | return ERR_PTR(-ENOMEM); | ||
90 | } | ||
91 | |||
92 | ei = PROC_I(inode); | ||
93 | if (inode->i_state & I_NEW) { | ||
94 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||
95 | inode->i_op = &ns_inode_operations; | ||
96 | inode->i_mode = S_IFREG | S_IRUGO; | ||
97 | inode->i_fop = &ns_file_operations; | ||
98 | ei->ns_ops = ns_ops; | ||
99 | ei->ns = ns; | ||
100 | unlock_new_inode(inode); | ||
101 | } else { | ||
102 | ns_ops->put(ns); | ||
103 | } | ||
104 | |||
105 | d_set_d_op(dentry, &ns_dentry_operations); | ||
106 | result = d_instantiate_unique(dentry, inode); | ||
107 | if (result) { | ||
108 | dput(dentry); | ||
109 | dentry = result; | ||
110 | } | ||
111 | |||
112 | return dentry; | ||
113 | } | ||
114 | |||
115 | static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
116 | { | ||
117 | struct inode *inode = dentry->d_inode; | ||
118 | struct super_block *sb = inode->i_sb; | ||
119 | struct proc_inode *ei = PROC_I(inode); | ||
120 | struct task_struct *task; | ||
121 | struct dentry *ns_dentry; | ||
122 | void *error = ERR_PTR(-EACCES); | ||
123 | |||
124 | task = get_proc_task(inode); | ||
125 | if (!task) | ||
126 | goto out; | ||
127 | |||
128 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
129 | goto out_put_task; | ||
130 | |||
131 | ns_dentry = proc_ns_get_dentry(sb, task, ei->ns_ops); | ||
132 | if (IS_ERR(ns_dentry)) { | ||
133 | error = ERR_CAST(ns_dentry); | ||
134 | goto out_put_task; | ||
135 | } | ||
136 | |||
137 | dput(nd->path.dentry); | ||
138 | nd->path.dentry = ns_dentry; | ||
139 | error = NULL; | ||
140 | |||
141 | out_put_task: | ||
142 | put_task_struct(task); | ||
143 | out: | ||
144 | return error; | ||
145 | } | ||
146 | |||
147 | static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | ||
148 | { | ||
149 | struct inode *inode = dentry->d_inode; | ||
150 | struct proc_inode *ei = PROC_I(inode); | ||
151 | const struct proc_ns_operations *ns_ops = ei->ns_ops; | ||
152 | struct task_struct *task; | ||
153 | void *ns; | ||
154 | char name[50]; | ||
155 | int len = -EACCES; | ||
156 | |||
157 | task = get_proc_task(inode); | ||
158 | if (!task) | ||
159 | goto out; | ||
160 | |||
161 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
162 | goto out_put_task; | ||
163 | |||
164 | len = -ENOENT; | ||
165 | ns = ns_ops->get(task); | ||
166 | if (!ns) | ||
167 | goto out_put_task; | ||
168 | |||
169 | snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns_ops->inum(ns)); | ||
170 | len = strlen(name); | ||
171 | |||
172 | if (len > buflen) | ||
173 | len = buflen; | ||
174 | if (copy_to_user(buffer, name, len)) | ||
175 | len = -EFAULT; | ||
176 | |||
177 | ns_ops->put(ns); | ||
178 | out_put_task: | ||
179 | put_task_struct(task); | ||
180 | out: | ||
181 | return len; | ||
182 | } | ||
183 | |||
184 | static const struct inode_operations proc_ns_link_inode_operations = { | ||
185 | .readlink = proc_ns_readlink, | ||
186 | .follow_link = proc_ns_follow_link, | ||
187 | .setattr = proc_setattr, | ||
188 | }; | ||
189 | |||
33 | static struct dentry *proc_ns_instantiate(struct inode *dir, | 190 | static struct dentry *proc_ns_instantiate(struct inode *dir, |
34 | struct dentry *dentry, struct task_struct *task, const void *ptr) | 191 | struct dentry *dentry, struct task_struct *task, const void *ptr) |
35 | { | 192 | { |
@@ -37,21 +194,15 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, | |||
37 | struct inode *inode; | 194 | struct inode *inode; |
38 | struct proc_inode *ei; | 195 | struct proc_inode *ei; |
39 | struct dentry *error = ERR_PTR(-ENOENT); | 196 | struct dentry *error = ERR_PTR(-ENOENT); |
40 | void *ns; | ||
41 | 197 | ||
42 | inode = proc_pid_make_inode(dir->i_sb, task); | 198 | inode = proc_pid_make_inode(dir->i_sb, task); |
43 | if (!inode) | 199 | if (!inode) |
44 | goto out; | 200 | goto out; |
45 | 201 | ||
46 | ns = ns_ops->get(task); | ||
47 | if (!ns) | ||
48 | goto out_iput; | ||
49 | |||
50 | ei = PROC_I(inode); | 202 | ei = PROC_I(inode); |
51 | inode->i_mode = S_IFREG|S_IRUSR; | 203 | inode->i_mode = S_IFLNK|S_IRWXUGO; |
52 | inode->i_fop = &ns_file_operations; | 204 | inode->i_op = &proc_ns_link_inode_operations; |
53 | ei->ns_ops = ns_ops; | 205 | ei->ns_ops = ns_ops; |
54 | ei->ns = ns; | ||
55 | 206 | ||
56 | d_set_d_op(dentry, &pid_dentry_operations); | 207 | d_set_d_op(dentry, &pid_dentry_operations); |
57 | d_add(dentry, inode); | 208 | d_add(dentry, inode); |
@@ -60,9 +211,6 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, | |||
60 | error = NULL; | 211 | error = NULL; |
61 | out: | 212 | out: |
62 | return error; | 213 | return error; |
63 | out_iput: | ||
64 | iput(inode); | ||
65 | goto out; | ||
66 | } | 214 | } |
67 | 215 | ||
68 | static int proc_ns_fill_cache(struct file *filp, void *dirent, | 216 | static int proc_ns_fill_cache(struct file *filp, void *dirent, |
@@ -89,10 +237,6 @@ static int proc_ns_dir_readdir(struct file *filp, void *dirent, | |||
89 | if (!task) | 237 | if (!task) |
90 | goto out_no_task; | 238 | goto out_no_task; |
91 | 239 | ||
92 | ret = -EPERM; | ||
93 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
94 | goto out; | ||
95 | |||
96 | ret = 0; | 240 | ret = 0; |
97 | i = filp->f_pos; | 241 | i = filp->f_pos; |
98 | switch (i) { | 242 | switch (i) { |
@@ -152,10 +296,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |||
152 | if (!task) | 296 | if (!task) |
153 | goto out_no_task; | 297 | goto out_no_task; |
154 | 298 | ||
155 | error = ERR_PTR(-EPERM); | ||
156 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | ||
157 | goto out; | ||
158 | |||
159 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; | 299 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; |
160 | for (entry = ns_entries; entry < last; entry++) { | 300 | for (entry = ns_entries; entry < last; entry++) { |
161 | if (strlen((*entry)->name) != len) | 301 | if (strlen((*entry)->name) != len) |
@@ -163,7 +303,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |||
163 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) | 303 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) |
164 | break; | 304 | break; |
165 | } | 305 | } |
166 | error = ERR_PTR(-ENOENT); | ||
167 | if (entry == last) | 306 | if (entry == last) |
168 | goto out; | 307 | goto out; |
169 | 308 | ||
@@ -198,3 +337,7 @@ out_invalid: | |||
198 | return ERR_PTR(-EINVAL); | 337 | return ERR_PTR(-EINVAL); |
199 | } | 338 | } |
200 | 339 | ||
340 | bool proc_ns_inode(struct inode *inode) | ||
341 | { | ||
342 | return inode->i_fop == &ns_file_operations; | ||
343 | } | ||