Implement ability to specify uid:gid when extracting files; Misc code cleanup
This commit is contained in:
parent
bfe9d25c86
commit
eae8abffc7
@ -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
|
||||
|
58
src/node.rs
58
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<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();
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user