aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Galibert <galibert@pobox.com>2005-06-22 13:16:29 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 16:07:33 -0400
commit00a926422765064cb28e218d4837411c88bf6a3e (patch)
tree1e9ad635821c7b037014307d1a2657dc56b17acf
parentae3884621bf5b4caff7785b9a417f262202965b2 (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.c114
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--include/linux/nfs_fs.h3
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 */
234static inline 236static inline
235int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) 237int 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 */
264static inline
265int 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 */
258static inline 296static inline
259int find_dirent_page(nfs_readdir_descriptor_t *desc) 297int 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 */
296static inline 338static inline
297int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) 339int 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 */
441static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) 500static 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/*