diff mbox series

NFS: fix client permission error after adding user to permissible group

Message ID 20221213100241.254681-1-chengen.du@canonical.com (mailing list archive)
State New, archived
Headers show
Series NFS: fix client permission error after adding user to permissible group | expand

Commit Message

Chengen Du Dec. 13, 2022, 10:02 a.m. UTC
The access cache only expires if either NFS_INO_INVALID_ACCESS flag is on
or timeout (without delegation).
Adding a user to a group in the NFS server will not cause any file
attributes to change.
The client will encounter permission errors until other file attributes
are changed or the memory cache is dropped.

Steps to reproduce the issue:
1.[client side] testuser is not part of testgroup
  testuser@kinetic:~$ ls -ld /mnt/private/
  drwxrwx--- 2 root testgroup 4096 Nov 24 08:23 /mnt/private/
  testuser@kinetic:~$ mktemp -p /mnt/private/
  mktemp: failed to create file via template
  ‘/mnt/private/tmp.XXXXXXXXXX’: Permission denied
2.[server side] add testuser into testgroup, which has access to folder
  root@kinetic:~$ usermod -aG testgroup testuser &&
  echo `date +'%s'` > /proc/net/rpc/auth.unix.gid/flush
3.[client side] create a file again but still fail
  testuser@kinetic:~$ mktemp -p /mnt/private/
  mktemp: failed to create file via template
  ‘/mnt/private/tmp.XXXXXXXXXX’: Permission denied

Signed-off-by: Chengen Du <chengen.du@canonical.com>
---
 fs/nfs/dir.c            |  2 +-
 fs/nfs/inode.c          | 12 ++++++++++++
 fs/nfs/nfs4_fs.h        |  3 +++
 fs/nfs/nfs4proc.c       |  5 +++--
 fs/nfs/nfs4xdr.c        | 25 +++++++++++++++++++++++++
 fs/nfsd/nfs4xdr.c       | 21 +++++++++++++++++++++
 fs/nfsd/nfsd.h          |  3 ++-
 include/linux/nfs4.h    |  1 +
 include/linux/nfs_xdr.h |  2 ++
 9 files changed, 70 insertions(+), 4 deletions(-)

Comments

kernel test robot Dec. 13, 2022, 12:13 p.m. UTC | #1
Hi Chengen,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on trondmy-nfs/linux-next]
[also build test WARNING on linus/master v6.1 next-20221213]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Chengen-Du/NFS-fix-client-permission-error-after-adding-user-to-permissible-group/20221213-180531
base:   git://git.linux-nfs.org/projects/trondmy/linux-nfs.git linux-next
patch link:    https://lore.kernel.org/r/20221213100241.254681-1-chengen.du%40canonical.com
patch subject: [PATCH] NFS: fix client permission error after adding user to permissible group
config: arc-defconfig
compiler: arc-elf-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/7206036f9265a95f957d3f58a973c9ab27979284
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Chengen-Du/NFS-fix-client-permission-error-after-adding-user-to-permissible-group/20221213-180531
        git checkout 7206036f9265a95f957d3f58a973c9ab27979284
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash fs/nfs/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/nfs/dir.c:2937:26: warning: no previous prototype for 'nfs_access_search_rbtree' [-Wmissing-prototypes]
    2937 | struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~
--
   fs/nfs/inode.c: In function 'nfs_update_inode':
   fs/nfs/inode.c:2186:33: error: implicit declaration of function 'nfs_access_search_rbtree' [-Werror=implicit-function-declaration]
    2186 |                         cache = nfs_access_search_rbtree(inode, cred);
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~
>> fs/nfs/inode.c:2186:31: warning: assignment to 'struct nfs_access_entry *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
    2186 |                         cache = nfs_access_search_rbtree(inode, cred);
         |                               ^
   cc1: some warnings being treated as errors


vim +/nfs_access_search_rbtree +2937 fs/nfs/dir.c

  2936	
> 2937	struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
  2938	{
  2939		struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
  2940	
  2941		while (n != NULL) {
  2942			struct nfs_access_entry *entry =
  2943				rb_entry(n, struct nfs_access_entry, rb_node);
  2944			int cmp = access_cmp(cred, entry);
  2945	
  2946			if (cmp < 0)
  2947				n = n->rb_left;
  2948			else if (cmp > 0)
  2949				n = n->rb_right;
  2950			else
  2951				return entry;
  2952		}
  2953		return NULL;
  2954	}
  2955
kernel test robot Dec. 13, 2022, 12:54 p.m. UTC | #2
Hi Chengen,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on trondmy-nfs/linux-next]
[also build test ERROR on linus/master v6.1 next-20221213]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Chengen-Du/NFS-fix-client-permission-error-after-adding-user-to-permissible-group/20221213-180531
base:   git://git.linux-nfs.org/projects/trondmy/linux-nfs.git linux-next
patch link:    https://lore.kernel.org/r/20221213100241.254681-1-chengen.du%40canonical.com
patch subject: [PATCH] NFS: fix client permission error after adding user to permissible group
config: arc-defconfig
compiler: arc-elf-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/7206036f9265a95f957d3f58a973c9ab27979284
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Chengen-Du/NFS-fix-client-permission-error-after-adding-user-to-permissible-group/20221213-180531
        git checkout 7206036f9265a95f957d3f58a973c9ab27979284
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   fs/nfs/inode.c: In function 'nfs_update_inode':
>> fs/nfs/inode.c:2186:33: error: implicit declaration of function 'nfs_access_search_rbtree' [-Werror=implicit-function-declaration]
    2186 |                         cache = nfs_access_search_rbtree(inode, cred);
         |                                 ^~~~~~~~~~~~~~~~~~~~~~~~
   fs/nfs/inode.c:2186:31: warning: assignment to 'struct nfs_access_entry *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
    2186 |                         cache = nfs_access_search_rbtree(inode, cred);
         |                               ^
   cc1: some warnings being treated as errors


vim +/nfs_access_search_rbtree +2186 fs/nfs/inode.c

  1970	
  1971	
  1972	/*
  1973	 * Many nfs protocol calls return the new file attributes after
  1974	 * an operation.  Here we update the inode to reflect the state
  1975	 * of the server's inode.
  1976	 *
  1977	 * This is a bit tricky because we have to make sure all dirty pages
  1978	 * have been sent off to the server before calling invalidate_inode_pages.
  1979	 * To make sure no other process adds more write requests while we try
  1980	 * our best to flush them, we make them sleep during the attribute refresh.
  1981	 *
  1982	 * A very similar scenario holds for the dir cache.
  1983	 */
  1984	static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  1985	{
  1986		struct nfs_server *server = NFS_SERVER(inode);
  1987		struct nfs_inode *nfsi = NFS_I(inode);
  1988		loff_t cur_isize, new_isize;
  1989		u64 fattr_supported = server->fattr_valid;
  1990		unsigned long invalid = 0;
  1991		unsigned long now = jiffies;
  1992		unsigned long save_cache_validity;
  1993		bool have_writers = nfs_file_has_buffered_writers(nfsi);
  1994		bool cache_revalidated = true;
  1995		bool attr_changed = false;
  1996		bool have_delegation;
  1997	
  1998		dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%x)\n",
  1999				__func__, inode->i_sb->s_id, inode->i_ino,
  2000				nfs_display_fhandle_hash(NFS_FH(inode)),
  2001				atomic_read(&inode->i_count), fattr->valid);
  2002	
  2003		if (!(fattr->valid & NFS_ATTR_FATTR_FILEID)) {
  2004			/* Only a mounted-on-fileid? Just exit */
  2005			if (fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
  2006				return 0;
  2007		/* Has the inode gone and changed behind our back? */
  2008		} else if (nfsi->fileid != fattr->fileid) {
  2009			/* Is this perhaps the mounted-on fileid? */
  2010			if ((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) &&
  2011			    nfsi->fileid == fattr->mounted_on_fileid)
  2012				return 0;
  2013			printk(KERN_ERR "NFS: server %s error: fileid changed\n"
  2014				"fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
  2015				NFS_SERVER(inode)->nfs_client->cl_hostname,
  2016				inode->i_sb->s_id, (long long)nfsi->fileid,
  2017				(long long)fattr->fileid);
  2018			goto out_err;
  2019		}
  2020	
  2021		/*
  2022		 * Make sure the inode's type hasn't changed.
  2023		 */
  2024		if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode)) {
  2025			/*
  2026			* Big trouble! The inode has become a different object.
  2027			*/
  2028			printk(KERN_DEBUG "NFS: %s: inode %lu mode changed, %07o to %07o\n",
  2029					__func__, inode->i_ino, inode->i_mode, fattr->mode);
  2030			goto out_err;
  2031		}
  2032	
  2033		/* Update the fsid? */
  2034		if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
  2035				!nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
  2036				!IS_AUTOMOUNT(inode))
  2037			server->fsid = fattr->fsid;
  2038	
  2039		/* Save the delegation state before clearing cache_validity */
  2040		have_delegation = nfs_have_delegated_attributes(inode);
  2041	
  2042		/*
  2043		 * Update the read time so we don't revalidate too often.
  2044		 */
  2045		nfsi->read_cache_jiffies = fattr->time_start;
  2046	
  2047		save_cache_validity = nfsi->cache_validity;
  2048		nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
  2049				| NFS_INO_INVALID_ATIME
  2050				| NFS_INO_REVAL_FORCED
  2051				| NFS_INO_INVALID_BLOCKS);
  2052	
  2053		/* Do atomic weak cache consistency updates */
  2054		nfs_wcc_update_inode(inode, fattr);
  2055	
  2056		if (pnfs_layoutcommit_outstanding(inode)) {
  2057			nfsi->cache_validity |=
  2058				save_cache_validity &
  2059				(NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
  2060				 NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
  2061				 NFS_INO_INVALID_BLOCKS);
  2062			cache_revalidated = false;
  2063		}
  2064	
  2065		/* More cache consistency checks */
  2066		if (fattr->valid & NFS_ATTR_FATTR_CHANGE) {
  2067			if (!inode_eq_iversion_raw(inode, fattr->change_attr)) {
  2068				/* Could it be a race with writeback? */
  2069				if (!(have_writers || have_delegation)) {
  2070					invalid |= NFS_INO_INVALID_DATA
  2071						| NFS_INO_INVALID_ACCESS
  2072						| NFS_INO_INVALID_ACL
  2073						| NFS_INO_INVALID_XATTR;
  2074					/* Force revalidate of all attributes */
  2075					save_cache_validity |= NFS_INO_INVALID_CTIME
  2076						| NFS_INO_INVALID_MTIME
  2077						| NFS_INO_INVALID_SIZE
  2078						| NFS_INO_INVALID_BLOCKS
  2079						| NFS_INO_INVALID_NLINK
  2080						| NFS_INO_INVALID_MODE
  2081						| NFS_INO_INVALID_OTHER;
  2082					if (S_ISDIR(inode->i_mode))
  2083						nfs_force_lookup_revalidate(inode);
  2084					attr_changed = true;
  2085					dprintk("NFS: change_attr change on server for file %s/%ld\n",
  2086							inode->i_sb->s_id,
  2087							inode->i_ino);
  2088				} else if (!have_delegation)
  2089					nfsi->cache_validity |= NFS_INO_DATA_INVAL_DEFER;
  2090				inode_set_iversion_raw(inode, fattr->change_attr);
  2091			}
  2092		} else {
  2093			nfsi->cache_validity |=
  2094				save_cache_validity & NFS_INO_INVALID_CHANGE;
  2095			if (!have_delegation ||
  2096			    (nfsi->cache_validity & NFS_INO_INVALID_CHANGE) != 0)
  2097				cache_revalidated = false;
  2098		}
  2099	
  2100		if (fattr->valid & NFS_ATTR_FATTR_MTIME)
  2101			inode->i_mtime = fattr->mtime;
  2102		else if (fattr_supported & NFS_ATTR_FATTR_MTIME)
  2103			nfsi->cache_validity |=
  2104				save_cache_validity & NFS_INO_INVALID_MTIME;
  2105	
  2106		if (fattr->valid & NFS_ATTR_FATTR_CTIME)
  2107			inode->i_ctime = fattr->ctime;
  2108		else if (fattr_supported & NFS_ATTR_FATTR_CTIME)
  2109			nfsi->cache_validity |=
  2110				save_cache_validity & NFS_INO_INVALID_CTIME;
  2111	
  2112		/* Check if our cached file size is stale */
  2113		if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
  2114			new_isize = nfs_size_to_loff_t(fattr->size);
  2115			cur_isize = i_size_read(inode);
  2116			if (new_isize != cur_isize && !have_delegation) {
  2117				/* Do we perhaps have any outstanding writes, or has
  2118				 * the file grown beyond our last write? */
  2119				if (!nfs_have_writebacks(inode) || new_isize > cur_isize) {
  2120					trace_nfs_size_update(inode, new_isize);
  2121					i_size_write(inode, new_isize);
  2122					if (!have_writers)
  2123						invalid |= NFS_INO_INVALID_DATA;
  2124				}
  2125			}
  2126			if (new_isize == 0 &&
  2127			    !(fattr->valid & (NFS_ATTR_FATTR_SPACE_USED |
  2128					      NFS_ATTR_FATTR_BLOCKS_USED))) {
  2129				fattr->du.nfs3.used = 0;
  2130				fattr->valid |= NFS_ATTR_FATTR_SPACE_USED;
  2131			}
  2132		} else
  2133			nfsi->cache_validity |=
  2134				save_cache_validity & NFS_INO_INVALID_SIZE;
  2135	
  2136		if (fattr->valid & NFS_ATTR_FATTR_ATIME)
  2137			inode->i_atime = fattr->atime;
  2138		else if (fattr_supported & NFS_ATTR_FATTR_ATIME)
  2139			nfsi->cache_validity |=
  2140				save_cache_validity & NFS_INO_INVALID_ATIME;
  2141	
  2142		if (fattr->valid & NFS_ATTR_FATTR_MODE) {
  2143			if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
  2144				umode_t newmode = inode->i_mode & S_IFMT;
  2145				newmode |= fattr->mode & S_IALLUGO;
  2146				inode->i_mode = newmode;
  2147				invalid |= NFS_INO_INVALID_ACCESS
  2148					| NFS_INO_INVALID_ACL;
  2149			}
  2150		} else if (fattr_supported & NFS_ATTR_FATTR_MODE)
  2151			nfsi->cache_validity |=
  2152				save_cache_validity & NFS_INO_INVALID_MODE;
  2153	
  2154		if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
  2155			if (!uid_eq(inode->i_uid, fattr->uid)) {
  2156				invalid |= NFS_INO_INVALID_ACCESS
  2157					| NFS_INO_INVALID_ACL;
  2158				inode->i_uid = fattr->uid;
  2159			}
  2160		} else if (fattr_supported & NFS_ATTR_FATTR_OWNER)
  2161			nfsi->cache_validity |=
  2162				save_cache_validity & NFS_INO_INVALID_OTHER;
  2163	
  2164		if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
  2165			if (!gid_eq(inode->i_gid, fattr->gid)) {
  2166				invalid |= NFS_INO_INVALID_ACCESS
  2167					| NFS_INO_INVALID_ACL;
  2168				inode->i_gid = fattr->gid;
  2169			}
  2170		} else if (fattr_supported & NFS_ATTR_FATTR_GROUP)
  2171			nfsi->cache_validity |=
  2172				save_cache_validity & NFS_INO_INVALID_OTHER;
  2173	
  2174		if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
  2175			if (inode->i_nlink != fattr->nlink)
  2176				set_nlink(inode, fattr->nlink);
  2177		} else if (fattr_supported & NFS_ATTR_FATTR_NLINK)
  2178			nfsi->cache_validity |=
  2179				save_cache_validity & NFS_INO_INVALID_NLINK;
  2180	
  2181		if (fattr->valid & NFS_ATTR_FATTR_ACCESS) {
  2182			if (!(invalid & NFS_INO_INVALID_ACCESS)) {
  2183				const struct cred *cred = current_cred();
  2184				struct nfs_access_entry *cache;
  2185	
> 2186				cache = nfs_access_search_rbtree(inode, cred);
  2187				if (cache != NULL && cache->mask != fattr->access) {
  2188					invalid |= NFS_INO_INVALID_ACCESS;
  2189				}
  2190			}
  2191		}
  2192	
  2193		if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
  2194			/*
  2195			 * report the blocks in 512byte units
  2196			 */
  2197			inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
  2198		} else if (fattr_supported & NFS_ATTR_FATTR_SPACE_USED)
  2199			nfsi->cache_validity |=
  2200				save_cache_validity & NFS_INO_INVALID_BLOCKS;
  2201	
  2202		if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
  2203			inode->i_blocks = fattr->du.nfs2.blocks;
  2204		else if (fattr_supported & NFS_ATTR_FATTR_BLOCKS_USED)
  2205			nfsi->cache_validity |=
  2206				save_cache_validity & NFS_INO_INVALID_BLOCKS;
  2207	
  2208		/* Update attrtimeo value if we're out of the unstable period */
  2209		if (attr_changed) {
  2210			nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
  2211			nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
  2212			nfsi->attrtimeo_timestamp = now;
  2213			/* Set barrier to be more recent than all outstanding updates */
  2214			nfsi->attr_gencount = nfs_inc_attr_generation_counter();
  2215		} else {
  2216			if (cache_revalidated) {
  2217				if (!time_in_range_open(now, nfsi->attrtimeo_timestamp,
  2218					nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
  2219					nfsi->attrtimeo <<= 1;
  2220					if (nfsi->attrtimeo > NFS_MAXATTRTIMEO(inode))
  2221						nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
  2222				}
  2223				nfsi->attrtimeo_timestamp = now;
  2224			}
  2225			/* Set the barrier to be more recent than this fattr */
  2226			if ((long)(fattr->gencount - nfsi->attr_gencount) > 0)
  2227				nfsi->attr_gencount = fattr->gencount;
  2228		}
  2229	
  2230		/* Don't invalidate the data if we were to blame */
  2231		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
  2232					|| S_ISLNK(inode->i_mode)))
  2233			invalid &= ~NFS_INO_INVALID_DATA;
  2234		nfs_set_cache_invalid(inode, invalid);
  2235	
  2236		return 0;
  2237	 out_err:
  2238		/*
  2239		 * No need to worry about unhashing the dentry, as the
  2240		 * lookup validation will know that the inode is bad.
  2241		 * (But we fall through to invalidate the caches.)
  2242		 */
  2243		nfs_set_inode_stale_locked(inode);
  2244		return -ESTALE;
  2245	}
  2246
diff mbox series

Patch

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index f594dac436a7..966f680ebbc8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2929,7 +2929,7 @@  static int access_cmp(const struct cred *a, const struct nfs_access_entry *b)
 	return 0;
 }
 
-static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
+struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
 {
 	struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
 
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 6b2cfa59a1a2..8f952f86b126 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -2177,6 +2177,18 @@  static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		nfsi->cache_validity |=
 			save_cache_validity & NFS_INO_INVALID_NLINK;
 
+	if (fattr->valid & NFS_ATTR_FATTR_ACCESS) {
+		if (!(invalid & NFS_INO_INVALID_ACCESS)) {
+			const struct cred *cred = current_cred();
+			struct nfs_access_entry *cache;
+
+			cache = nfs_access_search_rbtree(inode, cred);
+			if (cache != NULL && cache->mask != fattr->access) {
+				invalid |= NFS_INO_INVALID_ACCESS;
+			}
+		}
+	}
+
 	if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
 		/*
 		 * report the blocks in 512byte units
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index cfef738d765e..68ed8d4f95a9 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -272,6 +272,9 @@  extern const struct dentry_operations nfs4_dentry_operations;
 int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
 		    unsigned, umode_t);
 
+extern struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode,
+				const struct cred *cred);
+
 /* fs_context.c */
 extern struct file_system_type nfs4_fs_type;
 
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 86ed5c0142c3..7f8790ab5c7a 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -206,7 +206,8 @@  const u32 nfs4_fattr_bitmap[3] = {
 	| FATTR4_WORD1_TIME_ACCESS
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY
-	| FATTR4_WORD1_MOUNTED_ON_FILEID,
+	| FATTR4_WORD1_MOUNTED_ON_FILEID
+	| FATTR4_WORD1_ACCESS,
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
 	FATTR4_WORD2_SECURITY_LABEL
 #endif
@@ -3820,7 +3821,7 @@  static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
 		nfs4_close_state(ctx->state, _nfs4_ctx_to_openmode(ctx));
 }
 
-#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL)
+#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_ACCESS - 1UL)
 #define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL)
 #define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_XATTR_SUPPORT - 1UL)
 
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index acfe5f4bda48..cf651e9b4a5d 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4296,6 +4296,26 @@  static int decode_attr_xattrsupport(struct xdr_stream *xdr, uint32_t *bitmap,
 	return 0;
 }
 
+static int decode_attr_access(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *access)
+{
+	__be32 *p;
+	int ret = 0;
+
+	*access = 0;
+	if (unlikely(bitmap[1] & (FATTR4_WORD1_ACCESS - 1U)))
+		return -EIO;
+	if (likely(bitmap[1] & FATTR4_WORD1_ACCESS)) {
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			return -EIO;
+		*access = be32_to_cpup(p);
+		bitmap[1] &= ~FATTR4_WORD1_ACCESS;
+		ret = NFS_ATTR_FATTR_ACCESS;
+	}
+	dprintk("%s: access=%u\n", __func__, (unsigned int)*access);
+	return ret;
+}
+
 static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_t attrlen)
 {
 	unsigned int attrwords = XDR_QUADLEN(attrlen);
@@ -4747,6 +4767,11 @@  static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		goto xdr_error;
 	fattr->valid |= status;
 
+	status = decode_attr_access(xdr, bitmap, &fattr->access);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = -EIO;
 	if (unlikely(bitmap[1]))
 		goto xdr_error;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 2b4ae858c89b..788e9faaa6e5 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3396,6 +3396,27 @@  nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
 		}
 		p = xdr_encode_hyper(p, ino);
 	}
+	if (bmval1 & FATTR4_WORD1_ACCESS) {
+		u32 access;
+		u32 supported;
+
+		access = NFS3_ACCESS_READ | NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND;
+		if (minorversion >= 2) {
+			access |= NFS4_ACCESS_XALIST | NFS4_ACCESS_XAREAD |
+								NFS4_ACCESS_XAWRITE;
+		}
+		if (S_ISDIR(d_inode(dentry)->i_mode))
+			access |= NFS3_ACCESS_DELETE | NFS3_ACCESS_LOOKUP;
+		else
+			access |= NFS3_ACCESS_EXECUTE;
+		status = nfsd_access(rqstp, fhp, &access, &supported);
+		if (status)
+			goto out;
+		p = xdr_reserve_space(xdr, 4);
+		if (!p)
+			goto out_resource;
+		*p++ = cpu_to_be32(access);
+	}
 #ifdef CONFIG_NFSD_PNFS
 	if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) {
 		status = nfsd4_encode_layout_types(xdr, exp->ex_layout_types);
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 93b42ef9ed91..648a4ec3e2d4 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -378,7 +378,8 @@  void		nfsd_lockd_shutdown(void);
  | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
  | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
  | FATTR4_WORD1_TIME_DELTA      | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
- | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
+ | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID \
+ | FATTR4_WORD1_ACCESS)
 
 #define NFSD4_SUPPORTED_ATTRS_WORD2 0
 
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 730003c4f4af..63d0e31c6552 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -451,6 +451,7 @@  enum lock_type4 {
 #define FATTR4_WORD1_TIME_MODIFY        (1UL << 21)
 #define FATTR4_WORD1_TIME_MODIFY_SET    (1UL << 22)
 #define FATTR4_WORD1_MOUNTED_ON_FILEID  (1UL << 23)
+#define FATTR4_WORD1_ACCESS             (1UL << 24)
 #define FATTR4_WORD1_DACL               (1UL << 26)
 #define FATTR4_WORD1_SACL               (1UL << 27)
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index e86cf6642d21..4626e27588de 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -78,6 +78,7 @@  struct nfs_fattr {
 	struct nfs4_string	*group_name;
 	struct nfs4_threshold	*mdsthreshold;	/* pNFS threshold hints */
 	struct nfs4_label	*label;
+	__u32 access;
 };
 
 #define NFS_ATTR_FATTR_TYPE		(1U << 0)
@@ -106,6 +107,7 @@  struct nfs_fattr {
 #define NFS_ATTR_FATTR_OWNER_NAME	(1U << 23)
 #define NFS_ATTR_FATTR_GROUP_NAME	(1U << 24)
 #define NFS_ATTR_FATTR_V4_SECURITY_LABEL (1U << 25)
+#define NFS_ATTR_FATTR_ACCESS (1U << 26)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \