pseudofs_vnops.c revision 90205
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 90205 2002-02-04 18:09:29Z rwatson $
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 * Perform getextattr
255 */
256static int
257pfs_getextattr(struct vop_getextattr_args *va)
258{
259	struct vnode *vn = va->a_vp;
260	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
261	struct pfs_node *pn = pvd->pvd_pn;
262	struct proc *proc = NULL;
263	int error;
264
265	PFS_TRACE((pd->pn_name));
266
267	if (pn->pn_getextattr == NULL)
268		PFS_RETURN (EOPNOTSUPP);
269
270	/*
271	 * This is necessary because either process' privileges may
272	 * have changed since the open() call.
273	 */
274	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
275		PFS_RETURN (EIO);
276
277	/* XXX duplicates bits of pfs_visible() */
278	if (pvd->pvd_pid != NO_PID) {
279		if ((proc = pfind(pvd->pvd_pid)) == NULL)
280			PFS_RETURN (EIO);
281		_PHOLD(proc);
282		PROC_UNLOCK(proc);
283	}
284
285	error = (pn->pn_getextattr)(curthread, proc, pn, va->a_attrnamespace,
286	    va->a_name, va->a_uio, va->a_cred);
287
288	if (proc != NULL)
289		PRELE(proc);
290
291	PFS_RETURN (error);
292}
293
294/*
295 * Look up a file or directory
296 *
297 * XXX NOTE!  pfs_lookup() has been hooked into vop_lookup_desc!  This
298 * will result in a lookup operation for a vnode which may already be
299 * cached, therefore we have to be careful to purge the VFS cache when
300 * reusing a vnode.
301 *
302 * This code will work, but is not really correct.  Normally we would hook
303 * vfs_cache_lookup() into vop_lookup_desc and hook pfs_lookup() into
304 * vop_cachedlookup_desc.
305 */
306static int
307pfs_lookup(struct vop_lookup_args *va)
308{
309	struct vnode *vn = va->a_dvp;
310	struct vnode **vpp = va->a_vpp;
311	struct componentname *cnp = va->a_cnp;
312	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
313	struct pfs_node *pd = pvd->pvd_pn;
314	struct pfs_node *pn, *pdn = NULL;
315	pid_t pid = pvd->pvd_pid;
316	char *pname;
317	int error, i, namelen;
318
319	PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr));
320
321	if (vn->v_type != VDIR)
322		PFS_RETURN (ENOTDIR);
323
324	/*
325	 * Don't support DELETE or RENAME.  CREATE is supported so
326	 * that O_CREAT will work, but the lookup will still fail if
327	 * the file does not exist.
328	 */
329	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
330		PFS_RETURN (EOPNOTSUPP);
331
332	/* shortcut: check if the name is too long */
333	if (cnp->cn_namelen >= PFS_NAMELEN)
334		PFS_RETURN (ENOENT);
335
336	/* check that parent directory is visisble... */
337	if (!pfs_visible(curthread, pd, pvd->pvd_pid))
338		PFS_RETURN (ENOENT);
339
340	/* self */
341	namelen = cnp->cn_namelen;
342	pname = cnp->cn_nameptr;
343	if (namelen == 1 && *pname == '.') {
344		pn = pd;
345		*vpp = vn;
346		VREF(vn);
347		PFS_RETURN (0);
348	}
349
350	/* parent */
351	if (cnp->cn_flags & ISDOTDOT) {
352		if (pd->pn_type == pfstype_root)
353			PFS_RETURN (EIO);
354		KASSERT(pd->pn_parent, ("non-root directory has no parent"));
355		/*
356		 * This one is tricky.  Descendents of procdir nodes
357		 * inherit their parent's process affinity, but
358		 * there's no easy reverse mapping.  For simplicity,
359		 * we assume that if this node is a procdir, its
360		 * parent isn't (which is correct as long as
361		 * descendents of procdir nodes are never procdir
362		 * nodes themselves)
363		 */
364		if (pd->pn_type == pfstype_procdir)
365			pid = NO_PID;
366		pn = pd->pn_parent;
367		goto got_pnode;
368	}
369
370	/* named node */
371	for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
372		if (pn->pn_type == pfstype_procdir)
373			pdn = pn;
374		else if (pn->pn_name[namelen] == '\0'
375		    && bcmp(pname, pn->pn_name, namelen) == 0)
376			goto got_pnode;
377
378	/* process dependent node */
379	if ((pn = pdn) != NULL) {
380		pid = 0;
381		for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
382			if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
383				break;
384		if (i == cnp->cn_namelen)
385			goto got_pnode;
386	}
387
388	PFS_RETURN (ENOENT);
389 got_pnode:
390	if (pn != pd->pn_parent && !pn->pn_parent)
391		pn->pn_parent = pd;
392	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
393		PFS_RETURN (ENOENT);
394	error = pfs_vncache_alloc(vn->v_mount, vpp, pn, pid);
395	if (error)
396		PFS_RETURN (error);
397	/*
398	 * XXX See comment at top of the routine.
399	 */
400	if (cnp->cn_flags & MAKEENTRY)
401		cache_enter(vn, *vpp, cnp);
402	PFS_RETURN (0);
403}
404
405/*
406 * Open a file or directory.
407 */
408static int
409pfs_open(struct vop_open_args *va)
410{
411	struct vnode *vn = va->a_vp;
412	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
413	struct pfs_node *pn = pvd->pvd_pn;
414	int mode = va->a_mode;
415
416	PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode));
417
418	/*
419	 * check if the file is visible to the caller
420	 *
421	 * XXX Not sure if this is necessary, as the VFS system calls
422         * XXX pfs_lookup() and pfs_access() first, and pfs_lookup()
423         * XXX calls pfs_visible().  There's a race condition here, but
424	 * XXX calling pfs_visible() from here doesn't really close it,
425	 * XXX and the only consequence of that race is an EIO further
426	 * XXX down the line.
427	 */
428	if (!pfs_visible(va->a_td, pn, pvd->pvd_pid))
429		PFS_RETURN (ENOENT);
430
431	/* check if the requested mode is permitted */
432	if (((mode & FREAD) && !(mode & PFS_RD)) ||
433	    ((mode & FWRITE) && !(mode & PFS_WR)))
434		PFS_RETURN (EPERM);
435
436	/* we don't support locking */
437	if ((mode & O_SHLOCK) || (mode & O_EXLOCK))
438		PFS_RETURN (EOPNOTSUPP);
439
440	PFS_RETURN (0);
441}
442
443/*
444 * Read from a file
445 */
446static int
447pfs_read(struct vop_read_args *va)
448{
449	struct vnode *vn = va->a_vp;
450	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
451	struct pfs_node *pn = pvd->pvd_pn;
452	struct uio *uio = va->a_uio;
453	struct proc *proc = NULL;
454	struct sbuf *sb = NULL;
455	char *ps;
456	int error, xlen;
457
458	PFS_TRACE((pn->pn_name));
459
460	if (vn->v_type != VREG)
461		PFS_RETURN (EINVAL);
462
463	if (!(pn->pn_flags & PFS_RD))
464		PFS_RETURN (EBADF);
465
466	if (pn->pn_func == NULL)
467		PFS_RETURN (EIO);
468
469	/*
470	 * This is necessary because either process' privileges may
471	 * have changed since the open() call.
472	 */
473	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
474		PFS_RETURN (EIO);
475
476	/* XXX duplicates bits of pfs_visible() */
477	if (pvd->pvd_pid != NO_PID) {
478		if ((proc = pfind(pvd->pvd_pid)) == NULL)
479			PFS_RETURN (EIO);
480		_PHOLD(proc);
481		PROC_UNLOCK(proc);
482	}
483
484	if (pn->pn_flags & PFS_RAWRD) {
485		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
486		if (proc != NULL)
487			PRELE(proc);
488		PFS_RETURN (error);
489	}
490
491	sb = sbuf_new(sb, NULL, uio->uio_offset + uio->uio_resid, 0);
492	if (sb == NULL) {
493		if (proc != NULL)
494			PRELE(proc);
495		PFS_RETURN (EIO);
496	}
497
498	error = (pn->pn_func)(curthread, proc, pn, sb, uio);
499
500	if (proc != NULL)
501		PRELE(proc);
502
503	if (error) {
504		sbuf_delete(sb);
505		PFS_RETURN (error);
506	}
507
508	/* XXX we should possibly detect and handle overflows */
509	sbuf_finish(sb);
510	ps = sbuf_data(sb) + uio->uio_offset;
511	xlen = sbuf_len(sb) - uio->uio_offset;
512	xlen = imin(xlen, uio->uio_resid);
513	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
514	sbuf_delete(sb);
515	PFS_RETURN (error);
516}
517
518/*
519 * Iterate through directory entries
520 */
521static int
522pfs_iterate(struct thread *td, pid_t pid, struct pfs_node *pd,
523            struct pfs_node **pn, struct proc **p)
524{
525	if ((*pn) == NULL)
526		*pn = pd->pn_nodes;
527	else
528 again:
529	if ((*pn)->pn_type != pfstype_procdir)
530		*pn = (*pn)->pn_next;
531
532	while (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
533		if (*p == NULL)
534			*p = LIST_FIRST(&allproc);
535		else
536			*p = LIST_NEXT(*p, p_list);
537		if (*p != NULL)
538			break;
539		*pn = (*pn)->pn_next;
540	}
541
542	if ((*pn) == NULL)
543		return (-1);
544
545	if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid))
546		goto again;
547
548	return (0);
549}
550
551/*
552 * Return directory entries.
553 */
554static int
555pfs_readdir(struct vop_readdir_args *va)
556{
557	struct vnode *vn = va->a_vp;
558	struct pfs_info *pi = (struct pfs_info *)vn->v_mount->mnt_data;
559	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
560	struct pfs_node *pd = pvd->pvd_pn;
561	pid_t pid = pvd->pvd_pid;
562	struct pfs_node *pn;
563	struct dirent entry;
564	struct uio *uio;
565	struct proc *p;
566	off_t offset;
567	int error, i, resid;
568
569	PFS_TRACE((pd->pn_name));
570
571	if (vn->v_type != VDIR)
572		PFS_RETURN (ENOTDIR);
573	uio = va->a_uio;
574
575	/* check if the directory is visible to the caller */
576	if (!pfs_visible(curthread, pd, pid))
577		PFS_RETURN (ENOENT);
578
579	/* only allow reading entire entries */
580	offset = uio->uio_offset;
581	resid = uio->uio_resid;
582	if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN)
583		PFS_RETURN (EINVAL);
584
585	/* skip unwanted entries */
586	sx_slock(&allproc_lock);
587	for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN)
588		if (pfs_iterate(curthread, pid, pd, &pn, &p) == -1) {
589			/* nothing left... */
590			sx_sunlock(&allproc_lock);
591			PFS_RETURN (0);
592		}
593
594	/* fill in entries */
595	entry.d_reclen = PFS_DELEN;
596	while (pfs_iterate(curthread, pid, pd, &pn, &p) != -1 && resid > 0) {
597		if (!pn->pn_parent)
598			pn->pn_parent = pd;
599		if (!pn->pn_fileno)
600			pfs_fileno_alloc(pi, pn);
601		if (pid != NO_PID)
602			entry.d_fileno = pn->pn_fileno * NO_PID + pid;
603		else
604			entry.d_fileno = pn->pn_fileno;
605		/* PFS_DELEN was picked to fit PFS_NAMLEN */
606		for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
607			entry.d_name[i] = pn->pn_name[i];
608		entry.d_name[i] = 0;
609		entry.d_namlen = i;
610		switch (pn->pn_type) {
611		case pfstype_procdir:
612			KASSERT(p != NULL,
613			    ("reached procdir node with p == NULL"));
614			entry.d_fileno = pn->pn_fileno * NO_PID + p->p_pid;
615			entry.d_namlen = snprintf(entry.d_name,
616			    PFS_NAMELEN, "%d", p->p_pid);
617			/* fall through */
618		case pfstype_root:
619		case pfstype_dir:
620		case pfstype_this:
621		case pfstype_parent:
622			entry.d_type = DT_DIR;
623			break;
624		case pfstype_file:
625			entry.d_type = DT_REG;
626			break;
627		case pfstype_symlink:
628			entry.d_type = DT_LNK;
629			break;
630		default:
631			sx_sunlock(&allproc_lock);
632			panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
633		}
634		PFS_TRACE((entry.d_name));
635		if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) {
636			sx_sunlock(&allproc_lock);
637			PFS_RETURN (error);
638		}
639		offset += PFS_DELEN;
640		resid -= PFS_DELEN;
641	}
642
643	sx_sunlock(&allproc_lock);
644	uio->uio_offset += offset;
645	PFS_RETURN (0);
646}
647
648/*
649 * Read a symbolic link
650 */
651static int
652pfs_readlink(struct vop_readlink_args *va)
653{
654	struct vnode *vn = va->a_vp;
655	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
656	struct pfs_node *pn = pvd->pvd_pn;
657	struct uio *uio = va->a_uio;
658	struct proc *proc = NULL;
659	char buf[MAXPATHLEN], *ps;
660	struct sbuf sb;
661	int error, xlen;
662
663	PFS_TRACE((pn->pn_name));
664
665	if (vn->v_type != VLNK)
666		PFS_RETURN (EINVAL);
667
668	if (pn->pn_func == NULL)
669		PFS_RETURN (EIO);
670
671	if (pvd->pvd_pid != NO_PID) {
672		if ((proc = pfind(pvd->pvd_pid)) == NULL)
673			PFS_RETURN (EIO);
674		_PHOLD(proc);
675		PROC_UNLOCK(proc);
676	}
677
678	/* sbuf_new() can't fail with a static buffer */
679	sbuf_new(&sb, buf, sizeof buf, 0);
680
681	error = (pn->pn_func)(curthread, proc, pn, &sb, NULL);
682
683	if (proc != NULL)
684		PRELE(proc);
685
686	if (error) {
687		sbuf_delete(&sb);
688		PFS_RETURN (error);
689	}
690
691	/* XXX we should detect and handle overflows */
692	sbuf_finish(&sb);
693	ps = sbuf_data(&sb) + uio->uio_offset;
694	xlen = sbuf_len(&sb) - uio->uio_offset;
695	xlen = imin(xlen, uio->uio_resid);
696	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
697	sbuf_delete(&sb);
698	PFS_RETURN (error);
699}
700
701/*
702 * Reclaim a vnode
703 */
704static int
705pfs_reclaim(struct vop_reclaim_args *va)
706{
707	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
708
709	return (pfs_vncache_free(va->a_vp));
710}
711
712/*
713 * Set attributes
714 */
715static int
716pfs_setattr(struct vop_setattr_args *va)
717{
718	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
719
720	if (va->a_vap->va_flags != (u_long)VNOVAL)
721		PFS_RETURN (EOPNOTSUPP);
722	PFS_RETURN (EPERM);
723}
724
725/*
726 * Read from a file
727 */
728static int
729pfs_write(struct vop_read_args *va)
730{
731	struct vnode *vn = va->a_vp;
732	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
733	struct pfs_node *pn = pvd->pvd_pn;
734	struct uio *uio = va->a_uio;
735	struct proc *proc = NULL;
736	struct sbuf sb;
737	int error;
738
739	PFS_TRACE((pn->pn_name));
740
741	if (vn->v_type != VREG)
742		PFS_RETURN (EINVAL);
743
744	if (!(pn->pn_flags & PFS_WR))
745		PFS_RETURN (EBADF);
746
747	if (pn->pn_func == NULL)
748		PFS_RETURN (EIO);
749
750	/*
751	 * This is necessary because either process' privileges may
752	 * have changed since the open() call.
753	 */
754	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
755		PFS_RETURN (EIO);
756
757	/* XXX duplicates bits of pfs_visible() */
758	if (pvd->pvd_pid != NO_PID) {
759		if ((proc = pfind(pvd->pvd_pid)) == NULL)
760			PFS_RETURN (EIO);
761		_PHOLD(proc);
762		PROC_UNLOCK(proc);
763	}
764
765	if (pn->pn_flags & PFS_RAWWR) {
766		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
767		if (proc != NULL)
768			PRELE(proc);
769		PFS_RETURN (error);
770	}
771
772	sbuf_uionew(&sb, uio, &error);
773	if (error)
774		PFS_RETURN (error);
775
776	error = (pn->pn_func)(curthread, proc, pn, &sb, uio);
777
778	if (proc != NULL)
779		PRELE(proc);
780
781	sbuf_delete(&sb);
782	PFS_RETURN (error);
783}
784
785/*
786 * Dummy operations */
787static int pfs_badop(void *va)		{ return (EOPNOTSUPP); }
788#if 0
789static int pfs_erofs(void *va)		{ return (EROFS); }
790static int pfs_null(void *va)		{ return (0); }
791#endif
792
793/*
794 * Vnode operations
795 */
796vop_t **pfs_vnodeop_p;
797static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = {
798	{ &vop_default_desc,		(vop_t *)vop_defaultop	},
799	{ &vop_access_desc,		(vop_t *)pfs_access	},
800	{ &vop_close_desc,		(vop_t *)pfs_close	},
801	{ &vop_create_desc,		(vop_t *)pfs_badop	},
802	{ &vop_getattr_desc,		(vop_t *)pfs_getattr	},
803	{ &vop_getextattr_desc,		(vop_t *)pfs_getextattr	},
804	{ &vop_ioctl_desc,		(vop_t *)pfs_ioctl	},
805	{ &vop_link_desc,		(vop_t *)pfs_badop	},
806	{ &vop_lookup_desc,		(vop_t *)pfs_lookup	},
807	{ &vop_mkdir_desc,		(vop_t *)pfs_badop	},
808	{ &vop_mknod_desc,		(vop_t *)pfs_badop	},
809	{ &vop_open_desc,		(vop_t *)pfs_open	},
810	{ &vop_read_desc,		(vop_t *)pfs_read	},
811	{ &vop_readdir_desc,		(vop_t *)pfs_readdir	},
812	{ &vop_readlink_desc,		(vop_t *)pfs_readlink	},
813	{ &vop_reclaim_desc,		(vop_t *)pfs_reclaim	},
814	{ &vop_remove_desc,		(vop_t *)pfs_badop	},
815	{ &vop_rename_desc,		(vop_t *)pfs_badop	},
816	{ &vop_rmdir_desc,		(vop_t *)pfs_badop	},
817	{ &vop_setattr_desc,		(vop_t *)pfs_setattr	},
818	{ &vop_symlink_desc,		(vop_t *)pfs_badop	},
819	{ &vop_write_desc,		(vop_t *)pfs_write	},
820	/* XXX I've probably forgotten a few that need pfs_badop */
821	{ NULL,				(vop_t *)NULL		}
822};
823
824static struct vnodeopv_desc pfs_vnodeop_opv_desc =
825	{ &pfs_vnodeop_p, pfs_vnodeop_entries };
826
827VNODEOP_SET(pfs_vnodeop_opv_desc);
828