ntfs_reader/
attribute.rs

1// Copyright (c) 2022, Matteo Bernacchia <dev@kikijiki.com>. All rights reserved.
2// This project is dual licensed under the Apache License 2.0 and the MIT license.
3// See the LICENSE files in the project root for details.
4
5use 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}