Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
SecSamDev committed Mar 4, 2024
2 parents 0429d68 + e08983c commit ea3916b
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 95 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "forensic-rs"
version = "0.10.0"
version = "0.11.0"
authors = ["Samuel Garcés Marín <samuel.garces@protonmail.com>"]
keywords = ["forensic", "windows", "parser", "registry", "cybersecurity"]
categories = ["parsing"]
Expand Down
154 changes: 83 additions & 71 deletions src/core/fs/stdfs.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
use std::{time::SystemTime, path::Path};

use crate::{traits::vfs::{VirtualFileSystem, VMetadata, VDirEntry, VFileType, VirtualFile}, err::ForensicResult};

use std::{io::ErrorKind, path::Path, time::SystemTime};

use crate::{
err::{ForensicError, ForensicResult},
traits::vfs::{VDirEntry, VFileType, VMetadata, VirtualFile, VirtualFileSystem},
};

/// this is an error handling routine.
///
/// - if `ts_res` contains a valid unix timestamp `ts`, then `Ok(Some(ts))` is returned
/// - if `ts_res` contains a value which cannot be converted into a unix timestamp, then Err(_) is returned
/// - if `ts_res` contains an error, then:
/// - if `kind() == Unsupported` then Ok(None) is returned (because this is not an error)
/// - otherwise, the error is returned
fn timestamp_from(ts_res: std::io::Result<SystemTime>) -> ForensicResult<Option<usize>> {
match ts_res {
Ok(ts) => match ts.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => Ok(Some(v.as_secs() as usize)),
Err(_why) => Err(ForensicError::IllegalTimestamp(format!(
"timestamp {ts:?} cannot be converted into a unix timestamp"
))),
},
Err(why) => {
if why.kind() == ErrorKind::Unsupported {
Ok(None)
} else {
Err(why.into())
}
}
}
}

/// A basic Virtual filesystem that uses the Rust standard library filesystem
///
#[derive(Clone)]
///
#[derive(Clone, Default)]
pub struct StdVirtualFS {}

impl StdVirtualFS {
pub fn new() -> Self {
Self{}
Self::default()
}
}

pub struct StdVirtualFile{
pub file : std::fs::File
pub struct StdVirtualFile {
pub file: std::fs::File,
}

impl std::io::Read for StdVirtualFile {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.file.read(buf)
Expand All @@ -31,27 +59,16 @@ impl VirtualFile for StdVirtualFile {
fn metadata(&self) -> ForensicResult<VMetadata> {
let metadata = self.file.metadata()?;
let file_type = if metadata.file_type().is_dir() {
VFileType::Directory
}else if metadata.file_type().is_symlink() {
VFileType::Directory
} else if metadata.file_type().is_symlink() {
VFileType::Symlink
}else{
} else {
VFileType::File
};
let created = match metadata.created()?.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => v,
Err(_e) => std::time::Duration::ZERO
}.as_secs() as usize;

let accessed = match metadata.accessed()?.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => v,
Err(_e) => std::time::Duration::ZERO
}.as_secs() as usize;

let modified = match metadata.modified()?.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => v,
Err(_e) => std::time::Duration::ZERO
}.as_secs() as usize;

let created = timestamp_from(metadata.created())?;
let accessed = timestamp_from(metadata.accessed())?;
let modified = timestamp_from(metadata.modified())?;

Ok(VMetadata {
created,
accessed,
Expand All @@ -63,8 +80,7 @@ impl VirtualFile for StdVirtualFile {
}

impl VirtualFileSystem for StdVirtualFS {

fn read_to_string(&mut self, path: &Path) -> ForensicResult<String>{
fn read_to_string(&mut self, path: &Path) -> ForensicResult<String> {
Ok(std::fs::read_to_string(path)?)
}

Expand All @@ -88,32 +104,22 @@ impl VirtualFileSystem for StdVirtualFS {
fn metadata(&mut self, path: &Path) -> ForensicResult<VMetadata> {
let metadata = std::fs::metadata(path)?;
let file_type = if metadata.file_type().is_dir() {
VFileType::Directory
}else if metadata.file_type().is_symlink() {
VFileType::Directory
} else if metadata.file_type().is_symlink() {
VFileType::Symlink
}else{
} else {
VFileType::File
};
let created = match metadata.created()?.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => v,
Err(_e) => std::time::Duration::ZERO
}.as_secs() as usize;

let accessed = match metadata.accessed()?.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => v,
Err(_e) => std::time::Duration::ZERO
}.as_secs() as usize;

let modified = match metadata.modified()?.duration_since(SystemTime::UNIX_EPOCH) {
Ok(v) => v,
Err(_e) => std::time::Duration::ZERO
}.as_secs() as usize;


let created = timestamp_from(metadata.created())?;
let accessed = timestamp_from(metadata.accessed())?;
let modified = timestamp_from(metadata.modified())?;

Ok(VMetadata {
created,
accessed,
modified,
file_type,
file_type,
size: metadata.len(),
})
}
Expand All @@ -125,9 +131,9 @@ impl VirtualFileSystem for StdVirtualFS {
let file_type = entry.file_type()?;
let file_entry = if file_type.is_dir() {
VDirEntry::Directory(entry.file_name().to_string_lossy().into_owned())
}else if file_type.is_symlink() {
} else if file_type.is_symlink() {
VDirEntry::Symlink(entry.file_name().to_string_lossy().into_owned())
}else{
} else {
VDirEntry::File(entry.file_name().to_string_lossy().into_owned())
};
ret.push(file_entry);
Expand All @@ -139,66 +145,72 @@ impl VirtualFileSystem for StdVirtualFS {
true
}

fn open(&mut self, path : &Path) -> ForensicResult<Box<dyn VirtualFile>> {
Ok(Box::new(StdVirtualFile{file:std::fs::File::open(path)?}))
fn open(&mut self, path: &Path) -> ForensicResult<Box<dyn VirtualFile>> {
Ok(Box::new(StdVirtualFile {
file: std::fs::File::open(path)?,
}))
}

fn duplicate(&self) -> Box<dyn VirtualFileSystem> {
Box::new(StdVirtualFS{})
Box::new(StdVirtualFS {})
}

fn from_file(&self, _file : Box<dyn VirtualFile>) -> ForensicResult<Box<dyn VirtualFileSystem>> {
fn from_file(&self, _file: Box<dyn VirtualFile>) -> ForensicResult<Box<dyn VirtualFileSystem>> {
Err(crate::err::ForensicError::NoMoreData)
}

fn from_fs(&self, _fs : Box<dyn VirtualFileSystem>) -> ForensicResult<Box<dyn VirtualFileSystem>> {
fn from_fs(
&self,
_fs: Box<dyn VirtualFileSystem>,
) -> ForensicResult<Box<dyn VirtualFileSystem>> {
Err(crate::err::ForensicError::NoMoreData)
}
fn exists(&self, path : &Path) -> bool {
fn exists(&self, path: &Path) -> bool {
path.exists()
}
}

#[cfg(test)]
mod tst {
use std::path::PathBuf;
use std::io::Write;
use crate::traits::vfs::VirtualFileSystem;
use std::io::Write;
use std::path::Path;

use crate::core::fs::StdVirtualFS;

const CONTENT: &'static str = "File_Content_Of_VFS";
const FILE_NAME: &'static str = "test_vfs_file.txt";
const CONTENT: &str = "File_Content_Of_VFS";
const FILE_NAME: &str = "test_vfs_file.txt";

#[test]
fn test_temp_file() {

let tmp = std::env::temp_dir();
let tmp_file = tmp.join(FILE_NAME);
let mut file = std::fs::File::create(&tmp_file).unwrap();
file.write_all(CONTENT.as_bytes()).unwrap();
drop(file);

let mut std_vfs = StdVirtualFS::new();
test_file_content(&mut std_vfs,&tmp_file);
assert!(std_vfs.read_dir(tmp.as_path()).unwrap().into_iter().map(|v| v.to_string()).collect::<Vec<String>>().contains(&"test_vfs_file.txt".to_string()));
test_file_content(&mut std_vfs, &tmp_file);
assert!(std_vfs
.read_dir(tmp.as_path())
.unwrap()
.into_iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.contains(&"test_vfs_file.txt".to_string()));
}

fn test_file_content(std_vfs : &mut impl VirtualFileSystem, tmp_file : &PathBuf) {
fn test_file_content(std_vfs: &mut impl VirtualFileSystem, tmp_file: &Path) {
let content = std_vfs.read_to_string(tmp_file).unwrap();
assert_eq!(CONTENT, content);

}

#[test]
fn should_allow_boxing() {
struct Test {
_fs : Box<dyn VirtualFileSystem>
_fs: Box<dyn VirtualFileSystem>,
}
let boxed = Box::new(StdVirtualFS::new());
Test {
_fs : boxed
};

Test { _fs: boxed };
}
}
}
5 changes: 5 additions & 0 deletions src/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub enum ForensicError {
BadFormat(BadFormatError),
Io(std::io::Error),
CastError,
IllegalTimestamp(String)
}

impl ForensicError {
Expand Down Expand Up @@ -62,6 +63,7 @@ impl Clone for ForensicError {
Self::Missing(e) => Self::Missing(e.clone()),
Self::BadFormat(e) => Self::BadFormat(e.clone()),
Self::Io(e) => Self::Io(std::io::Error::new(e.kind(), e.to_string())),
Self::IllegalTimestamp(reason) => Self::IllegalTimestamp(reason.clone()),
}
}
}
Expand All @@ -75,6 +77,7 @@ impl PartialEq for ForensicError {
(Self::PermissionError, Self::PermissionError) => true,
(Self::NoMoreData, Self::NoMoreData) => true,
(Self::CastError, Self::CastError) => true,
(Self::IllegalTimestamp(l0), Self::IllegalTimestamp(r0)) => l0 == r0,
_ => false
}
}
Expand Down Expand Up @@ -141,6 +144,7 @@ impl std::fmt::Display for ForensicError {
ForensicError::BadFormat(e) => f.write_fmt(format_args!("The data have an unexpected format: {}", e)),
ForensicError::Io(e) => f.write_fmt(format_args!("IO operations error: {}", e)),
ForensicError::CastError => f.write_str("The Into/Form operation cannot be executed"),
ForensicError::IllegalTimestamp(reason) => f.write_fmt(format_args!("Illegal timestamp: '{reason}'"))
}
}
}
Expand All @@ -159,6 +163,7 @@ impl std::error::Error for ForensicError {
ForensicError::BadFormat(e) => &e.0,
ForensicError::Io(_) => "IO operations error",
ForensicError::CastError => "The Into/Form operation cannot be executed",
ForensicError::IllegalTimestamp(_) => "Illegal timestamp"
}
}

Expand Down
Loading

0 comments on commit ea3916b

Please sign in to comment.