diff options
Diffstat (limited to 'fs/nfs/dir.c')
| -rw-r--r-- | fs/nfs/dir.c | 57 | 
1 files changed, 34 insertions, 23 deletions
| diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 57f578e2560a..b238d95ac48c 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -134,18 +134,19 @@ const struct inode_operations nfs4_dir_inode_operations = {  #endif /* CONFIG_NFS_V4 */ -static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred) +static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)  {  	struct nfs_open_dir_context *ctx;  	ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);  	if (ctx != NULL) {  		ctx->duped = 0; +		ctx->attr_gencount = NFS_I(dir)->attr_gencount;  		ctx->dir_cookie = 0;  		ctx->dup_cookie = 0;  		ctx->cred = get_rpccred(cred); -	} else -		ctx = ERR_PTR(-ENOMEM); -	return ctx; +		return ctx; +	} +	return  ERR_PTR(-ENOMEM);  }  static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) @@ -173,7 +174,7 @@ nfs_opendir(struct inode *inode, struct file *filp)  	cred = rpc_lookup_cred();  	if (IS_ERR(cred))  		return PTR_ERR(cred); -	ctx = alloc_nfs_open_dir_context(cred); +	ctx = alloc_nfs_open_dir_context(inode, cred);  	if (IS_ERR(ctx)) {  		res = PTR_ERR(ctx);  		goto out; @@ -323,7 +324,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri  {  	loff_t diff = desc->file->f_pos - desc->current_index;  	unsigned int index; -	struct nfs_open_dir_context *ctx = desc->file->private_data;  	if (diff < 0)  		goto out_eof; @@ -336,7 +336,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri  	index = (unsigned int)diff;  	*desc->dir_cookie = array->array[index].cookie;  	desc->cache_entry_index = index; -	ctx->duped = 0;  	return 0;  out_eof:  	desc->eof = 1; @@ -349,14 +348,34 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des  	int i;  	loff_t new_pos;  	int status = -EAGAIN; -	struct nfs_open_dir_context *ctx = desc->file->private_data;  	for (i = 0; i < array->size; i++) {  		if (array->array[i].cookie == *desc->dir_cookie) { +			struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode); +			struct nfs_open_dir_context *ctx = desc->file->private_data; +  			new_pos = desc->current_index + i; -			if (new_pos < desc->file->f_pos) { +			if (ctx->attr_gencount != nfsi->attr_gencount +			    || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { +				ctx->duped = 0; +				ctx->attr_gencount = nfsi->attr_gencount; +			} else if (new_pos < desc->file->f_pos) { +				if (ctx->duped > 0 +				    && ctx->dup_cookie == *desc->dir_cookie) { +					if (printk_ratelimit()) { +						pr_notice("NFS: directory %s/%s contains a readdir loop." +								"Please contact your server vendor.  " +								"The file: %s has duplicate cookie %llu\n", +								desc->file->f_dentry->d_parent->d_name.name, +								desc->file->f_dentry->d_name.name, +								array->array[i].string.name, +								*desc->dir_cookie); +					} +					status = -ELOOP; +					goto out; +				}  				ctx->dup_cookie = *desc->dir_cookie; -				ctx->duped = 1; +				ctx->duped = -1;  			}  			desc->file->f_pos = new_pos;  			desc->cache_entry_index = i; @@ -368,6 +387,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des  		if (*desc->dir_cookie == array->last_cookie)  			desc->eof = 1;  	} +out:  	return status;  } @@ -740,19 +760,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,  	struct nfs_cache_array *array = NULL;  	struct nfs_open_dir_context *ctx = file->private_data; -	if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) { -		if (printk_ratelimit()) { -			pr_notice("NFS: directory %s/%s contains a readdir loop.  " -				"Please contact your server vendor.  " -				"Offending cookie: %llu\n", -				file->f_dentry->d_parent->d_name.name, -				file->f_dentry->d_name.name, -				*desc->dir_cookie); -		} -		res = -ELOOP; -		goto out; -	} -  	array = nfs_readdir_get_array(desc->page);  	if (IS_ERR(array)) {  		res = PTR_ERR(array); @@ -774,6 +781,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,  			*desc->dir_cookie = array->array[i+1].cookie;  		else  			*desc->dir_cookie = array->last_cookie; +		if (ctx->duped != 0) +			ctx->duped = 1;  	}  	if (array->eof_index >= 0)  		desc->eof = 1; @@ -805,6 +814,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,  	struct page	*page = NULL;  	int		status;  	struct inode *inode = desc->file->f_path.dentry->d_inode; +	struct nfs_open_dir_context *ctx = desc->file->private_data;  	dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",  			(unsigned long long)*desc->dir_cookie); @@ -818,6 +828,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,  	desc->page_index = 0;  	desc->last_cookie = *desc->dir_cookie;  	desc->page = page; +	ctx->duped = 0;  	status = nfs_readdir_xdr_to_array(desc, page, inode);  	if (status < 0) | 
