-
Notifications
You must be signed in to change notification settings - Fork 30
/
record.rs
142 lines (115 loc) · 4.88 KB
/
record.rs
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
// Copyright 2021-2022 Colin Finck <colin@reactos.org>
// SPDX-License-Identifier: MIT OR Apache-2.0
use core::mem;
use alloc::vec::Vec;
use byteorder::{ByteOrder, LittleEndian};
use memoffset::{offset_of, span_of};
use crate::error::{NtfsError, Result};
use crate::types::NtfsPosition;
const NTFS_BLOCK_SIZE: usize = 512;
#[repr(C, packed)]
pub(crate) struct RecordHeader {
signature: [u8; 4],
update_sequence_offset: u16,
update_sequence_count: u16,
logfile_sequence_number: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct Record {
data: Vec<u8>,
position: NtfsPosition,
}
impl Record {
pub(crate) fn new(data: Vec<u8>, position: NtfsPosition) -> Self {
Self { data, position }
}
pub(crate) fn data(&self) -> &[u8] {
&self.data
}
pub(crate) fn fixup(&mut self) -> Result<()> {
let update_sequence_number = self.update_sequence_number()?;
let mut array_position = self.update_sequence_array_start() as usize;
let array_end =
self.update_sequence_offset() as usize + self.update_sequence_size() as usize;
// The Update Sequence Number (USN) is written to the last 2 bytes of each sector.
let mut sector_position = NTFS_BLOCK_SIZE - mem::size_of::<u16>();
while array_position < array_end {
let array_position_end = array_position + mem::size_of::<u16>();
let sector_position_end = sector_position + mem::size_of::<u16>();
if sector_position_end > self.data.len() {
return Err(NtfsError::UpdateSequenceArrayExceedsRecordSize {
position: self.position,
array_count: self.update_sequence_array_count(),
record_size: self.data.len(),
});
}
// The array contains the actual 2 bytes that need to be at `sector_position` after the fixup.
let new_bytes: [u8; 2] = self.data[array_position..array_position_end]
.try_into()
.unwrap();
// The current 2 bytes at `sector_position` before the fixup should equal the Update Sequence Number (USN).
// Otherwise, this sector is corrupted.
let bytes_to_update = &mut self.data[sector_position..sector_position_end];
if bytes_to_update != update_sequence_number {
return Err(NtfsError::UpdateSequenceNumberMismatch {
position: self.position + array_position,
expected: update_sequence_number,
actual: (&*bytes_to_update).try_into().unwrap(),
});
}
// Perform the actual fixup.
bytes_to_update.copy_from_slice(&new_bytes);
// Advance to the next array entry and sector.
array_position += mem::size_of::<u16>();
sector_position += NTFS_BLOCK_SIZE;
}
Ok(())
}
pub(crate) fn into_data(self) -> Vec<u8> {
self.data
}
pub(crate) fn len(&self) -> u32 {
// A record is never larger than a u32.
// Usually, it shouldn't even exceed a u16, but our code could handle that.
self.data.len() as u32
}
pub(crate) fn position(&self) -> NtfsPosition {
self.position
}
pub(crate) fn signature(&self) -> [u8; 4] {
self.data[span_of!(RecordHeader, signature)]
.try_into()
.unwrap()
}
fn update_sequence_array_count(&self) -> u16 {
let start = offset_of!(RecordHeader, update_sequence_count);
let update_sequence_count = LittleEndian::read_u16(&self.data[start..]);
// Subtract the Update Sequence Number (USN), so that only the number of array elements remains.
update_sequence_count - mem::size_of::<u16>() as u16
}
fn update_sequence_array_start(&self) -> u16 {
// The Update Sequence Number (USN) comes first and the array begins right after that.
self.update_sequence_offset() + mem::size_of::<u16>() as u16
}
fn update_sequence_number(&self) -> Result<[u8; 2]> {
let start = self.update_sequence_offset() as usize;
let end = start + mem::size_of::<u16>();
self.data
.get(start..end)
.and_then(|bytes| bytes.try_into().ok())
.ok_or(NtfsError::InvalidUpdateSequenceNumberRange {
position: self.position,
range: start..end,
size: self.data.len(),
})
}
fn update_sequence_offset(&self) -> u16 {
let start = offset_of!(RecordHeader, update_sequence_offset);
LittleEndian::read_u16(&self.data[start..])
}
pub(crate) fn update_sequence_size(&self) -> u32 {
let start = offset_of!(RecordHeader, update_sequence_count);
let update_sequence_count = LittleEndian::read_u16(&self.data[start..]);
update_sequence_count as u32 * mem::size_of::<u16>() as u32
}
}