diff options
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 160 |
1 files changed, 130 insertions, 30 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ff6155f5e8d9..b38a57e78a63 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/smp_lock.h> | 32 | #include <linux/smp_lock.h> |
33 | #include <linux/namei.h> | 33 | #include <linux/namei.h> |
34 | 34 | ||
35 | #include "nfs4_fs.h" | ||
35 | #include "delegation.h" | 36 | #include "delegation.h" |
36 | 37 | ||
37 | #define NFS_PARANOIA 1 | 38 | #define NFS_PARANOIA 1 |
@@ -50,8 +51,10 @@ static int nfs_mknod(struct inode *, struct dentry *, int, dev_t); | |||
50 | static int nfs_rename(struct inode *, struct dentry *, | 51 | static int nfs_rename(struct inode *, struct dentry *, |
51 | struct inode *, struct dentry *); | 52 | struct inode *, struct dentry *); |
52 | static int nfs_fsync_dir(struct file *, struct dentry *, int); | 53 | static int nfs_fsync_dir(struct file *, struct dentry *, int); |
54 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); | ||
53 | 55 | ||
54 | struct file_operations nfs_dir_operations = { | 56 | struct file_operations nfs_dir_operations = { |
57 | .llseek = nfs_llseek_dir, | ||
55 | .read = generic_read_dir, | 58 | .read = generic_read_dir, |
56 | .readdir = nfs_readdir, | 59 | .readdir = nfs_readdir, |
57 | .open = nfs_opendir, | 60 | .open = nfs_opendir, |
@@ -74,6 +77,27 @@ struct inode_operations nfs_dir_inode_operations = { | |||
74 | .setattr = nfs_setattr, | 77 | .setattr = nfs_setattr, |
75 | }; | 78 | }; |
76 | 79 | ||
80 | #ifdef CONFIG_NFS_V3 | ||
81 | struct inode_operations nfs3_dir_inode_operations = { | ||
82 | .create = nfs_create, | ||
83 | .lookup = nfs_lookup, | ||
84 | .link = nfs_link, | ||
85 | .unlink = nfs_unlink, | ||
86 | .symlink = nfs_symlink, | ||
87 | .mkdir = nfs_mkdir, | ||
88 | .rmdir = nfs_rmdir, | ||
89 | .mknod = nfs_mknod, | ||
90 | .rename = nfs_rename, | ||
91 | .permission = nfs_permission, | ||
92 | .getattr = nfs_getattr, | ||
93 | .setattr = nfs_setattr, | ||
94 | .listxattr = nfs3_listxattr, | ||
95 | .getxattr = nfs3_getxattr, | ||
96 | .setxattr = nfs3_setxattr, | ||
97 | .removexattr = nfs3_removexattr, | ||
98 | }; | ||
99 | #endif /* CONFIG_NFS_V3 */ | ||
100 | |||
77 | #ifdef CONFIG_NFS_V4 | 101 | #ifdef CONFIG_NFS_V4 |
78 | 102 | ||
79 | static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); | 103 | static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); |
@@ -90,6 +114,9 @@ struct inode_operations nfs4_dir_inode_operations = { | |||
90 | .permission = nfs_permission, | 114 | .permission = nfs_permission, |
91 | .getattr = nfs_getattr, | 115 | .getattr = nfs_getattr, |
92 | .setattr = nfs_setattr, | 116 | .setattr = nfs_setattr, |
117 | .getxattr = nfs4_getxattr, | ||
118 | .setxattr = nfs4_setxattr, | ||
119 | .listxattr = nfs4_listxattr, | ||
93 | }; | 120 | }; |
94 | 121 | ||
95 | #endif /* CONFIG_NFS_V4 */ | 122 | #endif /* CONFIG_NFS_V4 */ |
@@ -116,7 +143,8 @@ typedef struct { | |||
116 | struct page *page; | 143 | struct page *page; |
117 | unsigned long page_index; | 144 | unsigned long page_index; |
118 | u32 *ptr; | 145 | u32 *ptr; |
119 | u64 target; | 146 | u64 *dir_cookie; |
147 | loff_t current_index; | ||
120 | struct nfs_entry *entry; | 148 | struct nfs_entry *entry; |
121 | decode_dirent_t decode; | 149 | decode_dirent_t decode; |
122 | int plus; | 150 | int plus; |
@@ -164,12 +192,10 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) | |||
164 | NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; | 192 | NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; |
165 | /* Ensure consistent page alignment of the data. | 193 | /* Ensure consistent page alignment of the data. |
166 | * Note: assumes we have exclusive access to this mapping either | 194 | * Note: assumes we have exclusive access to this mapping either |
167 | * throught inode->i_sem or some other mechanism. | 195 | * through inode->i_sem or some other mechanism. |
168 | */ | 196 | */ |
169 | if (page->index == 0) { | 197 | if (page->index == 0) |
170 | invalidate_inode_pages(inode->i_mapping); | 198 | invalidate_inode_pages2_range(inode->i_mapping, PAGE_CACHE_SIZE, -1); |
171 | NFS_I(inode)->readdir_timestamp = timestamp; | ||
172 | } | ||
173 | unlock_page(page); | 199 | unlock_page(page); |
174 | return 0; | 200 | return 0; |
175 | error: | 201 | error: |
@@ -202,22 +228,22 @@ void dir_page_release(nfs_readdir_descriptor_t *desc) | |||
202 | 228 | ||
203 | /* | 229 | /* |
204 | * Given a pointer to a buffer that has already been filled by a call | 230 | * Given a pointer to a buffer that has already been filled by a call |
205 | * to readdir, find the next entry. | 231 | * to readdir, find the next entry with cookie '*desc->dir_cookie'. |
206 | * | 232 | * |
207 | * If the end of the buffer has been reached, return -EAGAIN, if not, | 233 | * If the end of the buffer has been reached, return -EAGAIN, if not, |
208 | * return the offset within the buffer of the next entry to be | 234 | * return the offset within the buffer of the next entry to be |
209 | * read. | 235 | * read. |
210 | */ | 236 | */ |
211 | static inline | 237 | static inline |
212 | int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) | 238 | int find_dirent(nfs_readdir_descriptor_t *desc) |
213 | { | 239 | { |
214 | struct nfs_entry *entry = desc->entry; | 240 | struct nfs_entry *entry = desc->entry; |
215 | int loop_count = 0, | 241 | int loop_count = 0, |
216 | status; | 242 | status; |
217 | 243 | ||
218 | while((status = dir_decode(desc)) == 0) { | 244 | while((status = dir_decode(desc)) == 0) { |
219 | dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); | 245 | dfprintk(VFS, "NFS: found cookie %Lu\n", (unsigned long long)entry->cookie); |
220 | if (entry->prev_cookie == desc->target) | 246 | if (entry->prev_cookie == *desc->dir_cookie) |
221 | break; | 247 | break; |
222 | if (loop_count++ > 200) { | 248 | if (loop_count++ > 200) { |
223 | loop_count = 0; | 249 | loop_count = 0; |
@@ -229,8 +255,44 @@ int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) | |||
229 | } | 255 | } |
230 | 256 | ||
231 | /* | 257 | /* |
232 | * Find the given page, and call find_dirent() in order to try to | 258 | * Given a pointer to a buffer that has already been filled by a call |
233 | * return the next entry. | 259 | * to readdir, find the entry at offset 'desc->file->f_pos'. |
260 | * | ||
261 | * If the end of the buffer has been reached, return -EAGAIN, if not, | ||
262 | * return the offset within the buffer of the next entry to be | ||
263 | * read. | ||
264 | */ | ||
265 | static inline | ||
266 | int find_dirent_index(nfs_readdir_descriptor_t *desc) | ||
267 | { | ||
268 | struct nfs_entry *entry = desc->entry; | ||
269 | int loop_count = 0, | ||
270 | status; | ||
271 | |||
272 | for(;;) { | ||
273 | status = dir_decode(desc); | ||
274 | if (status) | ||
275 | break; | ||
276 | |||
277 | dfprintk(VFS, "NFS: found cookie %Lu at index %Ld\n", (unsigned long long)entry->cookie, desc->current_index); | ||
278 | |||
279 | if (desc->file->f_pos == desc->current_index) { | ||
280 | *desc->dir_cookie = entry->cookie; | ||
281 | break; | ||
282 | } | ||
283 | desc->current_index++; | ||
284 | if (loop_count++ > 200) { | ||
285 | loop_count = 0; | ||
286 | schedule(); | ||
287 | } | ||
288 | } | ||
289 | dfprintk(VFS, "NFS: find_dirent_index() returns %d\n", status); | ||
290 | return status; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Find the given page, and call find_dirent() or find_dirent_index in | ||
295 | * order to try to return the next entry. | ||
234 | */ | 296 | */ |
235 | static inline | 297 | static inline |
236 | int find_dirent_page(nfs_readdir_descriptor_t *desc) | 298 | int find_dirent_page(nfs_readdir_descriptor_t *desc) |
@@ -253,7 +315,10 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) | |||
253 | /* NOTE: Someone else may have changed the READDIRPLUS flag */ | 315 | /* NOTE: Someone else may have changed the READDIRPLUS flag */ |
254 | desc->page = page; | 316 | desc->page = page; |
255 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | 317 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ |
256 | status = find_dirent(desc, page); | 318 | if (*desc->dir_cookie != 0) |
319 | status = find_dirent(desc); | ||
320 | else | ||
321 | status = find_dirent_index(desc); | ||
257 | if (status < 0) | 322 | if (status < 0) |
258 | dir_page_release(desc); | 323 | dir_page_release(desc); |
259 | out: | 324 | out: |
@@ -268,7 +333,8 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) | |||
268 | * Recurse through the page cache pages, and return a | 333 | * Recurse through the page cache pages, and return a |
269 | * filled nfs_entry structure of the next directory entry if possible. | 334 | * filled nfs_entry structure of the next directory entry if possible. |
270 | * | 335 | * |
271 | * The target for the search is 'desc->target'. | 336 | * The target for the search is '*desc->dir_cookie' if non-0, |
337 | * 'desc->file->f_pos' otherwise | ||
272 | */ | 338 | */ |
273 | static inline | 339 | static inline |
274 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | 340 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) |
@@ -276,7 +342,16 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | |||
276 | int loop_count = 0; | 342 | int loop_count = 0; |
277 | int res; | 343 | int res; |
278 | 344 | ||
279 | dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); | 345 | /* Always search-by-index from the beginning of the cache */ |
346 | if (*desc->dir_cookie == 0) { | ||
347 | dfprintk(VFS, "NFS: readdir_search_pagecache() searching for offset %Ld\n", (long long)desc->file->f_pos); | ||
348 | desc->page_index = 0; | ||
349 | desc->entry->cookie = desc->entry->prev_cookie = 0; | ||
350 | desc->entry->eof = 0; | ||
351 | desc->current_index = 0; | ||
352 | } else | ||
353 | dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); | ||
354 | |||
280 | for (;;) { | 355 | for (;;) { |
281 | res = find_dirent_page(desc); | 356 | res = find_dirent_page(desc); |
282 | if (res != -EAGAIN) | 357 | if (res != -EAGAIN) |
@@ -313,7 +388,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
313 | int loop_count = 0, | 388 | int loop_count = 0, |
314 | res; | 389 | res; |
315 | 390 | ||
316 | dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); | 391 | dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)entry->cookie); |
317 | 392 | ||
318 | for(;;) { | 393 | for(;;) { |
319 | unsigned d_type = DT_UNKNOWN; | 394 | unsigned d_type = DT_UNKNOWN; |
@@ -333,10 +408,11 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
333 | } | 408 | } |
334 | 409 | ||
335 | res = filldir(dirent, entry->name, entry->len, | 410 | res = filldir(dirent, entry->name, entry->len, |
336 | entry->prev_cookie, fileid, d_type); | 411 | file->f_pos, fileid, d_type); |
337 | if (res < 0) | 412 | if (res < 0) |
338 | break; | 413 | break; |
339 | file->f_pos = desc->target = entry->cookie; | 414 | file->f_pos++; |
415 | *desc->dir_cookie = entry->cookie; | ||
340 | if (dir_decode(desc) != 0) { | 416 | if (dir_decode(desc) != 0) { |
341 | desc->page_index ++; | 417 | desc->page_index ++; |
342 | break; | 418 | break; |
@@ -349,7 +425,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
349 | dir_page_release(desc); | 425 | dir_page_release(desc); |
350 | if (dentry != NULL) | 426 | if (dentry != NULL) |
351 | dput(dentry); | 427 | dput(dentry); |
352 | dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); | 428 | dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (unsigned long long)*desc->dir_cookie, res); |
353 | return res; | 429 | return res; |
354 | } | 430 | } |
355 | 431 | ||
@@ -375,14 +451,14 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
375 | struct page *page = NULL; | 451 | struct page *page = NULL; |
376 | int status; | 452 | int status; |
377 | 453 | ||
378 | dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); | 454 | dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); |
379 | 455 | ||
380 | page = alloc_page(GFP_HIGHUSER); | 456 | page = alloc_page(GFP_HIGHUSER); |
381 | if (!page) { | 457 | if (!page) { |
382 | status = -ENOMEM; | 458 | status = -ENOMEM; |
383 | goto out; | 459 | goto out; |
384 | } | 460 | } |
385 | desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->target, | 461 | desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, *desc->dir_cookie, |
386 | page, | 462 | page, |
387 | NFS_SERVER(inode)->dtsize, | 463 | NFS_SERVER(inode)->dtsize, |
388 | desc->plus); | 464 | desc->plus); |
@@ -391,7 +467,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
391 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | 467 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ |
392 | if (desc->error >= 0) { | 468 | if (desc->error >= 0) { |
393 | if ((status = dir_decode(desc)) == 0) | 469 | if ((status = dir_decode(desc)) == 0) |
394 | desc->entry->prev_cookie = desc->target; | 470 | desc->entry->prev_cookie = *desc->dir_cookie; |
395 | } else | 471 | } else |
396 | status = -EIO; | 472 | status = -EIO; |
397 | if (status < 0) | 473 | if (status < 0) |
@@ -412,8 +488,9 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
412 | goto out; | 488 | goto out; |
413 | } | 489 | } |
414 | 490 | ||
415 | /* The file offset position is now represented as a true offset into the | 491 | /* The file offset position represents the dirent entry number. A |
416 | * page cache as is the case in most of the other filesystems. | 492 | last cookie cache takes care of the common case of reading the |
493 | whole directory. | ||
417 | */ | 494 | */ |
418 | static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | 495 | static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) |
419 | { | 496 | { |
@@ -435,15 +512,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
435 | } | 512 | } |
436 | 513 | ||
437 | /* | 514 | /* |
438 | * filp->f_pos points to the file offset in the page cache. | 515 | * filp->f_pos points to the dirent entry number. |
439 | * but if the cache has meanwhile been zapped, we need to | 516 | * *desc->dir_cookie has the cookie for the next entry. We have |
440 | * read from the last dirent to revalidate f_pos | 517 | * to either find the entry with the appropriate number or |
441 | * itself. | 518 | * revalidate the cookie. |
442 | */ | 519 | */ |
443 | memset(desc, 0, sizeof(*desc)); | 520 | memset(desc, 0, sizeof(*desc)); |
444 | 521 | ||
445 | desc->file = filp; | 522 | desc->file = filp; |
446 | desc->target = filp->f_pos; | 523 | desc->dir_cookie = &((struct nfs_open_context *)filp->private_data)->dir_cookie; |
447 | desc->decode = NFS_PROTO(inode)->decode_dirent; | 524 | desc->decode = NFS_PROTO(inode)->decode_dirent; |
448 | desc->plus = NFS_USE_READDIRPLUS(inode); | 525 | desc->plus = NFS_USE_READDIRPLUS(inode); |
449 | 526 | ||
@@ -455,9 +532,10 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
455 | 532 | ||
456 | while(!desc->entry->eof) { | 533 | while(!desc->entry->eof) { |
457 | res = readdir_search_pagecache(desc); | 534 | res = readdir_search_pagecache(desc); |
535 | |||
458 | if (res == -EBADCOOKIE) { | 536 | if (res == -EBADCOOKIE) { |
459 | /* This means either end of directory */ | 537 | /* This means either end of directory */ |
460 | if (desc->entry->cookie != desc->target) { | 538 | if (*desc->dir_cookie && desc->entry->cookie != *desc->dir_cookie) { |
461 | /* Or that the server has 'lost' a cookie */ | 539 | /* Or that the server has 'lost' a cookie */ |
462 | res = uncached_readdir(desc, dirent, filldir); | 540 | res = uncached_readdir(desc, dirent, filldir); |
463 | if (res >= 0) | 541 | if (res >= 0) |
@@ -490,6 +568,28 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
490 | return 0; | 568 | return 0; |
491 | } | 569 | } |
492 | 570 | ||
571 | loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) | ||
572 | { | ||
573 | down(&filp->f_dentry->d_inode->i_sem); | ||
574 | switch (origin) { | ||
575 | case 1: | ||
576 | offset += filp->f_pos; | ||
577 | case 0: | ||
578 | if (offset >= 0) | ||
579 | break; | ||
580 | default: | ||
581 | offset = -EINVAL; | ||
582 | goto out; | ||
583 | } | ||
584 | if (offset != filp->f_pos) { | ||
585 | filp->f_pos = offset; | ||
586 | ((struct nfs_open_context *)filp->private_data)->dir_cookie = 0; | ||
587 | } | ||
588 | out: | ||
589 | up(&filp->f_dentry->d_inode->i_sem); | ||
590 | return offset; | ||
591 | } | ||
592 | |||
493 | /* | 593 | /* |
494 | * All directory operations under NFS are synchronous, so fsync() | 594 | * All directory operations under NFS are synchronous, so fsync() |
495 | * is a dummy operation. | 595 | * is a dummy operation. |