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),
|
||||
};
|
||||
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue