summaryrefslogtreecommitdiffstats
path: root/fs/fuse/readdir.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2018-10-01 04:07:04 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-10-01 04:07:04 -0400
commit7118883b44b8edfea732aadeb0d4424da3f152b2 (patch)
treebded501b21b8493499fb0eda642fae6fdd2413b8 /fs/fuse/readdir.c
parent3494927e090bf511e54eecaf33a8e56e5c0463db (diff)
fuse: use mtime for readdir cache verification
Store the modification time of the directory in the cache, obtained before starting to fill the cache. When reading the cache, verify that the directory hasn't changed, by checking if current modification time is the same as the one stored in the cache. This only needs to be done when the current file position is at the beginning of the directory, as mandated by POSIX. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse/readdir.c')
-rw-r--r--fs/fuse/readdir.c38
1 files changed, 34 insertions, 4 deletions
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index 18318cc31c05..dafd6543cca2 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -399,8 +399,10 @@ static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff,
399 return res; 399 return res;
400} 400}
401 401
402static void fuse_rdc_reset(struct fuse_inode *fi) 402static void fuse_rdc_reset(struct inode *inode)
403{ 403{
404 struct fuse_inode *fi = get_fuse_inode(inode);
405
404 fi->rdc.cached = false; 406 fi->rdc.cached = false;
405 fi->rdc.version++; 407 fi->rdc.version++;
406 fi->rdc.size = 0; 408 fi->rdc.size = 0;
@@ -413,6 +415,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
413{ 415{
414 struct fuse_file *ff = file->private_data; 416 struct fuse_file *ff = file->private_data;
415 struct inode *inode = file_inode(file); 417 struct inode *inode = file_inode(file);
418 struct fuse_conn *fc = get_fuse_conn(inode);
416 struct fuse_inode *fi = get_fuse_inode(inode); 419 struct fuse_inode *fi = get_fuse_inode(inode);
417 enum fuse_parse_result res; 420 enum fuse_parse_result res;
418 pgoff_t index; 421 pgoff_t index;
@@ -426,13 +429,41 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
426 ff->readdir.cache_off = 0; 429 ff->readdir.cache_off = 0;
427 } 430 }
428 431
432 /*
433 * We're just about to start reading into the cache or reading the
434 * cache; both cases require an up-to-date mtime value.
435 */
436 if (!ctx->pos && fc->auto_inval_data) {
437 int err = fuse_update_attributes(inode, file);
438
439 if (err)
440 return err;
441 }
442
429retry: 443retry:
430 spin_lock(&fi->rdc.lock); 444 spin_lock(&fi->rdc.lock);
445retry_locked:
431 if (!fi->rdc.cached) { 446 if (!fi->rdc.cached) {
447 /* Starting cache? Set cache mtime. */
448 if (!ctx->pos && !fi->rdc.size) {
449 fi->rdc.mtime = inode->i_mtime;
450 }
432 spin_unlock(&fi->rdc.lock); 451 spin_unlock(&fi->rdc.lock);
433 return UNCACHED; 452 return UNCACHED;
434 } 453 }
435 /* 454 /*
455 * When at the beginning of the directory (i.e. just after opendir(3) or
456 * rewinddir(3)), then need to check whether directory contents have
457 * changed, and reset the cache if so.
458 */
459 if (!ctx->pos) {
460 if (!timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) {
461 fuse_rdc_reset(inode);
462 goto retry_locked;
463 }
464 }
465
466 /*
436 * If cache version changed since the last getdents() call, then reset 467 * If cache version changed since the last getdents() call, then reset
437 * the cache stream. 468 * the cache stream.
438 */ 469 */
@@ -469,9 +500,8 @@ retry:
469 * Uh-oh: page gone missing, cache is useless 500 * Uh-oh: page gone missing, cache is useless
470 */ 501 */
471 if (fi->rdc.version == ff->readdir.version) 502 if (fi->rdc.version == ff->readdir.version)
472 fuse_rdc_reset(fi); 503 fuse_rdc_reset(inode);
473 spin_unlock(&fi->rdc.lock); 504 goto retry_locked;
474 return UNCACHED;
475 } 505 }
476 506
477 /* Make sure it's still the same version after getting the page. */ 507 /* Make sure it's still the same version after getting the page. */