1use crate::{api::*, attribute::NtfsAttribute, mft::Mft};
6
7pub struct NtfsFile<'a> {
8 pub number: u64,
9 pub header: &'a NtfsFileRecordHeader,
10 pub data: &'a [u8],
11}
12
13impl<'a> NtfsFile<'a> {
14 pub fn new(number: u64, data: &'a [u8]) -> Self {
15 unsafe {
16 let header = &*(data.as_ptr() as *const NtfsFileRecordHeader);
17 NtfsFile {
18 number,
19 header,
20 data,
21 }
22 }
23 }
24
25 pub fn number(&self) -> u64 {
26 self.number
27 }
28
29 pub fn reference_number(&self) -> u64 {
30 let seq = self.header.sequence_value as u64;
31 (seq << 48) | (self.number & 0x0000_FFFF_FFFF_FFFF)
32 }
33
34 pub fn get_file_id(&self) -> FileId {
35 FileId::Normal(self.reference_number())
36 }
37
38 pub fn is_valid(data: &[u8]) -> bool {
39 let header = unsafe { &*(data.as_ptr() as *const NtfsFileRecordHeader) };
40 if &header.signature != FILE_RECORD_SIGNATURE {
41 return false;
42 }
43
44 if header.update_sequence_length == 0 {
45 return false;
46 }
47
48 let usa_end =
49 header.update_sequence_offset as usize + header.update_sequence_length as usize * 2;
50
51 let usa_num = header.update_sequence_length as usize - 1;
52 let sector_num = data.len() / SECTOR_SIZE;
53
54 if usa_end > data.len() || usa_num > sector_num {
55 return false;
56 }
57
58 true
59 }
60
61 pub fn attributes<F>(&self, mut f: F)
62 where
63 F: FnMut(&NtfsAttribute),
64 {
65 let mut offset = self.header.attributes_offset as usize;
66 loop {
67 if offset >= self.header.used_size as usize {
68 break;
69 }
70
71 let att_type = u32::from_le_bytes(self.data[offset..offset + 4].try_into().unwrap());
72 if att_type == NtfsAttributeType::End as u32 {
73 break;
74 }
75
76 let att = NtfsAttribute::new(&self.data[offset..]);
77 f(&att);
78
79 offset += att.header.length as usize;
80 }
81 }
82
83 pub fn get_attribute(&self, attribute_type: NtfsAttributeType) -> Option<NtfsAttribute<'_>> {
84 let mut offset = self.header.attributes_offset as usize;
85
86 loop {
87 if offset >= self.header.used_size as usize {
88 break;
89 }
90 let att = NtfsAttribute::new(&self.data[offset..]);
91 if att.header.type_id == NtfsAttributeType::End as u32 {
92 break;
93 }
94 if att.header.type_id == attribute_type as u32 {
95 return Some(NtfsAttribute::new(&self.data[offset..]));
96 }
97
98 offset += att.header.length as usize;
99 }
100 None
101 }
102
103 pub fn get_best_file_name(&self, mft: &Mft) -> Option<NtfsFileName> {
104 let mut offset = self.header.attributes_offset as usize;
105 let mut best = None;
106
107 loop {
108 if offset >= self.header.used_size as usize {
109 break;
110 }
111 let att = NtfsAttribute::new(&self.data[offset..]);
112 if att.header.type_id == NtfsAttributeType::End as u32 {
113 break;
114 }
115
116 if att.header.type_id == NtfsAttributeType::FileName as u32 {
117 let name = att.as_name();
118
119 if !name.is_reparse_point() {
121 if name.header.namespace == NtfsFileNamespace::Win32 as u8
122 || name.header.namespace == NtfsFileNamespace::Win32AndDos as u8
123 {
124 return Some(*name);
125 } else {
126 best = Some(*name);
127 }
128 }
129 }
130
131 if att.header.type_id == NtfsAttributeType::AttributeList as u32 {
132 let header = unsafe {
133 &*(self.data[offset..].as_ptr() as *const NtfsResidentAttributeHeader)
134 };
135
136 let att_data = &self.data[offset + header.value_offset as usize..];
137
138 let mut att_offset = 0;
139 while att_offset < header.value_length as usize {
140 let entry = unsafe {
141 &*(att_data[att_offset..].as_ptr() as *const NtfsAttributeListEntry)
142 };
143 if entry.type_id == NtfsAttributeType::FileName as u32 {
144 let rec = mft.get_record(entry.reference())?;
145 let att = rec.get_attribute(NtfsAttributeType::FileName)?;
146 let name = att.as_name();
147
148 if !name.is_reparse_point() {
150 if name.header.namespace == NtfsFileNamespace::Win32 as u8
151 || name.header.namespace == NtfsFileNamespace::Win32AndDos as u8
152 {
153 return Some(*name);
154 } else {
155 best = Some(*name);
156 break;
157 }
158 }
159 }
160
161 att_offset += entry.length as usize;
162 att_offset += (8 - (att_offset % 8)) % 8;
164 }
165 }
166
167 offset += att.header.length as usize;
168 }
169
170 best
171 }
172
173 pub fn read_data(&self) -> Option<&[u8]> {
175 if let Some(att) = self.get_attribute(NtfsAttributeType::Data) {
176 assert!(att.header.is_non_resident != 0);
177 return Some(att.as_resident_data());
178 }
179 None
180 }
181
182 pub fn is_used(&self) -> bool {
183 self.header.flags & NtfsFileFlags::InUse as u16 != 0
184 }
185
186 pub fn is_directory(&self) -> bool {
187 self.header.flags & NtfsFileFlags::IsDirectory as u16 != 0
188 }
189}