afs: refactor afs_linux_dentry_revalidate
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index 18d013a..de2af82 100644 (file)
@@ -627,16 +627,6 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
 #endif /* F_GETLK64 && F_GETLK != F_GETLK64 */
 
     AFS_GLOCK();
-    if ((vcp->f.states & CRO)) {
-       if (flp->fl_type == F_WRLCK) {
-           code = EBADF;
-       } else {
-           code = 0;
-       }
-       AFS_GUNLOCK();
-       crfree(credp);
-       return code;
-    }
     code = afs_convert_code(afs_lockctl(vcp, &flock, cmd, credp));
     AFS_GUNLOCK();
 
@@ -942,44 +932,6 @@ canonical_dentry(struct inode *ip)
  * AFS Linux dentry operations
  **********************************************************************/
 
-/* fix_bad_parent() : called if this dentry's vcache is a root vcache
- * that has its mvid (parent dir's fid) pointer set to the wrong directory
- * due to being mounted in multiple points at once. fix_bad_parent()
- * calls afs_lookup() to correct the vcache's mvid, as well as the volume's
- * dotdotfid and mtpoint fid members.
- * Parameters:
- *   dp - dentry to be checked.
- *   credp - credentials
- *   vcp, pvc - item's and parent's vcache pointer
- * Return Values:
- *   None.
- * Sideeffects:
- *   This dentry's vcache's mvid will be set to the correct parent directory's
- *   fid.
- *   This root vnode's volume will have its dotdotfid and mtpoint fids set
- *   to the correct parent and mountpoint fids.
- */
-
-static inline void
-fix_bad_parent(struct dentry *dp, cred_t *credp, struct vcache *vcp, struct vcache *pvc) 
-{
-    struct vcache *avc = NULL;
-    int code;
-
-    /* force a lookup, so vcp->mvid is fixed up */
-    code = afs_lookup(pvc, (char *)dp->d_name.name, &avc, credp);
-    if (code || vcp != avc) {  /* bad, very bad.. */
-       afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
-                  "check_bad_parent: bad pointer returned from afs_lookup origvc newvc dentry",
-                  ICL_TYPE_POINTER, vcp, ICL_TYPE_POINTER, avc,
-                  ICL_TYPE_POINTER, dp);
-    }
-    if (avc)
-       AFS_RELE(AFSTOV(avc));
-
-    return;
-}
-
 /* afs_linux_revalidate
  * Ensure vcache is stat'd before use. Return 0 if entry is valid.
  */
@@ -1001,25 +953,6 @@ afs_linux_revalidate(struct dentry *dp)
        goto out;
     }
 
-#ifdef notyet
-    /* Make this a fast path (no crref), since it's called so often. */
-    if (vcp->states & CStatd) {
-       struct vcache *pvc = VTOAFS(dp->d_parent->d_inode);
-
-       if (*dp->d_name.name != '/' && vcp->mvstat == 2) {      /* root vnode */
-           if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) { /* bad parent */
-               credp = crref();
-               AFS_GLOCK();
-               fix_bad_parent(dp);     /* check and correct mvid */
-               AFS_GUNLOCK();
-               crfree(credp);
-           }
-       }
-       afs_DestroyAttr(vattr);
-       return 0;
-    }
-#endif
-
     /* This avoids the crref when we don't have to do it. Watch for
      * changes in afs_getattr that don't get replicated here!
      */
@@ -1238,37 +1171,34 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
            locked = 1;
        }
 
-       if (locked && vcp->mvstat == 1) {         /* mount point */
-           if (vcp->mvid && (vcp->f.states & CMValid)) {
-               int tryEvalOnly = 0;
-               int code = 0;
-               struct vrequest *treq = NULL;
-
-               code = afs_CreateReq(&treq, credp);
-               if (code) {
-                   dput(parent);
-                   goto bad_dentry;
-               }
-               if ((strcmp(dp->d_name.name, ".directory") == 0)) {
-                   tryEvalOnly = 1;
-               }
-               if (tryEvalOnly)
-                   code = afs_TryEvalFakeStat(&vcp, &fakestate, treq);
-               else
-                   code = afs_EvalFakeStat(&vcp, &fakestate, treq);
-               afs_DestroyReq(treq);
-               if ((tryEvalOnly && vcp->mvstat == 1) || code) {
-                   /* a mount point, not yet replaced by its directory */
-                   dput(parent);
-                   goto bad_dentry;
-               }
-           }
-       } else
-           if (locked && *dp->d_name.name != '/' && vcp->mvstat == 2) {        /* root vnode */
-               if (vcp->mvid->Fid.Volume != pvcp->f.fid.Fid.Volume) {  /* bad parent */
-                   fix_bad_parent(dp, credp, vcp, pvcp);       /* check and correct mvid */
+       if (locked) {
+           if (vcp->mvstat == 1) {         /* mount point */
+               if (vcp->mvid && (vcp->f.states & CMValid)) {
+                   int tryEvalOnly = 0;
+                   int code = 0;
+                   struct vrequest *treq = NULL;
+
+                   code = afs_CreateReq(&treq, credp);
+                   if (code) {
+                       dput(parent);
+                       goto bad_dentry;
+                   }
+                   if ((strcmp(dp->d_name.name, ".directory") == 0)) {
+                       tryEvalOnly = 1;
+                   }
+                   if (tryEvalOnly)
+                       code = afs_TryEvalFakeStat(&vcp, &fakestate, treq);
+                   else
+                       code = afs_EvalFakeStat(&vcp, &fakestate, treq);
+                   afs_DestroyReq(treq);
+                   if ((tryEvalOnly && vcp->mvstat == 1) || code) {
+                       /* a mount point, not yet replaced by its directory */
+                       dput(parent);
+                       goto bad_dentry;
+                   }
                }
            }
+       }
 
 #ifdef notdef
        /* If the last looker changes, we should make sure the current
@@ -1301,9 +1231,36 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
        if (locked && (parent_dv > dp->d_time || !(vcp->f.states & CStatd))) {
            struct vattr *vattr = NULL;
            int code;
+           int lookup_good;
 
            code = afs_lookup(pvcp, (char *)dp->d_name.name, &tvc, credp);
-           if (code || tvc != vcp) {
+
+           if (code) {
+               /* We couldn't perform the lookup, so we're not okay. */
+               lookup_good = 0;
+
+           } else if (tvc == vcp) {
+               /* We got back the same vcache, so we're good. */
+               lookup_good = 1;
+
+           } else if (tvc == VTOAFS(dp->d_inode)) {
+               /* We got back the same vcache, so we're good. This is
+                * different from the above case, because sometimes 'vcp' is
+                * not the same as the vcache for dp->d_inode, if 'vcp' was a
+                * mtpt and we evaluated it to a root dir. In rare cases,
+                * afs_lookup might not evalute the mtpt when we do, or vice
+                * versa, so the previous case will not succeed. But this is
+                * still 'correct', so make sure not to mark the dentry as
+                * invalid; it still points to the same thing! */
+               lookup_good = 1;
+
+           } else {
+               /* We got back a different file, so we're definitely not
+                * okay. */
+               lookup_good = 0;
+           }
+
+           if (!lookup_good) {
                dput(parent);
                /* Force unhash; the name doesn't point to this file
                 * anymore. */
@@ -1595,6 +1552,17 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
        ip->i_flags |= S_AUTOMOUNT;
 #endif
     }
+    /*
+     * Take an extra reference so the inode doesn't go away if
+     * d_splice_alias drops our reference on error.
+     */
+    if (ip)
+#ifdef HAVE_LINUX_IHOLD
+       ihold(ip);
+#else
+       igrab(ip);
+#endif
+
     newdp = d_splice_alias(ip, dp);
 
  done:
@@ -1603,10 +1571,31 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     /* It's ok for the file to not be found. That's noted by the caller by
      * seeing that the dp->d_inode field is NULL.
      */
-    if (!code || code == ENOENT)
-       return newdp;
-    else 
+    if (!code || code == ENOENT) {
+       /*
+        * d_splice_alias can return an error (EIO) if there is an existing
+        * connected directory alias for this dentry.
+        */
+       if (!IS_ERR(newdp)) {
+           iput(ip);
+           return newdp;
+       } else {
+           d_add(dp, ip);
+           /*
+            * Depending on the kernel version, d_splice_alias may or may
+            * not drop the inode reference on error.  If it didn't, do it
+            * here.
+            */
+#if defined(D_SPLICE_ALIAS_LEAK_ON_ERROR)
+           iput(ip);
+#endif
+           return NULL;
+       }
+    } else {
+       if (ip)
+           iput(ip);
        return ERR_PTR(afs_convert_code(code));
+    }
 }
 
 static int
@@ -1868,6 +1857,9 @@ afs_linux_ireadlink(struct inode *ip, char *target, int maxlen, uio_seg_t seg)
     struct uio tuio;
     struct iovec iov;
 
+    memset(&tuio, 0, sizeof(tuio));
+    memset(&iov, 0, sizeof(iov));
+
     setup_uio(&tuio, &iov, target, (afs_offs_t) 0, maxlen, UIO_READ, seg);
     code = afs_readlink(VTOAFS(ip), &tuio, credp);
     crfree(credp);
@@ -2621,6 +2613,9 @@ afs_linux_page_writeback(struct inode *ip, struct page *pp,
     struct iovec iovec;
     int f_flags = 0;
 
+    memset(&tuio, 0, sizeof(tuio));
+    memset(&iovec, 0, sizeof(iovec));
+
     buffer = kmap(pp) + offset;
     base = page_offset(pp) + offset;