@@ -23,6 +23,20 @@ mod error {
23
23
LoadIndex ( #[ from] crate :: store:: load_index:: Error ) ,
24
24
#[ error( transparent) ]
25
25
LoadPack ( #[ from] std:: io:: Error ) ,
26
+ #[ error( "Object {} referred to its base object {} by its id but it's not within a multi-index" , . id, . base_id) ]
27
+ ThinPackAtRest {
28
+ /// the id of the base object which lived outside of the multi-index
29
+ base_id : git_hash:: ObjectId ,
30
+ /// The original object to lookup
31
+ id : git_hash:: ObjectId ,
32
+ } ,
33
+ #[ error( "Reached recursion limit of {} while resolving ref delta bases for {}" , . max_depth, . id) ]
34
+ DeltaBaseRecursionLimit {
35
+ /// the maximum recursion depth we encountered.
36
+ max_depth : usize ,
37
+ /// The original object to lookup
38
+ id : git_hash:: ObjectId ,
39
+ } ,
26
40
#[ error( "The base object {} could not be found but is required to decode {}" , . base_id, . id) ]
27
41
DeltaBaseMissing {
28
42
/// the id of the base object which failed to lookup
@@ -41,6 +55,25 @@ mod error {
41
55
} ,
42
56
}
43
57
58
+ #[ derive( Copy , Clone ) ]
59
+ pub ( crate ) struct DeltaBaseRecursion < ' a > {
60
+ pub depth : usize ,
61
+ pub original_id : & ' a git_hash:: oid ,
62
+ }
63
+
64
+ impl < ' a > DeltaBaseRecursion < ' a > {
65
+ pub fn new ( id : & ' a git_hash:: oid ) -> Self {
66
+ Self {
67
+ original_id : id,
68
+ depth : 0 ,
69
+ }
70
+ }
71
+ pub fn inc_depth ( mut self ) -> Self {
72
+ self . depth += 1 ;
73
+ self
74
+ }
75
+ }
76
+
44
77
#[ cfg( test) ]
45
78
mod tests {
46
79
use super :: * ;
@@ -135,11 +168,20 @@ where
135
168
136
169
fn try_find_cached_inner < ' a > (
137
170
& self ,
138
- id : & oid ,
171
+ id : & git_hash :: oid ,
139
172
buffer : & ' a mut Vec < u8 > ,
140
173
pack_cache : & mut impl DecodeEntry ,
141
174
snapshot : & mut load_index:: Snapshot ,
175
+ recursion : Option < error:: DeltaBaseRecursion < ' _ > > ,
142
176
) -> Result < Option < ( Data < ' a > , Option < Location > ) > , Error > {
177
+ if let Some ( r) = recursion {
178
+ if r. depth >= self . max_recursion_depth {
179
+ return Err ( Error :: DeltaBaseRecursionLimit {
180
+ max_depth : self . max_recursion_depth ,
181
+ id : r. original_id . to_owned ( ) ,
182
+ } ) ;
183
+ }
184
+ }
143
185
' outer: loop {
144
186
{
145
187
let marker = snapshot. marker ;
@@ -197,15 +239,33 @@ where
197
239
entry_size : r. compressed_size + header_size,
198
240
} ) ,
199
241
) ) ,
200
- Err ( git_pack:: data:: decode_entry:: Error :: DeltaBaseUnresolved ( base_id) ) => {
242
+ Err ( git_pack:: data:: decode_entry:: Error :: DeltaBaseUnresolved ( base_id) )
243
+ if index. is_multi_pack ( ) =>
244
+ {
245
+ // Only with multi-pack indices it's allowed to jump to refer to other packs within this
246
+ // multi-pack. Otherwise this would consistute a thin pack which is only allowed in transit.
247
+ let _base_must_exist_in_multi_index =
248
+ index. lookup ( & base_id) . ok_or_else ( || Error :: ThinPackAtRest {
249
+ base_id,
250
+ id : id. to_owned ( ) ,
251
+ } ) ?;
252
+
201
253
// special case, and we just allocate here to make it work. It's an actual delta-ref object
202
254
// which is sent by some servers that points to an object outside of the pack we are looking
203
255
// at right now. With the complexities of loading packs, we go into recursion here. Git itself
204
256
// doesn't do a cycle check, and we won't either but limit the recursive depth.
205
257
// The whole ordeal isn't efficient due to memory allocation and later mem-copying when trying again.
206
258
let mut buf = Vec :: new ( ) ;
207
259
let obj_kind = self
208
- . try_find_cached_inner ( & base_id, & mut buf, pack_cache, snapshot)
260
+ . try_find_cached_inner (
261
+ & base_id,
262
+ & mut buf,
263
+ pack_cache,
264
+ snapshot,
265
+ recursion
266
+ . map ( |r| r. inc_depth ( ) )
267
+ . or_else ( || error:: DeltaBaseRecursion :: new ( id) . into ( ) ) ,
268
+ )
209
269
. map_err ( |err| Error :: DeltaBaseLookup {
210
270
err : Box :: new ( err) ,
211
271
base_id,
@@ -343,7 +403,7 @@ where
343
403
) -> Result < Option < ( Data < ' a > , Option < Location > ) > , Self :: Error > {
344
404
let id = id. as_ref ( ) ;
345
405
let mut snapshot = self . snapshot . borrow_mut ( ) ;
346
- self . try_find_cached_inner ( id, buffer, pack_cache, & mut snapshot)
406
+ self . try_find_cached_inner ( id, buffer, pack_cache, & mut snapshot, None )
347
407
}
348
408
349
409
fn location_by_oid ( & self , id : impl AsRef < oid > , buf : & mut Vec < u8 > ) -> Option < Location > {
0 commit comments