summaryrefslogtreecommitdiffstats
path: root/sys/fs/pseudofs/pseudofs_vncache.c
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2001-10-01 04:22:20 +0000
committerdes <des@FreeBSD.org>2001-10-01 04:22:20 +0000
commite1b05f5687fbe0556cfab55517800169bc5da426 (patch)
tree62268de8821a9da6754d96e01ebfdc5653ef7674 /sys/fs/pseudofs/pseudofs_vncache.c
parentd6dca2e412b6f853bdfed2ddcf74bd5d060e8d67 (diff)
downloadFreeBSD-src-e1b05f5687fbe0556cfab55517800169bc5da426.zip
FreeBSD-src-e1b05f5687fbe0556cfab55517800169bc5da426.tar.gz
YA pseudofs megacommit, part 1:
- Add a third callback to the pfs_node structure. This one simply returns non-zero if the specified requesting process is allowed to access the specified node for the specified target process. This is used in addition to the usual permission checks, e.g. when certain files don't make sense for certain (system) processes. - Make sure that pfs_lookup() and pfs_readdir() don't yap about files which aren't pfs_visible(). Also check pfs_visible() before performing reads and writes, to prevent the kind of races reported in SA-00:77 and SA-01:55 (fork a child, open /proc/child/ctl, have that child fork a setuid binary, and assume control of it). - Add some more trace points.
Diffstat (limited to 'sys/fs/pseudofs/pseudofs_vncache.c')
-rw-r--r--sys/fs/pseudofs/pseudofs_vncache.c96
1 files changed, 68 insertions, 28 deletions
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);
+}
OpenPOWER on IntegriCloud