diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs b/rust/hg-core/src/dirstate_tree/dirstate_map.rs --- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs +++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs @@ -228,7 +228,7 @@ pub fn new_v2( on_disk: &'on_disk [u8], ) -> Result<(Self, Option), DirstateError> { - on_disk::read(on_disk) + Ok(on_disk::read(on_disk)?) } #[timed] diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs @@ -108,14 +108,32 @@ let _ = std::mem::transmute::; } +/// Unexpected file format found in `.hg/dirstate` with the "v2" format. +pub(crate) struct DirstateV2ParseError; + +impl From for HgError { + fn from(_: DirstateV2ParseError) -> Self { + HgError::corrupted("dirstate-v2 parse error") + } +} + +impl From for crate::DirstateError { + fn from(error: DirstateV2ParseError) -> Self { + HgError::from(error).into() + } +} + pub(super) fn read<'on_disk>( on_disk: &'on_disk [u8], -) -> Result<(DirstateMap<'on_disk>, Option), DirstateError> { +) -> Result< + (DirstateMap<'on_disk>, Option), + DirstateV2ParseError, +> { if on_disk.is_empty() { return Ok((DirstateMap::empty(on_disk), None)); } - let (header, _) = Header::from_bytes(on_disk) - .map_err(|_| HgError::corrupted("truncated dirstate-v2"))?; + let (header, _) = + Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?; let Header { marker, parents, @@ -124,7 +142,7 @@ nodes_with_copy_source_count, } = header; if marker != V2_FORMAT_MARKER { - return Err(HgError::corrupted("missing dirstated-v2 marker").into()); + return Err(DirstateV2ParseError); } let dirstate_map = DirstateMap { on_disk, @@ -140,7 +158,7 @@ pub(super) fn path<'on_disk>( &self, on_disk: &'on_disk [u8], - ) -> Result, HgError> { + ) -> Result, DirstateV2ParseError> { let full_path = read_hg_path(on_disk, self.full_path)?; let base_name_start = usize::try_from(self.base_name_start.get()) // u32 -> usize, could only panic on a 16-bit CPU @@ -148,16 +166,14 @@ if base_name_start < full_path.len() { Ok(WithBasename::from_raw_parts(full_path, base_name_start)) } else { - Err(HgError::corrupted( - "dirstate-v2 base_name_start out of bounds", - )) + Err(DirstateV2ParseError) } } pub(super) fn copy_source<'on_disk>( &self, on_disk: &'on_disk [u8], - ) -> Result>, HgError> { + ) -> Result>, DirstateV2ParseError> { Ok(if self.copy_source.start.get() != 0 { Some(read_hg_path(on_disk, self.copy_source)?) } else { @@ -165,10 +181,16 @@ }) } - pub(super) fn entry(&self) -> Result, HgError> { + pub(super) fn entry( + &self, + ) -> Result, DirstateV2ParseError> { Ok(if self.entry.state != b'\0' { Some(DirstateEntry { - state: self.entry.state.try_into()?, + state: self + .entry + .state + .try_into() + .map_err(|_| DirstateV2ParseError)?, mode: self.entry.mode.get(), mtime: self.entry.mtime.get(), size: self.entry.size.get(), @@ -181,7 +203,7 @@ pub(super) fn to_in_memory_node<'on_disk>( &self, on_disk: &'on_disk [u8], - ) -> Result, HgError> { + ) -> Result, DirstateV2ParseError> { Ok(dirstate_map::Node { children: read_nodes(on_disk, self.children)?, copy_source: self.copy_source(on_disk)?, @@ -194,7 +216,7 @@ fn read_nodes( on_disk: &[u8], slice: ChildNodes, -) -> Result { +) -> Result { read_slice::(on_disk, slice)? .iter() .map(|node| { @@ -204,12 +226,18 @@ .map(dirstate_map::ChildNodes::InMemory) } -fn read_hg_path(on_disk: &[u8], slice: Slice) -> Result, HgError> { +fn read_hg_path( + on_disk: &[u8], + slice: Slice, +) -> Result, DirstateV2ParseError> { let bytes = read_slice::(on_disk, slice)?; Ok(Cow::Borrowed(HgPath::new(bytes))) } -fn read_slice(on_disk: &[u8], slice: Slice) -> Result<&[T], HgError> +fn read_slice( + on_disk: &[u8], + slice: Slice, +) -> Result<&[T], DirstateV2ParseError> where T: BytesCast, { @@ -221,9 +249,7 @@ .get(start..) .and_then(|bytes| T::slice_from_bytes(bytes, len).ok()) .map(|(slice, _rest)| slice) - .ok_or_else(|| { - HgError::corrupted("dirstate v2 slice is out of bounds") - }) + .ok_or_else(|| DirstateV2ParseError) } pub(super) fn write(