NFS: More excessive attribute revalidation in nfs_execute_ok()
[sfrench/cifs-2.6.git] / fs / nfs / dir.c
index 73f8b43d988cfa2f7b3366b1147dc296b55802ef..f0e39583af7e360759680d9dacf2634d0bc237c5 100644 (file)
@@ -904,23 +904,29 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
        dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n",
                        filp, offset, whence);
 
-       inode_lock(inode);
        switch (whence) {
-               case 1:
-                       offset += filp->f_pos;
-               case 0:
-                       if (offset >= 0)
-                               break;
-               default:
-                       offset = -EINVAL;
-                       goto out;
+       default:
+               return -EINVAL;
+       case SEEK_SET:
+               if (offset < 0)
+                       return -EINVAL;
+               inode_lock(inode);
+               break;
+       case SEEK_CUR:
+               if (offset == 0)
+                       return filp->f_pos;
+               inode_lock(inode);
+               offset += filp->f_pos;
+               if (offset < 0) {
+                       inode_unlock(inode);
+                       return -EINVAL;
+               }
        }
        if (offset != filp->f_pos) {
                filp->f_pos = offset;
                dir_ctx->dir_cookie = 0;
                dir_ctx->duped = 0;
        }
-out:
        inode_unlock(inode);
        return offset;
 }
@@ -1012,13 +1018,25 @@ int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags)
 
        if (IS_AUTOMOUNT(inode))
                return 0;
+
+       if (flags & LOOKUP_OPEN) {
+               switch (inode->i_mode & S_IFMT) {
+               case S_IFREG:
+                       /* A NFSv4 OPEN will revalidate later */
+                       if (server->caps & NFS_CAP_ATOMIC_OPEN)
+                               goto out;
+                       /* Fallthrough */
+               case S_IFDIR:
+                       if (server->flags & NFS_MOUNT_NOCTO)
+                               break;
+                       /* NFS close-to-open cache consistency validation */
+                       goto out_force;
+               }
+       }
+
        /* VFS wants an on-the-wire revalidation */
        if (flags & LOOKUP_REVAL)
                goto out_force;
-       /* This is an open(2) */
-       if ((flags & LOOKUP_OPEN) && !(server->flags & NFS_MOUNT_NOCTO) &&
-           (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
-               goto out_force;
 out:
        return (inode->i_nlink == 0) ? -ENOENT : 0;
 out_force:
@@ -1039,13 +1057,15 @@ out_force:
  *
  * If LOOKUP_RCU prevents us from performing a full check, return 1
  * suggesting a reval is needed.
+ *
+ * Note that when creating a new file, or looking up a rename target,
+ * then it shouldn't be necessary to revalidate a negative dentry.
  */
 static inline
 int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
                       unsigned int flags)
 {
-       /* Don't revalidate a negative dentry if we're creating a new file */
-       if (flags & LOOKUP_CREATE)
+       if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
                return 0;
        if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
                return 1;
@@ -1106,7 +1126,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_set_verifier;
 
        /* Force a full look up iff the parent directory has changed */
-       if (!nfs_is_exclusive_create(dir, flags) &&
+       if (!(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) &&
            nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
                error = nfs_lookup_verify_inode(inode, flags);
                if (error) {
@@ -1270,11 +1290,13 @@ static void nfs_drop_nlink(struct inode *inode)
 {
        spin_lock(&inode->i_lock);
        /* drop the inode if we're reasonably sure this is the last link */
-       if (inode->i_nlink == 1)
-               clear_nlink(inode);
+       if (inode->i_nlink > 0)
+               drop_nlink(inode);
+       NFS_I(inode)->attr_gencount = nfs_inc_attr_generation_counter();
        NFS_I(inode)->cache_validity |= NFS_INO_INVALID_CHANGE
                | NFS_INO_INVALID_CTIME
-               | NFS_INO_INVALID_OTHER;
+               | NFS_INO_INVALID_OTHER
+               | NFS_INO_REVAL_FORCED;
        spin_unlock(&inode->i_lock);
 }
 
@@ -1335,7 +1357,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
         * If we're doing an exclusive create, optimize away the lookup
         * but don't hash the dentry.
         */
-       if (nfs_is_exclusive_create(dir, flags))
+       if (nfs_is_exclusive_create(dir, flags) || flags & LOOKUP_RENAME_TARGET)
                return NULL;
 
        res = ERR_PTR(-ENOMEM);
@@ -1640,7 +1662,8 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                struct nfs_server *server = NFS_SB(dentry->d_sb);
-               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr, NULL);
+               error = server->nfs_client->rpc_ops->getattr(server, fhandle,
+                               fattr, NULL, NULL);
                if (error < 0)
                        goto out_error;
        }
@@ -2036,7 +2059,15 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        } else
                error = task->tk_status;
        rpc_put_task(task);
-       nfs_mark_for_revalidate(old_inode);
+       /* Ensure the inode attributes are revalidated */
+       if (error == 0) {
+               spin_lock(&old_inode->i_lock);
+               NFS_I(old_inode)->attr_gencount = nfs_inc_attr_generation_counter();
+               NFS_I(old_inode)->cache_validity |= NFS_INO_INVALID_CHANGE
+                       | NFS_INO_INVALID_CTIME
+                       | NFS_INO_REVAL_FORCED;
+               spin_unlock(&old_inode->i_lock);
+       }
 out:
        if (rehash)
                d_rehash(rehash);
@@ -2469,7 +2500,9 @@ static int nfs_execute_ok(struct inode *inode, int mask)
        struct nfs_server *server = NFS_SERVER(inode);
        int ret = 0;
 
-       if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS)) {
+       if (S_ISDIR(inode->i_mode))
+               return 0;
+       if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_OTHER)) {
                if (mask & MAY_NOT_BLOCK)
                        return -ECHILD;
                ret = __nfs_revalidate_inode(server, inode);