From eae8abffc7b1de949503667ea143aebe8febc4e8 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Mon, 15 Jan 2024 23:58:34 -0500 Subject: [PATCH] Implement ability to specify uid:gid when extracting files; Misc code cleanup --- README.md | 2 +- src/node.rs | 58 ++++++++++++++++++++++++++------------------------- src/stream.rs | 14 ++++++++++--- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index f0d7c8e..10373d0 100644 --- a/README.md +++ b/README.md @@ -36,5 +36,5 @@ to see Haggis implemented in other languages. ## Roadmap - [x] Create and extract archives - [x] List archive nodes -- [ ] Specify user/group permissions to extract as +- [x] Specify user/group permissions to extract as - [ ] Automatically detect zstd compressed archives diff --git a/src/node.rs b/src/node.rs index 6749e87..044a734 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,3 +1,4 @@ +#![allow(clippy::similar_names)] use { crate::{filetype::Flag, nix, Algorithm, Checksum, Error, File, FileType, Special}, md5::{Digest, Md5}, @@ -220,7 +221,12 @@ impl Node { /// Recreates the original file on disk which this node represents /// # Errors /// Returns `crate::Error` if io fails or certain other circumstances - pub fn extract(&self, prefix: Option<&str>) -> Result<(), Error> { + pub fn extract( + &self, + prefix: Option<&str>, + uid: Option, + gid: Option, + ) -> Result<(), Error> { let euid = nix::geteuid(); let path = 'blk: { if let Some(prefix) = prefix { @@ -239,9 +245,17 @@ impl Node { } else { PathBuf::from(&format!("./{path}")) }; + let uid = match uid { + Some(u) => u, + None => self.uid, + }; + let gid = match gid { + Some(g) => g, + None => self.gid, + }; if let Some(p) = n_path.parent() { if !p.exists() { - self.mkdir(p)?; + self.mkdir(p, uid, gid)?; } } match self.filetype { @@ -252,10 +266,10 @@ impl Node { } nix::mkfifo(n_path.to_str().ok_or(Error::BadPath)?, self.mode.into())?; if euid == 0 { - nix::chown(n_path.to_str().ok_or(Error::BadPath)?, self.uid, self.gid)?; + nix::chown(n_path.to_str().ok_or(Error::BadPath)?, uid, gid)?; } } - FileType::Block(ref b) => { + FileType::Block(ref sp) | FileType::Character(ref sp) => { if euid == 0 { if n_path.exists() { fs::remove_file(&n_path)?; @@ -263,22 +277,10 @@ impl Node { nix::mknod( n_path.to_str().ok_or(Error::BadPath)?, self.mode.into(), - b.major, - b.minor, - )?; - } - } - FileType::Character(ref c) => { - if euid == 0 { - if n_path.exists() { - fs::remove_file(&n_path)?; - } - nix::mknod( - n_path.to_str().ok_or(Error::BadPath)?, - self.mode.into(), - c.major, - c.minor, + sp.major, + sp.minor, )?; + nix::chown(n_path.to_str().ok_or(Error::BadPath)?, uid, gid)?; } } FileType::Normal(ref n) => { @@ -292,7 +294,7 @@ impl Node { writer.write_all(&n.data)?; } if euid == 0 { - nix::chown(n_path.to_str().ok_or(Error::BadPath)?, self.uid, self.gid)?; + nix::chown(n_path.to_str().ok_or(Error::BadPath)?, uid, gid)?; } nix::chmod(n_path.to_str().ok_or(Error::BadPath)?, self.mode.into())?; } @@ -317,18 +319,18 @@ impl Node { symlink(t, &n_path)?; } FileType::Directory => { - self.mkdir(&n_path)?; + self.mkdir(&n_path, uid, gid)?; } } Ok(()) } - fn mkdir(&self, dir: &Path) -> Result<(), Error> { + fn mkdir(&self, dir: &Path, uid: u32, gid: u32) -> Result<(), Error> { if !dir.exists() { fs::create_dir_all(dir)?; } if nix::geteuid() == 0 { - nix::chown(dir.to_str().ok_or(Error::NulError)?, self.uid, self.gid)?; + nix::chown(dir.to_str().ok_or(Error::NulError)?, uid, gid)?; } nix::chmod( dir.to_str().ok_or(Error::BadPath)?, @@ -412,7 +414,7 @@ mod tests { "5f1b6e6e31682fb6683db2e78db11e624527c897618f1a5b0a0b5256f557c22d" ); assert_eq!(LI, f.data); - node.extract(Some("test/output")).unwrap(); + node.extract(Some("test/output"), None, None).unwrap(); let f = fs::read_to_string("test/output/test/li.txt").unwrap(); assert_eq!(f.as_bytes(), LI); } @@ -444,7 +446,7 @@ mod tests { panic!(); }; assert_eq!(tgt, "li.txt"); - node.extract(Some("test/output")).unwrap(); + node.extract(Some("test/output"), None, None).unwrap(); let tgt = fs::read_link("test/output/test/lilnk.txt").unwrap(); assert_eq!(tgt, PathBuf::from("li.txt")); } @@ -488,8 +490,8 @@ mod tests { let fd = fs::File::open("test/li.node").unwrap(); let mut reader = BufReader::new(fd); let node0 = Node::read(&mut reader).unwrap(); - node1.extract(Some("test/output")).unwrap(); - node0.extract(Some("test/output")).unwrap(); + node1.extract(Some("test/output"), None, None).unwrap(); + node0.extract(Some("test/output"), None, None).unwrap(); } #[test] @@ -519,6 +521,6 @@ mod tests { eprintln!("Read incorrect filetype: {:?}", node.filetype); panic!(); }; - node.extract(Some("test/output")).unwrap(); + node.extract(Some("test/output"), None, None).unwrap(); } } diff --git a/src/stream.rs b/src/stream.rs index 80da367..7d13ada 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,3 +1,4 @@ +#![allow(clippy::similar_names)] #[cfg(feature = "parallel")] use { crate::FileType, @@ -64,9 +65,14 @@ impl Stream { /// Extracts an archive /// # Errors /// Returns `crate::Error` if io fails or several other error conditions - pub fn extract(&mut self, prefix: Option<&str>) -> Result<(), Error> { + pub fn extract( + &mut self, + prefix: Option<&str>, + uid: Option, + gid: Option, + ) -> Result<(), Error> { for node in self { - node?.extract(prefix)?; + node?.extract(prefix, uid, gid)?; } Ok(()) } @@ -78,12 +84,14 @@ impl Stream { pub fn par_extract( &mut self, prefix: Option<&str>, + uid: Option, + gid: Option, sender: &Sender, ) -> Result<(), Error> { let s = sender.clone(); self.into_iter().par_bridge().try_for_each_with(s, |s, n| { let n = n?; - n.extract(prefix)?; + n.extract(prefix, uid, gid)?; match n.filetype { FileType::Normal(f) => { s.send(Message::FileExtracted {