Some work on extract for Node, requiring some plumbing which is in

`nix` module
This commit is contained in:
Nathan Fisher 2023-07-05 10:01:40 -04:00
parent 283228ff46
commit 5fd1d09e77
6 changed files with 105 additions and 3 deletions

1
Cargo.lock generated
View File

@ -60,6 +60,7 @@ dependencies = [
name = "haggis"
version = "0.1.0"
dependencies = [
"libc",
"md-5",
"sha1",
"sha2",

View File

@ -4,8 +4,10 @@ version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
[dependencies]
libc = "0.2"
md-5 = "0.10"
sha1 = "0.10"
sha2 = "0.10"

View File

@ -1,4 +1,6 @@
use std::{array::TryFromSliceError, fmt, io, num::TryFromIntError, string::FromUtf8Error};
use std::{
array::TryFromSliceError, ffi::NulError, fmt, io, num::TryFromIntError, string::FromUtf8Error,
};
#[derive(Debug)]
pub enum Error {
@ -8,6 +10,7 @@ pub enum Error {
Slice(TryFromSliceError),
InvalidChecksum,
MissingData,
NulError,
UnexpectedData,
UnknownFileType,
Other(String),
@ -22,6 +25,7 @@ impl fmt::Display for Error {
Self::Io(e) => write!(f, "{e}"),
Self::InvalidChecksum => write!(f, "invalid checksum"),
Self::MissingData => write!(f, "missing data"),
Self::NulError => write!(f, "nul error"),
Self::UnexpectedData => write!(f, "unexpected data"),
Self::UnknownFileType => write!(f, "unknown file type"),
Self::Other(s) => write!(f, "{s}"),
@ -64,3 +68,9 @@ impl From<TryFromSliceError> for Error {
Self::Slice(value)
}
}
impl From<NulError> for Error {
fn from(_value: NulError) -> Self {
Self::NulError
}
}

View File

@ -2,6 +2,7 @@ mod checksum;
mod error;
mod file;
mod filetype;
pub mod nix;
mod node;
mod special;
mod stream;

37
src/nix/mod.rs Normal file
View File

@ -0,0 +1,37 @@
#![allow(clippy::must_use_candidate)]
use {
crate::Error,
std::{ffi::CString, io},
};
pub fn geteuid() -> u32 {
unsafe { libc::geteuid() }
}
pub fn chown(path: &str, uid: u32, gid: u32) -> Result<(), Error> {
let ret = unsafe { libc::chown(CString::new(path)?.as_ptr(), uid, gid) };
if ret == 0 {
Ok(())
} else {
Err(Error::Io(io::Error::last_os_error()))
}
}
pub fn mkfifo(path: &str, mode: u32) -> Result<(), Error> {
let ret = unsafe { libc::mkfifo(CString::new(path)?.as_ptr(), mode.try_into()?) };
if ret == 0 {
Ok(())
} else {
Err(Error::Io(io::Error::last_os_error()))
}
}
pub fn mknod(path: &str, mode: u32, major: u32, minor: u32) -> Result<(), Error> {
let dev = libc::makedev(major, minor);
let res = unsafe { libc::mknod(CString::new(path)?.as_ptr(), mode.try_into()?, dev) };
if res == 0 {
Ok(())
} else {
Err(Error::Io(io::Error::last_os_error()))
}
}

View File

@ -1,5 +1,5 @@
use {
crate::{Checksum, Error, File, FileType, Special},
crate::{nix, Checksum, Error, File, FileType, Special},
md5::{Digest, Md5},
sha1::Sha1,
sha2::Sha256,
@ -7,7 +7,8 @@ use {
collections::HashMap,
fs,
io::{BufReader, Read, Write},
os::unix::fs::MetadataExt,
os::unix::fs::{symlink, DirBuilderExt, MetadataExt},
path::{Path, PathBuf},
sync::Mutex,
},
};
@ -208,6 +209,56 @@ impl Node {
}
pub fn extract(&self, prefix: Option<&str>) -> Result<(), Error> {
let euid = nix::geteuid();
let path = 'blk: {
if let Some(prefix) = prefix {
if self.name.starts_with('/') {
break 'blk format!("{prefix}{}", self.name);
}
}
self.name.clone()
};
match self.filetype {
FileType::Eof => {}
FileType::Fifo => {
nix::mkfifo(&path, self.mode)?;
if euid == 0 {
nix::chown(&path, self.uid, self.gid)?;
}
},
FileType::Block(ref b) => todo!(),
FileType::Character(ref c) => todo!(),
FileType::Normal(ref n) => todo!(),
FileType::HardLink(ref t) => {
let target = if let Some(prefix) = prefix {
format!("{prefix}/{t}")
} else {
t.to_string()
};
fs::hard_link(&target, &path)?;
},
FileType::SoftLink(ref t) => {
symlink(&self.name, t)?;
}
FileType::Directory => {
self.mkdir(&PathBuf::from(&path))?;
}
}
todo!()
}
fn mkdir(&self, dir: &Path) -> Result<(), Error> {
if let Some(p) = dir.parent() {
if !p.exists() {
self.mkdir(&p)?;
}
}
if !dir.exists () {
fs::DirBuilder::new().mode(self.mode).create(dir)?;
}
if nix::geteuid() == 0 {
nix::chown(dir.to_str().ok_or(Error::NulError)?, self.uid, self.gid)?;
}
Ok(())
}
}