Implement ability to specify uid:gid when extracting files; Misc code cleanup

This commit is contained in:
Nathan Fisher 2024-01-15 23:58:34 -05:00
parent bfe9d25c86
commit eae8abffc7
3 changed files with 42 additions and 32 deletions

View File

@ -36,5 +36,5 @@ to see Haggis implemented in other languages.
## Roadmap ## Roadmap
- [x] Create and extract archives - [x] Create and extract archives
- [x] List archive nodes - [x] List archive nodes
- [ ] Specify user/group permissions to extract as - [x] Specify user/group permissions to extract as
- [ ] Automatically detect zstd compressed archives - [ ] Automatically detect zstd compressed archives

View File

@ -1,3 +1,4 @@
#![allow(clippy::similar_names)]
use { use {
crate::{filetype::Flag, nix, Algorithm, Checksum, Error, File, FileType, Special}, crate::{filetype::Flag, nix, Algorithm, Checksum, Error, File, FileType, Special},
md5::{Digest, Md5}, md5::{Digest, Md5},
@ -220,7 +221,12 @@ impl Node {
/// Recreates the original file on disk which this node represents /// Recreates the original file on disk which this node represents
/// # Errors /// # Errors
/// Returns `crate::Error` if io fails or certain other circumstances /// 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<u32>,
gid: Option<u32>,
) -> Result<(), Error> {
let euid = nix::geteuid(); let euid = nix::geteuid();
let path = 'blk: { let path = 'blk: {
if let Some(prefix) = prefix { if let Some(prefix) = prefix {
@ -239,9 +245,17 @@ impl Node {
} else { } else {
PathBuf::from(&format!("./{path}")) 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 let Some(p) = n_path.parent() {
if !p.exists() { if !p.exists() {
self.mkdir(p)?; self.mkdir(p, uid, gid)?;
} }
} }
match self.filetype { match self.filetype {
@ -252,10 +266,10 @@ impl Node {
} }
nix::mkfifo(n_path.to_str().ok_or(Error::BadPath)?, self.mode.into())?; nix::mkfifo(n_path.to_str().ok_or(Error::BadPath)?, self.mode.into())?;
if euid == 0 { 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 euid == 0 {
if n_path.exists() { if n_path.exists() {
fs::remove_file(&n_path)?; fs::remove_file(&n_path)?;
@ -263,22 +277,10 @@ impl Node {
nix::mknod( nix::mknod(
n_path.to_str().ok_or(Error::BadPath)?, n_path.to_str().ok_or(Error::BadPath)?,
self.mode.into(), self.mode.into(),
b.major, sp.major,
b.minor, sp.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,
)?; )?;
nix::chown(n_path.to_str().ok_or(Error::BadPath)?, uid, gid)?;
} }
} }
FileType::Normal(ref n) => { FileType::Normal(ref n) => {
@ -292,7 +294,7 @@ impl Node {
writer.write_all(&n.data)?; writer.write_all(&n.data)?;
} }
if euid == 0 { 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())?; nix::chmod(n_path.to_str().ok_or(Error::BadPath)?, self.mode.into())?;
} }
@ -317,18 +319,18 @@ impl Node {
symlink(t, &n_path)?; symlink(t, &n_path)?;
} }
FileType::Directory => { FileType::Directory => {
self.mkdir(&n_path)?; self.mkdir(&n_path, uid, gid)?;
} }
} }
Ok(()) Ok(())
} }
fn mkdir(&self, dir: &Path) -> Result<(), Error> { fn mkdir(&self, dir: &Path, uid: u32, gid: u32) -> Result<(), Error> {
if !dir.exists() { if !dir.exists() {
fs::create_dir_all(dir)?; fs::create_dir_all(dir)?;
} }
if nix::geteuid() == 0 { 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( nix::chmod(
dir.to_str().ok_or(Error::BadPath)?, dir.to_str().ok_or(Error::BadPath)?,
@ -412,7 +414,7 @@ mod tests {
"5f1b6e6e31682fb6683db2e78db11e624527c897618f1a5b0a0b5256f557c22d" "5f1b6e6e31682fb6683db2e78db11e624527c897618f1a5b0a0b5256f557c22d"
); );
assert_eq!(LI, f.data); 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(); let f = fs::read_to_string("test/output/test/li.txt").unwrap();
assert_eq!(f.as_bytes(), LI); assert_eq!(f.as_bytes(), LI);
} }
@ -444,7 +446,7 @@ mod tests {
panic!(); panic!();
}; };
assert_eq!(tgt, "li.txt"); 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(); let tgt = fs::read_link("test/output/test/lilnk.txt").unwrap();
assert_eq!(tgt, PathBuf::from("li.txt")); assert_eq!(tgt, PathBuf::from("li.txt"));
} }
@ -488,8 +490,8 @@ mod tests {
let fd = fs::File::open("test/li.node").unwrap(); let fd = fs::File::open("test/li.node").unwrap();
let mut reader = BufReader::new(fd); let mut reader = BufReader::new(fd);
let node0 = Node::read(&mut reader).unwrap(); let node0 = Node::read(&mut reader).unwrap();
node1.extract(Some("test/output")).unwrap(); node1.extract(Some("test/output"), None, None).unwrap();
node0.extract(Some("test/output")).unwrap(); node0.extract(Some("test/output"), None, None).unwrap();
} }
#[test] #[test]
@ -519,6 +521,6 @@ mod tests {
eprintln!("Read incorrect filetype: {:?}", node.filetype); eprintln!("Read incorrect filetype: {:?}", node.filetype);
panic!(); panic!();
}; };
node.extract(Some("test/output")).unwrap(); node.extract(Some("test/output"), None, None).unwrap();
} }
} }

View File

@ -1,3 +1,4 @@
#![allow(clippy::similar_names)]
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
use { use {
crate::FileType, crate::FileType,
@ -64,9 +65,14 @@ impl<R: Read + Send> Stream<R> {
/// Extracts an archive /// Extracts an archive
/// # Errors /// # Errors
/// Returns `crate::Error` if io fails or several other error conditions /// 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<u32>,
gid: Option<u32>,
) -> Result<(), Error> {
for node in self { for node in self {
node?.extract(prefix)?; node?.extract(prefix, uid, gid)?;
} }
Ok(()) Ok(())
} }
@ -78,12 +84,14 @@ impl<R: Read + Send> Stream<R> {
pub fn par_extract( pub fn par_extract(
&mut self, &mut self,
prefix: Option<&str>, prefix: Option<&str>,
uid: Option<u32>,
gid: Option<u32>,
sender: &Sender<Message>, sender: &Sender<Message>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let s = sender.clone(); let s = sender.clone();
self.into_iter().par_bridge().try_for_each_with(s, |s, n| { self.into_iter().par_bridge().try_for_each_with(s, |s, n| {
let n = n?; let n = n?;
n.extract(prefix)?; n.extract(prefix, uid, gid)?;
match n.filetype { match n.filetype {
FileType::Normal(f) => { FileType::Normal(f) => {
s.send(Message::FileExtracted { s.send(Message::FileExtracted {