pseudofs_vnops.c revision 87541
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: head/sys/fs/pseudofs/pseudofs_vnops.c 87541 2001-12-09 00:28:12Z des $
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 */
73static int
74pfs_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 */
101static int
102pfs_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 */
121static int
122pfs_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 */
155static int
156pfs_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 */
213static int
214pfs_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: %lx", pn->pn_name, va->a_command));
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 */
256static int
257pfs_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 */
355static int
356pfs_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 */
393static int
394pfs_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 */
468static int
469pfs_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 */
501static int
502pfs_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 */
598static int
599pfs_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 */
651static int
652pfs_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 */
662static int
663pfs_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 */
676static int
677pfs_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 */
735static int pfs_badop(void *va)		{ return (EOPNOTSUPP); }
736#if 0
737static int pfs_erofs(void *va)		{ return (EROFS); }
738static int pfs_null(void *va)		{ return (0); }
739#endif
740
741/*
742 * Vnode operations
743 */
744vop_t **pfs_vnodeop_p;
745static 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
771static struct vnodeopv_desc pfs_vnodeop_opv_desc =
772	{ &pfs_vnodeop_p, pfs_vnodeop_entries };
773
774VNODEOP_SET(pfs_vnodeop_opv_desc);
775