1use binrw::io::{Read, Seek, SeekFrom};
5use binrw::BinReaderExt;
6
7use crate::attribute::NtfsAttributeType;
8use crate::boot_sector::BootSector;
9use crate::error::{NtfsError, Result};
10use crate::file::{KnownNtfsFileRecordNumber, NtfsFile};
11use crate::structured_values::{NtfsVolumeInformation, NtfsVolumeName};
12use crate::traits::NtfsReadSeek;
13use crate::types::NtfsPosition;
14use crate::upcase_table::UpcaseTable;
15
16#[derive(Debug)]
18pub struct Ntfs {
19 cluster_size: u32,
21 sector_size: u16,
23 size: u64,
25 mft_position: NtfsPosition,
27 file_record_size: u32,
29 serial_number: u64,
31 upcase_table: Option<UpcaseTable>,
33}
34
35impl Ntfs {
36 #[allow(clippy::seek_to_start_instead_of_rewind)]
41 pub fn new<T>(fs: &mut T) -> Result<Self>
42 where
43 T: Read + Seek,
44 {
45 fs.seek(SeekFrom::Start(0))?;
47 let boot_sector = fs.read_le::<BootSector>()?;
48 boot_sector.validate()?;
49
50 let bpb = boot_sector.bpb();
51 let cluster_size = bpb.cluster_size()?;
52 let sector_size = bpb.sector_size()?;
53 let total_sectors = bpb.total_sectors();
54 let size = total_sectors
55 .checked_mul(sector_size as u64)
56 .ok_or(NtfsError::TotalSectorsTooBig { total_sectors })?;
57 let mft_position = NtfsPosition::none();
58 let file_record_size = bpb.file_record_size()?;
59 let serial_number = bpb.serial_number();
60 let upcase_table = None;
61
62 let mut ntfs = Self {
63 cluster_size,
64 sector_size,
65 size,
66 mft_position,
67 file_record_size,
68 serial_number,
69 upcase_table,
70 };
71 ntfs.mft_position = bpb.mft_lcn()?.position(&ntfs)?;
72
73 Ok(ntfs)
74 }
75
76 pub fn cluster_size(&self) -> u32 {
78 self.cluster_size
79 }
80
81 pub fn file<'n, T>(&'n self, fs: &mut T, file_record_number: u64) -> Result<NtfsFile<'n>>
86 where
87 T: Read + Seek,
88 {
89 let offset = file_record_number
90 .checked_mul(self.file_record_size as u64)
91 .ok_or(NtfsError::InvalidFileRecordNumber { file_record_number })?;
92
93 let mft = NtfsFile::new(self, fs, self.mft_position.value().unwrap(), 0)?;
99 let mft_data_attribute =
100 mft.find_resident_attribute(NtfsAttributeType::Data, None, None)?;
101 let mut mft_data_value = mft_data_attribute.value(fs)?;
102
103 mft_data_value.seek(fs, SeekFrom::Start(offset))?;
104 let position = mft_data_value
105 .data_position()
106 .value()
107 .ok_or(NtfsError::InvalidFileRecordNumber { file_record_number })?;
108
109 NtfsFile::new(self, fs, position, file_record_number)
110 }
111
112 pub fn file_record_size(&self) -> u32 {
114 self.file_record_size
115 }
116
117 pub fn mft_position(&self) -> NtfsPosition {
121 self.mft_position
122 }
123
124 pub fn read_upcase_table<T>(&mut self, fs: &mut T) -> Result<()>
129 where
130 T: Read + Seek,
131 {
132 let upcase_table = UpcaseTable::read(self, fs)?;
133 self.upcase_table = Some(upcase_table);
134 Ok(())
135 }
136
137 pub fn root_directory<'n, T>(&'n self, fs: &mut T) -> Result<NtfsFile<'n>>
139 where
140 T: Read + Seek,
141 {
142 self.file(fs, KnownNtfsFileRecordNumber::RootDirectory as u64)
143 }
144
145 pub fn sector_size(&self) -> u16 {
147 self.sector_size
148 }
149
150 pub fn serial_number(&self) -> u64 {
152 self.serial_number
153 }
154
155 pub fn size(&self) -> u64 {
157 self.size
158 }
159
160 pub(crate) fn upcase_table(&self) -> &UpcaseTable {
166 self.upcase_table
167 .as_ref()
168 .expect("You need to call read_upcase_table first")
169 }
170
171 pub fn volume_info<T>(&self, fs: &mut T) -> Result<NtfsVolumeInformation>
174 where
175 T: Read + Seek,
176 {
177 let volume_file = self.file(fs, KnownNtfsFileRecordNumber::Volume as u64)?;
178 volume_file.find_resident_attribute_structured_value::<NtfsVolumeInformation>(None)
179 }
180
181 pub fn volume_name<T>(&self, fs: &mut T) -> Option<Result<NtfsVolumeName>>
187 where
188 T: Read + Seek,
189 {
190 let volume_file = iter_try!(self.file(fs, KnownNtfsFileRecordNumber::Volume as u64));
191
192 match volume_file.find_resident_attribute_structured_value::<NtfsVolumeName>(None) {
193 Ok(volume_name) => Some(Ok(volume_name)),
194 Err(NtfsError::AttributeNotFound { .. }) => None,
195 Err(e) => Some(Err(e)),
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_basics() {
206 let mut testfs1 = crate::helpers::tests::testfs1();
207 let ntfs = Ntfs::new(&mut testfs1).unwrap();
208 assert_eq!(ntfs.cluster_size(), 512);
209 assert_eq!(ntfs.sector_size(), 512);
210 assert_eq!(ntfs.size(), 2096640);
211 }
212
213 #[test]
214 fn test_volume_info() {
215 let mut testfs1 = crate::helpers::tests::testfs1();
216 let ntfs = Ntfs::new(&mut testfs1).unwrap();
217 let volume_info = ntfs.volume_info(&mut testfs1).unwrap();
218 assert_eq!(volume_info.major_version(), 3);
219 assert_eq!(volume_info.minor_version(), 1);
220 }
221
222 #[test]
223 fn test_volume_name() {
224 let mut testfs1 = crate::helpers::tests::testfs1();
225 let ntfs = Ntfs::new(&mut testfs1).unwrap();
226 let volume_name = ntfs.volume_name(&mut testfs1).unwrap().unwrap();
227 assert_eq!(volume_name.name_length(), 14);
228 assert_eq!(volume_name.name(), "mylabel");
229 }
230}