diff --git a/crates/memofs/src/lib.rs b/crates/memofs/src/lib.rs index 48be421f..0e71185f 100644 --- a/crates/memofs/src/lib.rs +++ b/crates/memofs/src/lib.rs @@ -50,6 +50,11 @@ pub struct PrefetchCache { /// Populated during prefetch so that `get_dir_middleware` can skip /// per-file VFS metadata probes entirely. pub dir_init: HashMap>, + /// Directories that were recursively walked during prefetch. A metadata + /// cache miss is only treated as NotFound if the queried path falls + /// under one of these roots. Paths outside walked roots fall through + /// to the backend (they may exist but weren't covered by the walk). + pub walked_roots: Vec, } mod sealed { @@ -288,10 +293,16 @@ impl VfsInner { if let Some(&is_file) = cache.is_file.get(path) { return Ok(Metadata { is_file }); } - return Err(io::Error::new( - io::ErrorKind::NotFound, - "not in prefetch cache", - )); + if cache + .walked_roots + .iter() + .any(|root| path.starts_with(root)) + { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "not in prefetch cache", + )); + } } self.backend.metadata(path) @@ -754,6 +765,7 @@ mod test { is_file: HashMap::new(), children: HashMap::new(), dir_init: HashMap::new(), + walked_roots: Vec::new(), } } @@ -891,6 +903,7 @@ mod test { is_file: HashMap::new(), children: HashMap::new(), dir_init: HashMap::new(), + walked_roots: Vec::new(), }); let result = vfs.read(&file_path).unwrap(); @@ -937,6 +950,7 @@ mod test { is_file: HashMap::new(), children: HashMap::new(), dir_init: HashMap::new(), + walked_roots: Vec::new(), }); let handles: Vec<_> = (0..100) @@ -978,6 +992,7 @@ mod test { is_file: HashMap::new(), children: HashMap::new(), dir_init: HashMap::new(), + walked_roots: Vec::new(), }); for i in 0..50 { diff --git a/src/serve_session.rs b/src/serve_session.rs index 96650fe9..db9daed7 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -156,6 +156,7 @@ fn prefetch_project_files(project: &Project, sync_scripts_only: bool) -> io::Res is_file: HashMap::new(), children: HashMap::new(), dir_init: HashMap::new(), + walked_roots: Vec::new(), }); } @@ -181,11 +182,15 @@ fn prefetch_project_files(project: &Project, sync_scripts_only: bool) -> io::Res // Recursive walk of each $path root. Canonicalize to resolve ".." // before the equality/starts_with checks. let canonical_folder = std::fs::canonicalize(folder).unwrap_or_else(|_| folder.to_path_buf()); + let mut walked_roots: Vec = Vec::new(); for root in &roots { if !root.exists() { continue; } let canonical_root = std::fs::canonicalize(root).unwrap_or_else(|_| root.to_path_buf()); + if canonical_root != canonical_folder { + walked_roots.push(canonical_root.clone()); + } if canonical_root == canonical_folder { // $path points to the project folder itself -- shallow walk // already covers depth 0-1, fill in the subtree. @@ -286,6 +291,7 @@ fn prefetch_project_files(project: &Project, sync_scripts_only: bool) -> io::Res is_file: is_file_map, children: children_map, dir_init: dir_init_map, + walked_roots, }) } diff --git a/tests/tests/build.rs b/tests/tests/build.rs index cf01557f..2d8865b3 100644 --- a/tests/tests/build.rs +++ b/tests/tests/build.rs @@ -269,6 +269,7 @@ fn parallel_snapshot_with_prefetch_cache() { is_file, children: children_map, dir_init: std::collections::HashMap::new(), + walked_roots: Vec::new(), }); let snap_cached = librojo::snapshot_from_vfs(&ctx, &vfs_cached, &project_path) @@ -375,6 +376,7 @@ fn overlapping_path_roots_no_duplicate_children() { is_file, children: children_map, dir_init: std::collections::HashMap::new(), + walked_roots: Vec::new(), }); let snap = librojo::snapshot_from_vfs(&ctx, &vfs, &project_path)