1- mod from_tree {
1+ #[ allow( clippy:: empty_docs) ]
2+ ///
3+ pub mod from_tree {
24 use std:: collections:: VecDeque ;
35
46 use bstr:: { BStr , BString , ByteSlice , ByteVec } ;
@@ -10,6 +12,19 @@ mod from_tree {
1012 Entry , PathStorage , State , Version ,
1113 } ;
1214
15+ /// The error returned by [State::from_tree()].
16+ #[ derive( Debug , thiserror:: Error ) ]
17+ #[ allow( missing_docs) ]
18+ pub enum Error {
19+ #[ error( "The path \" {path}\" is invalid" ) ]
20+ InvalidComponent {
21+ path : BString ,
22+ source : gix_validate:: path:: component:: Error ,
23+ } ,
24+ #[ error( transparent) ]
25+ Traversal ( #[ from] gix_traverse:: tree:: breadthfirst:: Error ) ,
26+ }
27+
1328 /// Initialization
1429 impl State {
1530 /// Return a new and empty in-memory index assuming the given `object_hash`.
@@ -32,23 +47,42 @@ mod from_tree {
3247 }
3348 /// Create an index [`State`] by traversing `tree` recursively, accessing sub-trees
3449 /// with `objects`.
50+ /// `validate` is used to determine which validations to perform on every path component we see.
3551 ///
3652 /// **No extension data is currently produced**.
37- pub fn from_tree < Find > ( tree : & gix_hash:: oid , objects : Find ) -> Result < Self , breadthfirst:: Error >
53+ pub fn from_tree < Find > (
54+ tree : & gix_hash:: oid ,
55+ objects : Find ,
56+ validate : gix_validate:: path:: component:: Options ,
57+ ) -> Result < Self , Error >
3858 where
3959 Find : gix_object:: Find ,
4060 {
4161 let _span = gix_features:: trace:: coarse!( "gix_index::State::from_tree()" ) ;
4262 let mut buf = Vec :: new ( ) ;
43- let root = objects. find_tree_iter ( tree, & mut buf) ?;
44- let mut delegate = CollectEntries :: new ( ) ;
45- breadthfirst ( root, breadthfirst:: State :: default ( ) , & objects, & mut delegate) ?;
63+ let root = objects
64+ . find_tree_iter ( tree, & mut buf)
65+ . map_err ( breadthfirst:: Error :: from) ?;
66+ let mut delegate = CollectEntries :: new ( validate) ;
67+ match breadthfirst ( root, breadthfirst:: State :: default ( ) , & objects, & mut delegate) {
68+ Ok ( ( ) ) => { }
69+ Err ( gix_traverse:: tree:: breadthfirst:: Error :: Cancelled ) => {
70+ let ( path, err) = delegate
71+ . invalid_path
72+ . take ( )
73+ . expect ( "cancellation only happens on validation error" ) ;
74+ return Err ( Error :: InvalidComponent { path, source : err } ) ;
75+ }
76+ Err ( err) => return Err ( err. into ( ) ) ,
77+ }
4678
4779 let CollectEntries {
4880 mut entries,
4981 path_backing,
5082 path : _,
5183 path_deque : _,
84+ validate : _,
85+ invalid_path : _,
5286 } = delegate;
5387
5488 entries. sort_by ( |a, b| Entry :: cmp_filepaths ( a. path_in ( & path_backing) , b. path_in ( & path_backing) ) ) ;
@@ -76,15 +110,19 @@ mod from_tree {
76110 path_backing : PathStorage ,
77111 path : BString ,
78112 path_deque : VecDeque < BString > ,
113+ validate : gix_validate:: path:: component:: Options ,
114+ invalid_path : Option < ( BString , gix_validate:: path:: component:: Error ) > ,
79115 }
80116
81117 impl CollectEntries {
82- pub fn new ( ) -> CollectEntries {
118+ pub fn new ( validate : gix_validate :: path :: component :: Options ) -> CollectEntries {
83119 CollectEntries {
84120 entries : Vec :: new ( ) ,
85121 path_backing : Vec :: new ( ) ,
86122 path : BString :: default ( ) ,
87123 path_deque : VecDeque :: new ( ) ,
124+ validate,
125+ invalid_path : None ,
88126 }
89127 }
90128
@@ -93,6 +131,11 @@ mod from_tree {
93131 self . path . push ( b'/' ) ;
94132 }
95133 self . path . push_str ( name) ;
134+ if self . invalid_path . is_none ( ) {
135+ if let Err ( err) = gix_validate:: path:: component ( name, None , self . validate ) {
136+ self . invalid_path = Some ( ( self . path . clone ( ) , err) )
137+ }
138+ }
96139 }
97140
98141 pub fn add_entry ( & mut self , entry : & tree:: EntryRef < ' _ > ) {
@@ -103,6 +146,18 @@ mod from_tree {
103146 EntryKind :: Link => Mode :: SYMLINK ,
104147 EntryKind :: Commit => Mode :: COMMIT ,
105148 } ;
149+ // There are leaf-names that require special validation, specific to their mode.
150+ // Double-validate just for this case, as the previous validation didn't know the mode yet.
151+ if self . invalid_path . is_none ( ) {
152+ let start = self . path . rfind_byte ( b'/' ) . map ( |pos| pos + 1 ) . unwrap_or_default ( ) ;
153+ if let Err ( err) = gix_validate:: path:: component (
154+ self . path [ start..] . as_ref ( ) ,
155+ ( entry. mode . kind ( ) == EntryKind :: Link ) . then_some ( gix_validate:: path:: component:: Mode :: Symlink ) ,
156+ self . validate ,
157+ ) {
158+ self . invalid_path = Some ( ( self . path . clone ( ) , err) ) ;
159+ }
160+ }
106161
107162 let path_start = self . path_backing . len ( ) ;
108163 self . path_backing . extend_from_slice ( & self . path ) ;
@@ -117,6 +172,14 @@ mod from_tree {
117172
118173 self . entries . push ( new_entry) ;
119174 }
175+
176+ fn determine_action ( & self ) -> Action {
177+ if self . invalid_path . is_none ( ) {
178+ Action :: Continue
179+ } else {
180+ Action :: Cancel
181+ }
182+ }
120183 }
121184
122185 impl Visit for CollectEntries {
@@ -127,12 +190,12 @@ mod from_tree {
127190 . expect ( "every call is matched with push_tracked_path_component" ) ;
128191 }
129192
130- fn push_back_tracked_path_component ( & mut self , component : & bstr :: BStr ) {
193+ fn push_back_tracked_path_component ( & mut self , component : & BStr ) {
131194 self . push_element ( component) ;
132195 self . path_deque . push_back ( self . path . clone ( ) ) ;
133196 }
134197
135- fn push_path_component ( & mut self , component : & bstr :: BStr ) {
198+ fn push_path_component ( & mut self , component : & BStr ) {
136199 self . push_element ( component) ;
137200 }
138201
@@ -144,13 +207,13 @@ mod from_tree {
144207 }
145208 }
146209
147- fn visit_tree ( & mut self , _entry : & gix_object:: tree:: EntryRef < ' _ > ) -> gix_traverse :: tree :: visit :: Action {
148- Action :: Continue
210+ fn visit_tree ( & mut self , _entry : & gix_object:: tree:: EntryRef < ' _ > ) -> Action {
211+ self . determine_action ( )
149212 }
150213
151- fn visit_nontree ( & mut self , entry : & gix_object:: tree:: EntryRef < ' _ > ) -> gix_traverse :: tree :: visit :: Action {
214+ fn visit_nontree ( & mut self , entry : & gix_object:: tree:: EntryRef < ' _ > ) -> Action {
152215 self . add_entry ( entry) ;
153- Action :: Continue
216+ self . determine_action ( )
154217 }
155218 }
156219}
0 commit comments