ntfs_reader/
api.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 binread::BinRead;
6use time::OffsetDateTime;
7
8pub const SECTOR_SIZE: usize = 512;
9pub const MFT_RECORD: u64 = 0;
10pub const ROOT_RECORD: u64 = 5;
11pub const FIRST_NORMAL_RECORD: u64 = 24;
12pub const FILE_RECORD_SIGNATURE: &[u8; 4] = b"FILE";
13pub const EPOCH_DIFFERENCE: u64 = 116_444_736_000_000_000;
14
15#[allow(unused)]
16#[repr(C, packed)]
17#[derive(Clone, Copy, BinRead)]
18pub struct BootSector {
19    pub crap_0: [u8; 11],
20    pub sector_size: u16,
21    pub sectors_per_cluster: u8,
22    pub crap_1: [u8; 26],
23    pub total_sectors: u64,
24    pub mft_lcn: u64,
25    pub mft_lcn_mirror: u64,
26    pub file_record_size_info: i8,
27    pub crap_2: [u8; 447],
28}
29
30#[repr(C, packed)]
31pub struct NtfsFileRecordHeader {
32    // Record
33    pub signature: [u8; 4],
34    pub update_sequence_offset: u16,
35    pub update_sequence_length: u16,
36    pub logfile_sequence_number: u64,
37    // File
38    pub sequence_value: u16,
39    pub link_count: u16,
40    pub attributes_offset: u16,
41    pub flags: u16,
42    pub used_size: u32,
43    pub allocated_size: u32,
44    pub base_reference: u64,
45    pub next_attribute_id: u16,
46}
47
48#[repr(u16)]
49pub enum NtfsFileFlags {
50    InUse = 0x0001,
51    IsDirectory = 0x0002,
52}
53
54#[allow(unused)]
55#[repr(C, packed)]
56pub struct NtfsAttributeHeader {
57    pub type_id: u32,
58    pub length: u32,
59    pub is_non_resident: u8,
60    pub name_length: u8,
61    pub name_offset: u16,
62    pub flags: u16,
63    pub id: u16,
64}
65
66#[repr(C, packed)]
67pub struct NtfsResidentAttributeHeader {
68    pub attribute_header: NtfsAttributeHeader,
69    pub value_length: u32,
70    pub value_offset: u16,
71    pub indexed_flag: u8,
72}
73
74#[repr(C, packed)]
75pub struct NtfsNonResidentAttributeHeader {
76    pub attribute_header: NtfsAttributeHeader,
77    pub lowest_vcn: i64,
78    pub highest_vcn: i64,
79    pub data_runs_offset: u16,
80    pub compression_unit_exponent: u8,
81    pub reserved: [u8; 5],
82    pub allocated_size: u64,
83    pub data_size: u64,
84    pub initialized_size: u64,
85}
86
87#[repr(C, packed)]
88pub struct NtfsStandardInformation {
89    pub creation_time: u64,
90    pub modification_time: u64,
91    pub mft_record_modification_time: u64,
92    pub access_time: u64,
93    pub file_attributes: u32,
94}
95
96#[repr(u8)]
97pub enum NtfsFileNamespace {
98    Posix = 0,
99    Win32 = 1,
100    Dos = 2,
101    Win32AndDos = 3,
102}
103
104#[repr(C, packed)]
105#[derive(Copy, Clone)]
106pub struct NtfsFileNameHeader {
107    pub parent_directory_reference: u64,
108    pub crap_0: [u8; 32],
109    pub allocated_size: u64,
110    pub real_size: u64,
111    pub file_attributes: u32,
112    pub reparse_point_tag: u32,
113    pub name_length: u8,
114    pub namespace: u8,
115}
116
117#[repr(u32)]
118pub enum NtfsFileNameFlags {
119    ReadOnly = 0x0001,
120    Hidden = 0x0002,
121    System = 0x0004,
122    Archive = 0x0020,
123    Device = 0x0040,
124    Normal = 0x0080,
125    Temporary = 0x0100,
126    SparseFile = 0x0200,
127    ReparsePoint = 0x0400,
128    Compressed = 0x0800,
129    Offline = 0x1000,
130    NotContentIndexed = 0x2000,
131    Encrypted = 0x4000,
132    IsDirectory = 0x1000_0000,
133}
134
135#[repr(C, packed)]
136#[derive(Copy, Clone)]
137pub struct NtfsFileName {
138    pub header: NtfsFileNameHeader,
139    pub data: [u16; 255],
140}
141
142impl std::fmt::Display for NtfsFileName {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        let data = self.data;
145        let string = String::from_utf16_lossy(&data[..self.header.name_length as usize]);
146        write!(f, "{}", string)
147    }
148}
149
150#[repr(C, packed)]
151pub struct NtfsAttributeListEntry {
152    pub type_id: u32,
153    pub length: u16,
154    pub name_length: u8,
155    pub name_offset: u8,
156    pub starting_vcn: u64,
157    pub base_file_reference: u64,
158    pub id: u16,
159    //pub name: Option<String>,
160}
161
162impl NtfsAttributeListEntry {
163    pub fn reference(&self) -> u64 {
164        self.base_file_reference & 0x0000_FFFF_FFFF_FFFF
165    }
166}
167
168impl NtfsFileName {
169    pub fn parent(&self) -> u64 {
170        self.header.parent_directory_reference & 0x0000_FFFF_FFFF_FFFF
171    }
172
173    pub fn is_readonly(&self) -> bool {
174        self.header.file_attributes & NtfsFileNameFlags::ReadOnly as u32 != 0
175    }
176
177    pub fn is_hidden(&self) -> bool {
178        self.header.file_attributes & NtfsFileNameFlags::Hidden as u32 != 0
179    }
180
181    pub fn is_system(&self) -> bool {
182        self.header.file_attributes & NtfsFileNameFlags::System as u32 != 0
183    }
184
185    pub fn is_reparse_point(&self) -> bool {
186        self.header.file_attributes & NtfsFileNameFlags::ReparsePoint as u32 != 0
187    }
188}
189
190#[derive(Clone, Copy, Debug, Eq, PartialEq)]
191#[repr(u32)]
192pub enum NtfsAttributeType {
193    StandardInformation = 0x10,
194    AttributeList = 0x20,
195    FileName = 0x30,
196    Data = 0x80,
197    Bitmap = 0xB0,
198    End = 0xFFFF_FFFF,
199}
200
201pub fn ntfs_to_unix_time(src: u64) -> OffsetDateTime {
202    let unix = src.saturating_sub(EPOCH_DIFFERENCE) as i128;
203    OffsetDateTime::from_unix_timestamp_nanos(unix * 100).unwrap_or(OffsetDateTime::UNIX_EPOCH)
204}