1use std::mem::size_of;
6
7use crate::{
8 api::*,
9 errors::{NtfsReaderError, NtfsReaderResult},
10 volume::Volume,
11};
12
13#[derive(Debug, Clone)]
14pub enum DataRun {
15 Data { lcn: u64, length: u64 },
16 Sparse { length: u64 },
17}
18
19pub struct NtfsAttribute<'a> {
20 data: &'a [u8],
21 pub header: &'a NtfsAttributeHeader,
22 length: usize,
23}
24
25impl<'a> NtfsAttribute<'a> {
26 pub fn new(data: &'a [u8]) -> Option<Self> {
27 if data.len() < size_of::<NtfsAttributeHeader>() {
28 return None;
29 }
30
31 let header = unsafe { &*(data.as_ptr() as *const NtfsAttributeHeader) };
32 let length = header.length as usize;
33 if length == 0 || length > data.len() {
34 return None;
35 }
36
37 Some(Self {
38 data,
39 header,
40 length,
41 })
42 }
43
44 pub fn len(&self) -> usize {
45 self.length
46 }
47
48 pub fn is_empty(&self) -> bool {
49 self.length == 0
50 }
51
52 pub fn data(&self) -> &'a [u8] {
53 &self.data[..self.length]
54 }
55
56 pub fn resident_header(&self) -> Option<&'a NtfsResidentAttributeHeader> {
57 if self.header.is_non_resident != 0 {
58 return None;
59 }
60 if self.length < size_of::<NtfsResidentAttributeHeader>() {
61 return None;
62 }
63 Some(unsafe { &*(self.data.as_ptr() as *const NtfsResidentAttributeHeader) })
64 }
65
66 pub fn nonresident_header(&self) -> Option<&'a NtfsNonResidentAttributeHeader> {
67 if self.header.is_non_resident == 0 {
68 return None;
69 }
70 if self.length < size_of::<NtfsNonResidentAttributeHeader>() {
71 return None;
72 }
73 Some(unsafe { &*(self.data.as_ptr() as *const NtfsNonResidentAttributeHeader) })
74 }
75
76 pub fn get_resident(&self) -> Option<&'a [u8]> {
77 let header = self.resident_header()?;
78 let start = header.value_offset as usize;
79 let value_length = header.value_length as usize;
80 let end = start.checked_add(value_length)?;
81 if end > self.data().len() {
82 return None;
83 }
84 Some(&self.data()[start..end])
85 }
86
87 pub fn as_standard_info(&self) -> Option<&'a NtfsStandardInformation> {
88 if self.header.type_id != NtfsAttributeType::StandardInformation as u32 {
89 return None;
90 }
91 let slice = self.get_resident()?;
92 if slice.len() < size_of::<NtfsStandardInformation>() {
93 return None;
94 }
95 Some(unsafe { &*(slice.as_ptr() as *const NtfsStandardInformation) })
96 }
97
98 pub fn as_name(&self) -> Option<NtfsFileName> {
99 if self.header.type_id != NtfsAttributeType::FileName as u32 {
100 return None;
101 }
102 let slice = self.get_resident()?;
103 if slice.len() < size_of::<NtfsFileNameHeader>() {
104 return None;
105 }
106
107 let header = unsafe { *(slice.as_ptr() as *const NtfsFileNameHeader) };
108 let name_bytes = (header.name_length as usize).checked_mul(2)?;
109 let header_size = size_of::<NtfsFileNameHeader>();
110 let end = header_size.checked_add(name_bytes)?;
111 if end > slice.len() {
112 return None;
113 }
114
115 let char_count = header.name_length as usize;
116 if char_count > 255 {
117 return None;
118 }
119
120 let mut data = [0u16; 255];
121 if char_count > 0 {
122 let bytes = &slice[header_size..end];
123 for (i, slot) in data.iter_mut().take(char_count).enumerate() {
124 let byte_index = i * 2;
125 *slot = u16::from_le_bytes([bytes[byte_index], bytes[byte_index + 1]]);
126 }
127 }
128
129 Some(NtfsFileName { header, data })
130 }
131
132 pub fn as_resident_data(&self) -> Option<&'a [u8]> {
133 if self.header.type_id != NtfsAttributeType::Data as u32 {
134 return None;
135 }
136 self.get_resident()
137 }
138
139 pub fn get_nonresident_data_runs(
140 &self,
141 volume: &Volume,
142 ) -> NtfsReaderResult<(u64, Vec<DataRun>)> {
143 let header_nonres = self
144 .nonresident_header()
145 .ok_or(NtfsReaderError::InvalidDataRun {
146 details: "attribute is resident",
147 })?;
148
149 let mut out = Vec::new();
150
151 let total_size = header_nonres.data_size;
152 if total_size == 0 {
153 return Ok((total_size, out));
154 }
155
156 let start = header_nonres.data_runs_offset as usize;
157 if start > self.length {
158 return Err(NtfsReaderError::InvalidDataRun {
159 details: "data runs offset outside attribute",
160 });
161 }
162 let runs_data = &self.data()[start..];
163
164 let cluster_size = volume.cluster_size;
165 const BUF_SIZE: usize = 8;
166
167 let mut cursor = 0usize;
168 let mut prev_lcn = 0i128;
169 let mut total_run_length = 0u64;
170 loop {
171 if cursor >= runs_data.len() {
172 return Err(NtfsReaderError::InvalidDataRun {
173 details: "unterminated data run sequence",
174 });
175 }
176 if runs_data[cursor] == 0 {
177 break;
178 }
179
180 let descriptor = runs_data[cursor];
181 let cluster_count_b = (descriptor & 0x0f) as usize;
182 let cluster_offset_b = ((descriptor & 0xf0) >> 4) as usize;
183
184 if cluster_count_b == 0 || cluster_count_b > BUF_SIZE {
185 return Err(NtfsReaderError::InvalidDataRun {
186 details: "invalid cluster count field",
187 });
188 }
189 if cluster_offset_b > BUF_SIZE {
190 return Err(NtfsReaderError::InvalidDataRun {
191 details: "invalid cluster offset field",
192 });
193 }
194
195 cursor += 1;
196
197 if cursor + cluster_count_b > runs_data.len() {
198 return Err(NtfsReaderError::InvalidDataRun {
199 details: "unexpected end of run-length data",
200 });
201 }
202 let mut count_buf = [0u8; BUF_SIZE];
203 count_buf[..cluster_count_b]
204 .copy_from_slice(&runs_data[cursor..cursor + cluster_count_b]);
205 let cluster_count = u64::from_le_bytes(count_buf);
206 if cluster_count == 0 {
207 return Err(NtfsReaderError::InvalidDataRun {
208 details: "cluster count is zero",
209 });
210 }
211 cursor += cluster_count_b;
212
213 let run_length_bytes =
214 cluster_count
215 .checked_mul(cluster_size)
216 .ok_or(NtfsReaderError::InvalidDataRun {
217 details: "run length overflow",
218 })?;
219 total_run_length = total_run_length.checked_add(run_length_bytes).ok_or(
220 NtfsReaderError::InvalidDataRun {
221 details: "total run length overflow",
222 },
223 )?;
224
225 let lcn = if cluster_offset_b == 0 {
226 None
227 } else {
228 if cursor + cluster_offset_b > runs_data.len() {
229 return Err(NtfsReaderError::InvalidDataRun {
230 details: "unexpected end of run-offset data",
231 });
232 }
233 let mut offset_buf = [0u8; BUF_SIZE];
234 offset_buf[..cluster_offset_b]
235 .copy_from_slice(&runs_data[cursor..cursor + cluster_offset_b]);
236 let raw = i64::from_le_bytes(offset_buf);
237 let empty_bits = (BUF_SIZE - cluster_offset_b) * 8;
238 let cluster_offset = (raw << empty_bits) >> empty_bits;
239 cursor += cluster_offset_b;
240
241 let delta = (cluster_offset as i128)
242 .checked_mul(cluster_size as i128)
243 .ok_or(NtfsReaderError::InvalidDataRun {
244 details: "relative offset overflow",
245 })?;
246 let start = prev_lcn
247 .checked_add(delta)
248 .ok_or(NtfsReaderError::InvalidDataRun {
249 details: "relative offset overflow",
250 })?;
251 if start < 0 {
252 return Err(NtfsReaderError::InvalidDataRun {
253 details: "relative offset underflow",
254 });
255 }
256 prev_lcn = start;
257 Some(start as u64)
258 };
259
260 match lcn {
261 Some(start) => out.push(DataRun::Data {
262 lcn: start,
263 length: run_length_bytes,
264 }),
265 None => out.push(DataRun::Sparse {
266 length: run_length_bytes,
267 }),
268 }
269 }
270
271 if total_size > 0 && out.is_empty() {
272 return Err(NtfsReaderError::InvalidDataRun {
273 details: "attribute has size but no runs",
274 });
275 }
276
277 if total_run_length < total_size {
278 return Err(NtfsReaderError::InvalidDataRun {
279 details: "data runs shorter than declared size",
280 });
281 }
282
283 Ok((total_size, out))
284 }
285}