106 fn _static_assert_size_of() { |
106 fn _static_assert_size_of() { |
107 let _ = std::mem::transmute::<Header, [u8; 72]>; |
107 let _ = std::mem::transmute::<Header, [u8; 72]>; |
108 let _ = std::mem::transmute::<Node, [u8; 57]>; |
108 let _ = std::mem::transmute::<Node, [u8; 57]>; |
109 } |
109 } |
110 |
110 |
|
111 /// Unexpected file format found in `.hg/dirstate` with the "v2" format. |
|
112 pub(crate) struct DirstateV2ParseError; |
|
113 |
|
114 impl From<DirstateV2ParseError> for HgError { |
|
115 fn from(_: DirstateV2ParseError) -> Self { |
|
116 HgError::corrupted("dirstate-v2 parse error") |
|
117 } |
|
118 } |
|
119 |
|
120 impl From<DirstateV2ParseError> for crate::DirstateError { |
|
121 fn from(error: DirstateV2ParseError) -> Self { |
|
122 HgError::from(error).into() |
|
123 } |
|
124 } |
|
125 |
111 pub(super) fn read<'on_disk>( |
126 pub(super) fn read<'on_disk>( |
112 on_disk: &'on_disk [u8], |
127 on_disk: &'on_disk [u8], |
113 ) -> Result<(DirstateMap<'on_disk>, Option<DirstateParents>), DirstateError> { |
128 ) -> Result< |
|
129 (DirstateMap<'on_disk>, Option<DirstateParents>), |
|
130 DirstateV2ParseError, |
|
131 > { |
114 if on_disk.is_empty() { |
132 if on_disk.is_empty() { |
115 return Ok((DirstateMap::empty(on_disk), None)); |
133 return Ok((DirstateMap::empty(on_disk), None)); |
116 } |
134 } |
117 let (header, _) = Header::from_bytes(on_disk) |
135 let (header, _) = |
118 .map_err(|_| HgError::corrupted("truncated dirstate-v2"))?; |
136 Header::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?; |
119 let Header { |
137 let Header { |
120 marker, |
138 marker, |
121 parents, |
139 parents, |
122 root, |
140 root, |
123 nodes_with_entry_count, |
141 nodes_with_entry_count, |
124 nodes_with_copy_source_count, |
142 nodes_with_copy_source_count, |
125 } = header; |
143 } = header; |
126 if marker != V2_FORMAT_MARKER { |
144 if marker != V2_FORMAT_MARKER { |
127 return Err(HgError::corrupted("missing dirstated-v2 marker").into()); |
145 return Err(DirstateV2ParseError); |
128 } |
146 } |
129 let dirstate_map = DirstateMap { |
147 let dirstate_map = DirstateMap { |
130 on_disk, |
148 on_disk, |
131 root: read_nodes(on_disk, *root)?, |
149 root: read_nodes(on_disk, *root)?, |
132 nodes_with_entry_count: nodes_with_entry_count.get(), |
150 nodes_with_entry_count: nodes_with_entry_count.get(), |
138 |
156 |
139 impl Node { |
157 impl Node { |
140 pub(super) fn path<'on_disk>( |
158 pub(super) fn path<'on_disk>( |
141 &self, |
159 &self, |
142 on_disk: &'on_disk [u8], |
160 on_disk: &'on_disk [u8], |
143 ) -> Result<dirstate_map::NodeKey<'on_disk>, HgError> { |
161 ) -> Result<dirstate_map::NodeKey<'on_disk>, DirstateV2ParseError> { |
144 let full_path = read_hg_path(on_disk, self.full_path)?; |
162 let full_path = read_hg_path(on_disk, self.full_path)?; |
145 let base_name_start = usize::try_from(self.base_name_start.get()) |
163 let base_name_start = usize::try_from(self.base_name_start.get()) |
146 // u32 -> usize, could only panic on a 16-bit CPU |
164 // u32 -> usize, could only panic on a 16-bit CPU |
147 .expect("dirstate-v2 base_name_start out of bounds"); |
165 .expect("dirstate-v2 base_name_start out of bounds"); |
148 if base_name_start < full_path.len() { |
166 if base_name_start < full_path.len() { |
149 Ok(WithBasename::from_raw_parts(full_path, base_name_start)) |
167 Ok(WithBasename::from_raw_parts(full_path, base_name_start)) |
150 } else { |
168 } else { |
151 Err(HgError::corrupted( |
169 Err(DirstateV2ParseError) |
152 "dirstate-v2 base_name_start out of bounds", |
|
153 )) |
|
154 } |
170 } |
155 } |
171 } |
156 |
172 |
157 pub(super) fn copy_source<'on_disk>( |
173 pub(super) fn copy_source<'on_disk>( |
158 &self, |
174 &self, |
159 on_disk: &'on_disk [u8], |
175 on_disk: &'on_disk [u8], |
160 ) -> Result<Option<Cow<'on_disk, HgPath>>, HgError> { |
176 ) -> Result<Option<Cow<'on_disk, HgPath>>, DirstateV2ParseError> { |
161 Ok(if self.copy_source.start.get() != 0 { |
177 Ok(if self.copy_source.start.get() != 0 { |
162 Some(read_hg_path(on_disk, self.copy_source)?) |
178 Some(read_hg_path(on_disk, self.copy_source)?) |
163 } else { |
179 } else { |
164 None |
180 None |
165 }) |
181 }) |
166 } |
182 } |
167 |
183 |
168 pub(super) fn entry(&self) -> Result<Option<DirstateEntry>, HgError> { |
184 pub(super) fn entry( |
|
185 &self, |
|
186 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { |
169 Ok(if self.entry.state != b'\0' { |
187 Ok(if self.entry.state != b'\0' { |
170 Some(DirstateEntry { |
188 Some(DirstateEntry { |
171 state: self.entry.state.try_into()?, |
189 state: self |
|
190 .entry |
|
191 .state |
|
192 .try_into() |
|
193 .map_err(|_| DirstateV2ParseError)?, |
172 mode: self.entry.mode.get(), |
194 mode: self.entry.mode.get(), |
173 mtime: self.entry.mtime.get(), |
195 mtime: self.entry.mtime.get(), |
174 size: self.entry.size.get(), |
196 size: self.entry.size.get(), |
175 }) |
197 }) |
176 } else { |
198 } else { |
179 } |
201 } |
180 |
202 |
181 pub(super) fn to_in_memory_node<'on_disk>( |
203 pub(super) fn to_in_memory_node<'on_disk>( |
182 &self, |
204 &self, |
183 on_disk: &'on_disk [u8], |
205 on_disk: &'on_disk [u8], |
184 ) -> Result<dirstate_map::Node<'on_disk>, HgError> { |
206 ) -> Result<dirstate_map::Node<'on_disk>, DirstateV2ParseError> { |
185 Ok(dirstate_map::Node { |
207 Ok(dirstate_map::Node { |
186 children: read_nodes(on_disk, self.children)?, |
208 children: read_nodes(on_disk, self.children)?, |
187 copy_source: self.copy_source(on_disk)?, |
209 copy_source: self.copy_source(on_disk)?, |
188 entry: self.entry()?, |
210 entry: self.entry()?, |
189 tracked_descendants_count: self.tracked_descendants_count.get(), |
211 tracked_descendants_count: self.tracked_descendants_count.get(), |
192 } |
214 } |
193 |
215 |
194 fn read_nodes( |
216 fn read_nodes( |
195 on_disk: &[u8], |
217 on_disk: &[u8], |
196 slice: ChildNodes, |
218 slice: ChildNodes, |
197 ) -> Result<dirstate_map::ChildNodes, HgError> { |
219 ) -> Result<dirstate_map::ChildNodes, DirstateV2ParseError> { |
198 read_slice::<Node>(on_disk, slice)? |
220 read_slice::<Node>(on_disk, slice)? |
199 .iter() |
221 .iter() |
200 .map(|node| { |
222 .map(|node| { |
201 Ok((node.path(on_disk)?, node.to_in_memory_node(on_disk)?)) |
223 Ok((node.path(on_disk)?, node.to_in_memory_node(on_disk)?)) |
202 }) |
224 }) |
203 .collect::<Result<_, _>>() |
225 .collect::<Result<_, _>>() |
204 .map(dirstate_map::ChildNodes::InMemory) |
226 .map(dirstate_map::ChildNodes::InMemory) |
205 } |
227 } |
206 |
228 |
207 fn read_hg_path(on_disk: &[u8], slice: Slice) -> Result<Cow<HgPath>, HgError> { |
229 fn read_hg_path( |
|
230 on_disk: &[u8], |
|
231 slice: Slice, |
|
232 ) -> Result<Cow<HgPath>, DirstateV2ParseError> { |
208 let bytes = read_slice::<u8>(on_disk, slice)?; |
233 let bytes = read_slice::<u8>(on_disk, slice)?; |
209 Ok(Cow::Borrowed(HgPath::new(bytes))) |
234 Ok(Cow::Borrowed(HgPath::new(bytes))) |
210 } |
235 } |
211 |
236 |
212 fn read_slice<T>(on_disk: &[u8], slice: Slice) -> Result<&[T], HgError> |
237 fn read_slice<T>( |
|
238 on_disk: &[u8], |
|
239 slice: Slice, |
|
240 ) -> Result<&[T], DirstateV2ParseError> |
213 where |
241 where |
214 T: BytesCast, |
242 T: BytesCast, |
215 { |
243 { |
216 // Either `usize::MAX` would result in "out of bounds" error since a single |
244 // Either `usize::MAX` would result in "out of bounds" error since a single |
217 // `&[u8]` cannot occupy the entire addess space. |
245 // `&[u8]` cannot occupy the entire addess space. |
219 let len = usize::try_from(slice.len.get()).unwrap_or(std::usize::MAX); |
247 let len = usize::try_from(slice.len.get()).unwrap_or(std::usize::MAX); |
220 on_disk |
248 on_disk |
221 .get(start..) |
249 .get(start..) |
222 .and_then(|bytes| T::slice_from_bytes(bytes, len).ok()) |
250 .and_then(|bytes| T::slice_from_bytes(bytes, len).ok()) |
223 .map(|(slice, _rest)| slice) |
251 .map(|(slice, _rest)| slice) |
224 .ok_or_else(|| { |
252 .ok_or_else(|| DirstateV2ParseError) |
225 HgError::corrupted("dirstate v2 slice is out of bounds") |
|
226 }) |
|
227 } |
253 } |
228 |
254 |
229 pub(super) fn write( |
255 pub(super) fn write( |
230 dirstate_map: &mut DirstateMap, |
256 dirstate_map: &mut DirstateMap, |
231 parents: DirstateParents, |
257 parents: DirstateParents, |