Index: sys/kern/vfs_lookup.c =================================================================== --- sys/kern/vfs_lookup.c (revision 166) +++ sys/kern/vfs_lookup.c (working copy) @@ -461,6 +461,7 @@ /* XXX This should probably move to the top of function. */ if (cnp->cn_flags & SAVESTART) panic("lookup: SAVESTART"); + printf("success without updating parentdir %p\n", dp); goto success; } @@ -690,6 +691,13 @@ ndp->ni_startdir = ndp->ni_dvp; VREF(ndp->ni_startdir); } + + if (dp->v_watched && ndp->ni_vp && ndp->ni_vp != dp) { + printf("updating parentdir vp %p par %p\n", ndp->ni_vp, dp); + ndp->ni_vp->v_parentdir = dp; + RB_INSERT(vn_rb_tree, &dp->v_subwatched, ndp->ni_vp); + } + if (!wantparent) { if (ndp->ni_dvp != dp) vput(ndp->ni_dvp); Index: sys/kern/kern_event.c =================================================================== --- sys/kern/kern_event.c (revision 167) +++ sys/kern/kern_event.c (working copy) @@ -535,6 +535,9 @@ UIDINFO_UNLOCK(uip); knlist_add(&vp->v_pollinfo->vpi_selinfo.si_note, kn, 0); + + vp->v_watched++; + printf("vp: %p watched: %d\n", vp, vp->v_watched); done: NDFREE(&nd, NDF_ONLY_PNBUF); VOP_UNLOCK(vp, 0, curthread); @@ -547,7 +550,7 @@ filt_nvnodedetach(struct knote *kn) { struct uidinfo *uip; - struct vnode *vp; + struct vnode *vp, *v; vp = (struct vnode *)kn->kn_hook; KASSERT(vp->v_pollinfo != NULL, ("Missing v_pollinfo")); @@ -556,7 +559,18 @@ uip->ui_watchedvnodes--; UIDINFO_UNLOCK(uip); knlist_remove(&vp->v_pollinfo->vpi_selinfo.si_note, kn, 0); - vrele(vp); + /* XXX Should we VFS_LOCK_GIANT here? */ + if (vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread)) + panic("Couldn't lock vnode"); + vp->v_watched--; + if (vp->v_watched == 0) { + RB_FOREACH(v, vn_rb_tree, &vp->v_subwatched) { + printf("%s: removing %p from parent %p\n", __func__, v, vp); + RB_REMOVE(vn_rb_tree, &vp->v_subwatched, v); + } + } + printf("vp: %p watched: %d\n", vp, vp->v_watched); + vput(vp); } static int Index: sys/kern/vfs_cache.c =================================================================== --- sys/kern/vfs_cache.c (revision 166) +++ sys/kern/vfs_cache.c (working copy) @@ -656,11 +656,21 @@ return (error); error = cache_lookup(dvp, vpp, cnp); - if (error == 0) - return (VOP_CACHEDLOOKUP(dvp, vpp, cnp)); + if (error == 0) { + error = VOP_CACHEDLOOKUP(dvp, vpp, cnp); + goto done; + } if (error == ENOENT) return (error); - return (0); + error = 0; +done: + /* XXX locking in the DOTDOT case */ + if (dvp->v_watched && *vpp && dvp != *vpp && !(*vpp)->v_parentdir) { + printf("%s: adding %p to parent %p\n", __func__, *vpp, dvp); + (*vpp)->v_parentdir = dvp; + RB_INSERT(vn_rb_tree, &dvp->v_subwatched, *vpp); + } + return (error); } Index: sys/kern/vfs_subr.c =================================================================== --- sys/kern/vfs_subr.c (revision 166) +++ sys/kern/vfs_subr.c (working copy) @@ -856,6 +856,9 @@ */ LIST_INIT(&vp->v_cache_src); TAILQ_INIT(&vp->v_cache_dst); + + RB_INIT(&vp->v_subwatched); + /* * Finalize various vnode identity bits. */ @@ -2261,6 +2264,13 @@ if (vinvalbuf(vp, V_SAVE, td, 0, 0) != 0) vinvalbuf(vp, 0, td, 0, 0); + /* XXX free the rb tree, if it exists, remove from parent's rb */ + KASSERT(RB_EMPTY(vn->v_subwatched), ("v_subwatched not empty!")); + if (vp->v_parentdir) { + printf("removing %p from parent %p\n", vp, vp->v_parentdir); + RB_REMOVE(vn_rb_tree, &vp->v_parentdir->v_subwatched, vp); + } + /* * If purging an active vnode, it must be closed and * deactivated before being reclaimed. @@ -3412,9 +3422,16 @@ vop_create_post(void *ap, int rc) { struct vop_create_args *a = ap; + struct vnode *vp = *a->a_vpp; - if (!rc) + if (!rc) { + if (a->a_dvp->v_watched) { + printf("adding parentdir %p to %p (%d)\n", a->a_dvp, vp, a->a_dvp->v_watched); + vp->v_parentdir = a->a_dvp; + RB_INSERT(vn_rb_tree, &a->a_dvp->v_subwatched, vp); + } VFS_SEND_KNOTE(a->a_dvp, NOTE_WRITE); + } } void @@ -3432,18 +3449,30 @@ vop_mkdir_post(void *ap, int rc) { struct vop_mkdir_args *a = ap; + struct vnode *vp = *a->a_vpp; - if (!rc) + if (!rc) { + if (a->a_dvp->v_watched) { + vp->v_parentdir = a->a_dvp; + RB_INSERT(vn_rb_tree, &a->a_dvp->v_subwatched, vp); + } VFS_SEND_KNOTE(a->a_dvp, NOTE_WRITE | NOTE_LINK); + } } void vop_mknod_post(void *ap, int rc) { struct vop_mknod_args *a = ap; + struct vnode *vp = *a->a_vpp; - if (!rc) + if (!rc) { + if (a->a_dvp->v_watched) { + vp->v_parentdir = a->a_dvp; + RB_INSERT(vn_rb_tree, &a->a_dvp->v_subwatched, vp); + } VFS_SEND_KNOTE(a->a_dvp, NOTE_WRITE); + } } void @@ -3454,6 +3483,8 @@ if (!rc) { VFS_SEND_KNOTE(a->a_dvp, NOTE_WRITE); VFS_SEND_KNOTE(a->a_vp, NOTE_DELETE); + RB_REMOVE(vn_rb_tree, &a->a_dvp->v_subwatched, a->a_vp); + a->a_vp->v_parentdir = NULL; } } @@ -3463,6 +3494,11 @@ struct vop_rename_args *a = ap; if (!rc) { + if (a->a_tdvp->v_watched) { + a->a_fvp->v_parentdir = a->a_tdvp; + RB_INSERT(vn_rb_tree, &a->a_tdvp->v_subwatched, + a->a_fvp->v_parentdir); + } VFS_SEND_KNOTE(a->a_fdvp, NOTE_WRITE); VFS_SEND_KNOTE(a->a_tdvp, NOTE_WRITE); VFS_SEND_KNOTE(a->a_fvp, NOTE_RENAME); @@ -3486,9 +3522,17 @@ vop_setattr_post(void *ap, int rc) { struct vop_setattr_args *a = ap; + struct vnode *v; - if (!rc) + if (!rc) { + if (a->a_vp->v_parentdir) { + printf("vp: %p parent: %p children:", a->a_vp, a->a_vp->v_parentdir); + RB_FOREACH(v, vn_rb_tree, &a->a_vp->v_parentdir->v_subwatched) + printf(" %p", v); + printf("\n"); + } VFS_SEND_KNOTE(a->a_vp, NOTE_ATTRIB); + } } void @@ -3692,3 +3736,16 @@ } return (kn->kn_fflags != 0); } + +static int rb_vn_cmp(struct vnode *v1, struct vnode *v2); +RB_GENERATE(vn_rb_tree, vnode, v_rbnode, rb_vn_cmp); + +static int +rb_vn_cmp(struct vnode *v1, struct vnode *v2) +{ + if (v1 < v2) + return (-1); + if (v1 > v2) + return (1); + return (0); +} Index: sys/sys/vnode.h =================================================================== --- sys/sys/vnode.h (revision 166) +++ sys/sys/vnode.h (working copy) @@ -50,6 +50,8 @@ #include #include +#include + /* * The vnode is the focus of all file activity in UNIX. There is a * unique vnode allocated for each active file, each current directory, @@ -105,6 +107,10 @@ #if defined(_KERNEL) || defined(_KVM_VNODE) +//struct vn_rb_tree; +RB_HEAD(vn_rb_tree, vnode); +RB_PROTOTYPE(vn_rb_tree, vnode, v_rbnode, rb_vn_cmp); + struct vnode { /* * Fields which define the identity of the vnode. These fields are @@ -181,6 +187,12 @@ */ struct vpollinfo *v_pollinfo; /* G Poll events, p for *v_pi */ struct label *v_label; /* MAC label for vnode */ + + int v_watched; /* v */ + struct vnode *v_parentdir; /* v */ + struct vn_rb_tree v_subwatched; /* v */ + RB_ENTRY(vnode) v_rbnode; /* v */ + }; #endif /* defined(_KERNEL) || defined(_KVM_VNODE) */