xref: /freebsd/sys/fs/pseudofs/pseudofs_vnops.c (revision 8fa113e5fc65fe6abc757f0089f477a87ee4d185)
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: ", __FUNCTION__, __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 		    __FUNCTION__, __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 		/* XXX should lock td->td_proc? */
90 		if (p_cansee(td->td_proc, proc) != 0 ||
91 		    (pn->pn_vis != NULL && !(pn->pn_vis)(td, proc, pn)))
92 			r = 0;
93 		PROC_UNLOCK(proc);
94 	}
95 	PFS_RETURN (r);
96 }
97 
98 /*
99  * Verify permissions
100  */
101 static int
102 pfs_access(struct vop_access_args *va)
103 {
104 	struct vnode *vn = va->a_vp;
105 	struct vattr vattr;
106 	int error;
107 
108 	PFS_TRACE((((struct pfs_vdata *)vn->v_data)->pvd_pn->pn_name));
109 
110 	error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td);
111 	if (error)
112 		PFS_RETURN (error);
113 	error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid,
114 	    vattr.va_gid, va->a_mode, va->a_cred, NULL);
115 	PFS_RETURN (error);
116 }
117 
118 /*
119  * Close a file or directory
120  */
121 static int
122 pfs_close(struct vop_close_args *va)
123 {
124 	struct vnode *vn = va->a_vp;
125 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
126 	struct pfs_node *pn = pvd->pvd_pn;
127 	struct proc *proc;
128 	int error;
129 
130 	PFS_TRACE((pn->pn_name));
131 
132 	/*
133 	 * Do nothing unless this is the last close and the node has a
134 	 * last-close handler.
135 	 */
136 	if (vn->v_usecount > 1 || pn->pn_close == NULL)
137 		PFS_RETURN (0);
138 
139 	if (pvd->pvd_pid != NO_PID)
140 		proc = pfind(pvd->pvd_pid);
141 	else
142 		proc = NULL;
143 
144 	error = (pn->pn_close)(va->a_td, proc, pn);
145 
146 	if (proc != NULL)
147 		PROC_UNLOCK(proc);
148 
149 	PFS_RETURN (error);
150 }
151 
152 /*
153  * Get file attributes
154  */
155 static int
156 pfs_getattr(struct vop_getattr_args *va)
157 {
158 	struct vnode *vn = va->a_vp;
159 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
160 	struct pfs_node *pn = pvd->pvd_pn;
161 	struct vattr *vap = va->a_vap;
162 	struct proc *proc;
163 	int error = 0;
164 
165 	PFS_TRACE((pn->pn_name));
166 
167 	VATTR_NULL(vap);
168 	vap->va_type = vn->v_type;
169 	vap->va_fileid = pn->pn_fileno;
170 	vap->va_flags = 0;
171 	vap->va_blocksize = PAGE_SIZE;
172 	vap->va_bytes = vap->va_size = 0;
173 	vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
174 	vap->va_nlink = 1;
175 	nanotime(&vap->va_ctime);
176 	vap->va_atime = vap->va_mtime = vap->va_ctime;
177 
178 	switch (pn->pn_type) {
179 	case pfstype_procdir:
180 	case pfstype_root:
181 	case pfstype_dir:
182 		vap->va_mode = 0555;
183 		break;
184 	case pfstype_file:
185 	case pfstype_symlink:
186 		vap->va_mode = 0444;
187 		break;
188 	default:
189 		printf("shouldn't be here!\n");
190 		vap->va_mode = 0;
191 		break;
192 	}
193 
194 	if (pvd->pvd_pid != NO_PID) {
195 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
196 			PFS_RETURN (ENOENT);
197 		vap->va_uid = proc->p_ucred->cr_ruid;
198 		vap->va_gid = proc->p_ucred->cr_rgid;
199 		if (pn->pn_attr != NULL)
200 			error = (pn->pn_attr)(va->a_td, proc, pn, vap);
201 		PROC_UNLOCK(proc);
202 	} else {
203 		vap->va_uid = 0;
204 		vap->va_gid = 0;
205 	}
206 
207 	PFS_RETURN (error);
208 }
209 
210 /*
211  * Perform an ioctl
212  */
213 static int
214 pfs_ioctl(struct vop_ioctl_args *va)
215 {
216 	struct vnode *vn = va->a_vp;
217 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
218 	struct pfs_node *pn = pvd->pvd_pn;
219 	struct proc *proc = NULL;
220 	int error;
221 
222 	PFS_TRACE(("%s: %d", pn->pn_name, va->a_com));
223 
224 	if (vn->v_type != VREG)
225 		PFS_RETURN (EINVAL);
226 
227 	if (pn->pn_ioctl == NULL)
228 		PFS_RETURN (ENOTTY);
229 
230 	/*
231 	 * This is necessary because either process' privileges may
232 	 * have changed since the open() call.
233 	 */
234 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
235 		PFS_RETURN (EIO);
236 
237 	/* XXX duplicates bits of pfs_visible() */
238 	if (pvd->pvd_pid != NO_PID) {
239 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
240 			PFS_RETURN (EIO);
241 		_PHOLD(proc);
242 		PROC_UNLOCK(proc);
243 	}
244 
245 	error = (pn->pn_ioctl)(curthread, proc, pn, va->a_command, va->a_data);
246 
247 	if (proc != NULL)
248 		PRELE(proc);
249 
250 	PFS_RETURN (error);
251 }
252 
253 /*
254  * Look up a file or directory
255  */
256 static int
257 pfs_lookup(struct vop_lookup_args *va)
258 {
259 	struct vnode *vn = va->a_dvp;
260 	struct vnode **vpp = va->a_vpp;
261 	struct componentname *cnp = va->a_cnp;
262 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
263 	struct pfs_node *pd = pvd->pvd_pn;
264 	struct pfs_node *pn, *pdn = NULL;
265 	pid_t pid = pvd->pvd_pid;
266 	char *pname;
267 	int error, i, namelen;
268 
269 	PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr));
270 
271 	if (vn->v_type != VDIR)
272 		PFS_RETURN (ENOTDIR);
273 
274 	/*
275 	 * Don't support DELETE or RENAME.  CREATE is supported so
276 	 * that O_CREAT will work, but the lookup will still fail if
277 	 * the file does not exist.
278 	 */
279 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
280 		PFS_RETURN (EOPNOTSUPP);
281 
282 	/* shortcut: check if the name is too long */
283 	if (cnp->cn_namelen >= PFS_NAMELEN)
284 		PFS_RETURN (ENOENT);
285 
286 	/* check that parent directory is visisble... */
287 	if (!pfs_visible(curthread, pd, pvd->pvd_pid))
288 		PFS_RETURN (ENOENT);
289 
290 	/* self */
291 	namelen = cnp->cn_namelen;
292 	pname = cnp->cn_nameptr;
293 	if (namelen == 1 && *pname == '.') {
294 		pn = pd;
295 		*vpp = vn;
296 		VREF(vn);
297 		PFS_RETURN (0);
298 	}
299 
300 	/* parent */
301 	if (cnp->cn_flags & ISDOTDOT) {
302 		if (pd->pn_type == pfstype_root)
303 			PFS_RETURN (EIO);
304 		KASSERT(pd->pn_parent, ("non-root directory has no parent"));
305 		/*
306 		 * This one is tricky.  Descendents of procdir nodes
307 		 * inherit their parent's process affinity, but
308 		 * there's no easy reverse mapping.  For simplicity,
309 		 * we assume that if this node is a procdir, its
310 		 * parent isn't (which is correct as long as
311 		 * descendents of procdir nodes are never procdir
312 		 * nodes themselves)
313 		 */
314 		if (pd->pn_type == pfstype_procdir)
315 			pid = NO_PID;
316 		pn = pd->pn_parent;
317 		goto got_pnode;
318 	}
319 
320 	/* named node */
321 	for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
322 		if (pn->pn_type == pfstype_procdir)
323 			pdn = pn;
324 		else if (pn->pn_name[namelen] == '\0'
325 		    && bcmp(pname, pn->pn_name, namelen) == 0)
326 			goto got_pnode;
327 
328 	/* process dependent node */
329 	if ((pn = pdn) != NULL) {
330 		pid = 0;
331 		for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
332 			if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
333 				break;
334 		if (i == cnp->cn_namelen)
335 			goto got_pnode;
336 	}
337 
338 	PFS_RETURN (ENOENT);
339  got_pnode:
340 	if (pn != pd->pn_parent && !pn->pn_parent)
341 		pn->pn_parent = pd;
342 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
343 		PFS_RETURN (ENOENT);
344 	error = pfs_vncache_alloc(vn->v_mount, vpp, pn, pid);
345 	if (error)
346 		PFS_RETURN (error);
347 	if (cnp->cn_flags & MAKEENTRY)
348 		cache_enter(vn, *vpp, cnp);
349 	PFS_RETURN (0);
350 }
351 
352 /*
353  * Open a file or directory.
354  */
355 static int
356 pfs_open(struct vop_open_args *va)
357 {
358 	struct vnode *vn = va->a_vp;
359 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
360 	struct pfs_node *pn = pvd->pvd_pn;
361 	int mode = va->a_mode;
362 
363 	PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode));
364 
365 	/*
366 	 * check if the file is visible to the caller
367 	 *
368 	 * XXX Not sure if this is necessary, as the VFS system calls
369          * XXX pfs_lookup() and pfs_access() first, and pfs_lookup()
370          * XXX calls pfs_visible().  There's a race condition here, but
371 	 * XXX calling pfs_visible() from here doesn't really close it,
372 	 * XXX and the only consequence of that race is an EIO further
373 	 * XXX down the line.
374 	 */
375 	if (!pfs_visible(va->a_td, pn, pvd->pvd_pid))
376 		PFS_RETURN (ENOENT);
377 
378 	/* check if the requested mode is permitted */
379 	if (((mode & FREAD) && !(mode & PFS_RD)) ||
380 	    ((mode & FWRITE) && !(mode & PFS_WR)))
381 		PFS_RETURN (EPERM);
382 
383 	/* we don't support locking */
384 	if ((mode & O_SHLOCK) || (mode & O_EXLOCK))
385 		PFS_RETURN (EOPNOTSUPP);
386 
387 	PFS_RETURN (0);
388 }
389 
390 /*
391  * Read from a file
392  */
393 static int
394 pfs_read(struct vop_read_args *va)
395 {
396 	struct vnode *vn = va->a_vp;
397 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
398 	struct pfs_node *pn = pvd->pvd_pn;
399 	struct uio *uio = va->a_uio;
400 	struct proc *proc = NULL;
401 	struct sbuf *sb = NULL;
402 	char *ps;
403 	int error, xlen;
404 
405 	PFS_TRACE((pn->pn_name));
406 
407 	if (vn->v_type != VREG)
408 		PFS_RETURN (EINVAL);
409 
410 	if (!(pn->pn_flags & PFS_RD))
411 		PFS_RETURN (EBADF);
412 
413 	if (pn->pn_func == NULL)
414 		PFS_RETURN (EIO);
415 
416 	/*
417 	 * This is necessary because either process' privileges may
418 	 * have changed since the open() call.
419 	 */
420 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
421 		PFS_RETURN (EIO);
422 
423 	/* XXX duplicates bits of pfs_visible() */
424 	if (pvd->pvd_pid != NO_PID) {
425 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
426 			PFS_RETURN (EIO);
427 		_PHOLD(proc);
428 		PROC_UNLOCK(proc);
429 	}
430 
431 	if (pn->pn_flags & PFS_RAWRD) {
432 		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
433 		if (proc != NULL)
434 			PRELE(proc);
435 		PFS_RETURN (error);
436 	}
437 
438 	sb = sbuf_new(sb, NULL, uio->uio_offset + uio->uio_resid, 0);
439 	if (sb == NULL) {
440 		if (proc != NULL)
441 			PRELE(proc);
442 		PFS_RETURN (EIO);
443 	}
444 
445 	error = (pn->pn_func)(curthread, proc, pn, sb, uio);
446 
447 	if (proc != NULL)
448 		PRELE(proc);
449 
450 	if (error) {
451 		sbuf_delete(sb);
452 		PFS_RETURN (error);
453 	}
454 
455 	/* XXX we should possibly detect and handle overflows */
456 	sbuf_finish(sb);
457 	ps = sbuf_data(sb) + uio->uio_offset;
458 	xlen = sbuf_len(sb) - uio->uio_offset;
459 	xlen = imin(xlen, uio->uio_resid);
460 	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
461 	sbuf_delete(sb);
462 	PFS_RETURN (error);
463 }
464 
465 /*
466  * Iterate through directory entries
467  */
468 static int
469 pfs_iterate(struct thread *td, pid_t pid, struct pfs_node *pd,
470             struct pfs_node **pn, struct proc **p)
471 {
472 	if ((*pn) == NULL)
473 		*pn = pd->pn_nodes;
474 	else
475  again:
476 	if ((*pn)->pn_type != pfstype_procdir)
477 		*pn = (*pn)->pn_next;
478 
479 	while (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
480 		if (*p == NULL)
481 			*p = LIST_FIRST(&allproc);
482 		else
483 			*p = LIST_NEXT(*p, p_list);
484 		if (*p != NULL)
485 			break;
486 		*pn = (*pn)->pn_next;
487 	}
488 
489 	if ((*pn) == NULL)
490 		return (-1);
491 
492 	if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid))
493 		goto again;
494 
495 	return (0);
496 }
497 
498 /*
499  * Return directory entries.
500  */
501 static int
502 pfs_readdir(struct vop_readdir_args *va)
503 {
504 	struct vnode *vn = va->a_vp;
505 	struct pfs_info *pi = (struct pfs_info *)vn->v_mount->mnt_data;
506 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
507 	struct pfs_node *pd = pvd->pvd_pn;
508 	pid_t pid = pvd->pvd_pid;
509 	struct pfs_node *pn;
510 	struct dirent entry;
511 	struct uio *uio;
512 	struct proc *p;
513 	off_t offset;
514 	int error, i, resid;
515 
516 	PFS_TRACE((pd->pn_name));
517 
518 	if (vn->v_type != VDIR)
519 		PFS_RETURN (ENOTDIR);
520 	uio = va->a_uio;
521 
522 	/* check if the directory is visible to the caller */
523 	if (!pfs_visible(curthread, pd, pid))
524 		PFS_RETURN (ENOENT);
525 
526 	/* only allow reading entire entries */
527 	offset = uio->uio_offset;
528 	resid = uio->uio_resid;
529 	if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN)
530 		PFS_RETURN (EINVAL);
531 
532 	/* skip unwanted entries */
533 	sx_slock(&allproc_lock);
534 	for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN)
535 		if (pfs_iterate(curthread, pid, pd, &pn, &p) == -1) {
536 			/* nothing left... */
537 			sx_sunlock(&allproc_lock);
538 			PFS_RETURN (0);
539 		}
540 
541 	/* fill in entries */
542 	entry.d_reclen = PFS_DELEN;
543 	while (pfs_iterate(curthread, pid, pd, &pn, &p) != -1 && resid > 0) {
544 		if (!pn->pn_parent)
545 			pn->pn_parent = pd;
546 		if (!pn->pn_fileno)
547 			pfs_fileno_alloc(pi, pn);
548 		if (pid != NO_PID)
549 			entry.d_fileno = pn->pn_fileno * NO_PID + pid;
550 		else
551 			entry.d_fileno = pn->pn_fileno;
552 		/* PFS_DELEN was picked to fit PFS_NAMLEN */
553 		for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
554 			entry.d_name[i] = pn->pn_name[i];
555 		entry.d_name[i] = 0;
556 		entry.d_namlen = i;
557 		switch (pn->pn_type) {
558 		case pfstype_procdir:
559 			KASSERT(p != NULL,
560 			    ("reached procdir node with p == NULL"));
561 			entry.d_fileno = pn->pn_fileno * NO_PID + p->p_pid;
562 			entry.d_namlen = snprintf(entry.d_name,
563 			    PFS_NAMELEN, "%d", p->p_pid);
564 			/* fall through */
565 		case pfstype_root:
566 		case pfstype_dir:
567 		case pfstype_this:
568 		case pfstype_parent:
569 			entry.d_type = DT_DIR;
570 			break;
571 		case pfstype_file:
572 			entry.d_type = DT_REG;
573 			break;
574 		case pfstype_symlink:
575 			entry.d_type = DT_LNK;
576 			break;
577 		default:
578 			sx_sunlock(&allproc_lock);
579 			panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
580 		}
581 		PFS_TRACE((entry.d_name));
582 		if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) {
583 			sx_sunlock(&allproc_lock);
584 			PFS_RETURN (error);
585 		}
586 		offset += PFS_DELEN;
587 		resid -= PFS_DELEN;
588 	}
589 
590 	sx_sunlock(&allproc_lock);
591 	uio->uio_offset += offset;
592 	PFS_RETURN (0);
593 }
594 
595 /*
596  * Read a symbolic link
597  */
598 static int
599 pfs_readlink(struct vop_readlink_args *va)
600 {
601 	struct vnode *vn = va->a_vp;
602 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
603 	struct pfs_node *pn = pvd->pvd_pn;
604 	struct uio *uio = va->a_uio;
605 	struct proc *proc = NULL;
606 	char buf[MAXPATHLEN], *ps;
607 	struct sbuf sb;
608 	int error, xlen;
609 
610 	PFS_TRACE((pn->pn_name));
611 
612 	if (vn->v_type != VLNK)
613 		PFS_RETURN (EINVAL);
614 
615 	if (pn->pn_func == NULL)
616 		PFS_RETURN (EIO);
617 
618 	if (pvd->pvd_pid != NO_PID) {
619 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
620 			PFS_RETURN (EIO);
621 		_PHOLD(proc);
622 		PROC_UNLOCK(proc);
623 	}
624 
625 	/* sbuf_new() can't fail with a static buffer */
626 	sbuf_new(&sb, buf, sizeof buf, 0);
627 
628 	error = (pn->pn_func)(curthread, proc, pn, &sb, NULL);
629 
630 	if (proc != NULL)
631 		PRELE(proc);
632 
633 	if (error) {
634 		sbuf_delete(&sb);
635 		PFS_RETURN (error);
636 	}
637 
638 	/* XXX we should detect and handle overflows */
639 	sbuf_finish(&sb);
640 	ps = sbuf_data(&sb) + uio->uio_offset;
641 	xlen = sbuf_len(&sb) - uio->uio_offset;
642 	xlen = imin(xlen, uio->uio_resid);
643 	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
644 	sbuf_delete(&sb);
645 	PFS_RETURN (error);
646 }
647 
648 /*
649  * Reclaim a vnode
650  */
651 static int
652 pfs_reclaim(struct vop_reclaim_args *va)
653 {
654 	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
655 
656 	return (pfs_vncache_free(va->a_vp));
657 }
658 
659 /*
660  * Set attributes
661  */
662 static int
663 pfs_setattr(struct vop_setattr_args *va)
664 {
665 	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
666 
667 	if (va->a_vap->va_flags != (u_long)VNOVAL)
668 		PFS_RETURN (EOPNOTSUPP);
669 	/* XXX it's a bit more complex than that, really... */
670 	PFS_RETURN (0);
671 }
672 
673 /*
674  * Read from a file
675  */
676 static int
677 pfs_write(struct vop_read_args *va)
678 {
679 	struct vnode *vn = va->a_vp;
680 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
681 	struct pfs_node *pn = pvd->pvd_pn;
682 	struct uio *uio = va->a_uio;
683 	struct proc *proc = NULL;
684 	struct sbuf sb;
685 	int error;
686 
687 	PFS_TRACE((pn->pn_name));
688 
689 	if (vn->v_type != VREG)
690 		PFS_RETURN (EINVAL);
691 
692 	if (!(pn->pn_flags & PFS_WR))
693 		PFS_RETURN (EBADF);
694 
695 	if (pn->pn_func == NULL)
696 		PFS_RETURN (EIO);
697 
698 	/*
699 	 * This is necessary because either process' privileges may
700 	 * have changed since the open() call.
701 	 */
702 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
703 		PFS_RETURN (EIO);
704 
705 	/* XXX duplicates bits of pfs_visible() */
706 	if (pvd->pvd_pid != NO_PID) {
707 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
708 			PFS_RETURN (EIO);
709 		_PHOLD(proc);
710 		PROC_UNLOCK(proc);
711 	}
712 
713 	if (pn->pn_flags & PFS_RAWWR) {
714 		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
715 		if (proc != NULL)
716 			PRELE(proc);
717 		PFS_RETURN (error);
718 	}
719 
720 	sbuf_uionew(&sb, uio, &error);
721 	if (error)
722 		PFS_RETURN (error);
723 
724 	error = (pn->pn_func)(curthread, proc, pn, &sb, uio);
725 
726 	if (proc != NULL)
727 		PRELE(proc);
728 
729 	sbuf_delete(&sb);
730 	PFS_RETURN (error);
731 }
732 
733 /*
734  * Dummy operations */
735 static int pfs_badop(void *va)		{ return (EOPNOTSUPP); }
736 #if 0
737 static int pfs_erofs(void *va)		{ return (EROFS); }
738 static int pfs_null(void *va)		{ return (0); }
739 #endif
740 
741 /*
742  * Vnode operations
743  */
744 vop_t **pfs_vnodeop_p;
745 static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = {
746 	{ &vop_default_desc,		(vop_t *)vop_defaultop	},
747 	{ &vop_access_desc,		(vop_t *)pfs_access	},
748 	{ &vop_close_desc,		(vop_t *)pfs_close	},
749 	{ &vop_create_desc,		(vop_t *)pfs_badop	},
750 	{ &vop_getattr_desc,		(vop_t *)pfs_getattr	},
751 	{ &vop_ioctl_desc,		(vop_t *)pfs_ioctl	},
752 	{ &vop_link_desc,		(vop_t *)pfs_badop	},
753 	{ &vop_lookup_desc,		(vop_t *)pfs_lookup	},
754 	{ &vop_mkdir_desc,		(vop_t *)pfs_badop	},
755 	{ &vop_mknod_desc,		(vop_t *)pfs_badop	},
756 	{ &vop_open_desc,		(vop_t *)pfs_open	},
757 	{ &vop_read_desc,		(vop_t *)pfs_read	},
758 	{ &vop_readdir_desc,		(vop_t *)pfs_readdir	},
759 	{ &vop_readlink_desc,		(vop_t *)pfs_readlink	},
760 	{ &vop_reclaim_desc,		(vop_t *)pfs_reclaim	},
761 	{ &vop_remove_desc,		(vop_t *)pfs_badop	},
762 	{ &vop_rename_desc,		(vop_t *)pfs_badop	},
763 	{ &vop_rmdir_desc,		(vop_t *)pfs_badop	},
764 	{ &vop_setattr_desc,		(vop_t *)pfs_setattr	},
765 	{ &vop_symlink_desc,		(vop_t *)pfs_badop	},
766 	{ &vop_write_desc,		(vop_t *)pfs_write	},
767 	/* XXX I've probably forgotten a few that need pfs_badop */
768 	{ NULL,				(vop_t *)NULL		}
769 };
770 
771 static struct vnodeopv_desc pfs_vnodeop_opv_desc =
772 	{ &pfs_vnodeop_p, pfs_vnodeop_entries };
773 
774 VNODEOP_SET(pfs_vnodeop_opv_desc);
775