Merge pull request #167 from tavianator/stat

find/matchers: Implement -inum and -links
This commit is contained in:
Sylvestre Ledru
2022-06-27 08:53:01 +02:00
committed by GitHub
3 changed files with 190 additions and 0 deletions
+18
View File
@@ -20,6 +20,7 @@ mod prune;
mod quit;
mod regex;
mod size;
mod stat;
mod time;
mod type_matcher;
@@ -46,6 +47,7 @@ use self::prune::PruneMatcher;
use self::quit::QuitMatcher;
use self::regex::RegexMatcher;
use self::size::SizeMatcher;
use self::stat::{InodeMatcher, LinksMatcher};
use self::time::{FileTimeMatcher, FileTimeType, NewerMatcher};
use self::type_matcher::TypeMatcher;
@@ -401,6 +403,22 @@ fn build_matcher_tree(
.into_box(),
)
}
"-inum" => {
if i >= args.len() - 1 {
return Err(From::from(format!("missing argument to {}", args[i])));
}
let inum = convert_arg_to_comparable_value(args[i], args[i + 1])?;
i += 1;
Some(InodeMatcher::new(inum)?.into_box())
}
"-links" => {
if i >= args.len() - 1 {
return Err(From::from(format!("missing argument to {}", args[i])));
}
let inum = convert_arg_to_comparable_value(args[i], args[i + 1])?;
i += 1;
Some(LinksMatcher::new(inum)?.into_box())
}
"-executable" => Some(AccessMatcher::Executable.into_box()),
"-perm" => {
if i >= args.len() - 1 {
+138
View File
@@ -0,0 +1,138 @@
// Copyright 2022 Tavian Barnes
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
use std::error::Error;
use walkdir::DirEntry;
use super::{ComparableValue, Matcher, MatcherIO};
/// Inode number matcher.
pub struct InodeMatcher {
ino: ComparableValue,
}
impl InodeMatcher {
#[cfg(unix)]
pub fn new(ino: ComparableValue) -> Result<Self, Box<dyn Error>> {
Ok(Self { ino })
}
#[cfg(not(unix))]
pub fn new(_ino: ComparableValue) -> Result<Self, Box<dyn Error>> {
Err(From::from(
"Inode numbers are not available on this platform",
))
}
}
impl Matcher for InodeMatcher {
#[cfg(unix)]
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
match file_info.metadata() {
Ok(metadata) => self.ino.matches(metadata.ino()),
Err(_) => false,
}
}
#[cfg(not(unix))]
fn matches(&self, _: &DirEntry, _: &mut MatcherIO) -> bool {
unreachable!("Inode numbers are not available on this platform")
}
}
/// Link count matcher.
pub struct LinksMatcher {
nlink: ComparableValue,
}
impl LinksMatcher {
#[cfg(unix)]
pub fn new(nlink: ComparableValue) -> Result<Self, Box<dyn Error>> {
Ok(Self { nlink })
}
#[cfg(not(unix))]
pub fn new(_nlink: ComparableValue) -> Result<Self, Box<dyn Error>> {
Err(From::from("Link counts are not available on this platform"))
}
}
impl Matcher for LinksMatcher {
#[cfg(unix)]
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
match file_info.metadata() {
Ok(metadata) => self.nlink.matches(metadata.nlink()),
Err(_) => false,
}
}
#[cfg(not(unix))]
fn matches(&self, _: &DirEntry, _: &mut MatcherIO) -> bool {
unreachable!("Link counts are not available on this platform")
}
}
#[cfg(test)]
#[cfg(unix)]
mod tests {
use super::*;
use crate::find::matchers::tests::get_dir_entry_for;
use crate::find::matchers::Matcher;
use crate::find::tests::FakeDependencies;
#[test]
fn inode_matcher() {
let file_info = get_dir_entry_for("test_data/simple", "abbbc");
let metadata = file_info.metadata().unwrap();
let deps = FakeDependencies::new();
let matcher = InodeMatcher::new(ComparableValue::EqualTo(metadata.ino())).unwrap();
assert!(
matcher.matches(&file_info, &mut deps.new_matcher_io()),
"inode number should match"
);
let matcher = InodeMatcher::new(ComparableValue::LessThan(metadata.ino())).unwrap();
assert!(
!matcher.matches(&file_info, &mut deps.new_matcher_io()),
"inode number should not match"
);
let matcher = InodeMatcher::new(ComparableValue::MoreThan(metadata.ino())).unwrap();
assert!(
!matcher.matches(&file_info, &mut deps.new_matcher_io()),
"inode number should not match"
);
}
#[test]
fn links_matcher() {
let file_info = get_dir_entry_for("test_data/simple", "abbbc");
let deps = FakeDependencies::new();
let matcher = LinksMatcher::new(ComparableValue::EqualTo(1)).unwrap();
assert!(
matcher.matches(&file_info, &mut deps.new_matcher_io()),
"link count should match"
);
let matcher = LinksMatcher::new(ComparableValue::LessThan(1)).unwrap();
assert!(
!matcher.matches(&file_info, &mut deps.new_matcher_io()),
"link count should not match"
);
let matcher = LinksMatcher::new(ComparableValue::MoreThan(1)).unwrap();
assert!(
!matcher.matches(&file_info, &mut deps.new_matcher_io()),
"link count should not match"
);
}
}
+34
View File
@@ -407,6 +407,40 @@ fn find_perm() {
.success();
}
#[cfg(unix)]
#[serial(working_dir)]
#[test]
fn find_inum() {
use std::fs::metadata;
use std::os::unix::fs::MetadataExt;
let inum = metadata("test_data/simple/abbbc")
.expect("metadata for abbbc")
.ino()
.to_string();
Command::cargo_bin("find")
.expect("found binary")
.args(&["test_data", "-inum", &inum])
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::contains("abbbc"));
}
#[cfg(unix)]
#[serial(working_dir)]
#[test]
fn find_links() {
Command::cargo_bin("find")
.expect("found binary")
.args(&["test_data", "-links", "1"])
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::contains("abbbc"));
}
#[serial(working_dir)]
#[test]
fn find_mount_xdev() {