all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH pathpatterns] match_list: added `matches_path()` function, which matches only the path
@ 2023-08-09 10:19 Gabriel Goller
  2023-08-09 10:19 ` [pbs-devel] [PATCH proxmox-backup v3 1/1] fix #4380: check if file is excluded before running `stat()` Gabriel Goller
  2023-08-11  8:26 ` [pbs-devel] [PATCH pathpatterns] match_list: added `matches_path()` function, which matches only the path Wolfgang Bumiller
  0 siblings, 2 replies; 8+ messages in thread
From: Gabriel Goller @ 2023-08-09 10:19 UTC (permalink / raw)
  To: pbs-devel

Added `matches_path()` function, which only matches against the path and returns
an error if a file_mode pattern is found/needed in the matching list. This is
useful when we want to check if a file is excluded before running `stat()` on
the file to get the file_mode (which could fail).

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
 src/match_list.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 158 insertions(+), 1 deletion(-)

diff --git a/src/match_list.rs b/src/match_list.rs
index c5b14e0..acad328 100644
--- a/src/match_list.rs
+++ b/src/match_list.rs
@@ -1,6 +1,6 @@
 //! Helpers for include/exclude lists.
-
 use bitflags::bitflags;
+use std::fmt;
 
 use crate::PatternFlag;
 
@@ -39,6 +39,17 @@ impl Default for MatchFlag {
     }
 }
 
+#[derive(Debug, PartialEq)]
+pub struct FileModeRequiredForMatching;
+
+impl fmt::Display for FileModeRequiredForMatching {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "File mode is required for matching")
+    }
+}
+
+impl std::error::Error for FileModeRequiredForMatching {}
+
 /// A pattern entry. (Glob patterns or literal patterns.)
 // Note:
 // For regex we'd likely use the POSIX extended REs via `regexec(3)`, since we're targetting
@@ -304,12 +315,32 @@ impl MatchEntry {
 
         self.matches_path_exact(path)
     }
+
+    /// Check whether the path contains a matching suffix. Returns an error if a file mode is required.
+    pub fn matches_path<T: AsRef<[u8]>>(
+        &self,
+        path: T,
+    ) -> Result<bool, FileModeRequiredForMatching> {
+        self.matches_path_do(path.as_ref())
+    }
+
+    fn matches_path_do(&self, path: &[u8]) -> Result<bool, FileModeRequiredForMatching> {
+        if !self.flags.contains(MatchFlag::ANY_FILE_TYPE) {
+            return Err(FileModeRequiredForMatching);
+        }
+
+        Ok(self.matches_path_suffix_do(path))
+    }
 }
 
 #[doc(hidden)]
 pub trait MatchListEntry {
     fn entry_matches(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
     fn entry_matches_exact(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
+    fn entry_matches_path(
+        &self,
+        path: &[u8],
+    ) -> Result<Option<MatchType>, FileModeRequiredForMatching>;
 }
 
 impl MatchListEntry for &'_ MatchEntry {
@@ -328,6 +359,21 @@ impl MatchListEntry for &'_ MatchEntry {
             None
         }
     }
+
+    fn entry_matches_path(
+        &self,
+        path: &[u8],
+    ) -> Result<Option<MatchType>, FileModeRequiredForMatching> {
+        if let Ok(b) = self.matches_path(path) {
+            if b {
+                Ok(Some(self.match_type()))
+            } else {
+                Ok(None)
+            }
+        } else {
+            Err(FileModeRequiredForMatching)
+        }
+    }
 }
 
 impl MatchListEntry for &'_ &'_ MatchEntry {
@@ -346,6 +392,21 @@ impl MatchListEntry for &'_ &'_ MatchEntry {
             None
         }
     }
+
+    fn entry_matches_path(
+        &self,
+        path: &[u8],
+    ) -> Result<Option<MatchType>, FileModeRequiredForMatching> {
+        if let Ok(b) = self.matches_path(path) {
+            if b {
+                Ok(Some(self.match_type()))
+            } else {
+                Ok(None)
+            }
+        } else {
+            Err(FileModeRequiredForMatching)
+        }
+    }
 }
 
 /// This provides [`matches`](MatchList::matches) and [`matches_exact`](MatchList::matches_exact)
@@ -374,6 +435,20 @@ pub trait MatchList {
     }
 
     fn matches_exact_do(&self, path: &[u8], file_mode: Option<u32>) -> Option<MatchType>;
+
+    /// Check whether this list contains anything exactly matching the path, returns error if
+    /// `file_mode` is required for exact matching.
+    fn matches_path<T: AsRef<[u8]>>(
+        &self,
+        path: T,
+    ) -> Result<Option<MatchType>, FileModeRequiredForMatching> {
+        self.matches_path_do(path.as_ref())
+    }
+
+    fn matches_path_do(
+        &self,
+        path: &[u8],
+    ) -> Result<Option<MatchType>, FileModeRequiredForMatching>;
 }
 
 impl<'a, T> MatchList for T
@@ -408,6 +483,24 @@ where
 
         None
     }
+
+    fn matches_path_do(
+        &self,
+        path: &[u8],
+    ) -> Result<Option<MatchType>, FileModeRequiredForMatching> {
+        // This is an &self method on a `T where T: 'a`.
+        let this: &'a Self = unsafe { std::mem::transmute(self) };
+
+        for m in this.into_iter().rev() {
+            if let Ok(mt) = m.entry_matches_path(path) {
+                if mt.is_some() {
+                    return Ok(mt);
+                }
+            }
+        }
+
+        Err(FileModeRequiredForMatching)
+    }
 }
 
 #[test]
@@ -530,3 +623,67 @@ fn test_path_relativity() {
     assert_eq!(matchlist.matches("foo/slash", None), None);
     assert_eq!(matchlist.matches("foo/slash-a", None), None);
 }
+
+#[test]
+fn test_matches_path() {
+    let vec = vec![
+        MatchEntry::include(crate::Pattern::path("as*").unwrap()),
+        MatchEntry::include(crate::Pattern::path("a*").unwrap()),
+    ];
+    assert_eq!(vec.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let list: &[MatchEntry] = &vec[..];
+    assert_eq!(list.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let list: Vec<&MatchEntry> = vec.iter().collect();
+    assert_eq!(list.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let list: &[&MatchEntry] = &list[..];
+    assert_eq!(list.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let vec = vec![
+        MatchEntry::include(crate::Pattern::path("a*").unwrap()),
+        MatchEntry::include(crate::Pattern::path("a*").unwrap())
+            .flags(MatchFlag::MATCH_REGULAR_FILES),
+    ];
+    assert_eq!(vec.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let vec = vec![
+        MatchEntry::include(crate::Pattern::path("a*").unwrap()),
+        MatchEntry::include(crate::Pattern::path("asdf").unwrap()),
+    ];
+    assert_eq!(vec.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let list: &[MatchEntry] = &vec[..];
+    assert_eq!(list.matches_path("azdf"), Ok(Some(MatchType::Include)));
+
+    let vec = vec![
+        MatchEntry::include(crate::Pattern::path("a*").unwrap()).flags(MatchFlag::ANY_FILE_TYPE),
+        MatchEntry::include(crate::Pattern::path("asdf").unwrap()).flags(MatchFlag::ANY_FILE_TYPE),
+    ];
+    assert_eq!(vec.matches_path("asdf"), Ok(Some(MatchType::Include)));
+    assert_eq!(vec.matches_path("adbb"), Ok(Some(MatchType::Include)));
+}
+
+#[test]
+fn test_matches_path_error() {
+    let vec = vec![
+        MatchEntry::include(crate::Pattern::path("a*").unwrap())
+            .flags(MatchFlag::MATCH_REGULAR_FILES),
+        MatchEntry::include(crate::Pattern::path("asdf").unwrap()),
+    ];
+    assert_eq!(vec.matches_path("asdf"), Ok(Some(MatchType::Include)));
+
+    let list: &[MatchEntry] = &vec[..];
+    assert_eq!(list.matches_path("azdf"), Err(FileModeRequiredForMatching));
+
+    let list: Vec<&MatchEntry> = vec.iter().collect();
+    assert_eq!(list.matches_path("abdf"), Err(FileModeRequiredForMatching));
+
+    let list: &[&MatchEntry] = &list[..];
+    assert_eq!(list.matches_path("acdf"), Err(FileModeRequiredForMatching));
+
+    let vec = vec![MatchEntry::include(crate::Pattern::path("a*").unwrap())
+        .flags(MatchFlag::MATCH_DIRECTORIES)];
+    assert_eq!(vec.matches_path("asdf"), Err(FileModeRequiredForMatching));
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-08-14  9:33 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-09 10:19 [pbs-devel] [PATCH pathpatterns] match_list: added `matches_path()` function, which matches only the path Gabriel Goller
2023-08-09 10:19 ` [pbs-devel] [PATCH proxmox-backup v3 1/1] fix #4380: check if file is excluded before running `stat()` Gabriel Goller
2023-08-11  8:51   ` Wolfgang Bumiller
2023-08-14  7:41     ` Gabriel Goller
2023-08-11  8:26 ` [pbs-devel] [PATCH pathpatterns] match_list: added `matches_path()` function, which matches only the path Wolfgang Bumiller
2023-08-11  8:32   ` Wolfgang Bumiller
2023-08-11  8:38     ` Wolfgang Bumiller
2023-08-14  9:32   ` Gabriel Goller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal