Linux 3.17: Deal with d_splice_alias errors
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index 6f4000b..0cdd9e0 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,
@@ -827,7 +845,11 @@ struct file_operations afs_file_fops = {
   .sendfile =   generic_file_sendfile,
 #endif
 #if defined(STRUCT_FILE_OPERATIONS_HAS_SPLICE)
+# if defined(HAVE_LINUX_ITER_FILE_SPLICE_WRITE)
+  .splice_write = iter_file_splice_write,
+# else
   .splice_write = generic_file_splice_write,
+# endif
   .splice_read = generic_file_splice_read,
 #endif
   .release =   afs_linux_release,
@@ -932,10 +954,11 @@ 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 */
-    afs_lookup(pvc, (char *)dp->d_name.name, &avc, credp);
-    if (!avc || vcp != avc) {  /* bad, very bad.. */
+    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,
@@ -1270,11 +1293,16 @@ afs_linux_dentry_revalidate(struct dentry *dp, int flags)
            int code;
 
            code = afs_lookup(pvcp, (char *)dp->d_name.name, &tvc, credp);
-           if (!tvc || tvc != vcp) {
+           if (code || tvc != vcp) {
                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;
            }
 
@@ -1513,7 +1541,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);
 
@@ -1565,9 +1593,18 @@ 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))
+           return newdp;
+       else {
+           d_add(dp, ip);
+           return NULL;
+       }
+    } else
        return ERR_PTR(afs_convert_code(code));
 }