1use crate::error::{NtfsError, Result};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct Run {
19 pub length: u64,
21 pub lcn: Option<u64>,
23}
24
25const MAX_RUNS: usize = 1 << 20;
27
28pub fn decode(bytes: &[u8]) -> Result<Vec<Run>> {
35 let mut runs = Vec::new();
36 let mut pos = 0usize;
37 let mut current_lcn: i64 = 0;
38
39 for _ in 0..MAX_RUNS {
40 let Some(&header) = bytes.get(pos) else {
41 break; };
43 if header == 0 {
44 break; }
46 pos += 1;
47
48 let len_bytes = (header & 0x0F) as usize;
49 let off_bytes = (header >> 4) as usize;
50 if len_bytes == 0 || len_bytes > 8 {
51 return Err(NtfsError::BadRunlist("invalid run length field width"));
52 }
53 if off_bytes > 8 {
54 return Err(NtfsError::BadRunlist("invalid run offset field width"));
55 }
56
57 let length = read_uint(bytes, pos, len_bytes)
58 .ok_or(NtfsError::BadRunlist("length runs past end"))?;
59 pos += len_bytes;
60 if length == 0 {
61 return Err(NtfsError::BadRunlist("zero-length run"));
62 }
63
64 let lcn = if off_bytes == 0 {
65 None } else {
67 let delta = read_sint(bytes, pos, off_bytes)
68 .ok_or(NtfsError::BadRunlist("offset runs past end"))?;
69 pos += off_bytes;
70 current_lcn = current_lcn
71 .checked_add(delta)
72 .ok_or(NtfsError::BadRunlist("LCN overflow"))?;
73 if current_lcn < 0 {
74 return Err(NtfsError::BadRunlist("negative LCN"));
75 }
76 Some(current_lcn as u64)
77 };
78
79 runs.push(Run { length, lcn });
80 }
81
82 Ok(runs)
83}
84
85pub fn total_clusters(runs: &[Run]) -> Result<u64> {
91 let mut total = 0u64;
92 for r in runs {
93 total = total
94 .checked_add(r.length)
95 .ok_or(NtfsError::BadRunlist("total cluster count overflow"))?;
96 }
97 Ok(total)
98}
99
100fn read_uint(bytes: &[u8], pos: usize, n: usize) -> Option<u64> {
102 let slice = bytes.get(pos..pos.checked_add(n)?)?;
103 let mut v = 0u64;
104 for (i, &b) in slice.iter().enumerate() {
105 v |= u64::from(b) << (8 * i);
106 }
107 Some(v)
108}
109
110fn read_sint(bytes: &[u8], pos: usize, n: usize) -> Option<i64> {
113 let slice = bytes.get(pos..pos.checked_add(n)?)?;
114 let mut v = 0i64;
115 for (i, &b) in slice.iter().enumerate() {
116 v |= i64::from(b) << (8 * i);
117 }
118 let bits = n * 8;
119 if bits < 64 {
120 let sign_bit = 1i64 << (bits - 1);
121 if v & sign_bit != 0 {
122 v |= -(1i64 << bits); }
124 }
125 Some(v)
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn decodes_single_run() {
134 let runs = decode(&[0x21, 0x08, 0x00, 0x01, 0x00]).unwrap();
136 assert_eq!(
137 runs,
138 vec![Run {
139 length: 8,
140 lcn: Some(256)
141 }]
142 );
143 }
144
145 #[test]
146 fn decodes_multiple_runs_with_delta() {
147 let bytes = [0x21, 0x04, 0x00, 0x01, 0x21, 0x04, 0x00, 0x01, 0x00];
149 let runs = decode(&bytes).unwrap();
150 assert_eq!(
151 runs,
152 vec![
153 Run {
154 length: 4,
155 lcn: Some(256)
156 },
157 Run {
158 length: 4,
159 lcn: Some(512)
160 },
161 ]
162 );
163 }
164
165 #[test]
166 fn decodes_sparse_run() {
167 let runs = decode(&[0x01, 0x05, 0x00]).unwrap();
169 assert_eq!(
170 runs,
171 vec![Run {
172 length: 5,
173 lcn: None
174 }]
175 );
176 }
177
178 #[test]
179 fn decodes_negative_delta() {
180 let bytes = [0x21, 0x04, 0x00, 0x02, 0x11, 0x04, 0xFE, 0x00];
182 let runs = decode(&bytes).unwrap();
183 assert_eq!(runs[1].lcn, Some(510));
184 }
185
186 #[test]
187 fn sparse_run_does_not_shift_following_lcn() {
188 let bytes = [
190 0x21, 0x04, 0x00, 0x01, 0x01, 0x02, 0x11, 0x04, 0x01, 0x00,
194 ];
195 let runs = decode(&bytes).unwrap();
196 assert_eq!(runs[0].lcn, Some(256));
197 assert_eq!(runs[1].lcn, None);
198 assert_eq!(runs[2].lcn, Some(257));
199 }
200
201 #[test]
202 fn zero_header_ends_list() {
203 assert!(decode(&[0x00]).unwrap().is_empty());
204 assert!(decode(&[]).unwrap().is_empty());
205 }
206
207 #[test]
208 fn total_clusters_sums_lengths() {
209 let runs = vec![
210 Run {
211 length: 4,
212 lcn: Some(0),
213 },
214 Run {
215 length: 6,
216 lcn: None,
217 },
218 ];
219 assert_eq!(total_clusters(&runs).unwrap(), 10);
220 }
221
222 #[test]
225 fn rejects_length_field_too_large() {
226 assert!(matches!(decode(&[0x09]), Err(NtfsError::BadRunlist(_))));
228 }
229
230 #[test]
231 fn rejects_offset_field_too_large() {
232 assert!(matches!(
234 decode(&[0x91, 0x05]),
235 Err(NtfsError::BadRunlist(_))
236 ));
237 }
238
239 #[test]
240 fn decodes_full_width_eight_byte_offset() {
241 let rl = [0x81u8, 0x05, 0x10, 0, 0, 0, 0, 0, 0, 0];
243 let runs = decode(&rl).unwrap();
244 assert_eq!(runs[0].lcn, Some(0x10));
245 }
246
247 #[test]
248 fn rejects_truncated_run() {
249 assert!(matches!(
251 decode(&[0x21, 0x08]),
252 Err(NtfsError::BadRunlist(_))
253 ));
254 }
255
256 #[test]
257 fn rejects_zero_length_run() {
258 assert!(matches!(
259 decode(&[0x21, 0x00, 0x00, 0x01]),
260 Err(NtfsError::BadRunlist(_))
261 ));
262 }
263
264 #[test]
265 fn rejects_negative_lcn() {
266 assert!(matches!(
268 decode(&[0x11, 0x04, 0xFF]), Err(NtfsError::BadRunlist(_))
270 ));
271 }
272}