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
|
## 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
|
||||||
|
58
src/node.rs
58
src/node.rs
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user