Skip to content

Commit

Permalink
Add tools to unpack and pack a CFB file
Browse files Browse the repository at this point in the history
  • Loading branch information
Werni2A committed Jun 1, 2024
1 parent b793d47 commit 6e00246
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 0 deletions.
1 change: 1 addition & 0 deletions cfb_utils/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
19 changes: 19 additions & 0 deletions cfb_utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
17 changes: 17 additions & 0 deletions cfb_utils/README.md
Original file line number Diff line number Diff line change
@@ -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
```
77 changes: 77 additions & 0 deletions cfb_utils/src/bin/cfb_pack.rs
Original file line number Diff line number Diff line change
@@ -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 <unpacked_cfb_root_dir> <output_cfb_file>
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<u8> = 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<String> = 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);
}
82 changes: 82 additions & 0 deletions cfb_utils/src/bin/cfb_unpack.rs
Original file line number Diff line number Diff line change
@@ -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 <input_cfb_file> <unpacked_cfb_root_dir>
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::<Vec<_>>();

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<String> = 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);
}

0 comments on commit 6e00246

Please sign in to comment.