kernel - Do not allow destroyed namecache entries to be re-resolved
[dragonfly.git] / sys / kern / vfs_cache.c
index 5b92fe5..d4899d3 100644 (file)
@@ -186,16 +186,17 @@ static void _cache_setunresolved(struct namecache *ncp);
 static void _cache_cleanneg(int count);
 static void _cache_cleanpos(int count);
 static void _cache_cleandefered(void);
+static void _cache_unlink(struct namecache *ncp);
 
 /*
  * The new name cache statistics
  */
 SYSCTL_NODE(_vfs, OID_AUTO, cache, CTLFLAG_RW, 0, "Name cache statistics");
 static int numneg;
-SYSCTL_ULONG(_vfs_cache, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0,
+SYSCTL_INT(_vfs_cache, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0,
     "Number of negative namecache entries");
 static int numcache;
-SYSCTL_ULONG(_vfs_cache, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0,
+SYSCTL_INT(_vfs_cache, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0,
     "Number of namecaches entries");
 static u_long numcalls;
 SYSCTL_ULONG(_vfs_cache, OID_AUTO, numcalls, CTLFLAG_RD, &numcalls, 0,
@@ -901,6 +902,11 @@ _cache_setvp(struct mount *mp, struct namecache *ncp, struct vnode *vp)
                }
                atomic_add_int(&numcache, 1);
                ncp->nc_error = 0;
+               /* XXX: this is a hack to work-around the lack of a real pfs vfs
+                * implementation*/
+               if (mp != NULL)
+                       if (strncmp(mp->mnt_stat.f_fstypename, "null", 5) == 0)
+                               vp->v_pfsmp = mp;
        } else {
                /*
                 * When creating a negative cache hit we set the
@@ -916,7 +922,7 @@ _cache_setvp(struct mount *mp, struct namecache *ncp, struct vnode *vp)
                spin_unlock(&ncspin);
                ncp->nc_error = ENOENT;
                if (mp)
-                       ncp->nc_namecache_gen = mp->mnt_namecache_gen;
+                       VFS_NCPGEN_SET(mp, ncp);
        }
        ncp->nc_flag &= ~(NCF_UNRESOLVED | NCF_DEFEREDZAP);
 }
@@ -1028,8 +1034,7 @@ _cache_auto_unresolve(struct mount *mp, struct namecache *ncp)
         * If a resolved negative cache hit is invalid due to
         * the mount's namecache generation being bumped, zap it.
         */
-       if (ncp->nc_vp == NULL &&
-           ncp->nc_namecache_gen != mp->mnt_namecache_gen) {
+       if (ncp->nc_vp == NULL && VFS_NCPGEN_TEST(mp, ncp)) {
                _cache_setunresolved(ncp);
                return;
        }
@@ -1368,14 +1373,26 @@ cache_rename(struct nchandle *fnch, struct nchandle *tnch)
        struct nchash_head *nchpp;
        u_int32_t hash;
        char *oname;
+       char *nname;
+
+       if (tncp->nc_nlen) {
+               nname = kmalloc(tncp->nc_nlen + 1, M_VFSCACHE, M_WAITOK);
+               bcopy(tncp->nc_name, nname, tncp->nc_nlen);
+               nname[tncp->nc_nlen] = 0;
+       } else {
+               nname = NULL;
+       }
 
        /*
         * Rename fncp (unlink)
         */
        _cache_unlink_parent(fncp);
        oname = fncp->nc_name;
-       fncp->nc_name = tncp->nc_name;
+       fncp->nc_name = nname;
        fncp->nc_nlen = tncp->nc_nlen;
+       if (oname)
+               kfree(oname, M_VFSCACHE);
+
        tncp_par = tncp->nc_parent;
        _cache_hold(tncp_par);
        _cache_lock(tncp_par);
@@ -1396,13 +1413,24 @@ cache_rename(struct nchandle *fnch, struct nchandle *tnch)
        /*
         * Get rid of the overwritten tncp (unlink)
         */
-       _cache_setunresolved(tncp);
-       _cache_unlink_parent(tncp);
-       tncp->nc_name = NULL;
-       tncp->nc_nlen = 0;
+       _cache_unlink(tncp);
+}
 
-       if (oname)
-               kfree(oname, M_VFSCACHE);
+/*
+ * Perform actions consistent with unlinking a file.  The namecache
+ * entry is marked DESTROYED so it no longer shows up in searches,
+ * and will be physically deleted when the vnode goes away.
+ */
+void
+cache_unlink(struct nchandle *nch)
+{
+       _cache_unlink(nch->ncp);
+}
+
+static void
+_cache_unlink(struct namecache *ncp)
+{
+       ncp->nc_flag |= NCF_DESTROYED;
 }
 
 /*
@@ -2495,6 +2523,7 @@ cache_findmount_callback(struct mount *mp, void *data)
            mp->mnt_ncmounton.ncp == info->nch_ncp
        ) {
            info->result = mp;
+           atomic_add_int(&mp->mnt_refs, 1);
            return(-1);
        }
        return(0);
@@ -2513,6 +2542,12 @@ cache_findmount(struct nchandle *nch)
        return(info.result);
 }
 
+void
+cache_dropmount(struct mount *mp)
+{
+       atomic_add_int(&mp->mnt_refs, -1);
+}
+
 /*
  * Resolve an unresolved namecache entry, generally by looking it up.
  * The passed ncp must be locked and refd. 
@@ -2560,6 +2595,20 @@ restart:
                        return (ncp->nc_error);
        }
 
+       /*
+        * If the ncp was destroyed it will never resolve again.  This
+        * can basically only happen when someone is chdir'd into an
+        * empty directory which is then rmdir'd.  We want to catch this
+        * here and not dive the VFS because the VFS might actually
+        * have a way to re-resolve the disconnected ncp, which will
+        * result in inconsistencies in the cdir/nch for proc->p_fd.
+        */
+       if (ncp->nc_flag & NCF_DESTROYED) {
+               kprintf("Warning: cache_resolve: ncp '%s' was unlinked\n",
+                       ncp->nc_name);
+               return(EINVAL);
+       }
+
        /*
         * Mount points need special handling because the parent does not
         * belong to the same filesystem as the ncp.