|
@@ -162,6 +162,7 @@ struct nfs_cache_array_entry {
|
|
|
u64 cookie;
|
|
|
u64 ino;
|
|
|
struct qstr string;
|
|
|
+ unsigned char d_type;
|
|
|
};
|
|
|
|
|
|
struct nfs_cache_array {
|
|
@@ -171,8 +172,6 @@ struct nfs_cache_array {
|
|
|
struct nfs_cache_array_entry array[0];
|
|
|
};
|
|
|
|
|
|
-#define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry))
|
|
|
-
|
|
|
typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
|
|
|
typedef struct {
|
|
|
struct file *file;
|
|
@@ -257,13 +256,17 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
|
|
|
|
|
|
if (IS_ERR(array))
|
|
|
return PTR_ERR(array);
|
|
|
+
|
|
|
+ cache_entry = &array->array[array->size];
|
|
|
+
|
|
|
+ /* Check that this entry lies within the page bounds */
|
|
|
ret = -ENOSPC;
|
|
|
- if (array->size >= MAX_READDIR_ARRAY)
|
|
|
+ if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE)
|
|
|
goto out;
|
|
|
|
|
|
- cache_entry = &array->array[array->size];
|
|
|
cache_entry->cookie = entry->prev_cookie;
|
|
|
cache_entry->ino = entry->ino;
|
|
|
+ cache_entry->d_type = entry->d_type;
|
|
|
ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
|
|
|
if (ret)
|
|
|
goto out;
|
|
@@ -466,8 +469,9 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
|
|
struct xdr_stream stream;
|
|
|
struct xdr_buf buf;
|
|
|
__be32 *ptr = xdr_page;
|
|
|
- int status;
|
|
|
struct nfs_cache_array *array;
|
|
|
+ unsigned int count = 0;
|
|
|
+ int status;
|
|
|
|
|
|
buf.head->iov_base = xdr_page;
|
|
|
buf.head->iov_len = buflen;
|
|
@@ -488,6 +492,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ count++;
|
|
|
+
|
|
|
if (desc->plus == 1)
|
|
|
nfs_prime_dcache(desc->file->f_path.dentry, entry);
|
|
|
|
|
@@ -496,13 +502,14 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
|
|
break;
|
|
|
} while (!entry->eof);
|
|
|
|
|
|
- if (status == -EBADCOOKIE && entry->eof) {
|
|
|
+ if (count == 0 || (status == -EBADCOOKIE && entry->eof == 1)) {
|
|
|
array = nfs_readdir_get_array(page);
|
|
|
if (!IS_ERR(array)) {
|
|
|
array->eof_index = array->size;
|
|
|
status = 0;
|
|
|
nfs_readdir_release_array(page);
|
|
|
- }
|
|
|
+ } else
|
|
|
+ status = PTR_ERR(array);
|
|
|
}
|
|
|
return status;
|
|
|
}
|
|
@@ -696,21 +703,23 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
|
|
|
int i = 0;
|
|
|
int res = 0;
|
|
|
struct nfs_cache_array *array = NULL;
|
|
|
- unsigned int d_type = DT_UNKNOWN;
|
|
|
- struct dentry *dentry = NULL;
|
|
|
|
|
|
array = nfs_readdir_get_array(desc->page);
|
|
|
- if (IS_ERR(array))
|
|
|
- return PTR_ERR(array);
|
|
|
+ if (IS_ERR(array)) {
|
|
|
+ res = PTR_ERR(array);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
for (i = desc->cache_entry_index; i < array->size; i++) {
|
|
|
- d_type = DT_UNKNOWN;
|
|
|
+ struct nfs_cache_array_entry *ent;
|
|
|
|
|
|
- res = filldir(dirent, array->array[i].string.name,
|
|
|
- array->array[i].string.len, file->f_pos,
|
|
|
- nfs_compat_user_ino64(array->array[i].ino), d_type);
|
|
|
- if (res < 0)
|
|
|
+ ent = &array->array[i];
|
|
|
+ if (filldir(dirent, ent->string.name, ent->string.len,
|
|
|
+ file->f_pos, nfs_compat_user_ino64(ent->ino),
|
|
|
+ ent->d_type) < 0) {
|
|
|
+ desc->eof = 1;
|
|
|
break;
|
|
|
+ }
|
|
|
file->f_pos++;
|
|
|
desc->cache_entry_index = i;
|
|
|
if (i < (array->size-1))
|
|
@@ -722,9 +731,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
|
|
|
desc->eof = 1;
|
|
|
|
|
|
nfs_readdir_release_array(desc->page);
|
|
|
+out:
|
|
|
cache_page_release(desc);
|
|
|
- if (dentry != NULL)
|
|
|
- dput(dentry);
|
|
|
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
|
|
|
(unsigned long long)*desc->dir_cookie, res);
|
|
|
return res;
|
|
@@ -759,13 +767,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) {
|
|
|
- status = -EIO;
|
|
|
- goto out_release;
|
|
|
- }
|
|
|
-
|
|
|
desc->page_index = 0;
|
|
|
desc->page = page;
|
|
|
+
|
|
|
+ status = nfs_readdir_xdr_to_array(desc, page, inode);
|
|
|
+ if (status < 0)
|
|
|
+ goto out_release;
|
|
|
+
|
|
|
status = nfs_do_filldir(desc, dirent, filldir);
|
|
|
|
|
|
out:
|
|
@@ -816,14 +824,14 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|
|
res = readdir_search_pagecache(desc);
|
|
|
|
|
|
if (res == -EBADCOOKIE) {
|
|
|
+ res = 0;
|
|
|
/* This means either end of directory */
|
|
|
if (*desc->dir_cookie && desc->eof == 0) {
|
|
|
/* Or that the server has 'lost' a cookie */
|
|
|
res = uncached_readdir(desc, dirent, filldir);
|
|
|
- if (res >= 0)
|
|
|
+ if (res == 0)
|
|
|
continue;
|
|
|
}
|
|
|
- res = 0;
|
|
|
break;
|
|
|
}
|
|
|
if (res == -ETOOSMALL && desc->plus) {
|
|
@@ -838,10 +846,8 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|
|
break;
|
|
|
|
|
|
res = nfs_do_filldir(desc, dirent, filldir);
|
|
|
- if (res < 0) {
|
|
|
- res = 0;
|
|
|
+ if (res < 0)
|
|
|
break;
|
|
|
- }
|
|
|
}
|
|
|
out:
|
|
|
nfs_unblock_sillyrename(dentry);
|