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( |
|
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, |
|
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