linux-remove-negative-dentry-20041209
authorChas Williams <chas@cmf.nrl.navy.mil>
Thu, 9 Dec 2004 16:14:21 +0000 (16:14 +0000)
committerDerrick Brashear <shadow@dementia.org>
Thu, 9 Dec 2004 16:14:21 +0000 (16:14 +0000)
FIXES 16735

during unlink, afs sometimes renames the file instead of deleting it.
this isnt reflected properly in the dcache in linux. the following
patch attempts to address this issue. newname is renamed to afs_newname
and exported.

afs_linux_unlink() checks to see if the file is open. if so, it creates
a negative dcache entry using the name suggested by afs_newname(). then
dmove() moves (exchanges) dp and __dp. __dp is now a negative dentry
for the old name and is put/dropped.

src/afs/LINUX/osi_vnodeops.c
src/afs/VNOPS/afs_vnop_remove.c

index a414727..d6c86da 100644 (file)
@@ -1163,18 +1163,63 @@ afs_linux_link(struct dentry *olddp, struct inode *dip, struct dentry *newdp)
 int
 afs_linux_unlink(struct inode *dip, struct dentry *dp)
 {
-    int code;
+    int code = EBUSY;
     cred_t *credp = crref();
     const char *name = dp->d_name.name;
+    struct vcache *tvc = ITOAFS(dp->d_inode);
 
 #if defined(AFS_LINUX26_ENV)
     lock_kernel();
 #endif
+    if (((VREFCOUNT(tvc) > 0) && tvc->opens > 0)
+                               && !(tvc->states & CUnlinked)) {
+       struct dentry *__dp;
+       char *__name;
+       extern char *afs_newname();
+
+       __dp = NULL;
+       __name = NULL;
+       do {
+           dput(__dp);
+
+           AFS_GLOCK();
+           if (__name)
+               osi_FreeSmallSpace(__name);
+           __name = afs_newname();
+           AFS_GUNLOCK();
+
+           __dp = lookup_one_len(__name, dp->d_parent, strlen(__name));
+               
+           if (IS_ERR(__dp))
+               goto out;
+       } while (__dp->d_inode != NULL);
+
+       AFS_GLOCK();
+       code = afs_rename(ITOAFS(dip), dp->d_name.name, ITOAFS(dip), __dp->d_name.name, credp);
+       if (!code) {
+            tvc->mvid = __name;
+            crhold(credp);
+            if (tvc->uncred) {
+                crfree(tvc->uncred);
+            }
+            tvc->uncred = credp;
+           tvc->states |= CUnlinked;
+       }
+       AFS_GUNLOCK();
+
+       if (!code)
+           d_move(dp, __dp);
+       dput(__dp);
+
+       goto out;
+    }
+
     AFS_GLOCK();
     code = afs_remove(ITOAFS(dip), name, credp);
     AFS_GUNLOCK();
     if (!code)
        d_drop(dp);
+out:
 #if defined(AFS_LINUX26_ENV)
     unlock_kernel();
 #endif
index 202fff9..fc5431f 100644 (file)
@@ -14,9 +14,7 @@
  * afs_IsWired (DUX)
  * afsremove
  * afs_remove
- *
- * Local:
- * newname
+ * afs_newname
  *
  */
 #include <afsconfig.h>
@@ -193,8 +191,8 @@ afsremove(register struct vcache *adp, register struct dcache *tdc,
     return (0);
 }
 
-static char *
-newname(void)
+char *
+afs_newname(void)
 {
     char *name, *sp, *p = ".__afs";
     afs_int32 rd = afs_random() & 0xffff;
@@ -412,7 +410,7 @@ afs_remove(OSI_VC_ARG(adp), aname, acred)
 #endif
 #endif
     {
-       char *unlname = newname();
+       char *unlname = afs_newname();
 
        ReleaseWriteLock(&adp->lock);
        if (tdc)