procfs_vfsops.c revision 1.111.4.3 1 /* $NetBSD: procfs_vfsops.c,v 1.111.4.3 2024/09/16 08:42:20 martin 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.111.4.3 2024/09/16 08:42:20 martin Exp $");
80
81 #if defined(_KERNEL_OPT)
82 #include "opt_compat_netbsd.h"
83 #include "opt_sysv_ipc.h"
84 #endif
85
86 #include <sys/param.h>
87 #include <sys/atomic.h>
88 #include <sys/buf.h>
89 #include <sys/dirent.h>
90 #include <sys/file.h>
91 #include <sys/filedesc.h>
92 #include <sys/fstrans.h>
93 #include <sys/kauth.h>
94 #include <sys/kernel.h>
95 #include <sys/module.h>
96 #include <sys/mount.h>
97 #include <sys/proc.h>
98 #include <sys/signalvar.h>
99 #include <sys/sysctl.h>
100 #include <sys/syslog.h>
101 #include <sys/systm.h>
102 #include <sys/time.h>
103 #include <sys/vnode.h>
104
105 #include <miscfs/genfs/genfs.h>
106
107 #include <miscfs/procfs/procfs.h>
108
109 #include <uvm/uvm_extern.h> /* for PAGE_SIZE */
110
111 MODULE(MODULE_CLASS_VFS, procfs, "ptrace_common"
112 #if defined(SYSVSHM) || defined(SYSVSEM) || defined(SYSVMSG)
113 ",sysv_ipc"
114 #endif
115 );
116
117 VFS_PROTOS(procfs);
118
119 #define PROCFS_HASHSIZE 256
120 #define PROCFS_EXEC_HOOK ((void *)1)
121 #define PROCFS_EXIT_HOOK ((void *)2)
122
123 static kauth_listener_t procfs_listener;
124 static void *procfs_exechook;
125 static void *procfs_exithook;
126 LIST_HEAD(hashhead, pfsnode);
127 static u_long procfs_hashmask;
128 static struct hashhead *procfs_hashtab;
129 static kmutex_t procfs_hashlock;
130
131 static struct hashhead *
132 procfs_hashhead(pid_t pid)
133 {
134
135 return &procfs_hashtab[pid & procfs_hashmask];
136 }
137
138 void
139 procfs_hashrem(struct pfsnode *pfs)
140 {
141
142 mutex_enter(&procfs_hashlock);
143 LIST_REMOVE(pfs, pfs_hash);
144 mutex_exit(&procfs_hashlock);
145 }
146
147 /*
148 * VFS Operations.
149 *
150 * mount system call
151 */
152 /* ARGSUSED */
153 int
154 procfs_mount(
155 struct mount *mp,
156 const char *path,
157 void *data,
158 size_t *data_len)
159 {
160 struct lwp *l = curlwp;
161 struct procfsmount *pmnt;
162 struct procfs_args *args = data;
163 int error;
164
165 if (args == NULL)
166 return EINVAL;
167
168 if (UIO_MX & (UIO_MX-1)) {
169 log(LOG_ERR, "procfs: invalid directory entry size");
170 return (EINVAL);
171 }
172
173 if (mp->mnt_flag & MNT_GETARGS) {
174 if (*data_len < sizeof *args)
175 return EINVAL;
176
177 pmnt = VFSTOPROC(mp);
178 if (pmnt == NULL)
179 return EIO;
180 args->version = PROCFS_ARGSVERSION;
181 args->flags = pmnt->pmnt_flags;
182 *data_len = sizeof *args;
183 return 0;
184 }
185
186 if (mp->mnt_flag & MNT_UPDATE)
187 return (EOPNOTSUPP);
188
189 if (*data_len >= sizeof *args && args->version != PROCFS_ARGSVERSION)
190 return EINVAL;
191
192 pmnt = kmem_zalloc(sizeof(struct procfsmount), KM_SLEEP);
193
194 mp->mnt_stat.f_namemax = PROCFS_MAXNAMLEN;
195 mp->mnt_flag |= MNT_LOCAL;
196 mp->mnt_data = pmnt;
197 vfs_getnewfsid(mp);
198
199 error = set_statvfs_info(path, UIO_USERSPACE, "procfs", UIO_SYSSPACE,
200 mp->mnt_op->vfs_name, mp, l);
201 if (*data_len >= sizeof *args)
202 pmnt->pmnt_flags = args->flags;
203 else
204 pmnt->pmnt_flags = 0;
205
206 mp->mnt_iflag |= IMNT_MPSAFE | IMNT_SHRLOOKUP;
207 return error;
208 }
209
210 /*
211 * unmount system call
212 */
213 int
214 procfs_unmount(struct mount *mp, int mntflags)
215 {
216 int error;
217 int flags = 0;
218
219 if (mntflags & MNT_FORCE)
220 flags |= FORCECLOSE;
221
222 if ((error = vflush(mp, 0, flags)) != 0)
223 return (error);
224
225 kmem_free(mp->mnt_data, sizeof(struct procfsmount));
226 mp->mnt_data = NULL;
227
228 return 0;
229 }
230
231 int
232 procfs_root(struct mount *mp, int lktype, struct vnode **vpp)
233 {
234 int error;
235
236 error = procfs_allocvp(mp, vpp, 0, PFSroot, -1);
237 if (error == 0) {
238 error = vn_lock(*vpp, lktype);
239 if (error != 0) {
240 vrele(*vpp);
241 *vpp = NULL;
242 }
243 }
244
245 return error;
246 }
247
248 /* ARGSUSED */
249 int
250 procfs_start(struct mount *mp, int flags)
251 {
252
253 return (0);
254 }
255
256 /*
257 * Get file system statistics.
258 */
259 int
260 procfs_statvfs(struct mount *mp, struct statvfs *sbp)
261 {
262
263 genfs_statvfs(mp, sbp);
264
265 sbp->f_bsize = PAGE_SIZE;
266 sbp->f_frsize = PAGE_SIZE;
267 sbp->f_iosize = PAGE_SIZE;
268 sbp->f_blocks = 1;
269 sbp->f_files = maxproc; /* approx */
270 sbp->f_ffree = maxproc - atomic_load_relaxed(&nprocs); /* approx */
271 sbp->f_favail = maxproc - atomic_load_relaxed(&nprocs); /* approx */
272
273 return (0);
274 }
275
276 /*ARGSUSED*/
277 int
278 procfs_sync(
279 struct mount *mp,
280 int waitfor,
281 kauth_cred_t uc)
282 {
283
284 return (0);
285 }
286
287 /*ARGSUSED*/
288 int
289 procfs_vget(struct mount *mp, ino_t ino, int lktype,
290 struct vnode **vpp)
291 {
292 return (EOPNOTSUPP);
293 }
294
295 int
296 procfs_loadvnode(struct mount *mp, struct vnode *vp,
297 const void *key, size_t key_len, const void **new_key)
298 {
299 int error;
300 struct pfskey pfskey;
301 struct pfsnode *pfs;
302
303 KASSERT(key_len == sizeof(pfskey));
304 memcpy(&pfskey, key, key_len);
305
306 pfs = kmem_alloc(sizeof(*pfs), KM_SLEEP);
307 pfs->pfs_pid = pfskey.pk_pid;
308 pfs->pfs_type = pfskey.pk_type;
309 pfs->pfs_fd = pfskey.pk_fd;
310 pfs->pfs_vnode = vp;
311 pfs->pfs_mount = mp;
312 pfs->pfs_flags = 0;
313 pfs->pfs_fileno =
314 PROCFS_FILENO(pfs->pfs_pid, pfs->pfs_type, pfs->pfs_fd);
315 vp->v_tag = VT_PROCFS;
316 vp->v_op = procfs_vnodeop_p;
317 vp->v_data = pfs;
318
319 switch (pfs->pfs_type) {
320 case PFSroot: /* /proc = dr-xr-xr-x */
321 vp->v_vflag |= VV_ROOT;
322 /*FALLTHROUGH*/
323 case PFSproc: /* /proc/N = dr-xr-xr-x */
324 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
325 vp->v_type = VDIR;
326 break;
327
328 case PFStask: /* /proc/N/task = dr-xr-xr-x */
329 if (pfs->pfs_fd == -1) {
330 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|
331 S_IROTH|S_IXOTH;
332 vp->v_type = VDIR;
333 break;
334 }
335 /*FALLTHROUGH*/
336 case PFScurproc: /* /proc/curproc = lr-xr-xr-x */
337 case PFSself: /* /proc/self = lr-xr-xr-x */
338 case PFScwd: /* /proc/N/cwd = lr-xr-xr-x */
339 case PFSchroot: /* /proc/N/chroot = lr-xr-xr-x */
340 case PFSexe: /* /proc/N/exe = lr-xr-xr-x */
341 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
342 vp->v_type = VLNK;
343 break;
344
345 case PFSfd:
346 if (pfs->pfs_fd == -1) { /* /proc/N/fd = dr-x------ */
347 pfs->pfs_mode = S_IRUSR|S_IXUSR;
348 vp->v_type = VDIR;
349 } else { /* /proc/N/fd/M = [ps-]rw------- */
350 file_t *fp;
351 vnode_t *vxp;
352 struct proc *p;
353
354 mutex_enter(&proc_lock);
355 p = procfs_proc_find(mp, pfs->pfs_pid);
356 mutex_exit(&proc_lock);
357 if (p == NULL) {
358 error = ENOENT;
359 goto bad;
360 }
361 KASSERT(rw_read_held(&p->p_reflock));
362 if ((fp = fd_getfile2(p, pfs->pfs_fd)) == NULL) {
363 error = EBADF;
364 goto bad;
365 }
366
367 pfs->pfs_mode = S_IRUSR|S_IWUSR;
368 switch (fp->f_type) {
369 case DTYPE_VNODE:
370 vxp = fp->f_vnode;
371
372 /*
373 * We make symlinks for directories
374 * to avoid cycles.
375 */
376 if (vxp->v_type == VDIR ||
377 procfs_proc_is_linux_compat())
378 goto symlink;
379 vp->v_type = vxp->v_type;
380 break;
381 case DTYPE_PIPE:
382 vp->v_type = VFIFO;
383 break;
384 case DTYPE_SOCKET:
385 vp->v_type = VSOCK;
386 break;
387 case DTYPE_KQUEUE:
388 case DTYPE_MISC:
389 case DTYPE_SEM:
390 symlink:
391 pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|
392 S_IXGRP|S_IROTH|S_IXOTH;
393 vp->v_type = VLNK;
394 break;
395 default:
396 error = EOPNOTSUPP;
397 closef(fp);
398 goto bad;
399 }
400 closef(fp);
401 }
402 break;
403
404 case PFSfile: /* /proc/N/file = -rw------- */
405 case PFSmem: /* /proc/N/mem = -rw------- */
406 case PFSregs: /* /proc/N/regs = -rw------- */
407 case PFSfpregs: /* /proc/N/fpregs = -rw------- */
408 pfs->pfs_mode = S_IRUSR|S_IWUSR;
409 vp->v_type = VREG;
410 break;
411
412 case PFSnote: /* /proc/N/note = --w------ */
413 case PFSnotepg: /* /proc/N/notepg = --w------ */
414 pfs->pfs_mode = S_IWUSR;
415 vp->v_type = VREG;
416 break;
417
418 case PFSmap: /* /proc/N/map = -r-------- */
419 case PFSmaps: /* /proc/N/maps = -r-------- */
420 case PFSauxv: /* /proc/N/auxv = -r-------- */
421 case PFSenviron: /* /proc/N/environ = -r-------- */
422 pfs->pfs_mode = S_IRUSR;
423 vp->v_type = VREG;
424 break;
425
426 case PFSstatus: /* /proc/N/status = -r--r--r-- */
427 case PFSstat: /* /proc/N/stat = -r--r--r-- */
428 case PFScmdline: /* /proc/N/cmdline = -r--r--r-- */
429 case PFSemul: /* /proc/N/emul = -r--r--r-- */
430 case PFSmeminfo: /* /proc/meminfo = -r--r--r-- */
431 case PFScpustat: /* /proc/stat = -r--r--r-- */
432 case PFSdevices: /* /proc/devices = -r--r--r-- */
433 case PFScpuinfo: /* /proc/cpuinfo = -r--r--r-- */
434 case PFSuptime: /* /proc/uptime = -r--r--r-- */
435 case PFSmounts: /* /proc/mounts = -r--r--r-- */
436 case PFSloadavg: /* /proc/loadavg = -r--r--r-- */
437 case PFSstatm: /* /proc/N/statm = -r--r--r-- */
438 case PFSversion: /* /proc/version = -r--r--r-- */
439 case PFSlimit: /* /proc/limit = -r--r--r-- */
440 pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH;
441 vp->v_type = VREG;
442 break;
443
444 #ifdef __HAVE_PROCFS_MACHDEP
445 PROCFS_MACHDEP_NODETYPE_CASES
446 procfs_machdep_allocvp(vp);
447 break;
448 #endif
449
450 default:
451 panic("procfs_allocvp");
452 }
453
454 mutex_enter(&procfs_hashlock);
455 LIST_INSERT_HEAD(procfs_hashhead(pfs->pfs_pid), pfs, pfs_hash);
456 mutex_exit(&procfs_hashlock);
457
458 uvm_vnp_setsize(vp, 0);
459 *new_key = &pfs->pfs_key;
460
461 return 0;
462
463 bad:
464 vp->v_tag =VT_NON;
465 vp->v_type = VNON;
466 vp->v_op = NULL;
467 vp->v_data = NULL;
468 kmem_free(pfs, sizeof(*pfs));
469 return error;
470 }
471
472 void
473 procfs_init(void)
474 {
475
476 }
477
478 void
479 procfs_reinit(void)
480 {
481
482 }
483
484 void
485 procfs_done(void)
486 {
487
488 }
489
490 extern const struct vnodeopv_desc procfs_vnodeop_opv_desc;
491
492 const struct vnodeopv_desc * const procfs_vnodeopv_descs[] = {
493 &procfs_vnodeop_opv_desc,
494 NULL,
495 };
496
497 struct vfsops procfs_vfsops = {
498 .vfs_name = MOUNT_PROCFS,
499 .vfs_min_mount_data = sizeof (struct procfs_args),
500 .vfs_mount = procfs_mount,
501 .vfs_start = procfs_start,
502 .vfs_unmount = procfs_unmount,
503 .vfs_root = procfs_root,
504 .vfs_quotactl = (void *)eopnotsupp,
505 .vfs_statvfs = procfs_statvfs,
506 .vfs_sync = procfs_sync,
507 .vfs_vget = procfs_vget,
508 .vfs_loadvnode = procfs_loadvnode,
509 .vfs_fhtovp = (void *)eopnotsupp,
510 .vfs_vptofh = (void *)eopnotsupp,
511 .vfs_init = procfs_init,
512 .vfs_reinit = procfs_reinit,
513 .vfs_done = procfs_done,
514 .vfs_snapshot = (void *)eopnotsupp,
515 .vfs_extattrctl = vfs_stdextattrctl,
516 .vfs_suspendctl = genfs_suspendctl,
517 .vfs_renamelock_enter = genfs_renamelock_enter,
518 .vfs_renamelock_exit = genfs_renamelock_exit,
519 .vfs_fsync = (void *)eopnotsupp,
520 .vfs_opv_descs = procfs_vnodeopv_descs
521 };
522
523 static void
524 procfs_exechook_cb(struct proc *p, void *arg)
525 {
526 struct hashhead *head;
527 struct pfsnode *pfs;
528 struct mount *mp;
529 struct pfskey key;
530 struct vnode *vp;
531 int error;
532
533 if (arg == PROCFS_EXEC_HOOK && !(p->p_flag & PK_SUGID))
534 return;
535
536 head = procfs_hashhead(p->p_pid);
537
538 again:
539 mutex_enter(&procfs_hashlock);
540 LIST_FOREACH(pfs, head, pfs_hash) {
541 if (pfs->pfs_pid != p->p_pid)
542 continue;
543 mp = pfs->pfs_mount;
544 key = pfs->pfs_key;
545 vfs_ref(mp);
546 mutex_exit(&procfs_hashlock);
547
548 error = vcache_get(mp, &key, sizeof(key), &vp);
549 vfs_rele(mp);
550 if (error != 0)
551 goto again;
552 if (vrecycle(vp))
553 goto again;
554 do {
555 error = vfs_suspend(mp, 0);
556 } while (error == EINTR || error == ERESTART);
557 vgone(vp);
558 if (error == 0)
559 vfs_resume(mp);
560 goto again;
561 }
562 mutex_exit(&procfs_hashlock);
563 }
564
565 static int
566 procfs_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
567 void *arg0, void *arg1, void *arg2, void *arg3)
568 {
569 struct proc *p;
570 struct pfsnode *pfs;
571 int result;
572
573 result = KAUTH_RESULT_DEFER;
574 p = arg0;
575 pfs = arg1;
576
577 if (action != KAUTH_PROCESS_PROCFS)
578 return result;
579
580 switch (pfs->pfs_type) {
581 case PFSregs:
582 case PFSfpregs:
583 case PFSmem:
584 if (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) ||
585 ISSET(p->p_flag, PK_SUGID))
586 break;
587
588 /*FALLTHROUGH*/
589 default:
590 result = KAUTH_RESULT_ALLOW;
591 break;
592 }
593
594 return result;
595 }
596
597 SYSCTL_SETUP(procfs_sysctl_setup, "procfs sysctl")
598 {
599
600 sysctl_createv(clog, 0, NULL, NULL,
601 CTLFLAG_PERMANENT,
602 CTLTYPE_NODE, "procfs",
603 SYSCTL_DESCR("Process file system"),
604 NULL, 0, NULL, 0,
605 CTL_VFS, 12, CTL_EOL);
606 /*
607 * XXX the "12" above could be dynamic, thereby eliminating
608 * one more instance of the "number to vfs" mapping problem,
609 * but "12" is the order as taken from sys/mount.h
610 */
611 }
612
613 static int
614 procfs_modcmd(modcmd_t cmd, void *arg)
615 {
616 int error;
617
618 switch (cmd) {
619 case MODULE_CMD_INIT:
620 error = vfs_attach(&procfs_vfsops);
621 if (error != 0)
622 break;
623
624 procfs_listener = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
625 procfs_listener_cb, NULL);
626
627 procfs_exechook = exechook_establish(procfs_exechook_cb,
628 PROCFS_EXEC_HOOK);
629 procfs_exithook = exithook_establish(procfs_exechook_cb,
630 PROCFS_EXIT_HOOK);
631
632 mutex_init(&procfs_hashlock, MUTEX_DEFAULT, IPL_NONE);
633 procfs_hashtab = hashinit(PROCFS_HASHSIZE, HASH_LIST, true,
634 &procfs_hashmask);
635
636 break;
637 case MODULE_CMD_FINI:
638 error = vfs_detach(&procfs_vfsops);
639 if (error != 0)
640 break;
641 kauth_unlisten_scope(procfs_listener);
642 exechook_disestablish(procfs_exechook);
643 exithook_disestablish(procfs_exithook);
644 mutex_destroy(&procfs_hashlock);
645 hashdone(procfs_hashtab, HASH_LIST, procfs_hashmask);
646 break;
647 default:
648 error = ENOTTY;
649 break;
650 }
651
652 return (error);
653 }
654