Index: head/sys/fs/pseudofs/pseudofs.h =================================================================== --- head/sys/fs/pseudofs/pseudofs.h (revision 84385) +++ head/sys/fs/pseudofs/pseudofs.h (revision 84386) @@ -1,213 +1,220 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSEUDOFS_H_INCLUDED #define _PSEUDOFS_H_INCLUDED /* * Limits and constants */ #define PFS_NAMELEN 24 #define PFS_DELEN (8 + PFS_NAMELEN) typedef enum { pfstype_none = 0, pfstype_root, pfstype_dir, pfstype_this, pfstype_parent, pfstype_file, pfstype_symlink, pfstype_procdir } pfs_type_t; /* * Flags */ #define PFS_RD 0x0001 /* readable */ #define PFS_WR 0x0002 /* writeable */ #define PFS_RDWR (PFS_RD|PFS_WR) #define PFS_RAWRD 0x0004 /* raw reader */ #define PFS_RAWWR 0x0008 /* raw writer */ #define PFS_RAW (PFS_RAWRD|PFS_RAWWR) +#define PFS_DISABLED 0x8000 /* node is disabled */ /* * Data structures */ struct pfs_info; struct pfs_node; struct pfs_bitmap; /* * Filler callback */ #define PFS_FILL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct sbuf *sb, struct uio *uio #define PFS_FILL_PROTO(name) \ int name(PFS_FILL_ARGS); typedef int (*pfs_fill_t)(PFS_FILL_ARGS); /* * Attribute callback */ struct vattr; #define PFS_ATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct vattr *vap #define PFS_ATTR_PROTO(name) \ int name(PFS_ATTR_ARGS); typedef int (*pfs_attr_t)(PFS_ATTR_ARGS); struct pfs_bitmap; /* opaque */ /* * Visibility callback */ #define PFS_VIS_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_VIS_PROTO(name) \ int name(PFS_VIS_ARGS); typedef int (*pfs_vis_t)(PFS_VIS_ARGS); /* * pfs_info: describes a pseudofs instance */ struct pfs_info { char pi_name[MFSNAMELEN]; struct pfs_node *pi_root; /* members below this line aren't initialized */ /* currently, the mutex is only used to protect the bitmap */ struct mtx pi_mutex; struct pfs_bitmap *pi_bitmap; }; /* * pfs_node: describes a node (file or directory) within a pseudofs */ struct pfs_node { char pn_name[PFS_NAMELEN]; pfs_type_t pn_type; union { void *_pn_dummy; pfs_fill_t _pn_func; struct pfs_node *_pn_nodes; } u1; #define pn_func u1._pn_func #define pn_nodes u1._pn_nodes pfs_attr_t pn_attr; pfs_vis_t pn_vis; void *pn_data; int pn_flags; /* members below this line aren't initialized */ struct pfs_node *pn_parent; u_int32_t pn_fileno; }; #define PFS_NODE(name, type, fill, attr, vis, data, flags) \ { (name), (type), { (fill) }, (attr), (vis), (data), (flags) } #define PFS_DIR(name, nodes, attr, vis, data, flags) \ PFS_NODE(name, pfstype_dir, nodes, attr, vis, data, flags) #define PFS_ROOT(nodes) \ PFS_NODE("/", pfstype_root, nodes, NULL, NULL, NULL, 0) #define PFS_THIS \ PFS_NODE(".", pfstype_this, NULL, NULL, NULL, NULL, 0) #define PFS_PARENT \ PFS_NODE("..", pfstype_parent, NULL, NULL, NULL, NULL, 0) #define PFS_FILE(name, func, attr, vis, data, flags) \ PFS_NODE(name, pfstype_file, func, attr, vis, data, flags) #define PFS_SYMLINK(name, func, attr, vis, data, flags) \ PFS_NODE(name, pfstype_symlink, func, attr, vis, data, flags) #define PFS_PROCDIR(nodes, attr, vis, data, flags) \ PFS_NODE("", pfstype_procdir, nodes, attr, vis, data, flags) #define PFS_LASTNODE \ PFS_NODE("", pfstype_none, NULL, NULL, NULL, NULL, 0) /* * VFS interface */ int pfs_mount (struct pfs_info *pi, struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct thread *td); int pfs_unmount (struct mount *mp, int mntflags, struct thread *td); int pfs_root (struct mount *mp, struct vnode **vpp); int pfs_statfs (struct mount *mp, struct statfs *sbp, struct thread *td); int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); + +/* + * Other utility functions + */ +int pfs_disable (struct pfs_node *pn); +int pfs_enable (struct pfs_node *pn); /* * Now for some initialization magic... */ #define PSEUDOFS(name, root, version) \ \ static struct pfs_info name##_info = { \ #name, \ &(root) \ }; \ \ static int \ _##name##_mount(struct mount *mp, char *path, caddr_t data, \ struct nameidata *ndp, struct thread *td) { \ return pfs_mount(&name##_info, mp, path, data, ndp, td); \ } \ \ static int \ _##name##_init(struct vfsconf *vfc) { \ return pfs_init(&name##_info, vfc); \ } \ \ static int \ _##name##_uninit(struct vfsconf *vfc) { \ return pfs_uninit(&name##_info, vfc); \ } \ \ static struct vfsops name##_vfsops = { \ _##name##_mount, \ vfs_stdstart, \ pfs_unmount, \ pfs_root, \ vfs_stdquotactl, \ pfs_statfs, \ vfs_stdsync, \ vfs_stdvget, \ vfs_stdfhtovp, \ vfs_stdcheckexp, \ vfs_stdvptofh, \ _##name##_init, \ _##name##_uninit, \ vfs_stdextattrctl, \ }; \ VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC); \ MODULE_VERSION(name, version); \ MODULE_DEPEND(name, pseudofs, 2, 2, 2); #endif Index: head/sys/fs/pseudofs/pseudofs_vncache.c =================================================================== --- head/sys/fs/pseudofs/pseudofs_vncache.c (revision 84385) +++ head/sys/fs/pseudofs/pseudofs_vncache.c (revision 84386) @@ -1,219 +1,257 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); static struct mtx pfs_vncache_mutex; struct pfs_vdata *pfs_vncache; static void pfs_exit(struct proc *p); SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, "pseudofs vnode cache"); static int pfs_vncache_entries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, &pfs_vncache_entries, 0, "number of entries in the vnode cache"); static int pfs_vncache_maxentries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, &pfs_vncache_maxentries, 0, "highest number of entries in the vnode cache"); static int pfs_vncache_hits; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, &pfs_vncache_hits, 0, "number of cache hits since initialization"); static int pfs_vncache_misses; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, &pfs_vncache_misses, 0, "number of cache misses since initialization"); extern vop_t **pfs_vnodeop_p; /* * Initialize vnode cache */ void pfs_vncache_load(void) { mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", MTX_DEF|MTX_RECURSE); /* XXX at_exit() can fail with ENOMEN */ at_exit(pfs_exit); } /* * Tear down vnode cache */ void pfs_vncache_unload(void) { rm_at_exit(pfs_exit); + if (pfs_vncache_entries != 0) + printf("pfs_vncache_unload(): %d entries remaining\n", + pfs_vncache_entries); mtx_destroy(&pfs_vncache_mutex); } /* * Allocate a vnode */ int pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn, pid_t pid) { struct pfs_vdata *pvd; int error; /* see if the vnode is in the cache */ /* XXX linear search... not very efficient */ mtx_lock(&pfs_vncache_mutex); for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { if (pvd->pvd_pn == pn && pvd->pvd_pid == pid) { if (vget(pvd->pvd_vnode, 0, curthread) == 0) { ++pfs_vncache_hits; *vpp = pvd->pvd_vnode; mtx_unlock(&pfs_vncache_mutex); return (0); } /* XXX if this can happen, we're in trouble */ break; } } mtx_unlock(&pfs_vncache_mutex); ++pfs_vncache_misses; /* nope, get a new one */ MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); if (++pfs_vncache_entries > pfs_vncache_maxentries) pfs_vncache_maxentries = pfs_vncache_entries; error = getnewvnode(VT_PSEUDOFS, mp, pfs_vnodeop_p, vpp); if (error) return (error); pvd->pvd_pn = pn; pvd->pvd_pid = pid; (*vpp)->v_data = pvd; switch (pn->pn_type) { case pfstype_root: (*vpp)->v_flag = VROOT; #if 0 printf("root vnode allocated\n"); #endif /* fall through */ case pfstype_dir: case pfstype_this: case pfstype_parent: case pfstype_procdir: (*vpp)->v_type = VDIR; break; case pfstype_file: (*vpp)->v_type = VREG; break; case pfstype_symlink: (*vpp)->v_type = VLNK; break; case pfstype_none: KASSERT(0, ("pfs_vncache_alloc called for null node\n")); default: panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); } pvd->pvd_vnode = *vpp; mtx_lock(&pfs_vncache_mutex); pvd->pvd_prev = NULL; pvd->pvd_next = pfs_vncache; if (pvd->pvd_next) pvd->pvd_next->pvd_prev = pvd; pfs_vncache = pvd; mtx_unlock(&pfs_vncache_mutex); return (0); } /* * Free a vnode */ int pfs_vncache_free(struct vnode *vp) { struct pfs_vdata *pvd; mtx_lock(&pfs_vncache_mutex); pvd = (struct pfs_vdata *)vp->v_data; KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); if (pvd->pvd_next) pvd->pvd_next->pvd_prev = pvd->pvd_prev; if (pvd->pvd_prev) pvd->pvd_prev->pvd_next = pvd->pvd_next; else pfs_vncache = pvd->pvd_next; mtx_unlock(&pfs_vncache_mutex); --pfs_vncache_entries; FREE(pvd, M_PFSVNCACHE); vp->v_data = NULL; return (0); } /* * Free all vnodes associated with a defunct process */ static void pfs_exit(struct proc *p) { struct pfs_vdata *pvd, *prev; mtx_lock(&pfs_vncache_mutex); /* * The double loop is necessary because vgone() indirectly * calls pfs_vncache_free() which frees pvd, so we have to * backtrace one step every time we free a vnode. */ /* XXX linear search... not very efficient */ for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { while (pvd != NULL && pvd->pvd_pid == p->p_pid) { prev = pvd->pvd_prev; vgone(pvd->pvd_vnode); pvd = prev ? prev->pvd_next : pfs_vncache; } } mtx_unlock(&pfs_vncache_mutex); +} + +/* + * Disable a pseudofs node, and free all vnodes associated with it + */ +int +pfs_disable(struct pfs_node *pn) +{ + struct pfs_vdata *pvd, *prev; + + if (pn->pn_flags & PFS_DISABLED) + return (0); + mtx_lock(&pfs_vncache_mutex); + pn->pn_flags |= PFS_DISABLED; + /* see the comment about the double loop in pfs_exit() */ + /* XXX linear search... not very efficient */ + for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { + while (pvd != NULL && pvd->pvd_pn == pn) { + prev = pvd->pvd_prev; + vgone(pvd->pvd_vnode); + pvd = prev ? prev->pvd_next : pfs_vncache; + } + } + mtx_unlock(&pfs_vncache_mutex); + return (0); +} + +/* + * Re-enable a disabled pseudofs node + */ +int +pfs_enable(struct pfs_node *pn) +{ + pn->pn_flags &= ~PFS_DISABLED; + return (0); } Index: head/sys/fs/pseudofs/pseudofs_vnops.c =================================================================== --- head/sys/fs/pseudofs/pseudofs_vnops.c (revision 84385) +++ head/sys/fs/pseudofs/pseudofs_vnops.c (revision 84386) @@ -1,698 +1,700 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #define PFS_TRACE(foo) \ do { \ printf("pseudofs: %s(): ", __FUNCTION__); \ printf foo ; \ printf("\n"); \ } while (0) #define PFS_RETURN(err) \ do { \ printf("pseudofs: %s(): returning %d\n", __FUNCTION__, err); \ return (err); \ } while (0) #else #define PFS_TRACE(foo) \ do { /* nothing */ } while (0) #define PFS_RETURN(err) \ return (err) #endif /* * Returns non-zero if given file is visible to given process */ static int pfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid) { struct proc *proc; int r; PFS_TRACE(("%s (pid: %d, req: %d)", pn->pn_name, pid, td->td_proc->p_pid)); - if (pid == NO_PID) - PFS_RETURN (1); + + if (pn->pn_flags & PFS_DISABLED) + PFS_RETURN (0); r = 1; - if ((proc = pfind(pid)) == NULL) - PFS_RETURN (0); - /* XXX should lock td->td_proc? */ - if (p_cansee(td->td_proc, proc) != 0 || - (pn->pn_vis != NULL && !(pn->pn_vis)(td, proc, pn))) - r = 0; - PROC_UNLOCK(proc); + if (pid != NO_PID) { + if ((proc = pfind(pid)) == NULL) + PFS_RETURN (0); + /* XXX should lock td->td_proc? */ + if (p_cansee(td->td_proc, proc) != 0 || + (pn->pn_vis != NULL && !(pn->pn_vis)(td, proc, pn))) + r = 0; + PROC_UNLOCK(proc); + } PFS_RETURN (r); } /* * Verify permissions */ static int pfs_access(struct vop_access_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct vattr vattr; int error; PFS_TRACE((pn->pn_name)); error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td); if (error) PFS_RETURN (error); error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid, vattr.va_gid, va->a_mode, va->a_cred, NULL); PFS_RETURN (error); } /* * Close a file or directory */ static int pfs_close(struct vop_close_args *va) { PFS_RETURN (0); } /* * Get file attributes */ static int pfs_getattr(struct vop_getattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct vattr *vap = va->a_vap; struct proc *proc; int error = 0; PFS_TRACE((pn->pn_name)); VATTR_NULL(vap); vap->va_type = vn->v_type; vap->va_fileid = pn->pn_fileno; vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0]; vap->va_nlink = 1; nanotime(&vap->va_ctime); vap->va_atime = vap->va_mtime = vap->va_ctime; switch (pn->pn_type) { case pfstype_procdir: - /* XXX needs p_cansee */ case pfstype_root: case pfstype_dir: vap->va_mode = 0555; break; case pfstype_file: case pfstype_symlink: vap->va_mode = 0444; break; default: printf("shouldn't be here!\n"); vap->va_mode = 0; break; } if (pvd->pvd_pid != NO_PID) { if ((proc = pfind(pvd->pvd_pid)) == NULL) PFS_RETURN (ENOENT); vap->va_uid = proc->p_ucred->cr_ruid; vap->va_gid = proc->p_ucred->cr_rgid; if (pn->pn_attr != NULL) error = (pn->pn_attr)(va->a_td, proc, pn, vap); PROC_UNLOCK(proc); } else { vap->va_uid = 0; vap->va_gid = 0; } PFS_RETURN (error); } /* * Look up a file or directory */ static int pfs_lookup(struct vop_lookup_args *va) { struct vnode *vn = va->a_dvp; struct vnode **vpp = va->a_vpp; struct componentname *cnp = va->a_cnp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pd = pvd->pvd_pn; struct pfs_node *pn, *pdn = NULL; pid_t pid = pvd->pvd_pid; char *pname; int error, i, namelen; PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr)); if (vn->v_type != VDIR) PFS_RETURN (ENOTDIR); /* * Don't support DELETE or RENAME. CREATE is supported so * that O_CREAT will work, but the lookup will still fail if * the file does not exist. */ if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) PFS_RETURN (EOPNOTSUPP); /* shortcut: check if the name is too long */ if (cnp->cn_namelen >= PFS_NAMELEN) PFS_RETURN (ENOENT); /* check that parent directory is visisble... */ if (!pfs_visible(curthread, pd, pvd->pvd_pid)) PFS_RETURN (ENOENT); /* self */ namelen = cnp->cn_namelen; pname = cnp->cn_nameptr; if (namelen == 1 && *pname == '.') { pn = pd; *vpp = vn; VREF(vn); PFS_RETURN (0); } /* parent */ if (cnp->cn_flags & ISDOTDOT) { if (pd->pn_type == pfstype_root) PFS_RETURN (EIO); KASSERT(pd->pn_parent, ("non-root directory has no parent")); /* * This one is tricky. Descendents of procdir nodes * inherit their parent's process affinity, but * there's no easy reverse mapping. For simplicity, * we assume that if this node is a procdir, its * parent isn't (which is correct as long as * descendents of procdir nodes are never procdir * nodes themselves) */ if (pd->pn_type == pfstype_procdir) pid = NO_PID; pn = pd->pn_parent; goto got_pnode; } /* named node */ for (pn = pd->pn_nodes; pn->pn_type; ++pn) if (pn->pn_type == pfstype_procdir) pdn = pn; else if (pn->pn_name[namelen] == '\0' && bcmp(pname, pn->pn_name, namelen) == 0) goto got_pnode; /* process dependent node */ if ((pn = pdn) != NULL) { pid = 0; for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i) if ((pid = pid * 10 + pname[i] - '0') > PID_MAX) break; if (i == cnp->cn_namelen) goto got_pnode; } PFS_RETURN (ENOENT); got_pnode: if (pn != pd->pn_parent && !pn->pn_parent) pn->pn_parent = pd; if (!pfs_visible(curthread, pn, pvd->pvd_pid)) PFS_RETURN (ENOENT); error = pfs_vncache_alloc(vn->v_mount, vpp, pn, pid); if (error) PFS_RETURN (error); if (cnp->cn_flags & MAKEENTRY) cache_enter(vn, *vpp, cnp); PFS_RETURN (0); } /* * Open a file or directory. */ static int pfs_open(struct vop_open_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; int mode = va->a_mode; PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode)); /* * check if the file is visible to the caller * * XXX Not sure if this is necessary, as the VFS system calls * XXX pfs_lookup() and pfs_access() first, and pfs_lookup() * XXX calls pfs_visible(). There's a race condition here, but * XXX calling pfs_visible() from here doesn't really close it, * XXX and the only consequence of that race is an EIO further * XXX down the line. */ if (!pfs_visible(va->a_td, pn, pvd->pvd_pid)) PFS_RETURN (ENOENT); /* check if the requested mode is permitted */ if (((mode & FREAD) && !(mode & PFS_RD)) || ((mode & FWRITE) && !(mode & PFS_WR))) PFS_RETURN (EPERM); /* we don't support locking */ if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) PFS_RETURN (EOPNOTSUPP); PFS_RETURN (0); } /* * Read from a file */ static int pfs_read(struct vop_read_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc = NULL; struct sbuf *sb = NULL; char *ps; int error, xlen; PFS_TRACE((pn->pn_name)); if (vn->v_type != VREG) PFS_RETURN (EINVAL); if (!(pn->pn_flags & PFS_RD)) PFS_RETURN (EBADF); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid)) PFS_RETURN (EIO); /* XXX duplicates bits of pfs_visible() */ if (pvd->pvd_pid != NO_PID) { if ((proc = pfind(pvd->pvd_pid)) == NULL) PFS_RETURN (EIO); _PHOLD(proc); PROC_UNLOCK(proc); } if (pn->pn_flags & PFS_RAWRD) { error = (pn->pn_func)(curthread, proc, pn, NULL, uio); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } sb = sbuf_new(sb, NULL, uio->uio_offset + uio->uio_resid, 0); if (sb == NULL) { if (proc != NULL) PRELE(proc); PFS_RETURN (EIO); } error = (pn->pn_func)(curthread, proc, pn, sb, uio); if (proc != NULL) PRELE(proc); if (error) { sbuf_delete(sb); PFS_RETURN (error); } /* XXX we should possibly detect and handle overflows */ sbuf_finish(sb); ps = sbuf_data(sb) + uio->uio_offset; xlen = sbuf_len(sb) - uio->uio_offset; xlen = imin(xlen, uio->uio_resid); error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio)); sbuf_delete(sb); PFS_RETURN (error); } /* * Iterate through directory entries */ static int pfs_iterate(struct thread *td, pid_t pid, struct pfs_node **pn, struct proc **p) { if ((*pn)->pn_type == pfstype_none) return (-1); again: if ((*pn)->pn_type != pfstype_procdir) ++*pn; while ((*pn)->pn_type == pfstype_procdir) { if (*p == NULL) *p = LIST_FIRST(&allproc); else *p = LIST_NEXT(*p, p_list); if (*p != NULL) break; ++*pn; } if ((*pn)->pn_type == pfstype_none) return (-1); if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid)) goto again; return (0); } /* * Return directory entries. */ static int pfs_readdir(struct vop_readdir_args *va) { struct vnode *vn = va->a_vp; struct pfs_info *pi = (struct pfs_info *)vn->v_mount->mnt_data; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pd = pvd->pvd_pn; pid_t pid = pvd->pvd_pid; struct pfs_node *pn; struct dirent entry; struct uio *uio; struct proc *p; off_t offset; int error, i, resid; PFS_TRACE((pd->pn_name)); if (vn->v_type != VDIR) PFS_RETURN (ENOTDIR); uio = va->a_uio; /* check if the directory is visible to the caller */ if (!pfs_visible(curthread, pd, pid)) PFS_RETURN (ENOENT); /* only allow reading entire entries */ offset = uio->uio_offset; resid = uio->uio_resid; if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN) PFS_RETURN (EINVAL); /* skip unwanted entries */ sx_slock(&allproc_lock); for (pn = pd->pn_nodes, p = NULL; offset > 0; offset -= PFS_DELEN) if (pfs_iterate(curthread, pid, &pn, &p) == -1) break; /* fill in entries */ entry.d_reclen = PFS_DELEN; while (pfs_iterate(curthread, pid, &pn, &p) != -1 && resid > 0) { if (!pn->pn_parent) pn->pn_parent = pd; if (!pn->pn_fileno) pfs_fileno_alloc(pi, pn); if (pid != NO_PID) entry.d_fileno = pn->pn_fileno * NO_PID + pid; else entry.d_fileno = pn->pn_fileno; /* PFS_DELEN was picked to fit PFS_NAMLEN */ for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i) entry.d_name[i] = pn->pn_name[i]; entry.d_name[i] = 0; entry.d_namlen = i; switch (pn->pn_type) { case pfstype_procdir: KASSERT(p != NULL, ("reached procdir node with p == NULL")); entry.d_fileno = pn->pn_fileno * NO_PID + p->p_pid; entry.d_namlen = snprintf(entry.d_name, PFS_NAMELEN, "%d", p->p_pid); /* fall through */ case pfstype_root: case pfstype_dir: case pfstype_this: case pfstype_parent: entry.d_type = DT_DIR; break; case pfstype_file: entry.d_type = DT_REG; break; case pfstype_symlink: entry.d_type = DT_LNK; break; default: sx_sunlock(&allproc_lock); panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type); } if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) { sx_sunlock(&allproc_lock); PFS_RETURN (error); } offset += PFS_DELEN; resid -= PFS_DELEN; } sx_sunlock(&allproc_lock); uio->uio_offset += offset; PFS_RETURN (0); } /* * Read a symbolic link */ static int pfs_readlink(struct vop_readlink_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc = NULL; char buf[MAXPATHLEN], *ps; struct sbuf sb; int error, xlen; PFS_TRACE((pn->pn_name)); if (vn->v_type != VLNK) PFS_RETURN (EINVAL); if (pvd->pvd_pid != NO_PID) { if ((proc = pfind(pvd->pvd_pid)) == NULL) PFS_RETURN (EIO); _PHOLD(proc); PROC_UNLOCK(proc); } /* sbuf_new() can't fail with a static buffer */ sbuf_new(&sb, buf, sizeof buf, 0); error = (pn->pn_func)(curthread, proc, pn, &sb, NULL); if (proc != NULL) PRELE(proc); if (error) { sbuf_delete(&sb); PFS_RETURN (error); } /* XXX we should detect and handle overflows */ sbuf_finish(&sb); ps = sbuf_data(&sb) + uio->uio_offset; xlen = sbuf_len(&sb) - uio->uio_offset; xlen = imin(xlen, uio->uio_resid); error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio)); sbuf_delete(&sb); PFS_RETURN (error); } /* * Reclaim a vnode */ static int pfs_reclaim(struct vop_reclaim_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; PFS_TRACE((pn->pn_name)); return (pfs_vncache_free(va->a_vp)); } /* * Set attributes */ static int pfs_setattr(struct vop_setattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; PFS_TRACE((pn->pn_name)); if (va->a_vap->va_flags != (u_long)VNOVAL) PFS_RETURN (EOPNOTSUPP); /* XXX it's a bit more complex than that, really... */ PFS_RETURN (0); } /* * Read from a file */ static int pfs_write(struct vop_read_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc = NULL; struct sbuf sb; int error; PFS_TRACE((pn->pn_name)); if (vn->v_type != VREG) PFS_RETURN (EINVAL); if (!(pn->pn_flags & PFS_WR)) PFS_RETURN (EBADF); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid)) PFS_RETURN (EIO); /* XXX duplicates bits of pfs_visible() */ if (pvd->pvd_pid != NO_PID) { if ((proc = pfind(pvd->pvd_pid)) == NULL) PFS_RETURN (EIO); _PHOLD(proc); PROC_UNLOCK(proc); } if (pn->pn_flags & PFS_RAWWR) { error = (pn->pn_func)(curthread, proc, pn, NULL, uio); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } sbuf_uionew(&sb, uio, &error); if (error) PFS_RETURN (error); error = (pn->pn_func)(curthread, proc, pn, &sb, uio); if (proc != NULL) PRELE(proc); sbuf_delete(&sb); PFS_RETURN (error); } /* * Dummy operations */ static int pfs_badop(void *va) { return (EOPNOTSUPP); } #if 0 static int pfs_erofs(void *va) { return (EROFS); } static int pfs_null(void *va) { return (0); } #endif /* * Vnode operations */ vop_t **pfs_vnodeop_p; static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vop_defaultop }, { &vop_access_desc, (vop_t *)pfs_access }, { &vop_close_desc, (vop_t *)pfs_close }, { &vop_create_desc, (vop_t *)pfs_badop }, { &vop_getattr_desc, (vop_t *)pfs_getattr }, { &vop_link_desc, (vop_t *)pfs_badop }, { &vop_lookup_desc, (vop_t *)pfs_lookup }, { &vop_mkdir_desc, (vop_t *)pfs_badop }, { &vop_mknod_desc, (vop_t *)pfs_badop }, { &vop_open_desc, (vop_t *)pfs_open }, { &vop_read_desc, (vop_t *)pfs_read }, { &vop_readdir_desc, (vop_t *)pfs_readdir }, { &vop_readlink_desc, (vop_t *)pfs_readlink }, { &vop_reclaim_desc, (vop_t *)pfs_reclaim }, { &vop_remove_desc, (vop_t *)pfs_badop }, { &vop_rename_desc, (vop_t *)pfs_badop }, { &vop_rmdir_desc, (vop_t *)pfs_badop }, { &vop_setattr_desc, (vop_t *)pfs_setattr }, { &vop_symlink_desc, (vop_t *)pfs_badop }, { &vop_write_desc, (vop_t *)pfs_write }, /* XXX I've probably forgotten a few that need pfs_badop */ { NULL, (vop_t *)NULL } }; static struct vnodeopv_desc pfs_vnodeop_opv_desc = { &pfs_vnodeop_p, pfs_vnodeop_entries }; VNODEOP_SET(pfs_vnodeop_opv_desc);