Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/mft.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmer Ben-Amram <omerbenamram@gmail.com>2019-05-21 12:29:09 +0300
committerOmer Ben-Amram <omerbenamram@gmail.com>2019-05-21 12:29:09 +0300
commitd63f07e3ebcc54deeaae8ccd03c79d230563c58e (patch)
tree20eca14d1482fbfa6629f7b7da58d48ac24bdf59
parentc7642a52e77ac7420e2a20f92be538a1ae891e71 (diff)
more progress
-rw-r--r--src/bin/mft_dump.rs29
-rw-r--r--src/entry.rs48
-rw-r--r--src/mft.rs11
3 files changed, 36 insertions, 52 deletions
diff --git a/src/bin/mft_dump.rs b/src/bin/mft_dump.rs
index 32bfdaa..2404be6 100644
--- a/src/bin/mft_dump.rs
+++ b/src/bin/mft_dump.rs
@@ -35,8 +35,13 @@ impl OutputFormat {
#[serde(rename_all = "PascalCase")]
pub struct FlatMftEntryWithName {
pub signature: String,
- pub logfile_sequence_number: u64,
+
+ pub entry_id: u64,
pub sequence: u16,
+
+ pub base_entry_id: u64,
+ pub base_entry_sequence: u16,
+
pub hard_link_count: u16,
pub flags: EntryFlags,
@@ -44,19 +49,9 @@ pub struct FlatMftEntryWithName {
pub used_entry_size: u32,
pub total_entry_size: u32,
- pub parent_reference_entry: u64,
- pub parent_reference_sequence: u16,
-
- pub record_number: u64,
- pub entry_reference_entry: u64,
-
- /// Indicates whether the record is free or not.
- // TODO: implement
- pub in_use: bool,
/// Indicates whether the record is a directory.
pub is_a_directory: bool,
- /// The file extension (only for non-directories entries)
- pub entry_reference_sequence: u16,
+
/// All of these fields are present for entries that have an 0x10 attribute.
pub standard_info_flags: Option<FileAttributeFlags>,
pub standard_info_last_modified: Option<DateTime<Utc>>,
@@ -96,20 +91,16 @@ impl FlatMftEntryWithName {
}
FlatMftEntryWithName {
- record_number: entry.header.record_number,
+ entry_id: entry.header.record_number,
signature: String::from_utf8(entry.header.signature.to_ascii_uppercase()).unwrap(),
- logfile_sequence_number: entry.header.logfile_sequence_number,
sequence: entry.header.sequence,
hard_link_count: entry.header.hard_link_count,
flags: entry.header.flags,
used_entry_size: entry.header.used_entry_size,
total_entry_size: entry.header.total_entry_size,
- parent_reference_entry: entry.header.base_reference.entry,
- parent_reference_sequence: entry.header.base_reference.sequence,
- entry_reference_entry: entry.header.entry_reference.entry,
- in_use: false,
+ base_entry_id: entry.header.base_reference.entry,
+ base_entry_sequence: entry.header.base_reference.sequence,
is_a_directory: entry.is_dir(),
- entry_reference_sequence: entry.header.entry_reference.sequence,
standard_info_flags: standard_info.as_ref().and_then(|i| Some(i.file_flags)),
standard_info_last_modified: standard_info.as_ref().and_then(|i| Some(i.modified)),
standard_info_last_access: standard_info.as_ref().and_then(|i| Some(i.accessed)),
diff --git a/src/entry.rs b/src/entry.rs
index fcc18ab..f1d6dbc 100644
--- a/src/entry.rs
+++ b/src/entry.rs
@@ -55,11 +55,13 @@ pub struct EntryHeader {
/// The update sequence array must end before the last USHORT value in the first sector.
pub usa_offset: u16,
pub usa_size: u16,
+ /// Metadata transaction journal sequence number (Reserved1 in windows docs)
+ /// Contains a $LogFile Sequence Number (LSN) (metz)
+ pub metadata_transaction_journal: u64,
/// The sequence number.
/// This value is incremented each time that a file record segment is freed; it is 0 if the segment is not used.
/// The SequenceNumber field of a file reference must match the contents of this field;
/// if they do not match, the file reference is incorrect and probably obsolete.
- pub logfile_sequence_number: u64,
pub sequence: u16,
pub hard_link_count: u16,
/// The offset of the first attribute record, in bytes.
@@ -71,9 +73,8 @@ pub struct EntryHeader {
/// A file reference to the base file record segment for this file.
/// If this is the base file record, the value is 0. See MFT_SEGMENT_REFERENCE.
pub base_reference: MftReference,
- pub next_attribute_id: u16,
+ pub first_attribute_id: u16,
pub record_number: u64,
- pub entry_reference: MftReference,
}
bitflags! {
pub struct EntryFlags: u16 {
@@ -87,18 +88,12 @@ bitflags! {
impl_serialize_for_bitflags! {EntryFlags}
impl EntryHeader {
- pub fn from_reader<R: Read>(reader: &mut R) -> Result<EntryHeader> {
+ /// Reads an entry from a stream, will error if the entry is empty (zeroes)
+ /// Since the entry id is not present in the header, it should be provided by the caller.
+ pub fn from_reader<R: Read>(reader: &mut R, entry_id: u64) -> Result<EntryHeader> {
let mut signature = [0; 4];
reader.read_exact(&mut signature)?;
- // Corrupted entry
- ensure!(
- &signature != b"BAAD",
- err::InvalidEntrySignature {
- bad_sig: signature.to_vec()
- }
- );
-
// Empty entry
ensure!(
&signature != b"\x00\x00\x00\x00",
@@ -112,34 +107,30 @@ impl EntryHeader {
let logfile_sequence_number = reader.read_u64::<LittleEndian>()?;
let sequence = reader.read_u16::<LittleEndian>()?;
let hard_link_count = reader.read_u16::<LittleEndian>()?;
- let fst_attr_offset = reader.read_u16::<LittleEndian>()?;
+ let first_attribute_offset = reader.read_u16::<LittleEndian>()?;
let flags = EntryFlags::from_bits_truncate(reader.read_u16::<LittleEndian>()?);
let entry_size_real = reader.read_u32::<LittleEndian>()?;
let entry_size_allocated = reader.read_u32::<LittleEndian>()?;
+
let base_reference =
MftReference::from_reader(reader).context(err::FailedToReadMftReference)?;
- let next_attribute_id = reader.read_u16::<LittleEndian>()?;
-
- let _padding = reader.read_u16::<LittleEndian>()?;
- let record_number = u64::from(reader.read_u32::<LittleEndian>()?);
- let entry_reference = MftReference::new(record_number as u64, sequence);
+ let first_attribute_id = reader.read_u16::<LittleEndian>()?;
Ok(EntryHeader {
signature,
usa_offset,
usa_size,
- logfile_sequence_number,
+ metadata_transaction_journal: logfile_sequence_number,
sequence,
hard_link_count,
- first_attribute_record_offset: fst_attr_offset,
+ first_attribute_record_offset: first_attribute_offset,
flags,
used_entry_size: entry_size_real,
total_entry_size: entry_size_allocated,
base_reference,
- next_attribute_id,
- record_number,
- entry_reference,
+ first_attribute_id,
+ record_number: entry_id,
})
}
}
@@ -148,10 +139,10 @@ impl MftEntry {
/// Initializes an MFT Entry from a buffer.
/// Since the parser is the entity responsible for knowing the entry size,
/// we take ownership of the buffer instead of trying to read it from stream.
- pub fn from_buffer(mut buffer: Vec<u8>) -> Result<MftEntry> {
+ pub fn from_buffer(mut buffer: Vec<u8>, entry_number: u64) -> Result<MftEntry> {
let mut cursor = Cursor::new(&buffer);
// Get Header
- let entry_header = EntryHeader::from_reader(&mut cursor)?;
+ let entry_header = EntryHeader::from_reader(&mut cursor, entry_number)?;
trace!("Number of sectors: {:#?}", entry_header);
Self::apply_fixups(&entry_header, &mut buffer)?;
@@ -306,12 +297,13 @@ mod tests {
0x00, 0x00, 0xD5, 0x95, 0x00, 0x00, 0x53, 0x57, 0x81, 0x37, 0x00, 0x00, 0x00, 0x00,
];
- let entry_header = EntryHeader::from_reader(&mut Cursor::new(header_buffer)).unwrap();
+ let entry_header =
+ EntryHeader::from_reader(&mut Cursor::new(header_buffer), 38357).unwrap();
assert_eq!(&entry_header.signature, b"FILE");
assert_eq!(entry_header.usa_offset, 48);
assert_eq!(entry_header.usa_size, 3);
- assert_eq!(entry_header.logfile_sequence_number, 53_762_438_092);
+ assert_eq!(entry_header.metadata_transaction_journal, 53_762_438_092);
assert_eq!(entry_header.sequence, 5);
assert_eq!(entry_header.hard_link_count, 1);
assert_eq!(entry_header.first_attribute_record_offset, 56);
@@ -319,7 +311,7 @@ mod tests {
assert_eq!(entry_header.used_entry_size, 840);
assert_eq!(entry_header.total_entry_size, 1024);
assert_eq!(entry_header.base_reference.entry, 0);
- assert_eq!(entry_header.next_attribute_id, 6);
+ assert_eq!(entry_header.first_attribute_id, 6);
assert_eq!(entry_header.record_number, 38357);
}
}
diff --git a/src/mft.rs b/src/mft.rs
index 90beaf5..d644be8 100644
--- a/src/mft.rs
+++ b/src/mft.rs
@@ -49,7 +49,7 @@ impl MftParser<Cursor<Vec<u8>>> {
impl<T: ReadSeek> MftParser<T> {
pub fn from_read_seek(mut data: T, size: u64) -> Result<Self> {
// We use the first entry to guess the entry size for all the other records.
- let first_entry = EntryHeader::from_reader(&mut data)?;
+ let first_entry = EntryHeader::from_reader(&mut data, 0)?;
data.seek(SeekFrom::Start(0))?;
Ok(Self {
@@ -74,7 +74,7 @@ impl<T: ReadSeek> MftParser<T> {
self.data.read_exact(&mut entry_buffer)?;
- Ok(MftEntry::from_buffer(entry_buffer)?)
+ Ok(MftEntry::from_buffer(entry_buffer, entry_number)?)
}
/// Iterates over all the entries in the MFT.
@@ -86,8 +86,9 @@ impl<T: ReadSeek> MftParser<T> {
if count == total_entries {
None
} else {
+ let entry = Some(self.get_entry(count));
count += 1;
- Some(self.get_entry(count))
+ entry
}
})
}
@@ -95,7 +96,7 @@ impl<T: ReadSeek> MftParser<T> {
/// Gets the full path for an entry.
/// Caches computations.
pub fn get_full_path_for_entry(&mut self, entry: &MftEntry) -> Result<Option<PathBuf>> {
- let entry_id = entry.header.entry_reference.entry;
+ let entry_id = entry.header.record_number;
for attribute in entry.iter_attributes().filter_map(|a| a.ok()) {
if let AttrX30(filename_header) = attribute.data {
@@ -146,7 +147,7 @@ impl<T: ReadSeek> MftParser<T> {
let orphan = PathBuf::from("[Orphaned]").join(filename_header.name);
self.entries_cache
- .cache_set(entry.header.entry_reference.entry, orphan.clone());
+ .cache_set(entry.header.record_number, orphan.clone());
return Ok(Some(orphan));
}
}