From 85a85a3810d4f7153ea2b41439033a573b0f9b45 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Thu, 23 Mar 2023 14:43:25 -0400 Subject: [PATCH] Add `Item` struct, which contains both a `tar::Node` and a plist entry. This struct can be created from just a `Path` parameter, allowing to read the data contained in a file only once during package creation or extraction. --- src/item/mod.rs | 53 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/package/dependency.rs | 2 +- src/package/mod.rs | 16 +++++++++++- src/plist/mod.rs | 5 ++-- src/version/gitrev.rs | 2 +- src/version/mod.rs | 2 +- src/version/semver.rs | 2 +- tar/src/header.rs | 9 ++++--- tar/src/lib.rs | 2 +- tar/src/node.rs | 9 +++++-- 11 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 src/item/mod.rs diff --git a/src/item/mod.rs b/src/item/mod.rs new file mode 100644 index 0000000..1031729 --- /dev/null +++ b/src/item/mod.rs @@ -0,0 +1,53 @@ +use crate::Entry; +use sha2::{Digest, Sha256}; +use std::{error::Error, fmt::Write, fs, path::{Path, PathBuf}, io::Read, os::unix::fs::MetadataExt}; +use tar::{Node, Owner}; + +#[derive(Clone, Debug)] +pub struct Item { + pub entry: Entry, + pub node: Node, +} + +impl Item { + pub fn try_create(path: &Path) -> Result> { + let meta = fs::metadata(path)?; + let filename = format!("/{}", path.display()); + let owner = Some(Owner::default()); + if meta.is_file() { + let mut data = vec![]; + let mut fd = fs::File::open(path)?; + let path = PathBuf::from(&filename); + let size = fd.read_to_end(&mut data)?; + let mut sha256sum = String::new(); + let mut hasher = Sha256::new(); + hasher.update(&data); + let res = hasher.finalize(); + for c in res { + write!(sha256sum, "{c:02x}")?; + } + let node = Node::read_data_to_tar(&data, &filename, &meta, owner)?; + let mode = meta.mode(); + Ok(Self { + entry: Entry::File { path, sha256sum, mode, size }, + node, + }) + } else if meta.is_dir() { + let mode = meta.mode(); + let path = PathBuf::from(&filename); + Ok(Self { + entry: Entry::Directory { path, mode }, + node: Node::read_data_to_tar(&[0; 0], &filename, &meta, owner)?, + }) + } else if meta.is_symlink() { + let target = fs::read_link(path)?; + let path = PathBuf::from(&filename); + Ok(Self { + entry: Entry::Link { path, target }, + node: Node::read_data_to_tar(&[0; 0], &filename, &meta, owner)?, + }) + } else { + unreachable!(); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index cd6305a..37c0913 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ #![warn(clippy::all, clippy::pedantic)] +mod item; mod package; mod plist; mod version; pub use { + item::Item, package::{Dependency, Package}, plist::*, version::*, diff --git a/src/package/dependency.rs b/src/package/dependency.rs index ce267bf..966c0c1 100644 --- a/src/package/dependency.rs +++ b/src/package/dependency.rs @@ -4,7 +4,7 @@ use { serde::{Deserialize, Serialize}, }; -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Dependency { name: String, version: (Option, Option), diff --git a/src/package/mod.rs b/src/package/mod.rs index 6ce1e99..a6529ff 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -5,7 +5,19 @@ use { serde::{Deserialize, Serialize}, }; -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct User { + pub name: String, + pub uid: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct Group { + pub name: String, + pub gid: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Package { name: String, version: Version, @@ -13,4 +25,6 @@ pub struct Package { plist: Plist, size: usize, dependencies: Vec, + users: Option>, + groups: Option>, } diff --git a/src/plist/mod.rs b/src/plist/mod.rs index 847623c..216d810 100644 --- a/src/plist/mod.rs +++ b/src/plist/mod.rs @@ -13,7 +13,7 @@ use { walkdir::WalkDir, }; -#[derive(Deserialize, Serialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Plist { pub entries: Vec, } @@ -35,7 +35,7 @@ impl TryFrom<&Path> for Plist { } } -#[derive(Deserialize, Serialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum Entry { File { path: PathBuf, @@ -89,3 +89,4 @@ impl TryFrom<&Path> for Entry { } } } + diff --git a/src/version/gitrev.rs b/src/version/gitrev.rs index 94f14b2..ba18c62 100644 --- a/src/version/gitrev.rs +++ b/src/version/gitrev.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct GitRev { hash: String, datetime: u64, diff --git a/src/version/mod.rs b/src/version/mod.rs index 56e53c9..c8df478 100644 --- a/src/version/mod.rs +++ b/src/version/mod.rs @@ -5,7 +5,7 @@ mod semver; pub use {gitrev::GitRev, semver::SemVer}; -#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum Version { Number(u32), SemVer(SemVer), diff --git a/src/version/semver.rs b/src/version/semver.rs index 115386a..23c8438 100644 --- a/src/version/semver.rs +++ b/src/version/semver.rs @@ -3,7 +3,7 @@ use { std::cmp::Ordering, }; -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct SemVer { pub major: u32, pub minor: u32, diff --git a/tar/src/header.rs b/tar/src/header.rs index 85b5488..2479ca0 100644 --- a/tar/src/header.rs +++ b/tar/src/header.rs @@ -244,7 +244,11 @@ impl Header { Ok(header) } - pub fn new_from_meta(filename: &str, meta: &Metadata, owner: Option) -> Result { + pub fn new_from_meta( + filename: &str, + meta: &Metadata, + owner: Option, + ) -> Result { let mut header = Header::default(); header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); let mode = format!("{:07o}", meta.st_mode()); @@ -256,7 +260,7 @@ impl Header { gid: meta.st_gid(), username: get_username_for_uid(meta.st_uid())?.into(), groupname: get_groupname_for_gid(meta.st_gid())?.into(), - } + }, }; let uid = format!("{:07o}", owner.uid); header.uid[..uid.len()].copy_from_slice(uid.as_bytes()); @@ -365,4 +369,3 @@ pub fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Erro }; group.to_str() } - diff --git a/tar/src/lib.rs b/tar/src/lib.rs index 82ea03a..a8f22eb 100644 --- a/tar/src/lib.rs +++ b/tar/src/lib.rs @@ -8,7 +8,7 @@ mod header; mod node; pub use { error::Error, - header::{FileType, Header}, + header::{FileType, Header, Owner}, node::Node, }; diff --git a/tar/src/node.rs b/tar/src/node.rs index f53d7cd..a4aa2f8 100644 --- a/tar/src/node.rs +++ b/tar/src/node.rs @@ -1,4 +1,4 @@ -use crate::{Error, FileType, Header, header::Owner}; +use crate::{header::Owner, Error, FileType, Header}; use deku::prelude::*; use std::{ fs::{File, Metadata}, @@ -67,7 +67,12 @@ impl Node { } /// Create a Node from in memory data, given the filename and metadata - pub fn read_data_to_tar(data: &[u8], filename: &str, meta: &Metadata, owner: Option) -> Result { + pub fn read_data_to_tar( + data: &[u8], + filename: &str, + meta: &Metadata, + owner: Option, + ) -> Result { let header = Header::new_from_meta(filename, meta, owner)?; if header.link_indicator[0] != FileType::Normal as u8 { return Ok(Node {