1 | /* $NetBSD: procfs_vnops.c,v 1.194 2016/08/20 12:37:09 hannken Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Doran. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Copyright (c) 1993, 1995 |
34 | * The Regents of the University of California. All rights reserved. |
35 | * |
36 | * This code is derived from software contributed to Berkeley by |
37 | * Jan-Simon Pendry. |
38 | * |
39 | * Redistribution and use in source and binary forms, with or without |
40 | * modification, are permitted provided that the following conditions |
41 | * are met: |
42 | * 1. Redistributions of source code must retain the above copyright |
43 | * notice, this list of conditions and the following disclaimer. |
44 | * 2. Redistributions in binary form must reproduce the above copyright |
45 | * notice, this list of conditions and the following disclaimer in the |
46 | * documentation and/or other materials provided with the distribution. |
47 | * 3. Neither the name of the University nor the names of its contributors |
48 | * may be used to endorse or promote products derived from this software |
49 | * without specific prior written permission. |
50 | * |
51 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
52 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
54 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
55 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
56 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
57 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
59 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
60 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
61 | * SUCH DAMAGE. |
62 | * |
63 | * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 |
64 | */ |
65 | |
66 | /* |
67 | * Copyright (c) 1993 Jan-Simon Pendry |
68 | * |
69 | * This code is derived from software contributed to Berkeley by |
70 | * Jan-Simon Pendry. |
71 | * |
72 | * Redistribution and use in source and binary forms, with or without |
73 | * modification, are permitted provided that the following conditions |
74 | * are met: |
75 | * 1. Redistributions of source code must retain the above copyright |
76 | * notice, this list of conditions and the following disclaimer. |
77 | * 2. Redistributions in binary form must reproduce the above copyright |
78 | * notice, this list of conditions and the following disclaimer in the |
79 | * documentation and/or other materials provided with the distribution. |
80 | * 3. All advertising materials mentioning features or use of this software |
81 | * must display the following acknowledgement: |
82 | * This product includes software developed by the University of |
83 | * California, Berkeley and its contributors. |
84 | * 4. Neither the name of the University nor the names of its contributors |
85 | * may be used to endorse or promote products derived from this software |
86 | * without specific prior written permission. |
87 | * |
88 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
89 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
90 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
91 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
92 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
93 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
94 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
95 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
96 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
97 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
98 | * SUCH DAMAGE. |
99 | * |
100 | * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 |
101 | */ |
102 | |
103 | /* |
104 | * procfs vnode interface |
105 | */ |
106 | |
107 | #include <sys/cdefs.h> |
108 | __KERNEL_RCSID(0, "$NetBSD: procfs_vnops.c,v 1.194 2016/08/20 12:37:09 hannken Exp $" ); |
109 | |
110 | #include <sys/param.h> |
111 | #include <sys/systm.h> |
112 | #include <sys/time.h> |
113 | #include <sys/kernel.h> |
114 | #include <sys/file.h> |
115 | #include <sys/filedesc.h> |
116 | #include <sys/proc.h> |
117 | #include <sys/vnode.h> |
118 | #include <sys/namei.h> |
119 | #include <sys/malloc.h> |
120 | #include <sys/mount.h> |
121 | #include <sys/dirent.h> |
122 | #include <sys/resourcevar.h> |
123 | #include <sys/stat.h> |
124 | #include <sys/ptrace.h> |
125 | #include <sys/kauth.h> |
126 | |
127 | #include <uvm/uvm_extern.h> /* for PAGE_SIZE */ |
128 | |
129 | #include <machine/reg.h> |
130 | |
131 | #include <miscfs/genfs/genfs.h> |
132 | #include <miscfs/procfs/procfs.h> |
133 | |
134 | /* |
135 | * Vnode Operations. |
136 | * |
137 | */ |
138 | |
139 | static int procfs_validfile_linux(struct lwp *, struct mount *); |
140 | static int procfs_root_readdir_callback(struct proc *, void *); |
141 | static void procfs_dir(pfstype, struct lwp *, struct proc *, char **, char *, |
142 | size_t); |
143 | |
144 | /* |
145 | * This is a list of the valid names in the |
146 | * process-specific sub-directories. It is |
147 | * used in procfs_lookup and procfs_readdir |
148 | */ |
149 | static const struct proc_target { |
150 | u_char pt_type; |
151 | u_char pt_namlen; |
152 | const char *pt_name; |
153 | pfstype pt_pfstype; |
154 | int (*pt_valid)(struct lwp *, struct mount *); |
155 | } proc_targets[] = { |
156 | #define N(s) sizeof(s)-1, s |
157 | /* name type validp */ |
158 | { DT_DIR, N("." ), PFSproc, NULL }, |
159 | { DT_DIR, N(".." ), PFSroot, NULL }, |
160 | { DT_DIR, N("fd" ), PFSfd, NULL }, |
161 | { DT_REG, N("file" ), PFSfile, procfs_validfile }, |
162 | { DT_REG, N("mem" ), PFSmem, NULL }, |
163 | { DT_REG, N("regs" ), PFSregs, procfs_validregs }, |
164 | { DT_REG, N("fpregs" ), PFSfpregs, procfs_validfpregs }, |
165 | { DT_REG, N("ctl" ), PFSctl, NULL }, |
166 | { DT_REG, N("stat" ), PFSstat, procfs_validfile_linux }, |
167 | { DT_REG, N("status" ), PFSstatus, NULL }, |
168 | { DT_REG, N("note" ), PFSnote, NULL }, |
169 | { DT_REG, N("notepg" ), PFSnotepg, NULL }, |
170 | { DT_REG, N("map" ), PFSmap, procfs_validmap }, |
171 | { DT_REG, N("maps" ), PFSmaps, procfs_validmap }, |
172 | { DT_REG, N("cmdline" ), PFScmdline, NULL }, |
173 | { DT_REG, N("exe" ), PFSexe, procfs_validfile }, |
174 | { DT_LNK, N("cwd" ), PFScwd, NULL }, |
175 | { DT_LNK, N("root" ), PFSchroot, NULL }, |
176 | { DT_LNK, N("emul" ), PFSemul, NULL }, |
177 | { DT_REG, N("statm" ), PFSstatm, procfs_validfile_linux }, |
178 | { DT_DIR, N("task" ), PFStask, procfs_validfile_linux }, |
179 | #ifdef __HAVE_PROCFS_MACHDEP |
180 | PROCFS_MACHDEP_NODETYPE_DEFNS |
181 | #endif |
182 | #undef N |
183 | }; |
184 | static const int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]); |
185 | |
186 | /* |
187 | * List of files in the root directory. Note: the validate function will |
188 | * be called with p == NULL for these ones. |
189 | */ |
190 | static const struct proc_target proc_root_targets[] = { |
191 | #define N(s) sizeof(s)-1, s |
192 | /* name type validp */ |
193 | { DT_REG, N("meminfo" ), PFSmeminfo, procfs_validfile_linux }, |
194 | { DT_REG, N("cpuinfo" ), PFScpuinfo, procfs_validfile_linux }, |
195 | { DT_REG, N("uptime" ), PFSuptime, procfs_validfile_linux }, |
196 | { DT_REG, N("mounts" ), PFSmounts, procfs_validfile_linux }, |
197 | { DT_REG, N("devices" ), PFSdevices, procfs_validfile_linux }, |
198 | { DT_REG, N("stat" ), PFScpustat, procfs_validfile_linux }, |
199 | { DT_REG, N("loadavg" ), PFSloadavg, procfs_validfile_linux }, |
200 | { DT_REG, N("version" ), PFSversion, procfs_validfile_linux }, |
201 | #undef N |
202 | }; |
203 | static const int nproc_root_targets = |
204 | sizeof(proc_root_targets) / sizeof(proc_root_targets[0]); |
205 | |
206 | int procfs_lookup(void *); |
207 | #define procfs_create genfs_eopnotsupp |
208 | #define procfs_mknod genfs_eopnotsupp |
209 | int procfs_open(void *); |
210 | int procfs_close(void *); |
211 | int procfs_access(void *); |
212 | int procfs_getattr(void *); |
213 | int procfs_setattr(void *); |
214 | #define procfs_read procfs_rw |
215 | #define procfs_write procfs_rw |
216 | #define procfs_fcntl genfs_fcntl |
217 | #define procfs_ioctl genfs_enoioctl |
218 | #define procfs_poll genfs_poll |
219 | #define procfs_revoke genfs_revoke |
220 | #define procfs_fsync genfs_nullop |
221 | #define procfs_seek genfs_nullop |
222 | #define procfs_remove genfs_eopnotsupp |
223 | int procfs_link(void *); |
224 | #define procfs_rename genfs_eopnotsupp |
225 | #define procfs_mkdir genfs_eopnotsupp |
226 | #define procfs_rmdir genfs_eopnotsupp |
227 | int procfs_symlink(void *); |
228 | int procfs_readdir(void *); |
229 | int procfs_readlink(void *); |
230 | #define procfs_abortop genfs_abortop |
231 | int procfs_inactive(void *); |
232 | int procfs_reclaim(void *); |
233 | #define procfs_lock genfs_lock |
234 | #define procfs_unlock genfs_unlock |
235 | #define procfs_bmap genfs_badop |
236 | #define procfs_strategy genfs_badop |
237 | int procfs_print(void *); |
238 | int procfs_pathconf(void *); |
239 | #define procfs_islocked genfs_islocked |
240 | #define procfs_advlock genfs_einval |
241 | #define procfs_bwrite genfs_eopnotsupp |
242 | #define procfs_putpages genfs_null_putpages |
243 | |
244 | static int atoi(const char *, size_t); |
245 | |
246 | /* |
247 | * procfs vnode operations. |
248 | */ |
249 | int (**procfs_vnodeop_p)(void *); |
250 | const struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { |
251 | { &vop_default_desc, vn_default_error }, |
252 | { &vop_lookup_desc, procfs_lookup }, /* lookup */ |
253 | { &vop_create_desc, procfs_create }, /* create */ |
254 | { &vop_mknod_desc, procfs_mknod }, /* mknod */ |
255 | { &vop_open_desc, procfs_open }, /* open */ |
256 | { &vop_close_desc, procfs_close }, /* close */ |
257 | { &vop_access_desc, procfs_access }, /* access */ |
258 | { &vop_getattr_desc, procfs_getattr }, /* getattr */ |
259 | { &vop_setattr_desc, procfs_setattr }, /* setattr */ |
260 | { &vop_read_desc, procfs_read }, /* read */ |
261 | { &vop_write_desc, procfs_write }, /* write */ |
262 | { &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */ |
263 | { &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */ |
264 | { &vop_fcntl_desc, procfs_fcntl }, /* fcntl */ |
265 | { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */ |
266 | { &vop_poll_desc, procfs_poll }, /* poll */ |
267 | { &vop_revoke_desc, procfs_revoke }, /* revoke */ |
268 | { &vop_fsync_desc, procfs_fsync }, /* fsync */ |
269 | { &vop_seek_desc, procfs_seek }, /* seek */ |
270 | { &vop_remove_desc, procfs_remove }, /* remove */ |
271 | { &vop_link_desc, procfs_link }, /* link */ |
272 | { &vop_rename_desc, procfs_rename }, /* rename */ |
273 | { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */ |
274 | { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */ |
275 | { &vop_symlink_desc, procfs_symlink }, /* symlink */ |
276 | { &vop_readdir_desc, procfs_readdir }, /* readdir */ |
277 | { &vop_readlink_desc, procfs_readlink }, /* readlink */ |
278 | { &vop_abortop_desc, procfs_abortop }, /* abortop */ |
279 | { &vop_inactive_desc, procfs_inactive }, /* inactive */ |
280 | { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */ |
281 | { &vop_lock_desc, procfs_lock }, /* lock */ |
282 | { &vop_unlock_desc, procfs_unlock }, /* unlock */ |
283 | { &vop_bmap_desc, procfs_bmap }, /* bmap */ |
284 | { &vop_strategy_desc, procfs_strategy }, /* strategy */ |
285 | { &vop_print_desc, procfs_print }, /* print */ |
286 | { &vop_islocked_desc, procfs_islocked }, /* islocked */ |
287 | { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */ |
288 | { &vop_advlock_desc, procfs_advlock }, /* advlock */ |
289 | { &vop_putpages_desc, procfs_putpages }, /* putpages */ |
290 | { NULL, NULL } |
291 | }; |
292 | const struct vnodeopv_desc procfs_vnodeop_opv_desc = |
293 | { &procfs_vnodeop_p, procfs_vnodeop_entries }; |
294 | /* |
295 | * set things up for doing i/o on |
296 | * the pfsnode (vp). (vp) is locked |
297 | * on entry, and should be left locked |
298 | * on exit. |
299 | * |
300 | * for procfs we don't need to do anything |
301 | * in particular for i/o. all that is done |
302 | * is to support exclusive open on process |
303 | * memory images. |
304 | */ |
305 | int |
306 | procfs_open(void *v) |
307 | { |
308 | struct vop_open_args /* { |
309 | struct vnode *a_vp; |
310 | int a_mode; |
311 | kauth_cred_t a_cred; |
312 | } */ *ap = v; |
313 | struct pfsnode *pfs = VTOPFS(ap->a_vp); |
314 | struct lwp *l1; |
315 | struct proc *p2; |
316 | int error; |
317 | |
318 | if ((error = procfs_proc_lock(pfs->pfs_pid, &p2, ENOENT)) != 0) |
319 | return error; |
320 | |
321 | l1 = curlwp; /* tracer */ |
322 | |
323 | #define M2K(m) (((m) & FREAD) && ((m) & FWRITE) ? \ |
324 | KAUTH_REQ_PROCESS_PROCFS_RW : \ |
325 | (m) & FWRITE ? KAUTH_REQ_PROCESS_PROCFS_WRITE : \ |
326 | KAUTH_REQ_PROCESS_PROCFS_READ) |
327 | |
328 | mutex_enter(p2->p_lock); |
329 | error = kauth_authorize_process(l1->l_cred, KAUTH_PROCESS_PROCFS, |
330 | p2, pfs, KAUTH_ARG(M2K(ap->a_mode)), NULL); |
331 | mutex_exit(p2->p_lock); |
332 | if (error) { |
333 | procfs_proc_unlock(p2); |
334 | return (error); |
335 | } |
336 | |
337 | #undef M2K |
338 | |
339 | switch (pfs->pfs_type) { |
340 | case PFSmem: |
341 | if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) || |
342 | ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) { |
343 | error = EBUSY; |
344 | break; |
345 | } |
346 | |
347 | if (!proc_isunder(p2, l1)) { |
348 | error = EPERM; |
349 | break; |
350 | } |
351 | |
352 | if (ap->a_mode & FWRITE) |
353 | pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); |
354 | |
355 | break; |
356 | |
357 | case PFSregs: |
358 | case PFSfpregs: |
359 | if (!proc_isunder(p2, l1)) { |
360 | error = EPERM; |
361 | break; |
362 | } |
363 | break; |
364 | |
365 | default: |
366 | break; |
367 | } |
368 | |
369 | procfs_proc_unlock(p2); |
370 | return (error); |
371 | } |
372 | |
373 | /* |
374 | * close the pfsnode (vp) after doing i/o. |
375 | * (vp) is not locked on entry or exit. |
376 | * |
377 | * nothing to do for procfs other than undo |
378 | * any exclusive open flag (see _open above). |
379 | */ |
380 | int |
381 | procfs_close(void *v) |
382 | { |
383 | struct vop_close_args /* { |
384 | struct vnode *a_vp; |
385 | int a_fflag; |
386 | kauth_cred_t a_cred; |
387 | } */ *ap = v; |
388 | struct pfsnode *pfs = VTOPFS(ap->a_vp); |
389 | |
390 | switch (pfs->pfs_type) { |
391 | case PFSmem: |
392 | if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) |
393 | pfs->pfs_flags &= ~(FWRITE|O_EXCL); |
394 | break; |
395 | |
396 | default: |
397 | break; |
398 | } |
399 | |
400 | return (0); |
401 | } |
402 | |
403 | /* |
404 | * _inactive is called when the pfsnode |
405 | * is vrele'd and the reference count goes |
406 | * to zero. (vp) will be on the vnode free |
407 | * list, so to get it back vget() must be |
408 | * used. |
409 | * |
410 | * (vp) is locked on entry, but must be unlocked on exit. |
411 | */ |
412 | int |
413 | procfs_inactive(void *v) |
414 | { |
415 | struct vop_inactive_args /* { |
416 | struct vnode *a_vp; |
417 | bool *a_recycle; |
418 | } */ *ap = v; |
419 | struct vnode *vp = ap->a_vp; |
420 | struct pfsnode *pfs = VTOPFS(vp); |
421 | |
422 | mutex_enter(proc_lock); |
423 | *ap->a_recycle = (proc_find(pfs->pfs_pid) == NULL); |
424 | mutex_exit(proc_lock); |
425 | |
426 | VOP_UNLOCK(vp); |
427 | |
428 | return (0); |
429 | } |
430 | |
431 | /* |
432 | * _reclaim is called when getnewvnode() |
433 | * wants to make use of an entry on the vnode |
434 | * free list. at this time the filesystem needs |
435 | * to free any private data and remove the node |
436 | * from any private lists. |
437 | */ |
438 | int |
439 | procfs_reclaim(void *v) |
440 | { |
441 | struct vop_reclaim_args /* { |
442 | struct vnode *a_vp; |
443 | } */ *ap = v; |
444 | struct vnode *vp = ap->a_vp; |
445 | struct pfsnode *pfs = VTOPFS(vp); |
446 | |
447 | /* |
448 | * To interlock with procfs_revoke_vnodes(). |
449 | */ |
450 | mutex_enter(vp->v_interlock); |
451 | vp->v_data = NULL; |
452 | mutex_exit(vp->v_interlock); |
453 | kmem_free(pfs, sizeof(*pfs)); |
454 | return 0; |
455 | } |
456 | |
457 | /* |
458 | * Return POSIX pathconf information applicable to special devices. |
459 | */ |
460 | int |
461 | procfs_pathconf(void *v) |
462 | { |
463 | struct vop_pathconf_args /* { |
464 | struct vnode *a_vp; |
465 | int a_name; |
466 | register_t *a_retval; |
467 | } */ *ap = v; |
468 | |
469 | switch (ap->a_name) { |
470 | case _PC_LINK_MAX: |
471 | *ap->a_retval = LINK_MAX; |
472 | return (0); |
473 | case _PC_MAX_CANON: |
474 | *ap->a_retval = MAX_CANON; |
475 | return (0); |
476 | case _PC_MAX_INPUT: |
477 | *ap->a_retval = MAX_INPUT; |
478 | return (0); |
479 | case _PC_PIPE_BUF: |
480 | *ap->a_retval = PIPE_BUF; |
481 | return (0); |
482 | case _PC_CHOWN_RESTRICTED: |
483 | *ap->a_retval = 1; |
484 | return (0); |
485 | case _PC_VDISABLE: |
486 | *ap->a_retval = _POSIX_VDISABLE; |
487 | return (0); |
488 | case _PC_SYNC_IO: |
489 | *ap->a_retval = 1; |
490 | return (0); |
491 | default: |
492 | return (EINVAL); |
493 | } |
494 | /* NOTREACHED */ |
495 | } |
496 | |
497 | /* |
498 | * _print is used for debugging. |
499 | * just print a readable description |
500 | * of (vp). |
501 | */ |
502 | int |
503 | procfs_print(void *v) |
504 | { |
505 | struct vop_print_args /* { |
506 | struct vnode *a_vp; |
507 | } */ *ap = v; |
508 | struct pfsnode *pfs = VTOPFS(ap->a_vp); |
509 | |
510 | printf("tag VT_PROCFS, type %d, pid %d, mode %x, flags %lx\n" , |
511 | pfs->pfs_type, pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags); |
512 | return 0; |
513 | } |
514 | |
515 | int |
516 | procfs_link(void *v) |
517 | { |
518 | struct vop_link_v2_args /* { |
519 | struct vnode *a_dvp; |
520 | struct vnode *a_vp; |
521 | struct componentname *a_cnp; |
522 | } */ *ap = v; |
523 | |
524 | VOP_ABORTOP(ap->a_dvp, ap->a_cnp); |
525 | return (EROFS); |
526 | } |
527 | |
528 | int |
529 | procfs_symlink(void *v) |
530 | { |
531 | struct vop_symlink_v3_args /* { |
532 | struct vnode *a_dvp; |
533 | struct vnode **a_vpp; |
534 | struct componentname *a_cnp; |
535 | struct vattr *a_vap; |
536 | char *a_target; |
537 | } */ *ap = v; |
538 | |
539 | VOP_ABORTOP(ap->a_dvp, ap->a_cnp); |
540 | return (EROFS); |
541 | } |
542 | |
543 | /* |
544 | * Works out the path to (and vnode of) the target process's current |
545 | * working directory or chroot. If the caller is in a chroot and |
546 | * can't "reach" the target's cwd or root (or some other error |
547 | * occurs), a "/" is returned for the path and a NULL pointer is |
548 | * returned for the vnode. |
549 | */ |
550 | static void |
551 | procfs_dir(pfstype t, struct lwp *caller, struct proc *target, char **bpp, |
552 | char *path, size_t len) |
553 | { |
554 | struct cwdinfo *cwdi; |
555 | struct vnode *vp, *rvp; |
556 | char *bp; |
557 | |
558 | cwdi = caller->l_proc->p_cwdi; |
559 | rw_enter(&cwdi->cwdi_lock, RW_READER); |
560 | |
561 | rvp = cwdi->cwdi_rdir; |
562 | bp = bpp ? *bpp : NULL; |
563 | |
564 | switch (t) { |
565 | case PFScwd: |
566 | vp = target->p_cwdi->cwdi_cdir; |
567 | break; |
568 | case PFSchroot: |
569 | vp = target->p_cwdi->cwdi_rdir; |
570 | break; |
571 | case PFSexe: |
572 | vp = target->p_textvp; |
573 | break; |
574 | default: |
575 | rw_exit(&cwdi->cwdi_lock); |
576 | return; |
577 | } |
578 | |
579 | /* |
580 | * XXX: this horrible kludge avoids locking panics when |
581 | * attempting to lookup links that point to within procfs |
582 | */ |
583 | if (vp != NULL && vp->v_tag == VT_PROCFS) { |
584 | if (bpp) { |
585 | *--bp = '/'; |
586 | *bpp = bp; |
587 | } |
588 | rw_exit(&cwdi->cwdi_lock); |
589 | return; |
590 | } |
591 | |
592 | if (rvp == NULL) |
593 | rvp = rootvnode; |
594 | if (vp == NULL || getcwd_common(vp, rvp, bp ? &bp : NULL, path, |
595 | len / 2, 0, caller) != 0) { |
596 | vp = NULL; |
597 | if (bpp) { |
598 | /* |
599 | if (t == PFSexe) { |
600 | snprintf(path, len, "%s/%d/file" |
601 | mp->mnt_stat.f_mntonname, pfs->pfs_pid); |
602 | } else */ { |
603 | bp = *bpp; |
604 | *--bp = '/'; |
605 | } |
606 | } |
607 | } |
608 | |
609 | if (bpp) |
610 | *bpp = bp; |
611 | |
612 | rw_exit(&cwdi->cwdi_lock); |
613 | } |
614 | |
615 | /* |
616 | * Invent attributes for pfsnode (vp) and store |
617 | * them in (vap). |
618 | * Directories lengths are returned as zero since |
619 | * any real length would require the genuine size |
620 | * to be computed, and nothing cares anyway. |
621 | * |
622 | * this is relatively minimal for procfs. |
623 | */ |
624 | int |
625 | procfs_getattr(void *v) |
626 | { |
627 | struct vop_getattr_args /* { |
628 | struct vnode *a_vp; |
629 | struct vattr *a_vap; |
630 | kauth_cred_t a_cred; |
631 | } */ *ap = v; |
632 | struct pfsnode *pfs = VTOPFS(ap->a_vp); |
633 | struct vattr *vap = ap->a_vap; |
634 | struct proc *procp; |
635 | char *path; |
636 | int error; |
637 | |
638 | /* first check the process still exists */ |
639 | switch (pfs->pfs_type) { |
640 | case PFSroot: |
641 | case PFScurproc: |
642 | case PFSself: |
643 | procp = NULL; |
644 | break; |
645 | |
646 | default: |
647 | error = procfs_proc_lock(pfs->pfs_pid, &procp, ENOENT); |
648 | if (error != 0) |
649 | return (error); |
650 | break; |
651 | } |
652 | |
653 | switch (pfs->pfs_type) { |
654 | case PFStask: |
655 | if (pfs->pfs_fd == -1) { |
656 | path = NULL; |
657 | break; |
658 | } |
659 | /*FALLTHROUGH*/ |
660 | case PFScwd: |
661 | case PFSchroot: |
662 | case PFSexe: |
663 | path = malloc(MAXPATHLEN + 4, M_TEMP, M_WAITOK|M_CANFAIL); |
664 | if (path == NULL && procp != NULL) { |
665 | procfs_proc_unlock(procp); |
666 | return (ENOMEM); |
667 | } |
668 | break; |
669 | |
670 | default: |
671 | path = NULL; |
672 | break; |
673 | } |
674 | |
675 | if (procp != NULL) { |
676 | mutex_enter(procp->p_lock); |
677 | error = kauth_authorize_process(kauth_cred_get(), |
678 | KAUTH_PROCESS_CANSEE, procp, |
679 | KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL, NULL); |
680 | mutex_exit(procp->p_lock); |
681 | if (error != 0) { |
682 | procfs_proc_unlock(procp); |
683 | if (path != NULL) |
684 | free(path, M_TEMP); |
685 | return (ENOENT); |
686 | } |
687 | } |
688 | |
689 | error = 0; |
690 | |
691 | /* start by zeroing out the attributes */ |
692 | vattr_null(vap); |
693 | |
694 | /* next do all the common fields */ |
695 | vap->va_type = ap->a_vp->v_type; |
696 | vap->va_mode = pfs->pfs_mode; |
697 | vap->va_fileid = pfs->pfs_fileno; |
698 | vap->va_flags = 0; |
699 | vap->va_blocksize = PAGE_SIZE; |
700 | |
701 | /* |
702 | * Make all times be current TOD. |
703 | * |
704 | * It would be possible to get the process start |
705 | * time from the p_stats structure, but there's |
706 | * no "file creation" time stamp anyway, and the |
707 | * p_stats structure is not addressable if u. gets |
708 | * swapped out for that process. |
709 | */ |
710 | getnanotime(&vap->va_ctime); |
711 | vap->va_atime = vap->va_mtime = vap->va_ctime; |
712 | if (procp) |
713 | TIMEVAL_TO_TIMESPEC(&procp->p_stats->p_start, |
714 | &vap->va_birthtime); |
715 | else |
716 | getnanotime(&vap->va_birthtime); |
717 | |
718 | switch (pfs->pfs_type) { |
719 | case PFSmem: |
720 | case PFSregs: |
721 | case PFSfpregs: |
722 | #if defined(__HAVE_PROCFS_MACHDEP) && defined(PROCFS_MACHDEP_PROTECT_CASES) |
723 | PROCFS_MACHDEP_PROTECT_CASES |
724 | #endif |
725 | /* |
726 | * If the process has exercised some setuid or setgid |
727 | * privilege, then rip away read/write permission so |
728 | * that only root can gain access. |
729 | */ |
730 | if (procp->p_flag & PK_SUGID) |
731 | vap->va_mode &= ~(S_IRUSR|S_IWUSR); |
732 | /* FALLTHROUGH */ |
733 | case PFSctl: |
734 | case PFSstatus: |
735 | case PFSstat: |
736 | case PFSnote: |
737 | case PFSnotepg: |
738 | case PFSmap: |
739 | case PFSmaps: |
740 | case PFScmdline: |
741 | case PFSemul: |
742 | case PFSstatm: |
743 | if (pfs->pfs_type == PFSmap || pfs->pfs_type == PFSmaps) |
744 | vap->va_mode = S_IRUSR; |
745 | vap->va_nlink = 1; |
746 | vap->va_uid = kauth_cred_geteuid(procp->p_cred); |
747 | vap->va_gid = kauth_cred_getegid(procp->p_cred); |
748 | break; |
749 | case PFSmeminfo: |
750 | case PFSdevices: |
751 | case PFScpuinfo: |
752 | case PFSuptime: |
753 | case PFSmounts: |
754 | case PFScpustat: |
755 | case PFSloadavg: |
756 | case PFSversion: |
757 | vap->va_nlink = 1; |
758 | vap->va_uid = vap->va_gid = 0; |
759 | break; |
760 | |
761 | default: |
762 | break; |
763 | } |
764 | |
765 | /* |
766 | * now do the object specific fields |
767 | * |
768 | * The size could be set from struct reg, but it's hardly |
769 | * worth the trouble, and it puts some (potentially) machine |
770 | * dependent data into this machine-independent code. If it |
771 | * becomes important then this function should break out into |
772 | * a per-file stat function in the corresponding .c file. |
773 | */ |
774 | |
775 | switch (pfs->pfs_type) { |
776 | case PFSroot: |
777 | /* |
778 | * Set nlink to 1 to tell fts(3) we don't actually know. |
779 | */ |
780 | vap->va_nlink = 1; |
781 | vap->va_uid = 0; |
782 | vap->va_gid = 0; |
783 | vap->va_bytes = vap->va_size = DEV_BSIZE; |
784 | break; |
785 | |
786 | case PFSself: |
787 | case PFScurproc: { |
788 | char bf[16]; /* should be enough */ |
789 | vap->va_nlink = 1; |
790 | vap->va_uid = 0; |
791 | vap->va_gid = 0; |
792 | vap->va_bytes = vap->va_size = |
793 | snprintf(bf, sizeof(bf), "%ld" , (long)curproc->p_pid); |
794 | break; |
795 | } |
796 | case PFStask: |
797 | if (pfs->pfs_fd != -1) { |
798 | char bf[4]; /* should be enough */ |
799 | vap->va_nlink = 1; |
800 | vap->va_uid = 0; |
801 | vap->va_gid = 0; |
802 | vap->va_bytes = vap->va_size = |
803 | snprintf(bf, sizeof(bf), ".." ); |
804 | break; |
805 | } |
806 | /*FALLTHROUGH*/ |
807 | case PFSfd: |
808 | if (pfs->pfs_fd != -1) { |
809 | file_t *fp; |
810 | |
811 | fp = fd_getfile2(procp, pfs->pfs_fd); |
812 | if (fp == NULL) { |
813 | error = EBADF; |
814 | break; |
815 | } |
816 | vap->va_nlink = 1; |
817 | vap->va_uid = kauth_cred_geteuid(fp->f_cred); |
818 | vap->va_gid = kauth_cred_getegid(fp->f_cred); |
819 | switch (fp->f_type) { |
820 | case DTYPE_VNODE: |
821 | vap->va_bytes = vap->va_size = |
822 | fp->f_vnode->v_size; |
823 | break; |
824 | default: |
825 | vap->va_bytes = vap->va_size = 0; |
826 | break; |
827 | } |
828 | closef(fp); |
829 | break; |
830 | } |
831 | /*FALLTHROUGH*/ |
832 | case PFSproc: |
833 | vap->va_nlink = 2; |
834 | vap->va_uid = kauth_cred_geteuid(procp->p_cred); |
835 | vap->va_gid = kauth_cred_getegid(procp->p_cred); |
836 | vap->va_bytes = vap->va_size = DEV_BSIZE; |
837 | break; |
838 | |
839 | case PFSfile: |
840 | error = EOPNOTSUPP; |
841 | break; |
842 | |
843 | case PFSmem: |
844 | vap->va_bytes = vap->va_size = |
845 | ctob(procp->p_vmspace->vm_tsize + |
846 | procp->p_vmspace->vm_dsize + |
847 | procp->p_vmspace->vm_ssize); |
848 | break; |
849 | |
850 | #if defined(PT_GETREGS) || defined(PT_SETREGS) |
851 | case PFSregs: |
852 | vap->va_bytes = vap->va_size = sizeof(struct reg); |
853 | break; |
854 | #endif |
855 | |
856 | #if defined(PT_GETFPREGS) || defined(PT_SETFPREGS) |
857 | case PFSfpregs: |
858 | vap->va_bytes = vap->va_size = sizeof(struct fpreg); |
859 | break; |
860 | #endif |
861 | |
862 | case PFSctl: |
863 | case PFSstatus: |
864 | case PFSstat: |
865 | case PFSnote: |
866 | case PFSnotepg: |
867 | case PFScmdline: |
868 | case PFSmeminfo: |
869 | case PFSdevices: |
870 | case PFScpuinfo: |
871 | case PFSuptime: |
872 | case PFSmounts: |
873 | case PFScpustat: |
874 | case PFSloadavg: |
875 | case PFSstatm: |
876 | case PFSversion: |
877 | vap->va_bytes = vap->va_size = 0; |
878 | break; |
879 | case PFSmap: |
880 | case PFSmaps: |
881 | /* |
882 | * Advise a larger blocksize for the map files, so that |
883 | * they may be read in one pass. |
884 | */ |
885 | vap->va_blocksize = 4 * PAGE_SIZE; |
886 | vap->va_bytes = vap->va_size = 0; |
887 | break; |
888 | |
889 | case PFScwd: |
890 | case PFSchroot: |
891 | case PFSexe: { |
892 | char *bp; |
893 | |
894 | vap->va_nlink = 1; |
895 | vap->va_uid = 0; |
896 | vap->va_gid = 0; |
897 | bp = path + MAXPATHLEN; |
898 | *--bp = '\0'; |
899 | procfs_dir(pfs->pfs_type, curlwp, procp, &bp, path, |
900 | MAXPATHLEN); |
901 | vap->va_bytes = vap->va_size = strlen(bp); |
902 | break; |
903 | } |
904 | |
905 | case PFSemul: |
906 | vap->va_bytes = vap->va_size = strlen(procp->p_emul->e_name); |
907 | break; |
908 | |
909 | #ifdef __HAVE_PROCFS_MACHDEP |
910 | PROCFS_MACHDEP_NODETYPE_CASES |
911 | error = procfs_machdep_getattr(ap->a_vp, vap, procp); |
912 | break; |
913 | #endif |
914 | |
915 | default: |
916 | panic("procfs_getattr" ); |
917 | } |
918 | |
919 | if (procp != NULL) |
920 | procfs_proc_unlock(procp); |
921 | if (path != NULL) |
922 | free(path, M_TEMP); |
923 | |
924 | return (error); |
925 | } |
926 | |
927 | /*ARGSUSED*/ |
928 | int |
929 | procfs_setattr(void *v) |
930 | { |
931 | /* |
932 | * just fake out attribute setting |
933 | * it's not good to generate an error |
934 | * return, otherwise things like creat() |
935 | * will fail when they try to set the |
936 | * file length to 0. worse, this means |
937 | * that echo $note > /proc/$pid/note will fail. |
938 | */ |
939 | |
940 | return (0); |
941 | } |
942 | |
943 | /* |
944 | * implement access checking. |
945 | * |
946 | * actually, the check for super-user is slightly |
947 | * broken since it will allow read access to write-only |
948 | * objects. this doesn't cause any particular trouble |
949 | * but does mean that the i/o entry points need to check |
950 | * that the operation really does make sense. |
951 | */ |
952 | int |
953 | procfs_access(void *v) |
954 | { |
955 | struct vop_access_args /* { |
956 | struct vnode *a_vp; |
957 | int a_mode; |
958 | kauth_cred_t a_cred; |
959 | } */ *ap = v; |
960 | struct vattr va; |
961 | int error; |
962 | |
963 | if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred)) != 0) |
964 | return (error); |
965 | |
966 | return kauth_authorize_vnode(ap->a_cred, |
967 | KAUTH_ACCESS_ACTION(ap->a_mode, ap->a_vp->v_type, va.va_mode), |
968 | ap->a_vp, NULL, genfs_can_access(va.va_type, va.va_mode, |
969 | va.va_uid, va.va_gid, ap->a_mode, ap->a_cred)); |
970 | } |
971 | |
972 | /* |
973 | * lookup. this is incredibly complicated in the |
974 | * general case, however for most pseudo-filesystems |
975 | * very little needs to be done. |
976 | * |
977 | * Locking isn't hard here, just poorly documented. |
978 | * |
979 | * If we're looking up ".", just vref the parent & return it. |
980 | * |
981 | * If we're looking up "..", unlock the parent, and lock "..". If everything |
982 | * went ok, and we're on the last component and the caller requested the |
983 | * parent locked, try to re-lock the parent. We do this to prevent lock |
984 | * races. |
985 | * |
986 | * For anything else, get the needed node. Then unlock the parent if not |
987 | * the last component or not LOCKPARENT (i.e. if we wouldn't re-lock the |
988 | * parent in the .. case). |
989 | * |
990 | * We try to exit with the parent locked in error cases. |
991 | */ |
992 | int |
993 | procfs_lookup(void *v) |
994 | { |
995 | struct vop_lookup_v2_args /* { |
996 | struct vnode * a_dvp; |
997 | struct vnode ** a_vpp; |
998 | struct componentname * a_cnp; |
999 | } */ *ap = v; |
1000 | struct componentname *cnp = ap->a_cnp; |
1001 | struct vnode **vpp = ap->a_vpp; |
1002 | struct vnode *dvp = ap->a_dvp; |
1003 | const char *pname = cnp->cn_nameptr; |
1004 | const struct proc_target *pt = NULL; |
1005 | struct vnode *fvp; |
1006 | pid_t pid, vnpid; |
1007 | struct pfsnode *pfs; |
1008 | struct proc *p = NULL; |
1009 | struct lwp *plwp; |
1010 | int i, error; |
1011 | pfstype type; |
1012 | |
1013 | *vpp = NULL; |
1014 | |
1015 | if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) |
1016 | return (EROFS); |
1017 | |
1018 | if (cnp->cn_namelen == 1 && *pname == '.') { |
1019 | *vpp = dvp; |
1020 | vref(dvp); |
1021 | return (0); |
1022 | } |
1023 | |
1024 | pfs = VTOPFS(dvp); |
1025 | switch (pfs->pfs_type) { |
1026 | case PFSroot: |
1027 | /* |
1028 | * Shouldn't get here with .. in the root node. |
1029 | */ |
1030 | if (cnp->cn_flags & ISDOTDOT) |
1031 | return (EIO); |
1032 | |
1033 | for (i = 0; i < nproc_root_targets; i++) { |
1034 | pt = &proc_root_targets[i]; |
1035 | /* |
1036 | * check for node match. proc is always NULL here, |
1037 | * so call pt_valid with constant NULL lwp. |
1038 | */ |
1039 | if (cnp->cn_namelen == pt->pt_namlen && |
1040 | memcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && |
1041 | (pt->pt_valid == NULL || |
1042 | (*pt->pt_valid)(NULL, dvp->v_mount))) |
1043 | break; |
1044 | } |
1045 | |
1046 | if (i != nproc_root_targets) { |
1047 | error = procfs_allocvp(dvp->v_mount, vpp, 0, |
1048 | pt->pt_pfstype, -1); |
1049 | return (error); |
1050 | } |
1051 | |
1052 | if (CNEQ(cnp, "curproc" , 7)) { |
1053 | pid = curproc->p_pid; |
1054 | vnpid = 0; |
1055 | type = PFScurproc; |
1056 | } else if (CNEQ(cnp, "self" , 4)) { |
1057 | pid = curproc->p_pid; |
1058 | vnpid = 0; |
1059 | type = PFSself; |
1060 | } else { |
1061 | pid = (pid_t)atoi(pname, cnp->cn_namelen); |
1062 | vnpid = pid; |
1063 | type = PFSproc; |
1064 | } |
1065 | |
1066 | if (procfs_proc_lock(pid, &p, ESRCH) != 0) |
1067 | break; |
1068 | error = procfs_allocvp(dvp->v_mount, vpp, vnpid, type, -1); |
1069 | procfs_proc_unlock(p); |
1070 | return (error); |
1071 | |
1072 | case PFSproc: |
1073 | if (cnp->cn_flags & ISDOTDOT) { |
1074 | error = procfs_allocvp(dvp->v_mount, vpp, 0, PFSroot, |
1075 | -1); |
1076 | return (error); |
1077 | } |
1078 | |
1079 | if (procfs_proc_lock(pfs->pfs_pid, &p, ESRCH) != 0) |
1080 | break; |
1081 | |
1082 | mutex_enter(p->p_lock); |
1083 | LIST_FOREACH(plwp, &p->p_lwps, l_sibling) { |
1084 | if (plwp->l_stat != LSZOMB) |
1085 | break; |
1086 | } |
1087 | /* Process is exiting if no-LWPS or all LWPs are LSZOMB */ |
1088 | if (plwp == NULL) { |
1089 | mutex_exit(p->p_lock); |
1090 | procfs_proc_unlock(p); |
1091 | return ESRCH; |
1092 | } |
1093 | |
1094 | lwp_addref(plwp); |
1095 | mutex_exit(p->p_lock); |
1096 | |
1097 | for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) { |
1098 | int found; |
1099 | |
1100 | found = cnp->cn_namelen == pt->pt_namlen && |
1101 | memcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && |
1102 | (pt->pt_valid == NULL |
1103 | || (*pt->pt_valid)(plwp, dvp->v_mount)); |
1104 | if (found) |
1105 | break; |
1106 | } |
1107 | lwp_delref(plwp); |
1108 | |
1109 | if (i == nproc_targets) { |
1110 | procfs_proc_unlock(p); |
1111 | break; |
1112 | } |
1113 | if (pt->pt_pfstype == PFSfile) { |
1114 | fvp = p->p_textvp; |
1115 | /* We already checked that it exists. */ |
1116 | vref(fvp); |
1117 | procfs_proc_unlock(p); |
1118 | *vpp = fvp; |
1119 | return (0); |
1120 | } |
1121 | |
1122 | error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, |
1123 | pt->pt_pfstype, -1); |
1124 | procfs_proc_unlock(p); |
1125 | return (error); |
1126 | |
1127 | case PFSfd: { |
1128 | int fd; |
1129 | file_t *fp; |
1130 | |
1131 | if ((error = procfs_proc_lock(pfs->pfs_pid, &p, ENOENT)) != 0) |
1132 | return error; |
1133 | |
1134 | if (cnp->cn_flags & ISDOTDOT) { |
1135 | error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, |
1136 | PFSproc, -1); |
1137 | procfs_proc_unlock(p); |
1138 | return (error); |
1139 | } |
1140 | fd = atoi(pname, cnp->cn_namelen); |
1141 | |
1142 | fp = fd_getfile2(p, fd); |
1143 | if (fp == NULL) { |
1144 | procfs_proc_unlock(p); |
1145 | return ENOENT; |
1146 | } |
1147 | fvp = fp->f_vnode; |
1148 | |
1149 | /* Don't show directories */ |
1150 | if (fp->f_type == DTYPE_VNODE && fvp->v_type != VDIR) { |
1151 | vref(fvp); |
1152 | closef(fp); |
1153 | procfs_proc_unlock(p); |
1154 | *vpp = fvp; |
1155 | return 0; |
1156 | } |
1157 | |
1158 | closef(fp); |
1159 | error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, |
1160 | PFSfd, fd); |
1161 | procfs_proc_unlock(p); |
1162 | return error; |
1163 | } |
1164 | case PFStask: { |
1165 | int xpid; |
1166 | |
1167 | if ((error = procfs_proc_lock(pfs->pfs_pid, &p, ENOENT)) != 0) |
1168 | return error; |
1169 | |
1170 | if (cnp->cn_flags & ISDOTDOT) { |
1171 | error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, |
1172 | PFSproc, -1); |
1173 | procfs_proc_unlock(p); |
1174 | return (error); |
1175 | } |
1176 | xpid = atoi(pname, cnp->cn_namelen); |
1177 | |
1178 | if (xpid != pfs->pfs_pid) { |
1179 | procfs_proc_unlock(p); |
1180 | return ENOENT; |
1181 | } |
1182 | error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, |
1183 | PFStask, 0); |
1184 | procfs_proc_unlock(p); |
1185 | return error; |
1186 | } |
1187 | default: |
1188 | return (ENOTDIR); |
1189 | } |
1190 | |
1191 | return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS); |
1192 | } |
1193 | |
1194 | int |
1195 | procfs_validfile(struct lwp *l, struct mount *mp) |
1196 | { |
1197 | return l != NULL && l->l_proc != NULL && l->l_proc->p_textvp != NULL; |
1198 | } |
1199 | |
1200 | static int |
1201 | procfs_validfile_linux(struct lwp *l, struct mount *mp) |
1202 | { |
1203 | int flags; |
1204 | |
1205 | flags = VFSTOPROC(mp)->pmnt_flags; |
1206 | return (flags & PROCFSMNT_LINUXCOMPAT) && |
1207 | (l == NULL || l->l_proc == NULL || procfs_validfile(l, mp)); |
1208 | } |
1209 | |
1210 | struct procfs_root_readdir_ctx { |
1211 | struct uio *uiop; |
1212 | off_t *cookies; |
1213 | int ncookies; |
1214 | off_t off; |
1215 | off_t startoff; |
1216 | int error; |
1217 | }; |
1218 | |
1219 | static int |
1220 | procfs_root_readdir_callback(struct proc *p, void *arg) |
1221 | { |
1222 | struct procfs_root_readdir_ctx *ctxp = arg; |
1223 | struct dirent d; |
1224 | struct uio *uiop; |
1225 | int error; |
1226 | |
1227 | uiop = ctxp->uiop; |
1228 | if (uiop->uio_resid < UIO_MX) |
1229 | return -1; /* no space */ |
1230 | |
1231 | if (ctxp->off < ctxp->startoff) { |
1232 | ctxp->off++; |
1233 | return 0; |
1234 | } |
1235 | |
1236 | if (kauth_authorize_process(kauth_cred_get(), |
1237 | KAUTH_PROCESS_CANSEE, p, |
1238 | KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL, NULL) != 0) |
1239 | return 0; |
1240 | |
1241 | memset(&d, 0, UIO_MX); |
1242 | d.d_reclen = UIO_MX; |
1243 | d.d_fileno = PROCFS_FILENO(p->p_pid, PFSproc, -1); |
1244 | d.d_namlen = snprintf(d.d_name, |
1245 | UIO_MX - offsetof(struct dirent, d_name), "%ld" , (long)p->p_pid); |
1246 | d.d_type = DT_DIR; |
1247 | |
1248 | mutex_exit(proc_lock); |
1249 | error = uiomove(&d, UIO_MX, uiop); |
1250 | mutex_enter(proc_lock); |
1251 | if (error) { |
1252 | ctxp->error = error; |
1253 | return -1; |
1254 | } |
1255 | |
1256 | ctxp->ncookies++; |
1257 | if (ctxp->cookies) |
1258 | *(ctxp->cookies)++ = ctxp->off + 1; |
1259 | ctxp->off++; |
1260 | |
1261 | return 0; |
1262 | } |
1263 | |
1264 | /* |
1265 | * readdir returns directory entries from pfsnode (vp). |
1266 | * |
1267 | * the strategy here with procfs is to generate a single |
1268 | * directory entry at a time (struct dirent) and then |
1269 | * copy that out to userland using uiomove. a more efficent |
1270 | * though more complex implementation, would try to minimize |
1271 | * the number of calls to uiomove(). for procfs, this is |
1272 | * hardly worth the added code complexity. |
1273 | * |
1274 | * this should just be done through read() |
1275 | */ |
1276 | int |
1277 | procfs_readdir(void *v) |
1278 | { |
1279 | struct vop_readdir_args /* { |
1280 | struct vnode *a_vp; |
1281 | struct uio *a_uio; |
1282 | kauth_cred_t a_cred; |
1283 | int *a_eofflag; |
1284 | off_t **a_cookies; |
1285 | int *a_ncookies; |
1286 | } */ *ap = v; |
1287 | struct uio *uio = ap->a_uio; |
1288 | struct dirent d; |
1289 | struct pfsnode *pfs; |
1290 | off_t i; |
1291 | int error; |
1292 | off_t *cookies = NULL; |
1293 | int ncookies; |
1294 | struct vnode *vp; |
1295 | const struct proc_target *pt; |
1296 | struct procfs_root_readdir_ctx ctx; |
1297 | struct lwp *l; |
1298 | int nfd; |
1299 | |
1300 | vp = ap->a_vp; |
1301 | pfs = VTOPFS(vp); |
1302 | |
1303 | if (uio->uio_resid < UIO_MX) |
1304 | return (EINVAL); |
1305 | if (uio->uio_offset < 0) |
1306 | return (EINVAL); |
1307 | |
1308 | error = 0; |
1309 | i = uio->uio_offset; |
1310 | memset(&d, 0, UIO_MX); |
1311 | d.d_reclen = UIO_MX; |
1312 | ncookies = uio->uio_resid / UIO_MX; |
1313 | |
1314 | switch (pfs->pfs_type) { |
1315 | /* |
1316 | * this is for the process-specific sub-directories. |
1317 | * all that is needed to is copy out all the entries |
1318 | * from the procent[] table (top of this file). |
1319 | */ |
1320 | case PFSproc: { |
1321 | struct proc *p; |
1322 | |
1323 | if (i >= nproc_targets) |
1324 | return 0; |
1325 | |
1326 | if (procfs_proc_lock(pfs->pfs_pid, &p, ESRCH) != 0) |
1327 | break; |
1328 | |
1329 | if (ap->a_ncookies) { |
1330 | ncookies = min(ncookies, (nproc_targets - i)); |
1331 | cookies = malloc(ncookies * sizeof (off_t), |
1332 | M_TEMP, M_WAITOK); |
1333 | *ap->a_cookies = cookies; |
1334 | } |
1335 | |
1336 | for (pt = &proc_targets[i]; |
1337 | uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) { |
1338 | if (pt->pt_valid) { |
1339 | /* XXXSMP LWP can disappear */ |
1340 | mutex_enter(p->p_lock); |
1341 | l = LIST_FIRST(&p->p_lwps); |
1342 | KASSERT(l != NULL); |
1343 | mutex_exit(p->p_lock); |
1344 | if ((*pt->pt_valid)(l, vp->v_mount) == 0) |
1345 | continue; |
1346 | } |
1347 | |
1348 | d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, |
1349 | pt->pt_pfstype, -1); |
1350 | d.d_namlen = pt->pt_namlen; |
1351 | memcpy(d.d_name, pt->pt_name, pt->pt_namlen + 1); |
1352 | d.d_type = pt->pt_type; |
1353 | |
1354 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1355 | break; |
1356 | if (cookies) |
1357 | *cookies++ = i + 1; |
1358 | } |
1359 | |
1360 | procfs_proc_unlock(p); |
1361 | break; |
1362 | } |
1363 | case PFSfd: { |
1364 | struct proc *p; |
1365 | file_t *fp; |
1366 | int lim, nc = 0; |
1367 | |
1368 | if ((error = procfs_proc_lock(pfs->pfs_pid, &p, ESRCH)) != 0) |
1369 | return error; |
1370 | |
1371 | /* XXX Should this be by file as well? */ |
1372 | if (kauth_authorize_process(kauth_cred_get(), |
1373 | KAUTH_PROCESS_CANSEE, p, |
1374 | KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_OPENFILES), NULL, |
1375 | NULL) != 0) { |
1376 | procfs_proc_unlock(p); |
1377 | return ESRCH; |
1378 | } |
1379 | |
1380 | nfd = p->p_fd->fd_dt->dt_nfiles; |
1381 | |
1382 | lim = min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles); |
1383 | if (i >= lim) { |
1384 | procfs_proc_unlock(p); |
1385 | return 0; |
1386 | } |
1387 | |
1388 | if (ap->a_ncookies) { |
1389 | ncookies = min(ncookies, (nfd + 2 - i)); |
1390 | cookies = malloc(ncookies * sizeof (off_t), |
1391 | M_TEMP, M_WAITOK); |
1392 | *ap->a_cookies = cookies; |
1393 | } |
1394 | |
1395 | for (; i < 2 && uio->uio_resid >= UIO_MX; i++) { |
1396 | pt = &proc_targets[i]; |
1397 | d.d_namlen = pt->pt_namlen; |
1398 | d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, |
1399 | pt->pt_pfstype, -1); |
1400 | (void)memcpy(d.d_name, pt->pt_name, pt->pt_namlen + 1); |
1401 | d.d_type = pt->pt_type; |
1402 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1403 | break; |
1404 | if (cookies) |
1405 | *cookies++ = i + 1; |
1406 | nc++; |
1407 | } |
1408 | if (error) { |
1409 | ncookies = nc; |
1410 | break; |
1411 | } |
1412 | for (; uio->uio_resid >= UIO_MX && i < nfd; i++) { |
1413 | /* check the descriptor exists */ |
1414 | if ((fp = fd_getfile2(p, i - 2)) == NULL) |
1415 | continue; |
1416 | closef(fp); |
1417 | |
1418 | d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, PFSfd, i - 2); |
1419 | d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), |
1420 | "%lld" , (long long)(i - 2)); |
1421 | d.d_type = VREG; |
1422 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1423 | break; |
1424 | if (cookies) |
1425 | *cookies++ = i + 1; |
1426 | nc++; |
1427 | } |
1428 | ncookies = nc; |
1429 | procfs_proc_unlock(p); |
1430 | break; |
1431 | } |
1432 | case PFStask: { |
1433 | struct proc *p; |
1434 | int nc = 0; |
1435 | |
1436 | if ((error = procfs_proc_lock(pfs->pfs_pid, &p, ESRCH)) != 0) |
1437 | return error; |
1438 | |
1439 | nfd = 3; /* ., .., pid */ |
1440 | |
1441 | if (ap->a_ncookies) { |
1442 | ncookies = min(ncookies, (nfd + 2 - i)); |
1443 | cookies = malloc(ncookies * sizeof (off_t), |
1444 | M_TEMP, M_WAITOK); |
1445 | *ap->a_cookies = cookies; |
1446 | } |
1447 | |
1448 | for (; i < 2 && uio->uio_resid >= UIO_MX; i++) { |
1449 | pt = &proc_targets[i]; |
1450 | d.d_namlen = pt->pt_namlen; |
1451 | d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, |
1452 | pt->pt_pfstype, -1); |
1453 | (void)memcpy(d.d_name, pt->pt_name, pt->pt_namlen + 1); |
1454 | d.d_type = pt->pt_type; |
1455 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1456 | break; |
1457 | if (cookies) |
1458 | *cookies++ = i + 1; |
1459 | nc++; |
1460 | } |
1461 | if (error) { |
1462 | ncookies = nc; |
1463 | break; |
1464 | } |
1465 | for (; uio->uio_resid >= UIO_MX && i < nfd; i++) { |
1466 | /* check the descriptor exists */ |
1467 | d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, PFStask, |
1468 | i - 2); |
1469 | d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), |
1470 | "%ld" , (long)pfs->pfs_pid); |
1471 | d.d_type = DT_LNK; |
1472 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1473 | break; |
1474 | if (cookies) |
1475 | *cookies++ = i + 1; |
1476 | nc++; |
1477 | } |
1478 | ncookies = nc; |
1479 | procfs_proc_unlock(p); |
1480 | break; |
1481 | } |
1482 | |
1483 | /* |
1484 | * this is for the root of the procfs filesystem |
1485 | * what is needed are special entries for "curproc" |
1486 | * and "self" followed by an entry for each process |
1487 | * on allproc. |
1488 | */ |
1489 | |
1490 | case PFSroot: { |
1491 | int nc = 0; |
1492 | |
1493 | if (ap->a_ncookies) { |
1494 | /* |
1495 | * XXX Potentially allocating too much space here, |
1496 | * but I'm lazy. This loop needs some work. |
1497 | */ |
1498 | cookies = malloc(ncookies * sizeof (off_t), |
1499 | M_TEMP, M_WAITOK); |
1500 | *ap->a_cookies = cookies; |
1501 | } |
1502 | error = 0; |
1503 | /* 0 ... 3 are static entries. */ |
1504 | for (; i <= 3 && uio->uio_resid >= UIO_MX; i++) { |
1505 | switch (i) { |
1506 | case 0: /* `.' */ |
1507 | case 1: /* `..' */ |
1508 | d.d_fileno = PROCFS_FILENO(0, PFSroot, -1); |
1509 | d.d_namlen = i + 1; |
1510 | memcpy(d.d_name, ".." , d.d_namlen); |
1511 | d.d_name[i + 1] = '\0'; |
1512 | d.d_type = DT_DIR; |
1513 | break; |
1514 | |
1515 | case 2: |
1516 | d.d_fileno = PROCFS_FILENO(0, PFScurproc, -1); |
1517 | d.d_namlen = sizeof("curproc" ) - 1; |
1518 | memcpy(d.d_name, "curproc" , sizeof("curproc" )); |
1519 | d.d_type = DT_LNK; |
1520 | break; |
1521 | |
1522 | case 3: |
1523 | d.d_fileno = PROCFS_FILENO(0, PFSself, -1); |
1524 | d.d_namlen = sizeof("self" ) - 1; |
1525 | memcpy(d.d_name, "self" , sizeof("self" )); |
1526 | d.d_type = DT_LNK; |
1527 | break; |
1528 | } |
1529 | |
1530 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1531 | break; |
1532 | nc++; |
1533 | if (cookies) |
1534 | *cookies++ = i + 1; |
1535 | } |
1536 | /* 4 ... are process entries. */ |
1537 | ctx.uiop = uio; |
1538 | ctx.error = 0; |
1539 | ctx.off = 4; |
1540 | ctx.startoff = i; |
1541 | ctx.cookies = cookies; |
1542 | ctx.ncookies = nc; |
1543 | proclist_foreach_call(&allproc, |
1544 | procfs_root_readdir_callback, &ctx); |
1545 | cookies = ctx.cookies; |
1546 | nc = ctx.ncookies; |
1547 | error = ctx.error; |
1548 | if (error) |
1549 | break; |
1550 | |
1551 | /* misc entries. */ |
1552 | if (i < ctx.off) |
1553 | i = ctx.off; |
1554 | if (i >= ctx.off + nproc_root_targets) |
1555 | break; |
1556 | for (pt = &proc_root_targets[i - ctx.off]; |
1557 | uio->uio_resid >= UIO_MX && |
1558 | pt < &proc_root_targets[nproc_root_targets]; |
1559 | pt++, i++) { |
1560 | if (pt->pt_valid && |
1561 | (*pt->pt_valid)(NULL, vp->v_mount) == 0) |
1562 | continue; |
1563 | d.d_fileno = PROCFS_FILENO(0, pt->pt_pfstype, -1); |
1564 | d.d_namlen = pt->pt_namlen; |
1565 | memcpy(d.d_name, pt->pt_name, pt->pt_namlen + 1); |
1566 | d.d_type = pt->pt_type; |
1567 | |
1568 | if ((error = uiomove(&d, UIO_MX, uio)) != 0) |
1569 | break; |
1570 | nc++; |
1571 | if (cookies) |
1572 | *cookies++ = i + 1; |
1573 | } |
1574 | |
1575 | ncookies = nc; |
1576 | break; |
1577 | } |
1578 | |
1579 | default: |
1580 | error = ENOTDIR; |
1581 | break; |
1582 | } |
1583 | |
1584 | if (ap->a_ncookies) { |
1585 | if (error) { |
1586 | if (cookies) |
1587 | free(*ap->a_cookies, M_TEMP); |
1588 | *ap->a_ncookies = 0; |
1589 | *ap->a_cookies = NULL; |
1590 | } else |
1591 | *ap->a_ncookies = ncookies; |
1592 | } |
1593 | uio->uio_offset = i; |
1594 | return (error); |
1595 | } |
1596 | |
1597 | /* |
1598 | * readlink reads the link of `curproc' and others |
1599 | */ |
1600 | int |
1601 | procfs_readlink(void *v) |
1602 | { |
1603 | struct vop_readlink_args *ap = v; |
1604 | char bf[16]; /* should be enough */ |
1605 | char *bp = bf; |
1606 | char *path = NULL; |
1607 | int len = 0; |
1608 | int error = 0; |
1609 | struct pfsnode *pfs = VTOPFS(ap->a_vp); |
1610 | struct proc *pown; |
1611 | |
1612 | if (pfs->pfs_fileno == PROCFS_FILENO(0, PFScurproc, -1)) |
1613 | len = snprintf(bf, sizeof(bf), "%ld" , (long)curproc->p_pid); |
1614 | else if (pfs->pfs_fileno == PROCFS_FILENO(0, PFSself, -1)) |
1615 | len = snprintf(bf, sizeof(bf), "%s" , "curproc" ); |
1616 | else if (pfs->pfs_fileno == PROCFS_FILENO(pfs->pfs_pid, PFStask, 0)) |
1617 | len = snprintf(bf, sizeof(bf), ".." ); |
1618 | else if (pfs->pfs_fileno == PROCFS_FILENO(pfs->pfs_pid, PFScwd, -1) || |
1619 | pfs->pfs_fileno == PROCFS_FILENO(pfs->pfs_pid, PFSchroot, -1) || |
1620 | pfs->pfs_fileno == PROCFS_FILENO(pfs->pfs_pid, PFSexe, -1)) { |
1621 | if ((error = procfs_proc_lock(pfs->pfs_pid, &pown, ESRCH)) != 0) |
1622 | return error; |
1623 | path = malloc(MAXPATHLEN + 4, M_TEMP, M_WAITOK|M_CANFAIL); |
1624 | if (path == NULL) { |
1625 | procfs_proc_unlock(pown); |
1626 | return (ENOMEM); |
1627 | } |
1628 | bp = path + MAXPATHLEN; |
1629 | *--bp = '\0'; |
1630 | procfs_dir(PROCFS_TYPE(pfs->pfs_fileno), curlwp, pown, |
1631 | &bp, path, MAXPATHLEN); |
1632 | procfs_proc_unlock(pown); |
1633 | len = strlen(bp); |
1634 | } else { |
1635 | file_t *fp; |
1636 | struct vnode *vxp, *vp; |
1637 | |
1638 | if ((error = procfs_proc_lock(pfs->pfs_pid, &pown, ESRCH)) != 0) |
1639 | return error; |
1640 | |
1641 | fp = fd_getfile2(pown, pfs->pfs_fd); |
1642 | if (fp == NULL) { |
1643 | procfs_proc_unlock(pown); |
1644 | return EBADF; |
1645 | } |
1646 | |
1647 | switch (fp->f_type) { |
1648 | case DTYPE_VNODE: |
1649 | vxp = fp->f_vnode; |
1650 | if (vxp->v_type != VDIR) { |
1651 | error = EINVAL; |
1652 | break; |
1653 | } |
1654 | if ((path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK)) |
1655 | == NULL) { |
1656 | error = ENOMEM; |
1657 | break; |
1658 | } |
1659 | bp = path + MAXPATHLEN; |
1660 | *--bp = '\0'; |
1661 | |
1662 | /* |
1663 | * XXX: kludge to avoid locking against ourselves |
1664 | * in getcwd() |
1665 | */ |
1666 | if (vxp->v_tag == VT_PROCFS) { |
1667 | *--bp = '/'; |
1668 | } else { |
1669 | rw_enter(&curproc->p_cwdi->cwdi_lock, |
1670 | RW_READER); |
1671 | vp = curproc->p_cwdi->cwdi_rdir; |
1672 | if (vp == NULL) |
1673 | vp = rootvnode; |
1674 | error = getcwd_common(vxp, vp, &bp, path, |
1675 | MAXPATHLEN / 2, 0, curlwp); |
1676 | rw_exit(&curproc->p_cwdi->cwdi_lock); |
1677 | } |
1678 | if (error) |
1679 | break; |
1680 | len = strlen(bp); |
1681 | break; |
1682 | |
1683 | case DTYPE_MISC: |
1684 | len = snprintf(bf, sizeof(bf), "%s" , "[misc]" ); |
1685 | break; |
1686 | |
1687 | case DTYPE_KQUEUE: |
1688 | len = snprintf(bf, sizeof(bf), "%s" , "[kqueue]" ); |
1689 | break; |
1690 | |
1691 | case DTYPE_SEM: |
1692 | len = snprintf(bf, sizeof(bf), "%s" , "[ksem]" ); |
1693 | break; |
1694 | |
1695 | default: |
1696 | error = EINVAL; |
1697 | break; |
1698 | } |
1699 | closef(fp); |
1700 | procfs_proc_unlock(pown); |
1701 | } |
1702 | |
1703 | if (error == 0) |
1704 | error = uiomove(bp, len, ap->a_uio); |
1705 | if (path) |
1706 | free(path, M_TEMP); |
1707 | return error; |
1708 | } |
1709 | |
1710 | /* |
1711 | * convert decimal ascii to int |
1712 | */ |
1713 | static int |
1714 | atoi(const char *b, size_t len) |
1715 | { |
1716 | int p = 0; |
1717 | |
1718 | while (len--) { |
1719 | char c = *b++; |
1720 | if (c < '0' || c > '9') |
1721 | return -1; |
1722 | p = 10 * p + (c - '0'); |
1723 | } |
1724 | |
1725 | return p; |
1726 | } |
1727 | |