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 | ||
bf056bfa EB |
45 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) |
46 | { | |
47 | struct inode *inode = dentry->d_inode; | |
0bb80f24 | 48 | const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns.ns_ops; |
bf056bfa EB |
49 | |
50 | return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]", | |
51 | ns_ops->name, inode->i_ino); | |
52 | } | |
53 | ||
54 | const struct dentry_operations ns_dentry_operations = | |
55 | { | |
b26d4cd3 | 56 | .d_delete = always_delete_dentry, |
bf056bfa EB |
57 | .d_dname = ns_dname, |
58 | }; | |
59 | ||
60 | static struct dentry *proc_ns_get_dentry(struct super_block *sb, | |
61 | struct task_struct *task, const struct proc_ns_operations *ns_ops) | |
62 | { | |
63 | struct dentry *dentry, *result; | |
64 | struct inode *inode; | |
65 | struct proc_inode *ei; | |
66 | struct qstr qname = { .name = "", }; | |
67 | void *ns; | |
68 | ||
69 | ns = ns_ops->get(task); | |
70 | if (!ns) | |
71 | return ERR_PTR(-ENOENT); | |
72 | ||
73 | dentry = d_alloc_pseudo(sb, &qname); | |
74 | if (!dentry) { | |
75 | ns_ops->put(ns); | |
76 | return ERR_PTR(-ENOMEM); | |
77 | } | |
78 | ||
98f842e6 | 79 | inode = iget_locked(sb, ns_ops->inum(ns)); |
bf056bfa EB |
80 | if (!inode) { |
81 | dput(dentry); | |
82 | ns_ops->put(ns); | |
83 | return ERR_PTR(-ENOMEM); | |
84 | } | |
85 | ||
86 | ei = PROC_I(inode); | |
98f842e6 EB |
87 | if (inode->i_state & I_NEW) { |
88 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | |
89 | inode->i_op = &ns_inode_operations; | |
90 | inode->i_mode = S_IFREG | S_IRUGO; | |
91 | inode->i_fop = &ns_file_operations; | |
0bb80f24 DH |
92 | ei->ns.ns_ops = ns_ops; |
93 | ei->ns.ns = ns; | |
98f842e6 EB |
94 | unlock_new_inode(inode); |
95 | } else { | |
96 | ns_ops->put(ns); | |
97 | } | |
bf056bfa EB |
98 | |
99 | d_set_d_op(dentry, &ns_dentry_operations); | |
100 | result = d_instantiate_unique(dentry, inode); | |
101 | if (result) { | |
102 | dput(dentry); | |
103 | dentry = result; | |
104 | } | |
105 | ||
106 | return dentry; | |
107 | } | |
108 | ||
109 | static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) | |
110 | { | |
111 | struct inode *inode = dentry->d_inode; | |
112 | struct super_block *sb = inode->i_sb; | |
113 | struct proc_inode *ei = PROC_I(inode); | |
114 | struct task_struct *task; | |
db04dc67 | 115 | struct path ns_path; |
bf056bfa EB |
116 | void *error = ERR_PTR(-EACCES); |
117 | ||
118 | task = get_proc_task(inode); | |
119 | if (!task) | |
120 | goto out; | |
121 | ||
122 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | |
123 | goto out_put_task; | |
124 | ||
0bb80f24 | 125 | ns_path.dentry = proc_ns_get_dentry(sb, task, ei->ns.ns_ops); |
db04dc67 EB |
126 | if (IS_ERR(ns_path.dentry)) { |
127 | error = ERR_CAST(ns_path.dentry); | |
bf056bfa EB |
128 | goto out_put_task; |
129 | } | |
130 | ||
db04dc67 EB |
131 | ns_path.mnt = mntget(nd->path.mnt); |
132 | nd_jump_link(nd, &ns_path); | |
bf056bfa EB |
133 | error = NULL; |
134 | ||
135 | out_put_task: | |
136 | put_task_struct(task); | |
137 | out: | |
138 | return error; | |
139 | } | |
140 | ||
141 | static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | |
142 | { | |
143 | struct inode *inode = dentry->d_inode; | |
144 | struct proc_inode *ei = PROC_I(inode); | |
0bb80f24 | 145 | const struct proc_ns_operations *ns_ops = ei->ns.ns_ops; |
bf056bfa EB |
146 | struct task_struct *task; |
147 | void *ns; | |
148 | char name[50]; | |
5d826c84 | 149 | int res = -EACCES; |
bf056bfa EB |
150 | |
151 | task = get_proc_task(inode); | |
152 | if (!task) | |
153 | goto out; | |
154 | ||
155 | if (!ptrace_may_access(task, PTRACE_MODE_READ)) | |
156 | goto out_put_task; | |
157 | ||
5d826c84 | 158 | res = -ENOENT; |
bf056bfa EB |
159 | ns = ns_ops->get(task); |
160 | if (!ns) | |
161 | goto out_put_task; | |
162 | ||
98f842e6 | 163 | snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns_ops->inum(ns)); |
5d826c84 | 164 | res = readlink_copy(buffer, buflen, name); |
bf056bfa EB |
165 | ns_ops->put(ns); |
166 | out_put_task: | |
167 | put_task_struct(task); | |
168 | out: | |
5d826c84 | 169 | return res; |
bf056bfa EB |
170 | } |
171 | ||
172 | static const struct inode_operations proc_ns_link_inode_operations = { | |
173 | .readlink = proc_ns_readlink, | |
174 | .follow_link = proc_ns_follow_link, | |
175 | .setattr = proc_setattr, | |
176 | }; | |
177 | ||
c52a47ac | 178 | static int proc_ns_instantiate(struct inode *dir, |
6b4e306a EB |
179 | struct dentry *dentry, struct task_struct *task, const void *ptr) |
180 | { | |
181 | const struct proc_ns_operations *ns_ops = ptr; | |
182 | struct inode *inode; | |
183 | struct proc_inode *ei; | |
6b4e306a EB |
184 | |
185 | inode = proc_pid_make_inode(dir->i_sb, task); | |
186 | if (!inode) | |
187 | goto out; | |
188 | ||
189 | ei = PROC_I(inode); | |
bf056bfa EB |
190 | inode->i_mode = S_IFLNK|S_IRWXUGO; |
191 | inode->i_op = &proc_ns_link_inode_operations; | |
0bb80f24 | 192 | ei->ns.ns_ops = ns_ops; |
6b4e306a | 193 | |
1b26c9b3 | 194 | d_set_d_op(dentry, &pid_dentry_operations); |
6b4e306a EB |
195 | d_add(dentry, inode); |
196 | /* Close the race of the process dying before we return the dentry */ | |
0b728e19 | 197 | if (pid_revalidate(dentry, 0)) |
c52a47ac | 198 | return 0; |
6b4e306a | 199 | out: |
c52a47ac | 200 | return -ENOENT; |
6b4e306a EB |
201 | } |
202 | ||
f0c3b509 | 203 | static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) |
6b4e306a | 204 | { |
f0c3b509 | 205 | struct task_struct *task = get_proc_task(file_inode(file)); |
6b4e306a | 206 | const struct proc_ns_operations **entry, **last; |
6b4e306a | 207 | |
6b4e306a | 208 | if (!task) |
f0c3b509 | 209 | return -ENOENT; |
6b4e306a | 210 | |
f0c3b509 AV |
211 | if (!dir_emit_dots(file, ctx)) |
212 | goto out; | |
213 | if (ctx->pos >= 2 + ARRAY_SIZE(ns_entries)) | |
214 | goto out; | |
215 | entry = ns_entries + (ctx->pos - 2); | |
216 | last = &ns_entries[ARRAY_SIZE(ns_entries) - 1]; | |
217 | while (entry <= last) { | |
218 | const struct proc_ns_operations *ops = *entry; | |
219 | if (!proc_fill_cache(file, ctx, ops->name, strlen(ops->name), | |
220 | proc_ns_instantiate, task, ops)) | |
221 | break; | |
222 | ctx->pos++; | |
223 | entry++; | |
224 | } | |
6b4e306a EB |
225 | out: |
226 | put_task_struct(task); | |
f0c3b509 | 227 | return 0; |
6b4e306a EB |
228 | } |
229 | ||
230 | const struct file_operations proc_ns_dir_operations = { | |
231 | .read = generic_read_dir, | |
f0c3b509 | 232 | .iterate = proc_ns_dir_readdir, |
6b4e306a EB |
233 | }; |
234 | ||
235 | static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |
00cd8dd3 | 236 | struct dentry *dentry, unsigned int flags) |
6b4e306a | 237 | { |
c52a47ac | 238 | int error; |
6b4e306a EB |
239 | struct task_struct *task = get_proc_task(dir); |
240 | const struct proc_ns_operations **entry, **last; | |
241 | unsigned int len = dentry->d_name.len; | |
242 | ||
c52a47ac | 243 | error = -ENOENT; |
6b4e306a EB |
244 | |
245 | if (!task) | |
246 | goto out_no_task; | |
247 | ||
4c619aa0 AM |
248 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; |
249 | for (entry = ns_entries; entry < last; entry++) { | |
6b4e306a EB |
250 | if (strlen((*entry)->name) != len) |
251 | continue; | |
252 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) | |
253 | break; | |
254 | } | |
4c619aa0 | 255 | if (entry == last) |
6b4e306a EB |
256 | goto out; |
257 | ||
258 | error = proc_ns_instantiate(dir, dentry, task, *entry); | |
259 | out: | |
260 | put_task_struct(task); | |
261 | out_no_task: | |
c52a47ac | 262 | return ERR_PTR(error); |
6b4e306a EB |
263 | } |
264 | ||
265 | const struct inode_operations proc_ns_dir_inode_operations = { | |
266 | .lookup = proc_ns_dir_lookup, | |
267 | .getattr = pid_getattr, | |
268 | .setattr = proc_setattr, | |
269 | }; | |
270 | ||
271 | struct file *proc_ns_fget(int fd) | |
272 | { | |
273 | struct file *file; | |
274 | ||
275 | file = fget(fd); | |
276 | if (!file) | |
277 | return ERR_PTR(-EBADF); | |
278 | ||
279 | if (file->f_op != &ns_file_operations) | |
280 | goto out_invalid; | |
281 | ||
282 | return file; | |
283 | ||
284 | out_invalid: | |
285 | fput(file); | |
286 | return ERR_PTR(-EINVAL); | |
287 | } | |
288 | ||
0bb80f24 DH |
289 | struct proc_ns *get_proc_ns(struct inode *inode) |
290 | { | |
291 | return &PROC_I(inode)->ns; | |
292 | } | |
293 | ||
8823c079 EB |
294 | bool proc_ns_inode(struct inode *inode) |
295 | { | |
296 | return inode->i_fop == &ns_file_operations; | |
297 | } |