|
@@ -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,33 @@ 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. "
|
|
|
+ "Offending cookie: %llu\n",
|
|
|
+ desc->file->f_dentry->d_parent->d_name.name,
|
|
|
+ desc->file->f_dentry->d_name.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 +386,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 +759,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 +780,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 +813,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 +827,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)
|