diff options
Diffstat (limited to 'sys/fs')
-rw-r--r-- | sys/fs/pseudofs/pseudofs.h | 38 | ||||
-rw-r--r-- | sys/fs/pseudofs/pseudofs_vncache.c | 96 | ||||
-rw-r--r-- | sys/fs/pseudofs/pseudofs_vnops.c | 126 |
3 files changed, 181 insertions, 79 deletions
diff --git a/sys/fs/pseudofs/pseudofs.h b/sys/fs/pseudofs/pseudofs.h index 8bff258..e925d92 100644 --- a/sys/fs/pseudofs/pseudofs.h +++ b/sys/fs/pseudofs/pseudofs.h @@ -89,6 +89,15 @@ 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 { @@ -114,6 +123,7 @@ struct pfs_node { #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 */ @@ -121,24 +131,24 @@ struct pfs_node { u_int32_t pn_fileno; }; -#define PFS_NODE(name, type, fill, attr, data, flags) \ - { (name), (type), { (fill) }, (attr), (data), (flags) } -#define PFS_DIR(name, nodes, attr, data, flags) \ - PFS_NODE(name, pfstype_dir, nodes, attr, data, flags) +#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, 0) + PFS_NODE("/", pfstype_root, nodes, NULL, NULL, NULL, 0) #define PFS_THIS \ - PFS_NODE(".", pfstype_this, NULL, NULL, NULL, 0) + PFS_NODE(".", pfstype_this, NULL, NULL, NULL, NULL, 0) #define PFS_PARENT \ - PFS_NODE("..", pfstype_parent, NULL, NULL, NULL, 0) -#define PFS_FILE(name, func, attr, data, flags) \ - PFS_NODE(name, pfstype_file, func, attr, data, flags) -#define PFS_SYMLINK(name, func, attr, data, flags) \ - PFS_NODE(name, pfstype_symlink, func, attr, data, flags) -#define PFS_PROCDIR(nodes, attr, data, flags) \ - PFS_NODE("", pfstype_procdir, nodes, attr, data, flags) + 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, 0) + PFS_NODE("", pfstype_none, NULL, NULL, NULL, NULL, 0) /* * VFS interface diff --git a/sys/fs/pseudofs/pseudofs_vncache.c b/sys/fs/pseudofs/pseudofs_vncache.c index 3ff33d7..191f85c 100644 --- a/sys/fs/pseudofs/pseudofs_vncache.c +++ b/sys/fs/pseudofs/pseudofs_vncache.c @@ -35,6 +35,7 @@ #include <sys/malloc.h> #include <sys/mount.h> #include <sys/mutex.h> +#include <sys/proc.h> #include <sys/sbuf.h> #include <sys/sysctl.h> #include <sys/vnode.h> @@ -45,21 +46,30 @@ static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); static struct mtx pfs_vncache_mutex; - -struct pfs_vnode { - struct vnode *pv_vnode; - struct pfs_vnode *pv_next; -} *pfs_vncache; +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, +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, +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; @@ -70,7 +80,9 @@ extern vop_t **pfs_vnodeop_p; void pfs_vncache_load(void) { - mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", MTX_DEF); + mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", MTX_DEF|MTX_RECURSE); + /* XXX at_exit() can fail with ENOMEN */ + at_exit(pfs_exit); } /* @@ -79,6 +91,7 @@ pfs_vncache_load(void) void pfs_vncache_unload(void) { + rm_at_exit(pfs_exit); mtx_destroy(&pfs_vncache_mutex); } @@ -89,18 +102,17 @@ int pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn, pid_t pid) { - struct pfs_vnode *pv; 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 (pv = pfs_vncache; pv; pv = pv->pv_next) { - pvd = (struct pfs_vdata *)pv->pv_vnode->v_data; + for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { if (pvd->pvd_pn == pn && pvd->pvd_pid == pid) { - if (vget(pv->pv_vnode, 0, curthread) == 0) { + if (vget(pvd->pvd_vnode, 0, curthread) == 0) { ++pfs_vncache_hits; - *vpp = pv->pv_vnode; + *vpp = pvd->pvd_vnode; mtx_unlock(&pfs_vncache_mutex); return (0); } @@ -112,8 +124,9 @@ pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, ++pfs_vncache_misses; /* nope, get a new one */ - MALLOC(pv, struct pfs_vnode *, sizeof *pv, M_PFSVNCACHE, M_WAITOK); 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); @@ -126,6 +139,7 @@ pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, #if 0 printf("root vnode allocated\n"); #endif + /* fall through */ case pfstype_dir: case pfstype_this: case pfstype_parent: @@ -143,10 +157,13 @@ pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, default: panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); } - pv->pv_vnode = *vpp; + pvd->pvd_vnode = *vpp; mtx_lock(&pfs_vncache_mutex); - pv->pv_next = pfs_vncache; - pfs_vncache = pv; + 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); } @@ -157,23 +174,46 @@ pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, int pfs_vncache_free(struct vnode *vp) { - struct pfs_vnode *prev, *pv; struct pfs_vdata *pvd; mtx_lock(&pfs_vncache_mutex); - for (prev = NULL, pv = pfs_vncache; pv; prev = pv, pv = pv->pv_next) - if (pv->pv_vnode == vp) - break; - KASSERT(pv != NULL, ("pfs_vncache_free(): not in cache\n")); - if (prev) - prev->pv_next = pv->pv_next; + 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 = pv->pv_next; + pfs_vncache = pvd->pvd_next; mtx_unlock(&pfs_vncache_mutex); - - pvd = (struct pfs_vdata *)vp->v_data; + + --pfs_vncache_entries; FREE(pvd, M_PFSVNCACHE); vp->v_data = NULL; - FREE(pv, M_PFSVNCACHE); 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); +} diff --git a/sys/fs/pseudofs/pseudofs_vnops.c b/sys/fs/pseudofs/pseudofs_vnops.c index c582612..305ed21 100644 --- a/sys/fs/pseudofs/pseudofs_vnops.c +++ b/sys/fs/pseudofs/pseudofs_vnops.c @@ -67,6 +67,31 @@ #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); + + 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); + PFS_RETURN (r); +} + +/* * Verify permissions */ static int @@ -82,10 +107,10 @@ pfs_access(struct vop_access_args *va) error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td); if (error) - return (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); - return (error); + PFS_RETURN (error); } /* @@ -146,7 +171,7 @@ pfs_getattr(struct vop_getattr_args *va) 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)(curthread, proc, pn, vap); + error = (pn->pn_attr)(va->a_td, proc, pn, vap); PROC_UNLOCK(proc); } else { vap->va_uid = 0; @@ -168,7 +193,6 @@ pfs_lookup(struct vop_lookup_args *va) struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pd = pvd->pvd_pn; struct pfs_node *pn, *pdn = NULL; - struct proc *proc; pid_t pid = pvd->pvd_pid; char *pname; int error, i, namelen; @@ -190,12 +214,9 @@ pfs_lookup(struct vop_lookup_args *va) if (cnp->cn_namelen >= PFS_NAMELEN) PFS_RETURN (ENOENT); - /* check that owner process exists */ - if (pvd->pvd_pid != NO_PID) { - if ((proc = pfind(pvd->pvd_pid)) == NULL) - PFS_RETURN (ENOENT); - PROC_UNLOCK(proc); - } + /* check that parent directory is visisble... */ + if (!pfs_visible(curthread, pd, pvd->pvd_pid)) + PFS_RETURN (ENOENT); /* self */ namelen = cnp->cn_namelen; @@ -204,7 +225,7 @@ pfs_lookup(struct vop_lookup_args *va) pn = pd; *vpp = vn; VREF(vn); - goto got_vnode; + PFS_RETURN (0); } /* parent */ @@ -223,10 +244,8 @@ pfs_lookup(struct vop_lookup_args *va) */ if (pd->pn_type == pfstype_procdir) pid = NO_PID; - error = pfs_vncache_alloc(vn->v_mount, vpp, pd->pn_parent, pid); - if (error) - PFS_RETURN (error); - goto got_vnode; + pn = pd->pn_parent; + goto got_pnode; } /* named node */ @@ -249,12 +268,13 @@ pfs_lookup(struct vop_lookup_args *va) PFS_RETURN (ENOENT); got_pnode: - if (!pn->pn_parent) + 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); - got_vnode: if (cnp->cn_flags & MAKEENTRY) cache_enter(vn, *vpp, cnp); PFS_RETURN (0); @@ -270,11 +290,22 @@ pfs_open(struct vop_open_args *va) struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data; struct pfs_node *pn = pvd->pvd_pn; int mode = va->a_mode; - struct proc *proc; - int error; 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))) @@ -284,17 +315,7 @@ pfs_open(struct vop_open_args *va) if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) PFS_RETURN (EOPNOTSUPP); - error = 0; - if (pvd->pvd_pid != NO_PID) { - if ((proc = pfind(pvd->pvd_pid)) == NULL) - PFS_RETURN (ENOENT); - /* XXX should lock va->a_td->td_proc? */ - if (p_cansee(va->a_td->td_proc, proc) != 0) - error = ENOENT; - PROC_UNLOCK(proc); - } - - PFS_RETURN (error); + PFS_RETURN (0); } /* @@ -320,6 +341,14 @@ pfs_read(struct vop_read_args *va) 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); @@ -365,11 +394,12 @@ pfs_read(struct vop_read_args *va) * Iterate through directory entries */ static int -pfs_iterate(struct pfs_info *pi, struct pfs_node **pn, struct proc **p) +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; @@ -379,12 +409,15 @@ pfs_iterate(struct pfs_info *pi, struct pfs_node **pn, struct proc **p) else *p = LIST_NEXT(*p, p_list); if (*p != NULL) - return (0); + break; ++*pn; } if ((*pn)->pn_type == pfstype_none) return (-1); + + if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid)) + goto again; return (0); } @@ -399,6 +432,7 @@ pfs_readdir(struct vop_readdir_args *va) 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; @@ -412,6 +446,10 @@ pfs_readdir(struct vop_readdir_args *va) 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; @@ -421,18 +459,18 @@ pfs_readdir(struct vop_readdir_args *va) /* skip unwanted entries */ sx_slock(&allproc_lock); for (pn = pd->pn_nodes, p = NULL; offset > 0; offset -= PFS_DELEN) - if (pfs_iterate(pi, &pn, &p) == -1) + if (pfs_iterate(curthread, pid, &pn, &p) == -1) break; /* fill in entries */ entry.d_reclen = PFS_DELEN; - while (pfs_iterate(pi, &pn, &p) != -1 && resid > 0) { + 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 (pvd->pvd_pid != NO_PID) - entry.d_fileno = pn->pn_fileno * NO_PID + pvd->pvd_pid; + 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 */ @@ -533,6 +571,12 @@ pfs_readlink(struct vop_readlink_args *va) 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)); } @@ -576,6 +620,14 @@ pfs_write(struct vop_read_args *va) 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); @@ -636,7 +688,7 @@ static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = { { &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_erofs */ + /* XXX I've probably forgotten a few that need pfs_badop */ { NULL, (vop_t *)NULL } }; |