Show More
@@ -1,6 +1,7 | |||
|
1 | 1 | use crate::errors::HgError; |
|
2 | 2 | use crate::repo::Repo; |
|
3 | 3 | use crate::revlog::path_encode::path_encode; |
|
4 | use crate::revlog::revlog::RevlogEntry; | |
|
4 | 5 | use crate::revlog::revlog::{Revlog, RevlogError}; |
|
5 | 6 | use crate::revlog::NodePrefix; |
|
6 | 7 | use crate::revlog::Revision; |
@@ -23,7 +24,7 impl Filelog { | |||
|
23 | 24 | Ok(Self { revlog }) |
|
24 | 25 | } |
|
25 | 26 | |
|
26 |
/// The given node ID is that of the file as found in a |
|
|
27 | /// The given node ID is that of the file as found in a filelog, not of a | |
|
27 | 28 | /// changeset. |
|
28 | 29 | pub fn data_for_node( |
|
29 | 30 | &self, |
@@ -33,7 +34,7 impl Filelog { | |||
|
33 | 34 | self.data_for_rev(file_rev) |
|
34 | 35 | } |
|
35 | 36 | |
|
36 |
/// The given revision is that of the file as found in a |
|
|
37 | /// The given revision is that of the file as found in a filelog, not of a | |
|
37 | 38 | /// changeset. |
|
38 | 39 | pub fn data_for_rev( |
|
39 | 40 | &self, |
@@ -42,6 +43,25 impl Filelog { | |||
|
42 | 43 | let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?.into_owned(); |
|
43 | 44 | Ok(FilelogRevisionData(data.into())) |
|
44 | 45 | } |
|
46 | ||
|
47 | /// The given node ID is that of the file as found in a filelog, not of a | |
|
48 | /// changeset. | |
|
49 | pub fn entry_for_node( | |
|
50 | &self, | |
|
51 | file_node: impl Into<NodePrefix>, | |
|
52 | ) -> Result<FilelogEntry, RevlogError> { | |
|
53 | let file_rev = self.revlog.rev_from_node(file_node.into())?; | |
|
54 | self.entry_for_rev(file_rev) | |
|
55 | } | |
|
56 | ||
|
57 | /// The given revision is that of the file as found in a filelog, not of a | |
|
58 | /// changeset. | |
|
59 | pub fn entry_for_rev( | |
|
60 | &self, | |
|
61 | file_rev: Revision, | |
|
62 | ) -> Result<FilelogEntry, RevlogError> { | |
|
63 | Ok(FilelogEntry(self.revlog.get_entry(file_rev)?)) | |
|
64 | } | |
|
45 | 65 | } |
|
46 | 66 | |
|
47 | 67 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
@@ -50,6 +70,14 fn store_path(hg_path: &HgPath, suffix: | |||
|
50 | 70 | get_path_from_bytes(&encoded_bytes).into() |
|
51 | 71 | } |
|
52 | 72 | |
|
73 | pub struct FilelogEntry<'a>(RevlogEntry<'a>); | |
|
74 | ||
|
75 | impl FilelogEntry<'_> { | |
|
76 | pub fn data(&self) -> Result<FilelogRevisionData, HgError> { | |
|
77 | Ok(FilelogRevisionData(self.0.data()?.into_owned())) | |
|
78 | } | |
|
79 | } | |
|
80 | ||
|
53 | 81 | /// The data for one revision in a filelog, uncompressed and delta-resolved. |
|
54 | 82 | pub struct FilelogRevisionData(Vec<u8>); |
|
55 | 83 |
@@ -39,9 +39,13 impl From<NodeMapError> for RevlogError | |||
|
39 | 39 | } |
|
40 | 40 | } |
|
41 | 41 | |
|
42 | fn corrupted() -> HgError { | |
|
43 | HgError::corrupted("corrupted revlog") | |
|
44 | } | |
|
45 | ||
|
42 | 46 | impl RevlogError { |
|
43 | 47 | fn corrupted() -> Self { |
|
44 |
RevlogError::Other( |
|
|
48 | RevlogError::Other(corrupted()) | |
|
45 | 49 | } |
|
46 | 50 | } |
|
47 | 51 | |
@@ -191,7 +195,7 impl Revlog { | |||
|
191 | 195 | if rev == NULL_REVISION { |
|
192 | 196 | return Ok(Cow::Borrowed(&[])); |
|
193 | 197 | }; |
|
194 | self.get_entry(rev)?.data() | |
|
198 | Ok(self.get_entry(rev)?.data()?) | |
|
195 | 199 | } |
|
196 | 200 | |
|
197 | 201 | /// Check the hash of some given data against the recorded hash. |
@@ -222,13 +226,13 impl Revlog { | |||
|
222 | 226 | fn build_data_from_deltas( |
|
223 | 227 | snapshot: RevlogEntry, |
|
224 | 228 | deltas: &[RevlogEntry], |
|
225 |
) -> Result<Vec<u8>, |
|
|
229 | ) -> Result<Vec<u8>, HgError> { | |
|
226 | 230 | let snapshot = snapshot.data_chunk()?; |
|
227 | 231 | let deltas = deltas |
|
228 | 232 | .iter() |
|
229 | 233 | .rev() |
|
230 | 234 | .map(RevlogEntry::data_chunk) |
|
231 |
.collect::<Result<Vec< |
|
|
235 | .collect::<Result<Vec<_>, _>>()?; | |
|
232 | 236 | let patches: Vec<_> = |
|
233 | 237 | deltas.iter().map(|d| patch::PatchList::new(d)).collect(); |
|
234 | 238 | let patch = patch::fold_patch_lists(&patches); |
@@ -246,7 +250,10 impl Revlog { | |||
|
246 | 250 | } |
|
247 | 251 | |
|
248 | 252 | /// Get an entry of the revlog. |
|
249 | fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> { | |
|
253 | pub fn get_entry( | |
|
254 | &self, | |
|
255 | rev: Revision, | |
|
256 | ) -> Result<RevlogEntry, RevlogError> { | |
|
250 | 257 | let index_entry = self |
|
251 | 258 | .index |
|
252 | 259 | .get_entry(rev) |
@@ -281,8 +288,8 impl Revlog { | |||
|
281 | 288 | fn get_entry_internal( |
|
282 | 289 | &self, |
|
283 | 290 | rev: Revision, |
|
284 |
) -> Result<RevlogEntry, |
|
|
285 |
return self.get_entry(rev).map_err(|_| |
|
|
291 | ) -> Result<RevlogEntry, HgError> { | |
|
292 | return self.get_entry(rev).map_err(|_| corrupted()); | |
|
286 | 293 | } |
|
287 | 294 | } |
|
288 | 295 | |
@@ -304,7 +311,7 impl<'a> RevlogEntry<'a> { | |||
|
304 | 311 | } |
|
305 | 312 | |
|
306 | 313 | /// The data for this entry, after resolving deltas if any. |
|
307 |
pub fn data(&self) -> Result<Cow<'a, [u8]>, |
|
|
314 | pub fn data(&self) -> Result<Cow<'a, [u8]>, HgError> { | |
|
308 | 315 | let mut entry = self.clone(); |
|
309 | 316 | let mut delta_chain = vec![]; |
|
310 | 317 | |
@@ -328,7 +335,7 impl<'a> RevlogEntry<'a> { | |||
|
328 | 335 | .revlog |
|
329 | 336 | .index |
|
330 | 337 | .get_entry(self.rev) |
|
331 |
.ok_or( |
|
|
338 | .ok_or_else(corrupted)?; | |
|
332 | 339 | |
|
333 | 340 | let data = if delta_chain.is_empty() { |
|
334 | 341 | entry.data_chunk()? |
@@ -344,13 +351,13 impl<'a> RevlogEntry<'a> { | |||
|
344 | 351 | ) { |
|
345 | 352 | Ok(data) |
|
346 | 353 | } else { |
|
347 |
Err( |
|
|
354 | Err(corrupted()) | |
|
348 | 355 | } |
|
349 | 356 | } |
|
350 | 357 | |
|
351 | 358 | /// Extract the data contained in the entry. |
|
352 | 359 | /// This may be a delta. (See `is_delta`.) |
|
353 |
fn data_chunk(&self) -> Result<Cow<'a, [u8]>, |
|
|
360 | fn data_chunk(&self) -> Result<Cow<'a, [u8]>, HgError> { | |
|
354 | 361 | if self.bytes.is_empty() { |
|
355 | 362 | return Ok(Cow::Borrowed(&[])); |
|
356 | 363 | } |
@@ -365,39 +372,35 impl<'a> RevlogEntry<'a> { | |||
|
365 | 372 | // zstd data. |
|
366 | 373 | b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)), |
|
367 | 374 | // A proper new format should have had a repo/store requirement. |
|
368 |
_format_type => Err( |
|
|
375 | _format_type => Err(corrupted()), | |
|
369 | 376 | } |
|
370 | 377 | } |
|
371 | 378 | |
|
372 |
fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, |
|
|
379 | fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, HgError> { | |
|
373 | 380 | let mut decoder = ZlibDecoder::new(self.bytes); |
|
374 | 381 | if self.is_delta() { |
|
375 | 382 | let mut buf = Vec::with_capacity(self.compressed_len); |
|
376 | decoder | |
|
377 | .read_to_end(&mut buf) | |
|
378 | .map_err(|_| RevlogError::corrupted())?; | |
|
383 | decoder.read_to_end(&mut buf).map_err(|_| corrupted())?; | |
|
379 | 384 | Ok(buf) |
|
380 | 385 | } else { |
|
381 | 386 | let mut buf = vec![0; self.uncompressed_len]; |
|
382 | decoder | |
|
383 | .read_exact(&mut buf) | |
|
384 | .map_err(|_| RevlogError::corrupted())?; | |
|
387 | decoder.read_exact(&mut buf).map_err(|_| corrupted())?; | |
|
385 | 388 | Ok(buf) |
|
386 | 389 | } |
|
387 | 390 | } |
|
388 | 391 | |
|
389 |
fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, |
|
|
392 | fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, HgError> { | |
|
390 | 393 | if self.is_delta() { |
|
391 | 394 | let mut buf = Vec::with_capacity(self.compressed_len); |
|
392 | 395 | zstd::stream::copy_decode(self.bytes, &mut buf) |
|
393 |
.map_err(|_| |
|
|
396 | .map_err(|_| corrupted())?; | |
|
394 | 397 | Ok(buf) |
|
395 | 398 | } else { |
|
396 | 399 | let mut buf = vec![0; self.uncompressed_len]; |
|
397 | 400 | let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf) |
|
398 |
.map_err(|_| |
|
|
401 | .map_err(|_| corrupted())?; | |
|
399 | 402 | if len != self.uncompressed_len { |
|
400 |
Err( |
|
|
403 | Err(corrupted()) | |
|
401 | 404 | } else { |
|
402 | 405 | Ok(buf) |
|
403 | 406 | } |
@@ -512,17 +512,18 fn unsure_is_modified( | |||
|
512 | 512 | } |
|
513 | 513 | let filelog = repo.filelog(hg_path)?; |
|
514 | 514 | let fs_len = fs_metadata.len(); |
|
515 | let filelog_entry = | |
|
516 | filelog.entry_for_node(entry.node_id()?).map_err(|_| { | |
|
517 | HgError::corrupted("filelog missing node from manifest") | |
|
518 | })?; | |
|
515 | 519 | // TODO: check `fs_len` here like below, but based on |
|
516 | 520 | // `RevlogEntry::uncompressed_len` without decompressing the full filelog |
|
517 | 521 | // contents where possible. This is only valid if the revlog data does not |
|
518 | 522 | // contain metadata. See how Python’s `revlog.rawsize` calls |
|
519 | 523 | // `storageutil.filerevisioncopied`. |
|
520 | 524 | // (Maybe also check for content-modifying flags? See `revlog.size`.) |
|
521 |
let filelog_ |
|
|
522 | filelog.data_for_node(entry.node_id()?).map_err(|_| { | |
|
523 | HgError::corrupted("filelog missing node from manifest") | |
|
524 | })?; | |
|
525 | let contents_in_p1 = filelog_entry.file_data()?; | |
|
525 | let filelog_data = filelog_entry.data()?; | |
|
526 | let contents_in_p1 = filelog_data.file_data()?; | |
|
526 | 527 | if contents_in_p1.len() as u64 != fs_len { |
|
527 | 528 | // No need to read the file contents: |
|
528 | 529 | // it cannot be equal if it has a different length. |
General Comments 0
You need to be logged in to leave comments.
Login now