diff options
author | Olivier Galibert <galibert@pobox.com> | 2005-06-22 13:16:29 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-06-22 16:07:33 -0400 |
commit | 00a926422765064cb28e218d4837411c88bf6a3e (patch) | |
tree | 1e9ad635821c7b037014307d1a2657dc56b17acf | |
parent | ae3884621bf5b4caff7785b9a417f262202965b2 (diff) |
[PATCH] NFS: Hide NFS server-generated readdir cookies from userland
NFSv3 currently returns the unsigned 64-bit cookie directly to
userspace. The following patch causes the kernel to generate
loff_t offsets for the benefit of userland.
The current server-generated READDIR cookie is cached in the
nfs_open_context instead of in filp->f_pos, so we still end up work
correctly under directory insertions/deletion.
Signed-off-by: Olivier Galibert <galibert@pobox.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/dir.c | 114 | ||||
-rw-r--r-- | fs/nfs/inode.c | 2 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 3 |
3 files changed, 95 insertions, 24 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2c6a95945684..fceef29c65a3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -141,7 +141,9 @@ typedef struct { | |||
141 | struct page *page; | 141 | struct page *page; |
142 | unsigned long page_index; | 142 | unsigned long page_index; |
143 | u32 *ptr; | 143 | u32 *ptr; |
144 | u64 target; | 144 | u64 target_cookie; |
145 | int target_index; | ||
146 | int current_index; | ||
145 | struct nfs_entry *entry; | 147 | struct nfs_entry *entry; |
146 | decode_dirent_t decode; | 148 | decode_dirent_t decode; |
147 | int plus; | 149 | int plus; |
@@ -225,14 +227,14 @@ void dir_page_release(nfs_readdir_descriptor_t *desc) | |||
225 | 227 | ||
226 | /* | 228 | /* |
227 | * Given a pointer to a buffer that has already been filled by a call | 229 | * Given a pointer to a buffer that has already been filled by a call |
228 | * to readdir, find the next entry. | 230 | * to readdir, find the next entry with cookie 'desc->target_cookie'. |
229 | * | 231 | * |
230 | * If the end of the buffer has been reached, return -EAGAIN, if not, | 232 | * If the end of the buffer has been reached, return -EAGAIN, if not, |
231 | * return the offset within the buffer of the next entry to be | 233 | * return the offset within the buffer of the next entry to be |
232 | * read. | 234 | * read. |
233 | */ | 235 | */ |
234 | static inline | 236 | static inline |
235 | int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) | 237 | int find_dirent(nfs_readdir_descriptor_t *desc) |
236 | { | 238 | { |
237 | struct nfs_entry *entry = desc->entry; | 239 | struct nfs_entry *entry = desc->entry; |
238 | int loop_count = 0, | 240 | int loop_count = 0, |
@@ -240,7 +242,7 @@ int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) | |||
240 | 242 | ||
241 | while((status = dir_decode(desc)) == 0) { | 243 | while((status = dir_decode(desc)) == 0) { |
242 | dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); | 244 | dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); |
243 | if (entry->prev_cookie == desc->target) | 245 | if (entry->prev_cookie == desc->target_cookie) |
244 | break; | 246 | break; |
245 | if (loop_count++ > 200) { | 247 | if (loop_count++ > 200) { |
246 | loop_count = 0; | 248 | loop_count = 0; |
@@ -252,8 +254,44 @@ int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) | |||
252 | } | 254 | } |
253 | 255 | ||
254 | /* | 256 | /* |
255 | * Find the given page, and call find_dirent() in order to try to | 257 | * Given a pointer to a buffer that has already been filled by a call |
256 | * return the next entry. | 258 | * to readdir, find the entry at offset 'desc->target_index'. |
259 | * | ||
260 | * If the end of the buffer has been reached, return -EAGAIN, if not, | ||
261 | * return the offset within the buffer of the next entry to be | ||
262 | * read. | ||
263 | */ | ||
264 | static inline | ||
265 | int find_dirent_index(nfs_readdir_descriptor_t *desc) | ||
266 | { | ||
267 | struct nfs_entry *entry = desc->entry; | ||
268 | int loop_count = 0, | ||
269 | status; | ||
270 | |||
271 | for(;;) { | ||
272 | status = dir_decode(desc); | ||
273 | if (status) | ||
274 | break; | ||
275 | |||
276 | dfprintk(VFS, "NFS: found cookie %Lu at index %d\n", (long long)entry->cookie, desc->current_index); | ||
277 | |||
278 | if (desc->target_index == desc->current_index) { | ||
279 | desc->target_cookie = entry->cookie; | ||
280 | break; | ||
281 | } | ||
282 | desc->current_index++; | ||
283 | if (loop_count++ > 200) { | ||
284 | loop_count = 0; | ||
285 | schedule(); | ||
286 | } | ||
287 | } | ||
288 | dfprintk(VFS, "NFS: find_dirent_index() returns %d\n", status); | ||
289 | return status; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * Find the given page, and call find_dirent() or find_dirent_index in | ||
294 | * order to try to return the next entry. | ||
257 | */ | 295 | */ |
258 | static inline | 296 | static inline |
259 | int find_dirent_page(nfs_readdir_descriptor_t *desc) | 297 | int find_dirent_page(nfs_readdir_descriptor_t *desc) |
@@ -276,7 +314,10 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) | |||
276 | /* NOTE: Someone else may have changed the READDIRPLUS flag */ | 314 | /* NOTE: Someone else may have changed the READDIRPLUS flag */ |
277 | desc->page = page; | 315 | desc->page = page; |
278 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | 316 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ |
279 | status = find_dirent(desc, page); | 317 | if (desc->target_cookie) |
318 | status = find_dirent(desc); | ||
319 | else | ||
320 | status = find_dirent_index(desc); | ||
280 | if (status < 0) | 321 | if (status < 0) |
281 | dir_page_release(desc); | 322 | dir_page_release(desc); |
282 | out: | 323 | out: |
@@ -291,7 +332,8 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) | |||
291 | * Recurse through the page cache pages, and return a | 332 | * Recurse through the page cache pages, and return a |
292 | * filled nfs_entry structure of the next directory entry if possible. | 333 | * filled nfs_entry structure of the next directory entry if possible. |
293 | * | 334 | * |
294 | * The target for the search is 'desc->target'. | 335 | * The target for the search is 'desc->target_cookie' if non-0, |
336 | * 'desc->target_index' otherwise | ||
295 | */ | 337 | */ |
296 | static inline | 338 | static inline |
297 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | 339 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) |
@@ -299,7 +341,19 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | |||
299 | int loop_count = 0; | 341 | int loop_count = 0; |
300 | int res; | 342 | int res; |
301 | 343 | ||
302 | dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); | 344 | if (desc->target_cookie) |
345 | dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target_cookie); | ||
346 | else | ||
347 | dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie number %d\n", desc->target_index); | ||
348 | |||
349 | /* Always search-by-index from the beginning of the cache */ | ||
350 | if (!(desc->target_cookie)) { | ||
351 | desc->page_index = 0; | ||
352 | desc->entry->cookie = desc->entry->prev_cookie = 0; | ||
353 | desc->entry->eof = 0; | ||
354 | desc->current_index = 0; | ||
355 | } | ||
356 | |||
303 | for (;;) { | 357 | for (;;) { |
304 | res = find_dirent_page(desc); | 358 | res = find_dirent_page(desc); |
305 | if (res != -EAGAIN) | 359 | if (res != -EAGAIN) |
@@ -332,11 +386,12 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
332 | struct file *file = desc->file; | 386 | struct file *file = desc->file; |
333 | struct nfs_entry *entry = desc->entry; | 387 | struct nfs_entry *entry = desc->entry; |
334 | struct dentry *dentry = NULL; | 388 | struct dentry *dentry = NULL; |
389 | struct nfs_open_context *ctx = file->private_data; | ||
335 | unsigned long fileid; | 390 | unsigned long fileid; |
336 | int loop_count = 0, | 391 | int loop_count = 0, |
337 | res; | 392 | res; |
338 | 393 | ||
339 | dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); | 394 | dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)entry->cookie); |
340 | 395 | ||
341 | for(;;) { | 396 | for(;;) { |
342 | unsigned d_type = DT_UNKNOWN; | 397 | unsigned d_type = DT_UNKNOWN; |
@@ -356,10 +411,11 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
356 | } | 411 | } |
357 | 412 | ||
358 | res = filldir(dirent, entry->name, entry->len, | 413 | res = filldir(dirent, entry->name, entry->len, |
359 | entry->prev_cookie, fileid, d_type); | 414 | file->f_pos, fileid, d_type); |
360 | if (res < 0) | 415 | if (res < 0) |
361 | break; | 416 | break; |
362 | file->f_pos = desc->target = entry->cookie; | 417 | file->f_pos++; |
418 | desc->target_cookie = entry->cookie; | ||
363 | if (dir_decode(desc) != 0) { | 419 | if (dir_decode(desc) != 0) { |
364 | desc->page_index ++; | 420 | desc->page_index ++; |
365 | break; | 421 | break; |
@@ -369,10 +425,12 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
369 | schedule(); | 425 | schedule(); |
370 | } | 426 | } |
371 | } | 427 | } |
428 | ctx->dir_pos = file->f_pos; | ||
429 | ctx->dir_cookie = desc->target_cookie; | ||
372 | dir_page_release(desc); | 430 | dir_page_release(desc); |
373 | if (dentry != NULL) | 431 | if (dentry != NULL) |
374 | dput(dentry); | 432 | dput(dentry); |
375 | dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); | 433 | dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target_cookie, res); |
376 | return res; | 434 | return res; |
377 | } | 435 | } |
378 | 436 | ||
@@ -398,14 +456,14 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
398 | struct page *page = NULL; | 456 | struct page *page = NULL; |
399 | int status; | 457 | int status; |
400 | 458 | ||
401 | dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); | 459 | dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target_cookie); |
402 | 460 | ||
403 | page = alloc_page(GFP_HIGHUSER); | 461 | page = alloc_page(GFP_HIGHUSER); |
404 | if (!page) { | 462 | if (!page) { |
405 | status = -ENOMEM; | 463 | status = -ENOMEM; |
406 | goto out; | 464 | goto out; |
407 | } | 465 | } |
408 | desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->target, | 466 | desc->error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->target_cookie, |
409 | page, | 467 | page, |
410 | NFS_SERVER(inode)->dtsize, | 468 | NFS_SERVER(inode)->dtsize, |
411 | desc->plus); | 469 | desc->plus); |
@@ -414,7 +472,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
414 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | 472 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ |
415 | if (desc->error >= 0) { | 473 | if (desc->error >= 0) { |
416 | if ((status = dir_decode(desc)) == 0) | 474 | if ((status = dir_decode(desc)) == 0) |
417 | desc->entry->prev_cookie = desc->target; | 475 | desc->entry->prev_cookie = desc->target_cookie; |
418 | } else | 476 | } else |
419 | status = -EIO; | 477 | status = -EIO; |
420 | if (status < 0) | 478 | if (status < 0) |
@@ -435,13 +493,15 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
435 | goto out; | 493 | goto out; |
436 | } | 494 | } |
437 | 495 | ||
438 | /* The file offset position is now represented as a true offset into the | 496 | /* The file offset position represents the dirent entry number. A |
439 | * page cache as is the case in most of the other filesystems. | 497 | last cookie cache takes care of the common case of reading the |
498 | whole directory. | ||
440 | */ | 499 | */ |
441 | static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | 500 | static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) |
442 | { | 501 | { |
443 | struct dentry *dentry = filp->f_dentry; | 502 | struct dentry *dentry = filp->f_dentry; |
444 | struct inode *inode = dentry->d_inode; | 503 | struct inode *inode = dentry->d_inode; |
504 | struct nfs_open_context *ctx = filp->private_data; | ||
445 | nfs_readdir_descriptor_t my_desc, | 505 | nfs_readdir_descriptor_t my_desc, |
446 | *desc = &my_desc; | 506 | *desc = &my_desc; |
447 | struct nfs_entry my_entry; | 507 | struct nfs_entry my_entry; |
@@ -458,17 +518,22 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
458 | } | 518 | } |
459 | 519 | ||
460 | /* | 520 | /* |
461 | * filp->f_pos points to the file offset in the page cache. | 521 | * filp->f_pos points to the dirent entry number. |
462 | * but if the cache has meanwhile been zapped, we need to | 522 | * ctx->dir_pos has the number of the cached cookie. We have |
463 | * read from the last dirent to revalidate f_pos | 523 | * to either find the entry with the appropriate number or |
464 | * itself. | 524 | * revalidate the cookie. |
465 | */ | 525 | */ |
466 | memset(desc, 0, sizeof(*desc)); | 526 | memset(desc, 0, sizeof(*desc)); |
467 | 527 | ||
468 | desc->file = filp; | 528 | desc->file = filp; |
469 | desc->target = filp->f_pos; | ||
470 | desc->decode = NFS_PROTO(inode)->decode_dirent; | 529 | desc->decode = NFS_PROTO(inode)->decode_dirent; |
471 | desc->plus = NFS_USE_READDIRPLUS(inode); | 530 | desc->plus = NFS_USE_READDIRPLUS(inode); |
531 | desc->target_index = filp->f_pos; | ||
532 | |||
533 | if (filp->f_pos == ctx->dir_pos) | ||
534 | desc->target_cookie = ctx->dir_cookie; | ||
535 | else | ||
536 | desc->target_cookie = 0; | ||
472 | 537 | ||
473 | my_entry.cookie = my_entry.prev_cookie = 0; | 538 | my_entry.cookie = my_entry.prev_cookie = 0; |
474 | my_entry.eof = 0; | 539 | my_entry.eof = 0; |
@@ -478,9 +543,10 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
478 | 543 | ||
479 | while(!desc->entry->eof) { | 544 | while(!desc->entry->eof) { |
480 | res = readdir_search_pagecache(desc); | 545 | res = readdir_search_pagecache(desc); |
546 | |||
481 | if (res == -EBADCOOKIE) { | 547 | if (res == -EBADCOOKIE) { |
482 | /* This means either end of directory */ | 548 | /* This means either end of directory */ |
483 | if (desc->entry->cookie != desc->target) { | 549 | if (desc->target_cookie && desc->entry->cookie != desc->target_cookie) { |
484 | /* Or that the server has 'lost' a cookie */ | 550 | /* Or that the server has 'lost' a cookie */ |
485 | res = uncached_readdir(desc, dirent, filldir); | 551 | res = uncached_readdir(desc, dirent, filldir); |
486 | if (res >= 0) | 552 | if (res >= 0) |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 8a8d57d9d660..9fa02e7984ac 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -891,6 +891,8 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rp | |||
891 | ctx->state = NULL; | 891 | ctx->state = NULL; |
892 | ctx->lockowner = current->files; | 892 | ctx->lockowner = current->files; |
893 | ctx->error = 0; | 893 | ctx->error = 0; |
894 | ctx->dir_pos = 0; | ||
895 | ctx->dir_cookie = 0; | ||
894 | } | 896 | } |
895 | return ctx; | 897 | return ctx; |
896 | } | 898 | } |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4ceac9ddac93..f810195ef7ad 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -84,6 +84,9 @@ struct nfs_open_context { | |||
84 | int error; | 84 | int error; |
85 | 85 | ||
86 | struct list_head list; | 86 | struct list_head list; |
87 | |||
88 | int dir_pos; /* Directory cookie cache */ | ||
89 | __u64 dir_cookie; | ||
87 | }; | 90 | }; |
88 | 91 | ||
89 | /* | 92 | /* |