From 0ee823e00a29ca15ac2699ec588f9c1ff2c7b04d Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Thu, 3 Mar 2022 18:51:38 +0200 Subject: perf: cache user and group name lookups Sponsored-by: author --- src/fsentry.rs | 35 ++++++++++++++++++----------------- src/fsiter.rs | 23 +++++++++++++++-------- src/generation.rs | 9 +++++---- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/fsentry.rs b/src/fsentry.rs index 9f40fd0..90afd70 100644 --- a/src/fsentry.rs +++ b/src/fsentry.rs @@ -8,6 +8,7 @@ use std::fs::{FileType, Metadata}; use std::os::unix::ffi::OsStringExt; use std::os::unix::fs::FileTypeExt; use std::path::{Path, PathBuf}; +use users::{Groups, Users, UsersCache}; #[cfg(target_os = "linux")] use std::os::linux::fs::MetadataExt; @@ -69,7 +70,11 @@ pub enum FsEntryError { #[allow(clippy::len_without_is_empty)] impl FilesystemEntry { /// Create an `FsEntry` from a file's metadata. - pub fn from_metadata(path: &Path, meta: &Metadata) -> Result { + pub fn from_metadata( + path: &Path, + meta: &Metadata, + cache: &mut UsersCache, + ) -> Result { let kind = FilesystemKind::from_file_type(meta.file_type()); let symlink_target = if kind == FilesystemKind::Symlink { debug!("reading symlink target for {:?}", path); @@ -82,6 +87,16 @@ impl FilesystemEntry { let uid = meta.st_uid(); let gid = meta.st_gid(); + let user: String = if let Some(user) = cache.get_user_by_uid(uid) { + user.name().to_string_lossy().to_string() + } else { + "".to_string() + }; + let group = if let Some(group) = cache.get_group_by_gid(gid) { + group.name().to_string_lossy().to_string() + } else { + "".to_string() + }; Ok(Self { path: path.to_path_buf().into_os_string().into_vec(), @@ -95,8 +110,8 @@ impl FilesystemEntry { symlink_target, uid, gid, - user: get_username(uid), - group: get_groupname(gid), + user, + group, }) } @@ -152,20 +167,6 @@ impl FilesystemEntry { } } -fn get_username(uid: u32) -> String { - match users::get_user_by_uid(uid) { - None => "".to_string(), - Some(user) => user.name().to_os_string().to_string_lossy().into_owned(), - } -} - -fn get_groupname(gid: u32) -> String { - match users::get_group_by_gid(gid) { - None => "".to_string(), - Some(group) => group.name().to_os_string().to_string_lossy().into_owned(), - } -} - /// Different types of file system entries. #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum FilesystemKind { diff --git a/src/fsiter.rs b/src/fsiter.rs index 2747cce..8b231c1 100644 --- a/src/fsiter.rs +++ b/src/fsiter.rs @@ -1,8 +1,9 @@ //! Iterate over directory tree. use crate::fsentry::{FilesystemEntry, FsEntryError}; -use log::{debug, warn}; +use log::warn; use std::path::{Path, PathBuf}; +use users::UsersCache; use walkdir::{DirEntry, IntoIter, WalkDir}; /// Filesystem entry along with additional info about it. @@ -56,6 +57,7 @@ impl Iterator for FsIterator { /// Cachedir-aware adaptor for WalkDir: it skips the contents of dirs that contain CACHEDIR.TAG, /// but still yields entries for the dir and the tag themselves. struct SkipCachedirs { + cache: UsersCache, iter: IntoIter, exclude_cache_tag_directories: bool, // This is the last tag we've found. `next()` will yield it before asking `iter` for more @@ -66,6 +68,7 @@ struct SkipCachedirs { impl SkipCachedirs { fn new(iter: IntoIter, exclude_cache_tag_directories: bool) -> Self { Self { + cache: UsersCache::new(), iter, exclude_cache_tag_directories, cachedir_tag: None, @@ -109,7 +112,7 @@ impl SkipCachedirs { if content == CACHEDIR_TAG { self.iter.skip_current_dir(); - self.cachedir_tag = Some(new_entry(&tag_path, true)); + self.cachedir_tag = Some(new_entry(&tag_path, true, &mut self.cache)); } } } @@ -120,22 +123,26 @@ impl Iterator for SkipCachedirs { fn next(&mut self) -> Option { self.cachedir_tag.take().or_else(|| { let next = self.iter.next(); - debug!("walkdir found: {:?}", next); + // debug!("walkdir found: {:?}", next); match next { None => None, Some(Err(err)) => Some(Err(FsIterError::WalkDir(err))), Some(Ok(entry)) => { self.try_enqueue_cachedir_tag(&entry); - Some(new_entry(entry.path(), false)) + Some(new_entry(entry.path(), false, &mut self.cache)) } } }) } } -fn new_entry(path: &Path, is_cachedir_tag: bool) -> Result { +fn new_entry( + path: &Path, + is_cachedir_tag: bool, + cache: &mut UsersCache, +) -> Result { let meta = std::fs::symlink_metadata(path); - debug!("metadata for {:?}: {:?}", path, meta); + // debug!("metadata for {:?}: {:?}", path, meta); let meta = match meta { Ok(meta) => meta, Err(err) => { @@ -143,8 +150,8 @@ fn new_entry(path: &Path, is_cachedir_tag: bool) -> Result