173 |
173 |
174 /// Unexpected file format found in `.hg/dirstate` with the "v2" format. |
174 /// Unexpected file format found in `.hg/dirstate` with the "v2" format. |
175 /// |
175 /// |
176 /// This should only happen if Mercurial is buggy or a repository is corrupted. |
176 /// This should only happen if Mercurial is buggy or a repository is corrupted. |
177 #[derive(Debug)] |
177 #[derive(Debug)] |
178 pub struct DirstateV2ParseError; |
178 pub struct DirstateV2ParseError { |
|
179 message: String, |
|
180 } |
|
181 |
|
182 impl DirstateV2ParseError { |
|
183 pub fn new<S: Into<String>>(message: S) -> Self { |
|
184 Self { |
|
185 message: message.into(), |
|
186 } |
|
187 } |
|
188 } |
179 |
189 |
180 impl From<DirstateV2ParseError> for HgError { |
190 impl From<DirstateV2ParseError> for HgError { |
181 fn from(_: DirstateV2ParseError) -> Self { |
191 fn from(e: DirstateV2ParseError) -> Self { |
182 HgError::corrupted("dirstate-v2 parse error") |
192 HgError::corrupted(format!("dirstate-v2 parse error: {}", e.message)) |
183 } |
193 } |
184 } |
194 } |
185 |
195 |
186 impl From<DirstateV2ParseError> for crate::DirstateError { |
196 impl From<DirstateV2ParseError> for crate::DirstateError { |
187 fn from(error: DirstateV2ParseError) -> Self { |
197 fn from(error: DirstateV2ParseError) -> Self { |
260 } |
270 } |
261 |
271 |
262 pub fn read_docket( |
272 pub fn read_docket( |
263 on_disk: &[u8], |
273 on_disk: &[u8], |
264 ) -> Result<Docket<'_>, DirstateV2ParseError> { |
274 ) -> Result<Docket<'_>, DirstateV2ParseError> { |
265 let (header, uuid) = |
275 let (header, uuid) = DocketHeader::from_bytes(on_disk).map_err(|e| { |
266 DocketHeader::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?; |
276 DirstateV2ParseError::new(format!("when reading docket, {}", e)) |
|
277 })?; |
267 let uuid_size = header.uuid_size as usize; |
278 let uuid_size = header.uuid_size as usize; |
268 if header.marker == *V2_FORMAT_MARKER && uuid.len() == uuid_size { |
279 if header.marker == *V2_FORMAT_MARKER && uuid.len() == uuid_size { |
269 Ok(Docket { header, uuid }) |
280 Ok(Docket { header, uuid }) |
270 } else { |
281 } else { |
271 Err(DirstateV2ParseError) |
282 Err(DirstateV2ParseError::new( |
|
283 "invalid format marker or uuid size", |
|
284 )) |
272 } |
285 } |
273 } |
286 } |
274 |
287 |
275 pub(super) fn read<'on_disk>( |
288 pub(super) fn read<'on_disk>( |
276 on_disk: &'on_disk [u8], |
289 on_disk: &'on_disk [u8], |
279 if on_disk.is_empty() { |
292 if on_disk.is_empty() { |
280 let mut map = DirstateMap::empty(on_disk); |
293 let mut map = DirstateMap::empty(on_disk); |
281 map.dirstate_version = DirstateVersion::V2; |
294 map.dirstate_version = DirstateVersion::V2; |
282 return Ok(map); |
295 return Ok(map); |
283 } |
296 } |
284 let (meta, _) = TreeMetadata::from_bytes(metadata) |
297 let (meta, _) = TreeMetadata::from_bytes(metadata).map_err(|e| { |
285 .map_err(|_| DirstateV2ParseError)?; |
298 DirstateV2ParseError::new(format!("when parsing tree metadata, {}", e)) |
|
299 })?; |
286 let dirstate_map = DirstateMap { |
300 let dirstate_map = DirstateMap { |
287 on_disk, |
301 on_disk, |
288 root: dirstate_map::ChildNodes::OnDisk(read_nodes( |
302 root: dirstate_map::ChildNodes::OnDisk( |
289 on_disk, |
303 read_nodes(on_disk, meta.root_nodes).map_err(|mut e| { |
290 meta.root_nodes, |
304 e.message = format!("{}, when reading root notes", e.message); |
291 )?), |
305 e |
|
306 })?, |
|
307 ), |
292 nodes_with_entry_count: meta.nodes_with_entry_count.get(), |
308 nodes_with_entry_count: meta.nodes_with_entry_count.get(), |
293 nodes_with_copy_source_count: meta.nodes_with_copy_source_count.get(), |
309 nodes_with_copy_source_count: meta.nodes_with_copy_source_count.get(), |
294 ignore_patterns_hash: meta.ignore_patterns_hash, |
310 ignore_patterns_hash: meta.ignore_patterns_hash, |
295 unreachable_bytes: meta.unreachable_bytes.get(), |
311 unreachable_bytes: meta.unreachable_bytes.get(), |
296 old_data_size: on_disk.len(), |
312 old_data_size: on_disk.len(), |
315 let start = usize::try_from(start) |
331 let start = usize::try_from(start) |
316 // u32 -> usize, could only panic on a 16-bit CPU |
332 // u32 -> usize, could only panic on a 16-bit CPU |
317 .expect("dirstate-v2 base_name_start out of bounds"); |
333 .expect("dirstate-v2 base_name_start out of bounds"); |
318 Ok(start) |
334 Ok(start) |
319 } else { |
335 } else { |
320 Err(DirstateV2ParseError) |
336 Err(DirstateV2ParseError::new("not enough bytes for base name")) |
321 } |
337 } |
322 } |
338 } |
323 |
339 |
324 pub(super) fn base_name<'on_disk>( |
340 pub(super) fn base_name<'on_disk>( |
325 &self, |
341 &self, |
569 { |
585 { |
570 // Either `usize::MAX` would result in "out of bounds" error since a single |
586 // Either `usize::MAX` would result in "out of bounds" error since a single |
571 // `&[u8]` cannot occupy the entire addess space. |
587 // `&[u8]` cannot occupy the entire addess space. |
572 let start = start.get().try_into().unwrap_or(std::usize::MAX); |
588 let start = start.get().try_into().unwrap_or(std::usize::MAX); |
573 let len = len.try_into().unwrap_or(std::usize::MAX); |
589 let len = len.try_into().unwrap_or(std::usize::MAX); |
574 on_disk |
590 let bytes = match on_disk.get(start..) { |
575 .get(start..) |
591 Some(bytes) => bytes, |
576 .and_then(|bytes| T::slice_from_bytes(bytes, len).ok()) |
592 None => { |
|
593 return Err(DirstateV2ParseError::new( |
|
594 "not enough bytes from disk", |
|
595 )) |
|
596 } |
|
597 }; |
|
598 T::slice_from_bytes(bytes, len) |
|
599 .map_err(|e| { |
|
600 DirstateV2ParseError::new(format!("when reading a slice, {}", e)) |
|
601 }) |
577 .map(|(slice, _rest)| slice) |
602 .map(|(slice, _rest)| slice) |
578 .ok_or_else(|| DirstateV2ParseError) |
|
579 } |
603 } |
580 |
604 |
581 pub(crate) fn for_each_tracked_path<'on_disk>( |
605 pub(crate) fn for_each_tracked_path<'on_disk>( |
582 on_disk: &'on_disk [u8], |
606 on_disk: &'on_disk [u8], |
583 metadata: &[u8], |
607 metadata: &[u8], |
584 mut f: impl FnMut(&'on_disk HgPath), |
608 mut f: impl FnMut(&'on_disk HgPath), |
585 ) -> Result<(), DirstateV2ParseError> { |
609 ) -> Result<(), DirstateV2ParseError> { |
586 let (meta, _) = TreeMetadata::from_bytes(metadata) |
610 let (meta, _) = TreeMetadata::from_bytes(metadata).map_err(|e| { |
587 .map_err(|_| DirstateV2ParseError)?; |
611 DirstateV2ParseError::new(format!("when parsing tree metadata, {}", e)) |
|
612 })?; |
588 fn recur<'on_disk>( |
613 fn recur<'on_disk>( |
589 on_disk: &'on_disk [u8], |
614 on_disk: &'on_disk [u8], |
590 nodes: ChildNodes, |
615 nodes: ChildNodes, |
591 f: &mut impl FnMut(&'on_disk HgPath), |
616 f: &mut impl FnMut(&'on_disk HgPath), |
592 ) -> Result<(), DirstateV2ParseError> { |
617 ) -> Result<(), DirstateV2ParseError> { |