Merge tag 'nfs-for-4.13-2' of git://git.linux-nfs.org/projects/anna/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 Jul 2017 23:26:01 +0000 (16:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 Jul 2017 23:26:01 +0000 (16:26 -0700)
Pull NFS client bugfixes from Anna Schumaker:
 "Stable bugfix:
   - Fix error reporting regression

  Bugfixes:
   - Fix setting filelayout ds address race
   - Fix subtle access bug when using ACLs
   - Fix setting mnt3_counts array size
   - Fix a couple of pNFS commit races"

* tag 'nfs-for-4.13-2' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  NFS/filelayout: Fix racy setting of fl->dsaddr in filelayout_check_deviceid()
  NFS: Be more careful about mapping file permissions
  NFS: Store the raw NFS access mask in the inode's access cache
  NFSv3: Convert nfs3_proc_access() to use nfs_access_set_mask()
  NFS: Refactor NFS access to kernel access mask calculation
  net/sunrpc/xprt_sock: fix regression in connection error reporting.
  nfs: count correct array for mnt3_counts array size
  Revert commit 722f0b891198 ("pNFS: Don't send COMMITs to the DSes if...")
  pNFS/flexfiles: Handle expired layout segments in ff_layout_initiate_commit()
  NFS: Fix another COMMIT race in pNFS
  NFS: Fix a COMMIT race in pNFS
  mount: copy the port field into the cloned nfs_server structure.
  NFS: Don't run wake_up_bit() when nobody is waiting...
  nfs: add export operations

1  2 
fs/nfs/dir.c

diff --combined fs/nfs/dir.c
index 5ac484fe0dee04f9c09630468f64aa779190534d,37a6180ee2e8dcc80911bf1302324644fd20fb01..3522b1249019ce261db090af01baf9525302f10d
@@@ -1952,6 -1952,29 +1952,6 @@@ nfs_link(struct dentry *old_dentry, str
  }
  EXPORT_SYMBOL_GPL(nfs_link);
  
 -static void
 -nfs_complete_rename(struct rpc_task *task, struct nfs_renamedata *data)
 -{
 -      struct dentry *old_dentry = data->old_dentry;
 -      struct dentry *new_dentry = data->new_dentry;
 -      struct inode *old_inode = d_inode(old_dentry);
 -      struct inode *new_inode = d_inode(new_dentry);
 -
 -      nfs_mark_for_revalidate(old_inode);
 -
 -      switch (task->tk_status) {
 -      case 0:
 -              if (new_inode != NULL)
 -                      nfs_drop_nlink(new_inode);
 -              d_move(old_dentry, new_dentry);
 -              nfs_set_verifier(new_dentry,
 -                                      nfs_save_change_attribute(data->new_dir));
 -              break;
 -      case -ENOENT:
 -              nfs_dentry_handle_enoent(old_dentry);
 -      }
 -}
 -
  /*
   * RENAME
   * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
@@@ -1982,7 -2005,7 +1982,7 @@@ int nfs_rename(struct inode *old_dir, s
  {
        struct inode *old_inode = d_inode(old_dentry);
        struct inode *new_inode = d_inode(new_dentry);
 -      struct dentry *dentry = NULL;
 +      struct dentry *dentry = NULL, *rehash = NULL;
        struct rpc_task *task;
        int error = -EBUSY;
  
                 * To prevent any new references to the target during the
                 * rename, we unhash the dentry in advance.
                 */
 -              if (!d_unhashed(new_dentry))
 +              if (!d_unhashed(new_dentry)) {
                        d_drop(new_dentry);
 +                      rehash = new_dentry;
 +              }
  
                if (d_count(new_dentry) > 2) {
                        int err;
                                goto out;
  
                        new_dentry = dentry;
 +                      rehash = NULL;
                        new_inode = NULL;
                }
        }
        if (new_inode != NULL)
                NFS_PROTO(new_inode)->return_delegation(new_inode);
  
 -      task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
 -                                      nfs_complete_rename);
 +      task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL);
        if (IS_ERR(task)) {
                error = PTR_ERR(task);
                goto out;
        } else
                error = task->tk_status;
        rpc_put_task(task);
 +      nfs_mark_for_revalidate(old_inode);
  out:
 +      if (rehash)
 +              d_rehash(rehash);
        trace_nfs_rename_exit(old_dir, old_dentry,
                        new_dir, new_dentry, error);
 +      if (!error) {
 +              if (new_inode != NULL)
 +                      nfs_drop_nlink(new_inode);
 +              /*
 +               * The d_move() should be here instead of in an async RPC completion
 +               * handler because we need the proper locks to move the dentry.  If
 +               * we're interrupted by a signal, the async RPC completion handler
 +               * should mark the directories for revalidation.
 +               */
 +              d_move(old_dentry, new_dentry);
 +              nfs_set_verifier(new_dentry,
 +                                      nfs_save_change_attribute(new_dir));
 +      } else if (error == -ENOENT)
 +              nfs_dentry_handle_enoent(old_dentry);
 +
        /* new dentry created? */
        if (dentry)
                dput(dentry);
@@@ -2372,16 -2375,40 +2372,40 @@@ void nfs_access_add_cache(struct inode 
  }
  EXPORT_SYMBOL_GPL(nfs_access_add_cache);
  
+ #define NFS_MAY_READ (NFS4_ACCESS_READ)
+ #define NFS_MAY_WRITE (NFS4_ACCESS_MODIFY | \
+               NFS4_ACCESS_EXTEND | \
+               NFS4_ACCESS_DELETE)
+ #define NFS_FILE_MAY_WRITE (NFS4_ACCESS_MODIFY | \
+               NFS4_ACCESS_EXTEND)
+ #define NFS_DIR_MAY_WRITE NFS_MAY_WRITE
+ #define NFS_MAY_LOOKUP (NFS4_ACCESS_LOOKUP)
+ #define NFS_MAY_EXECUTE (NFS4_ACCESS_EXECUTE)
+ static int
+ nfs_access_calc_mask(u32 access_result, umode_t umode)
+ {
+       int mask = 0;
+       if (access_result & NFS_MAY_READ)
+               mask |= MAY_READ;
+       if (S_ISDIR(umode)) {
+               if ((access_result & NFS_DIR_MAY_WRITE) == NFS_DIR_MAY_WRITE)
+                       mask |= MAY_WRITE;
+               if ((access_result & NFS_MAY_LOOKUP) == NFS_MAY_LOOKUP)
+                       mask |= MAY_EXEC;
+       } else if (S_ISREG(umode)) {
+               if ((access_result & NFS_FILE_MAY_WRITE) == NFS_FILE_MAY_WRITE)
+                       mask |= MAY_WRITE;
+               if ((access_result & NFS_MAY_EXECUTE) == NFS_MAY_EXECUTE)
+                       mask |= MAY_EXEC;
+       } else if (access_result & NFS_MAY_WRITE)
+                       mask |= MAY_WRITE;
+       return mask;
+ }
  void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
  {
-       entry->mask = 0;
-       if (access_result & NFS4_ACCESS_READ)
-               entry->mask |= MAY_READ;
-       if (access_result &
-           (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
-               entry->mask |= MAY_WRITE;
-       if (access_result & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
-               entry->mask |= MAY_EXEC;
+       entry->mask = access_result;
  }
  EXPORT_SYMBOL_GPL(nfs_access_set_mask);
  
@@@ -2389,6 -2416,7 +2413,7 @@@ static int nfs_do_access(struct inode *
  {
        struct nfs_access_entry cache;
        bool may_block = (mask & MAY_NOT_BLOCK) == 0;
+       int cache_mask;
        int status;
  
        trace_nfs_access_enter(inode);
                goto out;
  
        /* Be clever: ask server to check for all possible rights */
-       cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
+       cache.mask = NFS_MAY_LOOKUP | NFS_MAY_EXECUTE
+                    | NFS_MAY_WRITE | NFS_MAY_READ;
        cache.cred = cred;
        cache.jiffies = jiffies;
        status = NFS_PROTO(inode)->access(inode, &cache);
        }
        nfs_access_add_cache(inode, &cache);
  out_cached:
-       if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
+       cache_mask = nfs_access_calc_mask(cache.mask, inode->i_mode);
+       if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
                status = -EACCES;
  out:
        trace_nfs_access_exit(inode, status);