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:
Christian Ebner 2019-12-10 13:01:39 +01:00 committed by Dietmar Maurer
parent f084505ec5
commit d3dbe52f37
3 changed files with 146 additions and 65 deletions

View File

@ -736,7 +736,11 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
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, _) => {
let filename_osstr = std::ffi::OsStr::from_bytes(filename.to_bytes());
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 {
match errno {
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,

View File

@ -290,6 +290,142 @@ impl MatchPattern {
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

View File

@ -819,7 +819,11 @@ impl<R: Read> SequentialDecoder<R> {
// there are no match pattern.
let mut matched = parent_matched;
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::Negative, _) => matched = MatchType::Negative,
(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(
parent: RawFd,
filename: &OsStr,