aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2011-07-30 12:45:35 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-07-30 14:34:50 -0400
commit0c0308066ca53fdf1423895f3a42838b67b3a5a8 (patch)
tree4c6f0d852456581a7ca17286601d18d7bb030632
parented1e6211a0a134ff23592c6f057af982ad5dab52 (diff)
NFS: Fix spurious readdir cookie loop messages
If the directory contents change, then we have to accept that the file->f_pos value may shrink if we do a 'search-by-cookie'. In that case, we should turn off the loop detection and let the NFS client try to recover. The patch also fixes a second loop detection bug by ensuring that after turning on the ctx->duped flag, we read at least one new cookie into ctx->dir_cookie before attempting to match with ctx->dup_cookie. Reported-by: Petr Vandrovec <petr@vandrovec.name> Cc: stable@kernel.org [2.6.39+] Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/dir.c56
-rw-r--r--include/linux/nfs_fs.h3
2 files changed, 35 insertions, 24 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 57f578e2560a..d23108b1e338 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -134,18 +134,19 @@ const struct inode_operations nfs4_dir_inode_operations = {
134 134
135#endif /* CONFIG_NFS_V4 */ 135#endif /* CONFIG_NFS_V4 */
136 136
137static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred) 137static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
138{ 138{
139 struct nfs_open_dir_context *ctx; 139 struct nfs_open_dir_context *ctx;
140 ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); 140 ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
141 if (ctx != NULL) { 141 if (ctx != NULL) {
142 ctx->duped = 0; 142 ctx->duped = 0;
143 ctx->attr_gencount = NFS_I(dir)->attr_gencount;
143 ctx->dir_cookie = 0; 144 ctx->dir_cookie = 0;
144 ctx->dup_cookie = 0; 145 ctx->dup_cookie = 0;
145 ctx->cred = get_rpccred(cred); 146 ctx->cred = get_rpccred(cred);
146 } else 147 return ctx;
147 ctx = ERR_PTR(-ENOMEM); 148 }
148 return ctx; 149 return ERR_PTR(-ENOMEM);
149} 150}
150 151
151static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) 152static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
@@ -173,7 +174,7 @@ nfs_opendir(struct inode *inode, struct file *filp)
173 cred = rpc_lookup_cred(); 174 cred = rpc_lookup_cred();
174 if (IS_ERR(cred)) 175 if (IS_ERR(cred))
175 return PTR_ERR(cred); 176 return PTR_ERR(cred);
176 ctx = alloc_nfs_open_dir_context(cred); 177 ctx = alloc_nfs_open_dir_context(inode, cred);
177 if (IS_ERR(ctx)) { 178 if (IS_ERR(ctx)) {
178 res = PTR_ERR(ctx); 179 res = PTR_ERR(ctx);
179 goto out; 180 goto out;
@@ -323,7 +324,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
323{ 324{
324 loff_t diff = desc->file->f_pos - desc->current_index; 325 loff_t diff = desc->file->f_pos - desc->current_index;
325 unsigned int index; 326 unsigned int index;
326 struct nfs_open_dir_context *ctx = desc->file->private_data;
327 327
328 if (diff < 0) 328 if (diff < 0)
329 goto out_eof; 329 goto out_eof;
@@ -336,7 +336,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
336 index = (unsigned int)diff; 336 index = (unsigned int)diff;
337 *desc->dir_cookie = array->array[index].cookie; 337 *desc->dir_cookie = array->array[index].cookie;
338 desc->cache_entry_index = index; 338 desc->cache_entry_index = index;
339 ctx->duped = 0;
340 return 0; 339 return 0;
341out_eof: 340out_eof:
342 desc->eof = 1; 341 desc->eof = 1;
@@ -349,14 +348,33 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
349 int i; 348 int i;
350 loff_t new_pos; 349 loff_t new_pos;
351 int status = -EAGAIN; 350 int status = -EAGAIN;
352 struct nfs_open_dir_context *ctx = desc->file->private_data;
353 351
354 for (i = 0; i < array->size; i++) { 352 for (i = 0; i < array->size; i++) {
355 if (array->array[i].cookie == *desc->dir_cookie) { 353 if (array->array[i].cookie == *desc->dir_cookie) {
354 struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode);
355 struct nfs_open_dir_context *ctx = desc->file->private_data;
356
356 new_pos = desc->current_index + i; 357 new_pos = desc->current_index + i;
357 if (new_pos < desc->file->f_pos) { 358 if (ctx->attr_gencount != nfsi->attr_gencount
359 || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) {
360 ctx->duped = 0;
361 ctx->attr_gencount = nfsi->attr_gencount;
362 } else if (new_pos < desc->file->f_pos) {
363 if (ctx->duped > 0
364 && ctx->dup_cookie == *desc->dir_cookie) {
365 if (printk_ratelimit()) {
366 pr_notice("NFS: directory %s/%s contains a readdir loop."
367 "Please contact your server vendor. "
368 "Offending cookie: %llu\n",
369 desc->file->f_dentry->d_parent->d_name.name,
370 desc->file->f_dentry->d_name.name,
371 *desc->dir_cookie);
372 }
373 status = -ELOOP;
374 goto out;
375 }
358 ctx->dup_cookie = *desc->dir_cookie; 376 ctx->dup_cookie = *desc->dir_cookie;
359 ctx->duped = 1; 377 ctx->duped = -1;
360 } 378 }
361 desc->file->f_pos = new_pos; 379 desc->file->f_pos = new_pos;
362 desc->cache_entry_index = i; 380 desc->cache_entry_index = i;
@@ -368,6 +386,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
368 if (*desc->dir_cookie == array->last_cookie) 386 if (*desc->dir_cookie == array->last_cookie)
369 desc->eof = 1; 387 desc->eof = 1;
370 } 388 }
389out:
371 return status; 390 return status;
372} 391}
373 392
@@ -740,19 +759,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
740 struct nfs_cache_array *array = NULL; 759 struct nfs_cache_array *array = NULL;
741 struct nfs_open_dir_context *ctx = file->private_data; 760 struct nfs_open_dir_context *ctx = file->private_data;
742 761
743 if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) {
744 if (printk_ratelimit()) {
745 pr_notice("NFS: directory %s/%s contains a readdir loop. "
746 "Please contact your server vendor. "
747 "Offending cookie: %llu\n",
748 file->f_dentry->d_parent->d_name.name,
749 file->f_dentry->d_name.name,
750 *desc->dir_cookie);
751 }
752 res = -ELOOP;
753 goto out;
754 }
755
756 array = nfs_readdir_get_array(desc->page); 762 array = nfs_readdir_get_array(desc->page);
757 if (IS_ERR(array)) { 763 if (IS_ERR(array)) {
758 res = PTR_ERR(array); 764 res = PTR_ERR(array);
@@ -774,6 +780,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
774 *desc->dir_cookie = array->array[i+1].cookie; 780 *desc->dir_cookie = array->array[i+1].cookie;
775 else 781 else
776 *desc->dir_cookie = array->last_cookie; 782 *desc->dir_cookie = array->last_cookie;
783 if (ctx->duped != 0)
784 ctx->duped = 1;
777 } 785 }
778 if (array->eof_index >= 0) 786 if (array->eof_index >= 0)
779 desc->eof = 1; 787 desc->eof = 1;
@@ -805,6 +813,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
805 struct page *page = NULL; 813 struct page *page = NULL;
806 int status; 814 int status;
807 struct inode *inode = desc->file->f_path.dentry->d_inode; 815 struct inode *inode = desc->file->f_path.dentry->d_inode;
816 struct nfs_open_dir_context *ctx = desc->file->private_data;
808 817
809 dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", 818 dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
810 (unsigned long long)*desc->dir_cookie); 819 (unsigned long long)*desc->dir_cookie);
@@ -818,6 +827,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
818 desc->page_index = 0; 827 desc->page_index = 0;
819 desc->last_cookie = *desc->dir_cookie; 828 desc->last_cookie = *desc->dir_cookie;
820 desc->page = page; 829 desc->page = page;
830 ctx->duped = 0;
821 831
822 status = nfs_readdir_xdr_to_array(desc, page, inode); 832 status = nfs_readdir_xdr_to_array(desc, page, inode);
823 if (status < 0) 833 if (status < 0)
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 8b579beb6358..b96fb99072ff 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -99,9 +99,10 @@ struct nfs_open_context {
99 99
100struct nfs_open_dir_context { 100struct nfs_open_dir_context {
101 struct rpc_cred *cred; 101 struct rpc_cred *cred;
102 unsigned long attr_gencount;
102 __u64 dir_cookie; 103 __u64 dir_cookie;
103 __u64 dup_cookie; 104 __u64 dup_cookie;
104 int duped; 105 signed char duped;
105}; 106};
106 107
107/* 108/*