Skip to content

Commit

Permalink
Implement simple caching for -fstype
Browse files Browse the repository at this point in the history
  • Loading branch information
nerdroychan committed Jul 12, 2024
1 parent 7f4423e commit d69d51e
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 8 deletions.
63 changes: 57 additions & 6 deletions src/find/matchers/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::cell::RefCell;
use std::path::Path;
use std::{
error::Error,
Expand All @@ -11,10 +12,16 @@ use std::{

use super::Matcher;

/// The latest mapping from dev_id to fs_type, used for saving mount info reads
pub struct Cache {
dev_id: String,
fs_type: String,
}

/// Get the filesystem type of a file.
/// 1. get the metadata of the file
/// 2. get the device ID of the metadata
/// 3. search the filesystem list
/// 3. search the cache, then the filesystem list
///
/// Returns an empty string when no file system list matches.
///
Expand All @@ -24,14 +31,24 @@ use super::Matcher;
///
/// This is only supported on Unix.
#[cfg(unix)]
pub fn get_file_system_type(path: &Path) -> Result<String, Box<dyn Error>> {
pub fn get_file_system_type(
path: &Path,
cache: &RefCell<Option<Cache>>,
) -> Result<String, Box<dyn Error>> {
use std::os::unix::fs::MetadataExt;

let metadata = match path.metadata() {
Ok(metadata) => metadata,
Err(err) => Err(err)?,
};
let dev_id = metadata.dev().to_string();

if let Some(cache) = cache.borrow().as_ref() {
if cache.dev_id == dev_id {
return Ok(cache.fs_type.clone());
}
}

let fs_list = match uucore::fsext::read_fs_list() {
Ok(fs_list) => fs_list,
Err(err) => Err(err)?,
Expand All @@ -41,6 +58,12 @@ pub fn get_file_system_type(path: &Path) -> Result<String, Box<dyn Error>> {
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type);

// cache the latest query if not a match before
cache.replace(Some(Cache {
dev_id,
fs_type: result.clone(),
}));

Ok(result)
}

Expand All @@ -50,11 +73,15 @@ pub fn get_file_system_type(path: &Path) -> Result<String, Box<dyn Error>> {
/// This is only supported on Unix.
pub struct FileSystemMatcher {
fs_text: String,
cache: RefCell<Option<Cache>>,
}

impl FileSystemMatcher {
pub fn new(fs_text: String) -> Self {
Self { fs_text }
Self {
fs_text,
cache: RefCell::new(None),
}
}
}

Expand All @@ -66,7 +93,7 @@ impl Matcher for FileSystemMatcher {
}
#[cfg(unix)]
{
match get_file_system_type(file_info.path()) {
match get_file_system_type(file_info.path(), &self.cache) {
Ok(result) => result == self.fs_text,
Err(_) => {
writeln!(
Expand All @@ -89,9 +116,14 @@ mod tests {
#[cfg(unix)]
fn test_fs_matcher() {
use crate::find::{
matchers::{fs::get_file_system_type, tests::get_dir_entry_for, Matcher},
matchers::{
fs::{get_file_system_type, Cache},
tests::get_dir_entry_for,
Matcher,
},
tests::FakeDependencies,
};
use std::cell::RefCell;
use std::fs::File;
use tempfile::Builder;

Expand All @@ -105,7 +137,26 @@ mod tests {
let _ = File::create(foo_path).expect("create temp file");
let file_info = get_dir_entry_for(&temp_dir.path().to_string_lossy(), "foo");

let target_fs_type = get_file_system_type(file_info.path()).unwrap();
// create an empty cache for initial fs type lookup
let empty_cache = RefCell::new(None);
let target_fs_type = get_file_system_type(file_info.path(), &empty_cache).unwrap();

// should work with unmatched cache, and the cache should be set to the last query result
let unmatched_cache = RefCell::new(Some(Cache {
dev_id: "foo".to_string(),
fs_type: "bar".to_string(),
}));
let target_fs_type_unmatched_cache =
get_file_system_type(file_info.path(), &unmatched_cache).unwrap();
assert_eq!(
target_fs_type, target_fs_type_unmatched_cache,
"get_file_system_type should return correct result with unmatched cache"
);
assert_eq!(
unmatched_cache.borrow().as_ref().unwrap().fs_type,
target_fs_type,
"get_file_system_type should set the cache to the last query result"
);

// should match fs type
let matcher = super::FileSystemMatcher::new(target_fs_type.clone());
Expand Down
4 changes: 3 additions & 1 deletion src/find/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1206,10 +1206,12 @@ mod tests {
fn test_fs_matcher() {
use crate::find::tests::FakeDependencies;
use matchers::fs::get_file_system_type;
use std::cell::RefCell;
use std::path::Path;

let path = Path::new("./test_data/simple/subdir");
let target_fs_type = get_file_system_type(path).unwrap();
let empty_cache = RefCell::new(None);
let target_fs_type = get_file_system_type(path, &empty_cache).unwrap();

// should match fs type
let deps = FakeDependencies::new();
Expand Down
4 changes: 3 additions & 1 deletion tests/find_cmd_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,10 +785,12 @@ fn find_age_range() {
#[serial(working_dir)]
fn find_fs() {
use findutils::find::matchers::fs::get_file_system_type;
use std::cell::RefCell;
use std::path::Path;

let path = Path::new("./test_data/simple/subdir");
let target_fs_type = get_file_system_type(path).unwrap();
let empty_cache = RefCell::new(None);
let target_fs_type = get_file_system_type(path, &empty_cache).unwrap();

// match fs type
Command::cargo_bin("find")
Expand Down

0 comments on commit d69d51e

Please sign in to comment.