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

fuse: use iversion for readdir cache verification

Use the internal iversion counter to make sure modifications of the
directory through this filesystem are not missed by the mtime check (due to
mtime granularity).

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

+21 -8
+14 -7
fs/fuse/dir.c
··· 14 14 #include <linux/namei.h> 15 15 #include <linux/slab.h> 16 16 #include <linux/xattr.h> 17 + #include <linux/iversion.h> 17 18 #include <linux/posix_acl.h> 18 19 19 20 static void fuse_advise_use_readdirplus(struct inode *dir) ··· 88 87 void fuse_invalidate_attr(struct inode *inode) 89 88 { 90 89 get_fuse_inode(inode)->i_time = 0; 90 + } 91 + 92 + static void fuse_dir_changed(struct inode *dir) 93 + { 94 + fuse_invalidate_attr(dir); 95 + inode_maybe_inc_iversion(dir, false); 91 96 } 92 97 93 98 /** ··· 454 447 kfree(forget); 455 448 d_instantiate(entry, inode); 456 449 fuse_change_entry_timeout(entry, &outentry); 457 - fuse_invalidate_attr(dir); 450 + fuse_dir_changed(dir); 458 451 err = finish_open(file, entry, generic_file_open); 459 452 if (err) { 460 453 fuse_sync_release(ff, flags); ··· 568 561 } else { 569 562 fuse_change_entry_timeout(entry, &outarg); 570 563 } 571 - fuse_invalidate_attr(dir); 564 + fuse_dir_changed(dir); 572 565 return 0; 573 566 574 567 out_put_forget_req: ··· 678 671 drop_nlink(inode); 679 672 spin_unlock(&fc->lock); 680 673 fuse_invalidate_attr(inode); 681 - fuse_invalidate_attr(dir); 674 + fuse_dir_changed(dir); 682 675 fuse_invalidate_entry_cache(entry); 683 676 fuse_update_ctime(inode); 684 677 } else if (err == -EINTR) ··· 700 693 err = fuse_simple_request(fc, &args); 701 694 if (!err) { 702 695 clear_nlink(d_inode(entry)); 703 - fuse_invalidate_attr(dir); 696 + fuse_dir_changed(dir); 704 697 fuse_invalidate_entry_cache(entry); 705 698 } else if (err == -EINTR) 706 699 fuse_invalidate_entry(entry); ··· 739 732 fuse_update_ctime(d_inode(newent)); 740 733 } 741 734 742 - fuse_invalidate_attr(olddir); 735 + fuse_dir_changed(olddir); 743 736 if (olddir != newdir) 744 - fuse_invalidate_attr(newdir); 737 + fuse_dir_changed(newdir); 745 738 746 739 /* newent will end up negative */ 747 740 if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) { ··· 974 967 if (!entry) 975 968 goto unlock; 976 969 977 - fuse_invalidate_attr(parent); 970 + fuse_dir_changed(parent); 978 971 fuse_invalidate_entry(entry); 979 972 980 973 if (child_nodeid != 0 && d_really_is_positive(entry)) {
+3
fs/fuse/fuse_i.h
··· 120 120 /* modification time of directory when cache was started */ 121 121 struct timespec64 mtime; 122 122 123 + /* iversion of directory when cache was started */ 124 + u64 iversion; 125 + 123 126 /* protects above fields */ 124 127 spinlock_t lock; 125 128 } rdc;
+4 -1
fs/fuse/readdir.c
··· 8 8 9 9 10 10 #include "fuse_i.h" 11 + #include <linux/iversion.h> 11 12 #include <linux/posix_acl.h> 12 13 #include <linux/pagemap.h> 13 14 #include <linux/highmem.h> ··· 448 447 /* Starting cache? Set cache mtime. */ 449 448 if (!ctx->pos && !fi->rdc.size) { 450 449 fi->rdc.mtime = inode->i_mtime; 450 + fi->rdc.iversion = inode_query_iversion(inode); 451 451 } 452 452 spin_unlock(&fi->rdc.lock); 453 453 return UNCACHED; ··· 459 457 * changed, and reset the cache if so. 460 458 */ 461 459 if (!ctx->pos) { 462 - if (!timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) { 460 + if (inode_peek_iversion(inode) != fi->rdc.iversion || 461 + !timespec64_equal(&fi->rdc.mtime, &inode->i_mtime)) { 463 462 fuse_rdc_reset(inode); 464 463 goto retry_locked; 465 464 }