Skip to content

Commit

Permalink
Added pbs parser for disk info (#24)
Browse files Browse the repository at this point in the history
* refactoring reader

* added pbs parser

* fixing clippy
  • Loading branch information
NikolaMilosa authored Nov 21, 2023
1 parent 2f7ebd3 commit 776ea71
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 31 deletions.
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod cli;
mod errors;
mod reader;
mod undelete_entry;
mod util;

fn main() -> Result<()> {
env_logger::builder()
Expand Down
65 changes: 34 additions & 31 deletions src/reader.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use log::info;
use mft::{
attribute::{
non_resident_attr::NonResidentAttr, x80::DataAttr, MftAttributeContent, MftAttributeType,
},
MftEntry,
};

use crate::errors::Result;
use crate::{
errors::Result,
util::{detect_file_system, FileSystems},
};
use std::{
fs::File,
io::{BufRead, BufReader, Read, Seek, SeekFrom},
Expand All @@ -15,14 +19,6 @@ use std::{
#[derive(Debug)]
pub struct Reader {
path: PathBuf,
reader_type: ReaderType,
}

#[derive(Debug)]
enum ReaderType {
Directory,
Image,
BlockDevice,
}

const CLUSTER_SIZE: usize = 4096; // 4 KiB
Expand All @@ -31,31 +27,48 @@ const SIGNATURES: [&[u8]; 3] = [b"FILE", b"BAAD", b"0000"];

impl Reader {
pub fn from_path(path: PathBuf) -> Result<Self> {
let reader_type = if path.is_dir() {
ReaderType::Directory
let path = if path.is_dir() {
let configued_path =
find_block_device(&path)?.ok_or_else(|| crate::errors::Error::Any {
detail: "Couldn't find block device for directory".to_string(),
})?;
info!(
"Running in dir mode, will map to block device: {}",
configued_path.display()
);
configued_path
} else if path.is_file() {
ReaderType::Image
info!("Running in image mode");
path
} else if path.starts_with("/dev/") {
ReaderType::BlockDevice
info!("Running in block mode");
path
} else {
return Err(crate::errors::Error::FailedToOpenFile {
path,
source: std::io::Error::new(std::io::ErrorKind::NotFound, "File not found"),
});
};

Ok(Self { path, reader_type })
let boot_sector = match detect_file_system(&path)? {
FileSystems::Ntfs(boot_sector) => boot_sector,
fs => {
return Err(crate::errors::Error::Any {
detail: format!("Detected an unsupported file system: {}", fs),
})
}
};

info!("Parsed boot sector: \n{:#?}", boot_sector);

Ok(Self { path })
}

pub fn read_mft(&self) -> Result<Vec<u8>> {
match self.reader_type {
ReaderType::Directory => self.read_mft_dir(),
ReaderType::Image => self.read_mft_bytes(),
ReaderType::BlockDevice => self.read_mft_bytes(),
}
self.read_mft_bytes()
}

fn read_mft_dir(&self) -> Result<Vec<u8>> {
fn _read_mft_dir(&self) -> Result<Vec<u8>> {
let path = self.path.join("$MFT");

if !path.exists() {
Expand Down Expand Up @@ -115,17 +128,7 @@ impl Reader {
}

fn read_from_data_run(&self, data: NonResidentAttr) -> Result<Vec<u8>> {
let mut file = match self.reader_type {
ReaderType::Directory => {
let block_device = find_block_device(&self.path)?;
let block_device = block_device.ok_or_else(|| crate::errors::Error::Any {
detail: "Couldn't find block device for directory".to_string(),
})?;

File::open(block_device)?
}
_ => File::open(&self.path)?,
};
let mut file = File::open(&self.path)?;
let mut bytes = vec![];
for dr in data.data_runs {
let mut cluster = vec![0; dr.lcn_length as usize * CLUSTER_SIZE];
Expand Down
164 changes: 164 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use crate::errors::Result;
use std::{
fmt::Display,
fs::File,
io::{Read, Seek},
path::Path,
};
#[derive(PartialEq, Clone)]
pub enum FileSystems {
Ntfs(NtfsBootSector),
Fat,
Ext,
Iso9660,
Hfs,
Unknown,
}

impl From<FileSystems> for &str {
fn from(value: FileSystems) -> Self {
match value {
FileSystems::Ntfs(_) => "Ntfs",
FileSystems::Fat => "Fat",
FileSystems::Ext => "Ext",
FileSystems::Iso9660 => "Iso9660",
FileSystems::Hfs => "Hfs",
FileSystems::Unknown => "Unknown",
}
}
}

impl Display for FileSystems {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.clone().into())
}
}

pub fn detect_file_system<P>(path: P) -> Result<FileSystems>
where
P: AsRef<Path>,
{
let mut file = File::open(path)?;
let mut boot_sector = [0u8; 512];

file.read_exact(&mut boot_sector)?;

if &boot_sector[3..7] == b"Ntfs" {
return Ok(FileSystems::Ntfs(NtfsBootSector::from_bytes(&boot_sector)?));
}

if &boot_sector[36..39] == b"Fat" {
return Ok(FileSystems::Fat);
}

if &boot_sector[0..2] == b"H+" || &boot_sector[0..4] == b"HfsJ" || &boot_sector[0..4] == b"Hfs+"
{
return Ok(FileSystems::Hfs);
}

if &boot_sector[56..58] == b"\x53\xEF" {
return Ok(FileSystems::Ext);
}

let mut iso_buffer = [0u8; 5];
file.seek(std::io::SeekFrom::Start(32769))?;
file.read_exact(&mut iso_buffer)?;

if &iso_buffer[..] == b"CD001" {
return Ok(FileSystems::Iso9660);
}

Ok(FileSystems::Unknown)
}

#[derive(Debug, Clone, PartialEq)]
pub struct NtfsBootSector {
oem_id: String,
bytes_per_sector: u16,
sectors_per_cluster: u8,
reserved_sectors: u16,
media_descriptor: u8,
sectors_per_track: u16,
num_heads: u16,
hidden_sectors: u32,
total_sectors: u64,
logical_cluster_mft: u64,
logical_cluster_mft_mirror: u64,
clusters_per_file_record_segment: u32,
clusters_per_index_buffer: u8,
volume_serial_number: u64,
checksum: u32,
}

impl NtfsBootSector {
fn from_bytes(boot_sector_data: &[u8]) -> Result<Self> {
Ok(Self {
oem_id: String::from_utf8_lossy(&boot_sector_data[3..11]).to_string(),
bytes_per_sector: u16::from_le_bytes([boot_sector_data[0x0B], boot_sector_data[0x0C]]),
sectors_per_cluster: boot_sector_data[0x0D],
reserved_sectors: u16::from_le_bytes([boot_sector_data[0x0E], boot_sector_data[0x0F]]),
media_descriptor: boot_sector_data[0x15],
sectors_per_track: u16::from_le_bytes([boot_sector_data[0x18], boot_sector_data[0x19]]),
num_heads: u16::from_le_bytes([boot_sector_data[0x1A], boot_sector_data[0x1B]]),
hidden_sectors: u32::from_le_bytes([
boot_sector_data[0x1C],
boot_sector_data[0x1D],
boot_sector_data[0x1E],
boot_sector_data[0x1F],
]),
total_sectors: u64::from_le_bytes([
boot_sector_data[0x28],
boot_sector_data[0x29],
boot_sector_data[0x2A],
boot_sector_data[0x2B],
boot_sector_data[0x2C],
boot_sector_data[0x2D],
boot_sector_data[0x2E],
boot_sector_data[0x2F],
]),
logical_cluster_mft: u64::from_le_bytes([
boot_sector_data[0x30],
boot_sector_data[0x31],
boot_sector_data[0x32],
boot_sector_data[0x33],
boot_sector_data[0x34],
boot_sector_data[0x35],
boot_sector_data[0x36],
boot_sector_data[0x37],
]),
logical_cluster_mft_mirror: u64::from_le_bytes([
boot_sector_data[0x38],
boot_sector_data[0x39],
boot_sector_data[0x3A],
boot_sector_data[0x3B],
boot_sector_data[0x3C],
boot_sector_data[0x3D],
boot_sector_data[0x3E],
boot_sector_data[0x3F],
]),
clusters_per_file_record_segment: u32::from_le_bytes([
boot_sector_data[0x40],
boot_sector_data[0x41],
boot_sector_data[0x42],
boot_sector_data[0x43],
]),
clusters_per_index_buffer: boot_sector_data[0x44],
volume_serial_number: u64::from_le_bytes([
boot_sector_data[0x48],
boot_sector_data[0x49],
boot_sector_data[0x4A],
boot_sector_data[0x4B],
boot_sector_data[0x4C],
boot_sector_data[0x4D],
boot_sector_data[0x4E],
boot_sector_data[0x4F],
]),
checksum: u32::from_le_bytes([
boot_sector_data[0x50],
boot_sector_data[0x51],
boot_sector_data[0x52],
boot_sector_data[0x53],
]),
})
}
}

0 comments on commit 776ea71

Please # to comment.