##// END OF EJS Templates
rhg: fix a crash on non-generaldelta revlogs...
Arseniy Alekseyev -
r49289:96ea4db4 default
parent child Browse files
Show More
@@ -84,6 +84,7 b' pub struct Index {'
84 84 /// Offsets of starts of index blocks.
85 85 /// Only needed when the index is interleaved with data.
86 86 offsets: Option<Vec<usize>>,
87 uses_generaldelta: bool,
87 88 }
88 89
89 90 impl Index {
@@ -100,6 +101,11 b' impl Index {'
100 101 return Err(HgError::corrupted("unsupported revlog version"));
101 102 }
102 103
104 // This is only correct because we know version is REVLOGV1.
105 // In v2 we always use generaldelta, while in v0 we never use
106 // generaldelta. Similar for [is_inline] (it's only used in v1).
107 let uses_generaldelta = header.format_flags().uses_generaldelta();
108
103 109 if header.format_flags().is_inline() {
104 110 let mut offset: usize = 0;
105 111 let mut offsets = Vec::new();
@@ -119,6 +125,7 b' impl Index {'
119 125 Ok(Self {
120 126 bytes,
121 127 offsets: Some(offsets),
128 uses_generaldelta,
122 129 })
123 130 } else {
124 131 Err(HgError::corrupted("unexpected inline revlog length")
@@ -128,10 +135,15 b' impl Index {'
128 135 Ok(Self {
129 136 bytes,
130 137 offsets: None,
138 uses_generaldelta,
131 139 })
132 140 }
133 141 }
134 142
143 pub fn uses_generaldelta(&self) -> bool {
144 self.uses_generaldelta
145 }
146
135 147 /// Value of the inline flag.
136 148 pub fn is_inline(&self) -> bool {
137 149 self.offsets.is_some()
@@ -259,7 +271,7 b" impl<'a> IndexEntry<'a> {"
259 271 }
260 272
261 273 /// Return the revision upon which the data has been derived.
262 pub fn base_revision(&self) -> Revision {
274 pub fn base_revision_or_base_of_delta_chain(&self) -> Revision {
263 275 // TODO Maybe return an Option when base_revision == rev?
264 276 // Requires to add rev to IndexEntry
265 277
@@ -297,7 +309,7 b' mod tests {'
297 309 offset: usize,
298 310 compressed_len: usize,
299 311 uncompressed_len: usize,
300 base_revision: Revision,
312 base_revision_or_base_of_delta_chain: Revision,
301 313 }
302 314
303 315 #[cfg(test)]
@@ -311,7 +323,7 b' mod tests {'
311 323 offset: 0,
312 324 compressed_len: 0,
313 325 uncompressed_len: 0,
314 base_revision: 0,
326 base_revision_or_base_of_delta_chain: 0,
315 327 }
316 328 }
317 329
@@ -350,8 +362,11 b' mod tests {'
350 362 self
351 363 }
352 364
353 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
354 self.base_revision = value;
365 pub fn with_base_revision_or_base_of_delta_chain(
366 &mut self,
367 value: Revision,
368 ) -> &mut Self {
369 self.base_revision_or_base_of_delta_chain = value;
355 370 self
356 371 }
357 372
@@ -374,7 +389,9 b' mod tests {'
374 389 bytes.extend(&[0u8; 2]); // Revision flags.
375 390 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
376 391 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
377 bytes.extend(&self.base_revision.to_be_bytes());
392 bytes.extend(
393 &self.base_revision_or_base_of_delta_chain.to_be_bytes(),
394 );
378 395 bytes
379 396 }
380 397 }
@@ -480,14 +497,16 b' mod tests {'
480 497 }
481 498
482 499 #[test]
483 fn test_base_revision() {
484 let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
500 fn test_base_revision_or_base_of_delta_chain() {
501 let bytes = IndexEntryBuilder::new()
502 .with_base_revision_or_base_of_delta_chain(1)
503 .build();
485 504 let entry = IndexEntry {
486 505 bytes: &bytes,
487 506 offset_override: None,
488 507 };
489 508
490 assert_eq!(entry.base_revision(), 1)
509 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1)
491 510 }
492 511
493 512 #[test]
@@ -191,11 +191,20 b' impl Revlog {'
191 191 // Todo return -> Cow
192 192 let mut entry = self.get_entry(rev)?;
193 193 let mut delta_chain = vec![];
194 while let Some(base_rev) = entry.base_rev {
194
195 // The meaning of `base_rev_or_base_of_delta_chain` depends on
196 // generaldelta. See the doc on `ENTRY_DELTA_BASE` in
197 // `mercurial/revlogutils/constants.py` and the code in
198 // [_chaininfo] and in [index_deltachain].
199 let uses_generaldelta = self.index.uses_generaldelta();
200 while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
201 let base_rev = if uses_generaldelta {
202 base_rev
203 } else {
204 entry.rev - 1
205 };
195 206 delta_chain.push(entry);
196 entry = self
197 .get_entry(base_rev)
198 .map_err(|_| RevlogError::corrupted())?;
207 entry = self.get_entry_internal(base_rev)?;
199 208 }
200 209
201 210 // TODO do not look twice in the index
@@ -291,14 +300,26 b' impl Revlog {'
291 300 bytes: data,
292 301 compressed_len: index_entry.compressed_len(),
293 302 uncompressed_len: index_entry.uncompressed_len(),
294 base_rev: if index_entry.base_revision() == rev {
303 base_rev_or_base_of_delta_chain: if index_entry
304 .base_revision_or_base_of_delta_chain()
305 == rev
306 {
295 307 None
296 308 } else {
297 Some(index_entry.base_revision())
309 Some(index_entry.base_revision_or_base_of_delta_chain())
298 310 },
299 311 };
300 312 Ok(entry)
301 313 }
314
315 /// when resolving internal references within revlog, any errors
316 /// should be reported as corruption, instead of e.g. "invalid revision"
317 fn get_entry_internal(
318 &self,
319 rev: Revision,
320 ) -> Result<RevlogEntry, RevlogError> {
321 return self.get_entry(rev).map_err(|_| RevlogError::corrupted());
322 }
302 323 }
303 324
304 325 /// The revlog entry's bytes and the necessary informations to extract
@@ -309,7 +330,7 b" pub struct RevlogEntry<'a> {"
309 330 bytes: &'a [u8],
310 331 compressed_len: usize,
311 332 uncompressed_len: usize,
312 base_rev: Option<Revision>,
333 base_rev_or_base_of_delta_chain: Option<Revision>,
313 334 }
314 335
315 336 impl<'a> RevlogEntry<'a> {
@@ -375,7 +396,7 b" impl<'a> RevlogEntry<'a> {"
375 396 /// Tell if the entry is a snapshot or a delta
376 397 /// (influences on decompression).
377 398 fn is_delta(&self) -> bool {
378 self.base_rev.is_some()
399 self.base_rev_or_base_of_delta_chain.is_some()
379 400 }
380 401 }
381 402
@@ -1,4 +1,3 b''
1
2 1 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
3 2
4 3 $ cat << EOF >> $HGRCPATH
@@ -21,9 +20,27 b''
21 20 1 0 prev
22 21 2 1 prev
23 22
24 rhg breaks on non-generaldelta revlogs:
23 rhg works on non-generaldelta revlogs:
25 24
26 $ $NO_FALLBACK hg cat f -r . | f --sha256 --size
27 abort: corrupted revlog (rhg !)
28 size=0, sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 (rhg !)
29 size=58, sha256=0cf0386dd4813cc3b957ea790146627dfc0ec42ad3fcf47221b9842e4d5764c1 (no-rhg !)
25 $ $NO_FALLBACK hg cat f -r .
26 1
27 2
28 3
29 4
30 5
31 6
32 7
33 8
34 9
35 10
36 11
37 12
38 13
39 14
40 15
41 16
42 17
43 18
44 19
45 20
46 footer
General Comments 0
You need to be logged in to leave comments. Login now