diff options
Diffstat (limited to 'fs/nfs/dir.c')
| -rw-r--r-- | fs/nfs/dir.c | 220 |
1 files changed, 123 insertions, 97 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 07ac3847e562..996dd8989a91 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/mount.h> | 34 | #include <linux/mount.h> |
| 35 | #include <linux/sched.h> | 35 | #include <linux/sched.h> |
| 36 | #include <linux/vmalloc.h> | 36 | #include <linux/vmalloc.h> |
| 37 | #include <linux/kmemleak.h> | ||
| 37 | 38 | ||
| 38 | #include "delegation.h" | 39 | #include "delegation.h" |
| 39 | #include "iostat.h" | 40 | #include "iostat.h" |
| @@ -56,7 +57,7 @@ static int nfs_rename(struct inode *, struct dentry *, | |||
| 56 | struct inode *, struct dentry *); | 57 | struct inode *, struct dentry *); |
| 57 | static int nfs_fsync_dir(struct file *, int); | 58 | static int nfs_fsync_dir(struct file *, int); |
| 58 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); | 59 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); |
| 59 | static int nfs_readdir_clear_array(struct page*, gfp_t); | 60 | static void nfs_readdir_clear_array(struct page*); |
| 60 | 61 | ||
| 61 | const struct file_operations nfs_dir_operations = { | 62 | const struct file_operations nfs_dir_operations = { |
| 62 | .llseek = nfs_llseek_dir, | 63 | .llseek = nfs_llseek_dir, |
| @@ -82,8 +83,8 @@ const struct inode_operations nfs_dir_inode_operations = { | |||
| 82 | .setattr = nfs_setattr, | 83 | .setattr = nfs_setattr, |
| 83 | }; | 84 | }; |
| 84 | 85 | ||
| 85 | const struct address_space_operations nfs_dir_addr_space_ops = { | 86 | const struct address_space_operations nfs_dir_aops = { |
| 86 | .releasepage = nfs_readdir_clear_array, | 87 | .freepage = nfs_readdir_clear_array, |
| 87 | }; | 88 | }; |
| 88 | 89 | ||
| 89 | #ifdef CONFIG_NFS_V3 | 90 | #ifdef CONFIG_NFS_V3 |
| @@ -161,6 +162,7 @@ struct nfs_cache_array_entry { | |||
| 161 | u64 cookie; | 162 | u64 cookie; |
| 162 | u64 ino; | 163 | u64 ino; |
| 163 | struct qstr string; | 164 | struct qstr string; |
| 165 | unsigned char d_type; | ||
| 164 | }; | 166 | }; |
| 165 | 167 | ||
| 166 | struct nfs_cache_array { | 168 | struct nfs_cache_array { |
| @@ -170,14 +172,13 @@ struct nfs_cache_array { | |||
| 170 | struct nfs_cache_array_entry array[0]; | 172 | struct nfs_cache_array_entry array[0]; |
| 171 | }; | 173 | }; |
| 172 | 174 | ||
| 173 | #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) | ||
| 174 | |||
| 175 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); | 175 | typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); |
| 176 | typedef struct { | 176 | typedef struct { |
| 177 | struct file *file; | 177 | struct file *file; |
| 178 | struct page *page; | 178 | struct page *page; |
| 179 | unsigned long page_index; | 179 | unsigned long page_index; |
| 180 | u64 *dir_cookie; | 180 | u64 *dir_cookie; |
| 181 | u64 last_cookie; | ||
| 181 | loff_t current_index; | 182 | loff_t current_index; |
| 182 | decode_dirent_t decode; | 183 | decode_dirent_t decode; |
| 183 | 184 | ||
| @@ -194,9 +195,13 @@ typedef struct { | |||
| 194 | static | 195 | static |
| 195 | struct nfs_cache_array *nfs_readdir_get_array(struct page *page) | 196 | struct nfs_cache_array *nfs_readdir_get_array(struct page *page) |
| 196 | { | 197 | { |
| 198 | void *ptr; | ||
| 197 | if (page == NULL) | 199 | if (page == NULL) |
| 198 | return ERR_PTR(-EIO); | 200 | return ERR_PTR(-EIO); |
| 199 | return (struct nfs_cache_array *)kmap(page); | 201 | ptr = kmap(page); |
| 202 | if (ptr == NULL) | ||
| 203 | return ERR_PTR(-ENOMEM); | ||
| 204 | return ptr; | ||
| 200 | } | 205 | } |
| 201 | 206 | ||
| 202 | static | 207 | static |
| @@ -209,14 +214,15 @@ void nfs_readdir_release_array(struct page *page) | |||
| 209 | * we are freeing strings created by nfs_add_to_readdir_array() | 214 | * we are freeing strings created by nfs_add_to_readdir_array() |
| 210 | */ | 215 | */ |
| 211 | static | 216 | static |
| 212 | int nfs_readdir_clear_array(struct page *page, gfp_t mask) | 217 | void nfs_readdir_clear_array(struct page *page) |
| 213 | { | 218 | { |
| 214 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | 219 | struct nfs_cache_array *array; |
| 215 | int i; | 220 | int i; |
| 221 | |||
| 222 | array = kmap_atomic(page, KM_USER0); | ||
| 216 | for (i = 0; i < array->size; i++) | 223 | for (i = 0; i < array->size; i++) |
| 217 | kfree(array->array[i].string.name); | 224 | kfree(array->array[i].string.name); |
| 218 | nfs_readdir_release_array(page); | 225 | kunmap_atomic(array, KM_USER0); |
| 219 | return 0; | ||
| 220 | } | 226 | } |
| 221 | 227 | ||
| 222 | /* | 228 | /* |
| @@ -231,6 +237,11 @@ int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int le | |||
| 231 | string->name = kmemdup(name, len, GFP_KERNEL); | 237 | string->name = kmemdup(name, len, GFP_KERNEL); |
| 232 | if (string->name == NULL) | 238 | if (string->name == NULL) |
| 233 | return -ENOMEM; | 239 | return -ENOMEM; |
| 240 | /* | ||
| 241 | * Avoid a kmemleak false positive. The pointer to the name is stored | ||
| 242 | * in a page cache page which kmemleak does not scan. | ||
| 243 | */ | ||
| 244 | kmemleak_not_leak(string->name); | ||
| 234 | string->hash = full_name_hash(name, len); | 245 | string->hash = full_name_hash(name, len); |
| 235 | return 0; | 246 | return 0; |
| 236 | } | 247 | } |
| @@ -244,20 +255,24 @@ int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) | |||
| 244 | 255 | ||
| 245 | if (IS_ERR(array)) | 256 | if (IS_ERR(array)) |
| 246 | return PTR_ERR(array); | 257 | return PTR_ERR(array); |
| 247 | ret = -EIO; | ||
| 248 | if (array->size >= MAX_READDIR_ARRAY) | ||
| 249 | goto out; | ||
| 250 | 258 | ||
| 251 | cache_entry = &array->array[array->size]; | 259 | cache_entry = &array->array[array->size]; |
| 260 | |||
| 261 | /* Check that this entry lies within the page bounds */ | ||
| 262 | ret = -ENOSPC; | ||
| 263 | if ((char *)&cache_entry[1] - (char *)page_address(page) > PAGE_SIZE) | ||
| 264 | goto out; | ||
| 265 | |||
| 252 | cache_entry->cookie = entry->prev_cookie; | 266 | cache_entry->cookie = entry->prev_cookie; |
| 253 | cache_entry->ino = entry->ino; | 267 | cache_entry->ino = entry->ino; |
| 268 | cache_entry->d_type = entry->d_type; | ||
| 254 | ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); | 269 | ret = nfs_readdir_make_qstr(&cache_entry->string, entry->name, entry->len); |
| 255 | if (ret) | 270 | if (ret) |
| 256 | goto out; | 271 | goto out; |
| 257 | array->last_cookie = entry->cookie; | 272 | array->last_cookie = entry->cookie; |
| 258 | if (entry->eof == 1) | ||
| 259 | array->eof_index = array->size; | ||
| 260 | array->size++; | 273 | array->size++; |
| 274 | if (entry->eof != 0) | ||
| 275 | array->eof_index = array->size; | ||
| 261 | out: | 276 | out: |
| 262 | nfs_readdir_release_array(page); | 277 | nfs_readdir_release_array(page); |
| 263 | return ret; | 278 | return ret; |
| @@ -272,7 +287,7 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri | |||
| 272 | if (diff < 0) | 287 | if (diff < 0) |
| 273 | goto out_eof; | 288 | goto out_eof; |
| 274 | if (diff >= array->size) { | 289 | if (diff >= array->size) { |
| 275 | if (array->eof_index > 0) | 290 | if (array->eof_index >= 0) |
| 276 | goto out_eof; | 291 | goto out_eof; |
| 277 | desc->current_index += array->size; | 292 | desc->current_index += array->size; |
| 278 | return -EAGAIN; | 293 | return -EAGAIN; |
| @@ -281,8 +296,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri | |||
| 281 | index = (unsigned int)diff; | 296 | index = (unsigned int)diff; |
| 282 | *desc->dir_cookie = array->array[index].cookie; | 297 | *desc->dir_cookie = array->array[index].cookie; |
| 283 | desc->cache_entry_index = index; | 298 | desc->cache_entry_index = index; |
| 284 | if (index == array->eof_index) | ||
| 285 | desc->eof = 1; | ||
| 286 | return 0; | 299 | return 0; |
| 287 | out_eof: | 300 | out_eof: |
| 288 | desc->eof = 1; | 301 | desc->eof = 1; |
| @@ -296,17 +309,16 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des | |||
| 296 | int status = -EAGAIN; | 309 | int status = -EAGAIN; |
| 297 | 310 | ||
| 298 | for (i = 0; i < array->size; i++) { | 311 | for (i = 0; i < array->size; i++) { |
| 299 | if (i == array->eof_index) { | ||
| 300 | desc->eof = 1; | ||
| 301 | status = -EBADCOOKIE; | ||
| 302 | } | ||
| 303 | if (array->array[i].cookie == *desc->dir_cookie) { | 312 | if (array->array[i].cookie == *desc->dir_cookie) { |
| 304 | desc->cache_entry_index = i; | 313 | desc->cache_entry_index = i; |
| 305 | status = 0; | 314 | return 0; |
| 306 | break; | ||
| 307 | } | 315 | } |
| 308 | } | 316 | } |
| 309 | 317 | if (array->eof_index >= 0) { | |
| 318 | status = -EBADCOOKIE; | ||
| 319 | if (*desc->dir_cookie == array->last_cookie) | ||
| 320 | desc->eof = 1; | ||
| 321 | } | ||
| 310 | return status; | 322 | return status; |
| 311 | } | 323 | } |
| 312 | 324 | ||
| @@ -314,10 +326,7 @@ static | |||
| 314 | int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) | 326 | int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) |
| 315 | { | 327 | { |
| 316 | struct nfs_cache_array *array; | 328 | struct nfs_cache_array *array; |
| 317 | int status = -EBADCOOKIE; | 329 | int status; |
| 318 | |||
| 319 | if (desc->dir_cookie == NULL) | ||
| 320 | goto out; | ||
| 321 | 330 | ||
| 322 | array = nfs_readdir_get_array(desc->page); | 331 | array = nfs_readdir_get_array(desc->page); |
| 323 | if (IS_ERR(array)) { | 332 | if (IS_ERR(array)) { |
| @@ -330,6 +339,10 @@ int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) | |||
| 330 | else | 339 | else |
| 331 | status = nfs_readdir_search_for_cookie(array, desc); | 340 | status = nfs_readdir_search_for_cookie(array, desc); |
| 332 | 341 | ||
| 342 | if (status == -EAGAIN) { | ||
| 343 | desc->last_cookie = array->last_cookie; | ||
| 344 | desc->page_index++; | ||
| 345 | } | ||
| 333 | nfs_readdir_release_array(desc->page); | 346 | nfs_readdir_release_array(desc->page); |
| 334 | out: | 347 | out: |
| 335 | return status; | 348 | return status; |
| @@ -381,13 +394,9 @@ int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct x | |||
| 381 | static | 394 | static |
| 382 | int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry) | 395 | int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry) |
| 383 | { | 396 | { |
| 384 | struct nfs_inode *node; | ||
| 385 | if (dentry->d_inode == NULL) | 397 | if (dentry->d_inode == NULL) |
| 386 | goto different; | 398 | goto different; |
| 387 | node = NFS_I(dentry->d_inode); | 399 | if (nfs_compare_fh(entry->fh, NFS_FH(dentry->d_inode)) != 0) |
| 388 | if (node->fh.size != entry->fh->size) | ||
| 389 | goto different; | ||
| 390 | if (strncmp(node->fh.data, entry->fh->data, node->fh.size) != 0) | ||
| 391 | goto different; | 400 | goto different; |
| 392 | return 1; | 401 | return 1; |
| 393 | different: | 402 | different: |
| @@ -449,14 +458,15 @@ out: | |||
| 449 | 458 | ||
| 450 | /* Perform conversion from xdr to cache array */ | 459 | /* Perform conversion from xdr to cache array */ |
| 451 | static | 460 | static |
| 452 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | 461 | int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, |
| 453 | void *xdr_page, struct page *page, unsigned int buflen) | 462 | void *xdr_page, struct page *page, unsigned int buflen) |
| 454 | { | 463 | { |
| 455 | struct xdr_stream stream; | 464 | struct xdr_stream stream; |
| 456 | struct xdr_buf buf; | 465 | struct xdr_buf buf; |
| 457 | __be32 *ptr = xdr_page; | 466 | __be32 *ptr = xdr_page; |
| 458 | int status; | ||
| 459 | struct nfs_cache_array *array; | 467 | struct nfs_cache_array *array; |
| 468 | unsigned int count = 0; | ||
| 469 | int status; | ||
| 460 | 470 | ||
| 461 | buf.head->iov_base = xdr_page; | 471 | buf.head->iov_base = xdr_page; |
| 462 | buf.head->iov_len = buflen; | 472 | buf.head->iov_len = buflen; |
| @@ -471,21 +481,32 @@ void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *e | |||
| 471 | 481 | ||
| 472 | do { | 482 | do { |
| 473 | status = xdr_decode(desc, entry, &stream); | 483 | status = xdr_decode(desc, entry, &stream); |
| 474 | if (status != 0) | 484 | if (status != 0) { |
| 485 | if (status == -EAGAIN) | ||
| 486 | status = 0; | ||
| 475 | break; | 487 | break; |
| 488 | } | ||
| 476 | 489 | ||
| 477 | if (nfs_readdir_add_to_array(entry, page) == -1) | 490 | count++; |
| 478 | break; | 491 | |
| 479 | if (desc->plus == 1) | 492 | if (desc->plus != 0) |
| 480 | nfs_prime_dcache(desc->file->f_path.dentry, entry); | 493 | nfs_prime_dcache(desc->file->f_path.dentry, entry); |
| 494 | |||
| 495 | status = nfs_readdir_add_to_array(entry, page); | ||
| 496 | if (status != 0) | ||
| 497 | break; | ||
| 481 | } while (!entry->eof); | 498 | } while (!entry->eof); |
| 482 | 499 | ||
| 483 | if (status == -EBADCOOKIE && entry->eof) { | 500 | if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) { |
| 484 | array = nfs_readdir_get_array(page); | 501 | array = nfs_readdir_get_array(page); |
| 485 | array->eof_index = array->size - 1; | 502 | if (!IS_ERR(array)) { |
| 486 | status = 0; | 503 | array->eof_index = array->size; |
| 487 | nfs_readdir_release_array(page); | 504 | status = 0; |
| 505 | nfs_readdir_release_array(page); | ||
| 506 | } else | ||
| 507 | status = PTR_ERR(array); | ||
| 488 | } | 508 | } |
| 509 | return status; | ||
| 489 | } | 510 | } |
| 490 | 511 | ||
| 491 | static | 512 | static |
| @@ -537,11 +558,11 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
| 537 | struct nfs_entry entry; | 558 | struct nfs_entry entry; |
| 538 | struct file *file = desc->file; | 559 | struct file *file = desc->file; |
| 539 | struct nfs_cache_array *array; | 560 | struct nfs_cache_array *array; |
| 540 | int status = 0; | 561 | int status = -ENOMEM; |
| 541 | unsigned int array_size = ARRAY_SIZE(pages); | 562 | unsigned int array_size = ARRAY_SIZE(pages); |
| 542 | 563 | ||
| 543 | entry.prev_cookie = 0; | 564 | entry.prev_cookie = 0; |
| 544 | entry.cookie = *desc->dir_cookie; | 565 | entry.cookie = desc->last_cookie; |
| 545 | entry.eof = 0; | 566 | entry.eof = 0; |
| 546 | entry.fh = nfs_alloc_fhandle(); | 567 | entry.fh = nfs_alloc_fhandle(); |
| 547 | entry.fattr = nfs_alloc_fattr(); | 568 | entry.fattr = nfs_alloc_fattr(); |
| @@ -549,6 +570,10 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
| 549 | goto out; | 570 | goto out; |
| 550 | 571 | ||
| 551 | array = nfs_readdir_get_array(page); | 572 | array = nfs_readdir_get_array(page); |
| 573 | if (IS_ERR(array)) { | ||
| 574 | status = PTR_ERR(array); | ||
| 575 | goto out; | ||
| 576 | } | ||
| 552 | memset(array, 0, sizeof(struct nfs_cache_array)); | 577 | memset(array, 0, sizeof(struct nfs_cache_array)); |
| 553 | array->eof_index = -1; | 578 | array->eof_index = -1; |
| 554 | 579 | ||
| @@ -556,12 +581,19 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
| 556 | if (!pages_ptr) | 581 | if (!pages_ptr) |
| 557 | goto out_release_array; | 582 | goto out_release_array; |
| 558 | do { | 583 | do { |
| 584 | unsigned int pglen; | ||
| 559 | status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode); | 585 | status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode); |
| 560 | 586 | ||
| 561 | if (status < 0) | 587 | if (status < 0) |
| 562 | break; | 588 | break; |
| 563 | nfs_readdir_page_filler(desc, &entry, pages_ptr, page, array_size * PAGE_SIZE); | 589 | pglen = status; |
| 564 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); | 590 | status = nfs_readdir_page_filler(desc, &entry, pages_ptr, page, pglen); |
| 591 | if (status < 0) { | ||
| 592 | if (status == -ENOSPC) | ||
| 593 | status = 0; | ||
| 594 | break; | ||
| 595 | } | ||
| 596 | } while (array->eof_index < 0); | ||
| 565 | 597 | ||
| 566 | nfs_readdir_free_large_page(pages_ptr, pages, array_size); | 598 | nfs_readdir_free_large_page(pages_ptr, pages, array_size); |
| 567 | out_release_array: | 599 | out_release_array: |
| @@ -582,8 +614,10 @@ static | |||
| 582 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) | 614 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) |
| 583 | { | 615 | { |
| 584 | struct inode *inode = desc->file->f_path.dentry->d_inode; | 616 | struct inode *inode = desc->file->f_path.dentry->d_inode; |
| 617 | int ret; | ||
| 585 | 618 | ||
| 586 | if (nfs_readdir_xdr_to_array(desc, page, inode) < 0) | 619 | ret = nfs_readdir_xdr_to_array(desc, page, inode); |
| 620 | if (ret < 0) | ||
| 587 | goto error; | 621 | goto error; |
| 588 | SetPageUptodate(page); | 622 | SetPageUptodate(page); |
| 589 | 623 | ||
| @@ -595,12 +629,14 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) | |||
| 595 | return 0; | 629 | return 0; |
| 596 | error: | 630 | error: |
| 597 | unlock_page(page); | 631 | unlock_page(page); |
| 598 | return -EIO; | 632 | return ret; |
| 599 | } | 633 | } |
| 600 | 634 | ||
| 601 | static | 635 | static |
| 602 | void cache_page_release(nfs_readdir_descriptor_t *desc) | 636 | void cache_page_release(nfs_readdir_descriptor_t *desc) |
| 603 | { | 637 | { |
| 638 | if (!desc->page->mapping) | ||
| 639 | nfs_readdir_clear_array(desc->page); | ||
| 604 | page_cache_release(desc->page); | 640 | page_cache_release(desc->page); |
| 605 | desc->page = NULL; | 641 | desc->page = NULL; |
| 606 | } | 642 | } |
| @@ -608,12 +644,8 @@ void cache_page_release(nfs_readdir_descriptor_t *desc) | |||
| 608 | static | 644 | static |
| 609 | struct page *get_cache_page(nfs_readdir_descriptor_t *desc) | 645 | struct page *get_cache_page(nfs_readdir_descriptor_t *desc) |
| 610 | { | 646 | { |
| 611 | struct page *page; | 647 | return read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, |
| 612 | page = read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, | ||
| 613 | desc->page_index, (filler_t *)nfs_readdir_filler, desc); | 648 | desc->page_index, (filler_t *)nfs_readdir_filler, desc); |
| 614 | if (IS_ERR(page)) | ||
| 615 | desc->eof = 1; | ||
| 616 | return page; | ||
| 617 | } | 649 | } |
| 618 | 650 | ||
| 619 | /* | 651 | /* |
| @@ -629,9 +661,8 @@ int find_cache_page(nfs_readdir_descriptor_t *desc) | |||
| 629 | return PTR_ERR(desc->page); | 661 | return PTR_ERR(desc->page); |
| 630 | 662 | ||
| 631 | res = nfs_readdir_search_array(desc); | 663 | res = nfs_readdir_search_array(desc); |
| 632 | if (res == 0) | 664 | if (res != 0) |
| 633 | return 0; | 665 | cache_page_release(desc); |
| 634 | cache_page_release(desc); | ||
| 635 | return res; | 666 | return res; |
| 636 | } | 667 | } |
| 637 | 668 | ||
| @@ -639,22 +670,18 @@ int find_cache_page(nfs_readdir_descriptor_t *desc) | |||
| 639 | static inline | 670 | static inline |
| 640 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | 671 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) |
| 641 | { | 672 | { |
| 642 | int res = -EAGAIN; | 673 | int res; |
| 643 | 674 | ||
| 644 | while (1) { | 675 | if (desc->page_index == 0) { |
| 645 | res = find_cache_page(desc); | 676 | desc->current_index = 0; |
| 646 | if (res != -EAGAIN) | 677 | desc->last_cookie = 0; |
| 647 | break; | ||
| 648 | desc->page_index++; | ||
| 649 | } | 678 | } |
| 679 | do { | ||
| 680 | res = find_cache_page(desc); | ||
| 681 | } while (res == -EAGAIN); | ||
| 650 | return res; | 682 | return res; |
| 651 | } | 683 | } |
| 652 | 684 | ||
| 653 | static inline unsigned int dt_type(struct inode *inode) | ||
| 654 | { | ||
| 655 | return (inode->i_mode >> 12) & 15; | ||
| 656 | } | ||
| 657 | |||
| 658 | /* | 685 | /* |
| 659 | * Once we've found the start of the dirent within a page: fill 'er up... | 686 | * Once we've found the start of the dirent within a page: fill 'er up... |
| 660 | */ | 687 | */ |
| @@ -666,35 +693,35 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
| 666 | int i = 0; | 693 | int i = 0; |
| 667 | int res = 0; | 694 | int res = 0; |
| 668 | struct nfs_cache_array *array = NULL; | 695 | struct nfs_cache_array *array = NULL; |
| 669 | unsigned int d_type = DT_UNKNOWN; | ||
| 670 | struct dentry *dentry = NULL; | ||
| 671 | 696 | ||
| 672 | array = nfs_readdir_get_array(desc->page); | 697 | array = nfs_readdir_get_array(desc->page); |
| 698 | if (IS_ERR(array)) { | ||
| 699 | res = PTR_ERR(array); | ||
| 700 | goto out; | ||
| 701 | } | ||
| 673 | 702 | ||
| 674 | for (i = desc->cache_entry_index; i < array->size; i++) { | 703 | for (i = desc->cache_entry_index; i < array->size; i++) { |
| 675 | d_type = DT_UNKNOWN; | 704 | struct nfs_cache_array_entry *ent; |
| 676 | 705 | ||
| 677 | res = filldir(dirent, array->array[i].string.name, | 706 | ent = &array->array[i]; |
| 678 | array->array[i].string.len, file->f_pos, | 707 | if (filldir(dirent, ent->string.name, ent->string.len, |
| 679 | nfs_compat_user_ino64(array->array[i].ino), d_type); | 708 | file->f_pos, nfs_compat_user_ino64(ent->ino), |
| 680 | if (res < 0) | 709 | ent->d_type) < 0) { |
| 710 | desc->eof = 1; | ||
| 681 | break; | 711 | break; |
| 712 | } | ||
| 682 | file->f_pos++; | 713 | file->f_pos++; |
| 683 | desc->cache_entry_index = i; | ||
| 684 | if (i < (array->size-1)) | 714 | if (i < (array->size-1)) |
| 685 | *desc->dir_cookie = array->array[i+1].cookie; | 715 | *desc->dir_cookie = array->array[i+1].cookie; |
| 686 | else | 716 | else |
| 687 | *desc->dir_cookie = array->last_cookie; | 717 | *desc->dir_cookie = array->last_cookie; |
| 688 | if (i == array->eof_index) { | ||
| 689 | desc->eof = 1; | ||
| 690 | break; | ||
| 691 | } | ||
| 692 | } | 718 | } |
| 719 | if (array->eof_index >= 0) | ||
| 720 | desc->eof = 1; | ||
| 693 | 721 | ||
| 694 | nfs_readdir_release_array(desc->page); | 722 | nfs_readdir_release_array(desc->page); |
| 723 | out: | ||
| 695 | cache_page_release(desc); | 724 | cache_page_release(desc); |
| 696 | if (dentry != NULL) | ||
| 697 | dput(dentry); | ||
| 698 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", | 725 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", |
| 699 | (unsigned long long)*desc->dir_cookie, res); | 726 | (unsigned long long)*desc->dir_cookie, res); |
| 700 | return res; | 727 | return res; |
| @@ -729,13 +756,14 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
| 729 | goto out; | 756 | goto out; |
| 730 | } | 757 | } |
| 731 | 758 | ||
| 732 | if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) { | ||
| 733 | status = -EIO; | ||
| 734 | goto out_release; | ||
| 735 | } | ||
| 736 | |||
| 737 | desc->page_index = 0; | 759 | desc->page_index = 0; |
| 760 | desc->last_cookie = *desc->dir_cookie; | ||
| 738 | desc->page = page; | 761 | desc->page = page; |
| 762 | |||
| 763 | status = nfs_readdir_xdr_to_array(desc, page, inode); | ||
| 764 | if (status < 0) | ||
| 765 | goto out_release; | ||
| 766 | |||
| 739 | status = nfs_do_filldir(desc, dirent, filldir); | 767 | status = nfs_do_filldir(desc, dirent, filldir); |
| 740 | 768 | ||
| 741 | out: | 769 | out: |
| @@ -757,7 +785,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 757 | struct inode *inode = dentry->d_inode; | 785 | struct inode *inode = dentry->d_inode; |
| 758 | nfs_readdir_descriptor_t my_desc, | 786 | nfs_readdir_descriptor_t my_desc, |
| 759 | *desc = &my_desc; | 787 | *desc = &my_desc; |
| 760 | int res = -ENOMEM; | 788 | int res; |
| 761 | 789 | ||
| 762 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", | 790 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", |
| 763 | dentry->d_parent->d_name.name, dentry->d_name.name, | 791 | dentry->d_parent->d_name.name, dentry->d_name.name, |
| @@ -782,18 +810,18 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 782 | if (res < 0) | 810 | if (res < 0) |
| 783 | goto out; | 811 | goto out; |
| 784 | 812 | ||
| 785 | while (desc->eof != 1) { | 813 | do { |
| 786 | res = readdir_search_pagecache(desc); | 814 | res = readdir_search_pagecache(desc); |
| 787 | 815 | ||
| 788 | if (res == -EBADCOOKIE) { | 816 | if (res == -EBADCOOKIE) { |
| 817 | res = 0; | ||
| 789 | /* This means either end of directory */ | 818 | /* This means either end of directory */ |
| 790 | if (*desc->dir_cookie && desc->eof == 0) { | 819 | if (*desc->dir_cookie && desc->eof == 0) { |
| 791 | /* Or that the server has 'lost' a cookie */ | 820 | /* Or that the server has 'lost' a cookie */ |
| 792 | res = uncached_readdir(desc, dirent, filldir); | 821 | res = uncached_readdir(desc, dirent, filldir); |
| 793 | if (res >= 0) | 822 | if (res == 0) |
| 794 | continue; | 823 | continue; |
| 795 | } | 824 | } |
| 796 | res = 0; | ||
| 797 | break; | 825 | break; |
| 798 | } | 826 | } |
| 799 | if (res == -ETOOSMALL && desc->plus) { | 827 | if (res == -ETOOSMALL && desc->plus) { |
| @@ -808,11 +836,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 808 | break; | 836 | break; |
| 809 | 837 | ||
| 810 | res = nfs_do_filldir(desc, dirent, filldir); | 838 | res = nfs_do_filldir(desc, dirent, filldir); |
| 811 | if (res < 0) { | 839 | if (res < 0) |
| 812 | res = 0; | ||
| 813 | break; | 840 | break; |
| 814 | } | 841 | } while (!desc->eof); |
| 815 | } | ||
| 816 | out: | 842 | out: |
| 817 | nfs_unblock_sillyrename(dentry); | 843 | nfs_unblock_sillyrename(dentry); |
| 818 | if (res > 0) | 844 | if (res > 0) |
| @@ -1345,12 +1371,12 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry | |||
| 1345 | res = NULL; | 1371 | res = NULL; |
| 1346 | goto out; | 1372 | goto out; |
| 1347 | /* This turned out not to be a regular file */ | 1373 | /* This turned out not to be a regular file */ |
| 1348 | case -EISDIR: | ||
| 1349 | case -ENOTDIR: | 1374 | case -ENOTDIR: |
| 1350 | goto no_open; | 1375 | goto no_open; |
| 1351 | case -ELOOP: | 1376 | case -ELOOP: |
| 1352 | if (!(nd->intent.open.flags & O_NOFOLLOW)) | 1377 | if (!(nd->intent.open.flags & O_NOFOLLOW)) |
| 1353 | goto no_open; | 1378 | goto no_open; |
| 1379 | /* case -EISDIR: */ | ||
| 1354 | /* case -EINVAL: */ | 1380 | /* case -EINVAL: */ |
| 1355 | default: | 1381 | default: |
| 1356 | res = ERR_CAST(inode); | 1382 | res = ERR_CAST(inode); |
