Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+37 -4
+3
fs/fuse/fuse_i.h
··· 117 117 /* version of the cache */ 118 118 u64 version; 119 119 120 + /* modification time of directory when cache was started */ 121 + struct timespec64 mtime; 122 + 120 123 /* protects above fields */ 121 124 spinlock_t lock; 122 125 } rdc;
+34 -4
fs/fuse/readdir.c
··· 399 399 return res; 400 400 } 401 401 402 - static void fuse_rdc_reset(struct fuse_inode *fi) 402 + static void fuse_rdc_reset(struct inode *inode) 403 403 { 404 + struct fuse_inode *fi = get_fuse_inode(inode); 405 + 404 406 fi->rdc.cached = false; 405 407 fi->rdc.version++; 406 408 fi->rdc.size = 0; ··· 415 413 { 416 414 struct fuse_file *ff = file->private_data; 417 415 struct inode *inode = file_inode(file); 416 + struct fuse_conn *fc = get_fuse_conn(inode); 418 417 struct fuse_inode *fi = get_fuse_inode(inode); 419 418 enum fuse_parse_result res; 420 419 pgoff_t index; ··· 429 426 ff->readdir.cache_off = 0; 430 427 } 431 428 429 + /* 430 + * We're just about to start reading into the cache or reading the 431 + * cache; both cases require an up-to-date mtime value. 432 + */ 433 + if (!ctx->pos && fc->auto_inval_data) { 434 + int err = fuse_update_attributes(inode, file); 435 + 436 + if (err) 437 + return err; 438 + } 439 + 432 440 retry: 433 441 spin_lock(&fi->rdc.lock); 442 + retry_locked: 434 443 if (!fi->rdc.cached) { 444 + /* Starting cache? Set cache mtime. */ 445 + if (!ctx->pos && !fi->rdc.size) { 446 + fi->rdc.mtime = inode->i_mtime; 447 + } 435 448 spin_unlock(&fi->rdc.lock); 436 449 return UNCACHED; 437 450 } 451 + /* 452 + * When at the beginning of the directory (i.e. just after opendir(3) or 453 + * rewinddir(3)), then need to check whether directory contents have 454 + * changed, and reset the cache if so. 455 + */ 456 + if (!ctx->pos) { 457 + if (!timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) { 458 + fuse_rdc_reset(inode); 459 + goto retry_locked; 460 + } 461 + } 462 + 438 463 /* 439 464 * If cache version changed since the last getdents() call, then reset 440 465 * the cache stream. ··· 500 469 * Uh-oh: page gone missing, cache is useless 501 470 */ 502 471 if (fi->rdc.version == ff->readdir.version) 503 - fuse_rdc_reset(fi); 504 - spin_unlock(&fi->rdc.lock); 505 - return UNCACHED; 472 + fuse_rdc_reset(inode); 473 + goto retry_locked; 506 474 } 507 475 508 476 /* Make sure it's still the same version after getting the page. */