From ad83f3805633e647d4de3d2354e5acdd5e700913 Mon Sep 17 00:00:00 2001 From: trivernis Date: Sun, 1 May 2022 18:03:39 +0200 Subject: [PATCH] Add support for xz format Signed-off-by: trivernis --- .gitignore | 4 +++- Cargo.lock | 26 ++++++++++++++++++++++++++ Cargo.toml | 1 + src/format/mod.rs | 10 +++++++++- src/format/tar.rs | 1 + src/format/xz.rs | 36 ++++++++++++++++++++++++++++++++++++ src/format/zip.rs | 3 +++ src/headers.rs | 1 - src/main.rs | 6 ------ 9 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 src/format/tar.rs create mode 100644 src/format/xz.rs diff --git a/.gitignore b/.gitignore index 1eb9fe0..b888e11 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ *.gz *.xz *.zip -out \ No newline at end of file +test.txt +out +out.txt \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 10f7cae..fbdee7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "build_const" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" + [[package]] name = "byteorder" version = "1.4.3" @@ -229,6 +235,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +dependencies = [ + "build_const", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -276,6 +291,7 @@ dependencies = [ "anyhow", "clap", "color-eyre", + "lzma-rs", "tracing", "tracing-subscriber", "zip", @@ -401,6 +417,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lzma-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba8ecb0450dfabce4ad72085eed0a75dffe8f21f7ada05638564ea9db2d7fb1" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "matchers" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 584eca6..84d4fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ anyhow = "1.0.57" color-eyre = "0.6.1" tracing = "0.1.34" zip = "0.6.2" +lzma-rs = "0.2.0" tracing-subscriber = {version = "0.3.11", features = ["env-filter"]} [dependencies.clap] diff --git a/src/format/mod.rs b/src/format/mod.rs index f3357ae..fade35f 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1,5 +1,8 @@ +mod tar; +mod xz; mod zip; +use crate::format::xz::XZFormat; use crate::format::zip::ZipFormat; use anyhow::{bail, Result}; use std::fs::File; @@ -8,7 +11,7 @@ use std::path::Path; pub enum Format { Zip(ZipFormat), - Xz, + Xz(XZFormat), Gz, Tar, } @@ -26,7 +29,11 @@ pub trait FileFormat: Sized { impl FileFormat for Format { fn parse(file: &FileObject) -> Result { if let Ok(zip) = ZipFormat::parse(file) { + tracing::info!("Detected zip format"); Ok(Self::Zip(zip)) + } else if let Ok(xz) = XZFormat::parse(file) { + tracing::info!("Detected xz format"); + Ok(Self::Xz(xz)) } else { bail!("Unknown file format"); } @@ -35,6 +42,7 @@ impl FileFormat for Format { fn extract(&self, file: &Path, output: &Path) -> Result<()> { match self { Format::Zip(zip) => zip.extract(file, output), + Format::Xz(xz) => xz.extract(file, output), _ => bail!("Not implemented"), } } diff --git a/src/format/tar.rs b/src/format/tar.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/format/tar.rs @@ -0,0 +1 @@ + diff --git a/src/format/xz.rs b/src/format/xz.rs new file mode 100644 index 0000000..05de7ea --- /dev/null +++ b/src/format/xz.rs @@ -0,0 +1,36 @@ +use crate::format::{FileFormat, FileObject}; +use anyhow::{bail, Context}; +use std::fs::File; +use std::io; +use std::io::BufReader; +use std::path::Path; + +pub const XZ_HEADER: &[u8] = &[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]; + +pub struct XZFormat; + +impl FileFormat for XZFormat { + fn parse(file: &FileObject) -> anyhow::Result { + if file.header.starts_with(XZ_HEADER) { + if !file.ext.ends_with("xz") && !file.ext.ends_with("lzma") && !file.ext.ends_with("7z") + { + tracing::warn!("The file has a xz signature but not a xz extension."); + } + Ok(Self) + } else { + bail!("Not an xz file"); + } + } + + fn extract(&self, file: &Path, output: &Path) -> anyhow::Result<()> { + if output.is_dir() { + bail!("The given output is a directory"); + } + let mut reader = BufReader::new(File::open(&file).context("Opening input file")?); + let mut output = File::create(&output).context("Creating output file")?; + tracing::debug!("Decompressing file {file:?} to output {output:?}"); + lzma_rs::xz_decompress(&mut reader, &mut output).context("Decompressing file")?; + + Ok(()) + } +} diff --git a/src/format/zip.rs b/src/format/zip.rs index b1f207e..b1c455b 100644 --- a/src/format/zip.rs +++ b/src/format/zip.rs @@ -22,6 +22,9 @@ impl FileFormat for ZipFormat { } fn extract(&self, file: &Path, output: &Path) -> Result<()> { + if output.is_file() { + bail!("The given output is a file"); + } let file = File::open(file)?; let mut archive = ZipArchive::new(file).context("Opening zip file")?; tracing::info!("Zip file with {} entries", archive.len()); diff --git a/src/headers.rs b/src/headers.rs index 30a1403..42b2cf1 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -2,4 +2,3 @@ /// Compression Methods pub const GZIP_HEADER: &[u8] = &[0x1f, 0x8b]; -pub const XZ_HEADER: &[u8] = &[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]; diff --git a/src/main.rs b/src/main.rs index d0a9ddb..749b1a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,12 +32,6 @@ fn main() { let args: Args = Args::parse(); match args.operation { Operation::Extract { output, file } => { - if !output.exists() { - fs::create_dir_all(&output).expect("Failed to create output directory"); - } - if output.is_file() { - panic!("The given output path is a file"); - } let format = parse_format(&file).expect("Failed to parse file format"); format .extract(&file, &output)