1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// Copyright 2021-2023 Colin Finck <colin@reactos.org>
// SPDX-License-Identifier: MIT OR Apache-2.0
use core::ops::Range;
use displaydoc::Display;
use crate::attribute::NtfsAttributeType;
use crate::types::NtfsPosition;
use crate::types::{Lcn, Vcn};
/// Central result type of ntfs.
pub type Result<T, E = NtfsError> = core::result::Result<T, E>;
/// Central error type of ntfs.
#[derive(Debug, Display)]
#[non_exhaustive]
pub enum NtfsError {
/// The NTFS file at byte position {position:#x} has no attribute of type {ty:?}, but it was expected
AttributeNotFound {
position: NtfsPosition,
ty: NtfsAttributeType,
},
/// The NTFS Attribute at byte position {position:#x} should have type {expected:?}, but it actually has type {actual:?}
AttributeOfDifferentType {
position: NtfsPosition,
expected: NtfsAttributeType,
actual: NtfsAttributeType,
},
/// The given buffer should have at least {expected} bytes, but it only has {actual} bytes
BufferTooSmall { expected: usize, actual: usize },
/// The NTFS Attribute at byte position {position:#x} has a length of {expected} bytes, but only {actual} bytes are left in the record
InvalidAttributeLength {
position: NtfsPosition,
expected: usize,
actual: usize,
},
/// The NTFS Attribute at byte position {position:#x} indicates a name length up to offset {expected}, but the attribute only has a size of {actual} bytes
InvalidAttributeNameLength {
position: NtfsPosition,
expected: usize,
actual: u32,
},
/// The NTFS Attribute at byte position {position:#x} indicates that its name starts at offset {expected}, but the attribute only has a size of {actual} bytes
InvalidAttributeNameOffset {
position: NtfsPosition,
expected: u16,
actual: u32,
},
/// The NTFS Data Run header at byte position {position:#x} indicates a maximum byte count of {expected}, but {actual} is the limit
InvalidByteCountInDataRunHeader {
position: NtfsPosition,
expected: u8,
actual: u8,
},
/// The cluster count {cluster_count} read from the NTFS Data Run header at byte position {position:#x} is invalid
InvalidClusterCountInDataRunHeader {
position: NtfsPosition,
cluster_count: u64,
},
/// The NTFS File Record at byte position {position:#x} indicates an allocated size of {expected} bytes, but the record only has a size of {actual} bytes
InvalidFileAllocatedSize {
position: NtfsPosition,
expected: u32,
actual: u32,
},
/// The requested NTFS File Record Number {file_record_number} is invalid
InvalidFileRecordNumber { file_record_number: u64 },
/// The NTFS File Record at byte position {position:#x} should have signature {expected:?}, but it has signature {actual:?}
InvalidFileSignature {
position: NtfsPosition,
expected: &'static [u8],
actual: [u8; 4],
},
/// The NTFS File Record at byte position {position:#x} indicates a used size of {expected} bytes, but only {actual} bytes are allocated
InvalidFileUsedSize {
position: NtfsPosition,
expected: u32,
actual: u32,
},
/// The NTFS Index Record at byte position {position:#x} indicates an allocated size of {expected} bytes, but the record only has a size of {actual} bytes
InvalidIndexAllocatedSize {
position: NtfsPosition,
expected: u32,
actual: u32,
},
/// The NTFS Index Entry at byte position {position:#x} references a data field in the range {range:?}, but the entry only has a size of {size} bytes
InvalidIndexEntryDataRange {
position: NtfsPosition,
range: Range<usize>,
size: u16,
},
/// The NTFS Index Entry at byte position {position:#x} reports a size of {expected} bytes, but it only has {actual} bytes
InvalidIndexEntrySize {
position: NtfsPosition,
expected: u16,
actual: u16,
},
/// The NTFS index root at byte position {position:#x} indicates that its entries start at offset {expected}, but the index root only has a size of {actual} bytes
InvalidIndexRootEntriesOffset {
position: NtfsPosition,
expected: usize,
actual: usize,
},
/// The NTFS index root at byte position {position:#x} indicates a used size up to offset {expected}, but the index root only has a size of {actual} bytes
InvalidIndexRootUsedSize {
position: NtfsPosition,
expected: usize,
actual: usize,
},
/// The NTFS Index Record at byte position {position:#x} should have signature {expected:?}, but it has signature {actual:?}
InvalidIndexSignature {
position: NtfsPosition,
expected: &'static [u8],
actual: [u8; 4],
},
/// The NTFS Index Record at byte position {position:#x} indicates a used size of {expected} bytes, but only {actual} bytes are allocated
InvalidIndexUsedSize {
position: NtfsPosition,
expected: u32,
actual: u32,
},
/// The MFT LCN in the BIOS Parameter Block of the NTFS filesystem is invalid.
InvalidMftLcn,
/// The NTFS Non Resident Value Data at byte position {position:#x} references a data field in the range {range:?}, but the entry only has a size of {size} bytes
InvalidNonResidentValueDataRange {
position: NtfsPosition,
range: Range<usize>,
size: usize,
},
/// The resident NTFS Attribute at byte position {position:#x} indicates a value length of {length} starting at offset {offset}, but the attribute only has a size of {actual} bytes
InvalidResidentAttributeValueLength {
position: NtfsPosition,
length: u32,
offset: u16,
actual: u32,
},
/// The resident NTFS Attribute at byte position {position:#x} indicates that its value starts at offset {expected}, but the attribute only has a size of {actual} bytes
InvalidResidentAttributeValueOffset {
position: NtfsPosition,
expected: u16,
actual: u32,
},
/// A record size field in the BIOS Parameter Block denotes {size_info}, which is invalid considering the cluster size of {cluster_size} bytes
InvalidRecordSizeInfo { size_info: i8, cluster_size: u32 },
/// The sectors per cluster field in the BIOS Parameter Block denotes {sectors_per_cluster:#04x}, which is invalid
InvalidSectorsPerCluster { sectors_per_cluster: u8 },
/// The NTFS structured value at byte position {position:#x} of type {ty:?} has {actual} bytes where {expected} bytes were expected
InvalidStructuredValueSize {
position: NtfsPosition,
ty: NtfsAttributeType,
expected: u64,
actual: u64,
},
/// The given time can't be represented as an NtfsTime
InvalidTime,
/// The 2-byte signature field at byte position {position:#x} should contain {expected:?}, but it contains {actual:?}
InvalidTwoByteSignature {
position: NtfsPosition,
expected: &'static [u8],
actual: [u8; 2],
},
/// The Upcase Table should have a size of {expected} bytes, but it has {actual} bytes
InvalidUpcaseTableSize { expected: u64, actual: u64 },
/// The NTFS Update Sequence Count of the record at byte position {position:#x} has the invalid value {update_sequence_count}
InvalidUpdateSequenceCount {
position: NtfsPosition,
update_sequence_count: u16,
},
/// The NTFS Update Sequence Number of the record at byte position {position:#x} references a data field in the range {range:?}, but the entry only has a size of {size} bytes
InvalidUpdateSequenceNumberRange {
position: NtfsPosition,
range: Range<usize>,
size: usize,
},
/// The VCN {vcn} read from the NTFS Data Run header at byte position {position:#x} cannot be added to the LCN {previous_lcn} calculated from previous data runs
InvalidVcnInDataRunHeader {
position: NtfsPosition,
vcn: Vcn,
previous_lcn: Lcn,
},
/// I/O error: {0:?}
Io(binrw::io::Error),
/// The Logical Cluster Number (LCN) {lcn} is too big to be multiplied by the cluster size
LcnTooBig { lcn: Lcn },
/// The index root at byte position {position:#x} is a large index, but no matching index allocation attribute was provided
MissingIndexAllocation { position: NtfsPosition },
/// The NTFS file at byte position {position:#x} is not a directory
NotADirectory { position: NtfsPosition },
/// The total sector count is too big to be multiplied by the sector size
TotalSectorsTooBig { total_sectors: u64 },
/// The NTFS Attribute at byte position {position:#x} should not belong to an Attribute List, but it does
UnexpectedAttributeListAttribute { position: NtfsPosition },
/// The NTFS Attribute at byte position {position:#x} should be resident, but it is non-resident
UnexpectedNonResidentAttribute { position: NtfsPosition },
/// The NTFS Attribute at byte position {position:#x} should be non-resident, but it is resident
UnexpectedResidentAttribute { position: NtfsPosition },
/// The type of the NTFS Attribute at byte position {position:#x} is {actual:#010x}, which is not supported
UnsupportedAttributeType { position: NtfsPosition, actual: u32 },
/// The cluster size is {actual} bytes, but it needs to be between {min} and {max}
UnsupportedClusterSize { min: u32, max: u32, actual: u32 },
/// The namespace of the NTFS file name starting at byte position {position:#x} is {actual}, which is not supported
UnsupportedFileNamespace { position: NtfsPosition, actual: u8 },
/// The sector size is {actual} bytes, but it needs to be between {min} and {max}
UnsupportedSectorSize { min: u16, max: u16, actual: u16 },
/// The Update Sequence Array (USA) of the record at byte position {position:#x} has entries for {array_count} blocks of 512 bytes, but the record is only {record_size} bytes long
UpdateSequenceArrayExceedsRecordSize {
position: NtfsPosition,
array_count: u16,
record_size: usize,
},
/// Sector corruption: The 2 bytes at byte position {position:#x} should match the Update Sequence Number (USN) {expected:?}, but they are {actual:?}
UpdateSequenceNumberMismatch {
position: NtfsPosition,
expected: [u8; 2],
actual: [u8; 2],
},
/// The index allocation at byte position {position:#x} references a Virtual Cluster Number (VCN) {expected}, but a record with VCN {actual} is found at that offset
VcnMismatchInIndexAllocation {
position: NtfsPosition,
expected: Vcn,
actual: Vcn,
},
/// The index allocation at byte position {position:#x} references a Virtual Cluster Number (VCN) {vcn}, but this VCN exceeds the boundaries of the filesystem
VcnOutOfBoundsInIndexAllocation { position: NtfsPosition, vcn: Vcn },
/// The Virtual Cluster Number (VCN) {vcn} is too big to be multiplied by the cluster size
VcnTooBig { vcn: Vcn },
}
impl From<binrw::error::Error> for NtfsError {
fn from(error: binrw::error::Error) -> Self {
if let binrw::error::Error::Io(io_error) = error {
Self::Io(io_error)
} else {
// We don't use any binrw attributes that result in other errors.
unreachable!("Got a binrw error of unexpected type: {:?}", error);
}
}
}
impl From<binrw::io::Error> for NtfsError {
fn from(error: binrw::io::Error) -> Self {
Self::Io(error)
}
}
// To stay compatible with standardized interfaces (e.g. io::Read, io::Seek),
// we sometimes need to convert from NtfsError to io::Error.
impl From<NtfsError> for binrw::io::Error {
fn from(error: NtfsError) -> Self {
if let NtfsError::Io(io_error) = error {
io_error
} else {
binrw::io::Error::new(binrw::io::ErrorKind::Other, error)
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for NtfsError {}