xref: /freebsd/sys/fs/pseudofs/pseudofs_vnops.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
1 /*-
2  * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  *	$FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/ctype.h>
35 #include <sys/dirent.h>
36 #include <sys/fcntl.h>
37 #include <sys/lock.h>
38 #include <sys/mount.h>
39 #include <sys/mutex.h>
40 #include <sys/namei.h>
41 #include <sys/proc.h>
42 #include <sys/sbuf.h>
43 #include <sys/sx.h>
44 #include <sys/sysctl.h>
45 #include <sys/vnode.h>
46 
47 #include <fs/pseudofs/pseudofs.h>
48 #include <fs/pseudofs/pseudofs_internal.h>
49 
50 #if 0
51 #define PFS_TRACE(foo) \
52 	do { \
53 		printf("pseudofs: %s(): line %d: ", __func__, __LINE__); \
54 		printf foo ; \
55 		printf("\n"); \
56 	} while (0)
57 #define PFS_RETURN(err) \
58 	do { \
59 		printf("pseudofs: %s(): line %d: returning %d\n", \
60 		    __func__, __LINE__, err); \
61 		return (err); \
62 	} while (0)
63 #else
64 #define PFS_TRACE(foo) \
65 	do { /* nothing */ } while (0)
66 #define PFS_RETURN(err) \
67 	return (err)
68 #endif
69 
70 /*
71  * Returns non-zero if given file is visible to given process
72  */
73 static int
74 pfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid)
75 {
76 	struct proc *proc;
77 	int r;
78 
79 	PFS_TRACE(("%s (pid: %d, req: %d)",
80 	    pn->pn_name, pid, td->td_proc->p_pid));
81 
82 	if (pn->pn_flags & PFS_DISABLED)
83 		PFS_RETURN (0);
84 
85 	r = 1;
86 	if (pid != NO_PID) {
87 		if ((proc = pfind(pid)) == NULL)
88 			PFS_RETURN (0);
89 		if (p_cansee(td->td_proc, proc) != 0 ||
90 		    (pn->pn_vis != NULL && !(pn->pn_vis)(td, proc, pn)))
91 			r = 0;
92 		PROC_UNLOCK(proc);
93 	}
94 	PFS_RETURN (r);
95 }
96 
97 /*
98  * Verify permissions
99  */
100 static int
101 pfs_access(struct vop_access_args *va)
102 {
103 	struct vnode *vn = va->a_vp;
104 	struct vattr vattr;
105 	int error;
106 
107 	PFS_TRACE((((struct pfs_vdata *)vn->v_data)->pvd_pn->pn_name));
108 
109 	error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td);
110 	if (error)
111 		PFS_RETURN (error);
112 	error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid,
113 	    vattr.va_gid, va->a_mode, va->a_cred, NULL);
114 	PFS_RETURN (error);
115 }
116 
117 /*
118  * Close a file or directory
119  */
120 static int
121 pfs_close(struct vop_close_args *va)
122 {
123 	struct vnode *vn = va->a_vp;
124 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
125 	struct pfs_node *pn = pvd->pvd_pn;
126 	struct proc *proc;
127 	int error;
128 
129 	PFS_TRACE((pn->pn_name));
130 
131 	/*
132 	 * Do nothing unless this is the last close and the node has a
133 	 * last-close handler.
134 	 */
135 	if (vn->v_usecount > 1 || pn->pn_close == NULL)
136 		PFS_RETURN (0);
137 
138 	if (pvd->pvd_pid != NO_PID)
139 		proc = pfind(pvd->pvd_pid);
140 	else
141 		proc = NULL;
142 
143 	error = (pn->pn_close)(va->a_td, proc, pn);
144 
145 	if (proc != NULL)
146 		PROC_UNLOCK(proc);
147 
148 	PFS_RETURN (error);
149 }
150 
151 /*
152  * Get file attributes
153  */
154 static int
155 pfs_getattr(struct vop_getattr_args *va)
156 {
157 	struct vnode *vn = va->a_vp;
158 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
159 	struct pfs_node *pn = pvd->pvd_pn;
160 	struct vattr *vap = va->a_vap;
161 	struct proc *proc;
162 	int error = 0;
163 
164 	PFS_TRACE((pn->pn_name));
165 
166 	VATTR_NULL(vap);
167 	vap->va_type = vn->v_type;
168 	vap->va_fileid = pn->pn_fileno;
169 	vap->va_flags = 0;
170 	vap->va_blocksize = PAGE_SIZE;
171 	vap->va_bytes = vap->va_size = 0;
172 	vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
173 	vap->va_nlink = 1;
174 	nanotime(&vap->va_ctime);
175 	vap->va_atime = vap->va_mtime = vap->va_ctime;
176 
177 	switch (pn->pn_type) {
178 	case pfstype_procdir:
179 	case pfstype_root:
180 	case pfstype_dir:
181 		vap->va_mode = 0555;
182 		break;
183 	case pfstype_file:
184 	case pfstype_symlink:
185 		vap->va_mode = 0444;
186 		break;
187 	default:
188 		printf("shouldn't be here!\n");
189 		vap->va_mode = 0;
190 		break;
191 	}
192 
193 	if (pvd->pvd_pid != NO_PID) {
194 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
195 			PFS_RETURN (ENOENT);
196 		vap->va_uid = proc->p_ucred->cr_ruid;
197 		vap->va_gid = proc->p_ucred->cr_rgid;
198 		if (pn->pn_attr != NULL)
199 			error = (pn->pn_attr)(va->a_td, proc, pn, vap);
200 		PROC_UNLOCK(proc);
201 	} else {
202 		vap->va_uid = 0;
203 		vap->va_gid = 0;
204 	}
205 
206 	PFS_RETURN (error);
207 }
208 
209 /*
210  * Perform an ioctl
211  */
212 static int
213 pfs_ioctl(struct vop_ioctl_args *va)
214 {
215 	struct vnode *vn = va->a_vp;
216 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
217 	struct pfs_node *pn = pvd->pvd_pn;
218 	struct proc *proc = NULL;
219 	int error;
220 
221 	PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command));
222 
223 	if (vn->v_type != VREG)
224 		PFS_RETURN (EINVAL);
225 
226 	if (pn->pn_ioctl == NULL)
227 		PFS_RETURN (ENOTTY);
228 
229 	/*
230 	 * This is necessary because either process' privileges may
231 	 * have changed since the open() call.
232 	 */
233 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
234 		PFS_RETURN (EIO);
235 
236 	/* XXX duplicates bits of pfs_visible() */
237 	if (pvd->pvd_pid != NO_PID) {
238 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
239 			PFS_RETURN (EIO);
240 		_PHOLD(proc);
241 		PROC_UNLOCK(proc);
242 	}
243 
244 	error = (pn->pn_ioctl)(curthread, proc, pn, va->a_command, va->a_data);
245 
246 	if (proc != NULL)
247 		PRELE(proc);
248 
249 	PFS_RETURN (error);
250 }
251 
252 /*
253  * Perform getextattr
254  */
255 static int
256 pfs_getextattr(struct vop_getextattr_args *va)
257 {
258 	struct vnode *vn = va->a_vp;
259 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
260 	struct pfs_node *pn = pvd->pvd_pn;
261 	struct proc *proc = NULL;
262 	int error;
263 
264 	PFS_TRACE((pd->pn_name));
265 
266 	if (pn->pn_getextattr == NULL)
267 		PFS_RETURN (EOPNOTSUPP);
268 
269 	/*
270 	 * This is necessary because either process' privileges may
271 	 * have changed since the open() call.
272 	 */
273 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
274 		PFS_RETURN (EIO);
275 
276 	/* XXX duplicates bits of pfs_visible() */
277 	if (pvd->pvd_pid != NO_PID) {
278 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
279 			PFS_RETURN (EIO);
280 		_PHOLD(proc);
281 		PROC_UNLOCK(proc);
282 	}
283 
284 	error = (pn->pn_getextattr)(curthread, proc, pn, va->a_attrnamespace,
285 	    va->a_name, va->a_uio, va->a_size, va->a_cred);
286 
287 	if (proc != NULL)
288 		PRELE(proc);
289 
290 	PFS_RETURN (error);
291 }
292 
293 /*
294  * Look up a file or directory
295  *
296  * XXX NOTE!  pfs_lookup() has been hooked into vop_lookup_desc!  This
297  * will result in a lookup operation for a vnode which may already be
298  * cached, therefore we have to be careful to purge the VFS cache when
299  * reusing a vnode.
300  *
301  * This code will work, but is not really correct.  Normally we would hook
302  * vfs_cache_lookup() into vop_lookup_desc and hook pfs_lookup() into
303  * vop_cachedlookup_desc.
304  */
305 static int
306 pfs_lookup(struct vop_lookup_args *va)
307 {
308 	struct vnode *vn = va->a_dvp;
309 	struct vnode **vpp = va->a_vpp;
310 	struct componentname *cnp = va->a_cnp;
311 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
312 	struct pfs_node *pd = pvd->pvd_pn;
313 	struct pfs_node *pn, *pdn = NULL;
314 	pid_t pid = pvd->pvd_pid;
315 	char *pname;
316 	int error, i, namelen;
317 
318 	PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr));
319 
320 	if (vn->v_type != VDIR)
321 		PFS_RETURN (ENOTDIR);
322 
323 	/*
324 	 * Don't support DELETE or RENAME.  CREATE is supported so
325 	 * that O_CREAT will work, but the lookup will still fail if
326 	 * the file does not exist.
327 	 */
328 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
329 		PFS_RETURN (EOPNOTSUPP);
330 
331 	/* shortcut: check if the name is too long */
332 	if (cnp->cn_namelen >= PFS_NAMELEN)
333 		PFS_RETURN (ENOENT);
334 
335 	/* check that parent directory is visisble... */
336 	if (!pfs_visible(curthread, pd, pvd->pvd_pid))
337 		PFS_RETURN (ENOENT);
338 
339 	/* self */
340 	namelen = cnp->cn_namelen;
341 	pname = cnp->cn_nameptr;
342 	if (namelen == 1 && *pname == '.') {
343 		pn = pd;
344 		*vpp = vn;
345 		VREF(vn);
346 		PFS_RETURN (0);
347 	}
348 
349 	/* parent */
350 	if (cnp->cn_flags & ISDOTDOT) {
351 		if (pd->pn_type == pfstype_root)
352 			PFS_RETURN (EIO);
353 		KASSERT(pd->pn_parent, ("non-root directory has no parent"));
354 		/*
355 		 * This one is tricky.  Descendents of procdir nodes
356 		 * inherit their parent's process affinity, but
357 		 * there's no easy reverse mapping.  For simplicity,
358 		 * we assume that if this node is a procdir, its
359 		 * parent isn't (which is correct as long as
360 		 * descendents of procdir nodes are never procdir
361 		 * nodes themselves)
362 		 */
363 		if (pd->pn_type == pfstype_procdir)
364 			pid = NO_PID;
365 		pn = pd->pn_parent;
366 		goto got_pnode;
367 	}
368 
369 	/* named node */
370 	for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
371 		if (pn->pn_type == pfstype_procdir)
372 			pdn = pn;
373 		else if (pn->pn_name[namelen] == '\0'
374 		    && bcmp(pname, pn->pn_name, namelen) == 0)
375 			goto got_pnode;
376 
377 	/* process dependent node */
378 	if ((pn = pdn) != NULL) {
379 		pid = 0;
380 		for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
381 			if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
382 				break;
383 		if (i == cnp->cn_namelen)
384 			goto got_pnode;
385 	}
386 
387 	PFS_RETURN (ENOENT);
388  got_pnode:
389 	if (pn != pd->pn_parent && !pn->pn_parent)
390 		pn->pn_parent = pd;
391 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
392 		PFS_RETURN (ENOENT);
393 	error = pfs_vncache_alloc(vn->v_mount, vpp, pn, pid);
394 	if (error)
395 		PFS_RETURN (error);
396 	/*
397 	 * XXX See comment at top of the routine.
398 	 */
399 	if (cnp->cn_flags & MAKEENTRY)
400 		cache_enter(vn, *vpp, cnp);
401 	PFS_RETURN (0);
402 }
403 
404 /*
405  * Open a file or directory.
406  */
407 static int
408 pfs_open(struct vop_open_args *va)
409 {
410 	struct vnode *vn = va->a_vp;
411 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
412 	struct pfs_node *pn = pvd->pvd_pn;
413 	int mode = va->a_mode;
414 
415 	PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode));
416 
417 	/*
418 	 * check if the file is visible to the caller
419 	 *
420 	 * XXX Not sure if this is necessary, as the VFS system calls
421          * XXX pfs_lookup() and pfs_access() first, and pfs_lookup()
422          * XXX calls pfs_visible().  There's a race condition here, but
423 	 * XXX calling pfs_visible() from here doesn't really close it,
424 	 * XXX and the only consequence of that race is an EIO further
425 	 * XXX down the line.
426 	 */
427 	if (!pfs_visible(va->a_td, pn, pvd->pvd_pid))
428 		PFS_RETURN (ENOENT);
429 
430 	/* check if the requested mode is permitted */
431 	if (((mode & FREAD) && !(mode & PFS_RD)) ||
432 	    ((mode & FWRITE) && !(mode & PFS_WR)))
433 		PFS_RETURN (EPERM);
434 
435 	/* we don't support locking */
436 	if ((mode & O_SHLOCK) || (mode & O_EXLOCK))
437 		PFS_RETURN (EOPNOTSUPP);
438 
439 	PFS_RETURN (0);
440 }
441 
442 /*
443  * Read from a file
444  */
445 static int
446 pfs_read(struct vop_read_args *va)
447 {
448 	struct vnode *vn = va->a_vp;
449 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
450 	struct pfs_node *pn = pvd->pvd_pn;
451 	struct uio *uio = va->a_uio;
452 	struct proc *proc = NULL;
453 	struct sbuf *sb = NULL;
454 	char *ps;
455 	int error, xlen;
456 
457 	PFS_TRACE((pn->pn_name));
458 
459 	if (vn->v_type != VREG)
460 		PFS_RETURN (EINVAL);
461 
462 	if (!(pn->pn_flags & PFS_RD))
463 		PFS_RETURN (EBADF);
464 
465 	if (pn->pn_func == NULL)
466 		PFS_RETURN (EIO);
467 
468 	/*
469 	 * This is necessary because either process' privileges may
470 	 * have changed since the open() call.
471 	 */
472 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
473 		PFS_RETURN (EIO);
474 
475 	/* XXX duplicates bits of pfs_visible() */
476 	if (pvd->pvd_pid != NO_PID) {
477 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
478 			PFS_RETURN (EIO);
479 		_PHOLD(proc);
480 		PROC_UNLOCK(proc);
481 	}
482 
483 	if (pn->pn_flags & PFS_RAWRD) {
484 		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
485 		if (proc != NULL)
486 			PRELE(proc);
487 		PFS_RETURN (error);
488 	}
489 
490 	sb = sbuf_new(sb, NULL, uio->uio_offset + uio->uio_resid, 0);
491 	if (sb == NULL) {
492 		if (proc != NULL)
493 			PRELE(proc);
494 		PFS_RETURN (EIO);
495 	}
496 
497 	error = (pn->pn_func)(curthread, proc, pn, sb, uio);
498 
499 	if (proc != NULL)
500 		PRELE(proc);
501 
502 	if (error) {
503 		sbuf_delete(sb);
504 		PFS_RETURN (error);
505 	}
506 
507 	/* XXX we should possibly detect and handle overflows */
508 	sbuf_finish(sb);
509 	ps = sbuf_data(sb) + uio->uio_offset;
510 	xlen = sbuf_len(sb) - uio->uio_offset;
511 	xlen = imin(xlen, uio->uio_resid);
512 	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
513 	sbuf_delete(sb);
514 	PFS_RETURN (error);
515 }
516 
517 /*
518  * Iterate through directory entries
519  */
520 static int
521 pfs_iterate(struct thread *td, pid_t pid, struct pfs_node *pd,
522             struct pfs_node **pn, struct proc **p)
523 {
524 	if ((*pn) == NULL)
525 		*pn = pd->pn_nodes;
526 	else
527  again:
528 	if ((*pn)->pn_type != pfstype_procdir)
529 		*pn = (*pn)->pn_next;
530 
531 	while (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
532 		if (*p == NULL)
533 			*p = LIST_FIRST(&allproc);
534 		else
535 			*p = LIST_NEXT(*p, p_list);
536 		if (*p != NULL)
537 			break;
538 		*pn = (*pn)->pn_next;
539 	}
540 
541 	if ((*pn) == NULL)
542 		return (-1);
543 
544 	if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid))
545 		goto again;
546 
547 	return (0);
548 }
549 
550 /*
551  * Return directory entries.
552  */
553 static int
554 pfs_readdir(struct vop_readdir_args *va)
555 {
556 	struct vnode *vn = va->a_vp;
557 	struct pfs_info *pi = (struct pfs_info *)vn->v_mount->mnt_data;
558 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
559 	struct pfs_node *pd = pvd->pvd_pn;
560 	pid_t pid = pvd->pvd_pid;
561 	struct pfs_node *pn;
562 	struct dirent entry;
563 	struct uio *uio;
564 	struct proc *p;
565 	off_t offset;
566 	int error, i, resid;
567 
568 	PFS_TRACE((pd->pn_name));
569 
570 	if (vn->v_type != VDIR)
571 		PFS_RETURN (ENOTDIR);
572 	uio = va->a_uio;
573 
574 	/* check if the directory is visible to the caller */
575 	if (!pfs_visible(curthread, pd, pid))
576 		PFS_RETURN (ENOENT);
577 
578 	/* only allow reading entire entries */
579 	offset = uio->uio_offset;
580 	resid = uio->uio_resid;
581 	if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN)
582 		PFS_RETURN (EINVAL);
583 
584 	/* skip unwanted entries */
585 	sx_slock(&allproc_lock);
586 	for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN)
587 		if (pfs_iterate(curthread, pid, pd, &pn, &p) == -1) {
588 			/* nothing left... */
589 			sx_sunlock(&allproc_lock);
590 			PFS_RETURN (0);
591 		}
592 
593 	/* fill in entries */
594 	entry.d_reclen = PFS_DELEN;
595 	while (pfs_iterate(curthread, pid, pd, &pn, &p) != -1 && resid > 0) {
596 		if (!pn->pn_parent)
597 			pn->pn_parent = pd;
598 		if (!pn->pn_fileno)
599 			pfs_fileno_alloc(pi, pn);
600 		if (pid != NO_PID)
601 			entry.d_fileno = pn->pn_fileno * NO_PID + pid;
602 		else
603 			entry.d_fileno = pn->pn_fileno;
604 		/* PFS_DELEN was picked to fit PFS_NAMLEN */
605 		for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
606 			entry.d_name[i] = pn->pn_name[i];
607 		entry.d_name[i] = 0;
608 		entry.d_namlen = i;
609 		switch (pn->pn_type) {
610 		case pfstype_procdir:
611 			KASSERT(p != NULL,
612 			    ("reached procdir node with p == NULL"));
613 			entry.d_fileno = pn->pn_fileno * NO_PID + p->p_pid;
614 			entry.d_namlen = snprintf(entry.d_name,
615 			    PFS_NAMELEN, "%d", p->p_pid);
616 			/* fall through */
617 		case pfstype_root:
618 		case pfstype_dir:
619 		case pfstype_this:
620 		case pfstype_parent:
621 			entry.d_type = DT_DIR;
622 			break;
623 		case pfstype_file:
624 			entry.d_type = DT_REG;
625 			break;
626 		case pfstype_symlink:
627 			entry.d_type = DT_LNK;
628 			break;
629 		default:
630 			sx_sunlock(&allproc_lock);
631 			panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
632 		}
633 		PFS_TRACE((entry.d_name));
634 		if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) {
635 			sx_sunlock(&allproc_lock);
636 			PFS_RETURN (error);
637 		}
638 		offset += PFS_DELEN;
639 		resid -= PFS_DELEN;
640 	}
641 
642 	sx_sunlock(&allproc_lock);
643 	uio->uio_offset += offset;
644 	PFS_RETURN (0);
645 }
646 
647 /*
648  * Read a symbolic link
649  */
650 static int
651 pfs_readlink(struct vop_readlink_args *va)
652 {
653 	struct vnode *vn = va->a_vp;
654 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
655 	struct pfs_node *pn = pvd->pvd_pn;
656 	struct uio *uio = va->a_uio;
657 	struct proc *proc = NULL;
658 	char buf[MAXPATHLEN], *ps;
659 	struct sbuf sb;
660 	int error, xlen;
661 
662 	PFS_TRACE((pn->pn_name));
663 
664 	if (vn->v_type != VLNK)
665 		PFS_RETURN (EINVAL);
666 
667 	if (pn->pn_func == NULL)
668 		PFS_RETURN (EIO);
669 
670 	if (pvd->pvd_pid != NO_PID) {
671 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
672 			PFS_RETURN (EIO);
673 		_PHOLD(proc);
674 		PROC_UNLOCK(proc);
675 	}
676 
677 	/* sbuf_new() can't fail with a static buffer */
678 	sbuf_new(&sb, buf, sizeof buf, 0);
679 
680 	error = (pn->pn_func)(curthread, proc, pn, &sb, NULL);
681 
682 	if (proc != NULL)
683 		PRELE(proc);
684 
685 	if (error) {
686 		sbuf_delete(&sb);
687 		PFS_RETURN (error);
688 	}
689 
690 	/* XXX we should detect and handle overflows */
691 	sbuf_finish(&sb);
692 	ps = sbuf_data(&sb) + uio->uio_offset;
693 	xlen = sbuf_len(&sb) - uio->uio_offset;
694 	xlen = imin(xlen, uio->uio_resid);
695 	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
696 	sbuf_delete(&sb);
697 	PFS_RETURN (error);
698 }
699 
700 /*
701  * Reclaim a vnode
702  */
703 static int
704 pfs_reclaim(struct vop_reclaim_args *va)
705 {
706 	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
707 
708 	return (pfs_vncache_free(va->a_vp));
709 }
710 
711 /*
712  * Set attributes
713  */
714 static int
715 pfs_setattr(struct vop_setattr_args *va)
716 {
717 	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
718 
719 	PFS_RETURN (EOPNOTSUPP);
720 }
721 
722 /*
723  * Read from a file
724  */
725 static int
726 pfs_write(struct vop_read_args *va)
727 {
728 	struct vnode *vn = va->a_vp;
729 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
730 	struct pfs_node *pn = pvd->pvd_pn;
731 	struct uio *uio = va->a_uio;
732 	struct proc *proc = NULL;
733 	struct sbuf sb;
734 	int error;
735 
736 	PFS_TRACE((pn->pn_name));
737 
738 	if (vn->v_type != VREG)
739 		PFS_RETURN (EINVAL);
740 
741 	if (!(pn->pn_flags & PFS_WR))
742 		PFS_RETURN (EBADF);
743 
744 	if (pn->pn_func == NULL)
745 		PFS_RETURN (EIO);
746 
747 	/*
748 	 * This is necessary because either process' privileges may
749 	 * have changed since the open() call.
750 	 */
751 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
752 		PFS_RETURN (EIO);
753 
754 	/* XXX duplicates bits of pfs_visible() */
755 	if (pvd->pvd_pid != NO_PID) {
756 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
757 			PFS_RETURN (EIO);
758 		_PHOLD(proc);
759 		PROC_UNLOCK(proc);
760 	}
761 
762 	if (pn->pn_flags & PFS_RAWWR) {
763 		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
764 		if (proc != NULL)
765 			PRELE(proc);
766 		PFS_RETURN (error);
767 	}
768 
769 	sbuf_uionew(&sb, uio, &error);
770 	if (error)
771 		PFS_RETURN (error);
772 
773 	error = (pn->pn_func)(curthread, proc, pn, &sb, uio);
774 
775 	if (proc != NULL)
776 		PRELE(proc);
777 
778 	sbuf_delete(&sb);
779 	PFS_RETURN (error);
780 }
781 
782 /*
783  * Vnode operations
784  */
785 vop_t **pfs_vnodeop_p;
786 static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = {
787 	{ &vop_default_desc,		(vop_t *)vop_defaultop	},
788 	{ &vop_access_desc,		(vop_t *)pfs_access	},
789 	{ &vop_close_desc,		(vop_t *)pfs_close	},
790 	{ &vop_create_desc,		(vop_t *)vop_eopnotsupp	},
791 	{ &vop_getattr_desc,		(vop_t *)pfs_getattr	},
792 	{ &vop_getextattr_desc,		(vop_t *)pfs_getextattr	},
793 	{ &vop_ioctl_desc,		(vop_t *)pfs_ioctl	},
794 	{ &vop_link_desc,		(vop_t *)vop_eopnotsupp	},
795 	{ &vop_lookup_desc,		(vop_t *)pfs_lookup	},
796 	{ &vop_mkdir_desc,		(vop_t *)vop_eopnotsupp	},
797 	{ &vop_mknod_desc,		(vop_t *)vop_eopnotsupp	},
798 	{ &vop_open_desc,		(vop_t *)pfs_open	},
799 	{ &vop_read_desc,		(vop_t *)pfs_read	},
800 	{ &vop_readdir_desc,		(vop_t *)pfs_readdir	},
801 	{ &vop_readlink_desc,		(vop_t *)pfs_readlink	},
802 	{ &vop_reclaim_desc,		(vop_t *)pfs_reclaim	},
803 	{ &vop_remove_desc,		(vop_t *)vop_eopnotsupp	},
804 	{ &vop_rename_desc,		(vop_t *)vop_eopnotsupp	},
805 	{ &vop_rmdir_desc,		(vop_t *)vop_eopnotsupp	},
806 	{ &vop_setattr_desc,		(vop_t *)pfs_setattr	},
807 	{ &vop_symlink_desc,		(vop_t *)vop_eopnotsupp	},
808 	{ &vop_write_desc,		(vop_t *)pfs_write	},
809 	/* XXX I've probably forgotten a few that need vop_eopnotsupp */
810 	{ NULL,				(vop_t *)NULL		}
811 };
812 
813 static struct vnodeopv_desc pfs_vnodeop_opv_desc =
814 	{ &pfs_vnodeop_p, pfs_vnodeop_entries };
815 
816 VNODEOP_SET(pfs_vnodeop_opv_desc);
817