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