##// END OF EJS Templates
rust: use HgError in RevlogError and Vfs...
Simon Sapin -
r47172:43d63979 default
parent child Browse files
Show More
@@ -35,6 +35,9 b' pub enum IoErrorContext {'
35 35
36 36 impl HgError {
37 37 pub fn corrupted(explanation: impl Into<String>) -> Self {
38 // TODO: capture a backtrace here and keep it in the error value
39 // to aid debugging?
40 // https://doc.rust-lang.org/std/backtrace/struct.Backtrace.html
38 41 HgError::CorruptedRepository(explanation.into())
39 42 }
40 43 }
@@ -3,6 +3,7 b''
3 3 //
4 4 // This software may be used and distributed according to the terms of the
5 5 // GNU General Public License version 2 or any later version.
6
6 7 mod ancestors;
7 8 pub mod dagops;
8 9 pub mod errors;
@@ -33,8 +33,8 b' pub fn cat('
33 33 let changelog = Changelog::open(repo)?;
34 34 let manifest = Manifest::open(repo)?;
35 35 let changelog_entry = changelog.get_rev(rev)?;
36 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
37 .map_err(|_| RevlogError::Corrupted)?;
36 let manifest_node =
37 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
38 38 let manifest_entry = manifest.get_node(manifest_node.into())?;
39 39 let mut bytes = vec![];
40 40
@@ -46,8 +46,7 b' pub fn cat('
46 46
47 47 let file_log =
48 48 Revlog::open(repo, &index_path, Some(&data_path))?;
49 let file_node = Node::from_hex(node_bytes)
50 .map_err(|_| RevlogError::Corrupted)?;
49 let file_node = Node::from_hex_for_repo(node_bytes)?;
51 50 let file_rev = file_log.get_node_rev(file_node.into())?;
52 51 let data = file_log.get_rev_data(file_rev)?;
53 52 if data.starts_with(&METADATA_DELIMITER) {
@@ -6,7 +6,7 b''
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::dirstate::parsers::parse_dirstate;
9 use crate::errors::{HgError, IoResultExt};
9 use crate::errors::HgError;
10 10 use crate::repo::Repo;
11 11 use crate::revlog::changelog::Changelog;
12 12 use crate::revlog::manifest::{Manifest, ManifestEntry};
@@ -25,12 +25,7 b' pub struct Dirstate {'
25 25
26 26 impl Dirstate {
27 27 pub fn new(repo: &Repo) -> Result<Self, HgError> {
28 let content = repo
29 .hg_vfs()
30 .read("dirstate")
31 // TODO: this will be more accurate when we use `HgError` in
32 // `Vfs::read`.
33 .for_file("dirstate".as_ref())?;
28 let content = repo.hg_vfs().read("dirstate")?;
34 29 Ok(Self { content })
35 30 }
36 31
@@ -57,8 +52,8 b' pub fn list_rev_tracked_files('
57 52 let changelog = Changelog::open(repo)?;
58 53 let manifest = Manifest::open(repo)?;
59 54 let changelog_entry = changelog.get_rev(rev)?;
60 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
61 .map_err(|_| RevlogError::Corrupted)?;
55 let manifest_node =
56 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
62 57 let manifest_entry = manifest.get_node(manifest_node.into())?;
63 58 Ok(FilesForRev(manifest_entry))
64 59 }
@@ -1,4 +1,4 b''
1 use crate::errors::HgError;
1 use crate::errors::{HgError, IoResultExt};
2 2 use crate::operations::{find_root, FindRootError};
3 3 use crate::requirements;
4 4 use memmap::{Mmap, MmapOptions};
@@ -68,24 +68,19 b" impl Vfs<'_> {"
68 68 pub(crate) fn read(
69 69 &self,
70 70 relative_path: impl AsRef<Path>,
71 ) -> std::io::Result<Vec<u8>> {
72 std::fs::read(self.base.join(relative_path))
73 }
74
75 pub(crate) fn open(
76 &self,
77 relative_path: impl AsRef<Path>,
78 ) -> std::io::Result<std::fs::File> {
79 std::fs::File::open(self.base.join(relative_path))
71 ) -> Result<Vec<u8>, HgError> {
72 let path = self.base.join(relative_path);
73 std::fs::read(&path).for_file(&path)
80 74 }
81 75
82 76 pub(crate) fn mmap_open(
83 77 &self,
84 78 relative_path: impl AsRef<Path>,
85 ) -> std::io::Result<Mmap> {
86 let file = self.open(relative_path)?;
79 ) -> Result<Mmap, HgError> {
80 let path = self.base.join(relative_path);
81 let file = std::fs::File::open(&path).for_file(&path)?;
87 82 // TODO: what are the safety requirements here?
88 let mmap = unsafe { MmapOptions::new().map(&file) }?;
83 let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?;
89 84 Ok(mmap)
90 85 }
91 86 }
@@ -1,4 +1,4 b''
1 use crate::errors::{HgError, HgResultExt, IoResultExt};
1 use crate::errors::{HgError, HgResultExt};
2 2 use crate::repo::Repo;
3 3
4 4 fn parse(bytes: &[u8]) -> Result<Vec<String>, HgError> {
@@ -22,11 +22,8 b' fn parse(bytes: &[u8]) -> Result<Vec<Str'
22 22 }
23 23
24 24 pub fn load(repo: &Repo) -> Result<Vec<String>, HgError> {
25 if let Some(bytes) = repo
26 .hg_vfs()
27 .read("requires")
28 .for_file("requires".as_ref())
29 .io_not_found_as_none()?
25 if let Some(bytes) =
26 repo.hg_vfs().read("requires").io_not_found_as_none()?
30 27 {
31 28 parse(&bytes)
32 29 } else {
@@ -1,3 +1,4 b''
1 use crate::errors::HgError;
1 2 use crate::repo::Repo;
2 3 use crate::revlog::revlog::{Revlog, RevlogError};
3 4 use crate::revlog::NodePrefix;
@@ -53,6 +54,8 b' impl ChangelogEntry {'
53 54 /// Return the node id of the `manifest` referenced by this `changelog`
54 55 /// entry.
55 56 pub fn manifest_node(&self) -> Result<&[u8], RevlogError> {
56 self.lines().next().ok_or(RevlogError::Corrupted)
57 self.lines()
58 .next()
59 .ok_or_else(|| HgError::corrupted("empty changelog entry").into())
57 60 }
58 61 }
@@ -3,6 +3,7 b' use std::ops::Deref;'
3 3
4 4 use byteorder::{BigEndian, ByteOrder};
5 5
6 use crate::errors::HgError;
6 7 use crate::revlog::node::Node;
7 8 use crate::revlog::revlog::RevlogError;
8 9 use crate::revlog::{Revision, NULL_REVISION};
@@ -44,7 +45,8 b' impl Index {'
44 45 offsets: Some(offsets),
45 46 })
46 47 } else {
47 Err(RevlogError::Corrupted)
48 Err(HgError::corrupted("unexpected inline revlog length")
49 .into())
48 50 }
49 51 } else {
50 52 Ok(Self {
@@ -8,6 +8,7 b''
8 8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
9 9 //! of a revision.
10 10
11 use crate::errors::HgError;
11 12 use bytes_cast::BytesCast;
12 13 use std::convert::{TryFrom, TryInto};
13 14 use std::fmt;
@@ -136,6 +137,19 b' impl Node {'
136 137 }
137 138 }
138 139
140 /// `from_hex`, but for input from an internal file of the repository such
141 /// as a changelog or manifest entry.
142 ///
143 /// An error is treated as repository corruption.
144 pub fn from_hex_for_repo(hex: impl AsRef<[u8]>) -> Result<Node, HgError> {
145 Self::from_hex(hex.as_ref()).map_err(|FromHexError| {
146 HgError::CorruptedRepository(format!(
147 "Expected a full hexadecimal node ID, found {}",
148 String::from_utf8_lossy(hex.as_ref())
149 ))
150 })
151 }
152
139 153 /// Provide access to binary data
140 154 ///
141 155 /// This is needed by FFI layers, for instance to return expected
@@ -1,3 +1,4 b''
1 use crate::errors::{HgError, HgResultExt};
1 2 use bytes_cast::{unaligned, BytesCast};
2 3 use memmap::Mmap;
3 4 use std::path::{Path, PathBuf};
@@ -38,12 +39,12 b' impl NodeMapDocket {'
38 39 index_path: &Path,
39 40 ) -> Result<Option<(Self, Mmap)>, RevlogError> {
40 41 let docket_path = index_path.with_extension("n");
41 let docket_bytes = match repo.store_vfs().read(&docket_path) {
42 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
43 return Ok(None)
44 }
45 Err(e) => return Err(RevlogError::IoError(e)),
46 Ok(bytes) => bytes,
42 let docket_bytes = if let Some(bytes) =
43 repo.store_vfs().read(&docket_path).io_not_found_as_none()?
44 {
45 bytes
46 } else {
47 return Ok(None);
47 48 };
48 49
49 50 let input = if let Some((&ONDISK_VERSION, rest)) =
@@ -54,36 +55,40 b' impl NodeMapDocket {'
54 55 return Ok(None);
55 56 };
56 57
57 let (header, rest) = DocketHeader::from_bytes(input)?;
58 /// Treat any error as a parse error
59 fn parse<T, E>(result: Result<T, E>) -> Result<T, RevlogError> {
60 result.map_err(|_| {
61 HgError::corrupted("nodemap docket parse error").into()
62 })
63 }
64
65 let (header, rest) = parse(DocketHeader::from_bytes(input))?;
58 66 let uid_size = header.uid_size as usize;
59 67 // TODO: do we care about overflow for 4 GB+ nodemap files on 32-bit
60 68 // systems?
61 69 let tip_node_size = header.tip_node_size.get() as usize;
62 70 let data_length = header.data_length.get() as usize;
63 let (uid, rest) = u8::slice_from_bytes(rest, uid_size)?;
64 let (_tip_node, _rest) = u8::slice_from_bytes(rest, tip_node_size)?;
65 let uid =
66 std::str::from_utf8(uid).map_err(|_| RevlogError::Corrupted)?;
71 let (uid, rest) = parse(u8::slice_from_bytes(rest, uid_size))?;
72 let (_tip_node, _rest) =
73 parse(u8::slice_from_bytes(rest, tip_node_size))?;
74 let uid = parse(std::str::from_utf8(uid))?;
67 75 let docket = NodeMapDocket { data_length };
68 76
69 77 let data_path = rawdata_path(&docket_path, uid);
70 // TODO: use `std::fs::read` here when the `persistent-nodemap.mmap`
78 // TODO: use `vfs.read()` here when the `persistent-nodemap.mmap`
71 79 // config is false?
72 match repo.store_vfs().mmap_open(&data_path) {
73 Ok(mmap) => {
74 if mmap.len() >= data_length {
75 Ok(Some((docket, mmap)))
76 } else {
77 Err(RevlogError::Corrupted)
78 }
80 if let Some(mmap) = repo
81 .store_vfs()
82 .mmap_open(&data_path)
83 .io_not_found_as_none()?
84 {
85 if mmap.len() >= data_length {
86 Ok(Some((docket, mmap)))
87 } else {
88 Err(HgError::corrupted("persistent nodemap too short").into())
79 89 }
80 Err(error) => {
81 if error.kind() == std::io::ErrorKind::NotFound {
82 Ok(None)
83 } else {
84 Err(RevlogError::IoError(error))
85 }
86 }
90 } else {
91 Ok(None)
87 92 }
88 93 }
89 94 }
@@ -13,25 +13,34 b' use zstd;'
13 13 use super::index::Index;
14 14 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
15 15 use super::nodemap;
16 use super::nodemap::NodeMap;
16 use super::nodemap::{NodeMap, NodeMapError};
17 17 use super::nodemap_docket::NodeMapDocket;
18 18 use super::patch;
19 use crate::errors::HgError;
19 20 use crate::repo::Repo;
20 21 use crate::revlog::Revision;
21 22
23 #[derive(derive_more::From)]
22 24 pub enum RevlogError {
23 IoError(std::io::Error),
24 UnsuportedVersion(u16),
25 25 InvalidRevision,
26 26 /// Found more than one entry whose ID match the requested prefix
27 27 AmbiguousPrefix,
28 Corrupted,
29 UnknowDataFormat(u8),
28 #[from]
29 Other(HgError),
30 30 }
31 31
32 impl From<bytes_cast::FromBytesError> for RevlogError {
33 fn from(_: bytes_cast::FromBytesError) -> Self {
34 RevlogError::Corrupted
32 impl From<NodeMapError> for RevlogError {
33 fn from(error: NodeMapError) -> Self {
34 match error {
35 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
36 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
37 }
38 }
39 }
40
41 impl RevlogError {
42 fn corrupted() -> Self {
43 RevlogError::Other(HgError::corrupted("corrupted revlog"))
35 44 }
36 45 }
37 46
@@ -59,14 +68,12 b' impl Revlog {'
59 68 data_path: Option<&Path>,
60 69 ) -> Result<Self, RevlogError> {
61 70 let index_path = index_path.as_ref();
62 let index_mmap = repo
63 .store_vfs()
64 .mmap_open(&index_path)
65 .map_err(RevlogError::IoError)?;
71 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
66 72
67 73 let version = get_version(&index_mmap);
68 74 if version != 1 {
69 return Err(RevlogError::UnsuportedVersion(version));
75 // A proper new version should have had a repo/store requirement.
76 return Err(RevlogError::corrupted());
70 77 }
71 78
72 79 let index = Index::new(Box::new(index_mmap))?;
@@ -80,10 +87,7 b' impl Revlog {'
80 87 None
81 88 } else {
82 89 let data_path = data_path.unwrap_or(&default_data_path);
83 let data_mmap = repo
84 .store_vfs()
85 .mmap_open(data_path)
86 .map_err(RevlogError::IoError)?;
90 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
87 91 Some(Box::new(data_mmap))
88 92 };
89 93
@@ -121,9 +125,7 b' impl Revlog {'
121 125 ) -> Result<Revision, RevlogError> {
122 126 if let Some(nodemap) = &self.nodemap {
123 127 return nodemap
124 .find_bin(&self.index, node)
125 // TODO: propagate details of this error:
126 .map_err(|_| RevlogError::Corrupted)?
128 .find_bin(&self.index, node)?
127 129 .ok_or(RevlogError::InvalidRevision);
128 130 }
129 131
@@ -136,7 +138,9 b' impl Revlog {'
136 138 let mut found_by_prefix = None;
137 139 for rev in (0..self.len() as Revision).rev() {
138 140 let index_entry =
139 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
141 self.index.get_entry(rev).ok_or(HgError::corrupted(
142 "revlog references a revision not in the index",
143 ))?;
140 144 if node == *index_entry.hash() {
141 145 return Ok(rev);
142 146 }
@@ -167,8 +171,9 b' impl Revlog {'
167 171 let mut delta_chain = vec![];
168 172 while let Some(base_rev) = entry.base_rev {
169 173 delta_chain.push(entry);
170 entry =
171 self.get_entry(base_rev).or(Err(RevlogError::Corrupted))?;
174 entry = self
175 .get_entry(base_rev)
176 .map_err(|_| RevlogError::corrupted())?;
172 177 }
173 178
174 179 // TODO do not look twice in the index
@@ -191,7 +196,7 b' impl Revlog {'
191 196 ) {
192 197 Ok(data)
193 198 } else {
194 Err(RevlogError::Corrupted)
199 Err(RevlogError::corrupted())
195 200 }
196 201 }
197 202
@@ -301,7 +306,8 b" impl<'a> RevlogEntry<'a> {"
301 306 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
302 307 // zstd data.
303 308 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
304 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
309 // A proper new format should have had a repo/store requirement.
310 _format_type => Err(RevlogError::corrupted()),
305 311 }
306 312 }
307 313
@@ -311,13 +317,13 b" impl<'a> RevlogEntry<'a> {"
311 317 let mut buf = Vec::with_capacity(self.compressed_len);
312 318 decoder
313 319 .read_to_end(&mut buf)
314 .or(Err(RevlogError::Corrupted))?;
320 .map_err(|_| RevlogError::corrupted())?;
315 321 Ok(buf)
316 322 } else {
317 323 let mut buf = vec![0; self.uncompressed_len];
318 324 decoder
319 325 .read_exact(&mut buf)
320 .or(Err(RevlogError::Corrupted))?;
326 .map_err(|_| RevlogError::corrupted())?;
321 327 Ok(buf)
322 328 }
323 329 }
@@ -326,14 +332,14 b" impl<'a> RevlogEntry<'a> {"
326 332 if self.is_delta() {
327 333 let mut buf = Vec::with_capacity(self.compressed_len);
328 334 zstd::stream::copy_decode(self.bytes, &mut buf)
329 .or(Err(RevlogError::Corrupted))?;
335 .map_err(|_| RevlogError::corrupted())?;
330 336 Ok(buf)
331 337 } else {
332 338 let mut buf = vec![0; self.uncompressed_len];
333 339 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
334 .or(Err(RevlogError::Corrupted))?;
340 .map_err(|_| RevlogError::corrupted())?;
335 341 if len != self.uncompressed_len {
336 Err(RevlogError::Corrupted)
342 Err(RevlogError::corrupted())
337 343 } else {
338 344 Ok(buf)
339 345 }
@@ -103,9 +103,6 b' impl From<FindRootError> for CommandErro'
103 103 impl From<(RevlogError, &str)> for CommandError {
104 104 fn from((err, rev): (RevlogError, &str)) -> CommandError {
105 105 match err {
106 RevlogError::IoError(err) => CommandError::Abort(Some(
107 utf8_to_local(&format!("abort: {}\n", err)).into(),
108 )),
109 106 RevlogError::InvalidRevision => CommandError::Abort(Some(
110 107 utf8_to_local(&format!(
111 108 "abort: invalid revision identifier {}\n",
@@ -120,27 +117,7 b' impl From<(RevlogError, &str)> for Comma'
120 117 ))
121 118 .into(),
122 119 )),
123 RevlogError::UnsuportedVersion(version) => {
124 CommandError::Abort(Some(
125 utf8_to_local(&format!(
126 "abort: unsupported revlog version {}\n",
127 version
128 ))
129 .into(),
130 ))
131 }
132 RevlogError::Corrupted => {
133 CommandError::Abort(Some("abort: corrupted revlog\n".into()))
134 }
135 RevlogError::UnknowDataFormat(format) => {
136 CommandError::Abort(Some(
137 utf8_to_local(&format!(
138 "abort: unknow revlog dataformat {:?}\n",
139 format
140 ))
141 .into(),
142 ))
143 }
120 RevlogError::Other(err) => CommandError::Other(err),
144 121 }
145 122 }
146 123 }
General Comments 0
You need to be logged in to leave comments. Login now