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:
		
				
					committed by
					
						
						Dietmar Maurer
					
				
			
			
				
	
			
			
			
						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,
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user