diff --git a/examples/passthroughfs.py b/examples/passthroughfs.py index 2f754bc..5331bec 100755 --- a/examples/passthroughfs.py +++ b/examples/passthroughfs.py @@ -72,10 +72,9 @@ class Operations(pyfuse3.Operations): - enable_writeback_cache = True - - def __init__(self, source: str) -> None: + def __init__(self, source: str, enable_writeback_cache: bool = False) -> None: super().__init__() + self.enable_writeback_cache = enable_writeback_cache self._inode_path_map: dict[InodeT, str | set[str]] = {pyfuse3.ROOT_INODE: source} self._lookup_cnt: defaultdict[InodeT, int] = defaultdict(lambda: 0) self._fd_inode_map: dict[int, InodeT] = dict() @@ -534,6 +533,12 @@ def parse_args(args: list[str]) -> Namespace: parser.add_argument( '--debug-fuse', action='store_true', default=False, help='Enable FUSE debugging output' ) + parser.add_argument( + '--enable-writeback-cache', + action='store_true', + default=False, + help='Enable writeback cache (default: disabled)', + ) return parser.parse_args(args) @@ -541,7 +546,7 @@ def parse_args(args: list[str]) -> Namespace: def main() -> None: options = parse_args(sys.argv[1:]) init_logging(options.debug) - operations = Operations(options.source) + operations = Operations(options.source, enable_writeback_cache=options.enable_writeback_cache) log.debug('Mounting...') fuse_options = set(pyfuse3.default_options) diff --git a/test/test_examples.py b/test/test_examples.py index e9f7f14..59acaa6 100755 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -96,7 +96,8 @@ def test_tmpfs(tmpdir): umount(mount_process, mnt_dir) -def test_passthroughfs(tmpdir): +@pytest.mark.parametrize('enable_writeback_cache', (True, False)) +def test_passthroughfs(tmpdir, enable_writeback_cache): mnt_dir = str(tmpdir.mkdir('mnt')) src_dir = str(tmpdir.mkdir('src')) cmdline = [ @@ -105,6 +106,8 @@ def test_passthroughfs(tmpdir): src_dir, mnt_dir, ] + if enable_writeback_cache: + cmdline.append('--enable-writeback-cache') mount_process = subprocess.Popen(cmdline, stdin=subprocess.DEVNULL, universal_newlines=True) try: wait_for_mount(mount_process, mnt_dir) @@ -125,7 +128,7 @@ def test_passthroughfs(tmpdir): tst_truncate_path(mnt_dir) tst_truncate_fd(mnt_dir) tst_unlink(mnt_dir) - tst_passthrough(src_dir, mnt_dir) + tst_passthrough(src_dir, mnt_dir, enable_writeback_cache=enable_writeback_cache) except: cleanup(mount_process, mnt_dir) raise @@ -408,7 +411,7 @@ def tst_rounding(mnt_dir, ns_tol=0): checked_unlink(filename, mnt_dir, isdir=True) -def tst_passthrough(src_dir, mnt_dir): +def tst_passthrough(src_dir, mnt_dir, enable_writeback_cache: bool = False): # Test propagation from source to mirror name = name_generator() src_name = os.path.join(src_dir, name) @@ -419,7 +422,7 @@ def tst_passthrough(src_dir, mnt_dir): fh.write('Hello, world') assert name in os.listdir(src_dir) assert name in os.listdir(mnt_dir) - assert_same_stats(src_name, mnt_name) + assert_same_stats(src_name, mnt_name, check_times=not enable_writeback_cache) # Test propagation from mirror to source name = name_generator() @@ -431,7 +434,7 @@ def tst_passthrough(src_dir, mnt_dir): fh.write('Hello, world') assert name in os.listdir(src_dir) assert name in os.listdir(mnt_dir) - assert_same_stats(src_name, mnt_name) + assert_same_stats(src_name, mnt_name, check_times=not enable_writeback_cache) # Test propagation inside subdirectory name = name_generator() @@ -446,10 +449,10 @@ def tst_passthrough(src_dir, mnt_dir): fh.write('Hello, world') assert name in os.listdir(src_dir) assert name in os.listdir(mnt_dir) - assert_same_stats(src_name, mnt_name) + assert_same_stats(src_name, mnt_name, check_times=not enable_writeback_cache) -def assert_same_stats(name1, name2): +def assert_same_stats(name1, name2, check_times: bool = True): stat1 = os.stat(name1) stat2 = os.stat(name2) @@ -471,4 +474,11 @@ def assert_same_stats(name1, name2): if name.endswith('_ns') and os.getenv('CI') == 'true': continue + # When FUSE writeback cache is enabled, the kernel maintains mtime/ctime + # internally and only flushes them to the underlying filesystem on close. + # Until then, the timestamps reported for the passthrough mount and the + # backing directory may legitimately differ, so skip strict time checks. + if name.endswith('_ns') and not check_times: + continue + assert v1 == v2, 'Attribute {} differs by {} ({} vs {})'.format(name, v1 - v2, v1, v2)