diff --git a/Cargo.lock b/Cargo.lock index c07f64b..484e750 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" @@ -226,7 +226,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.12", + "syn 2.0.13", ] [[package]] @@ -243,7 +243,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.13", ] [[package]] @@ -380,24 +380,36 @@ dependencies = [ name = "hpk" version = "0.1.0" dependencies = [ - "chrono", - "deku", + "hpk-package", "rayon", - "ron", "serde", - "sha2", - "tar", "ureq", "url", "walkdir", "zstd", ] +[[package]] +name = "hpk-package" +version = "0.1.0" +source = "git+https://git.hitchhiker-linux.org/jeang3nie/hpk-package.git#90b163eb1b7373085d3c5eb93919eb8ecfedb219" +dependencies = [ + "chrono", + "deku", + "libc", + "rayon", + "ron", + "serde", + "sha2", + "thiserror", + "walkdir", +] + [[package]] name = "iana-time-zone" -version = "0.1.55" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716f12fbcfac6ffab0a5e9ec51d0a0ff70503742bb2dc7b99396394c9dc323f0" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -574,9 +586,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.54" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -757,7 +769,7 @@ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.13", ] [[package]] @@ -807,9 +819,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.12" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", @@ -822,15 +834,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tar" -version = "0.1.0" -dependencies = [ - "deku", - "libc", - "thiserror", -] - [[package]] name = "termcolor" version = "1.2.0" @@ -857,7 +860,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.13", ] [[package]] @@ -1110,9 +1113,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ "windows-targets", ] @@ -1134,17 +1137,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.47.0", - "windows_aarch64_msvc 0.47.0", - "windows_i686_gnu 0.47.0", - "windows_i686_msvc 0.47.0", - "windows_x86_64_gnu 0.47.0", - "windows_x86_64_gnullvm 0.47.0", - "windows_x86_64_msvc 0.47.0", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -1155,9 +1158,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" @@ -1167,9 +1170,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" @@ -1179,9 +1182,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" @@ -1191,9 +1194,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" @@ -1203,9 +1206,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" @@ -1215,9 +1218,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" @@ -1227,9 +1230,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" diff --git a/Cargo.toml b/Cargo.toml index add8609..546c114 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,25 +6,12 @@ license = "GPL-3.0-only" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[workspace] -members = [ "tar" ] - -[workspace.dependencies] -deku = "0.16" - [dependencies] -deku.workspace = true +hpk-package = { git = "https://git.hitchhiker-linux.org/jeang3nie/hpk-package.git" } rayon = "1.7" -ron = "0.8" -sha2 = "0.10" -tar = { path = "tar" } walkdir = "2.3" zstd = "0.12" -[dependencies.chrono] -version = "0.4" -features = ["serde"] - [dependencies.serde] version = "1.0" features = ["derive"] diff --git a/src/creator/mod.rs b/src/creator/mod.rs index e143601..6c184fd 100644 --- a/src/creator/mod.rs +++ b/src/creator/mod.rs @@ -1,5 +1,6 @@ use { - crate::{Entry, Item, Package, Plist, Specs}, + crate::{Item, Package, Plist, Specs}, + hpk_package::Entry, rayon::prelude::{IntoParallelRefIterator, ParallelIterator}, std::{ borrow::BorrowMut, diff --git a/src/db/mod.rs b/src/db/mod.rs index 0dad30c..fc33e1b 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,7 +1,7 @@ use { crate::{Package, Repository, Version}, + hpk_package::ron::{self, ser::PrettyConfig}, rayon::prelude::*, - ron::ser::PrettyConfig, serde::{Deserialize, Serialize}, std::{ collections::HashMap, diff --git a/src/item/mod.rs b/src/item/mod.rs index cef089a..33829d0 100644 --- a/src/item/mod.rs +++ b/src/item/mod.rs @@ -1,5 +1,8 @@ -use crate::Entry; -use sha2::{Digest, Sha256}; +use hpk_package::{ + sha2::{Digest, Sha256}, + tar::{Node, Owner}, + Entry, +}; use std::{ error::Error, ffi::OsStr, @@ -9,7 +12,6 @@ use std::{ os::unix::fs::MetadataExt, path::{Path, PathBuf}, }; -use tar::{Node, Owner}; #[derive(Clone, Debug)] pub struct Item { diff --git a/src/lib.rs b/src/lib.rs index dd387ee..9fae73f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,7 @@ mod creator; mod db; mod hooks; mod item; -mod package; -mod plist; mod repository; -mod version; use std::path::PathBuf; @@ -14,12 +11,9 @@ pub use { creator::{Creator, Message}, db::Database, hooks::Hooks, + hpk_package::{tar, Dependency, GitRev, Package, Plist, Rapid, SemVer, Specs, Version}, item::Item, - package::{Dependency, Package, Specs}, - plist::*, repository::Repository, - tar, - version::*, }; const DB: [&str; 4] = ["var", "db", "hpk", "db.ron.zstd"]; diff --git a/src/package/dependency.rs b/src/package/dependency.rs deleted file mode 100644 index 64cbc29..0000000 --- a/src/package/dependency.rs +++ /dev/null @@ -1,28 +0,0 @@ -use { - super::Package, - crate::Version, - serde::{Deserialize, Serialize}, -}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct Dependency { - pub name: String, - pub version: (Option, Option), -} - -impl Dependency { - #[allow(clippy::must_use_candidate)] - pub fn satisfied(&self, package: &Package) -> bool { - if self.name.as_str() == package.name.as_str() { - match &self.version { - (Some(low), Some(high)) => &package.version >= low && &package.version < high, - (Some(low), None) => &package.version >= low, - (None, Some(high)) => &package.version < high, - // no version requirements - _ => true, - } - } else { - false - } - } -} diff --git a/src/package/mod.rs b/src/package/mod.rs deleted file mode 100644 index 16bc67c..0000000 --- a/src/package/mod.rs +++ /dev/null @@ -1,122 +0,0 @@ -mod dependency; -mod specs; - -use { - crate::{Plist, Version}, - ron::ser::{to_string_pretty, PrettyConfig}, - serde::{Deserialize, Serialize}, - std::{ - error::Error, - fs, - fs::File, - io::{BufWriter, Write}, - path::Path, - }, - tar::{Node, Owner}, -}; - -pub use {dependency::Dependency, specs::Specs}; - -#[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, Default, Serialize, Deserialize, PartialEq)] -/// the metadata associated with a package -pub struct Package { - /// The name of the package minus all version information - pub name: String, - /// The `Version` of the package - pub version: Version, - /// The release number for this package - pub release: u8, - /// a single line description of the package - pub description: String, - /// a more verbose description of the package - pub long_description: String, - /// an optional link to an - /// [AppStream](https://www.freedesktop.org/wiki/Distributions/AppStream/) - /// metadata file - pub appstream_data: Option, - /// a listing of all files, directories and symlinks which are a part of - /// this package - pub plist: Plist, - /// the total size of this package - pub size: usize, - /// all of this package's runtime dependencies - pub dependencies: Vec, - /// an optional list of users to be created upon installation - pub users: Option>, - /// an optional list of groups to be created upon installation - pub groups: Option>, - /// an optional post installation shell script to be run - pub post_install: Option, -} - -impl From for Package { - fn from(value: Specs) -> Self { - Package { - name: value.name, - version: value.version, - release: value.release, - description: value.description, - long_description: value.long_description, - appstream_data: value.appstream_data, - dependencies: value.dependencies, - users: value.users, - groups: value.groups, - post_install: value.post_install, - ..Default::default() - } - } -} - -impl Package { - fn as_ron(&self) -> Result { - let cfg = PrettyConfig::new().struct_names(true); - to_string_pretty(self, cfg) - } - - pub(crate) fn save_ron_and_create_tar_node( - &self, - outdir: &Path, - ) -> Result> { - if !outdir.exists() { - fs::create_dir_all(outdir)?; - } - let mut outfile = outdir.to_path_buf(); - outfile.push("package.ron"); - let fd = File::create(&outfile)?; - let s = self.as_ron()?; - let mut writer = BufWriter::new(&fd); - writer.write_all(s.as_bytes())?; - writer.flush()?; - let node = Node::read_data_to_tar( - s.as_bytes(), - "package.ron", - &outfile.metadata()?, - Some(Owner::default()), - )?; - Ok(node) - } - - /// Returns the formatted full package name including version and release strings - pub fn fullname(&self) -> String { - format!("{}-{}_{}", self.name, self.version, self.release) - } - - /// Tests whether this package is an update for another - pub fn is_upgrade(&self, other: &Self) -> bool { - self.name == other.name - && (self.version > other.version - || (self.version == other.version && self.release > other.release)) - } -} diff --git a/src/package/specs.rs b/src/package/specs.rs deleted file mode 100644 index 1cfcc83..0000000 --- a/src/package/specs.rs +++ /dev/null @@ -1,31 +0,0 @@ -use { - super::{Group, User}, - crate::{Dependency, Version}, - serde::{Deserialize, Serialize}, -}; - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct Specs { - /// The name of the package minus all version information - pub name: String, - /// The `Version` of the package - pub version: Version, - /// The release number for this package - pub release: u8, - /// a single line description of the package - pub description: String, - /// a more verbose description of the package - pub long_description: String, - /// an optional link to an - /// [AppStream](https://www.freedesktop.org/wiki/Distributions/AppStream/) - /// metadata file - pub appstream_data: Option, - /// all of this package's runtime dependencies - pub dependencies: Vec, - /// an optional list of users to be created upon installation - pub users: Option>, - /// an optional list of groups to be created upon installation - pub groups: Option>, - /// an optional post installation shell script to be run - pub post_install: Option, -} diff --git a/src/plist/mod.rs b/src/plist/mod.rs deleted file mode 100644 index 3551aca..0000000 --- a/src/plist/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -use { - rayon::prelude::*, - serde::{Deserialize, Serialize}, - sha2::{digest::Digest, Sha256}, - std::{ - error::Error, - fmt::Write, - fs, - io::Read, - os::unix::fs::MetadataExt, - path::{Path, PathBuf}, - }, - walkdir::WalkDir, -}; - -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct Plist { - pub entries: Vec, -} - -impl TryFrom<&Path> for Plist { - type Error = Box; - - fn try_from(value: &Path) -> Result { - let entries = WalkDir::new(value) - .into_iter() - .collect::>() - .par_iter() - .filter(|x| x.is_ok()) - .filter_map(|x| { - Entry::try_from(x.as_ref().unwrap().path().to_path_buf().as_path()).ok() - }) - .collect(); - Ok(Self { entries }) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub enum Entry { - File { - path: PathBuf, - sha256sum: String, - mode: u32, - size: usize, - }, - Directory { - path: PathBuf, - mode: u32, - }, - Link { - path: PathBuf, - target: PathBuf, - }, -} - -impl TryFrom<&Path> for Entry { - type Error = Box; - - fn try_from(value: &Path) -> Result { - let mut path = PathBuf::from("/"); - path.push(value); - let meta = fs::metadata(value)?; - if meta.is_file() { - let mut buf = vec![]; - let mut fd = fs::File::open(value)?; - let size = fd.read_to_end(&mut buf)?; - let mut sha256sum = String::new(); - let mut hasher = Sha256::new(); - hasher.update(&buf); - let res = hasher.finalize(); - for c in res { - write!(sha256sum, "{c:02x}")?; - } - let mode = meta.mode(); - Ok(Self::File { - path, - sha256sum, - mode, - size, - }) - } else if meta.is_dir() { - let mode = meta.mode(); - Ok(Self::Directory { path, mode }) - } else if meta.is_symlink() { - let target = fs::read_link(value)?; - Ok(Self::Link { path, target }) - } else { - unreachable!(); - } - } -} diff --git a/src/repository/mod.rs b/src/repository/mod.rs index 2a88db1..4ece0b5 100644 --- a/src/repository/mod.rs +++ b/src/repository/mod.rs @@ -1,10 +1,15 @@ -use std::sync::PoisonError; - use { crate::Package, + hpk_package::ron, rayon::prelude::*, serde::{Deserialize, Serialize}, - std::{collections::HashMap, sync::Mutex}, + std::{ + collections::HashMap, + error::Error, + io::Read, + sync::{mpsc::Sender, Mutex, PoisonError}, + time::Duration, + }, url::Url, }; @@ -15,7 +20,43 @@ pub struct Repository { pub packages: HashMap, } +#[derive(Clone, Debug)] +pub enum Message { + ContentLength(usize), + BytesRead(usize), + Failure(String), + Success(usize), +} + impl Repository { + pub fn build(name: &str, url: &Url, sender: Sender) -> Result> { + let url = url.join("packages.ron.zstd")?; + let resp = ureq::get(&url.to_string()) + .timeout(Duration::from_secs(10)) + .call()?; + if let Some(val) = resp.header("Content-Length") { + if let Ok(num) = val.parse::() { + sender.send(Message::ContentLength(num))?; + } + } + let reader = resp.into_reader(); + let mut reader = zstd::Decoder::new(reader)?; + let mut buf = vec![]; + loop { + let bytes = reader.read(&mut buf)?; + if bytes == 0 { + break; + } + sender.send(Message::BytesRead(bytes))?; + } + let packages = ron::de::from_bytes(&buf)?; + Ok(Self { + name: name.to_string(), + base_url: url.clone(), + packages, + }) + } + pub fn search_names(&self, query: &str) -> Result, PoisonError>> { let mut results = vec![]; if let Some(p) = self.packages.get(query) { diff --git a/src/version/gitrev.rs b/src/version/gitrev.rs deleted file mode 100644 index 4b6ef38..0000000 --- a/src/version/gitrev.rs +++ /dev/null @@ -1,79 +0,0 @@ -use { - crate::Version, - chrono::{offset::Utc, DateTime}, - serde::{Deserialize, Serialize}, - std::{cmp::Ordering, error::Error, fmt, str::FromStr}, -}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct GitRev { - /// the short revision hash - pub hash: String, - /// the time of the revision commit - pub datetime: DateTime, -} - -impl fmt::Display for GitRev { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "git_{}.{}", self.hash, self.datetime.format("%Y%m%d")) - } -} - -impl PartialOrd for GitRev { - fn partial_cmp(&self, other: &Self) -> Option { - self.datetime.partial_cmp(&other.datetime) - } -} - -impl Ord for GitRev { - fn cmp(&self, other: &Self) -> Ordering { - self.datetime.cmp(&other.datetime) - } -} - -#[derive(Debug)] -pub struct ParseGitRevError; - -impl fmt::Display for ParseGitRevError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Error parsing git version") - } -} - -impl Error for ParseGitRevError {} - -impl From for ParseGitRevError { - fn from(_value: chrono::ParseError) -> Self { - Self - } -} - -impl FromStr for GitRev { - type Err = ParseGitRevError; - - fn from_str(s: &str) -> Result { - if let Some(gitrev) = s.strip_prefix("git_") { - if let Some((hash, date)) = gitrev.split_once('_') { - if hash.len() == 7 { - let datetime = DateTime::parse_from_str(date, "%Y%m%d")?; - return Ok(Self { - hash: hash.to_string(), - datetime: datetime.into(), - }); - } - } - } - Err(ParseGitRevError) - } -} - -impl TryFrom for GitRev { - type Error = ParseGitRevError; - - fn try_from(value: Version) -> Result { - match value { - Version::Git(g) => Ok(g), - _ => Err(ParseGitRevError), - } - } -} diff --git a/src/version/mod.rs b/src/version/mod.rs deleted file mode 100644 index 06b8057..0000000 --- a/src/version/mod.rs +++ /dev/null @@ -1,130 +0,0 @@ -use { - serde::{Deserialize, Serialize}, - std::{error::Error, fmt, str::FromStr}, -}; - -mod gitrev; -mod rapid; -mod semver; - -pub use {gitrev::GitRev, rapid::Rapid, semver::SemVer}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub enum Version { - Number(u32), - Rapid(Rapid), - SemVer(SemVer), - Git(GitRev), -} - -impl Default for Version { - fn default() -> Self { - Self::SemVer(SemVer { - major: 0, - minor: 1, - patch: 0, - }) - } -} - -impl fmt::Display for Version { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Number(n) => write!(f, "{n}"), - Self::SemVer(s) => write!(f, "{s}"), - Self::Rapid(r) => write!(f, "{r}"), - Self::Git(g) => write!(f, "{g}"), - } - } -} - -impl From for Version { - fn from(value: SemVer) -> Self { - Self::SemVer(value) - } -} - -impl From for Version { - fn from(value: Rapid) -> Self { - Self::Rapid(value) - } -} - -impl From for Version { - fn from(value: GitRev) -> Self { - Self::Git(value) - } -} - -impl From for Version { - fn from(value: u32) -> Self { - Self::Number(value) - } -} - -impl PartialOrd for Version { - #[allow(clippy::many_single_char_names)] - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (Self::Number(s), Self::Number(o)) => s.partial_cmp(o), - (Self::Number(s), Self::Rapid(o)) => s.partial_cmp(o), - (Self::Number(s), Self::SemVer(o)) => s.partial_cmp(o), - (Self::Rapid(s), Self::Number(o)) => s.partial_cmp(o), - (Self::Rapid(s), Self::Rapid(o)) => s.partial_cmp(o), - (Self::Rapid(s), Self::SemVer(o)) => s.partial_cmp(o), - (Self::SemVer(s), Self::Number(o)) => s.partial_cmp(o), - (Self::SemVer(s), Self::Rapid(o)) => s.partial_cmp(o), - (Self::SemVer(s), Self::SemVer(o)) => s.partial_cmp(o), - (Self::Git(s), Self::Git(o)) => s.partial_cmp(o), - _ => None, - } - } -} - -#[derive(Debug)] -pub struct ParseVersionError; - -impl fmt::Display for ParseVersionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "error parsing version") - } -} - -impl Error for ParseVersionError {} - -impl FromStr for Version { - type Err = ParseVersionError; - - fn from_str(s: &str) -> Result { - if let Ok(v) = s.parse::() { - Ok(v.into()) - } else if let Ok(v) = s.parse::() { - Ok(v.into()) - } else if let Ok(v) = s.parse::() { - Ok(v.into()) - } else if let Ok(v) = s.parse::() { - Ok(v.into()) - } else { - Err(ParseVersionError) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn cmp_semver_rapid_gt() { - let sem = "1.42.1".parse::().unwrap(); - let rpd = "1.42".parse::().unwrap(); - assert!(sem > rpd); - } - - #[test] - fn cmp_semver_rapid_eq() { - let sem = "1.42.0".parse::().unwrap(); - let rpd = "1.42".parse::().unwrap(); - assert!(sem == rpd); - } -} diff --git a/src/version/mod.rs.bak b/src/version/mod.rs.bak deleted file mode 100644 index 9208624..0000000 --- a/src/version/mod.rs.bak +++ /dev/null @@ -1,206 +0,0 @@ -use { - chrono::{offset::Utc, DateTime}, - serde::{Deserialize, Serialize}, - std::{error::Error, fmt, str::FromStr}, -}; - -mod gitrev; -mod rapid; -mod semver; - -pub use {gitrev::GitRev, rapid::Rapid, semver::SemVer}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub enum Version { - Number(u32), - Rapid { - major: u32, - minor: u32, - }, - SemVer { - major: u32, - minor: u32, - patch: u32, - }, - Git { - hash: String, - datetime: DateTime, - }, -} - -impl Default for Version { - fn default() -> Self { - Self::SemVer { - major: 0, - minor: 1, - patch: 0, - } - } -} - -impl fmt::Display for Version { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Number(n) => write!(f, "{n}"), - Self::SemVer { - major, - minor, - patch, - } => { - let v = SemVer { - major: *major, - minor: *minor, - patch: *patch, - }; - write!(f, "{v}") - } - Self::Rapid { major, minor } => { - let v = Rapid { - major: *major, - minor: *minor, - }; - write!(f, "{v}") - } - Self::Git { hash, datetime } => { - let v = GitRev { - hash: hash.clone(), - datetime: *datetime, - }; - write!(f, "{v}") - } - } - } -} - -impl From for Version { - fn from(value: SemVer) -> Self { - Self::SemVer { - major: value.major, - minor: value.minor, - patch: value.patch, - } - } -} - -impl From for Version { - fn from(value: Rapid) -> Self { - Self::Rapid { - major: value.major, - minor: value.minor, - } - } -} - -impl From for Version { - fn from(value: GitRev) -> Self { - Self::Git { - hash: value.hash, - datetime: value.datetime, - } - } -} - -impl From for Version { - fn from(value: u32) -> Self { - Self::Number(value) - } -} - -impl PartialOrd for Version { - #[allow(clippy::many_single_char_names)] - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (Self::Number(s), Self::Number(o)) => s.partial_cmp(o), - ( - Self::SemVer { - major, - minor, - patch, - }, - Self::SemVer { - major: a, - minor: b, - patch: c, - }, - ) => (major, minor, patch).partial_cmp(&(a, b, c)), - (Self::Rapid { major, minor }, Self::Rapid { major: a, minor: b }) => { - (major, minor).partial_cmp(&(a, b)) - } - ( - Self::Git { - hash: _a, - datetime: b, - }, - Self::Git { - hash: _c, - datetime: d, - }, - ) => b.partial_cmp(&d), - ( - Self::SemVer { - major, - minor, - patch, - }, - Self::Rapid { major: a, minor: b }, - ) => SemVer { - major: *major, - minor: *minor, - patch: *patch, - } - .partial_cmp(&Rapid { - major: *a, - minor: *b, - }), - _ => None, - } - } -} - -#[derive(Debug)] -pub struct ParseVersionError; - -impl fmt::Display for ParseVersionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "error parsing version") - } -} - -impl Error for ParseVersionError {} - -impl FromStr for Version { - type Err = ParseVersionError; - - fn from_str(s: &str) -> Result { - if let Ok(v) = s.parse::() { - Ok(v.into()) - } else if let Ok(v) = s.parse::() { - Ok(v.into()) - } else if let Ok(v) = s.parse::() { - Ok(v.into()) - } else if let Ok(v) = s.parse::() { - Ok(v.into()) - } else { - Err(ParseVersionError) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn cmp_semver_rapid_gt() { - let sem = "1.42.1".parse::().unwrap(); - let rpd = "1.42".parse::().unwrap(); - assert!(sem > rpd); - } - - #[test] - fn cmp_semver_rapid_eq() { - let sem = "1.42.0".parse::().unwrap(); - let rpd = "1.42".parse::().unwrap(); - assert!(sem == rpd); - } -} diff --git a/src/version/rapid.rs b/src/version/rapid.rs deleted file mode 100644 index 0be2946..0000000 --- a/src/version/rapid.rs +++ /dev/null @@ -1,168 +0,0 @@ -use crate::SemVer; - -use { - crate::Version, - serde::{Deserialize, Serialize}, - std::{cmp::Ordering, error::Error, fmt, num::ParseIntError, str::FromStr}, -}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct Rapid { - pub major: u32, - pub minor: u32, -} - -impl fmt::Display for Rapid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}.{}", self.major, self.minor) - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &Self) -> Option { - match self.major.partial_cmp(&other.major) { - Some(Ordering::Greater) => Some(Ordering::Greater), - Some(Ordering::Less) => Some(Ordering::Less), - Some(Ordering::Equal) => self.minor.partial_cmp(&other.minor), - None => None, - } - } -} - -impl Ord for Rapid { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match self.major.cmp(&other.major) { - Ordering::Greater => Ordering::Greater, - Ordering::Less => Ordering::Less, - Ordering::Equal => self.minor.cmp(&other.minor), - } - } -} - -impl PartialEq for Rapid { - fn eq(&self, other: &u32) -> bool { - self.major == *other && self.minor == 0 - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &u32) -> Option { - match self.major.partial_cmp(other) { - Some(Ordering::Greater) => Some(Ordering::Greater), - Some(Ordering::Less) => Some(Ordering::Less), - None => None, - Some(Ordering::Equal) => { - if self.minor == 0 { - Some(Ordering::Equal) - } else { - Some(Ordering::Greater) - } - } - } - } -} - -impl PartialEq for Rapid { - fn eq(&self, other: &SemVer) -> bool { - other.eq(self) - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &SemVer) -> Option { - match other.partial_cmp(self) { - Some(Ordering::Less) => Some(Ordering::Greater), - Some(Ordering::Greater) => Some(Ordering::Less), - Some(Ordering::Equal) => Some(Ordering::Equal), - None => None, - } - } -} - -impl PartialEq for u32 { - fn eq(&self, other: &Rapid) -> bool { - other.eq(self) - } -} - -impl PartialOrd for u32 { - fn partial_cmp(&self, other: &Rapid) -> Option { - match other.partial_cmp(self) { - Some(Ordering::Equal) => Some(Ordering::Equal), - Some(Ordering::Less) => Some(Ordering::Greater), - Some(Ordering::Greater) => Some(Ordering::Less), - None => None, - } - } -} - -#[derive(Debug, PartialEq)] -pub struct ParseRapidError; - -impl fmt::Display for ParseRapidError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Error parsing Rapid version") - } -} - -impl Error for ParseRapidError {} - -impl From for ParseRapidError { - fn from(_value: ParseIntError) -> Self { - Self - } -} - -impl FromStr for Rapid { - type Err = ParseRapidError; - - fn from_str(s: &str) -> Result { - let split = s.split('.').collect::>(); - match split.len() { - 2 => { - let major = split.first().unwrap().parse::()?; - let minor = split.get(1).unwrap().parse::()?; - Ok(Self { major, minor }) - } - _ => Err(ParseRapidError), - } - } -} - -impl TryFrom for Rapid { - type Error = ParseRapidError; - - fn try_from(value: Version) -> Result { - match value { - Version::SemVer(s) => { - if s.patch == 0 { - Ok(Self { - major: s.major, - minor: s.minor, - }) - } else { - Err(ParseRapidError) - } - } - Version::Rapid(s) => Ok(s), - Version::Number(major) => Ok(Self { major, minor: 0 }), - Version::Git(_) => Err(ParseRapidError), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parse_semver() { - assert_eq!( - "93.0".parse::(), - Ok(Rapid { - major: 93, - minor: 0, - }) - ); - } -} diff --git a/src/version/semver.rs b/src/version/semver.rs deleted file mode 100644 index cc9d3da..0000000 --- a/src/version/semver.rs +++ /dev/null @@ -1,191 +0,0 @@ -use { - crate::{Rapid, Version}, - serde::{Deserialize, Serialize}, - std::{cmp::Ordering, error::Error, fmt, num::ParseIntError, str::FromStr}, -}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct SemVer { - pub major: u32, - pub minor: u32, - pub patch: u32, -} - -impl fmt::Display for SemVer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}.{}.{}", self.major, self.minor, self.patch) - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, other: &Self) -> Option { - match self.major.partial_cmp(&other.major) { - Some(Ordering::Greater) => Some(Ordering::Greater), - Some(Ordering::Less) => Some(Ordering::Less), - None => None, - Some(Ordering::Equal) => match self.minor.partial_cmp(&other.minor) { - Some(Ordering::Greater) => Some(Ordering::Greater), - Some(Ordering::Less) => Some(Ordering::Less), - None => None, - Some(Ordering::Equal) => self.patch.partial_cmp(&other.patch), - }, - } - } -} - -impl Ord for SemVer { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match self.major.cmp(&other.major) { - Ordering::Greater => Ordering::Greater, - Ordering::Less => Ordering::Less, - Ordering::Equal => match self.minor.cmp(&other.minor) { - Ordering::Greater => Ordering::Greater, - Ordering::Less => Ordering::Less, - Ordering::Equal => self.patch.cmp(&other.patch), - }, - } - } -} - -impl PartialEq for SemVer { - fn eq(&self, other: &Rapid) -> bool { - self.major == other.major && self.minor == other.minor && self.patch == 0 - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, other: &Rapid) -> Option { - match self.major.partial_cmp(&other.major) { - Some(Ordering::Greater) => Some(Ordering::Greater), - Some(Ordering::Less) => Some(Ordering::Less), - None => None, - Some(Ordering::Equal) => match self.minor.partial_cmp(&other.minor) { - Some(Ordering::Greater) => Some(Ordering::Greater), - Some(Ordering::Less) => Some(Ordering::Less), - None => None, - Some(Ordering::Equal) => match self.patch { - 0 => Some(Ordering::Equal), - _ => Some(Ordering::Greater), - }, - }, - } - } -} - -impl PartialEq for SemVer { - fn eq(&self, other: &u32) -> bool { - self.major == *other && self.minor == 0 && self.patch == 0 - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, other: &u32) -> Option { - match self.major.cmp(other) { - Ordering::Greater => Some(Ordering::Greater), - Ordering::Less => Some(Ordering::Less), - Ordering::Equal => { - if self.minor == 0 && self.patch == 0 { - Some(Ordering::Equal) - } else { - Some(Ordering::Greater) - } - } - } - } -} - -impl PartialEq for u32 { - fn eq(&self, other: &SemVer) -> bool { - other.eq(self) - } -} - -impl PartialOrd for u32 { - fn partial_cmp(&self, other: &SemVer) -> Option { - match other.partial_cmp(self) { - Some(Ordering::Less) => Some(Ordering::Greater), - Some(Ordering::Greater) => Some(Ordering::Less), - Some(Ordering::Equal) => Some(Ordering::Equal), - None => None, - } - } -} - -#[derive(Debug, PartialEq)] -pub struct ParseSemVerError; - -impl fmt::Display for ParseSemVerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Error parsing SemVer") - } -} - -impl Error for ParseSemVerError {} - -impl From for ParseSemVerError { - fn from(_value: ParseIntError) -> Self { - Self - } -} - -impl FromStr for SemVer { - type Err = ParseSemVerError; - - fn from_str(s: &str) -> Result { - let split = s.split('.').collect::>(); - match split.len() { - 3 => { - let major = split.first().unwrap().parse::()?; - let minor = split.get(1).unwrap().parse::()?; - let patch = split.get(2).unwrap().parse::()?; - Ok(Self { - major, - minor, - patch, - }) - } - _ => Err(ParseSemVerError), - } - } -} - -impl TryFrom for SemVer { - type Error = ParseSemVerError; - fn try_from(value: Version) -> Result { - match value { - Version::SemVer(s) => Ok(SemVer { - major: s.major, - minor: s.minor, - patch: s.patch, - }), - Version::Rapid(r) => Ok(SemVer { - major: r.major, - minor: r.minor, - patch: 0, - }), - Version::Number(n) => Ok(Self { - major: n, - minor: 0, - patch: 0, - }), - Version::Git(_) => Err(ParseSemVerError), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parse_semver() { - assert_eq!( - "1.0.3".parse::(), - Ok(SemVer { - major: 1, - minor: 0, - patch: 3 - }) - ); - } -} diff --git a/tar/Cargo.toml b/tar/Cargo.toml deleted file mode 100644 index 14443b9..0000000 --- a/tar/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "tar" -version = "0.1.0" -edition = "2021" -license = "GPL-3.0-only" - -[dependencies] -deku = { workspace = true } -libc = "0.2" -thiserror = "1.0.40" diff --git a/tar/README.md b/tar/README.md deleted file mode 100644 index 062958e..0000000 --- a/tar/README.md +++ /dev/null @@ -1,8 +0,0 @@ -## About -Derived originally from [minitar](https://github.com/genonullfree/minitar), this -crate implements basic functionality for creating and extracting tar archives. It -has been adapted to allow creation of a Node (Tar header + 512byte blocks of data) -from the raw data plus file metadata. This allows for better efficiency when it -is embedded into another application (such as a package manager), as the raw data -and metadata about each file can be extracted once and reused for purposes such -as generating checksums, getting file sizes and creating packing lists. diff --git a/tar/src/error.rs b/tar/src/error.rs deleted file mode 100644 index 52f9f3a..0000000 --- a/tar/src/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::{fmt, io, num::ParseIntError, str::Utf8Error}; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("DekuError: {0}")] - Deku(#[from] deku::DekuError), - #[error("IoError: {0}")] - Io(#[from] io::Error), - #[error("Error in conversion of oct_to_dev")] - Utf8Error(#[from] Utf8Error), - #[error("Error in conversion of oct_to_dev")] - ParseIntError(#[from] ParseIntError), - #[error("End of tar")] - EndOfTar, - #[error("Invalid magic")] - InvalidMagic, - #[error("Invalid Checksum")] - InvalidChecksum, - #[error("Parse int failed")] - Parse(#[from] fmt::Error), -} diff --git a/tar/src/header.rs b/tar/src/header.rs deleted file mode 100644 index 337cda4..0000000 --- a/tar/src/header.rs +++ /dev/null @@ -1,441 +0,0 @@ -use crate::Error; -use deku::prelude::*; -use std::{ - env, - ffi::CStr, - fmt::{self, Write}, - fs::{self, Metadata}, - io, - ops::Deref, - os::{linux::fs::MetadataExt, unix::fs::FileTypeExt}, - path::PathBuf, -}; - -#[repr(u8)] -pub enum FileType { - Normal = 0x30, - Hardlink = 0x31, - Symlink = 0x32, - Char = 0x33, - Block = 0x34, - Dir = 0x35, - FIFO = 0x36, - Unknown = 0x00, -} - -impl From for FileType -where - T: Deref, -{ - fn from(meta: T) -> Self { - if meta.is_dir() { - return FileType::Dir; - } - - let file_type = meta.file_type(); - if file_type.is_fifo() { - return FileType::FIFO; - } else if file_type.is_char_device() { - return FileType::Char; - } else if file_type.is_block_device() { - return FileType::Block; - } else if file_type.is_fifo() { - return FileType::FIFO; - } else if file_type.is_symlink() { - return FileType::Symlink; - } else if file_type.is_file() { - return FileType::Normal; - } - - FileType::Unknown - } -} - -#[derive(Clone)] -pub struct Owner { - pub uid: u32, - pub gid: u32, - pub username: String, - pub groupname: String, -} - -impl Default for Owner { - fn default() -> Self { - Self { - uid: 0, - gid: 0, - username: "root".into(), - groupname: "root".into(), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, DekuRead, DekuWrite)] -#[deku(endian = "little")] -pub struct Header { - pub(crate) fname: [u8; 100], - pub(crate) mode: [u8; 8], - pub(crate) uid: [u8; 8], - pub(crate) gid: [u8; 8], - pub(crate) size: [u8; 12], - pub(crate) mtime: [u8; 12], - pub(crate) header_checksum: [u8; 8], - pub(crate) link_indicator: [u8; 1], - pub(crate) link_name: [u8; 100], - pub(crate) ustar_magic: [u8; 6], - pub(crate) ustar_version: [u8; 2], - pub(crate) username: [u8; 32], - pub(crate) groupname: [u8; 32], - pub(crate) device_major: [u8; 8], - pub(crate) device_minor: [u8; 8], - pub(crate) file_prefix: [u8; 155], - pub(crate) reserved: [u8; 12], -} - -impl Default for Header { - fn default() -> Self { - Self { - fname: [0; 100], - mode: [0; 8], - uid: [0; 8], - gid: [0; 8], - size: [0; 12], - mtime: [0; 12], - header_checksum: [0x20; 8], - link_indicator: [0; 1], - link_name: [0; 100], - ustar_magic: [0x75, 0x73, 0x74, 0x61, 0x72, 0x20], - ustar_version: [0x20, 0x00], - username: [0; 32], - groupname: [0; 32], - device_major: [0; 8], - device_minor: [0; 8], - file_prefix: [0; 155], - reserved: [0; 12], - } - } -} - -impl Header { - pub fn filename(&self) -> Result { - let mut s = String::new(); - for c in self.fname { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - Ok(s) - } - - pub fn mode(&self) -> Result { - let mut s = String::new(); - for c in self.mode { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - let mode = u32::from_str_radix(&s, 8)?; - Ok(mode) - } - - fn uid(&self) -> Result { - let mut s = String::new(); - for c in self.mode { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - let uid = u32::from_str_radix(&s, 8)?; - Ok(uid) - } - - fn gid(&self) -> Result { - let mut s = String::new(); - for c in self.mode { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - let gid = u32::from_str_radix(&s, 8)?; - Ok(gid) - } - - fn username(&self) -> Result { - let mut s = String::new(); - for c in self.username { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - Ok(s) - } - - fn groupname(&self) -> Result { - let mut s = String::new(); - for c in self.groupname { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - Ok(s) - } - - pub fn owner(&self) -> Result { - let uid = self.uid()?; - let gid = self.gid()?; - let username = self.username()?; - let groupname = self.groupname()?; - Ok(Owner { - uid, - gid, - username, - groupname, - }) - } - - pub fn prefix(&self) -> Result { - let mut s = String::new(); - for c in self.file_prefix { - if c != b'\0' { - write!(s, "{c}")?; - } else { - break; - } - } - Ok(s) - } - - pub fn new(filename: &str) -> Result { - let mut header = Header::default(); - let meta = fs::symlink_metadata(filename)?; - let (filename, prefix) = if filename.len() < 100 { - (filename.to_string(), None) - } else { - // Deal with file names longer than 100 bytes - let path = PathBuf::from(&filename); - let name = match path.file_name().and_then(|n| n.to_str()) { - Some(n) => n.to_string(), - None => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "Cannot get file name", - ))) - } - }; - let dir = match path.parent() { - Some(d) => d, - None => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "Cannot get path prefix", - ))) - } - }; - (name, Some(format!("{}", dir.display()))) - }; - - /* Fill in metadata */ - header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); - let mode = format!("{:07o}", (meta.st_mode() & 0o777)); - header.mode[..mode.len()].copy_from_slice(mode.as_bytes()); - let user = format!("{:07o}", meta.st_uid()); - header.uid[..user.len()].copy_from_slice(user.as_bytes()); - let group = format!("{:07o}", meta.st_gid()); - header.gid[..group.len()].copy_from_slice(group.as_bytes()); - let size = format!("{:011o}", meta.st_size()); - header.size[..size.len()].copy_from_slice(size.as_bytes()); - let mtime = format!("{:011o}", meta.st_mtime()); - header.mtime[..mtime.len()].copy_from_slice(mtime.as_bytes()); - if let Some(prefix) = prefix { - header.file_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes()); - } - - /* Get the file type and conditional metadata */ - header.link_indicator[0] = FileType::from(&meta) as u8; - if header.link_indicator[0] == FileType::Symlink as u8 { - let link = fs::read_link(filename)?.to_str().unwrap().to_string(); - header.link_name[..link.len()].copy_from_slice(link.as_bytes()); - } else if header.link_indicator[0] == FileType::Block as u8 { - let major = format!("{:07o}", meta.st_dev()); - header.device_major[..major.len()].copy_from_slice(major.as_bytes()); - let minor = format!("{:07o}", meta.st_rdev()); - header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes()); - } - - /* TODO: Find better way to get username */ - let key = "USER"; - if let Ok(val) = env::var(key) { - header.username[..val.len()].copy_from_slice(val.as_bytes()) - } - /* TODO: Find way to get groupname */ - - /* Update the header checksum value */ - header.update_checksum()?; - - Ok(header) - } - - pub fn new_from_meta( - filename: &str, - meta: &Metadata, - owner: Option, - ) -> Result { - let mut header = Header::default(); - let (filename, prefix) = if filename.len() < 100 { - (filename.to_string(), None) - } else { - // Deal with file names longer than 100 bytes - let path = PathBuf::from(&filename); - let name = match path.file_name().and_then(|n| n.to_str()) { - Some(n) => n.to_string(), - None => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "Cannot get file name", - ))) - } - }; - let dir = match path.parent() { - Some(d) => d, - None => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "Cannot get path prefix", - ))) - } - }; - (name, Some(format!("{}", dir.display()))) - }; - header.fname[..filename.len()].copy_from_slice(filename.as_bytes()); - let mode = format!("{:07o}", meta.st_mode()); - header.mode[..mode.len()].copy_from_slice(mode.as_bytes()); - let owner = match owner { - Some(o) => o, - None => Owner { - uid: meta.st_uid(), - 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()); - let gid = format!("{:07o}", owner.gid); - header.gid[..gid.len()].copy_from_slice(gid.as_bytes()); - let size = format!("{:011o}", meta.len()); - header.size[..size.len()].copy_from_slice(size.as_bytes()); - let mtime = format!("{:011o}", meta.st_mtime()); - header.mtime[..mtime.len()].copy_from_slice(mtime.as_bytes()); - if let Some(prefix) = prefix { - header.file_prefix[..prefix.len()].copy_from_slice(prefix.as_bytes()); - } - header.link_indicator[0] = FileType::from(meta) as u8; - if header.link_indicator[0] == FileType::Symlink as u8 { - let link = fs::read_link(filename)?.to_str().unwrap().to_string(); - header.link_name[..link.len()].copy_from_slice(link.as_bytes()); - } else if header.link_indicator[0] == FileType::Block as u8 { - let major = format!("{:07o}", meta.st_dev()); - header.device_major[..major.len()].copy_from_slice(major.as_bytes()); - let minor = format!("{:07o}", meta.st_rdev()); - header.device_minor[..minor.len()].copy_from_slice(minor.as_bytes()); - } - header.username[..owner.username.len()].copy_from_slice(owner.username.as_bytes()); - header.groupname[..owner.groupname.len()].copy_from_slice(owner.groupname.as_bytes()); - header.update_checksum()?; - Ok(header) - } - - /// Validates that the magic value received matches the magic value required in the Tar specification. - /// - /// # Example - /// - /// ``` - /// use tar::tar::Header; - /// let header = Header::default(); - /// if !header.validate_magic() { - /// println!("Magic value is invalid"); - /// } - /// ``` - pub fn validate_magic(self) -> bool { - self.ustar_magic == "ustar ".as_bytes() - } - - /// Validates the header checksum computes to the expected value. - /// - /// # Example - /// - /// ``` - /// use tar::tar::Header; - /// let header = Header::default(); - /// if header.validate_checksum().unwrap() { - /// println!("Checksum is valid"); - /// } - /// ``` - pub fn validate_checksum(self) -> Result { - let mut test = self; - let mut new = [0x20u8; 8]; - test.header_checksum.copy_from_slice(&[0x20; 8]); - - let tmp = format!("{:06o}\x00", test.calc_checksum()?); - new[..tmp.len()].copy_from_slice(tmp.as_bytes()); - - Ok(self.header_checksum == new) - } - - /// Updates the header checksum value. - /// - /// # Example - /// - /// ``` - /// use tar::tar::Header; - /// let mut header = Header::default(); - /// - /// /* Fill in header information */ - /// - /// header.update_checksum(); - /// ``` - pub fn update_checksum(&mut self) -> Result<(), Error> { - let checksum = format!("{:06o}\x00", self.calc_checksum()?); - self.header_checksum[..checksum.len()].copy_from_slice(checksum.as_bytes()); - Ok(()) - } - - fn calc_checksum(self) -> Result { - let out = self.to_bytes()?; - let mut checksum = 0; - for i in out { - checksum += i as usize; - } - Ok(checksum) - } -} - -fn get_username_for_uid<'a>(uid: u32) -> Result<&'a str, std::str::Utf8Error> { - let user = unsafe { - let pw = libc::getpwuid(uid); - let name = (*pw).pw_name; - CStr::from_ptr(name) - }; - user.to_str() -} - -pub fn get_groupname_for_gid<'a>(gid: u32) -> Result<&'a str, std::str::Utf8Error> { - let group = unsafe { - let gr = libc::getgrgid(gid); - let name = (*gr).gr_name; - CStr::from_ptr(name) - }; - group.to_str() -} diff --git a/tar/src/lib.rs b/tar/src/lib.rs deleted file mode 100644 index a5d6b95..0000000 --- a/tar/src/lib.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::{ - fs::File, - io::{self, BufReader, Write}, -}; - -mod error; -mod header; -mod node; -pub use { - error::Error, - header::{FileType, Header, Owner}, - node::Node, -}; - -#[derive(Default)] -pub struct Archive { - pub nodes: Vec, -} - -impl Archive { - pub fn to_vec(self) -> Result, Error> { - let mut buf = vec![]; - for node in self.nodes { - buf.extend(node.to_vec()?); - } - buf.write_all(&[0; 9216])?; - Ok(buf) - } - - /// Write out a vector of `TarNodes` to a file or something that implements ``std::io::Write`` and ``std::io::Copy``. - /// - /// # Example - /// - /// ``` - /// use std::fs::File; - /// use tar::tar::TarFile; - /// - /// let data = Archive::new("test/1.txt".to_string()).unwrap(); - /// - /// let out = File::create("test/2.tar".to_string()).unwrap(); - /// data.write(&out).unwrap(); - /// ``` - pub fn write(self, mut input: T) -> Result { - let mut written = 0; - for f in self.nodes.clone() { - written += f.write(input)?; - } - - /* Complete the write with 18 blocks of 512 ``0x00`` bytes per the specification */ - if !self.nodes.is_empty() { - input.write_all(&[0; 9216])?; - written += 9216; - } - - Ok(written) - } - - /// Create a new `TarFile` struct and initialize it with a `filename` file. This will read in the file to - /// the `TarFile` struct as a `TarNode`. - /// - /// # Example - /// - /// ``` - /// use tar::Archive; - /// - /// let data = Archive::new("test/1.txt".to_string()).unwrap(); - /// ``` - pub fn new(filename: &str) -> Result { - Ok(Self { - nodes: vec![Node::read_file_to_tar(filename)?], - }) - } - - /// Append another file to the `TarFile.file` vector. This adds a file to the internal representation of the tar file. - /// - /// # Example - /// - /// ``` - /// use tar::Archive; - /// - /// let mut data = Archive::new("test/1.txt".to_string()).unwrap(); - /// data.append("test/1.txt".to_string()).unwrap(); - /// ``` - pub fn append(&mut self, filename: &str) -> Result<(), Error> { - self.nodes.push(Node::read_file_to_tar(filename)?); - - Ok(()) - } - - /// Open and load an external tar file into the internal `TarFile` struct. This parses and loads up all the files - /// contained within the external tar file. - /// - /// # Example - /// - /// ``` - /// use tar::Archive; - /// - /// Archive::open("test/1.tar".to_string()).unwrap(); - /// ``` - pub fn open(filename: String) -> Result { - let file = File::open(&filename)?; - let mut reader = BufReader::new(file); - let mut out = Self { - nodes: Vec::::new(), - }; - - while let Ok(t) = Node::read(&mut reader) { - out.nodes.push(t); - } - - Ok(out) - } - - /// Remove the first file from the Tar that matches the filename and path. - /// - /// # Example - /// - /// ``` - /// use minitar::tar::TarFile; - /// - /// let mut data = TarFile::new("test/1.tar".to_string()).unwrap(); - /// data.remove("test/1.tar".to_string()).unwrap(); - /// ``` - pub fn remove(&mut self, filename: String) -> Result { - let mut name = [0u8; 100]; - name[..filename.len()].copy_from_slice(filename.as_bytes()); - if let Some(i) = &self.nodes.iter().position(|x| x.header.fname == name) { - self.nodes.remove(*i); - return Ok(true); - } - - Ok(false) - } -} diff --git a/tar/src/node.rs b/tar/src/node.rs deleted file mode 100644 index 81356be..0000000 --- a/tar/src/node.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::{header::Owner, Error, FileType, Header}; -use deku::prelude::*; -use std::{ - fs::{File, Metadata}, - io::{self, BufReader}, - str, -}; - -#[derive(Clone, Debug, Default)] -pub struct Node { - pub header: Header, - pub data: Vec<[u8; 512]>, -} - -impl Node { - pub fn to_vec(self) -> Result, DekuError> { - let mut buf = self.header.to_bytes()?; - for block in self.data { - buf.extend(block.to_vec()); - } - Ok(buf) - } - - /// Write out a single file within the tar to a file or something with a ``std::io::Write`` trait. - pub fn write(self, mut input: T) -> Result { - input.write_all(&self.header.to_bytes()?)?; - let mut written = 512; - for d in self.data { - input.write_all(&d)?; - written += d.len(); - } - - Ok(written) - } - - /// Read a TarNode in from a file or something with a ``std::io::Read`` trait. - pub fn read(mut input: T) -> Result { - let mut h = vec![0u8; 512]; - input.read_exact(&mut h)?; - - let (_, header) = Header::from_bytes((&h, 0))?; - if header == Header::default() { - return Err(Error::EndOfTar); - } - if !header.validate_magic() { - return Err(Error::InvalidMagic); - } - if !header.validate_checksum()? { - return Err(Error::InvalidChecksum); - } - - let chunks = (oct_to_dec(&header.size)? / 512) + 1; - Ok(Node { - header, - data: Node::chunk_file(&mut input, Some(chunks))?, - }) - } - - /// Open and read a file from the ``filename`` argument to a TarNode. - pub fn read_file_to_tar(filename: &str) -> Result { - let header = Header::new(filename)?; - if header.link_indicator[0] != FileType::Normal as u8 { - return Ok(Node { - header, - data: Vec::<[u8; 512]>::new(), - }); - } - - let file = File::open(filename)?; - let mut reader = BufReader::new(file); - Ok(Node { - header, - data: Node::chunk_file(&mut reader, None)?, - }) - } - - /// 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 { - let header = Header::new_from_meta(filename, meta, owner)?; - if header.link_indicator[0] != FileType::Normal as u8 { - return Ok(Node { - header, - data: Vec::<[u8; 512]>::new(), - }); - } - let mut reader = BufReader::new(data); - Ok(Node { - header, - data: Node::chunk_file(&mut reader, None)?, - }) - } - - /// Read in and split a file into ``512`` byte chunks. - fn chunk_file( - file: &mut T, - max_chunks: Option, - ) -> Result, Error> { - /* Extract the file data from the tar file */ - let mut out = Vec::<[u8; 512]>::new(); - let mut n = if let Some(max) = max_chunks { - max - } else { - usize::MAX - }; - - /* Carve out 512 bytes at a time */ - let mut buf: [u8; 512] = [0; 512]; - loop { - let len = file.read(&mut buf)?; - - n -= 1; - - /* If read len == 0, we've hit the EOF */ - if len == 0 || n == 0 { - break; - } - - /* Save this chunk */ - out.push(buf); - } - Ok(out) - } -} - -fn oct_to_dec(input: &[u8]) -> Result { - /* Convert the &[u8] to string and remove the null byte */ - let mut s = str::from_utf8(input)?.to_string(); - s.pop(); - - /* Convert to usize from octal */ - Ok(usize::from_str_radix(&s, 8)?) -}