175295Sdes/*-
2230132Suqs * Copyright (c) 2001 Dag-Erling Co��dan Sm��rgrav
375295Sdes * All rights reserved.
475295Sdes *
575295Sdes * Redistribution and use in source and binary forms, with or without
675295Sdes * modification, are permitted provided that the following conditions
775295Sdes * are met:
875295Sdes * 1. Redistributions of source code must retain the above copyright
975295Sdes *    notice, this list of conditions and the following disclaimer
1075295Sdes *    in this position and unchanged.
1175295Sdes * 2. Redistributions in binary form must reproduce the above copyright
1275295Sdes *    notice, this list of conditions and the following disclaimer in the
1375295Sdes *    documentation and/or other materials provided with the distribution.
1475295Sdes * 3. The name of the author may not be used to endorse or promote products
1575295Sdes *    derived from this software without specific prior written permission.
1675295Sdes *
1775295Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1875295Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1975295Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2075295Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2175295Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2275295Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2375295Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2475295Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2575295Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2675295Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2775295Sdes */
2875295Sdes
29143592Sdes#include <sys/cdefs.h>
30143592Sdes__FBSDID("$FreeBSD$");
31143592Sdes
32143592Sdes#include "opt_pseudofs.h"
33143592Sdes
3475295Sdes#include <sys/param.h>
3575295Sdes#include <sys/kernel.h>
3675295Sdes#include <sys/systm.h>
3775295Sdes#include <sys/ctype.h>
3875295Sdes#include <sys/dirent.h>
3984187Sdes#include <sys/fcntl.h>
40120665Snectar#include <sys/limits.h>
4178073Sdes#include <sys/lock.h>
42143596Sdes#include <sys/malloc.h>
4375295Sdes#include <sys/mount.h>
4477967Sdes#include <sys/mutex.h>
4575295Sdes#include <sys/namei.h>
4675295Sdes#include <sys/proc.h>
4775295Sdes#include <sys/sbuf.h>
4875295Sdes#include <sys/sx.h>
4975295Sdes#include <sys/sysctl.h>
5075295Sdes#include <sys/vnode.h>
5175295Sdes
5275295Sdes#include <fs/pseudofs/pseudofs.h>
5375295Sdes#include <fs/pseudofs/pseudofs_internal.h>
5475295Sdes
55190806Sdes#define KASSERT_PN_IS_DIR(pn)						\
56190806Sdes	KASSERT((pn)->pn_type == pfstype_root ||			\
57190806Sdes	    (pn)->pn_type == pfstype_dir ||				\
58190806Sdes	    (pn)->pn_type == pfstype_procdir,				\
59190806Sdes	    ("%s(): VDIR vnode refers to non-directory pfs_node", __func__))
60190806Sdes
61190806Sdes#define KASSERT_PN_IS_FILE(pn)						\
62190806Sdes	KASSERT((pn)->pn_type == pfstype_file,				\
63190806Sdes	    ("%s(): VREG vnode refers to non-file pfs_node", __func__))
64190806Sdes
65190806Sdes#define KASSERT_PN_IS_LINK(pn)						\
66190806Sdes	KASSERT((pn)->pn_type == pfstype_symlink,			\
67190806Sdes	    ("%s(): VLNK vnode refers to non-link pfs_node", __func__))
68190806Sdes
6975295Sdes/*
70168764Sdes * Returns the fileno, adjusted for target pid
71168720Sdes */
72168720Sdesstatic uint32_t
73168764Sdespn_fileno(struct pfs_node *pn, pid_t pid)
74168720Sdes{
75168764Sdes
76168764Sdes	KASSERT(pn->pn_fileno > 0,
77168764Sdes	    ("%s(): no fileno allocated", __func__));
78168720Sdes	if (pid != NO_PID)
79168720Sdes		return (pn->pn_fileno * NO_PID + pid);
80168720Sdes	return (pn->pn_fileno);
81168720Sdes}
82168720Sdes
83168720Sdes/*
84168764Sdes * Returns non-zero if given file is visible to given thread.
8584246Sdes */
8684246Sdesstatic int
87168764Sdespfs_visible_proc(struct thread *td, struct pfs_node *pn, struct proc *proc)
88168764Sdes{
89168764Sdes	int visible;
90168764Sdes
91168764Sdes	if (proc == NULL)
92168764Sdes		return (0);
93168764Sdes
94168764Sdes	PROC_LOCK_ASSERT(proc, MA_OWNED);
95168764Sdes
96168764Sdes	visible = ((proc->p_flag & P_WEXIT) == 0);
97168764Sdes	if (visible)
98168764Sdes		visible = (p_cansee(td, proc) == 0);
99168764Sdes	if (visible && pn->pn_vis != NULL)
100168764Sdes		visible = pn_vis(td, proc, pn);
101168764Sdes	if (!visible)
102168764Sdes		return (0);
103168764Sdes	return (1);
104168764Sdes}
105168764Sdes
106168764Sdesstatic int
107297267Skibpfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid,
108297267Skib    bool allproc_locked, struct proc **p)
10984246Sdes{
11084246Sdes	struct proc *proc;
11197940Sdes
11284246Sdes	PFS_TRACE(("%s (pid: %d, req: %d)",
11384246Sdes	    pn->pn_name, pid, td->td_proc->p_pid));
11484386Sdes
115168764Sdes	if (p)
116168764Sdes		*p = NULL;
117168764Sdes	if (pid == NO_PID)
118168764Sdes		PFS_RETURN (1);
119297267Skib	proc = allproc_locked ? pfind_locked(pid) : pfind(pid);
120297267Skib	if (proc == NULL)
12184386Sdes		PFS_RETURN (0);
122168764Sdes	if (pfs_visible_proc(td, pn, proc)) {
123168764Sdes		if (p)
124155920Sjhb			*p = proc;
125168764Sdes		else
126155920Sjhb			PROC_UNLOCK(proc);
127168764Sdes		PFS_RETURN (1);
128168764Sdes	}
129168764Sdes	PROC_UNLOCK(proc);
130168764Sdes	PFS_RETURN (0);
13184246Sdes}
13284246Sdes
13384246Sdes/*
13475295Sdes * Verify permissions
13575295Sdes */
13675295Sdesstatic int
13775295Sdespfs_access(struct vop_access_args *va)
13875295Sdes{
13975295Sdes	struct vnode *vn = va->a_vp;
140168764Sdes	struct pfs_vdata *pvd = vn->v_data;
14175295Sdes	struct vattr vattr;
14275295Sdes	int error;
14397940Sdes
144168764Sdes	PFS_TRACE(("%s", pvd->pvd_pn->pn_name));
145168768Sdes	(void)pvd;
14697940Sdes
147182371Sattilio	error = VOP_GETATTR(vn, &vattr, va->a_cred);
14875295Sdes	if (error)
14984246Sdes		PFS_RETURN (error);
15075295Sdes	error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid,
151184413Strasz	    vattr.va_gid, va->a_accmode, va->a_cred, NULL);
15284246Sdes	PFS_RETURN (error);
15375295Sdes}
15475295Sdes
15575295Sdes/*
15675295Sdes * Close a file or directory
15775295Sdes */
15875295Sdesstatic int
15975295Sdespfs_close(struct vop_close_args *va)
16075295Sdes{
16186969Sdes	struct vnode *vn = va->a_vp;
162168764Sdes	struct pfs_vdata *pvd = vn->v_data;
16386969Sdes	struct pfs_node *pn = pvd->pvd_pn;
16486969Sdes	struct proc *proc;
16586969Sdes	int error;
16686969Sdes
167168764Sdes	PFS_TRACE(("%s", pn->pn_name));
168168764Sdes	pfs_assert_not_owned(pn);
16986969Sdes
17086969Sdes	/*
17186969Sdes	 * Do nothing unless this is the last close and the node has a
17286969Sdes	 * last-close handler.
17386969Sdes	 */
174103936Sjeff	if (vrefcnt(vn) > 1 || pn->pn_close == NULL)
17586969Sdes		PFS_RETURN (0);
17686969Sdes
177168764Sdes	if (pvd->pvd_pid != NO_PID) {
17886969Sdes		proc = pfind(pvd->pvd_pid);
179168764Sdes	} else {
18086969Sdes		proc = NULL;
181168764Sdes	}
18297940Sdes
183168764Sdes	error = pn_close(va->a_td, proc, pn);
18497940Sdes
18586969Sdes	if (proc != NULL)
18686969Sdes		PROC_UNLOCK(proc);
18797940Sdes
18886969Sdes	PFS_RETURN (error);
18975295Sdes}
19075295Sdes
19175295Sdes/*
19275295Sdes * Get file attributes
19375295Sdes */
19475295Sdesstatic int
19575295Sdespfs_getattr(struct vop_getattr_args *va)
19675295Sdes{
19775295Sdes	struct vnode *vn = va->a_vp;
198168764Sdes	struct pfs_vdata *pvd = vn->v_data;
19977998Sdes	struct pfs_node *pn = pvd->pvd_pn;
20075295Sdes	struct vattr *vap = va->a_vap;
20184098Sdes	struct proc *proc;
20284098Sdes	int error = 0;
20375295Sdes
204168764Sdes	PFS_TRACE(("%s", pn->pn_name));
205168764Sdes	pfs_assert_not_owned(pn);
20697940Sdes
207297267Skib	if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc))
208119122Sdes		PFS_RETURN (ENOENT);
209119122Sdes
21075295Sdes	vap->va_type = vn->v_type;
211168764Sdes	vap->va_fileid = pn_fileno(pn, pvd->pvd_pid);
21275295Sdes	vap->va_flags = 0;
21375295Sdes	vap->va_blocksize = PAGE_SIZE;
21475295Sdes	vap->va_bytes = vap->va_size = 0;
215183215Skib	vap->va_filerev = 0;
21675295Sdes	vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
21775295Sdes	vap->va_nlink = 1;
21875295Sdes	nanotime(&vap->va_ctime);
21975295Sdes	vap->va_atime = vap->va_mtime = vap->va_ctime;
22075295Sdes
22184098Sdes	switch (pn->pn_type) {
22284098Sdes	case pfstype_procdir:
22384098Sdes	case pfstype_root:
22484098Sdes	case pfstype_dir:
225168764Sdes#if 0
226168764Sdes		pfs_lock(pn);
227168764Sdes		/* compute link count */
228168764Sdes		pfs_unlock(pn);
229168764Sdes#endif
23084098Sdes		vap->va_mode = 0555;
23184098Sdes		break;
23284098Sdes	case pfstype_file:
23384187Sdes	case pfstype_symlink:
23484098Sdes		vap->va_mode = 0444;
23584098Sdes		break;
23684098Sdes	default:
23784098Sdes		printf("shouldn't be here!\n");
23884098Sdes		vap->va_mode = 0;
23984098Sdes		break;
24084098Sdes	}
24184098Sdes
242155920Sjhb	if (proc != NULL) {
24384098Sdes		vap->va_uid = proc->p_ucred->cr_ruid;
24484098Sdes		vap->va_gid = proc->p_ucred->cr_rgid;
24584098Sdes	} else {
24684098Sdes		vap->va_uid = 0;
24784098Sdes		vap->va_gid = 0;
24884098Sdes	}
24997940Sdes
250188677Sdes	if (pn->pn_attr != NULL)
251188677Sdes		error = pn_attr(curthread, proc, pn, vap);
252188677Sdes
253188677Sdes	if(proc != NULL)
254188677Sdes		PROC_UNLOCK(proc);
255188677Sdes
25684098Sdes	PFS_RETURN (error);
25775295Sdes}
25875295Sdes
25975295Sdes/*
26085561Sdes * Perform an ioctl
26185561Sdes */
26285561Sdesstatic int
26385561Sdespfs_ioctl(struct vop_ioctl_args *va)
26485561Sdes{
265193919Skib	struct vnode *vn;
266193919Skib	struct pfs_vdata *pvd;
267193919Skib	struct pfs_node *pn;
268155920Sjhb	struct proc *proc;
26985561Sdes	int error;
27085561Sdes
271193919Skib	vn = va->a_vp;
272193919Skib	vn_lock(vn, LK_SHARED | LK_RETRY);
273193919Skib	if (vn->v_iflag & VI_DOOMED) {
274193919Skib		VOP_UNLOCK(vn, 0);
275193919Skib		return (EBADF);
276193919Skib	}
277193919Skib	pvd = vn->v_data;
278193919Skib	pn = pvd->pvd_pn;
279193919Skib
28087541Sdes	PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command));
281168764Sdes	pfs_assert_not_owned(pn);
28297940Sdes
283193919Skib	if (vn->v_type != VREG) {
284193919Skib		VOP_UNLOCK(vn, 0);
28585561Sdes		PFS_RETURN (EINVAL);
286193919Skib	}
287190806Sdes	KASSERT_PN_IS_FILE(pn);
28885561Sdes
289193919Skib	if (pn->pn_ioctl == NULL) {
290193919Skib		VOP_UNLOCK(vn, 0);
29185561Sdes		PFS_RETURN (ENOTTY);
292193919Skib	}
29397940Sdes
29485561Sdes	/*
295116639Sjmg	 * This is necessary because process' privileges may
29685561Sdes	 * have changed since the open() call.
29785561Sdes	 */
298297267Skib	if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc)) {
299193919Skib		VOP_UNLOCK(vn, 0);
30085561Sdes		PFS_RETURN (EIO);
301193919Skib	}
30297940Sdes
303168764Sdes	error = pn_ioctl(curthread, proc, pn, va->a_command, va->a_data);
30497940Sdes
30585561Sdes	if (proc != NULL)
306168764Sdes		PROC_UNLOCK(proc);
30797940Sdes
308193919Skib	VOP_UNLOCK(vn, 0);
30985561Sdes	PFS_RETURN (error);
31085561Sdes}
31185561Sdes
31285561Sdes/*
31387670Sgreen * Perform getextattr
31487670Sgreen */
31587670Sgreenstatic int
31687670Sgreenpfs_getextattr(struct vop_getextattr_args *va)
31787670Sgreen{
31887670Sgreen	struct vnode *vn = va->a_vp;
319168764Sdes	struct pfs_vdata *pvd = vn->v_data;
32087670Sgreen	struct pfs_node *pn = pvd->pvd_pn;
321155920Sjhb	struct proc *proc;
32287670Sgreen	int error;
32387670Sgreen
324168764Sdes	PFS_TRACE(("%s", pn->pn_name));
325168764Sdes	pfs_assert_not_owned(pn);
32687670Sgreen
32787670Sgreen	/*
32887670Sgreen	 * This is necessary because either process' privileges may
32987670Sgreen	 * have changed since the open() call.
33087670Sgreen	 */
331297267Skib	if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc))
33287670Sgreen		PFS_RETURN (EIO);
33397940Sdes
334168764Sdes	if (pn->pn_getextattr == NULL)
335168764Sdes		error = EOPNOTSUPP;
336168764Sdes	else
337168764Sdes		error = pn_getextattr(curthread, proc, pn,
338168764Sdes		    va->a_attrnamespace, va->a_name, va->a_uio,
339168764Sdes		    va->a_size, va->a_cred);
340155920Sjhb
341168764Sdes	if (proc != NULL)
34287670Sgreen		PROC_UNLOCK(proc);
34397940Sdes
34487670Sgreen	PFS_RETURN (error);
34587670Sgreen}
34687670Sgreen
34787670Sgreen/*
348186617Smarcus * Convert a vnode to its component name
349186617Smarcus */
350186617Smarcusstatic int
351186617Smarcuspfs_vptocnp(struct vop_vptocnp_args *ap)
352186617Smarcus{
353186617Smarcus	struct vnode *vp = ap->a_vp;
354186617Smarcus	struct vnode **dvp = ap->a_vpp;
355186617Smarcus	struct pfs_vdata *pvd = vp->v_data;
356186617Smarcus	struct pfs_node *pd = pvd->pvd_pn;
357186617Smarcus	struct pfs_node *pn;
358186617Smarcus	struct mount *mp;
359186617Smarcus	char *buf = ap->a_buf;
360186617Smarcus	int *buflen = ap->a_buflen;
361186617Smarcus	char pidbuf[PFS_NAMELEN];
362186617Smarcus	pid_t pid = pvd->pvd_pid;
363186617Smarcus	int len, i, error, locked;
364186617Smarcus
365186617Smarcus	i = *buflen;
366186617Smarcus	error = 0;
367186617Smarcus
368186617Smarcus	pfs_lock(pd);
369186617Smarcus
370186617Smarcus	if (vp->v_type == VDIR && pd->pn_type == pfstype_root) {
371186617Smarcus		*dvp = vp;
372186617Smarcus		vhold(*dvp);
373186617Smarcus		pfs_unlock(pd);
374186617Smarcus		PFS_RETURN (0);
375186617Smarcus	} else if (vp->v_type == VDIR && pd->pn_type == pfstype_procdir) {
376186617Smarcus		len = snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
377186617Smarcus		i -= len;
378186617Smarcus		if (i < 0) {
379186617Smarcus			error = ENOMEM;
380186617Smarcus			goto failed;
381186617Smarcus		}
382186617Smarcus		bcopy(pidbuf, buf + i, len);
383186617Smarcus	} else {
384192973Sdes		len = strlen(pd->pn_name);
385192973Sdes		i -= len;
386186617Smarcus		if (i < 0) {
387186617Smarcus			error = ENOMEM;
388186617Smarcus			goto failed;
389186617Smarcus		}
390192973Sdes		bcopy(pd->pn_name, buf + i, len);
391186617Smarcus	}
392186617Smarcus
393186617Smarcus	pn = pd->pn_parent;
394186617Smarcus	pfs_unlock(pd);
395186617Smarcus
396186617Smarcus	mp = vp->v_mount;
397186617Smarcus	error = vfs_busy(mp, 0);
398186617Smarcus	if (error)
399186617Smarcus		return (error);
400186617Smarcus
401186617Smarcus	/*
402186617Smarcus	 * vp is held by caller.
403186617Smarcus	 */
404186617Smarcus	locked = VOP_ISLOCKED(vp);
405186617Smarcus	VOP_UNLOCK(vp, 0);
406186617Smarcus
407186617Smarcus	error = pfs_vncache_alloc(mp, dvp, pn, pid);
408186617Smarcus	if (error) {
409186617Smarcus		vn_lock(vp, locked | LK_RETRY);
410186617Smarcus		vfs_unbusy(mp);
411186617Smarcus		PFS_RETURN(error);
412186617Smarcus	}
413186617Smarcus
414186617Smarcus	*buflen = i;
415227697Skib	VOP_UNLOCK(*dvp, 0);
416186617Smarcus	vn_lock(vp, locked | LK_RETRY);
417186617Smarcus	vfs_unbusy(mp);
418186617Smarcus
419186617Smarcus	PFS_RETURN (0);
420186617Smarcusfailed:
421186617Smarcus	pfs_unlock(pd);
422186617Smarcus	PFS_RETURN(error);
423186617Smarcus}
424186617Smarcus
425186617Smarcus/*
42675295Sdes * Look up a file or directory
42775295Sdes */
42875295Sdesstatic int
429143597Sdespfs_lookup(struct vop_cachedlookup_args *va)
43075295Sdes{
43177998Sdes	struct vnode *vn = va->a_dvp;
43275295Sdes	struct vnode **vpp = va->a_vpp;
43375295Sdes	struct componentname *cnp = va->a_cnp;
434168764Sdes	struct pfs_vdata *pvd = vn->v_data;
43577998Sdes	struct pfs_node *pd = pvd->pvd_pn;
43677998Sdes	struct pfs_node *pn, *pdn = NULL;
437232541Skib	struct mount *mp;
43877998Sdes	pid_t pid = pvd->pvd_pid;
43975295Sdes	char *pname;
440168764Sdes	int error, i, namelen, visible;
44175295Sdes
44284098Sdes	PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr));
443168764Sdes	pfs_assert_not_owned(pd);
44497940Sdes
44577998Sdes	if (vn->v_type != VDIR)
44684098Sdes		PFS_RETURN (ENOTDIR);
447190806Sdes	KASSERT_PN_IS_DIR(pd);
44897940Sdes
449143597Sdes	error = VOP_ACCESS(vn, VEXEC, cnp->cn_cred, cnp->cn_thread);
450143597Sdes	if (error)
451143597Sdes		PFS_RETURN (error);
452143597Sdes
45384098Sdes	/*
45484098Sdes	 * Don't support DELETE or RENAME.  CREATE is supported so
45584098Sdes	 * that O_CREAT will work, but the lookup will still fail if
45684098Sdes	 * the file does not exist.
45784098Sdes	 */
458143597Sdes	if ((cnp->cn_flags & ISLASTCN) &&
459143597Sdes	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
46084187Sdes		PFS_RETURN (EOPNOTSUPP);
46175295Sdes
46284098Sdes	/* shortcut: check if the name is too long */
46375295Sdes	if (cnp->cn_namelen >= PFS_NAMELEN)
46484098Sdes		PFS_RETURN (ENOENT);
46584098Sdes
466155920Sjhb	/* check that parent directory is visible... */
467297267Skib	if (!pfs_visible(curthread, pd, pvd->pvd_pid, false, NULL))
46884246Sdes		PFS_RETURN (ENOENT);
46997940Sdes
47075295Sdes	/* self */
47177998Sdes	namelen = cnp->cn_namelen;
47275295Sdes	pname = cnp->cn_nameptr;
473143597Sdes	if (namelen == 1 && pname[0] == '.') {
47475295Sdes		pn = pd;
47577998Sdes		*vpp = vn;
47677998Sdes		VREF(vn);
47784246Sdes		PFS_RETURN (0);
47875295Sdes	}
47975295Sdes
480232541Skib	mp = vn->v_mount;
481232541Skib
48275295Sdes	/* parent */
48375295Sdes	if (cnp->cn_flags & ISDOTDOT) {
48475295Sdes		if (pd->pn_type == pfstype_root)
48584098Sdes			PFS_RETURN (EIO);
486232541Skib		error = vfs_busy(mp, MBF_NOWAIT);
487232541Skib		if (error != 0) {
488232541Skib			vfs_ref(mp);
489232541Skib			VOP_UNLOCK(vn, 0);
490232541Skib			error = vfs_busy(mp, 0);
491232541Skib			vn_lock(vn, LK_EXCLUSIVE | LK_RETRY);
492232541Skib			vfs_rel(mp);
493232541Skib			if (error != 0)
494232541Skib				PFS_RETURN(ENOENT);
495232541Skib			if (vn->v_iflag & VI_DOOMED) {
496232541Skib				vfs_unbusy(mp);
497232541Skib				PFS_RETURN(ENOENT);
498232541Skib			}
499232541Skib		}
500175294Sattilio		VOP_UNLOCK(vn, 0);
501168764Sdes		KASSERT(pd->pn_parent != NULL,
502168764Sdes		    ("%s(): non-root directory has no parent", __func__));
50377998Sdes		/*
50477998Sdes		 * This one is tricky.  Descendents of procdir nodes
50577998Sdes		 * inherit their parent's process affinity, but
50677998Sdes		 * there's no easy reverse mapping.  For simplicity,
50777998Sdes		 * we assume that if this node is a procdir, its
50877998Sdes		 * parent isn't (which is correct as long as
50977998Sdes		 * descendents of procdir nodes are never procdir
51077998Sdes		 * nodes themselves)
51177998Sdes		 */
51277998Sdes		if (pd->pn_type == pfstype_procdir)
51377998Sdes			pid = NO_PID;
514168764Sdes		pfs_lock(pd);
51584246Sdes		pn = pd->pn_parent;
516168764Sdes		pfs_unlock(pd);
51784246Sdes		goto got_pnode;
51875295Sdes	}
51975295Sdes
520168764Sdes	pfs_lock(pd);
521168764Sdes
52277998Sdes	/* named node */
52385128Sdes	for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
52477998Sdes		if (pn->pn_type == pfstype_procdir)
52577998Sdes			pdn = pn;
526143597Sdes		else if (pn->pn_name[namelen] == '\0' &&
527168764Sdes		    bcmp(pname, pn->pn_name, namelen) == 0) {
528168764Sdes			pfs_unlock(pd);
52975295Sdes			goto got_pnode;
530168764Sdes		}
53177998Sdes
53277998Sdes	/* process dependent node */
53377998Sdes	if ((pn = pdn) != NULL) {
53477998Sdes		pid = 0;
53577998Sdes		for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
53677998Sdes			if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
53775295Sdes				break;
538168764Sdes		if (i == cnp->cn_namelen) {
539168764Sdes			pfs_unlock(pd);
54075295Sdes			goto got_pnode;
541168764Sdes		}
54275295Sdes	}
54397940Sdes
544168764Sdes	pfs_unlock(pd);
545168764Sdes
54684187Sdes	PFS_RETURN (ENOENT);
547168764Sdes
54875295Sdes got_pnode:
549168764Sdes	pfs_assert_not_owned(pd);
550168764Sdes	pfs_assert_not_owned(pn);
551297267Skib	visible = pfs_visible(curthread, pn, pid, false, NULL);
552168764Sdes	if (!visible) {
553144208Sjeff		error = ENOENT;
554144208Sjeff		goto failed;
555144208Sjeff	}
55699566Sjeff
557232541Skib	error = pfs_vncache_alloc(mp, vpp, pn, pid);
55875295Sdes	if (error)
559144208Sjeff		goto failed;
56099566Sjeff
561232541Skib	if (cnp->cn_flags & ISDOTDOT) {
562232541Skib		vfs_unbusy(mp);
563232541Skib		vn_lock(vn, LK_EXCLUSIVE | LK_RETRY);
564232541Skib		if (vn->v_iflag & VI_DOOMED) {
565232541Skib			vput(*vpp);
566232541Skib			*vpp = NULL;
567232541Skib			PFS_RETURN(ENOENT);
568232541Skib		}
569232541Skib	}
570206894Skib	if (cnp->cn_flags & MAKEENTRY && !(vn->v_iflag & VI_DOOMED))
57177998Sdes		cache_enter(vn, *vpp, cnp);
57284098Sdes	PFS_RETURN (0);
573144208Sjeff failed:
574232541Skib	if (cnp->cn_flags & ISDOTDOT) {
575232541Skib		vfs_unbusy(mp);
576232541Skib		vn_lock(vn, LK_EXCLUSIVE | LK_RETRY);
577232541Skib		*vpp = NULL;
578232541Skib	}
579144208Sjeff	PFS_RETURN(error);
58075295Sdes}
58175295Sdes
58275295Sdes/*
58375295Sdes * Open a file or directory.
58475295Sdes */
58575295Sdesstatic int
58675295Sdespfs_open(struct vop_open_args *va)
58775295Sdes{
58884098Sdes	struct vnode *vn = va->a_vp;
589168764Sdes	struct pfs_vdata *pvd = vn->v_data;
59084098Sdes	struct pfs_node *pn = pvd->pvd_pn;
59184187Sdes	int mode = va->a_mode;
59284098Sdes
59384187Sdes	PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode));
594168764Sdes	pfs_assert_not_owned(pn);
59584187Sdes
59684187Sdes	/* check if the requested mode is permitted */
59784187Sdes	if (((mode & FREAD) && !(mode & PFS_RD)) ||
59884187Sdes	    ((mode & FWRITE) && !(mode & PFS_WR)))
59984187Sdes		PFS_RETURN (EPERM);
60084187Sdes
60184187Sdes	/* we don't support locking */
60284187Sdes	if ((mode & O_SHLOCK) || (mode & O_EXLOCK))
60384187Sdes		PFS_RETURN (EOPNOTSUPP);
60497940Sdes
60584246Sdes	PFS_RETURN (0);
60675295Sdes}
60775295Sdes
60875295Sdes/*
60975295Sdes * Read from a file
61075295Sdes */
61175295Sdesstatic int
61275295Sdespfs_read(struct vop_read_args *va)
61375295Sdes{
61475295Sdes	struct vnode *vn = va->a_vp;
615168764Sdes	struct pfs_vdata *pvd = vn->v_data;
61677998Sdes	struct pfs_node *pn = pvd->pvd_pn;
61775295Sdes	struct uio *uio = va->a_uio;
618155920Sjhb	struct proc *proc;
61977964Sdes	struct sbuf *sb = NULL;
620186561Skib	int error, locked;
621259506Skib	off_t buflen;
62275295Sdes
623168764Sdes	PFS_TRACE(("%s", pn->pn_name));
624168764Sdes	pfs_assert_not_owned(pn);
62597940Sdes
62675295Sdes	if (vn->v_type != VREG)
62784187Sdes		PFS_RETURN (EINVAL);
628190806Sdes	KASSERT_PN_IS_FILE(pn);
62975295Sdes
63084187Sdes	if (!(pn->pn_flags & PFS_RD))
63184187Sdes		PFS_RETURN (EBADF);
63283927Sdes
633168764Sdes	if (pn->pn_fill == NULL)
63485561Sdes		PFS_RETURN (EIO);
63597940Sdes
63684246Sdes	/*
63784246Sdes	 * This is necessary because either process' privileges may
63884246Sdes	 * have changed since the open() call.
63984246Sdes	 */
640297267Skib	if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc))
64184246Sdes		PFS_RETURN (EIO);
642155920Sjhb	if (proc != NULL) {
64377998Sdes		_PHOLD(proc);
64477998Sdes		PROC_UNLOCK(proc);
64577998Sdes	}
64683927Sdes
647186561Skib	vhold(vn);
648186561Skib	locked = VOP_ISLOCKED(vn);
649186561Skib	VOP_UNLOCK(vn, 0);
650186561Skib
65183927Sdes	if (pn->pn_flags & PFS_RAWRD) {
652194990Skib		PFS_TRACE(("%zd resid", uio->uio_resid));
653168764Sdes		error = pn_fill(curthread, proc, pn, NULL, uio);
654194990Skib		PFS_TRACE(("%zd resid", uio->uio_resid));
655186561Skib		goto ret;
65683927Sdes	}
65797940Sdes
658259506Skib	if (uio->uio_resid < 0 || uio->uio_offset < 0 ||
659259506Skib	    uio->uio_resid > OFF_MAX - uio->uio_offset) {
660186561Skib		error = EINVAL;
661186561Skib		goto ret;
662120665Snectar	}
663259506Skib	buflen = uio->uio_offset + uio->uio_resid;
664229694Sjh	if (buflen > MAXPHYS)
665229694Sjh		buflen = MAXPHYS;
666168764Sdes
667229694Sjh	sb = sbuf_new(sb, NULL, buflen + 1, 0);
66877998Sdes	if (sb == NULL) {
669186561Skib		error = EIO;
670186561Skib		goto ret;
67177998Sdes	}
67275295Sdes
673168764Sdes	error = pn_fill(curthread, proc, pn, sb, uio);
67477998Sdes
67578018Sdes	if (error) {
67678018Sdes		sbuf_delete(sb);
677186561Skib		goto ret;
67878018Sdes	}
67997940Sdes
680229694Sjh	/*
681229694Sjh	 * XXX: If the buffer overflowed, sbuf_len() will not return
682229694Sjh	 * the data length. Then just use the full length because an
683229694Sjh	 * overflowed sbuf must be full.
684229694Sjh	 */
685229694Sjh	if (sbuf_finish(sb) == 0)
686229694Sjh		buflen = sbuf_len(sb);
687229694Sjh	error = uiomove_frombuf(sbuf_data(sb), buflen, uio);
68877964Sdes	sbuf_delete(sb);
689186561Skibret:
690186561Skib	vn_lock(vn, locked | LK_RETRY);
691186561Skib	vdrop(vn);
692186561Skib	if (proc != NULL)
693186561Skib		PRELE(proc);
69484187Sdes	PFS_RETURN (error);
69575295Sdes}
69675295Sdes
69775295Sdes/*
69877998Sdes * Iterate through directory entries
69977998Sdes */
70077998Sdesstatic int
701168764Sdespfs_iterate(struct thread *td, struct proc *proc, struct pfs_node *pd,
70297940Sdes	    struct pfs_node **pn, struct proc **p)
70377998Sdes{
704168764Sdes	int visible;
705168764Sdes
706155920Sjhb	sx_assert(&allproc_lock, SX_SLOCKED);
707168764Sdes	pfs_assert_owned(pd);
708119069Sdes again:
709119069Sdes	if (*pn == NULL) {
710119069Sdes		/* first node */
71185128Sdes		*pn = pd->pn_nodes;
712119069Sdes	} else if ((*pn)->pn_type != pfstype_procdir) {
713119069Sdes		/* next node */
71485128Sdes		*pn = (*pn)->pn_next;
715119069Sdes	}
716119069Sdes	if (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
717119069Sdes		/* next process */
71877998Sdes		if (*p == NULL)
71977998Sdes			*p = LIST_FIRST(&allproc);
72077998Sdes		else
72177998Sdes			*p = LIST_NEXT(*p, p_list);
722119069Sdes		/* out of processes: next node */
723119069Sdes		if (*p == NULL)
724119069Sdes			*pn = (*pn)->pn_next;
725168764Sdes		else
726168764Sdes			PROC_LOCK(*p);
72777998Sdes	}
72897940Sdes
72985128Sdes	if ((*pn) == NULL)
73077998Sdes		return (-1);
73184246Sdes
732168764Sdes	if (*p != NULL) {
733168764Sdes		visible = pfs_visible_proc(td, *pn, *p);
734168764Sdes		PROC_UNLOCK(*p);
735168764Sdes	} else if (proc != NULL) {
736168764Sdes		visible = pfs_visible_proc(td, *pn, proc);
737168764Sdes	} else {
738168764Sdes		visible = 1;
739168764Sdes	}
740168764Sdes	if (!visible)
74184246Sdes		goto again;
74297940Sdes
74377998Sdes	return (0);
74477998Sdes}
74577998Sdes
746227550Spho/* Directory entry list */
747227550Sphostruct pfsentry {
748227550Spho	STAILQ_ENTRY(pfsentry)	link;
749227550Spho	struct dirent		entry;
750227550Spho};
751227550SphoSTAILQ_HEAD(pfsdirentlist, pfsentry);
752227550Spho
75377998Sdes/*
75475295Sdes * Return directory entries.
75575295Sdes */
75675295Sdesstatic int
75775295Sdespfs_readdir(struct vop_readdir_args *va)
75875295Sdes{
75975295Sdes	struct vnode *vn = va->a_vp;
760168764Sdes	struct pfs_vdata *pvd = vn->v_data;
76177998Sdes	struct pfs_node *pd = pvd->pvd_pn;
76284246Sdes	pid_t pid = pvd->pvd_pid;
763168764Sdes	struct proc *p, *proc;
76477998Sdes	struct pfs_node *pn;
76575295Sdes	struct uio *uio;
766227550Spho	struct pfsentry *pfsent, *pfsent2;
767227550Spho	struct pfsdirentlist lst;
76875295Sdes	off_t offset;
76975295Sdes	int error, i, resid;
77075295Sdes
771227550Spho	STAILQ_INIT(&lst);
772227550Spho	error = 0;
773168720Sdes	KASSERT(pd->pn_info == vn->v_mount->mnt_data,
774168764Sdes	    ("%s(): pn_info does not match mountpoint", __func__));
775168764Sdes	PFS_TRACE(("%s pid %lu", pd->pn_name, (unsigned long)pid));
776168764Sdes	pfs_assert_not_owned(pd);
77797940Sdes
77875295Sdes	if (vn->v_type != VDIR)
77984187Sdes		PFS_RETURN (ENOTDIR);
780190806Sdes	KASSERT_PN_IS_DIR(pd);
78175295Sdes	uio = va->a_uio;
78275295Sdes
78375295Sdes	/* only allow reading entire entries */
78475295Sdes	offset = uio->uio_offset;
78575295Sdes	resid = uio->uio_resid;
786143596Sdes	if (offset < 0 || offset % PFS_DELEN != 0 ||
787143596Sdes	    (resid && resid < PFS_DELEN))
78884187Sdes		PFS_RETURN (EINVAL);
789143596Sdes	if (resid == 0)
790143596Sdes		PFS_RETURN (0);
79175295Sdes
792168764Sdes	sx_slock(&allproc_lock);
793168764Sdes	pfs_lock(pd);
794168764Sdes
795168764Sdes        /* check if the directory is visible to the caller */
796297267Skib        if (!pfs_visible(curthread, pd, pid, true, &proc)) {
797168764Sdes		sx_sunlock(&allproc_lock);
798168764Sdes		pfs_unlock(pd);
799168764Sdes                PFS_RETURN (ENOENT);
800168764Sdes	}
801168764Sdes	KASSERT(pid == NO_PID || proc != NULL,
802168764Sdes	    ("%s(): no process for pid %lu", __func__, (unsigned long)pid));
803168764Sdes
80475295Sdes	/* skip unwanted entries */
805168764Sdes	for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN) {
806168764Sdes		if (pfs_iterate(curthread, proc, pd, &pn, &p) == -1) {
80785128Sdes			/* nothing left... */
808168764Sdes			if (proc != NULL)
809168764Sdes				PROC_UNLOCK(proc);
810168764Sdes			pfs_unlock(pd);
81185128Sdes			sx_sunlock(&allproc_lock);
81285128Sdes			PFS_RETURN (0);
81385128Sdes		}
814168764Sdes	}
81597940Sdes
81675295Sdes	/* fill in entries */
817168764Sdes	while (pfs_iterate(curthread, proc, pd, &pn, &p) != -1 &&
818143596Sdes	    resid >= PFS_DELEN) {
819227550Spho		if ((pfsent = malloc(sizeof(struct pfsentry), M_IOV,
820227550Spho		    M_NOWAIT | M_ZERO)) == NULL) {
821227550Spho			error = ENOMEM;
822227550Spho			break;
823227550Spho		}
824227550Spho		pfsent->entry.d_reclen = PFS_DELEN;
825227550Spho		pfsent->entry.d_fileno = pn_fileno(pn, pid);
82675295Sdes		/* PFS_DELEN was picked to fit PFS_NAMLEN */
82775295Sdes		for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
828227550Spho			pfsent->entry.d_name[i] = pn->pn_name[i];
829227550Spho		pfsent->entry.d_name[i] = 0;
830227550Spho		pfsent->entry.d_namlen = i;
83175295Sdes		switch (pn->pn_type) {
83277998Sdes		case pfstype_procdir:
83377998Sdes			KASSERT(p != NULL,
83477998Sdes			    ("reached procdir node with p == NULL"));
835227550Spho			pfsent->entry.d_namlen = snprintf(pfsent->entry.d_name,
83677998Sdes			    PFS_NAMELEN, "%d", p->p_pid);
83777998Sdes			/* fall through */
83875295Sdes		case pfstype_root:
83975295Sdes		case pfstype_dir:
84075295Sdes		case pfstype_this:
84175295Sdes		case pfstype_parent:
842227550Spho			pfsent->entry.d_type = DT_DIR;
84375295Sdes			break;
84475295Sdes		case pfstype_file:
845227550Spho			pfsent->entry.d_type = DT_REG;
84675295Sdes			break;
84775295Sdes		case pfstype_symlink:
848227550Spho			pfsent->entry.d_type = DT_LNK;
84975295Sdes			break;
85075295Sdes		default:
85175295Sdes			panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
85275295Sdes		}
853227550Spho		PFS_TRACE(("%s", pfsent->entry.d_name));
854227550Spho		STAILQ_INSERT_TAIL(&lst, pfsent, link);
85575295Sdes		offset += PFS_DELEN;
85675295Sdes		resid -= PFS_DELEN;
85775295Sdes	}
858168764Sdes	if (proc != NULL)
859168764Sdes		PROC_UNLOCK(proc);
860168764Sdes	pfs_unlock(pd);
86177998Sdes	sx_sunlock(&allproc_lock);
862227550Spho	i = 0;
863227550Spho	STAILQ_FOREACH_SAFE(pfsent, &lst, link, pfsent2) {
864227550Spho		if (error == 0)
865227550Spho			error = uiomove(&pfsent->entry, PFS_DELEN, uio);
866227550Spho		free(pfsent, M_IOV);
867227550Spho		i++;
868227550Spho	}
869227576Skib	PFS_TRACE(("%d bytes", i * PFS_DELEN));
870131871Sdes	PFS_RETURN (error);
87175295Sdes}
87275295Sdes
87375295Sdes/*
87475295Sdes * Read a symbolic link
87575295Sdes */
87675295Sdesstatic int
87775295Sdespfs_readlink(struct vop_readlink_args *va)
87875295Sdes{
87975295Sdes	struct vnode *vn = va->a_vp;
880168764Sdes	struct pfs_vdata *pvd = vn->v_data;
88177998Sdes	struct pfs_node *pn = pvd->pvd_pn;
88275295Sdes	struct uio *uio = va->a_uio;
88377998Sdes	struct proc *proc = NULL;
884168764Sdes	char buf[PATH_MAX];
88575295Sdes	struct sbuf sb;
886193176Skib	int error, locked;
88775295Sdes
888168764Sdes	PFS_TRACE(("%s", pn->pn_name));
889168764Sdes	pfs_assert_not_owned(pn);
89097940Sdes
89175295Sdes	if (vn->v_type != VLNK)
89284187Sdes		PFS_RETURN (EINVAL);
893190806Sdes	KASSERT_PN_IS_LINK(pn);
89475295Sdes
895168764Sdes	if (pn->pn_fill == NULL)
89685561Sdes		PFS_RETURN (EIO);
89797940Sdes
89877998Sdes	if (pvd->pvd_pid != NO_PID) {
89977998Sdes		if ((proc = pfind(pvd->pvd_pid)) == NULL)
90084187Sdes			PFS_RETURN (EIO);
901155922Sjhb		if (proc->p_flag & P_WEXIT) {
902155922Sjhb			PROC_UNLOCK(proc);
903155922Sjhb			PFS_RETURN (EIO);
904155922Sjhb		}
90577998Sdes		_PHOLD(proc);
90677998Sdes		PROC_UNLOCK(proc);
90777998Sdes	}
908193176Skib	vhold(vn);
909193176Skib	locked = VOP_ISLOCKED(vn);
910193176Skib	VOP_UNLOCK(vn, 0);
91197940Sdes
91275295Sdes	/* sbuf_new() can't fail with a static buffer */
91375295Sdes	sbuf_new(&sb, buf, sizeof buf, 0);
91475295Sdes
915168764Sdes	error = pn_fill(curthread, proc, pn, &sb, NULL);
91677998Sdes
91777998Sdes	if (proc != NULL)
91877998Sdes		PRELE(proc);
919193176Skib	vn_lock(vn, locked | LK_RETRY);
920193176Skib	vdrop(vn);
92197940Sdes
92278018Sdes	if (error) {
92378018Sdes		sbuf_delete(&sb);
92484187Sdes		PFS_RETURN (error);
92578018Sdes	}
92697940Sdes
927229692Sjh	if (sbuf_finish(&sb) != 0) {
928229692Sjh		sbuf_delete(&sb);
929229692Sjh		PFS_RETURN (ENAMETOOLONG);
930229692Sjh	}
931229692Sjh
932120665Snectar	error = uiomove_frombuf(sbuf_data(&sb), sbuf_len(&sb), uio);
93375295Sdes	sbuf_delete(&sb);
93484187Sdes	PFS_RETURN (error);
93575295Sdes}
93675295Sdes
93775295Sdes/*
93875295Sdes * Reclaim a vnode
93975295Sdes */
94075295Sdesstatic int
94175295Sdespfs_reclaim(struct vop_reclaim_args *va)
94275295Sdes{
943168764Sdes	struct vnode *vn = va->a_vp;
944168764Sdes	struct pfs_vdata *pvd = vn->v_data;
945168764Sdes	struct pfs_node *pn = pvd->pvd_pn;
94688234Sdillon
947168764Sdes	PFS_TRACE(("%s", pn->pn_name));
948168764Sdes	pfs_assert_not_owned(pn);
949168764Sdes
95075295Sdes	return (pfs_vncache_free(va->a_vp));
95175295Sdes}
95275295Sdes
95375295Sdes/*
95475295Sdes * Set attributes
95575295Sdes */
95675295Sdesstatic int
95775295Sdespfs_setattr(struct vop_setattr_args *va)
95875295Sdes{
959168764Sdes	struct vnode *vn = va->a_vp;
960168764Sdes	struct pfs_vdata *pvd = vn->v_data;
961168764Sdes	struct pfs_node *pn = pvd->pvd_pn;
96290206Srwatson
963168764Sdes	PFS_TRACE(("%s", pn->pn_name));
964168764Sdes	pfs_assert_not_owned(pn);
965168764Sdes
96690206Srwatson	PFS_RETURN (EOPNOTSUPP);
96775295Sdes}
96875295Sdes
96975295Sdes/*
970145714Sdes * Write to a file
97175295Sdes */
97283927Sdesstatic int
973138290Sphkpfs_write(struct vop_write_args *va)
97483927Sdes{
97583927Sdes	struct vnode *vn = va->a_vp;
976168764Sdes	struct pfs_vdata *pvd = vn->v_data;
97783927Sdes	struct pfs_node *pn = pvd->pvd_pn;
97883927Sdes	struct uio *uio = va->a_uio;
979155920Sjhb	struct proc *proc;
98084098Sdes	struct sbuf sb;
98183927Sdes	int error;
98283927Sdes
983168764Sdes	PFS_TRACE(("%s", pn->pn_name));
984168764Sdes	pfs_assert_not_owned(pn);
98597940Sdes
98683927Sdes	if (vn->v_type != VREG)
98784187Sdes		PFS_RETURN (EINVAL);
988190806Sdes	KASSERT_PN_IS_FILE(pn);
98983927Sdes
99084187Sdes	if (!(pn->pn_flags & PFS_WR))
99184187Sdes		PFS_RETURN (EBADF);
99283927Sdes
993168764Sdes	if (pn->pn_fill == NULL)
99485561Sdes		PFS_RETURN (EIO);
99597940Sdes
99684246Sdes	/*
99784246Sdes	 * This is necessary because either process' privileges may
99884246Sdes	 * have changed since the open() call.
99984246Sdes	 */
1000297267Skib	if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc))
100184246Sdes		PFS_RETURN (EIO);
1002155920Sjhb	if (proc != NULL) {
100383927Sdes		_PHOLD(proc);
100483927Sdes		PROC_UNLOCK(proc);
100583927Sdes	}
100683927Sdes
100783927Sdes	if (pn->pn_flags & PFS_RAWWR) {
1008168764Sdes		error = pn_fill(curthread, proc, pn, NULL, uio);
100983927Sdes		if (proc != NULL)
101083927Sdes			PRELE(proc);
101184187Sdes		PFS_RETURN (error);
101283927Sdes	}
101384098Sdes
101484098Sdes	sbuf_uionew(&sb, uio, &error);
1015168764Sdes	if (error) {
1016168764Sdes		if (proc != NULL)
1017168764Sdes			PRELE(proc);
101884187Sdes		PFS_RETURN (error);
1019168764Sdes	}
102097940Sdes
1021168764Sdes	error = pn_fill(curthread, proc, pn, &sb, uio);
102284098Sdes
1023168764Sdes	sbuf_delete(&sb);
102484098Sdes	if (proc != NULL)
102584098Sdes		PRELE(proc);
102684187Sdes	PFS_RETURN (error);
102783927Sdes}
102883927Sdes
102983927Sdes/*
103075295Sdes * Vnode operations
103175295Sdes */
1032138290Sphkstruct vop_vector pfs_vnodeops = {
1033138290Sphk	.vop_default =		&default_vnodeops,
1034140196Sphk
1035138290Sphk	.vop_access =		pfs_access,
1036143597Sdes	.vop_cachedlookup =	pfs_lookup,
1037138290Sphk	.vop_close =		pfs_close,
1038138290Sphk	.vop_create =		VOP_EOPNOTSUPP,
1039138290Sphk	.vop_getattr =		pfs_getattr,
1040138290Sphk	.vop_getextattr =	pfs_getextattr,
1041138290Sphk	.vop_ioctl =		pfs_ioctl,
1042138290Sphk	.vop_link =		VOP_EOPNOTSUPP,
1043143597Sdes	.vop_lookup =		vfs_cache_lookup,
1044138290Sphk	.vop_mkdir =		VOP_EOPNOTSUPP,
1045138290Sphk	.vop_mknod =		VOP_EOPNOTSUPP,
1046138290Sphk	.vop_open =		pfs_open,
1047138290Sphk	.vop_read =		pfs_read,
1048138290Sphk	.vop_readdir =		pfs_readdir,
1049138290Sphk	.vop_readlink =		pfs_readlink,
1050138290Sphk	.vop_reclaim =		pfs_reclaim,
1051138290Sphk	.vop_remove =		VOP_EOPNOTSUPP,
1052138290Sphk	.vop_rename =		VOP_EOPNOTSUPP,
1053138290Sphk	.vop_rmdir =		VOP_EOPNOTSUPP,
1054138290Sphk	.vop_setattr =		pfs_setattr,
1055138290Sphk	.vop_symlink =		VOP_EOPNOTSUPP,
1056186617Smarcus	.vop_vptocnp =		pfs_vptocnp,
1057138290Sphk	.vop_write =		pfs_write,
1058138290Sphk	/* XXX I've probably forgotten a few that need VOP_EOPNOTSUPP */
105975295Sdes};
1060