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
- [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

View File

@ -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<u32>,
gid: Option<u32>,
) -> 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();
}
}

View File

@ -1,3 +1,4 @@
#![allow(clippy::similar_names)]
#[cfg(feature = "parallel")]
use {
crate::FileType,
@ -64,9 +65,14 @@ impl<R: Read + Send> Stream<R> {
/// 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<u32>,
gid: Option<u32>,
) -> Result<(), Error> {
for node in self {
node?.extract(prefix)?;
node?.extract(prefix, uid, gid)?;
}
Ok(())
}
@ -78,12 +84,14 @@ impl<R: Read + Send> Stream<R> {
pub fn par_extract(
&mut self,
prefix: Option<&str>,
uid: Option<u32>,
gid: Option<u32>,
sender: &Sender<Message>,
) -> 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 {