pxar: match_pattern: refactor match_filename
The match_filename() in sequentail_decoder and encoder are moved to be static functions of MatchPattern. This allows to reuse the code also in the catalog find implementation. Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
parent
f084505ec5
commit
d3dbe52f37
|
@ -736,7 +736,11 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
Err(err) => bail!("fstat {:?} failed - {}", self.full_path(), err),
|
Err(err) => bail!("fstat {:?} failed - {}", self.full_path(), err),
|
||||||
};
|
};
|
||||||
|
|
||||||
match match_filename(&filename, &stat, &local_match_pattern)? {
|
match MatchPattern::match_filename_exclude(
|
||||||
|
&filename,
|
||||||
|
is_directory(&stat),
|
||||||
|
&local_match_pattern,
|
||||||
|
)? {
|
||||||
(MatchType::Positive, _) => {
|
(MatchType::Positive, _) => {
|
||||||
let filename_osstr = std::ffi::OsStr::from_bytes(filename.to_bytes());
|
let filename_osstr = std::ffi::OsStr::from_bytes(filename.to_bytes());
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
@ -1227,32 +1231,6 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a match, an updated MatchPattern list to pass to the matched child is returned.
|
|
||||||
fn match_filename(
|
|
||||||
filename: &CStr,
|
|
||||||
stat: &FileStat,
|
|
||||||
match_pattern: &[MatchPattern],
|
|
||||||
) -> Result<(MatchType, Vec<MatchPattern>), Error> {
|
|
||||||
let mut child_pattern = Vec::new();
|
|
||||||
let mut match_state = MatchType::None;
|
|
||||||
|
|
||||||
for pattern in match_pattern {
|
|
||||||
match pattern.matches_filename(filename, is_directory(&stat))? {
|
|
||||||
MatchType::None => {}
|
|
||||||
MatchType::Positive => match_state = MatchType::Positive,
|
|
||||||
MatchType::Negative => match_state = MatchType::Negative,
|
|
||||||
match_type => {
|
|
||||||
if match_state != MatchType::Positive && match_state != MatchType::Negative {
|
|
||||||
match_state = match_type;
|
|
||||||
}
|
|
||||||
child_pattern.push(pattern.get_rest_pattern());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((match_state, child_pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn errno_is_unsupported(errno: Errno) -> bool {
|
fn errno_is_unsupported(errno: Errno) -> bool {
|
||||||
match errno {
|
match errno {
|
||||||
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,
|
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,
|
||||||
|
|
|
@ -290,6 +290,142 @@ impl MatchPattern {
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Match the given filename against the set of match patterns.
|
||||||
|
///
|
||||||
|
/// A positive match is intended to includes the full subtree (unless another
|
||||||
|
/// negative match excludes entries later).
|
||||||
|
/// The `MatchType` together with an updated `MatchPattern` list for passing
|
||||||
|
/// to the matched child is returned.
|
||||||
|
/// ```
|
||||||
|
/// # use std::ffi::CString;
|
||||||
|
/// # use self::proxmox_backup::pxar::{MatchPattern, MatchType};
|
||||||
|
/// # fn main() -> Result<(), failure::Error> {
|
||||||
|
/// let patterns = vec![
|
||||||
|
/// MatchPattern::from_line(b"some/match/pattern/")?.unwrap(),
|
||||||
|
/// MatchPattern::from_line(b"to_match/")?.unwrap()
|
||||||
|
/// ];
|
||||||
|
/// let filename = CString::new("some")?;
|
||||||
|
/// let is_dir = true;
|
||||||
|
/// let (match_type, child_pattern) = MatchPattern::match_filename_include(
|
||||||
|
/// &filename,
|
||||||
|
/// is_dir,
|
||||||
|
/// &patterns
|
||||||
|
/// )?;
|
||||||
|
/// assert_eq!(match_type, MatchType::PartialPositive);
|
||||||
|
/// /// child pattern will be the same as ...
|
||||||
|
/// let pattern = MatchPattern::from_line(b"match/pattern/")?.unwrap();
|
||||||
|
///
|
||||||
|
/// let filename = CString::new("to_match")?;
|
||||||
|
/// let is_dir = true;
|
||||||
|
/// let (match_type, child_pattern) = MatchPattern::match_filename_include(
|
||||||
|
/// &filename,
|
||||||
|
/// is_dir,
|
||||||
|
/// &patterns
|
||||||
|
/// )?;
|
||||||
|
/// assert_eq!(match_type, MatchType::Positive);
|
||||||
|
/// /// child pattern will be the same as ...
|
||||||
|
/// let pattern = MatchPattern::from_line(b"**/*")?.unwrap();
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn match_filename_include(
|
||||||
|
filename: &CStr,
|
||||||
|
is_dir: bool,
|
||||||
|
match_pattern: &[MatchPattern],
|
||||||
|
) -> Result<(MatchType, Vec<MatchPattern>), Error> {
|
||||||
|
let mut child_pattern = Vec::new();
|
||||||
|
let mut match_state = MatchType::None;
|
||||||
|
|
||||||
|
for pattern in match_pattern {
|
||||||
|
match pattern.matches_filename(filename, is_dir)? {
|
||||||
|
MatchType::None => continue,
|
||||||
|
MatchType::Positive => {
|
||||||
|
match_state = MatchType::Positive;
|
||||||
|
// Full match so lets include everything below this node
|
||||||
|
let incl_pattern = MatchPattern::from_line(b"**/*").unwrap().unwrap();
|
||||||
|
child_pattern.push(incl_pattern);
|
||||||
|
}
|
||||||
|
MatchType::Negative => match_state = MatchType::Negative,
|
||||||
|
MatchType::PartialPositive => {
|
||||||
|
if match_state != MatchType::Negative && match_state != MatchType::Positive {
|
||||||
|
match_state = MatchType::PartialPositive;
|
||||||
|
}
|
||||||
|
child_pattern.push(pattern.get_rest_pattern());
|
||||||
|
}
|
||||||
|
MatchType::PartialNegative => {
|
||||||
|
if match_state == MatchType::PartialPositive {
|
||||||
|
match_state = MatchType::PartialNegative;
|
||||||
|
}
|
||||||
|
child_pattern.push(pattern.get_rest_pattern());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((match_state, child_pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Match the given filename against the set of match patterns.
|
||||||
|
///
|
||||||
|
/// A positive match is intended to exclude the full subtree, independent of
|
||||||
|
/// matches deeper down the tree.
|
||||||
|
/// The `MatchType` together with an updated `MatchPattern` list for passing
|
||||||
|
/// to the matched child is returned.
|
||||||
|
/// ```
|
||||||
|
/// # use std::ffi::CString;
|
||||||
|
/// # use self::proxmox_backup::pxar::{MatchPattern, MatchType};
|
||||||
|
/// # fn main() -> Result<(), failure::Error> {
|
||||||
|
/// let patterns = vec![
|
||||||
|
/// MatchPattern::from_line(b"some/match/pattern/")?.unwrap(),
|
||||||
|
/// MatchPattern::from_line(b"to_match/")?.unwrap()
|
||||||
|
/// ];
|
||||||
|
/// let filename = CString::new("some")?;
|
||||||
|
/// let is_dir = true;
|
||||||
|
/// let (match_type, child_pattern) = MatchPattern::match_filename_exclude(
|
||||||
|
/// &filename,
|
||||||
|
/// is_dir,
|
||||||
|
/// &patterns
|
||||||
|
/// )?;
|
||||||
|
/// assert_eq!(match_type, MatchType::PartialPositive);
|
||||||
|
/// /// child pattern will be the same as ...
|
||||||
|
/// let pattern = MatchPattern::from_line(b"match/pattern/")?.unwrap();
|
||||||
|
///
|
||||||
|
/// let filename = CString::new("to_match")?;
|
||||||
|
/// let is_dir = true;
|
||||||
|
/// let (match_type, child_pattern) = MatchPattern::match_filename_exclude(
|
||||||
|
/// &filename,
|
||||||
|
/// is_dir,
|
||||||
|
/// &patterns
|
||||||
|
/// )?;
|
||||||
|
/// assert_eq!(match_type, MatchType::Positive);
|
||||||
|
/// /// child pattern will be empty
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn match_filename_exclude(
|
||||||
|
filename: &CStr,
|
||||||
|
is_dir: bool,
|
||||||
|
match_pattern: &[MatchPattern],
|
||||||
|
) -> Result<(MatchType, Vec<MatchPattern>), Error> {
|
||||||
|
let mut child_pattern = Vec::new();
|
||||||
|
let mut match_state = MatchType::None;
|
||||||
|
|
||||||
|
for pattern in match_pattern {
|
||||||
|
match pattern.matches_filename(filename, is_dir)? {
|
||||||
|
MatchType::None => {}
|
||||||
|
MatchType::Positive => match_state = MatchType::Positive,
|
||||||
|
MatchType::Negative => match_state = MatchType::Negative,
|
||||||
|
match_type => {
|
||||||
|
if match_state != MatchType::Positive && match_state != MatchType::Negative {
|
||||||
|
match_state = match_type;
|
||||||
|
}
|
||||||
|
child_pattern.push(pattern.get_rest_pattern());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((match_state, child_pattern))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splits the `CStr` slice at the first slash encountered and returns the
|
// Splits the `CStr` slice at the first slash encountered and returns the
|
||||||
|
|
|
@ -819,7 +819,11 @@ impl<R: Read> SequentialDecoder<R> {
|
||||||
// there are no match pattern.
|
// there are no match pattern.
|
||||||
let mut matched = parent_matched;
|
let mut matched = parent_matched;
|
||||||
if !match_pattern.is_empty() {
|
if !match_pattern.is_empty() {
|
||||||
match match_filename(filename, ifmt == libc::S_IFDIR, match_pattern)? {
|
match MatchPattern::match_filename_include(
|
||||||
|
&CString::new(filename.as_bytes())?,
|
||||||
|
ifmt == libc::S_IFDIR,
|
||||||
|
match_pattern,
|
||||||
|
)? {
|
||||||
(MatchType::None, _) => matched = MatchType::None,
|
(MatchType::None, _) => matched = MatchType::None,
|
||||||
(MatchType::Negative, _) => matched = MatchType::Negative,
|
(MatchType::Negative, _) => matched = MatchType::Negative,
|
||||||
(match_type, pattern) => {
|
(match_type, pattern) => {
|
||||||
|
@ -1111,43 +1115,6 @@ impl<R: Read> SequentialDecoder<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_filename(
|
|
||||||
filename: &OsStr,
|
|
||||||
is_dir: bool,
|
|
||||||
match_pattern: &[MatchPattern],
|
|
||||||
) -> Result<(MatchType, Vec<MatchPattern>), Error> {
|
|
||||||
let mut child_pattern = Vec::new();
|
|
||||||
let mut match_state = MatchType::None;
|
|
||||||
// read_filename() checks for nul bytes, so it is save to unwrap here
|
|
||||||
let name = CString::new(filename.as_bytes()).unwrap();
|
|
||||||
|
|
||||||
for pattern in match_pattern {
|
|
||||||
match pattern.matches_filename(&name, is_dir)? {
|
|
||||||
MatchType::None => {}
|
|
||||||
MatchType::Positive => {
|
|
||||||
match_state = MatchType::Positive;
|
|
||||||
let incl_pattern = MatchPattern::from_line(b"**/*").unwrap().unwrap();
|
|
||||||
child_pattern.push(incl_pattern.get_rest_pattern());
|
|
||||||
}
|
|
||||||
MatchType::Negative => match_state = MatchType::Negative,
|
|
||||||
MatchType::PartialPositive => {
|
|
||||||
if match_state != MatchType::Negative && match_state != MatchType::Positive {
|
|
||||||
match_state = MatchType::PartialPositive;
|
|
||||||
}
|
|
||||||
child_pattern.push(pattern.get_rest_pattern());
|
|
||||||
}
|
|
||||||
MatchType::PartialNegative => {
|
|
||||||
if match_state == MatchType::PartialPositive {
|
|
||||||
match_state = MatchType::PartialNegative;
|
|
||||||
}
|
|
||||||
child_pattern.push(pattern.get_rest_pattern());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((match_state, child_pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn file_openat(
|
fn file_openat(
|
||||||
parent: RawFd,
|
parent: RawFd,
|
||||||
filename: &OsStr,
|
filename: &OsStr,
|
||||||
|
|
Loading…
Reference in New Issue