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

csv.rs « src - github.com/windirstat/mft.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 3420b6eb2b728d91ea59b027be58b08940ea350f (plain)
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
use crate::attribute::header::ResidentialHeader;

use crate::attribute::{FileAttributeFlags, MftAttributeType};
use crate::entry::EntryFlags;
use crate::{MftAttribute, MftEntry, MftParser, ReadSeek};

use serde::Serialize;

use chrono::{DateTime, Utc};
use std::path::PathBuf;

/// Used for CSV output
#[derive(Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct FlatMftEntryWithName {
    pub signature: String,

    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,

    /// The size of the file, in bytes.
    pub used_entry_size: u32,
    pub total_entry_size: u32,

    /// The size of the file, if available, from the X80 attribute.
    /// Will be 0 if no $DATA attribute is found.
    pub file_size: u64,

    /// Indicates whether the record is a directory.
    pub is_a_directory: bool,
    /// Indicates whether the record has the `ALLOCATED` bit turned off.
    pub is_deleted: bool,

    /// Indicates whether the record has alternate data streams.
    pub has_alternate_data_streams: bool,

    /// 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>>,
    pub standard_info_last_access: Option<DateTime<Utc>>,
    pub standard_info_created: Option<DateTime<Utc>>,
    /// All of these fields are present for entries that have an 0x30 attribute.
    pub file_name_flags: Option<FileAttributeFlags>,
    pub file_name_last_modified: Option<DateTime<Utc>>,
    pub file_name_last_access: Option<DateTime<Utc>>,
    pub file_name_created: Option<DateTime<Utc>>,

    pub full_path: PathBuf,
}

impl FlatMftEntryWithName {
    pub fn from_entry(
        entry: &MftEntry,
        parser: &mut MftParser<impl ReadSeek>,
    ) -> FlatMftEntryWithName {
        let entry_attributes: Vec<MftAttribute> = entry
            .iter_attributes_matching(Some(vec![
                MftAttributeType::FileName,
                MftAttributeType::StandardInformation,
                MftAttributeType::DATA,
            ]))
            .filter_map(Result::ok)
            .collect();

        let file_name = entry_attributes
            .iter()
            .find(|a| a.header.type_code == MftAttributeType::FileName)
            .and_then(|a| a.data.clone().into_file_name());

        let standard_info = entry_attributes
            .iter()
            .find(|a| a.header.type_code == MftAttributeType::StandardInformation)
            .and_then(|a| a.data.clone().into_standard_info());

        let data_attr = entry_attributes
            .iter()
            .find(|a| a.header.type_code == MftAttributeType::DATA);

        let file_size = match data_attr {
            Some(attr) => match &attr.header.residential_header {
                ResidentialHeader::Resident(r) => u64::from(r.data_size),
                ResidentialHeader::NonResident(nr) => nr.file_size,
            },
            _ => 0,
        };

        let has_ads = entry_attributes
            .iter()
            .any(|a| a.header.type_code == MftAttributeType::DATA && !a.header.name.is_empty());

        FlatMftEntryWithName {
            entry_id: entry.header.record_number,
            signature: String::from_utf8(entry.header.signature.to_ascii_uppercase())
                .expect("It should be either FILE or BAAD (valid utf-8)"),
            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,
            base_entry_id: entry.header.base_reference.entry,
            base_entry_sequence: entry.header.base_reference.sequence,
            is_a_directory: entry.is_dir(),
            is_deleted: !entry.header.flags.contains(EntryFlags::ALLOCATED),
            has_alternate_data_streams: has_ads,
            standard_info_flags: standard_info.as_ref().map(|i| i.file_flags),
            standard_info_last_modified: standard_info.as_ref().map(|i| i.modified),
            standard_info_last_access: standard_info.as_ref().map(|i| i.accessed),
            standard_info_created: standard_info.as_ref().map(|i| i.created),
            file_name_flags: file_name.as_ref().map(|i| i.flags),
            file_name_last_modified: file_name.as_ref().map(|i| i.modified),
            file_name_last_access: file_name.as_ref().map(|i| i.accessed),
            file_name_created: file_name.as_ref().map(|i| i.created),
            file_size,
            full_path: parser
                .get_full_path_for_entry(entry)
                .expect("I/O Err")
                .unwrap_or_default(),
        }
    }
}