From 1be5541c746e4c18376fd61aad8da1082b8b5721 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 8 Jun 2026 12:23:20 +0200 Subject: [PATCH] find: avoid redundant stat per -fstype evaluation The -fstype matcher called path.symlink_metadata() (a statx/lstat syscall) on every entry before consulting its cache, just to obtain the device id. With several -fstype clauses on the same expression (as updatedb builds: nfs/NFS/proc), every file was stat'd once per clause. Derive the device id from WalkEntry::metadata() instead, which is cached on the entry in a OnceCell and shared across all matchers, so each file is stat'd at most once regardless of how many -fstype clauses run. On 'updatedb --localpaths=/usr' (~506k files) this drops statx calls from ~1.52M to ~506k and wall-clock from ~1.82s to ~1.32s, with byte-identical output. --- src/find/matchers/fs.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/find/matchers/fs.rs b/src/find/matchers/fs.rs index 2a668a3..f8ae47b 100644 --- a/src/find/matchers/fs.rs +++ b/src/find/matchers/fs.rs @@ -42,8 +42,18 @@ pub fn get_file_system_type(path: &Path, cache: &RefCell>) -> URes Ok(metadata) => metadata, Err(err) => Err(err)?, }; - let dev_id = metadata.dev().to_string(); + fs_type_for_dev(metadata.dev().to_string(), cache) +} + +/// Resolve a device id to its filesystem type, consulting (and updating) `cache`. +/// +/// Callers are expected to obtain `dev_id` from metadata they already hold (e.g. the cached +/// [`WalkEntry::metadata`]), so that no extra `stat`/`statx` syscall is issued per entry. +/// +/// This is only supported on Unix. +#[cfg(unix)] +pub fn fs_type_for_dev(dev_id: String, cache: &RefCell>) -> UResult { if let Some(cache) = cache.borrow().as_ref() { if cache.dev_id == dev_id { return Ok(cache.fs_type.clone()); @@ -94,7 +104,24 @@ impl FileSystemMatcher { impl Matcher for FileSystemMatcher { #[cfg(unix)] fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool { - if let Ok(result) = get_file_system_type(file_info.path(), &self.cache) { + use std::os::unix::fs::MetadataExt; + + // Reuse the metadata already cached on the entry (a single shared `statx` per entry) + // rather than issuing a fresh `lstat`/`statx` here. With several `-fstype` clauses (as + // `updatedb` builds) this turns N stats per file into one. + let Ok(metadata) = file_info.metadata() else { + writeln!( + &mut stderr(), + "Error getting filesystem type for {}", + file_info.path().to_string_lossy() + ) + .unwrap(); + + return false; + }; + let dev_id = metadata.dev().to_string(); + + if let Ok(result) = fs_type_for_dev(dev_id, &self.cache) { result == self.fs_text } else { writeln!(