afs: refactor afs_linux_dentry_revalidate
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index 2685915..de2af82 100644 (file)
@@ -101,8 +101,11 @@ afs_linux_VerifyVCache(struct vcache *avc, cred_t **retcred) {
     return afs_convert_code(code);
 }
 
-#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
-# ifdef LINUX_HAS_NONVECTOR_AIO
+#if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER) || defined(HAVE_LINUX_GENERIC_FILE_AIO_READ)
+# if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER)
+static ssize_t
+afs_linux_read_iter(struct kiocb *iocb, struct iov_iter *iter)
+# elif defined(LINUX_HAS_NONVECTOR_AIO)
 static ssize_t
 afs_linux_aio_read(struct kiocb *iocb, char __user *buf, size_t bufsize,
                    loff_t pos)
@@ -115,6 +118,11 @@ afs_linux_aio_read(struct kiocb *iocb, const struct iovec *buf,
     struct file *fp = iocb->ki_filp;
     ssize_t code = 0;
     struct vcache *vcp = VTOAFS(fp->f_dentry->d_inode);
+# if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER)
+    loff_t pos = iocb->ki_pos;
+    unsigned long bufsize = iter->nr_segs;
+# endif
+
 
     AFS_GLOCK();
     afs_Trace4(afs_iclSetp, CM_TRACE_AIOREADOP, ICL_TYPE_POINTER, vcp,
@@ -127,7 +135,11 @@ afs_linux_aio_read(struct kiocb *iocb, const struct iovec *buf,
         * so we optimise by not using it */
        osi_FlushPages(vcp, NULL);      /* ensure stale pages are gone */
        AFS_GUNLOCK();
+# if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER)
+       code = generic_file_read_iter(iocb, iter);
+# else
        code = generic_file_aio_read(iocb, buf, bufsize, pos);
+# endif
        AFS_GLOCK();
     }
 
@@ -172,8 +184,11 @@ afs_linux_read(struct file *fp, char *buf, size_t count, loff_t * offp)
  * also take care of re-positioning the pointer if file is open in append
  * mode. Call fake open/close to ensure we do writes of core dumps.
  */
-#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
-# ifdef LINUX_HAS_NONVECTOR_AIO
+#if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER) || defined(HAVE_LINUX_GENERIC_FILE_AIO_READ)
+# if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER)
+static ssize_t
+afs_linux_write_iter(struct kiocb *iocb, struct iov_iter *iter)
+# elif defined(LINUX_HAS_NONVECTOR_AIO)
 static ssize_t
 afs_linux_aio_write(struct kiocb *iocb, const char __user *buf, size_t bufsize,
                     loff_t pos)
@@ -186,6 +201,10 @@ afs_linux_aio_write(struct kiocb *iocb, const struct iovec *buf,
     ssize_t code = 0;
     struct vcache *vcp = VTOAFS(iocb->ki_filp->f_dentry->d_inode);
     cred_t *credp;
+# if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER)
+    loff_t pos = iocb->ki_pos;
+    unsigned long bufsize = iter->nr_segs;
+# endif
 
     AFS_GLOCK();
 
@@ -201,7 +220,11 @@ afs_linux_aio_write(struct kiocb *iocb, const struct iovec *buf,
     ReleaseWriteLock(&vcp->lock);
     if (code == 0) {
            AFS_GUNLOCK();
+# if defined(STRUCT_FILE_OPERATIONS_HAS_READ_ITER)
+           code = generic_file_write_iter(iocb, iter);
+# else
            code = generic_file_aio_write(iocb, buf, bufsize, pos);
+# endif
            AFS_GLOCK();
     }
 
@@ -604,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();
 
@@ -803,7 +816,12 @@ struct file_operations afs_dir_fops = {
 };
 
 struct file_operations afs_file_fops = {
-#ifdef HAVE_LINUX_GENERIC_FILE_AIO_READ
+#ifdef STRUCT_FILE_OPERATIONS_HAS_READ_ITER
+  .read_iter = afs_linux_read_iter,
+  .write_iter =        afs_linux_write_iter,
+  .read =      new_sync_read,
+  .write =     new_sync_write,
+#elif defined(HAVE_LINUX_GENERIC_FILE_AIO_READ)
   .aio_read =  afs_linux_aio_read,
   .aio_write = afs_linux_aio_write,
   .read =      do_sync_read,
@@ -914,43 +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;
-
-    /* force a lookup, so vcp->mvid is fixed up */
-    afs_lookup(pvc, (char *)dp->d_name.name, &avc, credp);
-    if (!avc || 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.
  */
@@ -972,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!
      */
@@ -1209,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
@@ -1272,13 +1231,45 @@ 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 (!tvc || 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 if name is known not to exist. */
-               if (code == ENOENT)
-                   force_drop = 1;
+               /* Force unhash; the name doesn't point to this file
+                * anymore. */
+               force_drop = 1;
+               if (code && code != ENOENT) {
+                   /* ...except if we couldn't perform the actual lookup,
+                    * we don't know if the name points to this file or not. */
+                   force_drop = 0;
+               }
                goto bad_dentry;
            }
 
@@ -1517,7 +1508,7 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     AFS_GLOCK();
     code = afs_lookup(VTOAFS(dip), (char *)comp, &vcp, credp);
     
-    if (vcp) {
+    if (!code) {
        struct vattr *vattr = NULL;
        struct vcache *parent_vc = VTOAFS(dip);
 
@@ -1561,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:
@@ -1569,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
@@ -1834,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);
@@ -2587,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;