linux/fs/nfs/dir.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/fs/nfs/dir.c
 *
 *  Copyright (C) 1992  Rick Sladkey
 *
 *  nfs directory handling functions
 *
 * 10 Apr 1996	Added silly rename for unlink	--okir
 * 28 Sep 1996	Improved directory cache --okir
 * 23 Aug 1997  Claus Heine [email protected] 
 *              Re-implemented silly rename for unlink, newly implemented
 *              silly rename for nfs_rename() following the suggestions
 *              of Olaf Kirch (okir) found in this file.
 *              Following Linus comments on my original hack, this version
 *              depends only on the dcache stuff and doesn't touch the inode
 *              layer (iput() and friends).
 *  6 Jun 1999	Cache readdir lookups in the page cache. -DaveM
 */

#include <linux/compat.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/pagemap.h>
#include <linux/pagevec.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/swap.h>
#include <linux/sched.h>
#include <linux/kmemleak.h>
#include <linux/xattr.h>
#include <linux/hash.h>

#include "delegation.h"
#include "iostat.h"
#include "internal.h"
#include "fscache.h"

#include "nfstrace.h"

/* #define NFS_DEBUG_VERBOSE 1 */

static int nfs_opendir(struct inode *, struct file *);
static int nfs_closedir(struct inode *, struct file *);
static int nfs_readdir(struct file *, struct dir_context *);
static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
static loff_t nfs_llseek_dir(struct file *, loff_t, int);
static void nfs_readdir_clear_array(struct folio *);
static int nfs_do_create(struct inode *dir, struct dentry *dentry,
			 umode_t mode, int open_flags);

const struct file_operations nfs_dir_operations =;

const struct address_space_operations nfs_dir_aops =;

#define NFS_INIT_DTSIZE

static struct nfs_open_dir_context *
alloc_nfs_open_dir_context(struct inode *dir)
{}

static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_context *ctx)
{}

/*
 * Open file
 */
static int
nfs_opendir(struct inode *inode, struct file *filp)
{}

static int
nfs_closedir(struct inode *inode, struct file *filp)
{}

struct nfs_cache_array_entry {};

struct nfs_cache_array {};

struct nfs_readdir_descriptor {};

static void nfs_set_dtsize(struct nfs_readdir_descriptor *desc, unsigned int sz)
{}

static void nfs_shrink_dtsize(struct nfs_readdir_descriptor *desc)
{}

static void nfs_grow_dtsize(struct nfs_readdir_descriptor *desc)
{}

static void nfs_readdir_folio_init_array(struct folio *folio, u64 last_cookie,
					 u64 change_attr)
{}

/*
 * we are freeing strings created by nfs_add_to_readdir_array()
 */
static void nfs_readdir_clear_array(struct folio *folio)
{}

static void nfs_readdir_folio_reinit_array(struct folio *folio, u64 last_cookie,
					   u64 change_attr)
{}

static struct folio *
nfs_readdir_folio_array_alloc(u64 last_cookie, gfp_t gfp_flags)
{}

static void nfs_readdir_folio_array_free(struct folio *folio)
{}

static u64 nfs_readdir_array_index_cookie(struct nfs_cache_array *array)
{}

static void nfs_readdir_array_set_eof(struct nfs_cache_array *array)
{}

static bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
{}

/*
 * the caller is responsible for freeing qstr.name
 * when called by nfs_readdir_add_to_array, the strings will be freed in
 * nfs_clear_readdir_array()
 */
static const char *nfs_readdir_copy_name(const char *name, unsigned int len)
{}

static size_t nfs_readdir_array_maxentries(void)
{}

/*
 * Check that the next array entry lies entirely within the page bounds
 */
static int nfs_readdir_array_can_expand(struct nfs_cache_array *array)
{}

static int nfs_readdir_folio_array_append(struct folio *folio,
					  const struct nfs_entry *entry,
					  u64 *cookie)
{}

#define NFS_READDIR_COOKIE_MASK
/*
 * Hash algorithm allowing content addressible access to sequences
 * of directory cookies. Content is addressed by the value of the
 * cookie index of the first readdir entry in a page.
 *
 * We select only the first 18 bits to avoid issues with excessive
 * memory use for the page cache XArray. 18 bits should allow the caching
 * of 262144 pages of sequences of readdir entries. Since each page holds
 * 127 readdir entries for a typical 64-bit system, that works out to a
 * cache of ~ 33 million entries per directory.
 */
static pgoff_t nfs_readdir_folio_cookie_hash(u64 cookie)
{}

static bool nfs_readdir_folio_validate(struct folio *folio, u64 last_cookie,
				       u64 change_attr)
{}

static void nfs_readdir_folio_unlock_and_put(struct folio *folio)
{}

static void nfs_readdir_folio_init_and_validate(struct folio *folio, u64 cookie,
						u64 change_attr)
{}

static struct folio *nfs_readdir_folio_get_locked(struct address_space *mapping,
						  u64 cookie, u64 change_attr)
{}

static u64 nfs_readdir_folio_last_cookie(struct folio *folio)
{}

static bool nfs_readdir_folio_needs_filling(struct folio *folio)
{}

static void nfs_readdir_folio_set_eof(struct folio *folio)
{}

static struct folio *nfs_readdir_folio_get_next(struct address_space *mapping,
						u64 cookie, u64 change_attr)
{}

static inline
int is_32bit_api(void)
{}

static
bool nfs_readdir_use_cookie(const struct file *filp)
{}

static void nfs_readdir_seek_next_array(struct nfs_cache_array *array,
					struct nfs_readdir_descriptor *desc)
{}

static void nfs_readdir_rewind_search(struct nfs_readdir_descriptor *desc)
{}

static int nfs_readdir_search_for_pos(struct nfs_cache_array *array,
				      struct nfs_readdir_descriptor *desc)
{}

static bool nfs_readdir_array_cookie_in_range(struct nfs_cache_array *array,
					      u64 cookie)
{}

static int nfs_readdir_search_for_cookie(struct nfs_cache_array *array,
					 struct nfs_readdir_descriptor *desc)
{}

static int nfs_readdir_search_array(struct nfs_readdir_descriptor *desc)
{}

/* Fill a page with xdr information before transferring to the cache page */
static int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
				  __be32 *verf, u64 cookie,
				  struct page **pages, size_t bufsize,
				  __be32 *verf_res)
{}

static int xdr_decode(struct nfs_readdir_descriptor *desc,
		      struct nfs_entry *entry, struct xdr_stream *xdr)
{}

/* Match file and dirent using either filehandle or fileid
 * Note: caller is responsible for checking the fsid
 */
static
int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
{}

#define NFS_READDIR_CACHE_USAGE_THRESHOLD

static bool nfs_use_readdirplus(struct inode *dir, struct dir_context *ctx,
				unsigned int cache_hits,
				unsigned int cache_misses)
{}

/*
 * This function is called by the getattr code to request the
 * use of readdirplus to accelerate any future lookups in the same
 * directory.
 */
void nfs_readdir_record_entry_cache_hit(struct inode *dir)
{}

/*
 * This function is mainly for use by nfs_getattr().
 *
 * If this is an 'ls -l', we want to force use of readdirplus.
 */
void nfs_readdir_record_entry_cache_miss(struct inode *dir)
{}

static void nfs_lookup_advise_force_readdirplus(struct inode *dir,
						unsigned int flags)
{}

static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
		unsigned long dir_verifier)
{}

static int nfs_readdir_entry_decode(struct nfs_readdir_descriptor *desc,
				    struct nfs_entry *entry,
				    struct xdr_stream *stream)
{}

/* Perform conversion from xdr to cache array */
static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
				    struct nfs_entry *entry,
				    struct page **xdr_pages, unsigned int buflen,
				    struct folio **arrays, size_t narrays,
				    u64 change_attr)
{}

static void nfs_readdir_free_pages(struct page **pages, size_t npages)
{}

/*
 * nfs_readdir_alloc_pages() will allocate pages that must be freed with a call
 * to nfs_readdir_free_pages()
 */
static struct page **nfs_readdir_alloc_pages(size_t npages)
{}

static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
				    __be32 *verf_arg, __be32 *verf_res,
				    struct folio **arrays, size_t narrays)
{}

static void nfs_readdir_folio_put(struct nfs_readdir_descriptor *desc)
{}

static void
nfs_readdir_folio_unlock_and_put_cached(struct nfs_readdir_descriptor *desc)
{}

static struct folio *
nfs_readdir_folio_get_cached(struct nfs_readdir_descriptor *desc)
{}

/*
 * Returns 0 if desc->dir_cookie was found on page desc->page_index
 * and locks the page to prevent removal from the page cache.
 */
static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
{}

/* Search for desc->dir_cookie from the beginning of the page cache */
static int readdir_search_pagecache(struct nfs_readdir_descriptor *desc)
{}

#define NFS_READDIR_CACHE_MISS_THRESHOLD

/*
 * Once we've found the start of the dirent within a page: fill 'er up...
 */
static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
			   const __be32 *verf)
{}

/*
 * If we cannot find a cookie in our cache, we suspect that this is
 * because it points to a deleted file, so we ask the server to return
 * whatever it thinks is the next entry. We then feed this to filldir.
 * If all goes well, we should then be able to find our way round the
 * cache on the next call to readdir_search_pagecache();
 *
 * NOTE: we cannot add the anonymous page to the pagecache because
 *	 the data it contains might not be page aligned. Besides,
 *	 we should already have a complete representation of the
 *	 directory in the page cache by the time we get here.
 */
static int uncached_readdir(struct nfs_readdir_descriptor *desc)
{}

static bool nfs_readdir_handle_cache_misses(struct inode *inode,
					    struct nfs_readdir_descriptor *desc,
					    unsigned int cache_misses,
					    bool force_clear)
{}

/* The file offset position represents the dirent entry number.  A
   last cookie cache takes care of the common case of reading the
   whole directory.
 */
static int nfs_readdir(struct file *file, struct dir_context *ctx)
{}

static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
{}

/*
 * All directory operations under NFS are synchronous, so fsync()
 * is a dummy operation.
 */
static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
			 int datasync)
{}

/**
 * nfs_force_lookup_revalidate - Mark the directory as having changed
 * @dir: pointer to directory inode
 *
 * This forces the revalidation code in nfs_lookup_revalidate() to do a
 * full lookup on all child dentries of 'dir' whenever a change occurs
 * on the server that might have invalidated our dcache.
 *
 * Note that we reserve bit '0' as a tag to let us know when a dentry
 * was revalidated while holding a delegation on its inode.
 *
 * The caller should be holding dir->i_lock
 */
void nfs_force_lookup_revalidate(struct inode *dir)
{}
EXPORT_SYMBOL_GPL();

/**
 * nfs_verify_change_attribute - Detects NFS remote directory changes
 * @dir: pointer to parent directory inode
 * @verf: previously saved change attribute
 *
 * Return "false" if the verifiers doesn't match the change attribute.
 * This would usually indicate that the directory contents have changed on
 * the server, and that any dentries need revalidating.
 */
static bool nfs_verify_change_attribute(struct inode *dir, unsigned long verf)
{}

static void nfs_set_verifier_delegated(unsigned long *verf)
{}

#if IS_ENABLED(CONFIG_NFS_V4)
static void nfs_unset_verifier_delegated(unsigned long *verf)
{}
#endif /* IS_ENABLED(CONFIG_NFS_V4) */

static bool nfs_test_verifier_delegated(unsigned long verf)
{}

static bool nfs_verifier_is_delegated(struct dentry *dentry)
{}

static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
{}

/**
 * nfs_set_verifier - save a parent directory verifier in the dentry
 * @dentry: pointer to dentry
 * @verf: verifier to save
 *
 * Saves the parent directory verifier in @dentry. If the inode has
 * a delegation, we also tag the dentry as having been revalidated
 * while holding a delegation so that we know we don't have to
 * look it up again after a directory change.
 */
void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
{}
EXPORT_SYMBOL_GPL();

#if IS_ENABLED(CONFIG_NFS_V4)
/**
 * nfs_clear_verifier_delegated - clear the dir verifier delegation tag
 * @inode: pointer to inode
 *
 * Iterates through the dentries in the inode alias list and clears
 * the tag used to indicate that the dentry has been revalidated
 * while holding a delegation.
 * This function is intended for use when the delegation is being
 * returned or revoked.
 */
void nfs_clear_verifier_delegated(struct inode *inode)
{}
EXPORT_SYMBOL_GPL();
#endif /* IS_ENABLED(CONFIG_NFS_V4) */

static int nfs_dentry_verify_change(struct inode *dir, struct dentry *dentry)
{}

/*
 * A check for whether or not the parent directory has changed.
 * In the case it has, we assume that the dentries are untrustworthy
 * and may need to be looked up again.
 * If rcu_walk prevents us from performing a full check, return 0.
 */
static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
			      int rcu_walk)
{}

/*
 * Use intent information to check whether or not we're going to do
 * an O_EXCL create using this path component.
 */
static int nfs_is_exclusive_create(struct inode *dir, unsigned int flags)
{}

/*
 * Inode and filehandle revalidation for lookups.
 *
 * We force revalidation in the cases where the VFS sets LOOKUP_REVAL,
 * or if the intent information indicates that we're about to open this
 * particular file and the "nocto" mount flag is not set.
 *
 */
static
int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags)
{}

static void nfs_mark_dir_for_revalidate(struct inode *inode)
{}

/*
 * We judge how long we want to trust negative
 * dentries by looking at the parent inode mtime.
 *
 * If parent mtime has changed, we revalidate, else we wait for a
 * period corresponding to the parent's attribute cache timeout value.
 *
 * If LOOKUP_RCU prevents us from performing a full check, return 1
 * suggesting a reval is needed.
 *
 * Note that when creating a new file, or looking up a rename target,
 * then it shouldn't be necessary to revalidate a negative dentry.
 */
static inline
int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
		       unsigned int flags)
{}

static int
nfs_lookup_revalidate_done(struct inode *dir, struct dentry *dentry,
			   struct inode *inode, int error)
{}

static int
nfs_lookup_revalidate_negative(struct inode *dir, struct dentry *dentry,
			       unsigned int flags)
{}

static int
nfs_lookup_revalidate_delegated(struct inode *dir, struct dentry *dentry,
				struct inode *inode)
{}

static int nfs_lookup_revalidate_dentry(struct inode *dir,
					struct dentry *dentry,
					struct inode *inode, unsigned int flags)
{}

/*
 * This is called every time the dcache has a lookup hit,
 * and we should check whether we can really trust that
 * lookup.
 *
 * NOTE! The hit can be a negative hit too, don't assume
 * we have an inode!
 *
 * If the parent directory is seen to have changed, we throw out the
 * cached dentry and do a new lookup.
 */
static int
nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
			 unsigned int flags)
{}

static int
__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
			int (*reval)(struct inode *, struct dentry *, unsigned int))
{}

static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
{}

static void block_revalidate(struct dentry *dentry)
{}

static void unblock_revalidate(struct dentry *dentry)
{}

/*
 * A weaker form of d_revalidate for revalidating just the d_inode(dentry)
 * when we don't really care about the dentry name. This is called when a
 * pathwalk ends on a dentry that was not found via a normal lookup in the
 * parent dir (e.g.: ".", "..", procfs symlinks or mountpoint traversals).
 *
 * In this situation, we just want to verify that the inode itself is OK
 * since the dentry might have changed on the server.
 */
static int nfs_weak_revalidate(struct dentry *dentry, unsigned int flags)
{}

/*
 * This is called from dput() when d_count is going to 0.
 */
static int nfs_dentry_delete(const struct dentry *dentry)
{}

/* Ensure that we revalidate inode->i_nlink */
static void nfs_drop_nlink(struct inode *inode)
{}

/*
 * Called when the dentry loses inode.
 * We use it to clean up silly-renamed files.
 */
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{}

static void nfs_d_release(struct dentry *dentry)
{}

const struct dentry_operations nfs_dentry_operations =;
EXPORT_SYMBOL_GPL();

struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
{}
EXPORT_SYMBOL_GPL();

void nfs_d_prune_case_insensitive_aliases(struct inode *inode)
{}
EXPORT_SYMBOL_GPL();

#if IS_ENABLED(CONFIG_NFS_V4)
static int nfs4_lookup_revalidate(struct dentry *, unsigned int);

const struct dentry_operations nfs4_dentry_operations =;
EXPORT_SYMBOL_GPL();

static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags, struct file *filp)
{}

static int do_open(struct inode *inode, struct file *filp)
{}

static int nfs_finish_open(struct nfs_open_context *ctx,
			   struct dentry *dentry,
			   struct file *file, unsigned open_flags)
{}

int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
		    struct file *file, unsigned open_flags,
		    umode_t mode)
{}
EXPORT_SYMBOL_GPL();

static int
nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
			  unsigned int flags)
{}

static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
{}

#endif /* CONFIG_NFSV4 */

int nfs_atomic_open_v23(struct inode *dir, struct dentry *dentry,
			struct file *file, unsigned int open_flags,
			umode_t mode)
{}
EXPORT_SYMBOL_GPL();

struct dentry *
nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
				struct nfs_fattr *fattr)
{}
EXPORT_SYMBOL_GPL();

/*
 * Code common to create, mkdir, and mknod.
 */
int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
				struct nfs_fattr *fattr)
{}
EXPORT_SYMBOL_GPL();

/*
 * Following a failed create operation, we drop the dentry rather
 * than retain a negative dentry. This avoids a problem in the event
 * that the operation succeeded on the server, but an error in the
 * reply path made it appear to have failed.
 */
static int nfs_do_create(struct inode *dir, struct dentry *dentry,
			 umode_t mode, int open_flags)
{}

int nfs_create(struct mnt_idmap *idmap, struct inode *dir,
	       struct dentry *dentry, umode_t mode, bool excl)
{}
EXPORT_SYMBOL_GPL();

/*
 * See comments for nfs_proc_create regarding failed operations.
 */
int
nfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
	  struct dentry *dentry, umode_t mode, dev_t rdev)
{}
EXPORT_SYMBOL_GPL();

/*
 * See comments for nfs_proc_create regarding failed operations.
 */
int nfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
	      struct dentry *dentry, umode_t mode)
{}
EXPORT_SYMBOL_GPL();

static void nfs_dentry_handle_enoent(struct dentry *dentry)
{}

static void nfs_dentry_remove_handle_error(struct inode *dir,
					   struct dentry *dentry, int error)
{}

int nfs_rmdir(struct inode *dir, struct dentry *dentry)
{}
EXPORT_SYMBOL_GPL();

/*
 * Remove a file after making sure there are no pending writes,
 * and after checking that the file has only one user. 
 *
 * We invalidate the attribute cache and free the inode prior to the operation
 * to avoid possible races if the server reuses the inode.
 */
static int nfs_safe_remove(struct dentry *dentry)
{}

/*  We do silly rename. In case sillyrename() returns -EBUSY, the inode
 *  belongs to an active ".nfs..." file and we return -EBUSY.
 *
 *  If sillyrename() returns 0, we do nothing, otherwise we unlink.
 */
int nfs_unlink(struct inode *dir, struct dentry *dentry)
{}
EXPORT_SYMBOL_GPL();

/*
 * To create a symbolic link, most file systems instantiate a new inode,
 * add a page to it containing the path, then write it out to the disk
 * using prepare_write/commit_write.
 *
 * Unfortunately the NFS client can't create the in-core inode first
 * because it needs a file handle to create an in-core inode (see
 * fs/nfs/inode.c:nfs_fhget).  We only have a file handle *after* the
 * symlink request has completed on the server.
 *
 * So instead we allocate a raw page, copy the symname into it, then do
 * the SYMLINK request with the page as the buffer.  If it succeeds, we
 * now have a new file handle and can instantiate an in-core NFS inode
 * and move the raw page into its mapping.
 */
int nfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
		struct dentry *dentry, const char *symname)
{}
EXPORT_SYMBOL_GPL();

int
nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{}
EXPORT_SYMBOL_GPL();

static void
nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data)
{}

/*
 * RENAME
 * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
 * different file handle for the same inode after a rename (e.g. when
 * moving to a different directory). A fail-safe method to do so would
 * be to look up old_dir/old_name, create a link to new_dir/new_name and
 * rename the old file using the sillyrename stuff. This way, the original
 * file in old_dir will go away when the last process iput()s the inode.
 *
 * FIXED.
 * 
 * It actually works quite well. One needs to have the possibility for
 * at least one ".nfs..." file in each directory the file ever gets
 * moved or linked to which happens automagically with the new
 * implementation that only depends on the dcache stuff instead of
 * using the inode layer
 *
 * Unfortunately, things are a little more complicated than indicated
 * above. For a cross-directory move, we want to make sure we can get
 * rid of the old inode after the operation.  This means there must be
 * no pending writes (if it's a file), and the use count must be 1.
 * If these conditions are met, we can drop the dentries before doing
 * the rename.
 */
int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
	       struct dentry *old_dentry, struct inode *new_dir,
	       struct dentry *new_dentry, unsigned int flags)
{}
EXPORT_SYMBOL_GPL();

static DEFINE_SPINLOCK(nfs_access_lru_lock);
static LIST_HEAD(nfs_access_lru_list);
static atomic_long_t nfs_access_nr_entries;

static unsigned long nfs_access_max_cachesize =;
module_param(nfs_access_max_cachesize, ulong, 0644);
MODULE_PARM_DESC();

static void nfs_access_free_entry(struct nfs_access_entry *entry)
{}

static void nfs_access_free_list(struct list_head *head)
{}

static unsigned long
nfs_do_access_cache_scan(unsigned int nr_to_scan)
{}

unsigned long
nfs_access_cache_scan(struct shrinker *shrink, struct shrink_control *sc)
{}


unsigned long
nfs_access_cache_count(struct shrinker *shrink, struct shrink_control *sc)
{}

static void
nfs_access_cache_enforce_limit(void)
{}

static void __nfs_access_zap_cache(struct nfs_inode *nfsi, struct list_head *head)
{}

void nfs_access_zap_cache(struct inode *inode)
{}
EXPORT_SYMBOL_GPL();

static int access_cmp(const struct cred *a, const struct nfs_access_entry *b)
{}

static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, const struct cred *cred)
{}

static u64 nfs_access_login_time(const struct task_struct *task,
				 const struct cred *cred)
{}

static int nfs_access_get_cached_locked(struct inode *inode, const struct cred *cred, u32 *mask, bool may_block)
{}

static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cred, u32 *mask)
{}

int nfs_access_get_cached(struct inode *inode, const struct cred *cred,
			  u32 *mask, bool may_block)
{}
EXPORT_SYMBOL_GPL();

static void nfs_access_add_rbtree(struct inode *inode,
				  struct nfs_access_entry *set,
				  const struct cred *cred)
{}

void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set,
			  const struct cred *cred)
{}
EXPORT_SYMBOL_GPL();

#define NFS_MAY_READ
#define NFS_MAY_WRITE
#define NFS_FILE_MAY_WRITE
#define NFS_DIR_MAY_WRITE
#define NFS_MAY_LOOKUP
#define NFS_MAY_EXECUTE
static int
nfs_access_calc_mask(u32 access_result, umode_t umode)
{}

void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
{}
EXPORT_SYMBOL_GPL();

static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
{}

static int nfs_open_permission_mask(int openflags)
{}

int nfs_may_open(struct inode *inode, const struct cred *cred, int openflags)
{}
EXPORT_SYMBOL_GPL();

static int nfs_execute_ok(struct inode *inode, int mask)
{}

int nfs_permission(struct mnt_idmap *idmap,
		   struct inode *inode,
		   int mask)
{}
EXPORT_SYMBOL_GPL();