diff --git a/rpmust/.gitignore b/rpmust/.gitignore index ae9ea50078ef99e806d95bb3ac4275a15df0c52f..2d8007368f494587fcffb9ec83db7a2939c368c1 100644 --- a/rpmust/.gitignore +++ b/rpmust/.gitignore @@ -19,3 +19,7 @@ Cargo.lock # IDE .idea + +# rpmust +rpmust/*.yaml +rpmust/*.cpio \ No newline at end of file diff --git a/rpmust/Cargo.toml b/rpmust/Cargo.toml index d03d1767bffb186197905a7617c575a42dd2a42c..f895f2b6e2fa28236e993511b34698e49d35fe88 100644 --- a/rpmust/Cargo.toml +++ b/rpmust/Cargo.toml @@ -15,15 +15,6 @@ num = "0.4" enum-primitive-derive = "0.2" enum-display-derive = "0.1" cpio = "0.2" -# consider migrating to flate2 -libflate = "1" -sha2 = "0.9" -md-5 = "0.9" -sha1 = "0.6" -rand = { version = "0.8" } -pgp = { version="0.7.2", optional = true } -chrono = "0.4" -log = "0.4" -itertools = "0.10" -hex = { version = "0.4", features = ["std"] } -zstd = "0.9.0" \ No newline at end of file +clap = { version = "3.0.14", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.8" \ No newline at end of file diff --git a/rpmust/src/main.rs b/rpmust/src/main.rs index 93ae3f7b7fdab30057693cd90aa0d71d1a7e077d..f320e4d27ac2e9f518ba85ff8259332688bced48 100644 --- a/rpmust/src/main.rs +++ b/rpmust/src/main.rs @@ -1,25 +1,103 @@ +/*! +本项目是用rust解析rpm文件,开发文档请参考:https://openeuler.feishu.cn/docs/doccnBADaRx5bdfu2zHjqNfnPKe + +USAGE: + rpmust [SUBCOMMAND] + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +SUBCOMMANDS: + build merge the RPMPackageMetadata.yaml and out.cpio to a rpm file + decode turn the rpm file to RPMPackageMetadata.yaml and out.cpio + help Print this message or the help of the given subcommand(s) + +例如: + +```bash +$ rpmust decode ../../src/test/stratovirt.rpm +$ rpmust build ./RPMPackageMetadata.yaml + +``` + +*/ + use std::io; use std::io::prelude::*; use std::fs::File; -use std::env::*; -pub mod rpm; use rpm::*; +extern crate clap; + +use clap::{Arg, App, arg}; + +pub mod rpm; + fn main() -> io::Result<()> { - let mut file = std::fs::File::open("./test/stratovirt.rpm").expect("should be able to open rpm file"); - let file_size = file.metadata().unwrap().len(); - println!("{}",file_size); - let mut buf_reader = std::io::BufReader::with_capacity(file_size as usize,file); - let rpmmeta = RPMPackageMetadata::parse(&mut buf_reader); - let rpm = rpmmeta.unwrap(); + let matches = App::new("rpmust") + .version("0.1.0") + .author("Guan jian ") + .subcommand( + App::new("decode") + .about("turn the rpm file to RPMPackageMetadata.yaml and out.cpio") + .arg(arg!( ... "The path of rpm file")) + ) + .subcommand( + App::new("build") + .about("merge the RPMPackageMetadata.yaml and out.cpio to a rpm file") + .arg(arg!( ... "The path of RPMPackageMetadata.yaml")) + ) + .get_matches(); + + match matches.subcommand() { + Some(("decode", _sub_matches)) => { + /// get the rpm file path + let file_address = _sub_matches.value_of("PATH"); + let mut file = std::fs::File::open(file_address.unwrap()).expect("should be able to open rpm file"); + + /// get size of rpm file + /// in order to caculate the start + /// of cpio + let file_size = file.metadata().unwrap().len(); + let mut buf_reader = std::io::BufReader::with_capacity(file_size as usize,file); + let rpmmeta = RPMPackageMetadata::parse(&mut buf_reader); + let rpm = rpmmeta.unwrap(); - //file.read_to_end(& mut buf_reader); - println!("{:#?}",rpm.signature.index_entries); - println!("{:#?}",rpm.header.index_entries); + /// output the RPMPackageMetadata.yaml + let s = serde_yaml::to_string(&rpm).unwrap(); + let mut buffer = File::create("RPMPackageMetadata.yaml").unwrap(); + buffer.write_all(s.as_bytes())?; + /// output the out.cpio + let mut out_file = File::create("out.cpio")?; + out_file.write_all(buf_reader.fill_buf().unwrap())?; + } + Some(("build", _sub_matches)) => { + let yaml_path = _sub_matches.value_of("PATH"); + let mut file = std::fs::File::open(yaml_path.unwrap()).unwrap(); + let mut yaml_str = String::new(); + file.read_to_string(&mut yaml_str).unwrap(); + let rpm: RPMPackageMetadata = serde_yaml::from_str(&yaml_str).expect("yaml read failed!"); + println!("{:#?}",rpm.signature.index_entries); + println!("{:#?}",rpm.header.index_entries); + } + _ => {}, + } + + - let mut out_file = File::create("out.cpio")?; + + - out_file.write_all(buf_reader.fill_buf().unwrap()); + + + + + + + + + Ok(()) } diff --git a/rpmust/src/rpm/headers/constants.rs b/rpmust/src/rpm/headers/constants.rs index 063cec187b4fa78987a0e29bf9d929b2edf8e6b2..22205483a0e51a89edb09884ae70e570d923adc1 100644 --- a/rpmust/src/rpm/headers/constants.rs +++ b/rpmust/src/rpm/headers/constants.rs @@ -4,6 +4,7 @@ //! C headers. use std::fmt::Display; +use serde::{Serialize, Deserialize}; pub const HEADER_IMAGE: isize = 61; pub const HEADER_SIGNATURES: isize = 62; @@ -22,6 +23,8 @@ pub const RPMTAG_SIG_BASE: isize = HEADER_SIGBASE; Copy, Clone, enum_display_derive::Display, + Serialize, + Deserialize, )] #[allow(non_camel_case_types)] pub enum IndexTag { @@ -365,6 +368,8 @@ pub enum IndexTag { Copy, Clone, enum_display_derive::Display, + Serialize, + Deserialize, )] #[allow(non_camel_case_types)] pub enum IndexSignatureTag { diff --git a/rpmust/src/rpm/headers/header.rs b/rpmust/src/rpm/headers/header.rs index 01f7e3db179750e78f8246584cb705d211968404..006ab24bb38944546a5e1f560050893cd1d1ceb2 100644 --- a/rpmust/src/rpm/headers/header.rs +++ b/rpmust/src/rpm/headers/header.rs @@ -1,4 +1,3 @@ - use std::fmt; use nom::bytes::complete; use nom::number::complete::{be_i16, be_i32, be_i64, be_i8, be_u32, be_u8}; @@ -6,6 +5,7 @@ use num::*; use super::constants::{self,*}; use std::convert::TryInto; use super::errors::*; +use serde::{Serialize, Deserialize}; pub trait Tag: num::FromPrimitive + num::ToPrimitive + PartialEq + fmt::Display + fmt::Debug + Copy + TypeName @@ -23,16 +23,13 @@ impl Tag for T where { } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Header { pub(crate) index_header: IndexHeader, pub(crate) index_entries: Vec>, pub(crate) store: Vec, } - - - impl Header where T: Tag, @@ -116,7 +113,7 @@ where }) } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub(crate) struct IndexHeader { /// rpm specific magic header pub(crate) magic: [u8; 3], @@ -161,7 +158,7 @@ impl IndexHeader { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub(crate) struct IndexEntry { pub(crate) tag: T, pub(crate) data: IndexData, @@ -224,7 +221,7 @@ impl Header { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub(crate) enum IndexData { Null, Char(Vec), diff --git a/rpmust/src/rpm/headers/lead.rs b/rpmust/src/rpm/headers/lead.rs index 95c6788884d3399d5023d09cf534ca06a94fdd3a..6a656dfd31978d37f77215a52cfe819f6e444af1 100644 --- a/rpmust/src/rpm/headers/lead.rs +++ b/rpmust/src/rpm/headers/lead.rs @@ -1,15 +1,22 @@ use nom::bytes::complete; use nom::number::complete::{be_u16, be_u8}; - +use std::io::prelude::*; +use std::fmt; use super::constants::*; use super::errors::*; +use serde::{Serialize, Deserialize}; +use std::marker::PhantomData; +use serde::ser::{Serializer, SerializeTuple}; +use serde::de::{Deserializer, Visitor, SeqAccess, Error}; +#[derive(PartialEq, Serialize, Deserialize)] pub struct Lead { magic: [u8; 4], major: u8, minor: u8, package_type: u16, arch: u16, + #[serde(with = "BigArray")] name: [u8; 66], os: u16, signature_type: u16, @@ -95,4 +102,59 @@ impl Lead { reserved: rest.try_into().unwrap(), }) } +} + +/// impl the serialize and deserialize for [T; 66] +/// 66 is a unusual number so they didn't impl +trait BigArray<'de>: Sized { + fn serialize(&self, serializer: S) -> Result + where S: Serializer; + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de>; +} + +impl<'de, T> BigArray<'de> for [T; 66] + where T: Default + Copy + Serialize + Deserialize<'de> +{ + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + let mut seq = serializer.serialize_tuple(self.len())?; + for elem in &self[..] { + seq.serialize_element(elem)?; + } + seq.end() + } + + fn deserialize(deserializer: D) -> Result<[T; 66], D::Error> + where D: Deserializer<'de> + { + struct ArrayVisitor { + element: PhantomData, + } + + impl<'de, T> Visitor<'de> for ArrayVisitor + where T: Default + Copy + Deserialize<'de> + { + type Value = [T; 66]; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("an array of length ", 66)) + } + + fn visit_seq(self, mut seq: A) -> Result<[T; 66], A::Error> + where A: SeqAccess<'de> + { + let mut arr = [T::default(); 66]; + for i in 0..66 { + arr[i] = seq.next_element()? + .ok_or_else(|| Error::invalid_length(i, &self))?; + } + Ok(arr) + } + } + + let visitor = ArrayVisitor { element: PhantomData }; + deserializer.deserialize_tuple(66, visitor) + } } \ No newline at end of file diff --git a/rpmust/src/rpm/rpmmeta.rs b/rpmust/src/rpm/rpmmeta.rs index ee7a160dbecff70ce859698428f68bb465d4befc..e186f24735ae89db0571e931df071af2ee0d44cc 100644 --- a/rpmust/src/rpm/rpmmeta.rs +++ b/rpmust/src/rpm/rpmmeta.rs @@ -1,5 +1,7 @@ use super::headers::*; +use serde::{Serialize, Deserialize}; +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct RPMPackageMetadata { pub lead: Lead, pub signature: Header,