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