mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	NFS: Improve handling of directory verifiers
If the server insists on using the readdir verifiers in order to allow cookies to expire, then we should ensure that we cache the verifier with the cookie, so that we can return an error if the application tries to use the expired cookie. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Reviewed-by: Benjamin Coddington <bcodding@redhat.com> Tested-by: Benjamin Coddington <bcodding@redhat.com> Tested-by: Dave Wysochanski <dwysocha@redhat.com>
This commit is contained in:
		
							parent
							
								
									9fff59ed4c
								
							
						
					
					
						commit
						b593c09f83
					
				
					 3 changed files with 30 additions and 20 deletions
				
			
		
							
								
								
									
										35
									
								
								fs/nfs/dir.c
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								fs/nfs/dir.c
									
									
									
									
									
								
							| 
						 | 
					@ -155,6 +155,7 @@ struct nfs_readdir_descriptor {
 | 
				
			||||||
	loff_t		current_index;
 | 
						loff_t		current_index;
 | 
				
			||||||
	loff_t		prev_index;
 | 
						loff_t		prev_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__be32		verf[NFS_DIR_VERIFIER_SIZE];
 | 
				
			||||||
	unsigned long	dir_verifier;
 | 
						unsigned long	dir_verifier;
 | 
				
			||||||
	unsigned long	timestamp;
 | 
						unsigned long	timestamp;
 | 
				
			||||||
	unsigned long	gencount;
 | 
						unsigned long	gencount;
 | 
				
			||||||
| 
						 | 
					@ -466,15 +467,15 @@ static int nfs_readdir_search_array(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Fill a page with xdr information before transferring to the cache page */
 | 
					/* Fill a page with xdr information before transferring to the cache page */
 | 
				
			||||||
static int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
 | 
					static int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
 | 
				
			||||||
				  u64 cookie, struct page **pages,
 | 
									  __be32 *verf, u64 cookie,
 | 
				
			||||||
				  size_t bufsize)
 | 
									  struct page **pages, size_t bufsize,
 | 
				
			||||||
 | 
									  __be32 *verf_res)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = file_inode(desc->file);
 | 
						struct inode *inode = file_inode(desc->file);
 | 
				
			||||||
	__be32 verf_res[2];
 | 
					 | 
				
			||||||
	struct nfs_readdir_arg arg = {
 | 
						struct nfs_readdir_arg arg = {
 | 
				
			||||||
		.dentry = file_dentry(desc->file),
 | 
							.dentry = file_dentry(desc->file),
 | 
				
			||||||
		.cred = desc->file->f_cred,
 | 
							.cred = desc->file->f_cred,
 | 
				
			||||||
		.verf = NFS_I(inode)->cookieverf,
 | 
							.verf = verf,
 | 
				
			||||||
		.cookie = cookie,
 | 
							.cookie = cookie,
 | 
				
			||||||
		.pages = pages,
 | 
							.pages = pages,
 | 
				
			||||||
		.page_len = bufsize,
 | 
							.page_len = bufsize,
 | 
				
			||||||
| 
						 | 
					@ -503,8 +504,6 @@ static int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	desc->timestamp = timestamp;
 | 
						desc->timestamp = timestamp;
 | 
				
			||||||
	desc->gencount = gencount;
 | 
						desc->gencount = gencount;
 | 
				
			||||||
	memcpy(NFS_I(inode)->cookieverf, res.verf,
 | 
					 | 
				
			||||||
	       sizeof(NFS_I(inode)->cookieverf));
 | 
					 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -770,11 +769,13 @@ static struct page **nfs_readdir_alloc_pages(size_t npages)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
 | 
					static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
 | 
				
			||||||
				    struct page *page, struct inode *inode)
 | 
									    struct page *page, __be32 *verf_arg,
 | 
				
			||||||
 | 
									    __be32 *verf_res)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct page **pages;
 | 
						struct page **pages;
 | 
				
			||||||
	struct nfs_entry *entry;
 | 
						struct nfs_entry *entry;
 | 
				
			||||||
	size_t array_size;
 | 
						size_t array_size;
 | 
				
			||||||
 | 
						struct inode *inode = file_inode(desc->file);
 | 
				
			||||||
	size_t dtsize = NFS_SERVER(inode)->dtsize;
 | 
						size_t dtsize = NFS_SERVER(inode)->dtsize;
 | 
				
			||||||
	int status = -ENOMEM;
 | 
						int status = -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -801,8 +802,9 @@ static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		unsigned int pglen;
 | 
							unsigned int pglen;
 | 
				
			||||||
		status = nfs_readdir_xdr_filler(desc, entry->cookie,
 | 
							status = nfs_readdir_xdr_filler(desc, verf_arg, entry->cookie,
 | 
				
			||||||
						pages, dtsize);
 | 
											pages, dtsize,
 | 
				
			||||||
 | 
											verf_res);
 | 
				
			||||||
		if (status < 0)
 | 
							if (status < 0)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -854,13 +856,15 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = file_inode(desc->file);
 | 
						struct inode *inode = file_inode(desc->file);
 | 
				
			||||||
	struct nfs_inode *nfsi = NFS_I(inode);
 | 
						struct nfs_inode *nfsi = NFS_I(inode);
 | 
				
			||||||
 | 
						__be32 verf[NFS_DIR_VERIFIER_SIZE];
 | 
				
			||||||
	int res;
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	desc->page = nfs_readdir_page_get_cached(desc);
 | 
						desc->page = nfs_readdir_page_get_cached(desc);
 | 
				
			||||||
	if (!desc->page)
 | 
						if (!desc->page)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	if (nfs_readdir_page_needs_filling(desc->page)) {
 | 
						if (nfs_readdir_page_needs_filling(desc->page)) {
 | 
				
			||||||
		res = nfs_readdir_xdr_to_array(desc, desc->page, inode);
 | 
							res = nfs_readdir_xdr_to_array(desc, desc->page,
 | 
				
			||||||
 | 
										       nfsi->cookieverf, verf);
 | 
				
			||||||
		if (res < 0) {
 | 
							if (res < 0) {
 | 
				
			||||||
			nfs_readdir_page_unlock_and_put_cached(desc);
 | 
								nfs_readdir_page_unlock_and_put_cached(desc);
 | 
				
			||||||
			if (res == -EBADCOOKIE || res == -ENOTSYNC) {
 | 
								if (res == -EBADCOOKIE || res == -ENOTSYNC) {
 | 
				
			||||||
| 
						 | 
					@ -870,6 +874,7 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return res;
 | 
								return res;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							memcpy(nfsi->cookieverf, verf, sizeof(nfsi->cookieverf));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	res = nfs_readdir_search_array(desc);
 | 
						res = nfs_readdir_search_array(desc);
 | 
				
			||||||
	if (res == 0) {
 | 
						if (res == 0) {
 | 
				
			||||||
| 
						 | 
					@ -902,6 +907,7 @@ static int readdir_search_pagecache(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
static void nfs_do_filldir(struct nfs_readdir_descriptor *desc)
 | 
					static void nfs_do_filldir(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct file	*file = desc->file;
 | 
						struct file	*file = desc->file;
 | 
				
			||||||
 | 
						struct nfs_inode *nfsi = NFS_I(file_inode(file));
 | 
				
			||||||
	struct nfs_cache_array *array;
 | 
						struct nfs_cache_array *array;
 | 
				
			||||||
	unsigned int i = 0;
 | 
						unsigned int i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -915,6 +921,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
			desc->eof = true;
 | 
								desc->eof = true;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							memcpy(desc->verf, nfsi->cookieverf, sizeof(desc->verf));
 | 
				
			||||||
		if (i < (array->size-1))
 | 
							if (i < (array->size-1))
 | 
				
			||||||
			desc->dir_cookie = array->array[i+1].cookie;
 | 
								desc->dir_cookie = array->array[i+1].cookie;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
| 
						 | 
					@ -949,8 +956,8 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
static int uncached_readdir(struct nfs_readdir_descriptor *desc)
 | 
					static int uncached_readdir(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct page	*page = NULL;
 | 
						struct page	*page = NULL;
 | 
				
			||||||
 | 
						__be32		verf[NFS_DIR_VERIFIER_SIZE];
 | 
				
			||||||
	int		status;
 | 
						int		status;
 | 
				
			||||||
	struct inode *inode = file_inode(desc->file);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
 | 
						dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
 | 
				
			||||||
			(unsigned long long)desc->dir_cookie);
 | 
								(unsigned long long)desc->dir_cookie);
 | 
				
			||||||
| 
						 | 
					@ -967,7 +974,7 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
 | 
				
			||||||
	desc->duped = 0;
 | 
						desc->duped = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nfs_readdir_page_init_array(page, desc->dir_cookie);
 | 
						nfs_readdir_page_init_array(page, desc->dir_cookie);
 | 
				
			||||||
	status = nfs_readdir_xdr_to_array(desc, page, inode);
 | 
						status = nfs_readdir_xdr_to_array(desc, page, desc->verf, verf);
 | 
				
			||||||
	if (status < 0)
 | 
						if (status < 0)
 | 
				
			||||||
		goto out_release;
 | 
							goto out_release;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1023,6 +1030,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
 | 
				
			||||||
	desc->dup_cookie = dir_ctx->dup_cookie;
 | 
						desc->dup_cookie = dir_ctx->dup_cookie;
 | 
				
			||||||
	desc->duped = dir_ctx->duped;
 | 
						desc->duped = dir_ctx->duped;
 | 
				
			||||||
	desc->attr_gencount = dir_ctx->attr_gencount;
 | 
						desc->attr_gencount = dir_ctx->attr_gencount;
 | 
				
			||||||
 | 
						memcpy(desc->verf, dir_ctx->verf, sizeof(desc->verf));
 | 
				
			||||||
	spin_unlock(&file->f_lock);
 | 
						spin_unlock(&file->f_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
| 
						 | 
					@ -1061,6 +1069,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
 | 
				
			||||||
	dir_ctx->dup_cookie = desc->dup_cookie;
 | 
						dir_ctx->dup_cookie = desc->dup_cookie;
 | 
				
			||||||
	dir_ctx->duped = desc->duped;
 | 
						dir_ctx->duped = desc->duped;
 | 
				
			||||||
	dir_ctx->attr_gencount = desc->attr_gencount;
 | 
						dir_ctx->attr_gencount = desc->attr_gencount;
 | 
				
			||||||
 | 
						memcpy(dir_ctx->verf, desc->verf, sizeof(dir_ctx->verf));
 | 
				
			||||||
	spin_unlock(&file->f_lock);
 | 
						spin_unlock(&file->f_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(desc);
 | 
						kfree(desc);
 | 
				
			||||||
| 
						 | 
					@ -1101,6 +1110,8 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
 | 
				
			||||||
			dir_ctx->dir_cookie = offset;
 | 
								dir_ctx->dir_cookie = offset;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			dir_ctx->dir_cookie = 0;
 | 
								dir_ctx->dir_cookie = 0;
 | 
				
			||||||
 | 
							if (offset == 0)
 | 
				
			||||||
 | 
								memset(dir_ctx->verf, 0, sizeof(dir_ctx->verf));
 | 
				
			||||||
		dir_ctx->duped = 0;
 | 
							dir_ctx->duped = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	spin_unlock(&filp->f_lock);
 | 
						spin_unlock(&filp->f_lock);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,7 +229,6 @@ static void nfs_zap_caches_locked(struct inode *inode)
 | 
				
			||||||
	nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
 | 
						nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
 | 
				
			||||||
	nfsi->attrtimeo_timestamp = jiffies;
 | 
						nfsi->attrtimeo_timestamp = jiffies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
 | 
					 | 
				
			||||||
	if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
 | 
						if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
 | 
				
			||||||
		nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
 | 
							nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
 | 
				
			||||||
					| NFS_INO_INVALID_DATA
 | 
										| NFS_INO_INVALID_DATA
 | 
				
			||||||
| 
						 | 
					@ -1237,7 +1236,6 @@ EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
 | 
					static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nfs_inode *nfsi = NFS_I(inode);
 | 
					 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mapping->nrpages != 0) {
 | 
						if (mapping->nrpages != 0) {
 | 
				
			||||||
| 
						 | 
					@ -1250,11 +1248,6 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
 | 
				
			||||||
		if (ret < 0)
 | 
							if (ret < 0)
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (S_ISDIR(inode->i_mode)) {
 | 
					 | 
				
			||||||
		spin_lock(&inode->i_lock);
 | 
					 | 
				
			||||||
		memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
 | 
					 | 
				
			||||||
		spin_unlock(&inode->i_lock);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
 | 
						nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
 | 
				
			||||||
	nfs_fscache_wait_on_invalidate(inode);
 | 
						nfs_fscache_wait_on_invalidate(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,6 +45,11 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define NFS_RPC_SWAPFLAGS		(RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
 | 
					#define NFS_RPC_SWAPFLAGS		(RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Size of the NFS directory verifier
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define NFS_DIR_VERIFIER_SIZE		2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * NFSv3/v4 Access mode cache entry
 | 
					 * NFSv3/v4 Access mode cache entry
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -89,6 +94,7 @@ struct nfs_open_context {
 | 
				
			||||||
struct nfs_open_dir_context {
 | 
					struct nfs_open_dir_context {
 | 
				
			||||||
	struct list_head list;
 | 
						struct list_head list;
 | 
				
			||||||
	unsigned long attr_gencount;
 | 
						unsigned long attr_gencount;
 | 
				
			||||||
 | 
						__be32	verf[NFS_DIR_VERIFIER_SIZE];
 | 
				
			||||||
	__u64 dir_cookie;
 | 
						__u64 dir_cookie;
 | 
				
			||||||
	__u64 dup_cookie;
 | 
						__u64 dup_cookie;
 | 
				
			||||||
	signed char duped;
 | 
						signed char duped;
 | 
				
			||||||
| 
						 | 
					@ -156,7 +162,7 @@ struct nfs_inode {
 | 
				
			||||||
	 * This is the cookie verifier used for NFSv3 readdir
 | 
						 * This is the cookie verifier used for NFSv3 readdir
 | 
				
			||||||
	 * operations
 | 
						 * operations
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	__be32			cookieverf[2];
 | 
						__be32			cookieverf[NFS_DIR_VERIFIER_SIZE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	atomic_long_t		nrequests;
 | 
						atomic_long_t		nrequests;
 | 
				
			||||||
	struct nfs_mds_commit_info commit_info;
 | 
						struct nfs_mds_commit_info commit_info;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue