From 6e00246a3aaf2fdab67f3083ca5bd3e0cc016338 Mon Sep 17 00:00:00 2001 From: Dominik Wernberger Date: Sat, 1 Jun 2024 13:33:13 +0200 Subject: [PATCH] Add tools to unpack and pack a CFB file --- cfb_utils/.gitignore | 1 + cfb_utils/Cargo.toml | 19 ++++++++ cfb_utils/README.md | 17 +++++++ cfb_utils/src/bin/cfb_pack.rs | 77 +++++++++++++++++++++++++++++++ cfb_utils/src/bin/cfb_unpack.rs | 82 +++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 cfb_utils/.gitignore create mode 100644 cfb_utils/Cargo.toml create mode 100644 cfb_utils/README.md create mode 100644 cfb_utils/src/bin/cfb_pack.rs create mode 100644 cfb_utils/src/bin/cfb_unpack.rs diff --git a/cfb_utils/.gitignore b/cfb_utils/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/cfb_utils/.gitignore @@ -0,0 +1 @@ +/target diff --git a/cfb_utils/Cargo.toml b/cfb_utils/Cargo.toml new file mode 100644 index 0000000..8f8c7f4 --- /dev/null +++ b/cfb_utils/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cfb_utils" +version = "0.0.1" +edition = "2021" + +[dependencies] +cfb = "0.10.0" +walkdir = "2" +pathdiff = "0.1.0" +path-slash = "0.2.1" +byteorder = "1.5.0" + +[[bin]] +name = "cfb_pack" +path = "src/bin/cfb_pack.rs" + +[[bin]] +name = "cfb_unpack" +path = "src/bin/cfb_unpack.rs" diff --git a/cfb_utils/README.md b/cfb_utils/README.md new file mode 100644 index 0000000..7b27a3c --- /dev/null +++ b/cfb_utils/README.md @@ -0,0 +1,17 @@ +# CFB Utils + +Rust based CLI tools to extract CFB containers and rebuild them. Streams become files and storages become directories while unpacking, packing behaves vice versa. + +## Usage + +```bash +cargo build + +# Unpack your CFB file +cargo run --bin cfb_unpack YOUR_CFB.DSN ./output_dir + +# Modify streams + +# Pack the folder to a CFB file again +cargo run --bin cfb_pack ./output_dir YOUR_CFB2.DSN +``` diff --git a/cfb_utils/src/bin/cfb_pack.rs b/cfb_utils/src/bin/cfb_pack.rs new file mode 100644 index 0000000..2d5a23a --- /dev/null +++ b/cfb_utils/src/bin/cfb_pack.rs @@ -0,0 +1,77 @@ +use cfb; +use std::env; +use std::path::{Path, PathBuf}; +extern crate pathdiff; + +use std::fs; +use std::io::Write; +use std::process::exit; + +use walkdir::WalkDir; + +fn help() { + println!( + "usage: cfb_pack +Generates 'output_cfb_file' from 'unpacked_cfb_root_dir', where all all +directories become 'storage's and all files become 'stream's." + ); +} + +fn cfb_pack(output_cfb: &Path, unpacked_cfb_dir: &Path) { + println!("Packing: {:#?}", unpacked_cfb_dir); + println!("Into: {:#?}", output_cfb); + + let mut cfb = cfb::create(output_cfb).unwrap(); + + for entry in WalkDir::new(unpacked_cfb_dir) { + let entry = entry.unwrap(); + + let a = pathdiff::diff_paths(entry.path(), unpacked_cfb_dir).unwrap(); + let rel_path = a.as_path(); + + let tmp_str = rel_path.to_str().unwrap().replace(r"\", "/"); + let mut internal_path = String::from("/"); + internal_path.push_str(&tmp_str); + + // Skip root storage + if internal_path == "/" { + continue; + } + + if entry.metadata().unwrap().is_file() { + println!(" Creating stream: {:#?}", internal_path); + let data: Vec = fs::read(entry.path()).unwrap(); + // @TODO: Need to create parent storages first. Otherwise create_stream fails + let mut stream = cfb.create_stream(&internal_path).unwrap(); + stream.write_all(&data).unwrap(); + } + + if entry.metadata().unwrap().is_dir() { + println!(" Creating storage: {:#?}", internal_path); + cfb.create_storage_all(&internal_path).unwrap(); + } + } +} + +fn main() { + let args: Vec = env::args().collect(); + + let mut output_cfb = Path::new(""); + let mut unpacked_cfb_dir = Path::new(""); + + match args.len() { + 3 => { + unpacked_cfb_dir = Path::new(&args[1]); + output_cfb = Path::new(&args[2]); + } + _ => { + help(); + exit(1); + } + } + + let tmp: PathBuf = unpacked_cfb_dir.canonicalize().unwrap(); + let unpacked_cfb_dir = Path::new(&tmp); + + cfb_pack(output_cfb, unpacked_cfb_dir); +} diff --git a/cfb_utils/src/bin/cfb_unpack.rs b/cfb_utils/src/bin/cfb_unpack.rs new file mode 100644 index 0000000..16c0cfb --- /dev/null +++ b/cfb_utils/src/bin/cfb_unpack.rs @@ -0,0 +1,82 @@ +use cfb; +use std::path::{Path, PathBuf}; +extern crate pathdiff; +use std::env; +use std::fs; +use std::fs::File; +use std::io::{Read, Write}; +use std::process::exit; + +fn help() { + println!( + "usage: cfb_unpack +Unpacks 'input_cfb_file' to 'unpacked_cfb_root_dir', where all all +'storages's become directories and all 'stream's become files." + ); +} + +fn construct_external_path(cfb_path: &Path, fs_root_dir: &Path) -> PathBuf { + // Strip `cfb_path` from leading `/` + let rel_internal_path = Path::new(&cfb_path.to_str().unwrap()[1..]); + let tmp_str_path = fs_root_dir.join(rel_internal_path); + tmp_str_path +} + +fn cfb_unpack(input_cfb: &Path, unpacked_cfb_dir: &Path) { + println!("Unpacking: {:#?}", input_cfb); + println!("Into: {:#?}", unpacked_cfb_dir); + + let mut cfb = cfb::open(input_cfb).unwrap(); + + fs::create_dir_all(&unpacked_cfb_dir).unwrap(); + + let entries = cfb.walk().collect::>(); + + for entry in entries { + let external_path = construct_external_path(entry.path(), unpacked_cfb_dir); + + if entry.is_storage() { + println!(" Creating storage: {:#?}", external_path); + fs::create_dir_all(&external_path).unwrap(); + } + + if entry.is_stream() { + println!(" Creating stream: {:#?}", external_path); + + let data = { + let mut stream = cfb.open_stream(&entry.path()).unwrap(); + let mut buffer = Vec::new(); + stream.read_to_end(&mut buffer).unwrap(); + buffer + }; + + fs::create_dir_all(&external_path.parent().unwrap()).unwrap(); + let mut file = File::create(external_path).unwrap(); + // Write a slice of bytes to the file + file.write_all(&data).unwrap(); + } + } +} + +fn main() { + let args: Vec = env::args().collect(); + + let mut input_cfb = Path::new(""); + let mut unpacked_cfb_dir = Path::new(""); + + match args.len() { + 3 => { + input_cfb = Path::new(&args[1]); + unpacked_cfb_dir = Path::new(&args[2]); + } + _ => { + help(); + exit(1); + } + } + + let tmp: PathBuf = input_cfb.canonicalize().unwrap(); + let input_cfb = Path::new(&tmp); + + cfb_unpack(input_cfb, unpacked_cfb_dir); +}