Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * Copyright (c) 1993, 1995 Jan-Simon Pendry | |
3 | * Copyright (c) 1993, 1995 | |
4 | * The Regents of the University of California. All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Jan-Simon Pendry. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
dc71b7ab | 17 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
18 | * may be used to endorse or promote products derived from this software |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | * | |
33 | * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 | |
34 | * | |
35 | * $FreeBSD: src/sys/miscfs/procfs/procfs_vnops.c,v 1.76.2.7 2002/01/22 17:22:59 nectar Exp $ | |
36 | */ | |
37 | ||
38 | /* | |
39 | * procfs vnode interface | |
40 | */ | |
41 | ||
42 | #include <sys/param.h> | |
43 | #include <sys/systm.h> | |
44 | #include <sys/time.h> | |
45 | #include <sys/kernel.h> | |
46 | #include <sys/lock.h> | |
47 | #include <sys/fcntl.h> | |
48 | #include <sys/proc.h> | |
2b3f93ea | 49 | #include <sys/caps.h> |
984263bc MD |
50 | #include <sys/signalvar.h> |
51 | #include <sys/vnode.h> | |
52 | #include <sys/uio.h> | |
53 | #include <sys/mount.h> | |
54 | #include <sys/namei.h> | |
55 | #include <sys/dirent.h> | |
56 | #include <sys/malloc.h> | |
527fddf7 | 57 | #include <sys/reg.h> |
984263bc | 58 | #include <vm/vm_zone.h> |
1f2de5d4 | 59 | #include <vfs/procfs/procfs.h> |
984263bc MD |
60 | #include <sys/pioctl.h> |
61 | ||
e193508c MD |
62 | #include <sys/spinlock2.h> |
63 | ||
dcab9ef0 JS |
64 | #include <machine/limits.h> |
65 | ||
a6ee311a | 66 | static int procfs_access (struct vop_access_args *); |
66a1ddf5 | 67 | static int procfs_badop (struct vop_generic_args *); |
a6ee311a RG |
68 | static int procfs_bmap (struct vop_bmap_args *); |
69 | static int procfs_close (struct vop_close_args *); | |
70 | static int procfs_getattr (struct vop_getattr_args *); | |
71 | static int procfs_inactive (struct vop_inactive_args *); | |
72 | static int procfs_ioctl (struct vop_ioctl_args *); | |
e62afb5f | 73 | static int procfs_lookup (struct vop_old_lookup_args *); |
a6ee311a RG |
74 | static int procfs_open (struct vop_open_args *); |
75 | static int procfs_print (struct vop_print_args *); | |
76 | static int procfs_readdir (struct vop_readdir_args *); | |
77 | static int procfs_readlink (struct vop_readlink_args *); | |
78 | static int procfs_reclaim (struct vop_reclaim_args *); | |
79 | static int procfs_setattr (struct vop_setattr_args *); | |
984263bc | 80 | |
dcab9ef0 JS |
81 | static int procfs_readdir_proc(struct vop_readdir_args *); |
82 | static int procfs_readdir_root(struct vop_readdir_args *); | |
83 | ||
66a1ddf5 MD |
84 | /* |
85 | * procfs vnode operations. | |
86 | */ | |
87 | struct vop_ops procfs_vnode_vops = { | |
88 | .vop_default = vop_defaultop, | |
89 | .vop_access = procfs_access, | |
90 | .vop_advlock = (void *)procfs_badop, | |
91 | .vop_bmap = procfs_bmap, | |
92 | .vop_close = procfs_close, | |
93 | .vop_old_create = (void *)procfs_badop, | |
94 | .vop_getattr = procfs_getattr, | |
95 | .vop_inactive = procfs_inactive, | |
96 | .vop_old_link = (void *)procfs_badop, | |
97 | .vop_old_lookup = procfs_lookup, | |
98 | .vop_old_mkdir = (void *)procfs_badop, | |
99 | .vop_old_mknod = (void *)procfs_badop, | |
100 | .vop_open = procfs_open, | |
101 | .vop_pathconf = vop_stdpathconf, | |
102 | .vop_print = procfs_print, | |
103 | .vop_read = procfs_rw, | |
104 | .vop_readdir = procfs_readdir, | |
105 | .vop_readlink = procfs_readlink, | |
106 | .vop_reclaim = procfs_reclaim, | |
107 | .vop_old_remove = (void *)procfs_badop, | |
108 | .vop_old_rename = (void *)procfs_badop, | |
109 | .vop_old_rmdir = (void *)procfs_badop, | |
110 | .vop_setattr = procfs_setattr, | |
111 | .vop_old_symlink = (void *)procfs_badop, | |
112 | .vop_write = (void *)procfs_rw, | |
113 | .vop_ioctl = procfs_ioctl | |
114 | }; | |
115 | ||
116 | ||
984263bc MD |
117 | /* |
118 | * This is a list of the valid names in the | |
119 | * process-specific sub-directories. It is | |
120 | * used in procfs_lookup and procfs_readdir | |
121 | */ | |
122 | static struct proc_target { | |
123 | u_char pt_type; | |
124 | u_char pt_namlen; | |
125 | char *pt_name; | |
126 | pfstype pt_pfstype; | |
c7e98b2f | 127 | int (*pt_valid) (struct lwp *p); |
984263bc MD |
128 | } proc_targets[] = { |
129 | #define N(s) sizeof(s)-1, s | |
130 | /* name type validp */ | |
131 | { DT_DIR, N("."), Pproc, NULL }, | |
132 | { DT_DIR, N(".."), Proot, NULL }, | |
133 | { DT_REG, N("mem"), Pmem, NULL }, | |
134 | { DT_REG, N("regs"), Pregs, procfs_validregs }, | |
135 | { DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs }, | |
136 | { DT_REG, N("dbregs"), Pdbregs, procfs_validdbregs }, | |
137 | { DT_REG, N("ctl"), Pctl, NULL }, | |
138 | { DT_REG, N("status"), Pstatus, NULL }, | |
139 | { DT_REG, N("note"), Pnote, NULL }, | |
140 | { DT_REG, N("notepg"), Pnotepg, NULL }, | |
141 | { DT_REG, N("map"), Pmap, procfs_validmap }, | |
142 | { DT_REG, N("etype"), Ptype, procfs_validtype }, | |
143 | { DT_REG, N("cmdline"), Pcmdline, NULL }, | |
144 | { DT_REG, N("rlimit"), Prlimit, NULL }, | |
145 | { DT_LNK, N("file"), Pfile, NULL }, | |
f658e60a | 146 | { DT_LNK, N("exe"), Pfile, NULL }, |
984263bc MD |
147 | #undef N |
148 | }; | |
c157ff7a | 149 | static const int nproc_targets = NELEM(proc_targets); |
984263bc | 150 | |
a6ee311a | 151 | static pid_t atopid (const char *, u_int); |
984263bc MD |
152 | |
153 | /* | |
154 | * set things up for doing i/o on | |
155 | * the pfsnode (vp). (vp) is locked | |
156 | * on entry, and should be left locked | |
157 | * on exit. | |
158 | * | |
159 | * for procfs we don't need to do anything | |
160 | * in particular for i/o. all that is done | |
161 | * is to support exclusive open on process | |
162 | * memory images. | |
ac424f9b | 163 | * |
b478fdce SW |
164 | * procfs_open(struct vnode *a_vp, int a_mode, struct ucred *a_cred, |
165 | * struct file *a_fp) | |
984263bc MD |
166 | */ |
167 | static int | |
ac424f9b | 168 | procfs_open(struct vop_open_args *ap) |
984263bc MD |
169 | { |
170 | struct pfsnode *pfs = VTOPFS(ap->a_vp); | |
171 | struct proc *p1, *p2; | |
58c2553a | 172 | int error; |
984263bc | 173 | |
198c0ff2 | 174 | p2 = pfs_pfind(pfs->pfs_pid); |
984263bc MD |
175 | if (p2 == NULL) |
176 | return (ENOENT); | |
58c2553a MD |
177 | if (pfs->pfs_pid && !PRISON_CHECK(ap->a_cred, p2->p_ucred)) { |
178 | error = ENOENT; | |
179 | goto done; | |
180 | } | |
984263bc MD |
181 | |
182 | switch (pfs->pfs_type) { | |
183 | case Pmem: | |
184 | if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) || | |
58c2553a MD |
185 | ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) { |
186 | error = EBUSY; | |
187 | goto done; | |
188 | } | |
984263bc | 189 | |
87de5057 | 190 | p1 = curproc; |
dadab5e9 | 191 | KKASSERT(p1); |
984263bc | 192 | /* Can't trace a process that's currently exec'ing. */ |
4643740a | 193 | if ((p2->p_flags & P_INEXEC) != 0) { |
58c2553a MD |
194 | error = EAGAIN; |
195 | goto done; | |
196 | } | |
197 | if (!CHECKIO(p1, p2) || p_trespass(ap->a_cred, p2->p_ucred)) { | |
198 | error = EPERM; | |
199 | goto done; | |
200 | } | |
984263bc MD |
201 | |
202 | if (ap->a_mode & FWRITE) | |
203 | pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); | |
204 | ||
8ddc6004 | 205 | break; |
984263bc MD |
206 | |
207 | default: | |
208 | break; | |
209 | } | |
58c2553a MD |
210 | error = vop_stdopen(ap); |
211 | done: | |
f44c73be | 212 | pfs_pdone(p2); |
58c2553a | 213 | return error; |
984263bc MD |
214 | } |
215 | ||
216 | /* | |
217 | * close the pfsnode (vp) after doing i/o. | |
218 | * (vp) is not locked on entry or exit. | |
219 | * | |
220 | * nothing to do for procfs other than undo | |
221 | * any exclusive open flag (see _open above). | |
ac424f9b | 222 | * |
87de5057 | 223 | * procfs_close(struct vnode *a_vp, int a_fflag, struct ucred *a_cred) |
984263bc MD |
224 | */ |
225 | static int | |
ac424f9b | 226 | procfs_close(struct vop_close_args *ap) |
984263bc MD |
227 | { |
228 | struct pfsnode *pfs = VTOPFS(ap->a_vp); | |
229 | struct proc *p; | |
230 | ||
12cdc371 MD |
231 | /* |
232 | * Make sure the lock is exclusive for opencount tests | |
233 | */ | |
234 | vn_lock(ap->a_vp, LK_UPGRADE | LK_RETRY); | |
235 | ||
984263bc MD |
236 | switch (pfs->pfs_type) { |
237 | case Pmem: | |
238 | if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) | |
239 | pfs->pfs_flags &= ~(FWRITE|O_EXCL); | |
240 | /* | |
3c37c940 MD |
241 | * v_opencount determines the last real close on the vnode. |
242 | * | |
984263bc MD |
243 | * If this is the last close, then it checks to see if |
244 | * the target process has PF_LINGER set in p_pfsflags, | |
245 | * if this is *not* the case, then the process' stop flags | |
246 | * are cleared, and the process is woken up. This is | |
247 | * to help prevent the case where a process has been | |
248 | * told to stop on an event, but then the requesting process | |
249 | * has gone away or forgotten about it. | |
250 | */ | |
58c2553a | 251 | p = NULL; |
3c37c940 | 252 | if ((ap->a_vp->v_opencount < 2) |
b2a6ad87 MD |
253 | && ((p = pfs_pfind(pfs->pfs_pid)) != NULL || |
254 | (p = pfs_zpfind(pfs->pfs_pid)) != NULL) | |
984263bc | 255 | && !(p->p_pfsflags & PF_LINGER)) { |
287a8577 | 256 | spin_lock(&p->p_spin); |
984263bc MD |
257 | p->p_stops = 0; |
258 | p->p_step = 0; | |
287a8577 | 259 | spin_unlock(&p->p_spin); |
b2a6ad87 | 260 | wakeup(&p->p_stype); |
e0836e94 | 261 | wakeup(&p->p_step); |
984263bc | 262 | } |
f44c73be | 263 | pfs_pdone(p); |
984263bc MD |
264 | break; |
265 | default: | |
266 | break; | |
267 | } | |
268 | ||
8ddc6004 | 269 | return (vop_stdclose(ap)); |
984263bc MD |
270 | } |
271 | ||
272 | /* | |
273 | * do an ioctl operation on a pfsnode (vp). | |
274 | * (vp) is not locked on entry or exit. | |
275 | */ | |
276 | static int | |
ac424f9b | 277 | procfs_ioctl(struct vop_ioctl_args *ap) |
984263bc MD |
278 | { |
279 | struct pfsnode *pfs = VTOPFS(ap->a_vp); | |
dadab5e9 MD |
280 | struct proc *procp; |
281 | struct proc *p; | |
984263bc MD |
282 | int error; |
283 | int signo; | |
284 | struct procfs_status *psp; | |
285 | unsigned char flags; | |
286 | ||
aa7ecbbc | 287 | procp = pfs_pfind(pfs->pfs_pid); |
dadab5e9 | 288 | if (procp == NULL) |
984263bc | 289 | return ENOTTY; |
87de5057 | 290 | p = curproc; |
58c2553a MD |
291 | if (p == NULL) { |
292 | error = EINVAL; | |
293 | goto done; | |
294 | } | |
984263bc MD |
295 | |
296 | /* Can't trace a process that's currently exec'ing. */ | |
4643740a | 297 | if ((procp->p_flags & P_INEXEC) != 0) { |
58c2553a MD |
298 | error = EAGAIN; |
299 | goto done; | |
300 | } | |
301 | if (!CHECKIO(p, procp) || p_trespass(ap->a_cred, procp->p_ucred)) { | |
302 | error = EPERM; | |
303 | goto done; | |
304 | } | |
984263bc MD |
305 | |
306 | switch (ap->a_command) { | |
307 | case PIOCBIS: | |
86d81c56 | 308 | spin_lock(&procp->p_spin); |
984263bc | 309 | procp->p_stops |= *(unsigned int*)ap->a_data; |
86d81c56 | 310 | spin_unlock(&procp->p_spin); |
984263bc MD |
311 | break; |
312 | case PIOCBIC: | |
86d81c56 | 313 | spin_lock(&procp->p_spin); |
984263bc | 314 | procp->p_stops &= ~*(unsigned int*)ap->a_data; |
86d81c56 | 315 | spin_unlock(&procp->p_spin); |
984263bc MD |
316 | break; |
317 | case PIOCSFL: | |
318 | /* | |
319 | * NFLAGS is "non-suser_xxx flags" -- currently, only | |
320 | * PFS_ISUGID ("ignore set u/g id"); | |
321 | */ | |
322 | #define NFLAGS (PF_ISUGID) | |
323 | flags = (unsigned char)*(unsigned int*)ap->a_data; | |
2b3f93ea MD |
324 | if (flags & NFLAGS && |
325 | (error = caps_priv_check(ap->a_cred, SYSCAP_RESTRICTEDROOT))) | |
326 | { | |
58c2553a | 327 | goto done; |
2b3f93ea | 328 | } |
984263bc MD |
329 | procp->p_pfsflags = flags; |
330 | break; | |
331 | case PIOCGFL: | |
332 | *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags; | |
333 | break; | |
334 | case PIOCSTATUS: | |
e193508c MD |
335 | /* |
336 | * NOTE: syscall entry deals with stopevents and may run without | |
337 | * the MP lock. | |
338 | */ | |
984263bc | 339 | psp = (struct procfs_status *)ap->a_data; |
984263bc MD |
340 | psp->flags = procp->p_pfsflags; |
341 | psp->events = procp->p_stops; | |
287a8577 | 342 | spin_lock(&procp->p_spin); |
984263bc | 343 | if (procp->p_step) { |
e193508c | 344 | psp->state = 0; |
984263bc MD |
345 | psp->why = procp->p_stype; |
346 | psp->val = procp->p_xstat; | |
287a8577 | 347 | spin_unlock(&procp->p_spin); |
984263bc | 348 | } else { |
e193508c | 349 | psp->state = 1; |
287a8577 | 350 | spin_unlock(&procp->p_spin); |
e193508c MD |
351 | psp->why = 0; /* Not defined values */ |
352 | psp->val = 0; /* Not defined values */ | |
984263bc MD |
353 | } |
354 | break; | |
355 | case PIOCWAIT: | |
e193508c MD |
356 | /* |
357 | * NOTE: syscall entry deals with stopevents and may run without | |
358 | * the MP lock. | |
359 | */ | |
984263bc | 360 | psp = (struct procfs_status *)ap->a_data; |
287a8577 | 361 | spin_lock(&procp->p_spin); |
e193508c MD |
362 | while (procp->p_step == 0) { |
363 | tsleep_interlock(&procp->p_stype, PCATCH); | |
287a8577 | 364 | spin_unlock(&procp->p_spin); |
b2a6ad87 | 365 | if (procp->p_stops == 0) { |
2d20d96b | 366 | error = 0; |
b2a6ad87 MD |
367 | goto done; |
368 | } | |
369 | if (procp->p_flags & P_POSTEXIT) { | |
370 | error = EINVAL; | |
371 | goto done; | |
372 | } | |
373 | if (procp->p_flags & P_INEXEC) { | |
374 | error = EAGAIN; | |
375 | goto done; | |
376 | } | |
377 | error = tsleep(&procp->p_stype, PCATCH | PINTERLOCKED, | |
378 | "piocwait", 0); | |
984263bc | 379 | if (error) |
58c2553a | 380 | goto done; |
287a8577 | 381 | spin_lock(&procp->p_spin); |
984263bc | 382 | } |
287a8577 | 383 | spin_unlock(&procp->p_spin); |
984263bc MD |
384 | psp->state = 1; /* It stopped */ |
385 | psp->flags = procp->p_pfsflags; | |
386 | psp->events = procp->p_stops; | |
387 | psp->why = procp->p_stype; /* why it stopped */ | |
388 | psp->val = procp->p_xstat; /* any extra info */ | |
389 | break; | |
390 | case PIOCCONT: /* Restart a proc */ | |
e193508c MD |
391 | /* |
392 | * NOTE: syscall entry deals with stopevents and may run without | |
393 | * the MP lock. However, the caller is presumably interlocked | |
394 | * by having waited. | |
395 | */ | |
58c2553a MD |
396 | if (procp->p_step == 0) { |
397 | error = EINVAL; /* Can only start a stopped process */ | |
398 | goto done; | |
399 | } | |
984263bc | 400 | if ((signo = *(int*)ap->a_data) != 0) { |
58c2553a MD |
401 | if (signo >= NSIG || signo <= 0) { |
402 | error = EINVAL; | |
403 | goto done; | |
404 | } | |
84204577 | 405 | ksignal(procp, signo); |
984263bc MD |
406 | } |
407 | procp->p_step = 0; | |
408 | wakeup(&procp->p_step); | |
409 | break; | |
410 | default: | |
58c2553a MD |
411 | error = ENOTTY; |
412 | goto done; | |
984263bc | 413 | } |
58c2553a MD |
414 | error = 0; |
415 | done: | |
f44c73be | 416 | pfs_pdone(procp); |
bdf3e9ff | 417 | return error; |
984263bc MD |
418 | } |
419 | ||
420 | /* | |
421 | * do block mapping for pfsnode (vp). | |
422 | * since we don't use the buffer cache | |
423 | * for procfs this function should never | |
424 | * be called. in any case, it's not clear | |
425 | * what part of the kernel ever makes use | |
426 | * of this function. for sanity, this is the | |
427 | * usual no-op bmap, although returning | |
428 | * (EIO) would be a reasonable alternative. | |
ac424f9b | 429 | * |
08daea96 MD |
430 | * XXX mmap assumes buffer cache operation |
431 | * | |
432 | * procfs_bmap(struct vnode *a_vp, off_t a_loffset, | |
433 | * off_t *a_doffsetp, int *a_runp, int *a_runb) | |
984263bc MD |
434 | */ |
435 | static int | |
ac424f9b | 436 | procfs_bmap(struct vop_bmap_args *ap) |
984263bc | 437 | { |
54078292 MD |
438 | if (ap->a_doffsetp != NULL) |
439 | *ap->a_doffsetp = ap->a_loffset; | |
984263bc MD |
440 | if (ap->a_runp != NULL) |
441 | *ap->a_runp = 0; | |
54078292 MD |
442 | if (ap->a_runb != NULL) |
443 | *ap->a_runb = 0; | |
984263bc MD |
444 | return (0); |
445 | } | |
446 | ||
447 | /* | |
448 | * procfs_inactive is called when the pfsnode | |
449 | * is vrele'd and the reference count goes | |
450 | * to zero. (vp) will be on the vnode free | |
451 | * list, so to get it back vget() must be | |
452 | * used. | |
453 | * | |
454 | * (vp) is locked on entry, but must be unlocked on exit. | |
ac424f9b | 455 | * |
b478fdce | 456 | * procfs_inactive(struct vnode *a_vp) |
984263bc MD |
457 | */ |
458 | static int | |
ac424f9b | 459 | procfs_inactive(struct vop_inactive_args *ap) |
984263bc | 460 | { |
e193508c | 461 | struct pfsnode *pfs = VTOPFS(ap->a_vp); |
984263bc | 462 | |
e193508c MD |
463 | if (pfs->pfs_pid & PFS_DEAD) |
464 | vrecycle(ap->a_vp); | |
984263bc MD |
465 | return (0); |
466 | } | |
467 | ||
468 | /* | |
469 | * _reclaim is called when getnewvnode() | |
470 | * wants to make use of an entry on the vnode | |
471 | * free list. at this time the filesystem needs | |
472 | * to free any private data and remove the node | |
473 | * from any private lists. | |
ac424f9b CP |
474 | * |
475 | * procfs_reclaim(struct vnode *a_vp) | |
984263bc MD |
476 | */ |
477 | static int | |
ac424f9b | 478 | procfs_reclaim(struct vop_reclaim_args *ap) |
984263bc | 479 | { |
984263bc MD |
480 | return (procfs_freevp(ap->a_vp)); |
481 | } | |
482 | ||
483 | /* | |
484 | * _print is used for debugging. | |
485 | * just print a readable description | |
486 | * of (vp). | |
ac424f9b CP |
487 | * |
488 | * procfs_print(struct vnode *a_vp) | |
984263bc MD |
489 | */ |
490 | static int | |
ac424f9b | 491 | procfs_print(struct vop_print_args *ap) |
984263bc MD |
492 | { |
493 | struct pfsnode *pfs = VTOPFS(ap->a_vp); | |
494 | ||
086c1d7e | 495 | kprintf("tag VT_PROCFS, type %d, pid %ld, mode %x, flags %lx\n", |
984263bc MD |
496 | pfs->pfs_type, (long)pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags); |
497 | return (0); | |
498 | } | |
499 | ||
500 | /* | |
501 | * generic entry point for unsupported operations | |
502 | */ | |
503 | static int | |
66a1ddf5 | 504 | procfs_badop(struct vop_generic_args *ap) |
984263bc | 505 | { |
984263bc MD |
506 | return (EIO); |
507 | } | |
508 | ||
509 | /* | |
510 | * Invent attributes for pfsnode (vp) and store | |
511 | * them in (vap). | |
512 | * Directories lengths are returned as zero since | |
513 | * any real length would require the genuine size | |
514 | * to be computed, and nothing cares anyway. | |
515 | * | |
516 | * this is relatively minimal for procfs. | |
ac424f9b | 517 | * |
b478fdce | 518 | * procfs_getattr(struct vnode *a_vp, struct vattr *a_vap) |
984263bc MD |
519 | */ |
520 | static int | |
ac424f9b | 521 | procfs_getattr(struct vop_getattr_args *ap) |
984263bc MD |
522 | { |
523 | struct pfsnode *pfs = VTOPFS(ap->a_vp); | |
524 | struct vattr *vap = ap->a_vap; | |
525 | struct proc *procp; | |
526 | int error; | |
527 | ||
528 | /* | |
529 | * First make sure that the process and its credentials | |
530 | * still exist. | |
531 | */ | |
532 | switch (pfs->pfs_type) { | |
533 | case Proot: | |
534 | case Pcurproc: | |
58c2553a | 535 | procp = NULL; |
984263bc | 536 | break; |
984263bc | 537 | default: |
198c0ff2 | 538 | procp = pfs_pfind(pfs->pfs_pid); |
58c2553a MD |
539 | if (procp == NULL || procp->p_ucred == NULL) { |
540 | error = ENOENT; | |
541 | goto done; | |
542 | } | |
f44c73be | 543 | break; |
984263bc MD |
544 | } |
545 | ||
546 | error = 0; | |
547 | ||
548 | /* start by zeroing out the attributes */ | |
549 | VATTR_NULL(vap); | |
550 | ||
551 | /* next do all the common fields */ | |
552 | vap->va_type = ap->a_vp->v_type; | |
553 | vap->va_mode = pfs->pfs_mode; | |
554 | vap->va_fileid = pfs->pfs_fileno; | |
555 | vap->va_flags = 0; | |
556 | vap->va_blocksize = PAGE_SIZE; | |
557 | vap->va_bytes = vap->va_size = 0; | |
558 | vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; | |
559 | ||
560 | /* | |
561 | * Make all times be current TOD. | |
562 | * It would be possible to get the process start | |
563 | * time from the p_stat structure, but there's | |
564 | * no "file creation" time stamp anyway, and the | |
565 | * p_stat structure is not addressible if u. gets | |
566 | * swapped out for that process. | |
567 | */ | |
d489a79a | 568 | vfs_timestamp(&vap->va_ctime); |
984263bc MD |
569 | vap->va_atime = vap->va_mtime = vap->va_ctime; |
570 | ||
571 | /* | |
572 | * If the process has exercised some setuid or setgid | |
573 | * privilege, then rip away read/write permission so | |
574 | * that only root can gain access. | |
575 | */ | |
576 | switch (pfs->pfs_type) { | |
577 | case Pctl: | |
578 | case Pregs: | |
579 | case Pfpregs: | |
580 | case Pdbregs: | |
581 | case Pmem: | |
f44c73be | 582 | if (procp->p_flags & P_SUGID) { |
984263bc MD |
583 | vap->va_mode &= ~((VREAD|VWRITE)| |
584 | ((VREAD|VWRITE)>>3)| | |
585 | ((VREAD|VWRITE)>>6)); | |
f44c73be | 586 | } |
984263bc MD |
587 | break; |
588 | default: | |
589 | break; | |
590 | } | |
591 | ||
592 | /* | |
593 | * now do the object specific fields | |
594 | * | |
595 | * The size could be set from struct reg, but it's hardly | |
596 | * worth the trouble, and it puts some (potentially) machine | |
597 | * dependent data into this machine-independent code. If it | |
598 | * becomes important then this function should break out into | |
599 | * a per-file stat function in the corresponding .c file. | |
600 | */ | |
601 | ||
602 | vap->va_nlink = 1; | |
603 | if (procp) { | |
f44c73be MD |
604 | if (procp->p_ucred) { |
605 | vap->va_uid = procp->p_ucred->cr_uid; | |
606 | vap->va_gid = procp->p_ucred->cr_gid; | |
607 | } else { | |
608 | vap->va_uid = -1; | |
609 | vap->va_gid = -1; | |
610 | } | |
984263bc MD |
611 | } |
612 | ||
613 | switch (pfs->pfs_type) { | |
614 | case Proot: | |
615 | /* | |
616 | * Set nlink to 1 to tell fts(3) we don't actually know. | |
617 | */ | |
618 | vap->va_nlink = 1; | |
619 | vap->va_uid = 0; | |
620 | vap->va_gid = 0; | |
621 | vap->va_size = vap->va_bytes = DEV_BSIZE; | |
622 | break; | |
623 | ||
624 | case Pcurproc: { | |
625 | char buf[16]; /* should be enough */ | |
f44c73be | 626 | |
984263bc MD |
627 | vap->va_uid = 0; |
628 | vap->va_gid = 0; | |
f44c73be MD |
629 | vap->va_size = ksnprintf(buf, sizeof(buf), |
630 | "%ld", (long)curproc->p_pid); | |
631 | vap->va_bytes = vap->va_size; | |
984263bc MD |
632 | break; |
633 | } | |
634 | ||
635 | case Pproc: | |
636 | vap->va_nlink = nproc_targets; | |
637 | vap->va_size = vap->va_bytes = DEV_BSIZE; | |
638 | break; | |
639 | ||
640 | case Pfile: { | |
641 | char *fullpath, *freepath; | |
f44c73be | 642 | |
07cdb1d2 MD |
643 | if (procp->p_textnch.ncp) { |
644 | struct nchandle nch; | |
645 | ||
646 | cache_copy(&procp->p_textnch, &nch); | |
08a7d6d8 | 647 | error = cache_fullpath(procp, &nch, NULL, |
07cdb1d2 MD |
648 | &fullpath, &freepath, 0); |
649 | cache_drop(&nch); | |
650 | } else { | |
f44c73be | 651 | error = EINVAL; |
07cdb1d2 | 652 | } |
f44c73be | 653 | |
984263bc MD |
654 | if (error == 0) { |
655 | vap->va_size = strlen(fullpath); | |
efda3bd0 | 656 | kfree(freepath, M_TEMP); |
984263bc MD |
657 | } else { |
658 | vap->va_size = sizeof("unknown") - 1; | |
659 | error = 0; | |
660 | } | |
661 | vap->va_bytes = vap->va_size; | |
662 | break; | |
663 | } | |
664 | ||
665 | case Pmem: | |
666 | /* | |
667 | * If we denied owner access earlier, then we have to | |
668 | * change the owner to root - otherwise 'ps' and friends | |
669 | * will break even though they are setgid kmem. *SIGH* | |
670 | */ | |
4643740a | 671 | if (procp->p_flags & P_SUGID) |
984263bc | 672 | vap->va_uid = 0; |
f44c73be | 673 | else if (procp->p_ucred) |
984263bc | 674 | vap->va_uid = procp->p_ucred->cr_uid; |
f44c73be MD |
675 | else |
676 | vap->va_uid = -1; | |
984263bc MD |
677 | break; |
678 | ||
679 | case Pregs: | |
680 | vap->va_bytes = vap->va_size = sizeof(struct reg); | |
681 | break; | |
682 | ||
683 | case Pfpregs: | |
684 | vap->va_bytes = vap->va_size = sizeof(struct fpreg); | |
685 | break; | |
686 | ||
687 | case Pdbregs: | |
688 | vap->va_bytes = vap->va_size = sizeof(struct dbreg); | |
689 | break; | |
690 | ||
691 | case Ptype: | |
692 | case Pmap: | |
693 | case Pctl: | |
694 | case Pstatus: | |
695 | case Pnote: | |
696 | case Pnotepg: | |
697 | case Pcmdline: | |
698 | case Prlimit: | |
699 | break; | |
700 | ||
701 | default: | |
702 | panic("procfs_getattr"); | |
703 | } | |
58c2553a | 704 | done: |
f44c73be | 705 | pfs_pdone(procp); |
984263bc MD |
706 | return (error); |
707 | } | |
708 | ||
ac424f9b CP |
709 | /* |
710 | * procfs_setattr(struct vnode *a_vp, struct vattr *a_vap, | |
b478fdce | 711 | * struct ucred *a_cred) |
ac424f9b | 712 | */ |
984263bc | 713 | static int |
ac424f9b | 714 | procfs_setattr(struct vop_setattr_args *ap) |
984263bc | 715 | { |
984263bc MD |
716 | if (ap->a_vap->va_flags != VNOVAL) |
717 | return (EOPNOTSUPP); | |
718 | ||
719 | /* | |
720 | * just fake out attribute setting | |
721 | * it's not good to generate an error | |
722 | * return, otherwise things like creat() | |
723 | * will fail when they try to set the | |
724 | * file length to 0. worse, this means | |
725 | * that echo $note > /proc/$pid/note will fail. | |
726 | */ | |
727 | ||
728 | return (0); | |
729 | } | |
730 | ||
731 | /* | |
732 | * implement access checking. | |
733 | * | |
b478fdce | 734 | * procfs_access(struct vnode *a_vp, int a_mode, struct ucred *a_cred) |
984263bc MD |
735 | */ |
736 | static int | |
ac424f9b | 737 | procfs_access(struct vop_access_args *ap) |
984263bc | 738 | { |
984263bc MD |
739 | struct vattr vattr; |
740 | int error; | |
741 | ||
52cdca9a NT |
742 | error = VOP_GETATTR(ap->a_vp, &vattr); |
743 | if (!error) | |
744 | error = vop_helper_access(ap, vattr.va_uid, vattr.va_gid, | |
745 | vattr.va_mode, 0); | |
746 | return (error); | |
984263bc MD |
747 | } |
748 | ||
749 | /* | |
3446c007 MD |
750 | * lookup. this is incredibly complicated in the general case, however |
751 | * for most pseudo-filesystems very little needs to be done. | |
ac424f9b CP |
752 | * |
753 | * procfs_lookup(struct vnode *a_dvp, struct vnode **a_vpp, | |
754 | * struct componentname *a_cnp) | |
984263bc MD |
755 | */ |
756 | static int | |
e62afb5f | 757 | procfs_lookup(struct vop_old_lookup_args *ap) |
984263bc MD |
758 | { |
759 | struct componentname *cnp = ap->a_cnp; | |
760 | struct vnode **vpp = ap->a_vpp; | |
761 | struct vnode *dvp = ap->a_dvp; | |
762 | char *pname = cnp->cn_nameptr; | |
763 | /* struct proc *curp = cnp->cn_proc; */ | |
764 | struct proc_target *pt; | |
765 | pid_t pid; | |
766 | struct pfsnode *pfs; | |
767 | struct proc *p; | |
c7e98b2f | 768 | struct lwp *lp; |
984263bc | 769 | int i; |
3446c007 | 770 | int error; |
984263bc MD |
771 | |
772 | *vpp = NULL; | |
773 | ||
2b69e610 | 774 | if (cnp->cn_nameiop == NAMEI_DELETE || cnp->cn_nameiop == NAMEI_RENAME) |
984263bc MD |
775 | return (EROFS); |
776 | ||
58c2553a | 777 | p = NULL; |
3446c007 | 778 | error = 0; |
984263bc MD |
779 | if (cnp->cn_namelen == 1 && *pname == '.') { |
780 | *vpp = dvp; | |
3446c007 MD |
781 | vref(*vpp); |
782 | goto out; | |
984263bc MD |
783 | } |
784 | ||
785 | pfs = VTOPFS(dvp); | |
786 | switch (pfs->pfs_type) { | |
787 | case Proot: | |
2b69e610 | 788 | if (cnp->cn_flags & CNP_ISDOTDOT) |
984263bc MD |
789 | return (EIO); |
790 | ||
f658e60a | 791 | if (CNEQ(cnp, "curproc", 7) || CNEQ(cnp, "self", 4)) { |
3446c007 MD |
792 | error = procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc); |
793 | goto out; | |
794 | } | |
984263bc MD |
795 | |
796 | pid = atopid(pname, cnp->cn_namelen); | |
797 | if (pid == NO_PID) | |
798 | break; | |
799 | ||
198c0ff2 | 800 | p = pfs_pfind(pid); |
984263bc MD |
801 | if (p == NULL) |
802 | break; | |
803 | ||
dcab9ef0 JS |
804 | if (!PRISON_CHECK(ap->a_cnp->cn_cred, p->p_ucred)) |
805 | break; | |
806 | ||
28c57d20 HP |
807 | if (ps_showallprocs == 0 && ap->a_cnp->cn_cred->cr_uid != 0 && |
808 | ap->a_cnp->cn_cred->cr_uid != p->p_ucred->cr_uid) | |
809 | break; | |
810 | ||
3446c007 MD |
811 | error = procfs_allocvp(dvp->v_mount, vpp, pid, Pproc); |
812 | goto out; | |
984263bc MD |
813 | |
814 | case Pproc: | |
3446c007 MD |
815 | if (cnp->cn_flags & CNP_ISDOTDOT) { |
816 | error = procfs_root(dvp->v_mount, vpp); | |
817 | goto out; | |
818 | } | |
984263bc | 819 | |
198c0ff2 | 820 | p = pfs_pfind(pfs->pfs_pid); |
984263bc MD |
821 | if (p == NULL) |
822 | break; | |
c7e98b2f SS |
823 | /* XXX lwp */ |
824 | lp = FIRST_LWP_IN_PROC(p); | |
8b411d28 MD |
825 | if (lp == NULL) |
826 | break; | |
984263bc | 827 | |
dcab9ef0 JS |
828 | if (!PRISON_CHECK(ap->a_cnp->cn_cred, p->p_ucred)) |
829 | break; | |
830 | ||
28c57d20 HP |
831 | if (ps_showallprocs == 0 && ap->a_cnp->cn_cred->cr_uid != 0 && |
832 | ap->a_cnp->cn_cred->cr_uid != p->p_ucred->cr_uid) | |
833 | break; | |
834 | ||
984263bc MD |
835 | for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) { |
836 | if (cnp->cn_namelen == pt->pt_namlen && | |
837 | bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && | |
c7e98b2f | 838 | (pt->pt_valid == NULL || (*pt->pt_valid)(lp))) |
984263bc MD |
839 | goto found; |
840 | } | |
841 | break; | |
842 | found: | |
3446c007 MD |
843 | error = procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, |
844 | pt->pt_pfstype); | |
845 | goto out; | |
984263bc MD |
846 | |
847 | default: | |
3446c007 MD |
848 | error = ENOTDIR; |
849 | goto out; | |
984263bc | 850 | } |
3446c007 MD |
851 | if (cnp->cn_nameiop == NAMEI_LOOKUP) |
852 | error = ENOENT; | |
853 | else | |
854 | error = EROFS; | |
855 | /* | |
856 | * If no error occured *vpp will hold a referenced locked vnode. | |
aa723195 MD |
857 | * dvp was passed to us locked and *vpp must be returned locked. |
858 | * If *vpp != dvp then we should unlock dvp if (1) this is not the | |
859 | * last component or (2) CNP_LOCKPARENT is not set. | |
3446c007 MD |
860 | */ |
861 | out: | |
aa723195 MD |
862 | if (error == 0 && *vpp != dvp) { |
863 | if ((cnp->cn_flags & CNP_LOCKPARENT) == 0) { | |
864 | cnp->cn_flags |= CNP_PDIRUNLOCK; | |
a11aaa81 | 865 | vn_unlock(dvp); |
3446c007 MD |
866 | } |
867 | } | |
f44c73be | 868 | pfs_pdone(p); |
3446c007 | 869 | return (error); |
984263bc MD |
870 | } |
871 | ||
872 | /* | |
873 | * Does this process have a text file? | |
874 | */ | |
4d4fb029 | 875 | int |
c7e98b2f | 876 | procfs_validfile(struct lwp *lp) |
984263bc | 877 | { |
c7e98b2f | 878 | return (procfs_findtextvp(lp->lwp_proc) != NULLVP); |
984263bc MD |
879 | } |
880 | ||
881 | /* | |
882 | * readdir() returns directory entries from pfsnode (vp). | |
883 | * | |
884 | * We generate just one directory entry at a time, as it would probably | |
885 | * not pay off to buffer several entries locally to save uiomove calls. | |
ac424f9b CP |
886 | * |
887 | * procfs_readdir(struct vnode *a_vp, struct uio *a_uio, struct ucred *a_cred, | |
84009d92 | 888 | * int *a_eofflag, int *a_ncookies, off_t **a_cookies) |
984263bc MD |
889 | */ |
890 | static int | |
ac424f9b | 891 | procfs_readdir(struct vop_readdir_args *ap) |
984263bc | 892 | { |
984263bc | 893 | struct pfsnode *pfs; |
dcab9ef0 | 894 | int error; |
984263bc | 895 | |
dcab9ef0 | 896 | if (ap->a_uio->uio_offset < 0 || ap->a_uio->uio_offset > INT_MAX) |
984263bc | 897 | return (EINVAL); |
b458d1ab MD |
898 | error = vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY | LK_FAILRECLAIM); |
899 | if (error) | |
885ecb13 | 900 | return (error); |
dcab9ef0 | 901 | pfs = VTOPFS(ap->a_vp); |
984263bc MD |
902 | |
903 | switch (pfs->pfs_type) { | |
dcab9ef0 | 904 | case Pproc: |
885ecb13 MD |
905 | /* |
906 | * this is for the process-specific sub-directories. | |
907 | * all that is needed to is copy out all the entries | |
908 | * from the procent[] table (top of this file). | |
909 | */ | |
dcab9ef0 JS |
910 | error = procfs_readdir_proc(ap); |
911 | break; | |
dcab9ef0 | 912 | case Proot: |
885ecb13 MD |
913 | /* |
914 | * this is for the root of the procfs filesystem | |
915 | * what is needed is a special entry for "curproc" | |
916 | * followed by an entry for each process on allproc | |
917 | */ | |
dcab9ef0 | 918 | error = procfs_readdir_root(ap); |
984263bc | 919 | break; |
984263bc MD |
920 | default: |
921 | error = ENOTDIR; | |
922 | break; | |
923 | } | |
924 | ||
885ecb13 | 925 | vn_unlock(ap->a_vp); |
984263bc MD |
926 | return (error); |
927 | } | |
928 | ||
dcab9ef0 JS |
929 | static int |
930 | procfs_readdir_proc(struct vop_readdir_args *ap) | |
931 | { | |
932 | struct pfsnode *pfs; | |
933 | int error, i, retval; | |
934 | struct proc *p; | |
c7e98b2f | 935 | struct lwp *lp; |
dcab9ef0 JS |
936 | struct proc_target *pt; |
937 | struct uio *uio = ap->a_uio; | |
938 | ||
939 | pfs = VTOPFS(ap->a_vp); | |
198c0ff2 | 940 | p = pfs_pfind(pfs->pfs_pid); |
dcab9ef0 JS |
941 | if (p == NULL) |
942 | return(0); | |
58c2553a MD |
943 | if (!PRISON_CHECK(ap->a_cred, p->p_ucred)) { |
944 | error = 0; | |
945 | goto done; | |
946 | } | |
947 | /* XXX lwp, not MPSAFE */ | |
c7e98b2f | 948 | lp = FIRST_LWP_IN_PROC(p); |
8b411d28 MD |
949 | if (lp == NULL) { |
950 | error = EINVAL; | |
951 | goto done; | |
952 | } | |
dcab9ef0 JS |
953 | |
954 | error = 0; | |
fb0466c9 | 955 | i = (int)uio->uio_offset; |
58c2553a MD |
956 | if (i < 0) { |
957 | error = EINVAL; | |
958 | goto done; | |
959 | } | |
dcab9ef0 JS |
960 | |
961 | for (pt = &proc_targets[i]; | |
962 | !error && uio->uio_resid > 0 && i < nproc_targets; pt++, i++) { | |
c7e98b2f | 963 | if (pt->pt_valid && (*pt->pt_valid)(lp) == 0) |
dcab9ef0 JS |
964 | continue; |
965 | ||
966 | retval = vop_write_dirent(&error, uio, | |
967 | PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype), pt->pt_type, | |
968 | pt->pt_namlen, pt->pt_name); | |
969 | if (retval) | |
970 | break; | |
971 | } | |
972 | ||
fb0466c9 | 973 | uio->uio_offset = (off_t)i; |
58c2553a MD |
974 | error = 0; |
975 | done: | |
f44c73be | 976 | pfs_pdone(p); |
58c2553a | 977 | return error; |
dcab9ef0 JS |
978 | } |
979 | ||
51e64ff2 MD |
980 | struct procfs_readdir_root_info { |
981 | int error; | |
982 | int i; | |
983 | int pcnt; | |
984 | struct uio *uio; | |
985 | struct ucred *cred; | |
986 | }; | |
987 | ||
988 | static int procfs_readdir_root_callback(struct proc *p, void *data); | |
989 | ||
dcab9ef0 JS |
990 | static int |
991 | procfs_readdir_root(struct vop_readdir_args *ap) | |
992 | { | |
51e64ff2 | 993 | struct procfs_readdir_root_info info; |
dcab9ef0 | 994 | struct uio *uio = ap->a_uio; |
51e64ff2 MD |
995 | int res; |
996 | ||
563f4b23 | 997 | res = 0; |
51e64ff2 | 998 | info.error = 0; |
fb0466c9 MD |
999 | info.i = (int)uio->uio_offset; |
1000 | ||
1001 | if (info.i < 0) | |
1002 | return (EINVAL); | |
1003 | ||
51e64ff2 MD |
1004 | info.pcnt = 0; |
1005 | info.uio = uio; | |
1006 | info.cred = ap->a_cred; | |
f658e60a | 1007 | while (info.pcnt < 4) { |
51e64ff2 MD |
1008 | res = procfs_readdir_root_callback(NULL, &info); |
1009 | if (res < 0) | |
1010 | break; | |
1011 | } | |
1012 | if (res >= 0) | |
586c4308 | 1013 | allproc_scan(procfs_readdir_root_callback, &info, 0); |
fb0466c9 | 1014 | uio->uio_offset = (off_t)info.i; |
51e64ff2 MD |
1015 | |
1016 | return (info.error); | |
1017 | } | |
1018 | ||
1019 | static int | |
1020 | procfs_readdir_root_callback(struct proc *p, void *data) | |
1021 | { | |
1022 | struct procfs_readdir_root_info *info = data; | |
1023 | struct uio *uio; | |
1024 | int retval; | |
dcab9ef0 JS |
1025 | ino_t d_ino; |
1026 | const char *d_name; | |
1027 | char d_name_pid[20]; | |
1028 | size_t d_namlen; | |
1029 | uint8_t d_type; | |
1030 | ||
51e64ff2 | 1031 | uio = info->uio; |
dcab9ef0 | 1032 | |
51e64ff2 MD |
1033 | if (uio->uio_resid <= 0 || info->error) |
1034 | return(-1); | |
dcab9ef0 | 1035 | |
51e64ff2 MD |
1036 | switch (info->pcnt) { |
1037 | case 0: /* `.' */ | |
1038 | d_ino = PROCFS_FILENO(0, Proot); | |
1039 | d_name = "."; | |
1040 | d_namlen = 1; | |
1041 | d_type = DT_DIR; | |
1042 | break; | |
1043 | case 1: /* `..' */ | |
1044 | d_ino = PROCFS_FILENO(0, Proot); | |
1045 | d_name = ".."; | |
1046 | d_namlen = 2; | |
1047 | d_type = DT_DIR; | |
1048 | break; | |
dcab9ef0 | 1049 | |
51e64ff2 MD |
1050 | case 2: |
1051 | d_ino = PROCFS_FILENO(0, Pcurproc); | |
1052 | d_namlen = 7; | |
1053 | d_name = "curproc"; | |
1054 | d_type = DT_LNK; | |
1055 | break; | |
dcab9ef0 | 1056 | |
f658e60a RB |
1057 | case 3: |
1058 | d_ino = PROCFS_FILENO(0, Pcurproc); | |
1059 | d_namlen = 4; | |
1060 | d_name = "self"; | |
1061 | d_type = DT_LNK; | |
1062 | break; | |
51e64ff2 MD |
1063 | |
1064 | default: | |
1065 | if (!PRISON_CHECK(info->cred, p->p_ucred)) | |
1066 | return(0); | |
1067 | if (ps_showallprocs == 0 && | |
1068 | info->cred->cr_uid != 0 && | |
1069 | info->cred->cr_uid != p->p_ucred->cr_uid) { | |
1070 | return(0); | |
dcab9ef0 JS |
1071 | } |
1072 | ||
51e64ff2 MD |
1073 | /* |
1074 | * Skip entries we have already returned (optimization) | |
1075 | */ | |
1076 | if (info->pcnt < info->i) { | |
1077 | ++info->pcnt; | |
1078 | return(0); | |
1079 | } | |
1080 | ||
1081 | d_ino = PROCFS_FILENO(p->p_pid, Pproc); | |
f8c7a42d | 1082 | d_namlen = ksnprintf(d_name_pid, sizeof(d_name_pid), |
51e64ff2 MD |
1083 | "%ld", (long)p->p_pid); |
1084 | d_name = d_name_pid; | |
1085 | d_type = DT_DIR; | |
1086 | break; | |
dcab9ef0 | 1087 | } |
51e64ff2 MD |
1088 | |
1089 | /* | |
1090 | * Skip entries we have already returned (optimization) | |
1091 | */ | |
1092 | if (info->pcnt < info->i) { | |
1093 | ++info->pcnt; | |
1094 | return(0); | |
1095 | } | |
1096 | ||
1097 | retval = vop_write_dirent(&info->error, uio, | |
1098 | d_ino, d_type, d_namlen, d_name); | |
1099 | if (retval) | |
1100 | return(-1); | |
1101 | ++info->pcnt; | |
1102 | ++info->i; | |
dcab9ef0 JS |
1103 | return(0); |
1104 | } | |
1105 | ||
984263bc MD |
1106 | /* |
1107 | * readlink reads the link of `curproc' or `file' | |
1108 | */ | |
1109 | static int | |
ac424f9b | 1110 | procfs_readlink(struct vop_readlink_args *ap) |
984263bc MD |
1111 | { |
1112 | char buf[16]; /* should be enough */ | |
1113 | struct proc *procp; | |
1114 | struct vnode *vp = ap->a_vp; | |
1115 | struct pfsnode *pfs = VTOPFS(vp); | |
1116 | char *fullpath, *freepath; | |
1117 | int error, len; | |
1118 | ||
1119 | switch (pfs->pfs_type) { | |
1120 | case Pcurproc: | |
1121 | if (pfs->pfs_fileno != PROCFS_FILENO(0, Pcurproc)) | |
1122 | return (EINVAL); | |
1123 | ||
f8c7a42d | 1124 | len = ksnprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid); |
984263bc MD |
1125 | |
1126 | return (uiomove(buf, len, ap->a_uio)); | |
984263bc | 1127 | case Pfile: |
e09db544 MD |
1128 | /* |
1129 | * procfs's directory topology is somewhat asynchronous from | |
1130 | * reality so it is possible for pid requests to race exiting | |
1131 | * processes. In this situation, bit 31 is set in | |
1132 | * pfs->pfs_pid which guarantees that pfs_pfind() will return | |
1133 | * NULL. | |
1134 | * | |
1135 | * It is also possible to catch a process in the middle of | |
1136 | * an exit sequence so various fields might wind up being | |
1137 | * NULL that are not normally NULL. | |
1138 | */ | |
198c0ff2 | 1139 | procp = pfs_pfind(pfs->pfs_pid); |
41c20dac | 1140 | if (procp == NULL || procp->p_ucred == NULL) { |
f44c73be | 1141 | pfs_pdone(procp); |
984263bc | 1142 | return (uiomove("unknown", sizeof("unknown") - 1, |
f44c73be | 1143 | ap->a_uio)); |
984263bc | 1144 | } |
07cdb1d2 MD |
1145 | if (procp->p_textnch.ncp) { |
1146 | struct nchandle nch; | |
1147 | ||
1148 | cache_copy(&procp->p_textnch, &nch); | |
08a7d6d8 | 1149 | error = cache_fullpath(procp, &nch, NULL, |
f44c73be | 1150 | &fullpath, &freepath, 0); |
07cdb1d2 MD |
1151 | cache_drop(&nch); |
1152 | } else { | |
f44c73be | 1153 | error = EINVAL; |
07cdb1d2 | 1154 | } |
f44c73be | 1155 | |
58c2553a | 1156 | if (error != 0) { |
f44c73be | 1157 | pfs_pdone(procp); |
984263bc | 1158 | return (uiomove("unknown", sizeof("unknown") - 1, |
f44c73be | 1159 | ap->a_uio)); |
58c2553a | 1160 | } |
984263bc | 1161 | error = uiomove(fullpath, strlen(fullpath), ap->a_uio); |
efda3bd0 | 1162 | kfree(freepath, M_TEMP); |
f44c73be | 1163 | pfs_pdone(procp); |
984263bc MD |
1164 | return (error); |
1165 | default: | |
1166 | return (EINVAL); | |
1167 | } | |
1168 | } | |
1169 | ||
1170 | /* | |
1171 | * convert decimal ascii to pid_t | |
1172 | */ | |
1173 | static pid_t | |
ac424f9b | 1174 | atopid(const char *b, u_int len) |
984263bc MD |
1175 | { |
1176 | pid_t p = 0; | |
1177 | ||
1178 | while (len--) { | |
1179 | char c = *b++; | |
1180 | if (c < '0' || c > '9') | |
1181 | return (NO_PID); | |
1182 | p = 10 * p + (c - '0'); | |
1183 | if (p > PID_MAX) | |
1184 | return (NO_PID); | |
1185 | } | |
1186 | ||
1187 | return (p); | |
1188 | } | |
1189 |