ARM: Merge for-2637/s3c24xx/rx1950
[pandora-kernel.git] / fs / nfs / dir.c
index 5d7da3a..257e405 100644 (file)
@@ -172,7 +172,7 @@ struct nfs_cache_array {
 
 #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 *, int);
+typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
 typedef struct {
        struct file     *file;
        struct page     *page;
@@ -225,33 +225,42 @@ int nfs_readdir_clear_array(struct page *page, gfp_t mask)
  * nfs_clear_readdir_array()
  */
 static
-void nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len)
+int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len)
 {
        string->len = len;
        string->name = kmemdup(name, len, GFP_KERNEL);
-       string->hash = full_name_hash(string->name, string->len);
+       if (string->name == NULL)
+               return -ENOMEM;
+       string->hash = full_name_hash(name, len);
+       return 0;
 }
 
 static
 int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page)
 {
        struct nfs_cache_array *array = nfs_readdir_get_array(page);
+       struct nfs_cache_array_entry *cache_entry;
+       int ret;
+
        if (IS_ERR(array))
                return PTR_ERR(array);
-       if (array->size >= MAX_READDIR_ARRAY) {
-               nfs_readdir_release_array(page);
-               return -EIO;
-       }
+       ret = -EIO;
+       if (array->size >= MAX_READDIR_ARRAY)
+               goto out;
 
-       array->array[array->size].cookie = entry->prev_cookie;
+       cache_entry = &array->array[array->size];
+       cache_entry->cookie = entry->prev_cookie;
+       cache_entry->ino = entry->ino;
+       ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len);
+       if (ret)
+               goto out;
        array->last_cookie = entry->cookie;
-       array->array[array->size].ino = entry->ino;
-       nfs_readdir_make_qstr(&array->array[array->size].string, entry->name, entry->len);
        if (entry->eof == 1)
                array->eof_index = array->size;
        array->size++;
+out:
        nfs_readdir_release_array(page);
-       return 0;
+       return ret;
 }
 
 static
@@ -360,7 +369,7 @@ error:
 static
 int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream)
 {
-       __be32 *p = desc->decode(stream, entry, desc->plus);
+       __be32 *p = desc->decode(stream, entry, NFS_SERVER(desc->file->f_path.dentry->d_inode), desc->plus);
        if (IS_ERR(p))
                return PTR_ERR(p);
 
@@ -388,21 +397,24 @@ different:
 static
 void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
 {
-       struct qstr filename;
-       struct dentry *dentry = NULL;
-       struct dentry *alias = NULL;
+       struct qstr filename = {
+               .len = entry->len,
+               .name = entry->name,
+       };
+       struct dentry *dentry;
+       struct dentry *alias;
        struct inode *dir = parent->d_inode;
        struct inode *inode;
 
-       nfs_readdir_make_qstr(&filename, entry->name, entry->len);
-       if (filename.len == 1 && filename.name[0] == '.')
-               dentry = dget(parent);
-       else if (filename.len == 2 && filename.name[0] == '.'
-                                  && filename.name[1] == '.')
-               dentry = dget_parent(parent);
-       else
-               dentry = d_lookup(parent, &filename);
+       if (filename.name[0] == '.') {
+               if (filename.len == 1)
+                       return;
+               if (filename.len == 2 && filename.name[1] == '.')
+                       return;
+       }
+       filename.hash = full_name_hash(filename.name, filename.len);
 
+       dentry = d_lookup(parent, &filename);
        if (dentry != NULL) {
                if (nfs_same_file(dentry, entry)) {
                        nfs_refresh_inode(dentry->d_inode, entry->fattr);
@@ -414,6 +426,9 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        }
 
        dentry = d_alloc(parent, &filename);
+       if (dentry == NULL)
+               return;
+
        dentry->d_op = NFS_PROTO(dir)->dentry_ops;
        inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
        if (IS_ERR(inode))
@@ -430,8 +445,6 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
 
 out:
        dput(dentry);
-       kfree(filename.name);
-       return;
 }
 
 /* Perform conversion from xdr to cache array */
@@ -442,6 +455,8 @@ void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *e
        struct xdr_stream stream;
        struct xdr_buf buf;
        __be32 *ptr = xdr_page;
+       int status;
+       struct nfs_cache_array *array;
 
        buf.head->iov_base = xdr_page;
        buf.head->iov_len = buflen;
@@ -453,11 +468,23 @@ void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *e
 
        xdr_init_decode(&stream, &buf, ptr);
 
-       while (xdr_decode(desc, entry, &stream) == 0) {
+
+       do {
+               status = xdr_decode(desc, entry, &stream);
+               if (status != 0)
+                       break;
+
                if (nfs_readdir_add_to_array(entry, page) == -1)
                        break;
                if (desc->plus == 1)
                        nfs_prime_dcache(desc->file->f_path.dentry, entry);
+       } while (!entry->eof);
+
+       if (status == -EBADCOOKIE && entry->eof) {
+               array = nfs_readdir_get_array(page);
+               array->eof_index = array->size - 1;
+               status = 0;
+               nfs_readdir_release_array(page);
        }
 }
 
@@ -494,7 +521,7 @@ void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
                pages[i] = page;
        }
 
-       ptr = vm_map_ram(pages, NFS_MAX_READDIR_PAGES, 0, PAGE_KERNEL);
+       ptr = vm_map_ram(pages, npages, 0, PAGE_KERNEL);
        if (!IS_ERR_OR_NULL(ptr))
                return ptr;
 out_freepages:
@@ -556,7 +583,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
 {
        struct inode    *inode = desc->file->f_path.dentry->d_inode;
 
-       if (nfs_readdir_xdr_to_array(desc, page, inode) == -1)
+       if (nfs_readdir_xdr_to_array(desc, page, inode) < 0)
                goto error;
        SetPageUptodate(page);