msdosfs: fix deleted-but-open files handling.
authorNicolas Thery <nthery@gmail.com>
Sun, 1 Mar 2009 19:03:45 +0000 (20:03 +0100)
committerNicolas Thery <nthery@gmail.com>
Sun, 1 Mar 2009 19:03:45 +0000 (20:03 +0100)
When deleting a file, msdosfs keeps its denode in the denode cache until it is
reclaimed.  This causes a collision in the cache when recycling the directory
entry of a deleted but still open file for a new or renamed file.  This
collision was incorrecly handled resulting in a kernel panic (rename case) or
syscall error and corrupted in-core state (new file case).

Fix by allowing denodes pointing to the same directory entry to coexist in the
cache as long as a single one of them represents an existing file.

Reported-by: corecore@
Dragonfly-bug: <http://bugs.dragonflybsd.org/issue1286>
(cherry picked from commit 629f33a733bba046a296b5f1dfa4ef45bc388cb4)

sys/vfs/msdosfs/msdosfs_denode.c

index 70747e5..9b66d46 100644 (file)
@@ -72,6 +72,20 @@ static void msdosfs_hashrem (struct denode *dep);
 
 static MALLOC_DEFINE(M_MSDOSFSNODE, "MSDOSFS node", "MSDOSFS vnode private part");
 
+/*
+ * Hash table caching denode instances.
+ *
+ * denodes are keyed by the disk location (cluster num, entry offset) of the
+ * directory entry of the file they represent.
+ *
+ * denodes representing deleted but still opened files are left in this cache
+ * until reclaimed.  Deleted directory entries can be reused when files are
+ * renamed or new files created.  As a consequence, several denodes associated
+ * with the same entry may coexist in this cache as long as a single one of
+ * them map to an existing file (de_refcnt > 0).
+ *
+ * R/w access to this cache is protected by dehash_token.
+ */
 static struct denode **dehashtbl;
 static u_long dehash;                  /* size of hash table - 1 */
 #define        DEHASH(dev, dcl, doff)  (dehashtbl[(minor(dev) + (dcl) + (doff) /       \
@@ -164,6 +178,10 @@ loop:
        return (NULL);
 }
 
+/*
+ * Try to insert specified denode into the hash table.  Return 0 on success
+ * and EBUSY if there is already a denode with the same key.
+ */
 static
 int
 msdosfs_hashins(struct denode *dep)
@@ -176,12 +194,10 @@ msdosfs_hashins(struct denode *dep)
        while ((deq = *depp) != NULL) {
                if (deq->de_dev == dep->de_dev &&
                    deq->de_dirclust == dep->de_dirclust &&
-                   deq->de_diroffset == dep->de_diroffset) {
+                   deq->de_diroffset == dep->de_diroffset &&
+                   deq->de_refcnt > 0) {
                        lwkt_reltoken(&ilock);
-                       if (dep->de_refcnt)
-                               return(EBUSY);
-                       else
-                               return(EINVAL);
+                       return(EBUSY);
                }
                depp = &deq->de_next;
        }