Commit | Line | Data |
---|---|---|
6b4e306a EB |
1 | #include <linux/proc_fs.h> |
2 | #include <linux/nsproxy.h> | |
3 | #include <linux/sched.h> | |
4 | #include <linux/ptrace.h> | |
5 | #include <linux/fs_struct.h> | |
6 | #include <linux/mount.h> | |
7 | #include <linux/path.h> | |
8 | #include <linux/namei.h> | |
9 | #include <linux/file.h> | |
10 | #include <linux/utsname.h> | |
11 | #include <net/net_namespace.h> | |
6b4e306a EB |
12 | #include <linux/ipc_namespace.h> |
13 | #include <linux/pid_namespace.h> | |
cde1975b | 14 | #include <linux/user_namespace.h> |
6b4e306a EB |
15 | #include "internal.h" |
16 | ||
17 | ||
18 | static const struct proc_ns_operations *ns_entries[] = { | |
13b6f576 EB |
19 | #ifdef CONFIG_NET_NS |
20 | &netns_operations, | |
21 | #endif | |
34482e89 EB |
22 | #ifdef CONFIG_UTS_NS |
23 | &utsns_operations, | |
24 | #endif | |
a00eaf11 EB |
25 | #ifdef CONFIG_IPC_NS |
26 | &ipcns_operations, | |
27 | #endif | |
57e8391d EB |
28 | #ifdef CONFIG_PID_NS |
29 | &pidns_operations, | |
cde1975b EB |
30 | #endif |
31 | #ifdef CONFIG_USER_NS | |
32 | &userns_operations, | |
57e8391d | 33 | #endif |
8823c079 | 34 | &mntns_operations, |
6b4e306a EB |
35 | }; |
36 | ||
37 | static const struct file_operations ns_file_operations = { | |
38 | .llseek = no_llseek, | |
39 | }; | |
40 | ||
bf056bfa EB |
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 = new_inode(sb); | |
86 | if (!inode) { | |
87 | dput(dentry); | |
88 | ns_ops->put(ns); | |
89 | return ERR_PTR(-ENOMEM); | |
90 | } | |
91 | ||
92 | ei = PROC_I(inode); | |
93 | inode->i_ino = get_next_ino(); | |
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 | ||
101 | d_set_d_op(dentry, &ns_dentry_operations); | |
102 | result = d_instantiate_unique(dentry, inode); | |
103 | if (result) { | |
104 | dput(dentry); | |
105 | dentry = result; | |
106 | } | |
107 | ||
108 | return dentry; | |
109 | } | |
110 | ||
111 | static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) | |
112 | { | |
113 | struct inode *inode = dentry->d_inode; | |
114 | struct super_block *sb = inode->i_sb; | |
115 | struct proc_inode *ei = PROC_I(inode); | |
116 | struct task_struct *task; | |
117 | struct dentry *ns_dentry; | |
118 | void *error = ERR_PTR(-EACCES); | |
119 | ||
120 | task = get_proc_task(inode); | |
121 | if (!task) | |
122 | goto out; | |
123 | ||
124 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | |
125 | goto out_put_task; | |
126 | ||
127 | ns_dentry = proc_ns_get_dentry(sb, task, ei->ns_ops); | |
128 | if (IS_ERR(ns_dentry)) { | |
129 | error = ERR_CAST(ns_dentry); | |
130 | goto out_put_task; | |
131 | } | |
132 | ||
133 | dput(nd->path.dentry); | |
134 | nd->path.dentry = ns_dentry; | |
135 | error = NULL; | |
136 | ||
137 | out_put_task: | |
138 | put_task_struct(task); | |
139 | out: | |
140 | return error; | |
141 | } | |
142 | ||
143 | static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | |
144 | { | |
145 | struct inode *inode = dentry->d_inode; | |
146 | struct proc_inode *ei = PROC_I(inode); | |
147 | const struct proc_ns_operations *ns_ops = ei->ns_ops; | |
148 | struct task_struct *task; | |
149 | void *ns; | |
150 | char name[50]; | |
151 | int len = -EACCES; | |
152 | ||
153 | task = get_proc_task(inode); | |
154 | if (!task) | |
155 | goto out; | |
156 | ||
157 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | |
158 | goto out_put_task; | |
159 | ||
160 | len = -ENOENT; | |
161 | ns = ns_ops->get(task); | |
162 | if (!ns) | |
163 | goto out_put_task; | |
164 | ||
165 | snprintf(name, sizeof(name), "%s", ns_ops->name); | |
166 | len = strlen(name); | |
167 | ||
168 | if (len > buflen) | |
169 | len = buflen; | |
170 | if (copy_to_user(buffer, ns_ops->name, len)) | |
171 | len = -EFAULT; | |
172 | ||
173 | ns_ops->put(ns); | |
174 | out_put_task: | |
175 | put_task_struct(task); | |
176 | out: | |
177 | return len; | |
178 | } | |
179 | ||
180 | static const struct inode_operations proc_ns_link_inode_operations = { | |
181 | .readlink = proc_ns_readlink, | |
182 | .follow_link = proc_ns_follow_link, | |
183 | .setattr = proc_setattr, | |
184 | }; | |
185 | ||
6b4e306a EB |
186 | static struct dentry *proc_ns_instantiate(struct inode *dir, |
187 | struct dentry *dentry, struct task_struct *task, const void *ptr) | |
188 | { | |
189 | const struct proc_ns_operations *ns_ops = ptr; | |
190 | struct inode *inode; | |
191 | struct proc_inode *ei; | |
192 | struct dentry *error = ERR_PTR(-ENOENT); | |
193 | ||
194 | inode = proc_pid_make_inode(dir->i_sb, task); | |
195 | if (!inode) | |
196 | goto out; | |
197 | ||
198 | ei = PROC_I(inode); | |
bf056bfa EB |
199 | inode->i_mode = S_IFLNK|S_IRWXUGO; |
200 | inode->i_op = &proc_ns_link_inode_operations; | |
201 | ei->ns_ops = ns_ops; | |
6b4e306a | 202 | |
1b26c9b3 | 203 | d_set_d_op(dentry, &pid_dentry_operations); |
6b4e306a EB |
204 | d_add(dentry, inode); |
205 | /* Close the race of the process dying before we return the dentry */ | |
0b728e19 | 206 | if (pid_revalidate(dentry, 0)) |
6b4e306a EB |
207 | error = NULL; |
208 | out: | |
209 | return error; | |
6b4e306a EB |
210 | } |
211 | ||
212 | static int proc_ns_fill_cache(struct file *filp, void *dirent, | |
213 | filldir_t filldir, struct task_struct *task, | |
214 | const struct proc_ns_operations *ops) | |
215 | { | |
216 | return proc_fill_cache(filp, dirent, filldir, | |
217 | ops->name, strlen(ops->name), | |
218 | proc_ns_instantiate, task, ops); | |
219 | } | |
220 | ||
221 | static int proc_ns_dir_readdir(struct file *filp, void *dirent, | |
222 | filldir_t filldir) | |
223 | { | |
224 | int i; | |
225 | struct dentry *dentry = filp->f_path.dentry; | |
226 | struct inode *inode = dentry->d_inode; | |
227 | struct task_struct *task = get_proc_task(inode); | |
228 | const struct proc_ns_operations **entry, **last; | |
229 | ino_t ino; | |
230 | int ret; | |
231 | ||
232 | ret = -ENOENT; | |
233 | if (!task) | |
234 | goto out_no_task; | |
235 | ||
6b4e306a EB |
236 | ret = 0; |
237 | i = filp->f_pos; | |
238 | switch (i) { | |
239 | case 0: | |
240 | ino = inode->i_ino; | |
241 | if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) | |
242 | goto out; | |
243 | i++; | |
244 | filp->f_pos++; | |
245 | /* fall through */ | |
246 | case 1: | |
247 | ino = parent_ino(dentry); | |
248 | if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) | |
249 | goto out; | |
250 | i++; | |
251 | filp->f_pos++; | |
252 | /* fall through */ | |
253 | default: | |
254 | i -= 2; | |
255 | if (i >= ARRAY_SIZE(ns_entries)) { | |
256 | ret = 1; | |
257 | goto out; | |
258 | } | |
259 | entry = ns_entries + i; | |
260 | last = &ns_entries[ARRAY_SIZE(ns_entries) - 1]; | |
261 | while (entry <= last) { | |
262 | if (proc_ns_fill_cache(filp, dirent, filldir, | |
263 | task, *entry) < 0) | |
264 | goto out; | |
265 | filp->f_pos++; | |
266 | entry++; | |
267 | } | |
268 | } | |
269 | ||
270 | ret = 1; | |
271 | out: | |
272 | put_task_struct(task); | |
273 | out_no_task: | |
274 | return ret; | |
275 | } | |
276 | ||
277 | const struct file_operations proc_ns_dir_operations = { | |
278 | .read = generic_read_dir, | |
279 | .readdir = proc_ns_dir_readdir, | |
280 | }; | |
281 | ||
282 | static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |
00cd8dd3 | 283 | struct dentry *dentry, unsigned int flags) |
6b4e306a EB |
284 | { |
285 | struct dentry *error; | |
286 | struct task_struct *task = get_proc_task(dir); | |
287 | const struct proc_ns_operations **entry, **last; | |
288 | unsigned int len = dentry->d_name.len; | |
289 | ||
290 | error = ERR_PTR(-ENOENT); | |
291 | ||
292 | if (!task) | |
293 | goto out_no_task; | |
294 | ||
4c619aa0 AM |
295 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; |
296 | for (entry = ns_entries; entry < last; entry++) { | |
6b4e306a EB |
297 | if (strlen((*entry)->name) != len) |
298 | continue; | |
299 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) | |
300 | break; | |
301 | } | |
4c619aa0 | 302 | if (entry == last) |
6b4e306a EB |
303 | goto out; |
304 | ||
305 | error = proc_ns_instantiate(dir, dentry, task, *entry); | |
306 | out: | |
307 | put_task_struct(task); | |
308 | out_no_task: | |
309 | return error; | |
310 | } | |
311 | ||
312 | const struct inode_operations proc_ns_dir_inode_operations = { | |
313 | .lookup = proc_ns_dir_lookup, | |
314 | .getattr = pid_getattr, | |
315 | .setattr = proc_setattr, | |
316 | }; | |
317 | ||
318 | struct file *proc_ns_fget(int fd) | |
319 | { | |
320 | struct file *file; | |
321 | ||
322 | file = fget(fd); | |
323 | if (!file) | |
324 | return ERR_PTR(-EBADF); | |
325 | ||
326 | if (file->f_op != &ns_file_operations) | |
327 | goto out_invalid; | |
328 | ||
329 | return file; | |
330 | ||
331 | out_invalid: | |
332 | fput(file); | |
333 | return ERR_PTR(-EINVAL); | |
334 | } | |
335 | ||
8823c079 EB |
336 | bool proc_ns_inode(struct inode *inode) |
337 | { | |
338 | return inode->i_fop == &ns_file_operations; | |
339 | } |