162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/nfs/dir.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1992  Rick Sladkey
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  nfs directory handling functions
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * 10 Apr 1996	Added silly rename for unlink	--okir
1062306a36Sopenharmony_ci * 28 Sep 1996	Improved directory cache --okir
1162306a36Sopenharmony_ci * 23 Aug 1997  Claus Heine claus@momo.math.rwth-aachen.de
1262306a36Sopenharmony_ci *              Re-implemented silly rename for unlink, newly implemented
1362306a36Sopenharmony_ci *              silly rename for nfs_rename() following the suggestions
1462306a36Sopenharmony_ci *              of Olaf Kirch (okir) found in this file.
1562306a36Sopenharmony_ci *              Following Linus comments on my original hack, this version
1662306a36Sopenharmony_ci *              depends only on the dcache stuff and doesn't touch the inode
1762306a36Sopenharmony_ci *              layer (iput() and friends).
1862306a36Sopenharmony_ci *  6 Jun 1999	Cache readdir lookups in the page cache. -DaveM
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/compat.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/time.h>
2462306a36Sopenharmony_ci#include <linux/errno.h>
2562306a36Sopenharmony_ci#include <linux/stat.h>
2662306a36Sopenharmony_ci#include <linux/fcntl.h>
2762306a36Sopenharmony_ci#include <linux/string.h>
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/mm.h>
3162306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
3262306a36Sopenharmony_ci#include <linux/nfs_fs.h>
3362306a36Sopenharmony_ci#include <linux/nfs_mount.h>
3462306a36Sopenharmony_ci#include <linux/pagemap.h>
3562306a36Sopenharmony_ci#include <linux/pagevec.h>
3662306a36Sopenharmony_ci#include <linux/namei.h>
3762306a36Sopenharmony_ci#include <linux/mount.h>
3862306a36Sopenharmony_ci#include <linux/swap.h>
3962306a36Sopenharmony_ci#include <linux/sched.h>
4062306a36Sopenharmony_ci#include <linux/kmemleak.h>
4162306a36Sopenharmony_ci#include <linux/xattr.h>
4262306a36Sopenharmony_ci#include <linux/hash.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include "delegation.h"
4562306a36Sopenharmony_ci#include "iostat.h"
4662306a36Sopenharmony_ci#include "internal.h"
4762306a36Sopenharmony_ci#include "fscache.h"
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include "nfstrace.h"
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* #define NFS_DEBUG_VERBOSE 1 */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int nfs_opendir(struct inode *, struct file *);
5462306a36Sopenharmony_cistatic int nfs_closedir(struct inode *, struct file *);
5562306a36Sopenharmony_cistatic int nfs_readdir(struct file *, struct dir_context *);
5662306a36Sopenharmony_cistatic int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
5762306a36Sopenharmony_cistatic loff_t nfs_llseek_dir(struct file *, loff_t, int);
5862306a36Sopenharmony_cistatic void nfs_readdir_clear_array(struct folio *);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciconst struct file_operations nfs_dir_operations = {
6162306a36Sopenharmony_ci	.llseek		= nfs_llseek_dir,
6262306a36Sopenharmony_ci	.read		= generic_read_dir,
6362306a36Sopenharmony_ci	.iterate_shared	= nfs_readdir,
6462306a36Sopenharmony_ci	.open		= nfs_opendir,
6562306a36Sopenharmony_ci	.release	= nfs_closedir,
6662306a36Sopenharmony_ci	.fsync		= nfs_fsync_dir,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciconst struct address_space_operations nfs_dir_aops = {
7062306a36Sopenharmony_ci	.free_folio = nfs_readdir_clear_array,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define NFS_INIT_DTSIZE PAGE_SIZE
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic struct nfs_open_dir_context *
7662306a36Sopenharmony_cialloc_nfs_open_dir_context(struct inode *dir)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(dir);
7962306a36Sopenharmony_ci	struct nfs_open_dir_context *ctx;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
8262306a36Sopenharmony_ci	if (ctx != NULL) {
8362306a36Sopenharmony_ci		ctx->attr_gencount = nfsi->attr_gencount;
8462306a36Sopenharmony_ci		ctx->dtsize = NFS_INIT_DTSIZE;
8562306a36Sopenharmony_ci		spin_lock(&dir->i_lock);
8662306a36Sopenharmony_ci		if (list_empty(&nfsi->open_files) &&
8762306a36Sopenharmony_ci		    (nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER))
8862306a36Sopenharmony_ci			nfs_set_cache_invalid(dir,
8962306a36Sopenharmony_ci					      NFS_INO_INVALID_DATA |
9062306a36Sopenharmony_ci						      NFS_INO_REVAL_FORCED);
9162306a36Sopenharmony_ci		list_add_tail_rcu(&ctx->list, &nfsi->open_files);
9262306a36Sopenharmony_ci		memcpy(ctx->verf, nfsi->cookieverf, sizeof(ctx->verf));
9362306a36Sopenharmony_ci		spin_unlock(&dir->i_lock);
9462306a36Sopenharmony_ci		return ctx;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	return  ERR_PTR(-ENOMEM);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_context *ctx)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	spin_lock(&dir->i_lock);
10262306a36Sopenharmony_ci	list_del_rcu(&ctx->list);
10362306a36Sopenharmony_ci	spin_unlock(&dir->i_lock);
10462306a36Sopenharmony_ci	kfree_rcu(ctx, rcu_head);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Open file
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistatic int
11162306a36Sopenharmony_cinfs_opendir(struct inode *inode, struct file *filp)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int res = 0;
11462306a36Sopenharmony_ci	struct nfs_open_dir_context *ctx;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	dfprintk(FILE, "NFS: open dir(%pD2)\n", filp);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSOPEN);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ctx = alloc_nfs_open_dir_context(inode);
12162306a36Sopenharmony_ci	if (IS_ERR(ctx)) {
12262306a36Sopenharmony_ci		res = PTR_ERR(ctx);
12362306a36Sopenharmony_ci		goto out;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	filp->private_data = ctx;
12662306a36Sopenharmony_ciout:
12762306a36Sopenharmony_ci	return res;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int
13162306a36Sopenharmony_cinfs_closedir(struct inode *inode, struct file *filp)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	put_nfs_open_dir_context(file_inode(filp), filp->private_data);
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistruct nfs_cache_array_entry {
13862306a36Sopenharmony_ci	u64 cookie;
13962306a36Sopenharmony_ci	u64 ino;
14062306a36Sopenharmony_ci	const char *name;
14162306a36Sopenharmony_ci	unsigned int name_len;
14262306a36Sopenharmony_ci	unsigned char d_type;
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistruct nfs_cache_array {
14662306a36Sopenharmony_ci	u64 change_attr;
14762306a36Sopenharmony_ci	u64 last_cookie;
14862306a36Sopenharmony_ci	unsigned int size;
14962306a36Sopenharmony_ci	unsigned char folio_full : 1,
15062306a36Sopenharmony_ci		      folio_is_eof : 1,
15162306a36Sopenharmony_ci		      cookies_are_ordered : 1;
15262306a36Sopenharmony_ci	struct nfs_cache_array_entry array[];
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistruct nfs_readdir_descriptor {
15662306a36Sopenharmony_ci	struct file	*file;
15762306a36Sopenharmony_ci	struct folio	*folio;
15862306a36Sopenharmony_ci	struct dir_context *ctx;
15962306a36Sopenharmony_ci	pgoff_t		folio_index;
16062306a36Sopenharmony_ci	pgoff_t		folio_index_max;
16162306a36Sopenharmony_ci	u64		dir_cookie;
16262306a36Sopenharmony_ci	u64		last_cookie;
16362306a36Sopenharmony_ci	loff_t		current_index;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	__be32		verf[NFS_DIR_VERIFIER_SIZE];
16662306a36Sopenharmony_ci	unsigned long	dir_verifier;
16762306a36Sopenharmony_ci	unsigned long	timestamp;
16862306a36Sopenharmony_ci	unsigned long	gencount;
16962306a36Sopenharmony_ci	unsigned long	attr_gencount;
17062306a36Sopenharmony_ci	unsigned int	cache_entry_index;
17162306a36Sopenharmony_ci	unsigned int	buffer_fills;
17262306a36Sopenharmony_ci	unsigned int	dtsize;
17362306a36Sopenharmony_ci	bool clear_cache;
17462306a36Sopenharmony_ci	bool plus;
17562306a36Sopenharmony_ci	bool eob;
17662306a36Sopenharmony_ci	bool eof;
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void nfs_set_dtsize(struct nfs_readdir_descriptor *desc, unsigned int sz)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(file_inode(desc->file));
18262306a36Sopenharmony_ci	unsigned int maxsize = server->dtsize;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (sz > maxsize)
18562306a36Sopenharmony_ci		sz = maxsize;
18662306a36Sopenharmony_ci	if (sz < NFS_MIN_FILE_IO_SIZE)
18762306a36Sopenharmony_ci		sz = NFS_MIN_FILE_IO_SIZE;
18862306a36Sopenharmony_ci	desc->dtsize = sz;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void nfs_shrink_dtsize(struct nfs_readdir_descriptor *desc)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	nfs_set_dtsize(desc, desc->dtsize >> 1);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void nfs_grow_dtsize(struct nfs_readdir_descriptor *desc)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	nfs_set_dtsize(desc, desc->dtsize << 1);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void nfs_readdir_folio_init_array(struct folio *folio, u64 last_cookie,
20262306a36Sopenharmony_ci					 u64 change_attr)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct nfs_cache_array *array;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	array = kmap_local_folio(folio, 0);
20762306a36Sopenharmony_ci	array->change_attr = change_attr;
20862306a36Sopenharmony_ci	array->last_cookie = last_cookie;
20962306a36Sopenharmony_ci	array->size = 0;
21062306a36Sopenharmony_ci	array->folio_full = 0;
21162306a36Sopenharmony_ci	array->folio_is_eof = 0;
21262306a36Sopenharmony_ci	array->cookies_are_ordered = 1;
21362306a36Sopenharmony_ci	kunmap_local(array);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/*
21762306a36Sopenharmony_ci * we are freeing strings created by nfs_add_to_readdir_array()
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic void nfs_readdir_clear_array(struct folio *folio)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct nfs_cache_array *array;
22262306a36Sopenharmony_ci	unsigned int i;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	array = kmap_local_folio(folio, 0);
22562306a36Sopenharmony_ci	for (i = 0; i < array->size; i++)
22662306a36Sopenharmony_ci		kfree(array->array[i].name);
22762306a36Sopenharmony_ci	array->size = 0;
22862306a36Sopenharmony_ci	kunmap_local(array);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void nfs_readdir_folio_reinit_array(struct folio *folio, u64 last_cookie,
23262306a36Sopenharmony_ci					   u64 change_attr)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	nfs_readdir_clear_array(folio);
23562306a36Sopenharmony_ci	nfs_readdir_folio_init_array(folio, last_cookie, change_attr);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic struct folio *
23962306a36Sopenharmony_cinfs_readdir_folio_array_alloc(u64 last_cookie, gfp_t gfp_flags)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct folio *folio = folio_alloc(gfp_flags, 0);
24262306a36Sopenharmony_ci	if (folio)
24362306a36Sopenharmony_ci		nfs_readdir_folio_init_array(folio, last_cookie, 0);
24462306a36Sopenharmony_ci	return folio;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void nfs_readdir_folio_array_free(struct folio *folio)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	if (folio) {
25062306a36Sopenharmony_ci		nfs_readdir_clear_array(folio);
25162306a36Sopenharmony_ci		folio_put(folio);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic u64 nfs_readdir_array_index_cookie(struct nfs_cache_array *array)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	return array->size == 0 ? array->last_cookie : array->array[0].cookie;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic void nfs_readdir_array_set_eof(struct nfs_cache_array *array)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	array->folio_is_eof = 1;
26362306a36Sopenharmony_ci	array->folio_full = 1;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	return array->folio_full;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/*
27262306a36Sopenharmony_ci * the caller is responsible for freeing qstr.name
27362306a36Sopenharmony_ci * when called by nfs_readdir_add_to_array, the strings will be freed in
27462306a36Sopenharmony_ci * nfs_clear_readdir_array()
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic const char *nfs_readdir_copy_name(const char *name, unsigned int len)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	const char *ret = kmemdup_nul(name, len, GFP_KERNEL);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/*
28162306a36Sopenharmony_ci	 * Avoid a kmemleak false positive. The pointer to the name is stored
28262306a36Sopenharmony_ci	 * in a page cache page which kmemleak does not scan.
28362306a36Sopenharmony_ci	 */
28462306a36Sopenharmony_ci	if (ret != NULL)
28562306a36Sopenharmony_ci		kmemleak_not_leak(ret);
28662306a36Sopenharmony_ci	return ret;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic size_t nfs_readdir_array_maxentries(void)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	return (PAGE_SIZE - sizeof(struct nfs_cache_array)) /
29262306a36Sopenharmony_ci	       sizeof(struct nfs_cache_array_entry);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*
29662306a36Sopenharmony_ci * Check that the next array entry lies entirely within the page bounds
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_cistatic int nfs_readdir_array_can_expand(struct nfs_cache_array *array)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	if (array->folio_full)
30162306a36Sopenharmony_ci		return -ENOSPC;
30262306a36Sopenharmony_ci	if (array->size == nfs_readdir_array_maxentries()) {
30362306a36Sopenharmony_ci		array->folio_full = 1;
30462306a36Sopenharmony_ci		return -ENOSPC;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	return 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int nfs_readdir_folio_array_append(struct folio *folio,
31062306a36Sopenharmony_ci					  const struct nfs_entry *entry,
31162306a36Sopenharmony_ci					  u64 *cookie)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct nfs_cache_array *array;
31462306a36Sopenharmony_ci	struct nfs_cache_array_entry *cache_entry;
31562306a36Sopenharmony_ci	const char *name;
31662306a36Sopenharmony_ci	int ret = -ENOMEM;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	name = nfs_readdir_copy_name(entry->name, entry->len);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	array = kmap_local_folio(folio, 0);
32162306a36Sopenharmony_ci	if (!name)
32262306a36Sopenharmony_ci		goto out;
32362306a36Sopenharmony_ci	ret = nfs_readdir_array_can_expand(array);
32462306a36Sopenharmony_ci	if (ret) {
32562306a36Sopenharmony_ci		kfree(name);
32662306a36Sopenharmony_ci		goto out;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	cache_entry = &array->array[array->size];
33062306a36Sopenharmony_ci	cache_entry->cookie = array->last_cookie;
33162306a36Sopenharmony_ci	cache_entry->ino = entry->ino;
33262306a36Sopenharmony_ci	cache_entry->d_type = entry->d_type;
33362306a36Sopenharmony_ci	cache_entry->name_len = entry->len;
33462306a36Sopenharmony_ci	cache_entry->name = name;
33562306a36Sopenharmony_ci	array->last_cookie = entry->cookie;
33662306a36Sopenharmony_ci	if (array->last_cookie <= cache_entry->cookie)
33762306a36Sopenharmony_ci		array->cookies_are_ordered = 0;
33862306a36Sopenharmony_ci	array->size++;
33962306a36Sopenharmony_ci	if (entry->eof != 0)
34062306a36Sopenharmony_ci		nfs_readdir_array_set_eof(array);
34162306a36Sopenharmony_ciout:
34262306a36Sopenharmony_ci	*cookie = array->last_cookie;
34362306a36Sopenharmony_ci	kunmap_local(array);
34462306a36Sopenharmony_ci	return ret;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci#define NFS_READDIR_COOKIE_MASK (U32_MAX >> 14)
34862306a36Sopenharmony_ci/*
34962306a36Sopenharmony_ci * Hash algorithm allowing content addressible access to sequences
35062306a36Sopenharmony_ci * of directory cookies. Content is addressed by the value of the
35162306a36Sopenharmony_ci * cookie index of the first readdir entry in a page.
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci * We select only the first 18 bits to avoid issues with excessive
35462306a36Sopenharmony_ci * memory use for the page cache XArray. 18 bits should allow the caching
35562306a36Sopenharmony_ci * of 262144 pages of sequences of readdir entries. Since each page holds
35662306a36Sopenharmony_ci * 127 readdir entries for a typical 64-bit system, that works out to a
35762306a36Sopenharmony_ci * cache of ~ 33 million entries per directory.
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_cistatic pgoff_t nfs_readdir_folio_cookie_hash(u64 cookie)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	if (cookie == 0)
36262306a36Sopenharmony_ci		return 0;
36362306a36Sopenharmony_ci	return hash_64(cookie, 18);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic bool nfs_readdir_folio_validate(struct folio *folio, u64 last_cookie,
36762306a36Sopenharmony_ci				       u64 change_attr)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct nfs_cache_array *array = kmap_local_folio(folio, 0);
37062306a36Sopenharmony_ci	int ret = true;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (array->change_attr != change_attr)
37362306a36Sopenharmony_ci		ret = false;
37462306a36Sopenharmony_ci	if (nfs_readdir_array_index_cookie(array) != last_cookie)
37562306a36Sopenharmony_ci		ret = false;
37662306a36Sopenharmony_ci	kunmap_local(array);
37762306a36Sopenharmony_ci	return ret;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void nfs_readdir_folio_unlock_and_put(struct folio *folio)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	folio_unlock(folio);
38362306a36Sopenharmony_ci	folio_put(folio);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void nfs_readdir_folio_init_and_validate(struct folio *folio, u64 cookie,
38762306a36Sopenharmony_ci						u64 change_attr)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	if (folio_test_uptodate(folio)) {
39062306a36Sopenharmony_ci		if (nfs_readdir_folio_validate(folio, cookie, change_attr))
39162306a36Sopenharmony_ci			return;
39262306a36Sopenharmony_ci		nfs_readdir_clear_array(folio);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	nfs_readdir_folio_init_array(folio, cookie, change_attr);
39562306a36Sopenharmony_ci	folio_mark_uptodate(folio);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic struct folio *nfs_readdir_folio_get_locked(struct address_space *mapping,
39962306a36Sopenharmony_ci						  u64 cookie, u64 change_attr)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	pgoff_t index = nfs_readdir_folio_cookie_hash(cookie);
40262306a36Sopenharmony_ci	struct folio *folio;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	folio = filemap_grab_folio(mapping, index);
40562306a36Sopenharmony_ci	if (IS_ERR(folio))
40662306a36Sopenharmony_ci		return NULL;
40762306a36Sopenharmony_ci	nfs_readdir_folio_init_and_validate(folio, cookie, change_attr);
40862306a36Sopenharmony_ci	return folio;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic u64 nfs_readdir_folio_last_cookie(struct folio *folio)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct nfs_cache_array *array;
41462306a36Sopenharmony_ci	u64 ret;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	array = kmap_local_folio(folio, 0);
41762306a36Sopenharmony_ci	ret = array->last_cookie;
41862306a36Sopenharmony_ci	kunmap_local(array);
41962306a36Sopenharmony_ci	return ret;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic bool nfs_readdir_folio_needs_filling(struct folio *folio)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct nfs_cache_array *array;
42562306a36Sopenharmony_ci	bool ret;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	array = kmap_local_folio(folio, 0);
42862306a36Sopenharmony_ci	ret = !nfs_readdir_array_is_full(array);
42962306a36Sopenharmony_ci	kunmap_local(array);
43062306a36Sopenharmony_ci	return ret;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic void nfs_readdir_folio_set_eof(struct folio *folio)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct nfs_cache_array *array;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	array = kmap_local_folio(folio, 0);
43862306a36Sopenharmony_ci	nfs_readdir_array_set_eof(array);
43962306a36Sopenharmony_ci	kunmap_local(array);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic struct folio *nfs_readdir_folio_get_next(struct address_space *mapping,
44362306a36Sopenharmony_ci						u64 cookie, u64 change_attr)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	pgoff_t index = nfs_readdir_folio_cookie_hash(cookie);
44662306a36Sopenharmony_ci	struct folio *folio;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	folio = __filemap_get_folio(mapping, index,
44962306a36Sopenharmony_ci			FGP_LOCK|FGP_CREAT|FGP_NOFS|FGP_NOWAIT,
45062306a36Sopenharmony_ci			mapping_gfp_mask(mapping));
45162306a36Sopenharmony_ci	if (IS_ERR(folio))
45262306a36Sopenharmony_ci		return NULL;
45362306a36Sopenharmony_ci	nfs_readdir_folio_init_and_validate(folio, cookie, change_attr);
45462306a36Sopenharmony_ci	if (nfs_readdir_folio_last_cookie(folio) != cookie)
45562306a36Sopenharmony_ci		nfs_readdir_folio_reinit_array(folio, cookie, change_attr);
45662306a36Sopenharmony_ci	return folio;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic inline
46062306a36Sopenharmony_ciint is_32bit_api(void)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
46362306a36Sopenharmony_ci	return in_compat_syscall();
46462306a36Sopenharmony_ci#else
46562306a36Sopenharmony_ci	return (BITS_PER_LONG == 32);
46662306a36Sopenharmony_ci#endif
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic
47062306a36Sopenharmony_cibool nfs_readdir_use_cookie(const struct file *filp)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	if ((filp->f_mode & FMODE_32BITHASH) ||
47362306a36Sopenharmony_ci	    (!(filp->f_mode & FMODE_64BITHASH) && is_32bit_api()))
47462306a36Sopenharmony_ci		return false;
47562306a36Sopenharmony_ci	return true;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void nfs_readdir_seek_next_array(struct nfs_cache_array *array,
47962306a36Sopenharmony_ci					struct nfs_readdir_descriptor *desc)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	if (array->folio_full) {
48262306a36Sopenharmony_ci		desc->last_cookie = array->last_cookie;
48362306a36Sopenharmony_ci		desc->current_index += array->size;
48462306a36Sopenharmony_ci		desc->cache_entry_index = 0;
48562306a36Sopenharmony_ci		desc->folio_index++;
48662306a36Sopenharmony_ci	} else
48762306a36Sopenharmony_ci		desc->last_cookie = nfs_readdir_array_index_cookie(array);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic void nfs_readdir_rewind_search(struct nfs_readdir_descriptor *desc)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	desc->current_index = 0;
49362306a36Sopenharmony_ci	desc->last_cookie = 0;
49462306a36Sopenharmony_ci	desc->folio_index = 0;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int nfs_readdir_search_for_pos(struct nfs_cache_array *array,
49862306a36Sopenharmony_ci				      struct nfs_readdir_descriptor *desc)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	loff_t diff = desc->ctx->pos - desc->current_index;
50162306a36Sopenharmony_ci	unsigned int index;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (diff < 0)
50462306a36Sopenharmony_ci		goto out_eof;
50562306a36Sopenharmony_ci	if (diff >= array->size) {
50662306a36Sopenharmony_ci		if (array->folio_is_eof)
50762306a36Sopenharmony_ci			goto out_eof;
50862306a36Sopenharmony_ci		nfs_readdir_seek_next_array(array, desc);
50962306a36Sopenharmony_ci		return -EAGAIN;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	index = (unsigned int)diff;
51362306a36Sopenharmony_ci	desc->dir_cookie = array->array[index].cookie;
51462306a36Sopenharmony_ci	desc->cache_entry_index = index;
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ciout_eof:
51762306a36Sopenharmony_ci	desc->eof = true;
51862306a36Sopenharmony_ci	return -EBADCOOKIE;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic bool nfs_readdir_array_cookie_in_range(struct nfs_cache_array *array,
52262306a36Sopenharmony_ci					      u64 cookie)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	if (!array->cookies_are_ordered)
52562306a36Sopenharmony_ci		return true;
52662306a36Sopenharmony_ci	/* Optimisation for monotonically increasing cookies */
52762306a36Sopenharmony_ci	if (cookie >= array->last_cookie)
52862306a36Sopenharmony_ci		return false;
52962306a36Sopenharmony_ci	if (array->size && cookie < array->array[0].cookie)
53062306a36Sopenharmony_ci		return false;
53162306a36Sopenharmony_ci	return true;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int nfs_readdir_search_for_cookie(struct nfs_cache_array *array,
53562306a36Sopenharmony_ci					 struct nfs_readdir_descriptor *desc)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	unsigned int i;
53862306a36Sopenharmony_ci	int status = -EAGAIN;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!nfs_readdir_array_cookie_in_range(array, desc->dir_cookie))
54162306a36Sopenharmony_ci		goto check_eof;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	for (i = 0; i < array->size; i++) {
54462306a36Sopenharmony_ci		if (array->array[i].cookie == desc->dir_cookie) {
54562306a36Sopenharmony_ci			if (nfs_readdir_use_cookie(desc->file))
54662306a36Sopenharmony_ci				desc->ctx->pos = desc->dir_cookie;
54762306a36Sopenharmony_ci			else
54862306a36Sopenharmony_ci				desc->ctx->pos = desc->current_index + i;
54962306a36Sopenharmony_ci			desc->cache_entry_index = i;
55062306a36Sopenharmony_ci			return 0;
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_cicheck_eof:
55462306a36Sopenharmony_ci	if (array->folio_is_eof) {
55562306a36Sopenharmony_ci		status = -EBADCOOKIE;
55662306a36Sopenharmony_ci		if (desc->dir_cookie == array->last_cookie)
55762306a36Sopenharmony_ci			desc->eof = true;
55862306a36Sopenharmony_ci	} else
55962306a36Sopenharmony_ci		nfs_readdir_seek_next_array(array, desc);
56062306a36Sopenharmony_ci	return status;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int nfs_readdir_search_array(struct nfs_readdir_descriptor *desc)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct nfs_cache_array *array;
56662306a36Sopenharmony_ci	int status;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	array = kmap_local_folio(desc->folio, 0);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (desc->dir_cookie == 0)
57162306a36Sopenharmony_ci		status = nfs_readdir_search_for_pos(array, desc);
57262306a36Sopenharmony_ci	else
57362306a36Sopenharmony_ci		status = nfs_readdir_search_for_cookie(array, desc);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	kunmap_local(array);
57662306a36Sopenharmony_ci	return status;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/* Fill a page with xdr information before transferring to the cache page */
58062306a36Sopenharmony_cistatic int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
58162306a36Sopenharmony_ci				  __be32 *verf, u64 cookie,
58262306a36Sopenharmony_ci				  struct page **pages, size_t bufsize,
58362306a36Sopenharmony_ci				  __be32 *verf_res)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct inode *inode = file_inode(desc->file);
58662306a36Sopenharmony_ci	struct nfs_readdir_arg arg = {
58762306a36Sopenharmony_ci		.dentry = file_dentry(desc->file),
58862306a36Sopenharmony_ci		.cred = desc->file->f_cred,
58962306a36Sopenharmony_ci		.verf = verf,
59062306a36Sopenharmony_ci		.cookie = cookie,
59162306a36Sopenharmony_ci		.pages = pages,
59262306a36Sopenharmony_ci		.page_len = bufsize,
59362306a36Sopenharmony_ci		.plus = desc->plus,
59462306a36Sopenharmony_ci	};
59562306a36Sopenharmony_ci	struct nfs_readdir_res res = {
59662306a36Sopenharmony_ci		.verf = verf_res,
59762306a36Sopenharmony_ci	};
59862306a36Sopenharmony_ci	unsigned long	timestamp, gencount;
59962306a36Sopenharmony_ci	int		error;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci again:
60262306a36Sopenharmony_ci	timestamp = jiffies;
60362306a36Sopenharmony_ci	gencount = nfs_inc_attr_generation_counter();
60462306a36Sopenharmony_ci	desc->dir_verifier = nfs_save_change_attribute(inode);
60562306a36Sopenharmony_ci	error = NFS_PROTO(inode)->readdir(&arg, &res);
60662306a36Sopenharmony_ci	if (error < 0) {
60762306a36Sopenharmony_ci		/* We requested READDIRPLUS, but the server doesn't grok it */
60862306a36Sopenharmony_ci		if (error == -ENOTSUPP && desc->plus) {
60962306a36Sopenharmony_ci			NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS;
61062306a36Sopenharmony_ci			desc->plus = arg.plus = false;
61162306a36Sopenharmony_ci			goto again;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci		goto error;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci	desc->timestamp = timestamp;
61662306a36Sopenharmony_ci	desc->gencount = gencount;
61762306a36Sopenharmony_cierror:
61862306a36Sopenharmony_ci	return error;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int xdr_decode(struct nfs_readdir_descriptor *desc,
62262306a36Sopenharmony_ci		      struct nfs_entry *entry, struct xdr_stream *xdr)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct inode *inode = file_inode(desc->file);
62562306a36Sopenharmony_ci	int error;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	error = NFS_PROTO(inode)->decode_dirent(xdr, entry, desc->plus);
62862306a36Sopenharmony_ci	if (error)
62962306a36Sopenharmony_ci		return error;
63062306a36Sopenharmony_ci	entry->fattr->time_start = desc->timestamp;
63162306a36Sopenharmony_ci	entry->fattr->gencount = desc->gencount;
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/* Match file and dirent using either filehandle or fileid
63662306a36Sopenharmony_ci * Note: caller is responsible for checking the fsid
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_cistatic
63962306a36Sopenharmony_ciint nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct inode *inode;
64262306a36Sopenharmony_ci	struct nfs_inode *nfsi;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (d_really_is_negative(dentry))
64562306a36Sopenharmony_ci		return 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	inode = d_inode(dentry);
64862306a36Sopenharmony_ci	if (is_bad_inode(inode) || NFS_STALE(inode))
64962306a36Sopenharmony_ci		return 0;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	nfsi = NFS_I(inode);
65262306a36Sopenharmony_ci	if (entry->fattr->fileid != nfsi->fileid)
65362306a36Sopenharmony_ci		return 0;
65462306a36Sopenharmony_ci	if (entry->fh->size && nfs_compare_fh(entry->fh, &nfsi->fh) != 0)
65562306a36Sopenharmony_ci		return 0;
65662306a36Sopenharmony_ci	return 1;
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci#define NFS_READDIR_CACHE_USAGE_THRESHOLD (8UL)
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic bool nfs_use_readdirplus(struct inode *dir, struct dir_context *ctx,
66262306a36Sopenharmony_ci				unsigned int cache_hits,
66362306a36Sopenharmony_ci				unsigned int cache_misses)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
66662306a36Sopenharmony_ci		return false;
66762306a36Sopenharmony_ci	if (ctx->pos == 0 ||
66862306a36Sopenharmony_ci	    cache_hits + cache_misses > NFS_READDIR_CACHE_USAGE_THRESHOLD)
66962306a36Sopenharmony_ci		return true;
67062306a36Sopenharmony_ci	return false;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/*
67462306a36Sopenharmony_ci * This function is called by the getattr code to request the
67562306a36Sopenharmony_ci * use of readdirplus to accelerate any future lookups in the same
67662306a36Sopenharmony_ci * directory.
67762306a36Sopenharmony_ci */
67862306a36Sopenharmony_civoid nfs_readdir_record_entry_cache_hit(struct inode *dir)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(dir);
68162306a36Sopenharmony_ci	struct nfs_open_dir_context *ctx;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (nfs_server_capable(dir, NFS_CAP_READDIRPLUS) &&
68462306a36Sopenharmony_ci	    S_ISDIR(dir->i_mode)) {
68562306a36Sopenharmony_ci		rcu_read_lock();
68662306a36Sopenharmony_ci		list_for_each_entry_rcu (ctx, &nfsi->open_files, list)
68762306a36Sopenharmony_ci			atomic_inc(&ctx->cache_hits);
68862306a36Sopenharmony_ci		rcu_read_unlock();
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci/*
69362306a36Sopenharmony_ci * This function is mainly for use by nfs_getattr().
69462306a36Sopenharmony_ci *
69562306a36Sopenharmony_ci * If this is an 'ls -l', we want to force use of readdirplus.
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_civoid nfs_readdir_record_entry_cache_miss(struct inode *dir)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(dir);
70062306a36Sopenharmony_ci	struct nfs_open_dir_context *ctx;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (nfs_server_capable(dir, NFS_CAP_READDIRPLUS) &&
70362306a36Sopenharmony_ci	    S_ISDIR(dir->i_mode)) {
70462306a36Sopenharmony_ci		rcu_read_lock();
70562306a36Sopenharmony_ci		list_for_each_entry_rcu (ctx, &nfsi->open_files, list)
70662306a36Sopenharmony_ci			atomic_inc(&ctx->cache_misses);
70762306a36Sopenharmony_ci		rcu_read_unlock();
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void nfs_lookup_advise_force_readdirplus(struct inode *dir,
71262306a36Sopenharmony_ci						unsigned int flags)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
71562306a36Sopenharmony_ci		return;
71662306a36Sopenharmony_ci	if (flags & (LOOKUP_EXCL | LOOKUP_PARENT | LOOKUP_REVAL))
71762306a36Sopenharmony_ci		return;
71862306a36Sopenharmony_ci	nfs_readdir_record_entry_cache_miss(dir);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic
72262306a36Sopenharmony_civoid nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
72362306a36Sopenharmony_ci		unsigned long dir_verifier)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct qstr filename = QSTR_INIT(entry->name, entry->len);
72662306a36Sopenharmony_ci	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
72762306a36Sopenharmony_ci	struct dentry *dentry;
72862306a36Sopenharmony_ci	struct dentry *alias;
72962306a36Sopenharmony_ci	struct inode *inode;
73062306a36Sopenharmony_ci	int status;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (!(entry->fattr->valid & NFS_ATTR_FATTR_FILEID))
73362306a36Sopenharmony_ci		return;
73462306a36Sopenharmony_ci	if (!(entry->fattr->valid & NFS_ATTR_FATTR_FSID))
73562306a36Sopenharmony_ci		return;
73662306a36Sopenharmony_ci	if (filename.len == 0)
73762306a36Sopenharmony_ci		return;
73862306a36Sopenharmony_ci	/* Validate that the name doesn't contain any illegal '\0' */
73962306a36Sopenharmony_ci	if (strnlen(filename.name, filename.len) != filename.len)
74062306a36Sopenharmony_ci		return;
74162306a36Sopenharmony_ci	/* ...or '/' */
74262306a36Sopenharmony_ci	if (strnchr(filename.name, filename.len, '/'))
74362306a36Sopenharmony_ci		return;
74462306a36Sopenharmony_ci	if (filename.name[0] == '.') {
74562306a36Sopenharmony_ci		if (filename.len == 1)
74662306a36Sopenharmony_ci			return;
74762306a36Sopenharmony_ci		if (filename.len == 2 && filename.name[1] == '.')
74862306a36Sopenharmony_ci			return;
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci	filename.hash = full_name_hash(parent, filename.name, filename.len);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	dentry = d_lookup(parent, &filename);
75362306a36Sopenharmony_ciagain:
75462306a36Sopenharmony_ci	if (!dentry) {
75562306a36Sopenharmony_ci		dentry = d_alloc_parallel(parent, &filename, &wq);
75662306a36Sopenharmony_ci		if (IS_ERR(dentry))
75762306a36Sopenharmony_ci			return;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci	if (!d_in_lookup(dentry)) {
76062306a36Sopenharmony_ci		/* Is there a mountpoint here? If so, just exit */
76162306a36Sopenharmony_ci		if (!nfs_fsid_equal(&NFS_SB(dentry->d_sb)->fsid,
76262306a36Sopenharmony_ci					&entry->fattr->fsid))
76362306a36Sopenharmony_ci			goto out;
76462306a36Sopenharmony_ci		if (nfs_same_file(dentry, entry)) {
76562306a36Sopenharmony_ci			if (!entry->fh->size)
76662306a36Sopenharmony_ci				goto out;
76762306a36Sopenharmony_ci			nfs_set_verifier(dentry, dir_verifier);
76862306a36Sopenharmony_ci			status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
76962306a36Sopenharmony_ci			if (!status)
77062306a36Sopenharmony_ci				nfs_setsecurity(d_inode(dentry), entry->fattr);
77162306a36Sopenharmony_ci			trace_nfs_readdir_lookup_revalidate(d_inode(parent),
77262306a36Sopenharmony_ci							    dentry, 0, status);
77362306a36Sopenharmony_ci			goto out;
77462306a36Sopenharmony_ci		} else {
77562306a36Sopenharmony_ci			trace_nfs_readdir_lookup_revalidate_failed(
77662306a36Sopenharmony_ci				d_inode(parent), dentry, 0);
77762306a36Sopenharmony_ci			d_invalidate(dentry);
77862306a36Sopenharmony_ci			dput(dentry);
77962306a36Sopenharmony_ci			dentry = NULL;
78062306a36Sopenharmony_ci			goto again;
78162306a36Sopenharmony_ci		}
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci	if (!entry->fh->size) {
78462306a36Sopenharmony_ci		d_lookup_done(dentry);
78562306a36Sopenharmony_ci		goto out;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
78962306a36Sopenharmony_ci	alias = d_splice_alias(inode, dentry);
79062306a36Sopenharmony_ci	d_lookup_done(dentry);
79162306a36Sopenharmony_ci	if (alias) {
79262306a36Sopenharmony_ci		if (IS_ERR(alias))
79362306a36Sopenharmony_ci			goto out;
79462306a36Sopenharmony_ci		dput(dentry);
79562306a36Sopenharmony_ci		dentry = alias;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci	nfs_set_verifier(dentry, dir_verifier);
79862306a36Sopenharmony_ci	trace_nfs_readdir_lookup(d_inode(parent), dentry, 0);
79962306a36Sopenharmony_ciout:
80062306a36Sopenharmony_ci	dput(dentry);
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int nfs_readdir_entry_decode(struct nfs_readdir_descriptor *desc,
80462306a36Sopenharmony_ci				    struct nfs_entry *entry,
80562306a36Sopenharmony_ci				    struct xdr_stream *stream)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	int ret;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (entry->fattr->label)
81062306a36Sopenharmony_ci		entry->fattr->label->len = NFS4_MAXLABELLEN;
81162306a36Sopenharmony_ci	ret = xdr_decode(desc, entry, stream);
81262306a36Sopenharmony_ci	if (ret || !desc->plus)
81362306a36Sopenharmony_ci		return ret;
81462306a36Sopenharmony_ci	nfs_prime_dcache(file_dentry(desc->file), entry, desc->dir_verifier);
81562306a36Sopenharmony_ci	return 0;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci/* Perform conversion from xdr to cache array */
81962306a36Sopenharmony_cistatic int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
82062306a36Sopenharmony_ci				    struct nfs_entry *entry,
82162306a36Sopenharmony_ci				    struct page **xdr_pages, unsigned int buflen,
82262306a36Sopenharmony_ci				    struct folio **arrays, size_t narrays,
82362306a36Sopenharmony_ci				    u64 change_attr)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct address_space *mapping = desc->file->f_mapping;
82662306a36Sopenharmony_ci	struct folio *new, *folio = *arrays;
82762306a36Sopenharmony_ci	struct xdr_stream stream;
82862306a36Sopenharmony_ci	struct page *scratch;
82962306a36Sopenharmony_ci	struct xdr_buf buf;
83062306a36Sopenharmony_ci	u64 cookie;
83162306a36Sopenharmony_ci	int status;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	scratch = alloc_page(GFP_KERNEL);
83462306a36Sopenharmony_ci	if (scratch == NULL)
83562306a36Sopenharmony_ci		return -ENOMEM;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	xdr_init_decode_pages(&stream, &buf, xdr_pages, buflen);
83862306a36Sopenharmony_ci	xdr_set_scratch_page(&stream, scratch);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	do {
84162306a36Sopenharmony_ci		status = nfs_readdir_entry_decode(desc, entry, &stream);
84262306a36Sopenharmony_ci		if (status != 0)
84362306a36Sopenharmony_ci			break;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		status = nfs_readdir_folio_array_append(folio, entry, &cookie);
84662306a36Sopenharmony_ci		if (status != -ENOSPC)
84762306a36Sopenharmony_ci			continue;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		if (folio->mapping != mapping) {
85062306a36Sopenharmony_ci			if (!--narrays)
85162306a36Sopenharmony_ci				break;
85262306a36Sopenharmony_ci			new = nfs_readdir_folio_array_alloc(cookie, GFP_KERNEL);
85362306a36Sopenharmony_ci			if (!new)
85462306a36Sopenharmony_ci				break;
85562306a36Sopenharmony_ci			arrays++;
85662306a36Sopenharmony_ci			*arrays = folio = new;
85762306a36Sopenharmony_ci		} else {
85862306a36Sopenharmony_ci			new = nfs_readdir_folio_get_next(mapping, cookie,
85962306a36Sopenharmony_ci							 change_attr);
86062306a36Sopenharmony_ci			if (!new)
86162306a36Sopenharmony_ci				break;
86262306a36Sopenharmony_ci			if (folio != *arrays)
86362306a36Sopenharmony_ci				nfs_readdir_folio_unlock_and_put(folio);
86462306a36Sopenharmony_ci			folio = new;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci		desc->folio_index_max++;
86762306a36Sopenharmony_ci		status = nfs_readdir_folio_array_append(folio, entry, &cookie);
86862306a36Sopenharmony_ci	} while (!status && !entry->eof);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	switch (status) {
87162306a36Sopenharmony_ci	case -EBADCOOKIE:
87262306a36Sopenharmony_ci		if (!entry->eof)
87362306a36Sopenharmony_ci			break;
87462306a36Sopenharmony_ci		nfs_readdir_folio_set_eof(folio);
87562306a36Sopenharmony_ci		fallthrough;
87662306a36Sopenharmony_ci	case -EAGAIN:
87762306a36Sopenharmony_ci		status = 0;
87862306a36Sopenharmony_ci		break;
87962306a36Sopenharmony_ci	case -ENOSPC:
88062306a36Sopenharmony_ci		status = 0;
88162306a36Sopenharmony_ci		if (!desc->plus)
88262306a36Sopenharmony_ci			break;
88362306a36Sopenharmony_ci		while (!nfs_readdir_entry_decode(desc, entry, &stream))
88462306a36Sopenharmony_ci			;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	if (folio != *arrays)
88862306a36Sopenharmony_ci		nfs_readdir_folio_unlock_and_put(folio);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	put_page(scratch);
89162306a36Sopenharmony_ci	return status;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic void nfs_readdir_free_pages(struct page **pages, size_t npages)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	while (npages--)
89762306a36Sopenharmony_ci		put_page(pages[npages]);
89862306a36Sopenharmony_ci	kfree(pages);
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci/*
90262306a36Sopenharmony_ci * nfs_readdir_alloc_pages() will allocate pages that must be freed with a call
90362306a36Sopenharmony_ci * to nfs_readdir_free_pages()
90462306a36Sopenharmony_ci */
90562306a36Sopenharmony_cistatic struct page **nfs_readdir_alloc_pages(size_t npages)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct page **pages;
90862306a36Sopenharmony_ci	size_t i;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	pages = kmalloc_array(npages, sizeof(*pages), GFP_KERNEL);
91162306a36Sopenharmony_ci	if (!pages)
91262306a36Sopenharmony_ci		return NULL;
91362306a36Sopenharmony_ci	for (i = 0; i < npages; i++) {
91462306a36Sopenharmony_ci		struct page *page = alloc_page(GFP_KERNEL);
91562306a36Sopenharmony_ci		if (page == NULL)
91662306a36Sopenharmony_ci			goto out_freepages;
91762306a36Sopenharmony_ci		pages[i] = page;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci	return pages;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ciout_freepages:
92262306a36Sopenharmony_ci	nfs_readdir_free_pages(pages, i);
92362306a36Sopenharmony_ci	return NULL;
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
92762306a36Sopenharmony_ci				    __be32 *verf_arg, __be32 *verf_res,
92862306a36Sopenharmony_ci				    struct folio **arrays, size_t narrays)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	u64 change_attr;
93162306a36Sopenharmony_ci	struct page **pages;
93262306a36Sopenharmony_ci	struct folio *folio = *arrays;
93362306a36Sopenharmony_ci	struct nfs_entry *entry;
93462306a36Sopenharmony_ci	size_t array_size;
93562306a36Sopenharmony_ci	struct inode *inode = file_inode(desc->file);
93662306a36Sopenharmony_ci	unsigned int dtsize = desc->dtsize;
93762306a36Sopenharmony_ci	unsigned int pglen;
93862306a36Sopenharmony_ci	int status = -ENOMEM;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
94162306a36Sopenharmony_ci	if (!entry)
94262306a36Sopenharmony_ci		return -ENOMEM;
94362306a36Sopenharmony_ci	entry->cookie = nfs_readdir_folio_last_cookie(folio);
94462306a36Sopenharmony_ci	entry->fh = nfs_alloc_fhandle();
94562306a36Sopenharmony_ci	entry->fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
94662306a36Sopenharmony_ci	entry->server = NFS_SERVER(inode);
94762306a36Sopenharmony_ci	if (entry->fh == NULL || entry->fattr == NULL)
94862306a36Sopenharmony_ci		goto out;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	array_size = (dtsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
95162306a36Sopenharmony_ci	pages = nfs_readdir_alloc_pages(array_size);
95262306a36Sopenharmony_ci	if (!pages)
95362306a36Sopenharmony_ci		goto out;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	change_attr = inode_peek_iversion_raw(inode);
95662306a36Sopenharmony_ci	status = nfs_readdir_xdr_filler(desc, verf_arg, entry->cookie, pages,
95762306a36Sopenharmony_ci					dtsize, verf_res);
95862306a36Sopenharmony_ci	if (status < 0)
95962306a36Sopenharmony_ci		goto free_pages;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	pglen = status;
96262306a36Sopenharmony_ci	if (pglen != 0)
96362306a36Sopenharmony_ci		status = nfs_readdir_folio_filler(desc, entry, pages, pglen,
96462306a36Sopenharmony_ci						  arrays, narrays, change_attr);
96562306a36Sopenharmony_ci	else
96662306a36Sopenharmony_ci		nfs_readdir_folio_set_eof(folio);
96762306a36Sopenharmony_ci	desc->buffer_fills++;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cifree_pages:
97062306a36Sopenharmony_ci	nfs_readdir_free_pages(pages, array_size);
97162306a36Sopenharmony_ciout:
97262306a36Sopenharmony_ci	nfs_free_fattr(entry->fattr);
97362306a36Sopenharmony_ci	nfs_free_fhandle(entry->fh);
97462306a36Sopenharmony_ci	kfree(entry);
97562306a36Sopenharmony_ci	return status;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void nfs_readdir_folio_put(struct nfs_readdir_descriptor *desc)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	folio_put(desc->folio);
98162306a36Sopenharmony_ci	desc->folio = NULL;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic void
98562306a36Sopenharmony_cinfs_readdir_folio_unlock_and_put_cached(struct nfs_readdir_descriptor *desc)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	folio_unlock(desc->folio);
98862306a36Sopenharmony_ci	nfs_readdir_folio_put(desc);
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic struct folio *
99262306a36Sopenharmony_cinfs_readdir_folio_get_cached(struct nfs_readdir_descriptor *desc)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct address_space *mapping = desc->file->f_mapping;
99562306a36Sopenharmony_ci	u64 change_attr = inode_peek_iversion_raw(mapping->host);
99662306a36Sopenharmony_ci	u64 cookie = desc->last_cookie;
99762306a36Sopenharmony_ci	struct folio *folio;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	folio = nfs_readdir_folio_get_locked(mapping, cookie, change_attr);
100062306a36Sopenharmony_ci	if (!folio)
100162306a36Sopenharmony_ci		return NULL;
100262306a36Sopenharmony_ci	if (desc->clear_cache && !nfs_readdir_folio_needs_filling(folio))
100362306a36Sopenharmony_ci		nfs_readdir_folio_reinit_array(folio, cookie, change_attr);
100462306a36Sopenharmony_ci	return folio;
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/*
100862306a36Sopenharmony_ci * Returns 0 if desc->dir_cookie was found on page desc->page_index
100962306a36Sopenharmony_ci * and locks the page to prevent removal from the page cache.
101062306a36Sopenharmony_ci */
101162306a36Sopenharmony_cistatic int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct inode *inode = file_inode(desc->file);
101462306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
101562306a36Sopenharmony_ci	__be32 verf[NFS_DIR_VERIFIER_SIZE];
101662306a36Sopenharmony_ci	int res;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	desc->folio = nfs_readdir_folio_get_cached(desc);
101962306a36Sopenharmony_ci	if (!desc->folio)
102062306a36Sopenharmony_ci		return -ENOMEM;
102162306a36Sopenharmony_ci	if (nfs_readdir_folio_needs_filling(desc->folio)) {
102262306a36Sopenharmony_ci		/* Grow the dtsize if we had to go back for more pages */
102362306a36Sopenharmony_ci		if (desc->folio_index == desc->folio_index_max)
102462306a36Sopenharmony_ci			nfs_grow_dtsize(desc);
102562306a36Sopenharmony_ci		desc->folio_index_max = desc->folio_index;
102662306a36Sopenharmony_ci		trace_nfs_readdir_cache_fill(desc->file, nfsi->cookieverf,
102762306a36Sopenharmony_ci					     desc->last_cookie,
102862306a36Sopenharmony_ci					     desc->folio->index, desc->dtsize);
102962306a36Sopenharmony_ci		res = nfs_readdir_xdr_to_array(desc, nfsi->cookieverf, verf,
103062306a36Sopenharmony_ci					       &desc->folio, 1);
103162306a36Sopenharmony_ci		if (res < 0) {
103262306a36Sopenharmony_ci			nfs_readdir_folio_unlock_and_put_cached(desc);
103362306a36Sopenharmony_ci			trace_nfs_readdir_cache_fill_done(inode, res);
103462306a36Sopenharmony_ci			if (res == -EBADCOOKIE || res == -ENOTSYNC) {
103562306a36Sopenharmony_ci				invalidate_inode_pages2(desc->file->f_mapping);
103662306a36Sopenharmony_ci				nfs_readdir_rewind_search(desc);
103762306a36Sopenharmony_ci				trace_nfs_readdir_invalidate_cache_range(
103862306a36Sopenharmony_ci					inode, 0, MAX_LFS_FILESIZE);
103962306a36Sopenharmony_ci				return -EAGAIN;
104062306a36Sopenharmony_ci			}
104162306a36Sopenharmony_ci			return res;
104262306a36Sopenharmony_ci		}
104362306a36Sopenharmony_ci		/*
104462306a36Sopenharmony_ci		 * Set the cookie verifier if the page cache was empty
104562306a36Sopenharmony_ci		 */
104662306a36Sopenharmony_ci		if (desc->last_cookie == 0 &&
104762306a36Sopenharmony_ci		    memcmp(nfsi->cookieverf, verf, sizeof(nfsi->cookieverf))) {
104862306a36Sopenharmony_ci			memcpy(nfsi->cookieverf, verf,
104962306a36Sopenharmony_ci			       sizeof(nfsi->cookieverf));
105062306a36Sopenharmony_ci			invalidate_inode_pages2_range(desc->file->f_mapping, 1,
105162306a36Sopenharmony_ci						      -1);
105262306a36Sopenharmony_ci			trace_nfs_readdir_invalidate_cache_range(
105362306a36Sopenharmony_ci				inode, 1, MAX_LFS_FILESIZE);
105462306a36Sopenharmony_ci		}
105562306a36Sopenharmony_ci		desc->clear_cache = false;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci	res = nfs_readdir_search_array(desc);
105862306a36Sopenharmony_ci	if (res == 0)
105962306a36Sopenharmony_ci		return 0;
106062306a36Sopenharmony_ci	nfs_readdir_folio_unlock_and_put_cached(desc);
106162306a36Sopenharmony_ci	return res;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci/* Search for desc->dir_cookie from the beginning of the page cache */
106562306a36Sopenharmony_cistatic int readdir_search_pagecache(struct nfs_readdir_descriptor *desc)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	int res;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	do {
107062306a36Sopenharmony_ci		res = find_and_lock_cache_page(desc);
107162306a36Sopenharmony_ci	} while (res == -EAGAIN);
107262306a36Sopenharmony_ci	return res;
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci#define NFS_READDIR_CACHE_MISS_THRESHOLD (16UL)
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci/*
107862306a36Sopenharmony_ci * Once we've found the start of the dirent within a page: fill 'er up...
107962306a36Sopenharmony_ci */
108062306a36Sopenharmony_cistatic void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
108162306a36Sopenharmony_ci			   const __be32 *verf)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	struct file	*file = desc->file;
108462306a36Sopenharmony_ci	struct nfs_cache_array *array;
108562306a36Sopenharmony_ci	unsigned int i;
108662306a36Sopenharmony_ci	bool first_emit = !desc->dir_cookie;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	array = kmap_local_folio(desc->folio, 0);
108962306a36Sopenharmony_ci	for (i = desc->cache_entry_index; i < array->size; i++) {
109062306a36Sopenharmony_ci		struct nfs_cache_array_entry *ent;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		/*
109362306a36Sopenharmony_ci		 * nfs_readdir_handle_cache_misses return force clear at
109462306a36Sopenharmony_ci		 * (cache_misses > NFS_READDIR_CACHE_MISS_THRESHOLD) for
109562306a36Sopenharmony_ci		 * readdir heuristic, NFS_READDIR_CACHE_MISS_THRESHOLD + 1
109662306a36Sopenharmony_ci		 * entries need be emitted here.
109762306a36Sopenharmony_ci		 */
109862306a36Sopenharmony_ci		if (first_emit && i > NFS_READDIR_CACHE_MISS_THRESHOLD + 2) {
109962306a36Sopenharmony_ci			desc->eob = true;
110062306a36Sopenharmony_ci			break;
110162306a36Sopenharmony_ci		}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci		ent = &array->array[i];
110462306a36Sopenharmony_ci		if (!dir_emit(desc->ctx, ent->name, ent->name_len,
110562306a36Sopenharmony_ci		    nfs_compat_user_ino64(ent->ino), ent->d_type)) {
110662306a36Sopenharmony_ci			desc->eob = true;
110762306a36Sopenharmony_ci			break;
110862306a36Sopenharmony_ci		}
110962306a36Sopenharmony_ci		memcpy(desc->verf, verf, sizeof(desc->verf));
111062306a36Sopenharmony_ci		if (i == array->size - 1) {
111162306a36Sopenharmony_ci			desc->dir_cookie = array->last_cookie;
111262306a36Sopenharmony_ci			nfs_readdir_seek_next_array(array, desc);
111362306a36Sopenharmony_ci		} else {
111462306a36Sopenharmony_ci			desc->dir_cookie = array->array[i + 1].cookie;
111562306a36Sopenharmony_ci			desc->last_cookie = array->array[0].cookie;
111662306a36Sopenharmony_ci		}
111762306a36Sopenharmony_ci		if (nfs_readdir_use_cookie(file))
111862306a36Sopenharmony_ci			desc->ctx->pos = desc->dir_cookie;
111962306a36Sopenharmony_ci		else
112062306a36Sopenharmony_ci			desc->ctx->pos++;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci	if (array->folio_is_eof)
112362306a36Sopenharmony_ci		desc->eof = !desc->eob;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	kunmap_local(array);
112662306a36Sopenharmony_ci	dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %llu\n",
112762306a36Sopenharmony_ci			(unsigned long long)desc->dir_cookie);
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci/*
113162306a36Sopenharmony_ci * If we cannot find a cookie in our cache, we suspect that this is
113262306a36Sopenharmony_ci * because it points to a deleted file, so we ask the server to return
113362306a36Sopenharmony_ci * whatever it thinks is the next entry. We then feed this to filldir.
113462306a36Sopenharmony_ci * If all goes well, we should then be able to find our way round the
113562306a36Sopenharmony_ci * cache on the next call to readdir_search_pagecache();
113662306a36Sopenharmony_ci *
113762306a36Sopenharmony_ci * NOTE: we cannot add the anonymous page to the pagecache because
113862306a36Sopenharmony_ci *	 the data it contains might not be page aligned. Besides,
113962306a36Sopenharmony_ci *	 we should already have a complete representation of the
114062306a36Sopenharmony_ci *	 directory in the page cache by the time we get here.
114162306a36Sopenharmony_ci */
114262306a36Sopenharmony_cistatic int uncached_readdir(struct nfs_readdir_descriptor *desc)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	struct folio	**arrays;
114562306a36Sopenharmony_ci	size_t		i, sz = 512;
114662306a36Sopenharmony_ci	__be32		verf[NFS_DIR_VERIFIER_SIZE];
114762306a36Sopenharmony_ci	int		status = -ENOMEM;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %llu\n",
115062306a36Sopenharmony_ci			(unsigned long long)desc->dir_cookie);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	arrays = kcalloc(sz, sizeof(*arrays), GFP_KERNEL);
115362306a36Sopenharmony_ci	if (!arrays)
115462306a36Sopenharmony_ci		goto out;
115562306a36Sopenharmony_ci	arrays[0] = nfs_readdir_folio_array_alloc(desc->dir_cookie, GFP_KERNEL);
115662306a36Sopenharmony_ci	if (!arrays[0])
115762306a36Sopenharmony_ci		goto out;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	desc->folio_index = 0;
116062306a36Sopenharmony_ci	desc->cache_entry_index = 0;
116162306a36Sopenharmony_ci	desc->last_cookie = desc->dir_cookie;
116262306a36Sopenharmony_ci	desc->folio_index_max = 0;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	trace_nfs_readdir_uncached(desc->file, desc->verf, desc->last_cookie,
116562306a36Sopenharmony_ci				   -1, desc->dtsize);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	status = nfs_readdir_xdr_to_array(desc, desc->verf, verf, arrays, sz);
116862306a36Sopenharmony_ci	if (status < 0) {
116962306a36Sopenharmony_ci		trace_nfs_readdir_uncached_done(file_inode(desc->file), status);
117062306a36Sopenharmony_ci		goto out_free;
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	for (i = 0; !desc->eob && i < sz && arrays[i]; i++) {
117462306a36Sopenharmony_ci		desc->folio = arrays[i];
117562306a36Sopenharmony_ci		nfs_do_filldir(desc, verf);
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci	desc->folio = NULL;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	/*
118062306a36Sopenharmony_ci	 * Grow the dtsize if we have to go back for more pages,
118162306a36Sopenharmony_ci	 * or shrink it if we're reading too many.
118262306a36Sopenharmony_ci	 */
118362306a36Sopenharmony_ci	if (!desc->eof) {
118462306a36Sopenharmony_ci		if (!desc->eob)
118562306a36Sopenharmony_ci			nfs_grow_dtsize(desc);
118662306a36Sopenharmony_ci		else if (desc->buffer_fills == 1 &&
118762306a36Sopenharmony_ci			 i < (desc->folio_index_max >> 1))
118862306a36Sopenharmony_ci			nfs_shrink_dtsize(desc);
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ciout_free:
119162306a36Sopenharmony_ci	for (i = 0; i < sz && arrays[i]; i++)
119262306a36Sopenharmony_ci		nfs_readdir_folio_array_free(arrays[i]);
119362306a36Sopenharmony_ciout:
119462306a36Sopenharmony_ci	if (!nfs_readdir_use_cookie(desc->file))
119562306a36Sopenharmony_ci		nfs_readdir_rewind_search(desc);
119662306a36Sopenharmony_ci	desc->folio_index_max = -1;
119762306a36Sopenharmony_ci	kfree(arrays);
119862306a36Sopenharmony_ci	dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, status);
119962306a36Sopenharmony_ci	return status;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic bool nfs_readdir_handle_cache_misses(struct inode *inode,
120362306a36Sopenharmony_ci					    struct nfs_readdir_descriptor *desc,
120462306a36Sopenharmony_ci					    unsigned int cache_misses,
120562306a36Sopenharmony_ci					    bool force_clear)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	if (desc->ctx->pos == 0 || !desc->plus)
120862306a36Sopenharmony_ci		return false;
120962306a36Sopenharmony_ci	if (cache_misses <= NFS_READDIR_CACHE_MISS_THRESHOLD && !force_clear)
121062306a36Sopenharmony_ci		return false;
121162306a36Sopenharmony_ci	trace_nfs_readdir_force_readdirplus(inode);
121262306a36Sopenharmony_ci	return true;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci/* The file offset position represents the dirent entry number.  A
121662306a36Sopenharmony_ci   last cookie cache takes care of the common case of reading the
121762306a36Sopenharmony_ci   whole directory.
121862306a36Sopenharmony_ci */
121962306a36Sopenharmony_cistatic int nfs_readdir(struct file *file, struct dir_context *ctx)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	struct dentry	*dentry = file_dentry(file);
122262306a36Sopenharmony_ci	struct inode	*inode = d_inode(dentry);
122362306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
122462306a36Sopenharmony_ci	struct nfs_open_dir_context *dir_ctx = file->private_data;
122562306a36Sopenharmony_ci	struct nfs_readdir_descriptor *desc;
122662306a36Sopenharmony_ci	unsigned int cache_hits, cache_misses;
122762306a36Sopenharmony_ci	bool force_clear;
122862306a36Sopenharmony_ci	int res;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n",
123162306a36Sopenharmony_ci			file, (long long)ctx->pos);
123262306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	/*
123562306a36Sopenharmony_ci	 * ctx->pos points to the dirent entry number.
123662306a36Sopenharmony_ci	 * *desc->dir_cookie has the cookie for the next entry. We have
123762306a36Sopenharmony_ci	 * to either find the entry with the appropriate number or
123862306a36Sopenharmony_ci	 * revalidate the cookie.
123962306a36Sopenharmony_ci	 */
124062306a36Sopenharmony_ci	nfs_revalidate_mapping(inode, file->f_mapping);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	res = -ENOMEM;
124362306a36Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
124462306a36Sopenharmony_ci	if (!desc)
124562306a36Sopenharmony_ci		goto out;
124662306a36Sopenharmony_ci	desc->file = file;
124762306a36Sopenharmony_ci	desc->ctx = ctx;
124862306a36Sopenharmony_ci	desc->folio_index_max = -1;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	spin_lock(&file->f_lock);
125162306a36Sopenharmony_ci	desc->dir_cookie = dir_ctx->dir_cookie;
125262306a36Sopenharmony_ci	desc->folio_index = dir_ctx->page_index;
125362306a36Sopenharmony_ci	desc->last_cookie = dir_ctx->last_cookie;
125462306a36Sopenharmony_ci	desc->attr_gencount = dir_ctx->attr_gencount;
125562306a36Sopenharmony_ci	desc->eof = dir_ctx->eof;
125662306a36Sopenharmony_ci	nfs_set_dtsize(desc, dir_ctx->dtsize);
125762306a36Sopenharmony_ci	memcpy(desc->verf, dir_ctx->verf, sizeof(desc->verf));
125862306a36Sopenharmony_ci	cache_hits = atomic_xchg(&dir_ctx->cache_hits, 0);
125962306a36Sopenharmony_ci	cache_misses = atomic_xchg(&dir_ctx->cache_misses, 0);
126062306a36Sopenharmony_ci	force_clear = dir_ctx->force_clear;
126162306a36Sopenharmony_ci	spin_unlock(&file->f_lock);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (desc->eof) {
126462306a36Sopenharmony_ci		res = 0;
126562306a36Sopenharmony_ci		goto out_free;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	desc->plus = nfs_use_readdirplus(inode, ctx, cache_hits, cache_misses);
126962306a36Sopenharmony_ci	force_clear = nfs_readdir_handle_cache_misses(inode, desc, cache_misses,
127062306a36Sopenharmony_ci						      force_clear);
127162306a36Sopenharmony_ci	desc->clear_cache = force_clear;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	do {
127462306a36Sopenharmony_ci		res = readdir_search_pagecache(desc);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		if (res == -EBADCOOKIE) {
127762306a36Sopenharmony_ci			res = 0;
127862306a36Sopenharmony_ci			/* This means either end of directory */
127962306a36Sopenharmony_ci			if (desc->dir_cookie && !desc->eof) {
128062306a36Sopenharmony_ci				/* Or that the server has 'lost' a cookie */
128162306a36Sopenharmony_ci				res = uncached_readdir(desc);
128262306a36Sopenharmony_ci				if (res == 0)
128362306a36Sopenharmony_ci					continue;
128462306a36Sopenharmony_ci				if (res == -EBADCOOKIE || res == -ENOTSYNC)
128562306a36Sopenharmony_ci					res = 0;
128662306a36Sopenharmony_ci			}
128762306a36Sopenharmony_ci			break;
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci		if (res == -ETOOSMALL && desc->plus) {
129062306a36Sopenharmony_ci			nfs_zap_caches(inode);
129162306a36Sopenharmony_ci			desc->plus = false;
129262306a36Sopenharmony_ci			desc->eof = false;
129362306a36Sopenharmony_ci			continue;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci		if (res < 0)
129662306a36Sopenharmony_ci			break;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		nfs_do_filldir(desc, nfsi->cookieverf);
129962306a36Sopenharmony_ci		nfs_readdir_folio_unlock_and_put_cached(desc);
130062306a36Sopenharmony_ci		if (desc->folio_index == desc->folio_index_max)
130162306a36Sopenharmony_ci			desc->clear_cache = force_clear;
130262306a36Sopenharmony_ci	} while (!desc->eob && !desc->eof);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	spin_lock(&file->f_lock);
130562306a36Sopenharmony_ci	dir_ctx->dir_cookie = desc->dir_cookie;
130662306a36Sopenharmony_ci	dir_ctx->last_cookie = desc->last_cookie;
130762306a36Sopenharmony_ci	dir_ctx->attr_gencount = desc->attr_gencount;
130862306a36Sopenharmony_ci	dir_ctx->page_index = desc->folio_index;
130962306a36Sopenharmony_ci	dir_ctx->force_clear = force_clear;
131062306a36Sopenharmony_ci	dir_ctx->eof = desc->eof;
131162306a36Sopenharmony_ci	dir_ctx->dtsize = desc->dtsize;
131262306a36Sopenharmony_ci	memcpy(dir_ctx->verf, desc->verf, sizeof(dir_ctx->verf));
131362306a36Sopenharmony_ci	spin_unlock(&file->f_lock);
131462306a36Sopenharmony_ciout_free:
131562306a36Sopenharmony_ci	kfree(desc);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ciout:
131862306a36Sopenharmony_ci	dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res);
131962306a36Sopenharmony_ci	return res;
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	struct nfs_open_dir_context *dir_ctx = filp->private_data;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n",
132762306a36Sopenharmony_ci			filp, offset, whence);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	switch (whence) {
133062306a36Sopenharmony_ci	default:
133162306a36Sopenharmony_ci		return -EINVAL;
133262306a36Sopenharmony_ci	case SEEK_SET:
133362306a36Sopenharmony_ci		if (offset < 0)
133462306a36Sopenharmony_ci			return -EINVAL;
133562306a36Sopenharmony_ci		spin_lock(&filp->f_lock);
133662306a36Sopenharmony_ci		break;
133762306a36Sopenharmony_ci	case SEEK_CUR:
133862306a36Sopenharmony_ci		if (offset == 0)
133962306a36Sopenharmony_ci			return filp->f_pos;
134062306a36Sopenharmony_ci		spin_lock(&filp->f_lock);
134162306a36Sopenharmony_ci		offset += filp->f_pos;
134262306a36Sopenharmony_ci		if (offset < 0) {
134362306a36Sopenharmony_ci			spin_unlock(&filp->f_lock);
134462306a36Sopenharmony_ci			return -EINVAL;
134562306a36Sopenharmony_ci		}
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci	if (offset != filp->f_pos) {
134862306a36Sopenharmony_ci		filp->f_pos = offset;
134962306a36Sopenharmony_ci		dir_ctx->page_index = 0;
135062306a36Sopenharmony_ci		if (!nfs_readdir_use_cookie(filp)) {
135162306a36Sopenharmony_ci			dir_ctx->dir_cookie = 0;
135262306a36Sopenharmony_ci			dir_ctx->last_cookie = 0;
135362306a36Sopenharmony_ci		} else {
135462306a36Sopenharmony_ci			dir_ctx->dir_cookie = offset;
135562306a36Sopenharmony_ci			dir_ctx->last_cookie = offset;
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci		dir_ctx->eof = false;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci	spin_unlock(&filp->f_lock);
136062306a36Sopenharmony_ci	return offset;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci/*
136462306a36Sopenharmony_ci * All directory operations under NFS are synchronous, so fsync()
136562306a36Sopenharmony_ci * is a dummy operation.
136662306a36Sopenharmony_ci */
136762306a36Sopenharmony_cistatic int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
136862306a36Sopenharmony_ci			 int datasync)
136962306a36Sopenharmony_ci{
137062306a36Sopenharmony_ci	dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp, datasync);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	nfs_inc_stats(file_inode(filp), NFSIOS_VFSFSYNC);
137362306a36Sopenharmony_ci	return 0;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci/**
137762306a36Sopenharmony_ci * nfs_force_lookup_revalidate - Mark the directory as having changed
137862306a36Sopenharmony_ci * @dir: pointer to directory inode
137962306a36Sopenharmony_ci *
138062306a36Sopenharmony_ci * This forces the revalidation code in nfs_lookup_revalidate() to do a
138162306a36Sopenharmony_ci * full lookup on all child dentries of 'dir' whenever a change occurs
138262306a36Sopenharmony_ci * on the server that might have invalidated our dcache.
138362306a36Sopenharmony_ci *
138462306a36Sopenharmony_ci * Note that we reserve bit '0' as a tag to let us know when a dentry
138562306a36Sopenharmony_ci * was revalidated while holding a delegation on its inode.
138662306a36Sopenharmony_ci *
138762306a36Sopenharmony_ci * The caller should be holding dir->i_lock
138862306a36Sopenharmony_ci */
138962306a36Sopenharmony_civoid nfs_force_lookup_revalidate(struct inode *dir)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	NFS_I(dir)->cache_change_attribute += 2;
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci/**
139662306a36Sopenharmony_ci * nfs_verify_change_attribute - Detects NFS remote directory changes
139762306a36Sopenharmony_ci * @dir: pointer to parent directory inode
139862306a36Sopenharmony_ci * @verf: previously saved change attribute
139962306a36Sopenharmony_ci *
140062306a36Sopenharmony_ci * Return "false" if the verifiers doesn't match the change attribute.
140162306a36Sopenharmony_ci * This would usually indicate that the directory contents have changed on
140262306a36Sopenharmony_ci * the server, and that any dentries need revalidating.
140362306a36Sopenharmony_ci */
140462306a36Sopenharmony_cistatic bool nfs_verify_change_attribute(struct inode *dir, unsigned long verf)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	return (verf & ~1UL) == nfs_save_change_attribute(dir);
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_cistatic void nfs_set_verifier_delegated(unsigned long *verf)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	*verf |= 1UL;
141262306a36Sopenharmony_ci}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
141562306a36Sopenharmony_cistatic void nfs_unset_verifier_delegated(unsigned long *verf)
141662306a36Sopenharmony_ci{
141762306a36Sopenharmony_ci	*verf &= ~1UL;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_NFS_V4) */
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_cistatic bool nfs_test_verifier_delegated(unsigned long verf)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	return verf & 1;
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistatic bool nfs_verifier_is_delegated(struct dentry *dentry)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	return nfs_test_verifier_delegated(dentry->d_time);
142962306a36Sopenharmony_ci}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
143462306a36Sopenharmony_ci	struct inode *dir = d_inode(dentry->d_parent);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	if (!nfs_verify_change_attribute(dir, verf))
143762306a36Sopenharmony_ci		return;
143862306a36Sopenharmony_ci	if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
143962306a36Sopenharmony_ci		nfs_set_verifier_delegated(&verf);
144062306a36Sopenharmony_ci	dentry->d_time = verf;
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci/**
144462306a36Sopenharmony_ci * nfs_set_verifier - save a parent directory verifier in the dentry
144562306a36Sopenharmony_ci * @dentry: pointer to dentry
144662306a36Sopenharmony_ci * @verf: verifier to save
144762306a36Sopenharmony_ci *
144862306a36Sopenharmony_ci * Saves the parent directory verifier in @dentry. If the inode has
144962306a36Sopenharmony_ci * a delegation, we also tag the dentry as having been revalidated
145062306a36Sopenharmony_ci * while holding a delegation so that we know we don't have to
145162306a36Sopenharmony_ci * look it up again after a directory change.
145262306a36Sopenharmony_ci */
145362306a36Sopenharmony_civoid nfs_set_verifier(struct dentry *dentry, unsigned long verf)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	spin_lock(&dentry->d_lock);
145762306a36Sopenharmony_ci	nfs_set_verifier_locked(dentry, verf);
145862306a36Sopenharmony_ci	spin_unlock(&dentry->d_lock);
145962306a36Sopenharmony_ci}
146062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_set_verifier);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
146362306a36Sopenharmony_ci/**
146462306a36Sopenharmony_ci * nfs_clear_verifier_delegated - clear the dir verifier delegation tag
146562306a36Sopenharmony_ci * @inode: pointer to inode
146662306a36Sopenharmony_ci *
146762306a36Sopenharmony_ci * Iterates through the dentries in the inode alias list and clears
146862306a36Sopenharmony_ci * the tag used to indicate that the dentry has been revalidated
146962306a36Sopenharmony_ci * while holding a delegation.
147062306a36Sopenharmony_ci * This function is intended for use when the delegation is being
147162306a36Sopenharmony_ci * returned or revoked.
147262306a36Sopenharmony_ci */
147362306a36Sopenharmony_civoid nfs_clear_verifier_delegated(struct inode *inode)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct dentry *alias;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (!inode)
147862306a36Sopenharmony_ci		return;
147962306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
148062306a36Sopenharmony_ci	hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
148162306a36Sopenharmony_ci		spin_lock(&alias->d_lock);
148262306a36Sopenharmony_ci		nfs_unset_verifier_delegated(&alias->d_time);
148362306a36Sopenharmony_ci		spin_unlock(&alias->d_lock);
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);
148862306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_NFS_V4) */
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_cistatic int nfs_dentry_verify_change(struct inode *dir, struct dentry *dentry)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE) &&
149362306a36Sopenharmony_ci	    d_really_is_negative(dentry))
149462306a36Sopenharmony_ci		return dentry->d_time == inode_peek_iversion_raw(dir);
149562306a36Sopenharmony_ci	return nfs_verify_change_attribute(dir, dentry->d_time);
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci/*
149962306a36Sopenharmony_ci * A check for whether or not the parent directory has changed.
150062306a36Sopenharmony_ci * In the case it has, we assume that the dentries are untrustworthy
150162306a36Sopenharmony_ci * and may need to be looked up again.
150262306a36Sopenharmony_ci * If rcu_walk prevents us from performing a full check, return 0.
150362306a36Sopenharmony_ci */
150462306a36Sopenharmony_cistatic int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
150562306a36Sopenharmony_ci			      int rcu_walk)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	if (IS_ROOT(dentry))
150862306a36Sopenharmony_ci		return 1;
150962306a36Sopenharmony_ci	if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
151062306a36Sopenharmony_ci		return 0;
151162306a36Sopenharmony_ci	if (!nfs_dentry_verify_change(dir, dentry))
151262306a36Sopenharmony_ci		return 0;
151362306a36Sopenharmony_ci	/* Revalidate nfsi->cache_change_attribute before we declare a match */
151462306a36Sopenharmony_ci	if (nfs_mapping_need_revalidate_inode(dir)) {
151562306a36Sopenharmony_ci		if (rcu_walk)
151662306a36Sopenharmony_ci			return 0;
151762306a36Sopenharmony_ci		if (__nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
151862306a36Sopenharmony_ci			return 0;
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci	if (!nfs_dentry_verify_change(dir, dentry))
152162306a36Sopenharmony_ci		return 0;
152262306a36Sopenharmony_ci	return 1;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci/*
152662306a36Sopenharmony_ci * Use intent information to check whether or not we're going to do
152762306a36Sopenharmony_ci * an O_EXCL create using this path component.
152862306a36Sopenharmony_ci */
152962306a36Sopenharmony_cistatic int nfs_is_exclusive_create(struct inode *dir, unsigned int flags)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	if (NFS_PROTO(dir)->version == 2)
153262306a36Sopenharmony_ci		return 0;
153362306a36Sopenharmony_ci	return flags & LOOKUP_EXCL;
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci/*
153762306a36Sopenharmony_ci * Inode and filehandle revalidation for lookups.
153862306a36Sopenharmony_ci *
153962306a36Sopenharmony_ci * We force revalidation in the cases where the VFS sets LOOKUP_REVAL,
154062306a36Sopenharmony_ci * or if the intent information indicates that we're about to open this
154162306a36Sopenharmony_ci * particular file and the "nocto" mount flag is not set.
154262306a36Sopenharmony_ci *
154362306a36Sopenharmony_ci */
154462306a36Sopenharmony_cistatic
154562306a36Sopenharmony_ciint nfs_lookup_verify_inode(struct inode *inode, unsigned int flags)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
154862306a36Sopenharmony_ci	int ret;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	if (IS_AUTOMOUNT(inode))
155162306a36Sopenharmony_ci		return 0;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (flags & LOOKUP_OPEN) {
155462306a36Sopenharmony_ci		switch (inode->i_mode & S_IFMT) {
155562306a36Sopenharmony_ci		case S_IFREG:
155662306a36Sopenharmony_ci			/* A NFSv4 OPEN will revalidate later */
155762306a36Sopenharmony_ci			if (server->caps & NFS_CAP_ATOMIC_OPEN)
155862306a36Sopenharmony_ci				goto out;
155962306a36Sopenharmony_ci			fallthrough;
156062306a36Sopenharmony_ci		case S_IFDIR:
156162306a36Sopenharmony_ci			if (server->flags & NFS_MOUNT_NOCTO)
156262306a36Sopenharmony_ci				break;
156362306a36Sopenharmony_ci			/* NFS close-to-open cache consistency validation */
156462306a36Sopenharmony_ci			goto out_force;
156562306a36Sopenharmony_ci		}
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/* VFS wants an on-the-wire revalidation */
156962306a36Sopenharmony_ci	if (flags & LOOKUP_REVAL)
157062306a36Sopenharmony_ci		goto out_force;
157162306a36Sopenharmony_ciout:
157262306a36Sopenharmony_ci	if (inode->i_nlink > 0 ||
157362306a36Sopenharmony_ci	    (inode->i_nlink == 0 &&
157462306a36Sopenharmony_ci	     test_bit(NFS_INO_PRESERVE_UNLINKED, &NFS_I(inode)->flags)))
157562306a36Sopenharmony_ci		return 0;
157662306a36Sopenharmony_ci	else
157762306a36Sopenharmony_ci		return -ESTALE;
157862306a36Sopenharmony_ciout_force:
157962306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
158062306a36Sopenharmony_ci		return -ECHILD;
158162306a36Sopenharmony_ci	ret = __nfs_revalidate_inode(server, inode);
158262306a36Sopenharmony_ci	if (ret != 0)
158362306a36Sopenharmony_ci		return ret;
158462306a36Sopenharmony_ci	goto out;
158562306a36Sopenharmony_ci}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_cistatic void nfs_mark_dir_for_revalidate(struct inode *inode)
158862306a36Sopenharmony_ci{
158962306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
159062306a36Sopenharmony_ci	nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE);
159162306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci/*
159562306a36Sopenharmony_ci * We judge how long we want to trust negative
159662306a36Sopenharmony_ci * dentries by looking at the parent inode mtime.
159762306a36Sopenharmony_ci *
159862306a36Sopenharmony_ci * If parent mtime has changed, we revalidate, else we wait for a
159962306a36Sopenharmony_ci * period corresponding to the parent's attribute cache timeout value.
160062306a36Sopenharmony_ci *
160162306a36Sopenharmony_ci * If LOOKUP_RCU prevents us from performing a full check, return 1
160262306a36Sopenharmony_ci * suggesting a reval is needed.
160362306a36Sopenharmony_ci *
160462306a36Sopenharmony_ci * Note that when creating a new file, or looking up a rename target,
160562306a36Sopenharmony_ci * then it shouldn't be necessary to revalidate a negative dentry.
160662306a36Sopenharmony_ci */
160762306a36Sopenharmony_cistatic inline
160862306a36Sopenharmony_ciint nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
160962306a36Sopenharmony_ci		       unsigned int flags)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
161262306a36Sopenharmony_ci		return 0;
161362306a36Sopenharmony_ci	if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
161462306a36Sopenharmony_ci		return 1;
161562306a36Sopenharmony_ci	/* Case insensitive server? Revalidate negative dentries */
161662306a36Sopenharmony_ci	if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
161762306a36Sopenharmony_ci		return 1;
161862306a36Sopenharmony_ci	return !nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU);
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic int
162262306a36Sopenharmony_cinfs_lookup_revalidate_done(struct inode *dir, struct dentry *dentry,
162362306a36Sopenharmony_ci			   struct inode *inode, int error)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	switch (error) {
162662306a36Sopenharmony_ci	case 1:
162762306a36Sopenharmony_ci		break;
162862306a36Sopenharmony_ci	case 0:
162962306a36Sopenharmony_ci		/*
163062306a36Sopenharmony_ci		 * We can't d_drop the root of a disconnected tree:
163162306a36Sopenharmony_ci		 * its d_hash is on the s_anon list and d_drop() would hide
163262306a36Sopenharmony_ci		 * it from shrink_dcache_for_unmount(), leading to busy
163362306a36Sopenharmony_ci		 * inodes on unmount and further oopses.
163462306a36Sopenharmony_ci		 */
163562306a36Sopenharmony_ci		if (inode && IS_ROOT(dentry))
163662306a36Sopenharmony_ci			error = 1;
163762306a36Sopenharmony_ci		break;
163862306a36Sopenharmony_ci	}
163962306a36Sopenharmony_ci	trace_nfs_lookup_revalidate_exit(dir, dentry, 0, error);
164062306a36Sopenharmony_ci	return error;
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic int
164462306a36Sopenharmony_cinfs_lookup_revalidate_negative(struct inode *dir, struct dentry *dentry,
164562306a36Sopenharmony_ci			       unsigned int flags)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	int ret = 1;
164862306a36Sopenharmony_ci	if (nfs_neg_need_reval(dir, dentry, flags)) {
164962306a36Sopenharmony_ci		if (flags & LOOKUP_RCU)
165062306a36Sopenharmony_ci			return -ECHILD;
165162306a36Sopenharmony_ci		ret = 0;
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci	return nfs_lookup_revalidate_done(dir, dentry, NULL, ret);
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic int
165762306a36Sopenharmony_cinfs_lookup_revalidate_delegated(struct inode *dir, struct dentry *dentry,
165862306a36Sopenharmony_ci				struct inode *inode)
165962306a36Sopenharmony_ci{
166062306a36Sopenharmony_ci	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
166162306a36Sopenharmony_ci	return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
166262306a36Sopenharmony_ci}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_cistatic int nfs_lookup_revalidate_dentry(struct inode *dir,
166562306a36Sopenharmony_ci					struct dentry *dentry,
166662306a36Sopenharmony_ci					struct inode *inode, unsigned int flags)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	struct nfs_fh *fhandle;
166962306a36Sopenharmony_ci	struct nfs_fattr *fattr;
167062306a36Sopenharmony_ci	unsigned long dir_verifier;
167162306a36Sopenharmony_ci	int ret;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	ret = -ENOMEM;
167662306a36Sopenharmony_ci	fhandle = nfs_alloc_fhandle();
167762306a36Sopenharmony_ci	fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
167862306a36Sopenharmony_ci	if (fhandle == NULL || fattr == NULL)
167962306a36Sopenharmony_ci		goto out;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	dir_verifier = nfs_save_change_attribute(dir);
168262306a36Sopenharmony_ci	ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
168362306a36Sopenharmony_ci	if (ret < 0) {
168462306a36Sopenharmony_ci		switch (ret) {
168562306a36Sopenharmony_ci		case -ESTALE:
168662306a36Sopenharmony_ci		case -ENOENT:
168762306a36Sopenharmony_ci			ret = 0;
168862306a36Sopenharmony_ci			break;
168962306a36Sopenharmony_ci		case -ETIMEDOUT:
169062306a36Sopenharmony_ci			if (NFS_SERVER(inode)->flags & NFS_MOUNT_SOFTREVAL)
169162306a36Sopenharmony_ci				ret = 1;
169262306a36Sopenharmony_ci		}
169362306a36Sopenharmony_ci		goto out;
169462306a36Sopenharmony_ci	}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	/* Request help from readdirplus */
169762306a36Sopenharmony_ci	nfs_lookup_advise_force_readdirplus(dir, flags);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	ret = 0;
170062306a36Sopenharmony_ci	if (nfs_compare_fh(NFS_FH(inode), fhandle))
170162306a36Sopenharmony_ci		goto out;
170262306a36Sopenharmony_ci	if (nfs_refresh_inode(inode, fattr) < 0)
170362306a36Sopenharmony_ci		goto out;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	nfs_setsecurity(inode, fattr);
170662306a36Sopenharmony_ci	nfs_set_verifier(dentry, dir_verifier);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	ret = 1;
170962306a36Sopenharmony_ciout:
171062306a36Sopenharmony_ci	nfs_free_fattr(fattr);
171162306a36Sopenharmony_ci	nfs_free_fhandle(fhandle);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	/*
171462306a36Sopenharmony_ci	 * If the lookup failed despite the dentry change attribute being
171562306a36Sopenharmony_ci	 * a match, then we should revalidate the directory cache.
171662306a36Sopenharmony_ci	 */
171762306a36Sopenharmony_ci	if (!ret && nfs_dentry_verify_change(dir, dentry))
171862306a36Sopenharmony_ci		nfs_mark_dir_for_revalidate(dir);
171962306a36Sopenharmony_ci	return nfs_lookup_revalidate_done(dir, dentry, inode, ret);
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci/*
172362306a36Sopenharmony_ci * This is called every time the dcache has a lookup hit,
172462306a36Sopenharmony_ci * and we should check whether we can really trust that
172562306a36Sopenharmony_ci * lookup.
172662306a36Sopenharmony_ci *
172762306a36Sopenharmony_ci * NOTE! The hit can be a negative hit too, don't assume
172862306a36Sopenharmony_ci * we have an inode!
172962306a36Sopenharmony_ci *
173062306a36Sopenharmony_ci * If the parent directory is seen to have changed, we throw out the
173162306a36Sopenharmony_ci * cached dentry and do a new lookup.
173262306a36Sopenharmony_ci */
173362306a36Sopenharmony_cistatic int
173462306a36Sopenharmony_cinfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
173562306a36Sopenharmony_ci			 unsigned int flags)
173662306a36Sopenharmony_ci{
173762306a36Sopenharmony_ci	struct inode *inode;
173862306a36Sopenharmony_ci	int error;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
174162306a36Sopenharmony_ci	inode = d_inode(dentry);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	if (!inode)
174462306a36Sopenharmony_ci		return nfs_lookup_revalidate_negative(dir, dentry, flags);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	if (is_bad_inode(inode)) {
174762306a36Sopenharmony_ci		dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
174862306a36Sopenharmony_ci				__func__, dentry);
174962306a36Sopenharmony_ci		goto out_bad;
175062306a36Sopenharmony_ci	}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	if ((flags & LOOKUP_RENAME_TARGET) && d_count(dentry) < 2 &&
175362306a36Sopenharmony_ci	    nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
175462306a36Sopenharmony_ci		goto out_bad;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	if (nfs_verifier_is_delegated(dentry))
175762306a36Sopenharmony_ci		return nfs_lookup_revalidate_delegated(dir, dentry, inode);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	/* Force a full look up iff the parent directory has changed */
176062306a36Sopenharmony_ci	if (!(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) &&
176162306a36Sopenharmony_ci	    nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
176262306a36Sopenharmony_ci		error = nfs_lookup_verify_inode(inode, flags);
176362306a36Sopenharmony_ci		if (error) {
176462306a36Sopenharmony_ci			if (error == -ESTALE)
176562306a36Sopenharmony_ci				nfs_mark_dir_for_revalidate(dir);
176662306a36Sopenharmony_ci			goto out_bad;
176762306a36Sopenharmony_ci		}
176862306a36Sopenharmony_ci		goto out_valid;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
177262306a36Sopenharmony_ci		return -ECHILD;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	if (NFS_STALE(inode))
177562306a36Sopenharmony_ci		goto out_bad;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
177862306a36Sopenharmony_ciout_valid:
177962306a36Sopenharmony_ci	return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
178062306a36Sopenharmony_ciout_bad:
178162306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
178262306a36Sopenharmony_ci		return -ECHILD;
178362306a36Sopenharmony_ci	return nfs_lookup_revalidate_done(dir, dentry, inode, 0);
178462306a36Sopenharmony_ci}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_cistatic int
178762306a36Sopenharmony_ci__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
178862306a36Sopenharmony_ci			int (*reval)(struct inode *, struct dentry *, unsigned int))
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci	struct dentry *parent;
179162306a36Sopenharmony_ci	struct inode *dir;
179262306a36Sopenharmony_ci	int ret;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	if (flags & LOOKUP_RCU) {
179562306a36Sopenharmony_ci		if (dentry->d_fsdata == NFS_FSDATA_BLOCKED)
179662306a36Sopenharmony_ci			return -ECHILD;
179762306a36Sopenharmony_ci		parent = READ_ONCE(dentry->d_parent);
179862306a36Sopenharmony_ci		dir = d_inode_rcu(parent);
179962306a36Sopenharmony_ci		if (!dir)
180062306a36Sopenharmony_ci			return -ECHILD;
180162306a36Sopenharmony_ci		ret = reval(dir, dentry, flags);
180262306a36Sopenharmony_ci		if (parent != READ_ONCE(dentry->d_parent))
180362306a36Sopenharmony_ci			return -ECHILD;
180462306a36Sopenharmony_ci	} else {
180562306a36Sopenharmony_ci		/* Wait for unlink to complete */
180662306a36Sopenharmony_ci		wait_var_event(&dentry->d_fsdata,
180762306a36Sopenharmony_ci			       dentry->d_fsdata != NFS_FSDATA_BLOCKED);
180862306a36Sopenharmony_ci		parent = dget_parent(dentry);
180962306a36Sopenharmony_ci		ret = reval(d_inode(parent), dentry, flags);
181062306a36Sopenharmony_ci		dput(parent);
181162306a36Sopenharmony_ci	}
181262306a36Sopenharmony_ci	return ret;
181362306a36Sopenharmony_ci}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_cistatic int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
181662306a36Sopenharmony_ci{
181762306a36Sopenharmony_ci	return __nfs_lookup_revalidate(dentry, flags, nfs_do_lookup_revalidate);
181862306a36Sopenharmony_ci}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci/*
182162306a36Sopenharmony_ci * A weaker form of d_revalidate for revalidating just the d_inode(dentry)
182262306a36Sopenharmony_ci * when we don't really care about the dentry name. This is called when a
182362306a36Sopenharmony_ci * pathwalk ends on a dentry that was not found via a normal lookup in the
182462306a36Sopenharmony_ci * parent dir (e.g.: ".", "..", procfs symlinks or mountpoint traversals).
182562306a36Sopenharmony_ci *
182662306a36Sopenharmony_ci * In this situation, we just want to verify that the inode itself is OK
182762306a36Sopenharmony_ci * since the dentry might have changed on the server.
182862306a36Sopenharmony_ci */
182962306a36Sopenharmony_cistatic int nfs_weak_revalidate(struct dentry *dentry, unsigned int flags)
183062306a36Sopenharmony_ci{
183162306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
183262306a36Sopenharmony_ci	int error = 0;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	/*
183562306a36Sopenharmony_ci	 * I believe we can only get a negative dentry here in the case of a
183662306a36Sopenharmony_ci	 * procfs-style symlink. Just assume it's correct for now, but we may
183762306a36Sopenharmony_ci	 * eventually need to do something more here.
183862306a36Sopenharmony_ci	 */
183962306a36Sopenharmony_ci	if (!inode) {
184062306a36Sopenharmony_ci		dfprintk(LOOKUPCACHE, "%s: %pd2 has negative inode\n",
184162306a36Sopenharmony_ci				__func__, dentry);
184262306a36Sopenharmony_ci		return 1;
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	if (is_bad_inode(inode)) {
184662306a36Sopenharmony_ci		dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
184762306a36Sopenharmony_ci				__func__, dentry);
184862306a36Sopenharmony_ci		return 0;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	error = nfs_lookup_verify_inode(inode, flags);
185262306a36Sopenharmony_ci	dfprintk(LOOKUPCACHE, "NFS: %s: inode %lu is %s\n",
185362306a36Sopenharmony_ci			__func__, inode->i_ino, error ? "invalid" : "valid");
185462306a36Sopenharmony_ci	return !error;
185562306a36Sopenharmony_ci}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci/*
185862306a36Sopenharmony_ci * This is called from dput() when d_count is going to 0.
185962306a36Sopenharmony_ci */
186062306a36Sopenharmony_cistatic int nfs_dentry_delete(const struct dentry *dentry)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	dfprintk(VFS, "NFS: dentry_delete(%pd2, %x)\n",
186362306a36Sopenharmony_ci		dentry, dentry->d_flags);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	/* Unhash any dentry with a stale inode */
186662306a36Sopenharmony_ci	if (d_really_is_positive(dentry) && NFS_STALE(d_inode(dentry)))
186762306a36Sopenharmony_ci		return 1;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
187062306a36Sopenharmony_ci		/* Unhash it, so that ->d_iput() would be called */
187162306a36Sopenharmony_ci		return 1;
187262306a36Sopenharmony_ci	}
187362306a36Sopenharmony_ci	if (!(dentry->d_sb->s_flags & SB_ACTIVE)) {
187462306a36Sopenharmony_ci		/* Unhash it, so that ancestors of killed async unlink
187562306a36Sopenharmony_ci		 * files will be cleaned up during umount */
187662306a36Sopenharmony_ci		return 1;
187762306a36Sopenharmony_ci	}
187862306a36Sopenharmony_ci	return 0;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci/* Ensure that we revalidate inode->i_nlink */
188362306a36Sopenharmony_cistatic void nfs_drop_nlink(struct inode *inode)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
188662306a36Sopenharmony_ci	/* drop the inode if we're reasonably sure this is the last link */
188762306a36Sopenharmony_ci	if (inode->i_nlink > 0)
188862306a36Sopenharmony_ci		drop_nlink(inode);
188962306a36Sopenharmony_ci	NFS_I(inode)->attr_gencount = nfs_inc_attr_generation_counter();
189062306a36Sopenharmony_ci	nfs_set_cache_invalid(
189162306a36Sopenharmony_ci		inode, NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
189262306a36Sopenharmony_ci			       NFS_INO_INVALID_NLINK);
189362306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
189462306a36Sopenharmony_ci}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci/*
189762306a36Sopenharmony_ci * Called when the dentry loses inode.
189862306a36Sopenharmony_ci * We use it to clean up silly-renamed files.
189962306a36Sopenharmony_ci */
190062306a36Sopenharmony_cistatic void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
190162306a36Sopenharmony_ci{
190262306a36Sopenharmony_ci	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
190362306a36Sopenharmony_ci		nfs_complete_unlink(dentry, inode);
190462306a36Sopenharmony_ci		nfs_drop_nlink(inode);
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci	iput(inode);
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cistatic void nfs_d_release(struct dentry *dentry)
191062306a36Sopenharmony_ci{
191162306a36Sopenharmony_ci	/* free cached devname value, if it survived that far */
191262306a36Sopenharmony_ci	if (unlikely(dentry->d_fsdata)) {
191362306a36Sopenharmony_ci		if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
191462306a36Sopenharmony_ci			WARN_ON(1);
191562306a36Sopenharmony_ci		else
191662306a36Sopenharmony_ci			kfree(dentry->d_fsdata);
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ciconst struct dentry_operations nfs_dentry_operations = {
192162306a36Sopenharmony_ci	.d_revalidate	= nfs_lookup_revalidate,
192262306a36Sopenharmony_ci	.d_weak_revalidate	= nfs_weak_revalidate,
192362306a36Sopenharmony_ci	.d_delete	= nfs_dentry_delete,
192462306a36Sopenharmony_ci	.d_iput		= nfs_dentry_iput,
192562306a36Sopenharmony_ci	.d_automount	= nfs_d_automount,
192662306a36Sopenharmony_ci	.d_release	= nfs_d_release,
192762306a36Sopenharmony_ci};
192862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_dentry_operations);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_cistruct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
193162306a36Sopenharmony_ci{
193262306a36Sopenharmony_ci	struct dentry *res;
193362306a36Sopenharmony_ci	struct inode *inode = NULL;
193462306a36Sopenharmony_ci	struct nfs_fh *fhandle = NULL;
193562306a36Sopenharmony_ci	struct nfs_fattr *fattr = NULL;
193662306a36Sopenharmony_ci	unsigned long dir_verifier;
193762306a36Sopenharmony_ci	int error;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry);
194062306a36Sopenharmony_ci	nfs_inc_stats(dir, NFSIOS_VFSLOOKUP);
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	if (unlikely(dentry->d_name.len > NFS_SERVER(dir)->namelen))
194362306a36Sopenharmony_ci		return ERR_PTR(-ENAMETOOLONG);
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/*
194662306a36Sopenharmony_ci	 * If we're doing an exclusive create, optimize away the lookup
194762306a36Sopenharmony_ci	 * but don't hash the dentry.
194862306a36Sopenharmony_ci	 */
194962306a36Sopenharmony_ci	if (nfs_is_exclusive_create(dir, flags) || flags & LOOKUP_RENAME_TARGET)
195062306a36Sopenharmony_ci		return NULL;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	res = ERR_PTR(-ENOMEM);
195362306a36Sopenharmony_ci	fhandle = nfs_alloc_fhandle();
195462306a36Sopenharmony_ci	fattr = nfs_alloc_fattr_with_label(NFS_SERVER(dir));
195562306a36Sopenharmony_ci	if (fhandle == NULL || fattr == NULL)
195662306a36Sopenharmony_ci		goto out;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	dir_verifier = nfs_save_change_attribute(dir);
195962306a36Sopenharmony_ci	trace_nfs_lookup_enter(dir, dentry, flags);
196062306a36Sopenharmony_ci	error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
196162306a36Sopenharmony_ci	if (error == -ENOENT) {
196262306a36Sopenharmony_ci		if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
196362306a36Sopenharmony_ci			dir_verifier = inode_peek_iversion_raw(dir);
196462306a36Sopenharmony_ci		goto no_entry;
196562306a36Sopenharmony_ci	}
196662306a36Sopenharmony_ci	if (error < 0) {
196762306a36Sopenharmony_ci		res = ERR_PTR(error);
196862306a36Sopenharmony_ci		goto out;
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
197162306a36Sopenharmony_ci	res = ERR_CAST(inode);
197262306a36Sopenharmony_ci	if (IS_ERR(res))
197362306a36Sopenharmony_ci		goto out;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	/* Notify readdir to use READDIRPLUS */
197662306a36Sopenharmony_ci	nfs_lookup_advise_force_readdirplus(dir, flags);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_cino_entry:
197962306a36Sopenharmony_ci	res = d_splice_alias(inode, dentry);
198062306a36Sopenharmony_ci	if (res != NULL) {
198162306a36Sopenharmony_ci		if (IS_ERR(res))
198262306a36Sopenharmony_ci			goto out;
198362306a36Sopenharmony_ci		dentry = res;
198462306a36Sopenharmony_ci	}
198562306a36Sopenharmony_ci	nfs_set_verifier(dentry, dir_verifier);
198662306a36Sopenharmony_ciout:
198762306a36Sopenharmony_ci	trace_nfs_lookup_exit(dir, dentry, flags, PTR_ERR_OR_ZERO(res));
198862306a36Sopenharmony_ci	nfs_free_fattr(fattr);
198962306a36Sopenharmony_ci	nfs_free_fhandle(fhandle);
199062306a36Sopenharmony_ci	return res;
199162306a36Sopenharmony_ci}
199262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_lookup);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_civoid nfs_d_prune_case_insensitive_aliases(struct inode *inode)
199562306a36Sopenharmony_ci{
199662306a36Sopenharmony_ci	/* Case insensitive server? Revalidate dentries */
199762306a36Sopenharmony_ci	if (inode && nfs_server_capable(inode, NFS_CAP_CASE_INSENSITIVE))
199862306a36Sopenharmony_ci		d_prune_aliases(inode);
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_d_prune_case_insensitive_aliases);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
200362306a36Sopenharmony_cistatic int nfs4_lookup_revalidate(struct dentry *, unsigned int);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ciconst struct dentry_operations nfs4_dentry_operations = {
200662306a36Sopenharmony_ci	.d_revalidate	= nfs4_lookup_revalidate,
200762306a36Sopenharmony_ci	.d_weak_revalidate	= nfs_weak_revalidate,
200862306a36Sopenharmony_ci	.d_delete	= nfs_dentry_delete,
200962306a36Sopenharmony_ci	.d_iput		= nfs_dentry_iput,
201062306a36Sopenharmony_ci	.d_automount	= nfs_d_automount,
201162306a36Sopenharmony_ci	.d_release	= nfs_d_release,
201262306a36Sopenharmony_ci};
201362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_dentry_operations);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_cistatic struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags, struct file *filp)
201662306a36Sopenharmony_ci{
201762306a36Sopenharmony_ci	return alloc_nfs_open_context(dentry, flags_to_mode(open_flags), filp);
201862306a36Sopenharmony_ci}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_cistatic int do_open(struct inode *inode, struct file *filp)
202162306a36Sopenharmony_ci{
202262306a36Sopenharmony_ci	nfs_fscache_open_file(inode, filp);
202362306a36Sopenharmony_ci	return 0;
202462306a36Sopenharmony_ci}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_cistatic int nfs_finish_open(struct nfs_open_context *ctx,
202762306a36Sopenharmony_ci			   struct dentry *dentry,
202862306a36Sopenharmony_ci			   struct file *file, unsigned open_flags)
202962306a36Sopenharmony_ci{
203062306a36Sopenharmony_ci	int err;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	err = finish_open(file, dentry, do_open);
203362306a36Sopenharmony_ci	if (err)
203462306a36Sopenharmony_ci		goto out;
203562306a36Sopenharmony_ci	if (S_ISREG(file_inode(file)->i_mode))
203662306a36Sopenharmony_ci		nfs_file_set_open_context(file, ctx);
203762306a36Sopenharmony_ci	else
203862306a36Sopenharmony_ci		err = -EOPENSTALE;
203962306a36Sopenharmony_ciout:
204062306a36Sopenharmony_ci	return err;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ciint nfs_atomic_open(struct inode *dir, struct dentry *dentry,
204462306a36Sopenharmony_ci		    struct file *file, unsigned open_flags,
204562306a36Sopenharmony_ci		    umode_t mode)
204662306a36Sopenharmony_ci{
204762306a36Sopenharmony_ci	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
204862306a36Sopenharmony_ci	struct nfs_open_context *ctx;
204962306a36Sopenharmony_ci	struct dentry *res;
205062306a36Sopenharmony_ci	struct iattr attr = { .ia_valid = ATTR_OPEN };
205162306a36Sopenharmony_ci	struct inode *inode;
205262306a36Sopenharmony_ci	unsigned int lookup_flags = 0;
205362306a36Sopenharmony_ci	unsigned long dir_verifier;
205462306a36Sopenharmony_ci	bool switched = false;
205562306a36Sopenharmony_ci	int created = 0;
205662306a36Sopenharmony_ci	int err;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	/* Expect a negative dentry */
205962306a36Sopenharmony_ci	BUG_ON(d_inode(dentry));
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	dfprintk(VFS, "NFS: atomic_open(%s/%lu), %pd\n",
206262306a36Sopenharmony_ci			dir->i_sb->s_id, dir->i_ino, dentry);
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	err = nfs_check_flags(open_flags);
206562306a36Sopenharmony_ci	if (err)
206662306a36Sopenharmony_ci		return err;
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	/* NFS only supports OPEN on regular files */
206962306a36Sopenharmony_ci	if ((open_flags & O_DIRECTORY)) {
207062306a36Sopenharmony_ci		if (!d_in_lookup(dentry)) {
207162306a36Sopenharmony_ci			/*
207262306a36Sopenharmony_ci			 * Hashed negative dentry with O_DIRECTORY: dentry was
207362306a36Sopenharmony_ci			 * revalidated and is fine, no need to perform lookup
207462306a36Sopenharmony_ci			 * again
207562306a36Sopenharmony_ci			 */
207662306a36Sopenharmony_ci			return -ENOENT;
207762306a36Sopenharmony_ci		}
207862306a36Sopenharmony_ci		lookup_flags = LOOKUP_OPEN|LOOKUP_DIRECTORY;
207962306a36Sopenharmony_ci		goto no_open;
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
208362306a36Sopenharmony_ci		return -ENAMETOOLONG;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	if (open_flags & O_CREAT) {
208662306a36Sopenharmony_ci		struct nfs_server *server = NFS_SERVER(dir);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci		if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
208962306a36Sopenharmony_ci			mode &= ~current_umask();
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci		attr.ia_valid |= ATTR_MODE;
209262306a36Sopenharmony_ci		attr.ia_mode = mode;
209362306a36Sopenharmony_ci	}
209462306a36Sopenharmony_ci	if (open_flags & O_TRUNC) {
209562306a36Sopenharmony_ci		attr.ia_valid |= ATTR_SIZE;
209662306a36Sopenharmony_ci		attr.ia_size = 0;
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	if (!(open_flags & O_CREAT) && !d_in_lookup(dentry)) {
210062306a36Sopenharmony_ci		d_drop(dentry);
210162306a36Sopenharmony_ci		switched = true;
210262306a36Sopenharmony_ci		dentry = d_alloc_parallel(dentry->d_parent,
210362306a36Sopenharmony_ci					  &dentry->d_name, &wq);
210462306a36Sopenharmony_ci		if (IS_ERR(dentry))
210562306a36Sopenharmony_ci			return PTR_ERR(dentry);
210662306a36Sopenharmony_ci		if (unlikely(!d_in_lookup(dentry)))
210762306a36Sopenharmony_ci			return finish_no_open(file, dentry);
210862306a36Sopenharmony_ci	}
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	ctx = create_nfs_open_context(dentry, open_flags, file);
211162306a36Sopenharmony_ci	err = PTR_ERR(ctx);
211262306a36Sopenharmony_ci	if (IS_ERR(ctx))
211362306a36Sopenharmony_ci		goto out;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	trace_nfs_atomic_open_enter(dir, ctx, open_flags);
211662306a36Sopenharmony_ci	inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr, &created);
211762306a36Sopenharmony_ci	if (created)
211862306a36Sopenharmony_ci		file->f_mode |= FMODE_CREATED;
211962306a36Sopenharmony_ci	if (IS_ERR(inode)) {
212062306a36Sopenharmony_ci		err = PTR_ERR(inode);
212162306a36Sopenharmony_ci		trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
212262306a36Sopenharmony_ci		put_nfs_open_context(ctx);
212362306a36Sopenharmony_ci		d_drop(dentry);
212462306a36Sopenharmony_ci		switch (err) {
212562306a36Sopenharmony_ci		case -ENOENT:
212662306a36Sopenharmony_ci			d_splice_alias(NULL, dentry);
212762306a36Sopenharmony_ci			if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
212862306a36Sopenharmony_ci				dir_verifier = inode_peek_iversion_raw(dir);
212962306a36Sopenharmony_ci			else
213062306a36Sopenharmony_ci				dir_verifier = nfs_save_change_attribute(dir);
213162306a36Sopenharmony_ci			nfs_set_verifier(dentry, dir_verifier);
213262306a36Sopenharmony_ci			break;
213362306a36Sopenharmony_ci		case -EISDIR:
213462306a36Sopenharmony_ci		case -ENOTDIR:
213562306a36Sopenharmony_ci			goto no_open;
213662306a36Sopenharmony_ci		case -ELOOP:
213762306a36Sopenharmony_ci			if (!(open_flags & O_NOFOLLOW))
213862306a36Sopenharmony_ci				goto no_open;
213962306a36Sopenharmony_ci			break;
214062306a36Sopenharmony_ci			/* case -EINVAL: */
214162306a36Sopenharmony_ci		default:
214262306a36Sopenharmony_ci			break;
214362306a36Sopenharmony_ci		}
214462306a36Sopenharmony_ci		goto out;
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci	file->f_mode |= FMODE_CAN_ODIRECT;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	err = nfs_finish_open(ctx, ctx->dentry, file, open_flags);
214962306a36Sopenharmony_ci	trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
215062306a36Sopenharmony_ci	put_nfs_open_context(ctx);
215162306a36Sopenharmony_ciout:
215262306a36Sopenharmony_ci	if (unlikely(switched)) {
215362306a36Sopenharmony_ci		d_lookup_done(dentry);
215462306a36Sopenharmony_ci		dput(dentry);
215562306a36Sopenharmony_ci	}
215662306a36Sopenharmony_ci	return err;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_cino_open:
215962306a36Sopenharmony_ci	res = nfs_lookup(dir, dentry, lookup_flags);
216062306a36Sopenharmony_ci	if (!res) {
216162306a36Sopenharmony_ci		inode = d_inode(dentry);
216262306a36Sopenharmony_ci		if ((lookup_flags & LOOKUP_DIRECTORY) && inode &&
216362306a36Sopenharmony_ci		    !(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
216462306a36Sopenharmony_ci			res = ERR_PTR(-ENOTDIR);
216562306a36Sopenharmony_ci		else if (inode && S_ISREG(inode->i_mode))
216662306a36Sopenharmony_ci			res = ERR_PTR(-EOPENSTALE);
216762306a36Sopenharmony_ci	} else if (!IS_ERR(res)) {
216862306a36Sopenharmony_ci		inode = d_inode(res);
216962306a36Sopenharmony_ci		if ((lookup_flags & LOOKUP_DIRECTORY) && inode &&
217062306a36Sopenharmony_ci		    !(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) {
217162306a36Sopenharmony_ci			dput(res);
217262306a36Sopenharmony_ci			res = ERR_PTR(-ENOTDIR);
217362306a36Sopenharmony_ci		} else if (inode && S_ISREG(inode->i_mode)) {
217462306a36Sopenharmony_ci			dput(res);
217562306a36Sopenharmony_ci			res = ERR_PTR(-EOPENSTALE);
217662306a36Sopenharmony_ci		}
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci	if (switched) {
217962306a36Sopenharmony_ci		d_lookup_done(dentry);
218062306a36Sopenharmony_ci		if (!res)
218162306a36Sopenharmony_ci			res = dentry;
218262306a36Sopenharmony_ci		else
218362306a36Sopenharmony_ci			dput(dentry);
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci	if (IS_ERR(res))
218662306a36Sopenharmony_ci		return PTR_ERR(res);
218762306a36Sopenharmony_ci	return finish_no_open(file, res);
218862306a36Sopenharmony_ci}
218962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_atomic_open);
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_cistatic int
219262306a36Sopenharmony_cinfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
219362306a36Sopenharmony_ci			  unsigned int flags)
219462306a36Sopenharmony_ci{
219562306a36Sopenharmony_ci	struct inode *inode;
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
219862306a36Sopenharmony_ci		goto full_reval;
219962306a36Sopenharmony_ci	if (d_mountpoint(dentry))
220062306a36Sopenharmony_ci		goto full_reval;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	inode = d_inode(dentry);
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/* We can't create new files in nfs_open_revalidate(), so we
220562306a36Sopenharmony_ci	 * optimize away revalidation of negative dentries.
220662306a36Sopenharmony_ci	 */
220762306a36Sopenharmony_ci	if (inode == NULL)
220862306a36Sopenharmony_ci		goto full_reval;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	if (nfs_verifier_is_delegated(dentry))
221162306a36Sopenharmony_ci		return nfs_lookup_revalidate_delegated(dir, dentry, inode);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	/* NFS only supports OPEN on regular files */
221462306a36Sopenharmony_ci	if (!S_ISREG(inode->i_mode))
221562306a36Sopenharmony_ci		goto full_reval;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	/* We cannot do exclusive creation on a positive dentry */
221862306a36Sopenharmony_ci	if (flags & (LOOKUP_EXCL | LOOKUP_REVAL))
221962306a36Sopenharmony_ci		goto reval_dentry;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	/* Check if the directory changed */
222262306a36Sopenharmony_ci	if (!nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU))
222362306a36Sopenharmony_ci		goto reval_dentry;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	/* Let f_op->open() actually open (and revalidate) the file */
222662306a36Sopenharmony_ci	return 1;
222762306a36Sopenharmony_cireval_dentry:
222862306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
222962306a36Sopenharmony_ci		return -ECHILD;
223062306a36Sopenharmony_ci	return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_cifull_reval:
223362306a36Sopenharmony_ci	return nfs_do_lookup_revalidate(dir, dentry, flags);
223462306a36Sopenharmony_ci}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_cistatic int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	return __nfs_lookup_revalidate(dentry, flags,
223962306a36Sopenharmony_ci			nfs4_do_lookup_revalidate);
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci#endif /* CONFIG_NFSV4 */
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_cistruct dentry *
224562306a36Sopenharmony_cinfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
224662306a36Sopenharmony_ci				struct nfs_fattr *fattr)
224762306a36Sopenharmony_ci{
224862306a36Sopenharmony_ci	struct dentry *parent = dget_parent(dentry);
224962306a36Sopenharmony_ci	struct inode *dir = d_inode(parent);
225062306a36Sopenharmony_ci	struct inode *inode;
225162306a36Sopenharmony_ci	struct dentry *d;
225262306a36Sopenharmony_ci	int error;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	d_drop(dentry);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	if (fhandle->size == 0) {
225762306a36Sopenharmony_ci		error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
225862306a36Sopenharmony_ci		if (error)
225962306a36Sopenharmony_ci			goto out_error;
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
226262306a36Sopenharmony_ci	if (!(fattr->valid & NFS_ATTR_FATTR)) {
226362306a36Sopenharmony_ci		struct nfs_server *server = NFS_SB(dentry->d_sb);
226462306a36Sopenharmony_ci		error = server->nfs_client->rpc_ops->getattr(server, fhandle,
226562306a36Sopenharmony_ci				fattr, NULL);
226662306a36Sopenharmony_ci		if (error < 0)
226762306a36Sopenharmony_ci			goto out_error;
226862306a36Sopenharmony_ci	}
226962306a36Sopenharmony_ci	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
227062306a36Sopenharmony_ci	d = d_splice_alias(inode, dentry);
227162306a36Sopenharmony_ciout:
227262306a36Sopenharmony_ci	dput(parent);
227362306a36Sopenharmony_ci	return d;
227462306a36Sopenharmony_ciout_error:
227562306a36Sopenharmony_ci	d = ERR_PTR(error);
227662306a36Sopenharmony_ci	goto out;
227762306a36Sopenharmony_ci}
227862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_add_or_obtain);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci/*
228162306a36Sopenharmony_ci * Code common to create, mkdir, and mknod.
228262306a36Sopenharmony_ci */
228362306a36Sopenharmony_ciint nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
228462306a36Sopenharmony_ci				struct nfs_fattr *fattr)
228562306a36Sopenharmony_ci{
228662306a36Sopenharmony_ci	struct dentry *d;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	d = nfs_add_or_obtain(dentry, fhandle, fattr);
228962306a36Sopenharmony_ci	if (IS_ERR(d))
229062306a36Sopenharmony_ci		return PTR_ERR(d);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	/* Callers don't care */
229362306a36Sopenharmony_ci	dput(d);
229462306a36Sopenharmony_ci	return 0;
229562306a36Sopenharmony_ci}
229662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_instantiate);
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci/*
229962306a36Sopenharmony_ci * Following a failed create operation, we drop the dentry rather
230062306a36Sopenharmony_ci * than retain a negative dentry. This avoids a problem in the event
230162306a36Sopenharmony_ci * that the operation succeeded on the server, but an error in the
230262306a36Sopenharmony_ci * reply path made it appear to have failed.
230362306a36Sopenharmony_ci */
230462306a36Sopenharmony_ciint nfs_create(struct mnt_idmap *idmap, struct inode *dir,
230562306a36Sopenharmony_ci	       struct dentry *dentry, umode_t mode, bool excl)
230662306a36Sopenharmony_ci{
230762306a36Sopenharmony_ci	struct iattr attr;
230862306a36Sopenharmony_ci	int open_flags = excl ? O_CREAT | O_EXCL : O_CREAT;
230962306a36Sopenharmony_ci	int error;
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	dfprintk(VFS, "NFS: create(%s/%lu), %pd\n",
231262306a36Sopenharmony_ci			dir->i_sb->s_id, dir->i_ino, dentry);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	attr.ia_mode = mode;
231562306a36Sopenharmony_ci	attr.ia_valid = ATTR_MODE;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	trace_nfs_create_enter(dir, dentry, open_flags);
231862306a36Sopenharmony_ci	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
231962306a36Sopenharmony_ci	trace_nfs_create_exit(dir, dentry, open_flags, error);
232062306a36Sopenharmony_ci	if (error != 0)
232162306a36Sopenharmony_ci		goto out_err;
232262306a36Sopenharmony_ci	return 0;
232362306a36Sopenharmony_ciout_err:
232462306a36Sopenharmony_ci	d_drop(dentry);
232562306a36Sopenharmony_ci	return error;
232662306a36Sopenharmony_ci}
232762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_create);
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci/*
233062306a36Sopenharmony_ci * See comments for nfs_proc_create regarding failed operations.
233162306a36Sopenharmony_ci */
233262306a36Sopenharmony_ciint
233362306a36Sopenharmony_cinfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
233462306a36Sopenharmony_ci	  struct dentry *dentry, umode_t mode, dev_t rdev)
233562306a36Sopenharmony_ci{
233662306a36Sopenharmony_ci	struct iattr attr;
233762306a36Sopenharmony_ci	int status;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	dfprintk(VFS, "NFS: mknod(%s/%lu), %pd\n",
234062306a36Sopenharmony_ci			dir->i_sb->s_id, dir->i_ino, dentry);
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	attr.ia_mode = mode;
234362306a36Sopenharmony_ci	attr.ia_valid = ATTR_MODE;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	trace_nfs_mknod_enter(dir, dentry);
234662306a36Sopenharmony_ci	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
234762306a36Sopenharmony_ci	trace_nfs_mknod_exit(dir, dentry, status);
234862306a36Sopenharmony_ci	if (status != 0)
234962306a36Sopenharmony_ci		goto out_err;
235062306a36Sopenharmony_ci	return 0;
235162306a36Sopenharmony_ciout_err:
235262306a36Sopenharmony_ci	d_drop(dentry);
235362306a36Sopenharmony_ci	return status;
235462306a36Sopenharmony_ci}
235562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_mknod);
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci/*
235862306a36Sopenharmony_ci * See comments for nfs_proc_create regarding failed operations.
235962306a36Sopenharmony_ci */
236062306a36Sopenharmony_ciint nfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
236162306a36Sopenharmony_ci	      struct dentry *dentry, umode_t mode)
236262306a36Sopenharmony_ci{
236362306a36Sopenharmony_ci	struct iattr attr;
236462306a36Sopenharmony_ci	int error;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	dfprintk(VFS, "NFS: mkdir(%s/%lu), %pd\n",
236762306a36Sopenharmony_ci			dir->i_sb->s_id, dir->i_ino, dentry);
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	attr.ia_valid = ATTR_MODE;
237062306a36Sopenharmony_ci	attr.ia_mode = mode | S_IFDIR;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	trace_nfs_mkdir_enter(dir, dentry);
237362306a36Sopenharmony_ci	error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
237462306a36Sopenharmony_ci	trace_nfs_mkdir_exit(dir, dentry, error);
237562306a36Sopenharmony_ci	if (error != 0)
237662306a36Sopenharmony_ci		goto out_err;
237762306a36Sopenharmony_ci	return 0;
237862306a36Sopenharmony_ciout_err:
237962306a36Sopenharmony_ci	d_drop(dentry);
238062306a36Sopenharmony_ci	return error;
238162306a36Sopenharmony_ci}
238262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_mkdir);
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_cistatic void nfs_dentry_handle_enoent(struct dentry *dentry)
238562306a36Sopenharmony_ci{
238662306a36Sopenharmony_ci	if (simple_positive(dentry))
238762306a36Sopenharmony_ci		d_delete(dentry);
238862306a36Sopenharmony_ci}
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_cistatic void nfs_dentry_remove_handle_error(struct inode *dir,
239162306a36Sopenharmony_ci					   struct dentry *dentry, int error)
239262306a36Sopenharmony_ci{
239362306a36Sopenharmony_ci	switch (error) {
239462306a36Sopenharmony_ci	case -ENOENT:
239562306a36Sopenharmony_ci		if (d_really_is_positive(dentry))
239662306a36Sopenharmony_ci			d_delete(dentry);
239762306a36Sopenharmony_ci		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
239862306a36Sopenharmony_ci		break;
239962306a36Sopenharmony_ci	case 0:
240062306a36Sopenharmony_ci		nfs_d_prune_case_insensitive_aliases(d_inode(dentry));
240162306a36Sopenharmony_ci		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
240262306a36Sopenharmony_ci	}
240362306a36Sopenharmony_ci}
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ciint nfs_rmdir(struct inode *dir, struct dentry *dentry)
240662306a36Sopenharmony_ci{
240762306a36Sopenharmony_ci	int error;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	dfprintk(VFS, "NFS: rmdir(%s/%lu), %pd\n",
241062306a36Sopenharmony_ci			dir->i_sb->s_id, dir->i_ino, dentry);
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	trace_nfs_rmdir_enter(dir, dentry);
241362306a36Sopenharmony_ci	if (d_really_is_positive(dentry)) {
241462306a36Sopenharmony_ci		down_write(&NFS_I(d_inode(dentry))->rmdir_sem);
241562306a36Sopenharmony_ci		error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
241662306a36Sopenharmony_ci		/* Ensure the VFS deletes this inode */
241762306a36Sopenharmony_ci		switch (error) {
241862306a36Sopenharmony_ci		case 0:
241962306a36Sopenharmony_ci			clear_nlink(d_inode(dentry));
242062306a36Sopenharmony_ci			break;
242162306a36Sopenharmony_ci		case -ENOENT:
242262306a36Sopenharmony_ci			nfs_dentry_handle_enoent(dentry);
242362306a36Sopenharmony_ci		}
242462306a36Sopenharmony_ci		up_write(&NFS_I(d_inode(dentry))->rmdir_sem);
242562306a36Sopenharmony_ci	} else
242662306a36Sopenharmony_ci		error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
242762306a36Sopenharmony_ci	nfs_dentry_remove_handle_error(dir, dentry, error);
242862306a36Sopenharmony_ci	trace_nfs_rmdir_exit(dir, dentry, error);
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	return error;
243162306a36Sopenharmony_ci}
243262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_rmdir);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci/*
243562306a36Sopenharmony_ci * Remove a file after making sure there are no pending writes,
243662306a36Sopenharmony_ci * and after checking that the file has only one user.
243762306a36Sopenharmony_ci *
243862306a36Sopenharmony_ci * We invalidate the attribute cache and free the inode prior to the operation
243962306a36Sopenharmony_ci * to avoid possible races if the server reuses the inode.
244062306a36Sopenharmony_ci */
244162306a36Sopenharmony_cistatic int nfs_safe_remove(struct dentry *dentry)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	struct inode *dir = d_inode(dentry->d_parent);
244462306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
244562306a36Sopenharmony_ci	int error = -EBUSY;
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	dfprintk(VFS, "NFS: safe_remove(%pd2)\n", dentry);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	/* If the dentry was sillyrenamed, we simply call d_delete() */
245062306a36Sopenharmony_ci	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
245162306a36Sopenharmony_ci		error = 0;
245262306a36Sopenharmony_ci		goto out;
245362306a36Sopenharmony_ci	}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	trace_nfs_remove_enter(dir, dentry);
245662306a36Sopenharmony_ci	if (inode != NULL) {
245762306a36Sopenharmony_ci		error = NFS_PROTO(dir)->remove(dir, dentry);
245862306a36Sopenharmony_ci		if (error == 0)
245962306a36Sopenharmony_ci			nfs_drop_nlink(inode);
246062306a36Sopenharmony_ci	} else
246162306a36Sopenharmony_ci		error = NFS_PROTO(dir)->remove(dir, dentry);
246262306a36Sopenharmony_ci	if (error == -ENOENT)
246362306a36Sopenharmony_ci		nfs_dentry_handle_enoent(dentry);
246462306a36Sopenharmony_ci	trace_nfs_remove_exit(dir, dentry, error);
246562306a36Sopenharmony_ciout:
246662306a36Sopenharmony_ci	return error;
246762306a36Sopenharmony_ci}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci/*  We do silly rename. In case sillyrename() returns -EBUSY, the inode
247062306a36Sopenharmony_ci *  belongs to an active ".nfs..." file and we return -EBUSY.
247162306a36Sopenharmony_ci *
247262306a36Sopenharmony_ci *  If sillyrename() returns 0, we do nothing, otherwise we unlink.
247362306a36Sopenharmony_ci */
247462306a36Sopenharmony_ciint nfs_unlink(struct inode *dir, struct dentry *dentry)
247562306a36Sopenharmony_ci{
247662306a36Sopenharmony_ci	int error;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	dfprintk(VFS, "NFS: unlink(%s/%lu, %pd)\n", dir->i_sb->s_id,
247962306a36Sopenharmony_ci		dir->i_ino, dentry);
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	trace_nfs_unlink_enter(dir, dentry);
248262306a36Sopenharmony_ci	spin_lock(&dentry->d_lock);
248362306a36Sopenharmony_ci	if (d_count(dentry) > 1 && !test_bit(NFS_INO_PRESERVE_UNLINKED,
248462306a36Sopenharmony_ci					     &NFS_I(d_inode(dentry))->flags)) {
248562306a36Sopenharmony_ci		spin_unlock(&dentry->d_lock);
248662306a36Sopenharmony_ci		/* Start asynchronous writeout of the inode */
248762306a36Sopenharmony_ci		write_inode_now(d_inode(dentry), 0);
248862306a36Sopenharmony_ci		error = nfs_sillyrename(dir, dentry);
248962306a36Sopenharmony_ci		goto out;
249062306a36Sopenharmony_ci	}
249162306a36Sopenharmony_ci	/* We must prevent any concurrent open until the unlink
249262306a36Sopenharmony_ci	 * completes.  ->d_revalidate will wait for ->d_fsdata
249362306a36Sopenharmony_ci	 * to clear.  We set it here to ensure no lookup succeeds until
249462306a36Sopenharmony_ci	 * the unlink is complete on the server.
249562306a36Sopenharmony_ci	 */
249662306a36Sopenharmony_ci	error = -ETXTBSY;
249762306a36Sopenharmony_ci	if (WARN_ON(dentry->d_flags & DCACHE_NFSFS_RENAMED) ||
249862306a36Sopenharmony_ci	    WARN_ON(dentry->d_fsdata == NFS_FSDATA_BLOCKED)) {
249962306a36Sopenharmony_ci		spin_unlock(&dentry->d_lock);
250062306a36Sopenharmony_ci		goto out;
250162306a36Sopenharmony_ci	}
250262306a36Sopenharmony_ci	/* old devname */
250362306a36Sopenharmony_ci	kfree(dentry->d_fsdata);
250462306a36Sopenharmony_ci	dentry->d_fsdata = NFS_FSDATA_BLOCKED;
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	spin_unlock(&dentry->d_lock);
250762306a36Sopenharmony_ci	error = nfs_safe_remove(dentry);
250862306a36Sopenharmony_ci	nfs_dentry_remove_handle_error(dir, dentry, error);
250962306a36Sopenharmony_ci	dentry->d_fsdata = NULL;
251062306a36Sopenharmony_ci	wake_up_var(&dentry->d_fsdata);
251162306a36Sopenharmony_ciout:
251262306a36Sopenharmony_ci	trace_nfs_unlink_exit(dir, dentry, error);
251362306a36Sopenharmony_ci	return error;
251462306a36Sopenharmony_ci}
251562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_unlink);
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci/*
251862306a36Sopenharmony_ci * To create a symbolic link, most file systems instantiate a new inode,
251962306a36Sopenharmony_ci * add a page to it containing the path, then write it out to the disk
252062306a36Sopenharmony_ci * using prepare_write/commit_write.
252162306a36Sopenharmony_ci *
252262306a36Sopenharmony_ci * Unfortunately the NFS client can't create the in-core inode first
252362306a36Sopenharmony_ci * because it needs a file handle to create an in-core inode (see
252462306a36Sopenharmony_ci * fs/nfs/inode.c:nfs_fhget).  We only have a file handle *after* the
252562306a36Sopenharmony_ci * symlink request has completed on the server.
252662306a36Sopenharmony_ci *
252762306a36Sopenharmony_ci * So instead we allocate a raw page, copy the symname into it, then do
252862306a36Sopenharmony_ci * the SYMLINK request with the page as the buffer.  If it succeeds, we
252962306a36Sopenharmony_ci * now have a new file handle and can instantiate an in-core NFS inode
253062306a36Sopenharmony_ci * and move the raw page into its mapping.
253162306a36Sopenharmony_ci */
253262306a36Sopenharmony_ciint nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
253362306a36Sopenharmony_ci		struct dentry *dentry, const char *symname)
253462306a36Sopenharmony_ci{
253562306a36Sopenharmony_ci	struct page *page;
253662306a36Sopenharmony_ci	char *kaddr;
253762306a36Sopenharmony_ci	struct iattr attr;
253862306a36Sopenharmony_ci	unsigned int pathlen = strlen(symname);
253962306a36Sopenharmony_ci	int error;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s)\n", dir->i_sb->s_id,
254262306a36Sopenharmony_ci		dir->i_ino, dentry, symname);
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	if (pathlen > PAGE_SIZE)
254562306a36Sopenharmony_ci		return -ENAMETOOLONG;
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	attr.ia_mode = S_IFLNK | S_IRWXUGO;
254862306a36Sopenharmony_ci	attr.ia_valid = ATTR_MODE;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	page = alloc_page(GFP_USER);
255162306a36Sopenharmony_ci	if (!page)
255262306a36Sopenharmony_ci		return -ENOMEM;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	kaddr = page_address(page);
255562306a36Sopenharmony_ci	memcpy(kaddr, symname, pathlen);
255662306a36Sopenharmony_ci	if (pathlen < PAGE_SIZE)
255762306a36Sopenharmony_ci		memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	trace_nfs_symlink_enter(dir, dentry);
256062306a36Sopenharmony_ci	error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
256162306a36Sopenharmony_ci	trace_nfs_symlink_exit(dir, dentry, error);
256262306a36Sopenharmony_ci	if (error != 0) {
256362306a36Sopenharmony_ci		dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s) error %d\n",
256462306a36Sopenharmony_ci			dir->i_sb->s_id, dir->i_ino,
256562306a36Sopenharmony_ci			dentry, symname, error);
256662306a36Sopenharmony_ci		d_drop(dentry);
256762306a36Sopenharmony_ci		__free_page(page);
256862306a36Sopenharmony_ci		return error;
256962306a36Sopenharmony_ci	}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	/*
257462306a36Sopenharmony_ci	 * No big deal if we can't add this page to the page cache here.
257562306a36Sopenharmony_ci	 * READLINK will get the missing page from the server if needed.
257662306a36Sopenharmony_ci	 */
257762306a36Sopenharmony_ci	if (!add_to_page_cache_lru(page, d_inode(dentry)->i_mapping, 0,
257862306a36Sopenharmony_ci							GFP_KERNEL)) {
257962306a36Sopenharmony_ci		SetPageUptodate(page);
258062306a36Sopenharmony_ci		unlock_page(page);
258162306a36Sopenharmony_ci		/*
258262306a36Sopenharmony_ci		 * add_to_page_cache_lru() grabs an extra page refcount.
258362306a36Sopenharmony_ci		 * Drop it here to avoid leaking this page later.
258462306a36Sopenharmony_ci		 */
258562306a36Sopenharmony_ci		put_page(page);
258662306a36Sopenharmony_ci	} else
258762306a36Sopenharmony_ci		__free_page(page);
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	return 0;
259062306a36Sopenharmony_ci}
259162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_symlink);
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ciint
259462306a36Sopenharmony_cinfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
259562306a36Sopenharmony_ci{
259662306a36Sopenharmony_ci	struct inode *inode = d_inode(old_dentry);
259762306a36Sopenharmony_ci	int error;
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	dfprintk(VFS, "NFS: link(%pd2 -> %pd2)\n",
260062306a36Sopenharmony_ci		old_dentry, dentry);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	trace_nfs_link_enter(inode, dir, dentry);
260362306a36Sopenharmony_ci	d_drop(dentry);
260462306a36Sopenharmony_ci	if (S_ISREG(inode->i_mode))
260562306a36Sopenharmony_ci		nfs_sync_inode(inode);
260662306a36Sopenharmony_ci	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
260762306a36Sopenharmony_ci	if (error == 0) {
260862306a36Sopenharmony_ci		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
260962306a36Sopenharmony_ci		ihold(inode);
261062306a36Sopenharmony_ci		d_add(dentry, inode);
261162306a36Sopenharmony_ci	}
261262306a36Sopenharmony_ci	trace_nfs_link_exit(inode, dir, dentry, error);
261362306a36Sopenharmony_ci	return error;
261462306a36Sopenharmony_ci}
261562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_link);
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_cistatic void
261862306a36Sopenharmony_cinfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data)
261962306a36Sopenharmony_ci{
262062306a36Sopenharmony_ci	struct dentry *new_dentry = data->new_dentry;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	new_dentry->d_fsdata = NULL;
262362306a36Sopenharmony_ci	wake_up_var(&new_dentry->d_fsdata);
262462306a36Sopenharmony_ci}
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci/*
262762306a36Sopenharmony_ci * RENAME
262862306a36Sopenharmony_ci * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
262962306a36Sopenharmony_ci * different file handle for the same inode after a rename (e.g. when
263062306a36Sopenharmony_ci * moving to a different directory). A fail-safe method to do so would
263162306a36Sopenharmony_ci * be to look up old_dir/old_name, create a link to new_dir/new_name and
263262306a36Sopenharmony_ci * rename the old file using the sillyrename stuff. This way, the original
263362306a36Sopenharmony_ci * file in old_dir will go away when the last process iput()s the inode.
263462306a36Sopenharmony_ci *
263562306a36Sopenharmony_ci * FIXED.
263662306a36Sopenharmony_ci *
263762306a36Sopenharmony_ci * It actually works quite well. One needs to have the possibility for
263862306a36Sopenharmony_ci * at least one ".nfs..." file in each directory the file ever gets
263962306a36Sopenharmony_ci * moved or linked to which happens automagically with the new
264062306a36Sopenharmony_ci * implementation that only depends on the dcache stuff instead of
264162306a36Sopenharmony_ci * using the inode layer
264262306a36Sopenharmony_ci *
264362306a36Sopenharmony_ci * Unfortunately, things are a little more complicated than indicated
264462306a36Sopenharmony_ci * above. For a cross-directory move, we want to make sure we can get
264562306a36Sopenharmony_ci * rid of the old inode after the operation.  This means there must be
264662306a36Sopenharmony_ci * no pending writes (if it's a file), and the use count must be 1.
264762306a36Sopenharmony_ci * If these conditions are met, we can drop the dentries before doing
264862306a36Sopenharmony_ci * the rename.
264962306a36Sopenharmony_ci */
265062306a36Sopenharmony_ciint nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
265162306a36Sopenharmony_ci	       struct dentry *old_dentry, struct inode *new_dir,
265262306a36Sopenharmony_ci	       struct dentry *new_dentry, unsigned int flags)
265362306a36Sopenharmony_ci{
265462306a36Sopenharmony_ci	struct inode *old_inode = d_inode(old_dentry);
265562306a36Sopenharmony_ci	struct inode *new_inode = d_inode(new_dentry);
265662306a36Sopenharmony_ci	struct dentry *dentry = NULL;
265762306a36Sopenharmony_ci	struct rpc_task *task;
265862306a36Sopenharmony_ci	bool must_unblock = false;
265962306a36Sopenharmony_ci	int error = -EBUSY;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	if (flags)
266262306a36Sopenharmony_ci		return -EINVAL;
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	dfprintk(VFS, "NFS: rename(%pd2 -> %pd2, ct=%d)\n",
266562306a36Sopenharmony_ci		 old_dentry, new_dentry,
266662306a36Sopenharmony_ci		 d_count(new_dentry));
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	trace_nfs_rename_enter(old_dir, old_dentry, new_dir, new_dentry);
266962306a36Sopenharmony_ci	/*
267062306a36Sopenharmony_ci	 * For non-directories, check whether the target is busy and if so,
267162306a36Sopenharmony_ci	 * make a copy of the dentry and then do a silly-rename. If the
267262306a36Sopenharmony_ci	 * silly-rename succeeds, the copied dentry is hashed and becomes
267362306a36Sopenharmony_ci	 * the new target.
267462306a36Sopenharmony_ci	 */
267562306a36Sopenharmony_ci	if (new_inode && !S_ISDIR(new_inode->i_mode)) {
267662306a36Sopenharmony_ci		/* We must prevent any concurrent open until the unlink
267762306a36Sopenharmony_ci		 * completes.  ->d_revalidate will wait for ->d_fsdata
267862306a36Sopenharmony_ci		 * to clear.  We set it here to ensure no lookup succeeds until
267962306a36Sopenharmony_ci		 * the unlink is complete on the server.
268062306a36Sopenharmony_ci		 */
268162306a36Sopenharmony_ci		error = -ETXTBSY;
268262306a36Sopenharmony_ci		if (WARN_ON(new_dentry->d_flags & DCACHE_NFSFS_RENAMED) ||
268362306a36Sopenharmony_ci		    WARN_ON(new_dentry->d_fsdata == NFS_FSDATA_BLOCKED))
268462306a36Sopenharmony_ci			goto out;
268562306a36Sopenharmony_ci		if (new_dentry->d_fsdata) {
268662306a36Sopenharmony_ci			/* old devname */
268762306a36Sopenharmony_ci			kfree(new_dentry->d_fsdata);
268862306a36Sopenharmony_ci			new_dentry->d_fsdata = NULL;
268962306a36Sopenharmony_ci		}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci		spin_lock(&new_dentry->d_lock);
269262306a36Sopenharmony_ci		if (d_count(new_dentry) > 2) {
269362306a36Sopenharmony_ci			int err;
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci			spin_unlock(&new_dentry->d_lock);
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci			/* copy the target dentry's name */
269862306a36Sopenharmony_ci			dentry = d_alloc(new_dentry->d_parent,
269962306a36Sopenharmony_ci					 &new_dentry->d_name);
270062306a36Sopenharmony_ci			if (!dentry)
270162306a36Sopenharmony_ci				goto out;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci			/* silly-rename the existing target ... */
270462306a36Sopenharmony_ci			err = nfs_sillyrename(new_dir, new_dentry);
270562306a36Sopenharmony_ci			if (err)
270662306a36Sopenharmony_ci				goto out;
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci			new_dentry = dentry;
270962306a36Sopenharmony_ci			new_inode = NULL;
271062306a36Sopenharmony_ci		} else {
271162306a36Sopenharmony_ci			new_dentry->d_fsdata = NFS_FSDATA_BLOCKED;
271262306a36Sopenharmony_ci			must_unblock = true;
271362306a36Sopenharmony_ci			spin_unlock(&new_dentry->d_lock);
271462306a36Sopenharmony_ci		}
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	if (S_ISREG(old_inode->i_mode))
271962306a36Sopenharmony_ci		nfs_sync_inode(old_inode);
272062306a36Sopenharmony_ci	task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
272162306a36Sopenharmony_ci				must_unblock ? nfs_unblock_rename : NULL);
272262306a36Sopenharmony_ci	if (IS_ERR(task)) {
272362306a36Sopenharmony_ci		error = PTR_ERR(task);
272462306a36Sopenharmony_ci		goto out;
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	error = rpc_wait_for_completion_task(task);
272862306a36Sopenharmony_ci	if (error != 0) {
272962306a36Sopenharmony_ci		((struct nfs_renamedata *)task->tk_calldata)->cancelled = 1;
273062306a36Sopenharmony_ci		/* Paired with the atomic_dec_and_test() barrier in rpc_do_put_task() */
273162306a36Sopenharmony_ci		smp_wmb();
273262306a36Sopenharmony_ci	} else
273362306a36Sopenharmony_ci		error = task->tk_status;
273462306a36Sopenharmony_ci	rpc_put_task(task);
273562306a36Sopenharmony_ci	/* Ensure the inode attributes are revalidated */
273662306a36Sopenharmony_ci	if (error == 0) {
273762306a36Sopenharmony_ci		spin_lock(&old_inode->i_lock);
273862306a36Sopenharmony_ci		NFS_I(old_inode)->attr_gencount = nfs_inc_attr_generation_counter();
273962306a36Sopenharmony_ci		nfs_set_cache_invalid(old_inode, NFS_INO_INVALID_CHANGE |
274062306a36Sopenharmony_ci							 NFS_INO_INVALID_CTIME |
274162306a36Sopenharmony_ci							 NFS_INO_REVAL_FORCED);
274262306a36Sopenharmony_ci		spin_unlock(&old_inode->i_lock);
274362306a36Sopenharmony_ci	}
274462306a36Sopenharmony_ciout:
274562306a36Sopenharmony_ci	trace_nfs_rename_exit(old_dir, old_dentry,
274662306a36Sopenharmony_ci			new_dir, new_dentry, error);
274762306a36Sopenharmony_ci	if (!error) {
274862306a36Sopenharmony_ci		if (new_inode != NULL)
274962306a36Sopenharmony_ci			nfs_drop_nlink(new_inode);
275062306a36Sopenharmony_ci		/*
275162306a36Sopenharmony_ci		 * The d_move() should be here instead of in an async RPC completion
275262306a36Sopenharmony_ci		 * handler because we need the proper locks to move the dentry.  If
275362306a36Sopenharmony_ci		 * we're interrupted by a signal, the async RPC completion handler
275462306a36Sopenharmony_ci		 * should mark the directories for revalidation.
275562306a36Sopenharmony_ci		 */
275662306a36Sopenharmony_ci		d_move(old_dentry, new_dentry);
275762306a36Sopenharmony_ci		nfs_set_verifier(old_dentry,
275862306a36Sopenharmony_ci					nfs_save_change_attribute(new_dir));
275962306a36Sopenharmony_ci	} else if (error == -ENOENT)
276062306a36Sopenharmony_ci		nfs_dentry_handle_enoent(old_dentry);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	/* new dentry created? */
276362306a36Sopenharmony_ci	if (dentry)
276462306a36Sopenharmony_ci		dput(dentry);
276562306a36Sopenharmony_ci	return error;
276662306a36Sopenharmony_ci}
276762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_rename);
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfs_access_lru_lock);
277062306a36Sopenharmony_cistatic LIST_HEAD(nfs_access_lru_list);
277162306a36Sopenharmony_cistatic atomic_long_t nfs_access_nr_entries;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_cistatic unsigned long nfs_access_max_cachesize = 4*1024*1024;
277462306a36Sopenharmony_cimodule_param(nfs_access_max_cachesize, ulong, 0644);
277562306a36Sopenharmony_ciMODULE_PARM_DESC(nfs_access_max_cachesize, "NFS access maximum total cache length");
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_cistatic void nfs_access_free_entry(struct nfs_access_entry *entry)
277862306a36Sopenharmony_ci{
277962306a36Sopenharmony_ci	put_group_info(entry->group_info);
278062306a36Sopenharmony_ci	kfree_rcu(entry, rcu_head);
278162306a36Sopenharmony_ci	smp_mb__before_atomic();
278262306a36Sopenharmony_ci	atomic_long_dec(&nfs_access_nr_entries);
278362306a36Sopenharmony_ci	smp_mb__after_atomic();
278462306a36Sopenharmony_ci}
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_cistatic void nfs_access_free_list(struct list_head *head)
278762306a36Sopenharmony_ci{
278862306a36Sopenharmony_ci	struct nfs_access_entry *cache;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	while (!list_empty(head)) {
279162306a36Sopenharmony_ci		cache = list_entry(head->next, struct nfs_access_entry, lru);
279262306a36Sopenharmony_ci		list_del(&cache->lru);
279362306a36Sopenharmony_ci		nfs_access_free_entry(cache);
279462306a36Sopenharmony_ci	}
279562306a36Sopenharmony_ci}
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_cistatic unsigned long
279862306a36Sopenharmony_cinfs_do_access_cache_scan(unsigned int nr_to_scan)
279962306a36Sopenharmony_ci{
280062306a36Sopenharmony_ci	LIST_HEAD(head);
280162306a36Sopenharmony_ci	struct nfs_inode *nfsi, *next;
280262306a36Sopenharmony_ci	struct nfs_access_entry *cache;
280362306a36Sopenharmony_ci	long freed = 0;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	spin_lock(&nfs_access_lru_lock);
280662306a36Sopenharmony_ci	list_for_each_entry_safe(nfsi, next, &nfs_access_lru_list, access_cache_inode_lru) {
280762306a36Sopenharmony_ci		struct inode *inode;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci		if (nr_to_scan-- == 0)
281062306a36Sopenharmony_ci			break;
281162306a36Sopenharmony_ci		inode = &nfsi->vfs_inode;
281262306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
281362306a36Sopenharmony_ci		if (list_empty(&nfsi->access_cache_entry_lru))
281462306a36Sopenharmony_ci			goto remove_lru_entry;
281562306a36Sopenharmony_ci		cache = list_entry(nfsi->access_cache_entry_lru.next,
281662306a36Sopenharmony_ci				struct nfs_access_entry, lru);
281762306a36Sopenharmony_ci		list_move(&cache->lru, &head);
281862306a36Sopenharmony_ci		rb_erase(&cache->rb_node, &nfsi->access_cache);
281962306a36Sopenharmony_ci		freed++;
282062306a36Sopenharmony_ci		if (!list_empty(&nfsi->access_cache_entry_lru))
282162306a36Sopenharmony_ci			list_move_tail(&nfsi->access_cache_inode_lru,
282262306a36Sopenharmony_ci					&nfs_access_lru_list);
282362306a36Sopenharmony_ci		else {
282462306a36Sopenharmony_ciremove_lru_entry:
282562306a36Sopenharmony_ci			list_del_init(&nfsi->access_cache_inode_lru);
282662306a36Sopenharmony_ci			smp_mb__before_atomic();
282762306a36Sopenharmony_ci			clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
282862306a36Sopenharmony_ci			smp_mb__after_atomic();
282962306a36Sopenharmony_ci		}
283062306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
283162306a36Sopenharmony_ci	}
283262306a36Sopenharmony_ci	spin_unlock(&nfs_access_lru_lock);
283362306a36Sopenharmony_ci	nfs_access_free_list(&head);
283462306a36Sopenharmony_ci	return freed;
283562306a36Sopenharmony_ci}
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ciunsigned long
283862306a36Sopenharmony_cinfs_access_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
283962306a36Sopenharmony_ci{
284062306a36Sopenharmony_ci	int nr_to_scan = sc->nr_to_scan;
284162306a36Sopenharmony_ci	gfp_t gfp_mask = sc->gfp_mask;
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL)
284462306a36Sopenharmony_ci		return SHRINK_STOP;
284562306a36Sopenharmony_ci	return nfs_do_access_cache_scan(nr_to_scan);
284662306a36Sopenharmony_ci}
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ciunsigned long
285062306a36Sopenharmony_cinfs_access_cache_count(struct shrinker *shrink, struct shrink_control *sc)
285162306a36Sopenharmony_ci{
285262306a36Sopenharmony_ci	return vfs_pressure_ratio(atomic_long_read(&nfs_access_nr_entries));
285362306a36Sopenharmony_ci}
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_cistatic void
285662306a36Sopenharmony_cinfs_access_cache_enforce_limit(void)
285762306a36Sopenharmony_ci{
285862306a36Sopenharmony_ci	long nr_entries = atomic_long_read(&nfs_access_nr_entries);
285962306a36Sopenharmony_ci	unsigned long diff;
286062306a36Sopenharmony_ci	unsigned int nr_to_scan;
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci	if (nr_entries < 0 || nr_entries <= nfs_access_max_cachesize)
286362306a36Sopenharmony_ci		return;
286462306a36Sopenharmony_ci	nr_to_scan = 100;
286562306a36Sopenharmony_ci	diff = nr_entries - nfs_access_max_cachesize;
286662306a36Sopenharmony_ci	if (diff < nr_to_scan)
286762306a36Sopenharmony_ci		nr_to_scan = diff;
286862306a36Sopenharmony_ci	nfs_do_access_cache_scan(nr_to_scan);
286962306a36Sopenharmony_ci}
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_cistatic void __nfs_access_zap_cache(struct nfs_inode *nfsi, struct list_head *head)
287262306a36Sopenharmony_ci{
287362306a36Sopenharmony_ci	struct rb_root *root_node = &nfsi->access_cache;
287462306a36Sopenharmony_ci	struct rb_node *n;
287562306a36Sopenharmony_ci	struct nfs_access_entry *entry;
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci	/* Unhook entries from the cache */
287862306a36Sopenharmony_ci	while ((n = rb_first(root_node)) != NULL) {
287962306a36Sopenharmony_ci		entry = rb_entry(n, struct nfs_access_entry, rb_node);
288062306a36Sopenharmony_ci		rb_erase(n, root_node);
288162306a36Sopenharmony_ci		list_move(&entry->lru, head);
288262306a36Sopenharmony_ci	}
288362306a36Sopenharmony_ci	nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
288462306a36Sopenharmony_ci}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_civoid nfs_access_zap_cache(struct inode *inode)
288762306a36Sopenharmony_ci{
288862306a36Sopenharmony_ci	LIST_HEAD(head);
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	if (test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags) == 0)
289162306a36Sopenharmony_ci		return;
289262306a36Sopenharmony_ci	/* Remove from global LRU init */
289362306a36Sopenharmony_ci	spin_lock(&nfs_access_lru_lock);
289462306a36Sopenharmony_ci	if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags))
289562306a36Sopenharmony_ci		list_del_init(&NFS_I(inode)->access_cache_inode_lru);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
289862306a36Sopenharmony_ci	__nfs_access_zap_cache(NFS_I(inode), &head);
289962306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
290062306a36Sopenharmony_ci	spin_unlock(&nfs_access_lru_lock);
290162306a36Sopenharmony_ci	nfs_access_free_list(&head);
290262306a36Sopenharmony_ci}
290362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_access_zap_cache);
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_cistatic int access_cmp(const struct cred *a, const struct nfs_access_entry *b)
290662306a36Sopenharmony_ci{
290762306a36Sopenharmony_ci	struct group_info *ga, *gb;
290862306a36Sopenharmony_ci	int g;
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	if (uid_lt(a->fsuid, b->fsuid))
291162306a36Sopenharmony_ci		return -1;
291262306a36Sopenharmony_ci	if (uid_gt(a->fsuid, b->fsuid))
291362306a36Sopenharmony_ci		return 1;
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	if (gid_lt(a->fsgid, b->fsgid))
291662306a36Sopenharmony_ci		return -1;
291762306a36Sopenharmony_ci	if (gid_gt(a->fsgid, b->fsgid))
291862306a36Sopenharmony_ci		return 1;
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	ga = a->group_info;
292162306a36Sopenharmony_ci	gb = b->group_info;
292262306a36Sopenharmony_ci	if (ga == gb)
292362306a36Sopenharmony_ci		return 0;
292462306a36Sopenharmony_ci	if (ga == NULL)
292562306a36Sopenharmony_ci		return -1;
292662306a36Sopenharmony_ci	if (gb == NULL)
292762306a36Sopenharmony_ci		return 1;
292862306a36Sopenharmony_ci	if (ga->ngroups < gb->ngroups)
292962306a36Sopenharmony_ci		return -1;
293062306a36Sopenharmony_ci	if (ga->ngroups > gb->ngroups)
293162306a36Sopenharmony_ci		return 1;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	for (g = 0; g < ga->ngroups; g++) {
293462306a36Sopenharmony_ci		if (gid_lt(ga->gid[g], gb->gid[g]))
293562306a36Sopenharmony_ci			return -1;
293662306a36Sopenharmony_ci		if (gid_gt(ga->gid[g], gb->gid[g]))
293762306a36Sopenharmony_ci			return 1;
293862306a36Sopenharmony_ci	}
293962306a36Sopenharmony_ci	return 0;
294062306a36Sopenharmony_ci}
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_cistatic struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
294362306a36Sopenharmony_ci{
294462306a36Sopenharmony_ci	struct rb_node *n = NFS_I(inode)->access_cache.rb_node;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci	while (n != NULL) {
294762306a36Sopenharmony_ci		struct nfs_access_entry *entry =
294862306a36Sopenharmony_ci			rb_entry(n, struct nfs_access_entry, rb_node);
294962306a36Sopenharmony_ci		int cmp = access_cmp(cred, entry);
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci		if (cmp < 0)
295262306a36Sopenharmony_ci			n = n->rb_left;
295362306a36Sopenharmony_ci		else if (cmp > 0)
295462306a36Sopenharmony_ci			n = n->rb_right;
295562306a36Sopenharmony_ci		else
295662306a36Sopenharmony_ci			return entry;
295762306a36Sopenharmony_ci	}
295862306a36Sopenharmony_ci	return NULL;
295962306a36Sopenharmony_ci}
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_cistatic u64 nfs_access_login_time(const struct task_struct *task,
296262306a36Sopenharmony_ci				 const struct cred *cred)
296362306a36Sopenharmony_ci{
296462306a36Sopenharmony_ci	const struct task_struct *parent;
296562306a36Sopenharmony_ci	const struct cred *pcred;
296662306a36Sopenharmony_ci	u64 ret;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	rcu_read_lock();
296962306a36Sopenharmony_ci	for (;;) {
297062306a36Sopenharmony_ci		parent = rcu_dereference(task->real_parent);
297162306a36Sopenharmony_ci		pcred = __task_cred(parent);
297262306a36Sopenharmony_ci		if (parent == task || cred_fscmp(pcred, cred) != 0)
297362306a36Sopenharmony_ci			break;
297462306a36Sopenharmony_ci		task = parent;
297562306a36Sopenharmony_ci	}
297662306a36Sopenharmony_ci	ret = task->start_time;
297762306a36Sopenharmony_ci	rcu_read_unlock();
297862306a36Sopenharmony_ci	return ret;
297962306a36Sopenharmony_ci}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_cistatic int nfs_access_get_cached_locked(struct inode *inode, const struct cred *cred, u32 *mask, bool may_block)
298262306a36Sopenharmony_ci{
298362306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
298462306a36Sopenharmony_ci	u64 login_time = nfs_access_login_time(current, cred);
298562306a36Sopenharmony_ci	struct nfs_access_entry *cache;
298662306a36Sopenharmony_ci	bool retry = true;
298762306a36Sopenharmony_ci	int err;
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
299062306a36Sopenharmony_ci	for(;;) {
299162306a36Sopenharmony_ci		if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
299262306a36Sopenharmony_ci			goto out_zap;
299362306a36Sopenharmony_ci		cache = nfs_access_search_rbtree(inode, cred);
299462306a36Sopenharmony_ci		err = -ENOENT;
299562306a36Sopenharmony_ci		if (cache == NULL)
299662306a36Sopenharmony_ci			goto out;
299762306a36Sopenharmony_ci		/* Found an entry, is our attribute cache valid? */
299862306a36Sopenharmony_ci		if (!nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS))
299962306a36Sopenharmony_ci			break;
300062306a36Sopenharmony_ci		if (!retry)
300162306a36Sopenharmony_ci			break;
300262306a36Sopenharmony_ci		err = -ECHILD;
300362306a36Sopenharmony_ci		if (!may_block)
300462306a36Sopenharmony_ci			goto out;
300562306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
300662306a36Sopenharmony_ci		err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
300762306a36Sopenharmony_ci		if (err)
300862306a36Sopenharmony_ci			return err;
300962306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
301062306a36Sopenharmony_ci		retry = false;
301162306a36Sopenharmony_ci	}
301262306a36Sopenharmony_ci	err = -ENOENT;
301362306a36Sopenharmony_ci	if ((s64)(login_time - cache->timestamp) > 0)
301462306a36Sopenharmony_ci		goto out;
301562306a36Sopenharmony_ci	*mask = cache->mask;
301662306a36Sopenharmony_ci	list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru);
301762306a36Sopenharmony_ci	err = 0;
301862306a36Sopenharmony_ciout:
301962306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
302062306a36Sopenharmony_ci	return err;
302162306a36Sopenharmony_ciout_zap:
302262306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
302362306a36Sopenharmony_ci	nfs_access_zap_cache(inode);
302462306a36Sopenharmony_ci	return -ENOENT;
302562306a36Sopenharmony_ci}
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_cistatic int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cred, u32 *mask)
302862306a36Sopenharmony_ci{
302962306a36Sopenharmony_ci	/* Only check the most recently returned cache entry,
303062306a36Sopenharmony_ci	 * but do it without locking.
303162306a36Sopenharmony_ci	 */
303262306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
303362306a36Sopenharmony_ci	u64 login_time = nfs_access_login_time(current, cred);
303462306a36Sopenharmony_ci	struct nfs_access_entry *cache;
303562306a36Sopenharmony_ci	int err = -ECHILD;
303662306a36Sopenharmony_ci	struct list_head *lh;
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci	rcu_read_lock();
303962306a36Sopenharmony_ci	if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)
304062306a36Sopenharmony_ci		goto out;
304162306a36Sopenharmony_ci	lh = rcu_dereference(list_tail_rcu(&nfsi->access_cache_entry_lru));
304262306a36Sopenharmony_ci	cache = list_entry(lh, struct nfs_access_entry, lru);
304362306a36Sopenharmony_ci	if (lh == &nfsi->access_cache_entry_lru ||
304462306a36Sopenharmony_ci	    access_cmp(cred, cache) != 0)
304562306a36Sopenharmony_ci		cache = NULL;
304662306a36Sopenharmony_ci	if (cache == NULL)
304762306a36Sopenharmony_ci		goto out;
304862306a36Sopenharmony_ci	if ((s64)(login_time - cache->timestamp) > 0)
304962306a36Sopenharmony_ci		goto out;
305062306a36Sopenharmony_ci	if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS))
305162306a36Sopenharmony_ci		goto out;
305262306a36Sopenharmony_ci	*mask = cache->mask;
305362306a36Sopenharmony_ci	err = 0;
305462306a36Sopenharmony_ciout:
305562306a36Sopenharmony_ci	rcu_read_unlock();
305662306a36Sopenharmony_ci	return err;
305762306a36Sopenharmony_ci}
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ciint nfs_access_get_cached(struct inode *inode, const struct cred *cred,
306062306a36Sopenharmony_ci			  u32 *mask, bool may_block)
306162306a36Sopenharmony_ci{
306262306a36Sopenharmony_ci	int status;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	status = nfs_access_get_cached_rcu(inode, cred, mask);
306562306a36Sopenharmony_ci	if (status != 0)
306662306a36Sopenharmony_ci		status = nfs_access_get_cached_locked(inode, cred, mask,
306762306a36Sopenharmony_ci		    may_block);
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	return status;
307062306a36Sopenharmony_ci}
307162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_access_get_cached);
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_cistatic void nfs_access_add_rbtree(struct inode *inode,
307462306a36Sopenharmony_ci				  struct nfs_access_entry *set,
307562306a36Sopenharmony_ci				  const struct cred *cred)
307662306a36Sopenharmony_ci{
307762306a36Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
307862306a36Sopenharmony_ci	struct rb_root *root_node = &nfsi->access_cache;
307962306a36Sopenharmony_ci	struct rb_node **p = &root_node->rb_node;
308062306a36Sopenharmony_ci	struct rb_node *parent = NULL;
308162306a36Sopenharmony_ci	struct nfs_access_entry *entry;
308262306a36Sopenharmony_ci	int cmp;
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
308562306a36Sopenharmony_ci	while (*p != NULL) {
308662306a36Sopenharmony_ci		parent = *p;
308762306a36Sopenharmony_ci		entry = rb_entry(parent, struct nfs_access_entry, rb_node);
308862306a36Sopenharmony_ci		cmp = access_cmp(cred, entry);
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci		if (cmp < 0)
309162306a36Sopenharmony_ci			p = &parent->rb_left;
309262306a36Sopenharmony_ci		else if (cmp > 0)
309362306a36Sopenharmony_ci			p = &parent->rb_right;
309462306a36Sopenharmony_ci		else
309562306a36Sopenharmony_ci			goto found;
309662306a36Sopenharmony_ci	}
309762306a36Sopenharmony_ci	rb_link_node(&set->rb_node, parent, p);
309862306a36Sopenharmony_ci	rb_insert_color(&set->rb_node, root_node);
309962306a36Sopenharmony_ci	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
310062306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
310162306a36Sopenharmony_ci	return;
310262306a36Sopenharmony_cifound:
310362306a36Sopenharmony_ci	rb_replace_node(parent, &set->rb_node, root_node);
310462306a36Sopenharmony_ci	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
310562306a36Sopenharmony_ci	list_del(&entry->lru);
310662306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
310762306a36Sopenharmony_ci	nfs_access_free_entry(entry);
310862306a36Sopenharmony_ci}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_civoid nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set,
311162306a36Sopenharmony_ci			  const struct cred *cred)
311262306a36Sopenharmony_ci{
311362306a36Sopenharmony_ci	struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
311462306a36Sopenharmony_ci	if (cache == NULL)
311562306a36Sopenharmony_ci		return;
311662306a36Sopenharmony_ci	RB_CLEAR_NODE(&cache->rb_node);
311762306a36Sopenharmony_ci	cache->fsuid = cred->fsuid;
311862306a36Sopenharmony_ci	cache->fsgid = cred->fsgid;
311962306a36Sopenharmony_ci	cache->group_info = get_group_info(cred->group_info);
312062306a36Sopenharmony_ci	cache->mask = set->mask;
312162306a36Sopenharmony_ci	cache->timestamp = ktime_get_ns();
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci	/* The above field assignments must be visible
312462306a36Sopenharmony_ci	 * before this item appears on the lru.  We cannot easily
312562306a36Sopenharmony_ci	 * use rcu_assign_pointer, so just force the memory barrier.
312662306a36Sopenharmony_ci	 */
312762306a36Sopenharmony_ci	smp_wmb();
312862306a36Sopenharmony_ci	nfs_access_add_rbtree(inode, cache, cred);
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	/* Update accounting */
313162306a36Sopenharmony_ci	smp_mb__before_atomic();
313262306a36Sopenharmony_ci	atomic_long_inc(&nfs_access_nr_entries);
313362306a36Sopenharmony_ci	smp_mb__after_atomic();
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	/* Add inode to global LRU list */
313662306a36Sopenharmony_ci	if (!test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) {
313762306a36Sopenharmony_ci		spin_lock(&nfs_access_lru_lock);
313862306a36Sopenharmony_ci		if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags))
313962306a36Sopenharmony_ci			list_add_tail(&NFS_I(inode)->access_cache_inode_lru,
314062306a36Sopenharmony_ci					&nfs_access_lru_list);
314162306a36Sopenharmony_ci		spin_unlock(&nfs_access_lru_lock);
314262306a36Sopenharmony_ci	}
314362306a36Sopenharmony_ci	nfs_access_cache_enforce_limit();
314462306a36Sopenharmony_ci}
314562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_access_add_cache);
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci#define NFS_MAY_READ (NFS_ACCESS_READ)
314862306a36Sopenharmony_ci#define NFS_MAY_WRITE (NFS_ACCESS_MODIFY | \
314962306a36Sopenharmony_ci		NFS_ACCESS_EXTEND | \
315062306a36Sopenharmony_ci		NFS_ACCESS_DELETE)
315162306a36Sopenharmony_ci#define NFS_FILE_MAY_WRITE (NFS_ACCESS_MODIFY | \
315262306a36Sopenharmony_ci		NFS_ACCESS_EXTEND)
315362306a36Sopenharmony_ci#define NFS_DIR_MAY_WRITE NFS_MAY_WRITE
315462306a36Sopenharmony_ci#define NFS_MAY_LOOKUP (NFS_ACCESS_LOOKUP)
315562306a36Sopenharmony_ci#define NFS_MAY_EXECUTE (NFS_ACCESS_EXECUTE)
315662306a36Sopenharmony_cistatic int
315762306a36Sopenharmony_cinfs_access_calc_mask(u32 access_result, umode_t umode)
315862306a36Sopenharmony_ci{
315962306a36Sopenharmony_ci	int mask = 0;
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	if (access_result & NFS_MAY_READ)
316262306a36Sopenharmony_ci		mask |= MAY_READ;
316362306a36Sopenharmony_ci	if (S_ISDIR(umode)) {
316462306a36Sopenharmony_ci		if ((access_result & NFS_DIR_MAY_WRITE) == NFS_DIR_MAY_WRITE)
316562306a36Sopenharmony_ci			mask |= MAY_WRITE;
316662306a36Sopenharmony_ci		if ((access_result & NFS_MAY_LOOKUP) == NFS_MAY_LOOKUP)
316762306a36Sopenharmony_ci			mask |= MAY_EXEC;
316862306a36Sopenharmony_ci	} else if (S_ISREG(umode)) {
316962306a36Sopenharmony_ci		if ((access_result & NFS_FILE_MAY_WRITE) == NFS_FILE_MAY_WRITE)
317062306a36Sopenharmony_ci			mask |= MAY_WRITE;
317162306a36Sopenharmony_ci		if ((access_result & NFS_MAY_EXECUTE) == NFS_MAY_EXECUTE)
317262306a36Sopenharmony_ci			mask |= MAY_EXEC;
317362306a36Sopenharmony_ci	} else if (access_result & NFS_MAY_WRITE)
317462306a36Sopenharmony_ci			mask |= MAY_WRITE;
317562306a36Sopenharmony_ci	return mask;
317662306a36Sopenharmony_ci}
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_civoid nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
317962306a36Sopenharmony_ci{
318062306a36Sopenharmony_ci	entry->mask = access_result;
318162306a36Sopenharmony_ci}
318262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_access_set_mask);
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_cistatic int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
318562306a36Sopenharmony_ci{
318662306a36Sopenharmony_ci	struct nfs_access_entry cache;
318762306a36Sopenharmony_ci	bool may_block = (mask & MAY_NOT_BLOCK) == 0;
318862306a36Sopenharmony_ci	int cache_mask = -1;
318962306a36Sopenharmony_ci	int status;
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	trace_nfs_access_enter(inode);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	status = nfs_access_get_cached(inode, cred, &cache.mask, may_block);
319462306a36Sopenharmony_ci	if (status == 0)
319562306a36Sopenharmony_ci		goto out_cached;
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	status = -ECHILD;
319862306a36Sopenharmony_ci	if (!may_block)
319962306a36Sopenharmony_ci		goto out;
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci	/*
320262306a36Sopenharmony_ci	 * Determine which access bits we want to ask for...
320362306a36Sopenharmony_ci	 */
320462306a36Sopenharmony_ci	cache.mask = NFS_ACCESS_READ | NFS_ACCESS_MODIFY | NFS_ACCESS_EXTEND |
320562306a36Sopenharmony_ci		     nfs_access_xattr_mask(NFS_SERVER(inode));
320662306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
320762306a36Sopenharmony_ci		cache.mask |= NFS_ACCESS_DELETE | NFS_ACCESS_LOOKUP;
320862306a36Sopenharmony_ci	else
320962306a36Sopenharmony_ci		cache.mask |= NFS_ACCESS_EXECUTE;
321062306a36Sopenharmony_ci	status = NFS_PROTO(inode)->access(inode, &cache, cred);
321162306a36Sopenharmony_ci	if (status != 0) {
321262306a36Sopenharmony_ci		if (status == -ESTALE) {
321362306a36Sopenharmony_ci			if (!S_ISDIR(inode->i_mode))
321462306a36Sopenharmony_ci				nfs_set_inode_stale(inode);
321562306a36Sopenharmony_ci			else
321662306a36Sopenharmony_ci				nfs_zap_caches(inode);
321762306a36Sopenharmony_ci		}
321862306a36Sopenharmony_ci		goto out;
321962306a36Sopenharmony_ci	}
322062306a36Sopenharmony_ci	nfs_access_add_cache(inode, &cache, cred);
322162306a36Sopenharmony_ciout_cached:
322262306a36Sopenharmony_ci	cache_mask = nfs_access_calc_mask(cache.mask, inode->i_mode);
322362306a36Sopenharmony_ci	if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
322462306a36Sopenharmony_ci		status = -EACCES;
322562306a36Sopenharmony_ciout:
322662306a36Sopenharmony_ci	trace_nfs_access_exit(inode, mask, cache_mask, status);
322762306a36Sopenharmony_ci	return status;
322862306a36Sopenharmony_ci}
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_cistatic int nfs_open_permission_mask(int openflags)
323162306a36Sopenharmony_ci{
323262306a36Sopenharmony_ci	int mask = 0;
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci	if (openflags & __FMODE_EXEC) {
323562306a36Sopenharmony_ci		/* ONLY check exec rights */
323662306a36Sopenharmony_ci		mask = MAY_EXEC;
323762306a36Sopenharmony_ci	} else {
323862306a36Sopenharmony_ci		if ((openflags & O_ACCMODE) != O_WRONLY)
323962306a36Sopenharmony_ci			mask |= MAY_READ;
324062306a36Sopenharmony_ci		if ((openflags & O_ACCMODE) != O_RDONLY)
324162306a36Sopenharmony_ci			mask |= MAY_WRITE;
324262306a36Sopenharmony_ci	}
324362306a36Sopenharmony_ci
324462306a36Sopenharmony_ci	return mask;
324562306a36Sopenharmony_ci}
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ciint nfs_may_open(struct inode *inode, const struct cred *cred, int openflags)
324862306a36Sopenharmony_ci{
324962306a36Sopenharmony_ci	return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
325062306a36Sopenharmony_ci}
325162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_may_open);
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_cistatic int nfs_execute_ok(struct inode *inode, int mask)
325462306a36Sopenharmony_ci{
325562306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(inode);
325662306a36Sopenharmony_ci	int ret = 0;
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
325962306a36Sopenharmony_ci		return 0;
326062306a36Sopenharmony_ci	if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_MODE)) {
326162306a36Sopenharmony_ci		if (mask & MAY_NOT_BLOCK)
326262306a36Sopenharmony_ci			return -ECHILD;
326362306a36Sopenharmony_ci		ret = __nfs_revalidate_inode(server, inode);
326462306a36Sopenharmony_ci	}
326562306a36Sopenharmony_ci	if (ret == 0 && !execute_ok(inode))
326662306a36Sopenharmony_ci		ret = -EACCES;
326762306a36Sopenharmony_ci	return ret;
326862306a36Sopenharmony_ci}
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ciint nfs_permission(struct mnt_idmap *idmap,
327162306a36Sopenharmony_ci		   struct inode *inode,
327262306a36Sopenharmony_ci		   int mask)
327362306a36Sopenharmony_ci{
327462306a36Sopenharmony_ci	const struct cred *cred = current_cred();
327562306a36Sopenharmony_ci	int res = 0;
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSACCESS);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
328062306a36Sopenharmony_ci		goto out;
328162306a36Sopenharmony_ci	/* Is this sys_access() ? */
328262306a36Sopenharmony_ci	if (mask & (MAY_ACCESS | MAY_CHDIR))
328362306a36Sopenharmony_ci		goto force_lookup;
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	switch (inode->i_mode & S_IFMT) {
328662306a36Sopenharmony_ci		case S_IFLNK:
328762306a36Sopenharmony_ci			goto out;
328862306a36Sopenharmony_ci		case S_IFREG:
328962306a36Sopenharmony_ci			if ((mask & MAY_OPEN) &&
329062306a36Sopenharmony_ci			   nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN))
329162306a36Sopenharmony_ci				return 0;
329262306a36Sopenharmony_ci			break;
329362306a36Sopenharmony_ci		case S_IFDIR:
329462306a36Sopenharmony_ci			/*
329562306a36Sopenharmony_ci			 * Optimize away all write operations, since the server
329662306a36Sopenharmony_ci			 * will check permissions when we perform the op.
329762306a36Sopenharmony_ci			 */
329862306a36Sopenharmony_ci			if ((mask & MAY_WRITE) && !(mask & MAY_READ))
329962306a36Sopenharmony_ci				goto out;
330062306a36Sopenharmony_ci	}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ciforce_lookup:
330362306a36Sopenharmony_ci	if (!NFS_PROTO(inode)->access)
330462306a36Sopenharmony_ci		goto out_notsup;
330562306a36Sopenharmony_ci
330662306a36Sopenharmony_ci	res = nfs_do_access(inode, cred, mask);
330762306a36Sopenharmony_ciout:
330862306a36Sopenharmony_ci	if (!res && (mask & MAY_EXEC))
330962306a36Sopenharmony_ci		res = nfs_execute_ok(inode, mask);
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	dfprintk(VFS, "NFS: permission(%s/%lu), mask=0x%x, res=%d\n",
331262306a36Sopenharmony_ci		inode->i_sb->s_id, inode->i_ino, mask, res);
331362306a36Sopenharmony_ci	return res;
331462306a36Sopenharmony_ciout_notsup:
331562306a36Sopenharmony_ci	if (mask & MAY_NOT_BLOCK)
331662306a36Sopenharmony_ci		return -ECHILD;
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci	res = nfs_revalidate_inode(inode, NFS_INO_INVALID_MODE |
331962306a36Sopenharmony_ci						  NFS_INO_INVALID_OTHER);
332062306a36Sopenharmony_ci	if (res == 0)
332162306a36Sopenharmony_ci		res = generic_permission(&nop_mnt_idmap, inode, mask);
332262306a36Sopenharmony_ci	goto out;
332362306a36Sopenharmony_ci}
332462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_permission);
3325