summaryrefslogtreecommitdiffstats
path: root/sys/fs/pseudofs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/pseudofs')
-rw-r--r--sys/fs/pseudofs/pseudofs.h38
-rw-r--r--sys/fs/pseudofs/pseudofs_vncache.c96
-rw-r--r--sys/fs/pseudofs/pseudofs_vnops.c126
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 }
};
OpenPOWER on IntegriCloud