1 | /* $NetBSD: procfs_vfsops.c,v 1.95 2016/11/03 03:53:32 pgoyette Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1993 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to Berkeley by |
8 | * Jan-Simon Pendry. |
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 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. |
33 | * |
34 | * @(#)procfs_vfsops.c 8.7 (Berkeley) 5/10/95 |
35 | */ |
36 | |
37 | /* |
38 | * Copyright (c) 1993 Jan-Simon Pendry |
39 | * |
40 | * This code is derived from software contributed to Berkeley by |
41 | * Jan-Simon Pendry. |
42 | * |
43 | * Redistribution and use in source and binary forms, with or without |
44 | * modification, are permitted provided that the following conditions |
45 | * are met: |
46 | * 1. Redistributions of source code must retain the above copyright |
47 | * notice, this list of conditions and the following disclaimer. |
48 | * 2. Redistributions in binary form must reproduce the above copyright |
49 | * notice, this list of conditions and the following disclaimer in the |
50 | * documentation and/or other materials provided with the distribution. |
51 | * 3. All advertising materials mentioning features or use of this software |
52 | * must display the following acknowledgement: |
53 | * This product includes software developed by the University of |
54 | * California, Berkeley and its contributors. |
55 | * 4. Neither the name of the University nor the names of its contributors |
56 | * may be used to endorse or promote products derived from this software |
57 | * without specific prior written permission. |
58 | * |
59 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
60 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
61 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
62 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
63 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
64 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
65 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
66 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
67 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
68 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
69 | * SUCH DAMAGE. |
70 | * |
71 | * @(#)procfs_vfsops.c 8.7 (Berkeley) 5/10/95 |
72 | */ |
73 | |
74 | /* |
75 | * procfs VFS interface |
76 | */ |
77 | |
78 | #include <sys/cdefs.h> |
79 | __KERNEL_RCSID(0, "$NetBSD: procfs_vfsops.c,v 1.95 2016/11/03 03:53:32 pgoyette Exp $" ); |
80 | |
81 | #if defined(_KERNEL_OPT) |
82 | #include "opt_compat_netbsd.h" |
83 | #endif |
84 | |
85 | #include <sys/param.h> |
86 | #include <sys/time.h> |
87 | #include <sys/kernel.h> |
88 | #include <sys/systm.h> |
89 | #include <sys/sysctl.h> |
90 | #include <sys/proc.h> |
91 | #include <sys/buf.h> |
92 | #include <sys/syslog.h> |
93 | #include <sys/mount.h> |
94 | #include <sys/dirent.h> |
95 | #include <sys/signalvar.h> |
96 | #include <sys/vnode.h> |
97 | #include <sys/file.h> |
98 | #include <sys/filedesc.h> |
99 | #include <sys/kauth.h> |
100 | #include <sys/module.h> |
101 | |
102 | #include <miscfs/genfs/genfs.h> |
103 | |
104 | #include <miscfs/procfs/procfs.h> |
105 | |
106 | #include <uvm/uvm_extern.h> /* for PAGE_SIZE */ |
107 | |
108 | MODULE(MODULE_CLASS_VFS, procfs, "ptrace_common" ); |
109 | |
110 | VFS_PROTOS(procfs); |
111 | |
112 | static struct sysctllog *procfs_sysctl_log; |
113 | |
114 | static kauth_listener_t procfs_listener; |
115 | |
116 | /* |
117 | * VFS Operations. |
118 | * |
119 | * mount system call |
120 | */ |
121 | /* ARGSUSED */ |
122 | int |
123 | procfs_mount( |
124 | struct mount *mp, |
125 | const char *path, |
126 | void *data, |
127 | size_t *data_len) |
128 | { |
129 | struct lwp *l = curlwp; |
130 | struct procfsmount *pmnt; |
131 | struct procfs_args *args = data; |
132 | int error; |
133 | |
134 | if (args == NULL) |
135 | return EINVAL; |
136 | |
137 | if (UIO_MX & (UIO_MX-1)) { |
138 | log(LOG_ERR, "procfs: invalid directory entry size" ); |
139 | return (EINVAL); |
140 | } |
141 | |
142 | if (mp->mnt_flag & MNT_GETARGS) { |
143 | if (*data_len < sizeof *args) |
144 | return EINVAL; |
145 | |
146 | pmnt = VFSTOPROC(mp); |
147 | if (pmnt == NULL) |
148 | return EIO; |
149 | args->version = PROCFS_ARGSVERSION; |
150 | args->flags = pmnt->pmnt_flags; |
151 | *data_len = sizeof *args; |
152 | return 0; |
153 | } |
154 | |
155 | if (mp->mnt_flag & MNT_UPDATE) |
156 | return (EOPNOTSUPP); |
157 | |
158 | if (*data_len >= sizeof *args && args->version != PROCFS_ARGSVERSION) |
159 | return EINVAL; |
160 | |
161 | pmnt = kmem_zalloc(sizeof(struct procfsmount), KM_SLEEP); |
162 | |
163 | mp->mnt_stat.f_namemax = PROCFS_MAXNAMLEN; |
164 | mp->mnt_flag |= MNT_LOCAL; |
165 | mp->mnt_data = pmnt; |
166 | vfs_getnewfsid(mp); |
167 | |
168 | error = set_statvfs_info(path, UIO_USERSPACE, "procfs" , UIO_SYSSPACE, |
169 | mp->mnt_op->vfs_name, mp, l); |
170 | pmnt->pmnt_exechook = exechook_establish(procfs_revoke_vnodes, mp); |
171 | if (*data_len >= sizeof *args) |
172 | pmnt->pmnt_flags = args->flags; |
173 | else |
174 | pmnt->pmnt_flags = 0; |
175 | |
176 | mp->mnt_iflag |= IMNT_MPSAFE; |
177 | return error; |
178 | } |
179 | |
180 | /* |
181 | * unmount system call |
182 | */ |
183 | int |
184 | procfs_unmount(struct mount *mp, int mntflags) |
185 | { |
186 | int error; |
187 | int flags = 0; |
188 | |
189 | if (mntflags & MNT_FORCE) |
190 | flags |= FORCECLOSE; |
191 | |
192 | if ((error = vflush(mp, 0, flags)) != 0) |
193 | return (error); |
194 | |
195 | exechook_disestablish(VFSTOPROC(mp)->pmnt_exechook); |
196 | |
197 | kmem_free(mp->mnt_data, sizeof(struct procfsmount)); |
198 | mp->mnt_data = NULL; |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | int |
204 | procfs_root(struct mount *mp, struct vnode **vpp) |
205 | { |
206 | int error; |
207 | |
208 | error = procfs_allocvp(mp, vpp, 0, PFSroot, -1); |
209 | if (error == 0) { |
210 | error = vn_lock(*vpp, LK_EXCLUSIVE); |
211 | if (error != 0) { |
212 | vrele(*vpp); |
213 | *vpp = NULL; |
214 | } |
215 | } |
216 | |
217 | return error; |
218 | } |
219 | |
220 | /* ARGSUSED */ |
221 | int |
222 | procfs_start(struct mount *mp, int flags) |
223 | { |
224 | |
225 | return (0); |
226 | } |
227 | |
228 | /* |
229 | * Get file system statistics. |
230 | */ |
231 | int |
232 | procfs_statvfs(struct mount *mp, struct statvfs *sbp) |
233 | { |
234 | |
235 | genfs_statvfs(mp, sbp); |
236 | |
237 | sbp->f_bsize = PAGE_SIZE; |
238 | sbp->f_frsize = PAGE_SIZE; |
239 | sbp->f_iosize = PAGE_SIZE; |
240 | sbp->f_blocks = 1; |
241 | sbp->f_files = maxproc; /* approx */ |
242 | sbp->f_ffree = maxproc - nprocs; /* approx */ |
243 | sbp->f_favail = maxproc - nprocs; /* approx */ |
244 | |
245 | return (0); |
246 | } |
247 | |
248 | /*ARGSUSED*/ |
249 | int |
250 | procfs_sync( |
251 | struct mount *mp, |
252 | int waitfor, |
253 | kauth_cred_t uc) |
254 | { |
255 | |
256 | return (0); |
257 | } |
258 | |
259 | /*ARGSUSED*/ |
260 | int |
261 | procfs_vget(struct mount *mp, ino_t ino, |
262 | struct vnode **vpp) |
263 | { |
264 | return (EOPNOTSUPP); |
265 | } |
266 | |
267 | int |
268 | procfs_loadvnode(struct mount *mp, struct vnode *vp, |
269 | const void *key, size_t key_len, const void **new_key) |
270 | { |
271 | int error; |
272 | struct pfskey pfskey; |
273 | struct pfsnode *pfs; |
274 | |
275 | KASSERT(key_len == sizeof(pfskey)); |
276 | memcpy(&pfskey, key, key_len); |
277 | |
278 | pfs = kmem_alloc(sizeof(*pfs), KM_SLEEP); |
279 | pfs->pfs_pid = pfskey.pk_pid; |
280 | pfs->pfs_type = pfskey.pk_type; |
281 | pfs->pfs_fd = pfskey.pk_fd; |
282 | pfs->pfs_vnode = vp; |
283 | pfs->pfs_flags = 0; |
284 | pfs->pfs_fileno = |
285 | PROCFS_FILENO(pfs->pfs_pid, pfs->pfs_type, pfs->pfs_fd); |
286 | vp->v_tag = VT_PROCFS; |
287 | vp->v_op = procfs_vnodeop_p; |
288 | vp->v_data = pfs; |
289 | |
290 | switch (pfs->pfs_type) { |
291 | case PFSroot: /* /proc = dr-xr-xr-x */ |
292 | vp->v_vflag |= VV_ROOT; |
293 | /*FALLTHROUGH*/ |
294 | case PFSproc: /* /proc/N = dr-xr-xr-x */ |
295 | pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; |
296 | vp->v_type = VDIR; |
297 | break; |
298 | |
299 | case PFStask: /* /proc/N/task = dr-xr-xr-x */ |
300 | if (pfs->pfs_fd == -1) { |
301 | pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP| |
302 | S_IROTH|S_IXOTH; |
303 | vp->v_type = VDIR; |
304 | break; |
305 | } |
306 | /*FALLTHROUGH*/ |
307 | case PFScurproc: /* /proc/curproc = lr-xr-xr-x */ |
308 | case PFSself: /* /proc/self = lr-xr-xr-x */ |
309 | case PFScwd: /* /proc/N/cwd = lr-xr-xr-x */ |
310 | case PFSchroot: /* /proc/N/chroot = lr-xr-xr-x */ |
311 | case PFSexe: /* /proc/N/exe = lr-xr-xr-x */ |
312 | pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; |
313 | vp->v_type = VLNK; |
314 | break; |
315 | |
316 | case PFSfd: |
317 | if (pfs->pfs_fd == -1) { /* /proc/N/fd = dr-x------ */ |
318 | pfs->pfs_mode = S_IRUSR|S_IXUSR; |
319 | vp->v_type = VDIR; |
320 | } else { /* /proc/N/fd/M = [ps-]rw------- */ |
321 | file_t *fp; |
322 | vnode_t *vxp; |
323 | struct proc *p; |
324 | |
325 | mutex_enter(proc_lock); |
326 | p = proc_find(pfs->pfs_pid); |
327 | mutex_exit(proc_lock); |
328 | if (p == NULL) { |
329 | error = ENOENT; |
330 | goto bad; |
331 | } |
332 | KASSERT(rw_read_held(&p->p_reflock)); |
333 | if ((fp = fd_getfile2(p, pfs->pfs_fd)) == NULL) { |
334 | error = EBADF; |
335 | goto bad; |
336 | } |
337 | |
338 | pfs->pfs_mode = S_IRUSR|S_IWUSR; |
339 | switch (fp->f_type) { |
340 | case DTYPE_VNODE: |
341 | vxp = fp->f_vnode; |
342 | |
343 | /* |
344 | * We make symlinks for directories |
345 | * to avoid cycles. |
346 | */ |
347 | if (vxp->v_type == VDIR) |
348 | goto symlink; |
349 | vp->v_type = vxp->v_type; |
350 | break; |
351 | case DTYPE_PIPE: |
352 | vp->v_type = VFIFO; |
353 | break; |
354 | case DTYPE_SOCKET: |
355 | vp->v_type = VSOCK; |
356 | break; |
357 | case DTYPE_KQUEUE: |
358 | case DTYPE_MISC: |
359 | case DTYPE_SEM: |
360 | symlink: |
361 | pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP| |
362 | S_IXGRP|S_IROTH|S_IXOTH; |
363 | vp->v_type = VLNK; |
364 | break; |
365 | default: |
366 | error = EOPNOTSUPP; |
367 | closef(fp); |
368 | goto bad; |
369 | } |
370 | closef(fp); |
371 | } |
372 | break; |
373 | |
374 | case PFSfile: /* /proc/N/file = -rw------- */ |
375 | case PFSmem: /* /proc/N/mem = -rw------- */ |
376 | case PFSregs: /* /proc/N/regs = -rw------- */ |
377 | case PFSfpregs: /* /proc/N/fpregs = -rw------- */ |
378 | pfs->pfs_mode = S_IRUSR|S_IWUSR; |
379 | vp->v_type = VREG; |
380 | break; |
381 | |
382 | case PFSctl: /* /proc/N/ctl = --w------ */ |
383 | case PFSnote: /* /proc/N/note = --w------ */ |
384 | case PFSnotepg: /* /proc/N/notepg = --w------ */ |
385 | pfs->pfs_mode = S_IWUSR; |
386 | vp->v_type = VREG; |
387 | break; |
388 | |
389 | case PFSmap: /* /proc/N/map = -r--r--r-- */ |
390 | case PFSmaps: /* /proc/N/maps = -r--r--r-- */ |
391 | case PFSstatus: /* /proc/N/status = -r--r--r-- */ |
392 | case PFSstat: /* /proc/N/stat = -r--r--r-- */ |
393 | case PFScmdline: /* /proc/N/cmdline = -r--r--r-- */ |
394 | case PFSemul: /* /proc/N/emul = -r--r--r-- */ |
395 | case PFSmeminfo: /* /proc/meminfo = -r--r--r-- */ |
396 | case PFScpustat: /* /proc/stat = -r--r--r-- */ |
397 | case PFSdevices: /* /proc/devices = -r--r--r-- */ |
398 | case PFScpuinfo: /* /proc/cpuinfo = -r--r--r-- */ |
399 | case PFSuptime: /* /proc/uptime = -r--r--r-- */ |
400 | case PFSmounts: /* /proc/mounts = -r--r--r-- */ |
401 | case PFSloadavg: /* /proc/loadavg = -r--r--r-- */ |
402 | case PFSstatm: /* /proc/N/statm = -r--r--r-- */ |
403 | case PFSversion: /* /proc/version = -r--r--r-- */ |
404 | pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH; |
405 | vp->v_type = VREG; |
406 | break; |
407 | |
408 | #ifdef __HAVE_PROCFS_MACHDEP |
409 | PROCFS_MACHDEP_NODETYPE_CASES |
410 | procfs_machdep_allocvp(vp); |
411 | break; |
412 | #endif |
413 | |
414 | default: |
415 | panic("procfs_allocvp" ); |
416 | } |
417 | |
418 | uvm_vnp_setsize(vp, 0); |
419 | *new_key = &pfs->pfs_key; |
420 | |
421 | return 0; |
422 | |
423 | bad: |
424 | vp->v_tag =VT_NON; |
425 | vp->v_type = VNON; |
426 | vp->v_op = NULL; |
427 | vp->v_data = NULL; |
428 | kmem_free(pfs, sizeof(*pfs)); |
429 | return error; |
430 | } |
431 | |
432 | void |
433 | procfs_init(void) |
434 | { |
435 | |
436 | } |
437 | |
438 | void |
439 | procfs_reinit(void) |
440 | { |
441 | |
442 | } |
443 | |
444 | void |
445 | procfs_done(void) |
446 | { |
447 | |
448 | } |
449 | |
450 | extern const struct vnodeopv_desc procfs_vnodeop_opv_desc; |
451 | |
452 | const struct vnodeopv_desc * const procfs_vnodeopv_descs[] = { |
453 | &procfs_vnodeop_opv_desc, |
454 | NULL, |
455 | }; |
456 | |
457 | struct vfsops procfs_vfsops = { |
458 | .vfs_name = MOUNT_PROCFS, |
459 | .vfs_min_mount_data = sizeof (struct procfs_args), |
460 | .vfs_mount = procfs_mount, |
461 | .vfs_start = procfs_start, |
462 | .vfs_unmount = procfs_unmount, |
463 | .vfs_root = procfs_root, |
464 | .vfs_quotactl = (void *)eopnotsupp, |
465 | .vfs_statvfs = procfs_statvfs, |
466 | .vfs_sync = procfs_sync, |
467 | .vfs_vget = procfs_vget, |
468 | .vfs_loadvnode = procfs_loadvnode, |
469 | .vfs_fhtovp = (void *)eopnotsupp, |
470 | .vfs_vptofh = (void *)eopnotsupp, |
471 | .vfs_init = procfs_init, |
472 | .vfs_reinit = procfs_reinit, |
473 | .vfs_done = procfs_done, |
474 | .vfs_snapshot = (void *)eopnotsupp, |
475 | .vfs_extattrctl = vfs_stdextattrctl, |
476 | .vfs_suspendctl = (void *)eopnotsupp, |
477 | .vfs_renamelock_enter = genfs_renamelock_enter, |
478 | .vfs_renamelock_exit = genfs_renamelock_exit, |
479 | .vfs_fsync = (void *)eopnotsupp, |
480 | .vfs_opv_descs = procfs_vnodeopv_descs |
481 | }; |
482 | |
483 | static int |
484 | procfs_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, |
485 | void *arg0, void *arg1, void *arg2, void *arg3) |
486 | { |
487 | struct proc *p; |
488 | struct pfsnode *pfs; |
489 | enum kauth_process_req req; |
490 | int result; |
491 | |
492 | result = KAUTH_RESULT_DEFER; |
493 | p = arg0; |
494 | pfs = arg1; |
495 | req = (enum kauth_process_req)(unsigned long)arg2; |
496 | |
497 | if (action != KAUTH_PROCESS_PROCFS) |
498 | return result; |
499 | |
500 | /* Privileged; let secmodel handle that. */ |
501 | if (req == KAUTH_REQ_PROCESS_PROCFS_CTL) |
502 | return result; |
503 | |
504 | switch (pfs->pfs_type) { |
505 | case PFSregs: |
506 | case PFSfpregs: |
507 | case PFSmem: |
508 | if (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) || |
509 | ISSET(p->p_flag, PK_SUGID)) |
510 | break; |
511 | |
512 | /*FALLTHROUGH*/ |
513 | default: |
514 | result = KAUTH_RESULT_ALLOW; |
515 | break; |
516 | } |
517 | |
518 | return result; |
519 | } |
520 | |
521 | |
522 | static int |
523 | procfs_modcmd(modcmd_t cmd, void *arg) |
524 | { |
525 | int error; |
526 | |
527 | switch (cmd) { |
528 | case MODULE_CMD_INIT: |
529 | error = vfs_attach(&procfs_vfsops); |
530 | if (error != 0) |
531 | break; |
532 | sysctl_createv(&procfs_sysctl_log, 0, NULL, NULL, |
533 | CTLFLAG_PERMANENT, |
534 | CTLTYPE_NODE, "procfs" , |
535 | SYSCTL_DESCR("Process file system" ), |
536 | NULL, 0, NULL, 0, |
537 | CTL_VFS, 12, CTL_EOL); |
538 | /* |
539 | * XXX the "12" above could be dynamic, thereby eliminating |
540 | * one more instance of the "number to vfs" mapping problem, |
541 | * but "12" is the order as taken from sys/mount.h |
542 | */ |
543 | |
544 | procfs_listener = kauth_listen_scope(KAUTH_SCOPE_PROCESS, |
545 | procfs_listener_cb, NULL); |
546 | |
547 | break; |
548 | case MODULE_CMD_FINI: |
549 | error = vfs_detach(&procfs_vfsops); |
550 | if (error != 0) |
551 | break; |
552 | sysctl_teardown(&procfs_sysctl_log); |
553 | kauth_unlisten_scope(procfs_listener); |
554 | break; |
555 | default: |
556 | error = ENOTTY; |
557 | break; |
558 | } |
559 | |
560 | return (error); |
561 | } |
562 | |