##// END OF EJS Templates
dirstate: better error messages when dirstate is corrupted...
Arseniy Alekseyev -
r51429:475c170b default
parent child Browse files
Show More
@@ -1,135 +1,150 b''
1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 use crate::errors::HgError;
6 use crate::errors::HgError;
7 use crate::utils::hg_path::HgPath;
7 use crate::utils::hg_path::HgPath;
8 use crate::{dirstate::EntryState, DirstateEntry, DirstateParents};
8 use crate::{dirstate::EntryState, DirstateEntry, DirstateParents};
9 use byteorder::{BigEndian, WriteBytesExt};
9 use byteorder::{BigEndian, WriteBytesExt};
10 use bytes_cast::{unaligned, BytesCast};
10 use bytes_cast::{unaligned, BytesCast};
11
11
12 /// Parents are stored in the dirstate as byte hashes.
12 /// Parents are stored in the dirstate as byte hashes.
13 pub const PARENT_SIZE: usize = 20;
13 pub const PARENT_SIZE: usize = 20;
14 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
14 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
15 const MIN_ENTRY_SIZE: usize = 17;
15 const MIN_ENTRY_SIZE: usize = 17;
16
16
17 type ParseResult<'a> = (
17 type ParseResult<'a> = (
18 &'a DirstateParents,
18 &'a DirstateParents,
19 Vec<(&'a HgPath, DirstateEntry)>,
19 Vec<(&'a HgPath, DirstateEntry)>,
20 Vec<(&'a HgPath, &'a HgPath)>,
20 Vec<(&'a HgPath, &'a HgPath)>,
21 );
21 );
22
22
23 pub fn parse_dirstate_parents(
23 pub fn parse_dirstate_parents(
24 contents: &[u8],
24 contents: &[u8],
25 ) -> Result<&DirstateParents, HgError> {
25 ) -> Result<&DirstateParents, HgError> {
26 let (parents, _rest) = DirstateParents::from_bytes(contents)
26 let (parents, _rest) = DirstateParents::from_bytes(contents)
27 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
27 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
28 Ok(parents)
28 Ok(parents)
29 }
29 }
30
30
31 #[logging_timer::time("trace")]
31 #[logging_timer::time("trace")]
32 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
32 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
33 let mut copies = Vec::new();
33 let mut copies = Vec::new();
34 let mut entries = Vec::new();
34 let mut entries = Vec::new();
35 let parents =
35 let parents =
36 parse_dirstate_entries(contents, |path, entry, copy_source| {
36 parse_dirstate_entries(contents, |path, entry, copy_source| {
37 if let Some(source) = copy_source {
37 if let Some(source) = copy_source {
38 copies.push((path, source));
38 copies.push((path, source));
39 }
39 }
40 entries.push((path, *entry));
40 entries.push((path, *entry));
41 Ok(())
41 Ok(())
42 })?;
42 })?;
43 Ok((parents, entries, copies))
43 Ok((parents, entries, copies))
44 }
44 }
45
45
46 #[derive(BytesCast)]
46 #[derive(BytesCast)]
47 #[repr(C)]
47 #[repr(C)]
48 struct RawEntry {
48 struct RawEntry {
49 state: u8,
49 state: u8,
50 mode: unaligned::I32Be,
50 mode: unaligned::I32Be,
51 size: unaligned::I32Be,
51 size: unaligned::I32Be,
52 mtime: unaligned::I32Be,
52 mtime: unaligned::I32Be,
53 length: unaligned::I32Be,
53 length: unaligned::I32Be,
54 }
54 }
55
55
56 pub fn parse_dirstate_entries<'a>(
56 pub fn parse_dirstate_entries<'a>(
57 mut contents: &'a [u8],
57 mut contents: &'a [u8],
58 mut each_entry: impl FnMut(
58 mut each_entry: impl FnMut(
59 &'a HgPath,
59 &'a HgPath,
60 &DirstateEntry,
60 &DirstateEntry,
61 Option<&'a HgPath>,
61 Option<&'a HgPath>,
62 ) -> Result<(), HgError>,
62 ) -> Result<(), HgError>,
63 ) -> Result<&'a DirstateParents, HgError> {
63 ) -> Result<&'a DirstateParents, HgError> {
64 let (parents, rest) = DirstateParents::from_bytes(contents)
64 let mut entry_idx = 0;
65 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
65 let original_len = contents.len();
66 let (parents, rest) =
67 DirstateParents::from_bytes(contents).map_err(|_| {
68 HgError::corrupted(format!(
69 "Too little data for dirstate: {} bytes.",
70 original_len
71 ))
72 })?;
66 contents = rest;
73 contents = rest;
67 while !contents.is_empty() {
74 while !contents.is_empty() {
68 let (raw_entry, rest) = RawEntry::from_bytes(contents)
75 let (raw_entry, rest) = RawEntry::from_bytes(contents)
69 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
76 .map_err(|_| HgError::corrupted(format!(
77 "dirstate corrupted: ran out of bytes at entry header {}, offset {}.",
78 entry_idx, original_len-contents.len())))?;
70
79
71 let entry = DirstateEntry::from_v1_data(
80 let entry = DirstateEntry::from_v1_data(
72 EntryState::try_from(raw_entry.state)?,
81 EntryState::try_from(raw_entry.state)?,
73 raw_entry.mode.get(),
82 raw_entry.mode.get(),
74 raw_entry.size.get(),
83 raw_entry.size.get(),
75 raw_entry.mtime.get(),
84 raw_entry.mtime.get(),
76 );
85 );
86 let filename_len = raw_entry.length.get() as usize;
77 let (paths, rest) =
87 let (paths, rest) =
78 u8::slice_from_bytes(rest, raw_entry.length.get() as usize)
88 u8::slice_from_bytes(rest, filename_len)
79 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
89 .map_err(|_|
90 HgError::corrupted(format!(
91 "dirstate corrupted: ran out of bytes at entry {}, offset {} (expected {} bytes).",
92 entry_idx, original_len-contents.len(), filename_len))
93 )?;
80
94
81 // `paths` is either a single path, or two paths separated by a NULL
95 // `paths` is either a single path, or two paths separated by a NULL
82 // byte
96 // byte
83 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
97 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
84 let path = HgPath::new(
98 let path = HgPath::new(
85 iter.next().expect("splitn always yields at least one item"),
99 iter.next().expect("splitn always yields at least one item"),
86 );
100 );
87 let copy_source = iter.next().map(HgPath::new);
101 let copy_source = iter.next().map(HgPath::new);
88 each_entry(path, &entry, copy_source)?;
102 each_entry(path, &entry, copy_source)?;
89
103
104 entry_idx += 1;
90 contents = rest;
105 contents = rest;
91 }
106 }
92 Ok(parents)
107 Ok(parents)
93 }
108 }
94
109
95 fn packed_filename_and_copy_source_size(
110 fn packed_filename_and_copy_source_size(
96 filename: &HgPath,
111 filename: &HgPath,
97 copy_source: Option<&HgPath>,
112 copy_source: Option<&HgPath>,
98 ) -> usize {
113 ) -> usize {
99 filename.len()
114 filename.len()
100 + if let Some(source) = copy_source {
115 + if let Some(source) = copy_source {
101 b"\0".len() + source.len()
116 b"\0".len() + source.len()
102 } else {
117 } else {
103 0
118 0
104 }
119 }
105 }
120 }
106
121
107 pub fn packed_entry_size(
122 pub fn packed_entry_size(
108 filename: &HgPath,
123 filename: &HgPath,
109 copy_source: Option<&HgPath>,
124 copy_source: Option<&HgPath>,
110 ) -> usize {
125 ) -> usize {
111 MIN_ENTRY_SIZE
126 MIN_ENTRY_SIZE
112 + packed_filename_and_copy_source_size(filename, copy_source)
127 + packed_filename_and_copy_source_size(filename, copy_source)
113 }
128 }
114
129
115 pub fn pack_entry(
130 pub fn pack_entry(
116 filename: &HgPath,
131 filename: &HgPath,
117 entry: &DirstateEntry,
132 entry: &DirstateEntry,
118 copy_source: Option<&HgPath>,
133 copy_source: Option<&HgPath>,
119 packed: &mut Vec<u8>,
134 packed: &mut Vec<u8>,
120 ) {
135 ) {
121 let length = packed_filename_and_copy_source_size(filename, copy_source);
136 let length = packed_filename_and_copy_source_size(filename, copy_source);
122 let (state, mode, size, mtime) = entry.v1_data();
137 let (state, mode, size, mtime) = entry.v1_data();
123
138
124 // Unwrapping because `impl std::io::Write for Vec<u8>` never errors
139 // Unwrapping because `impl std::io::Write for Vec<u8>` never errors
125 packed.write_u8(state).unwrap();
140 packed.write_u8(state).unwrap();
126 packed.write_i32::<BigEndian>(mode).unwrap();
141 packed.write_i32::<BigEndian>(mode).unwrap();
127 packed.write_i32::<BigEndian>(size).unwrap();
142 packed.write_i32::<BigEndian>(size).unwrap();
128 packed.write_i32::<BigEndian>(mtime).unwrap();
143 packed.write_i32::<BigEndian>(mtime).unwrap();
129 packed.write_i32::<BigEndian>(length as i32).unwrap();
144 packed.write_i32::<BigEndian>(length as i32).unwrap();
130 packed.extend(filename.as_bytes());
145 packed.extend(filename.as_bytes());
131 if let Some(source) = copy_source {
146 if let Some(source) = copy_source {
132 packed.push(b'\0');
147 packed.push(b'\0');
133 packed.extend(source.as_bytes());
148 packed.extend(source.as_bytes());
134 }
149 }
135 }
150 }
General Comments 0
You need to be logged in to leave comments. Login now