diff --git a/Cargo.lock b/Cargo.lock index 6f93062..d848996 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,6 +60,7 @@ dependencies = [ name = "haggis" version = "0.1.0" dependencies = [ + "libc", "md-5", "sha1", "sha2", diff --git a/Cargo.toml b/Cargo.toml index 033bce0..bab079e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/error.rs b/src/error.rs index bd5d703..bffaefd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 for Error { Self::Slice(value) } } + +impl From for Error { + fn from(_value: NulError) -> Self { + Self::NulError + } +} diff --git a/src/lib.rs b/src/lib.rs index 2b48b0e..a82b7fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ mod checksum; mod error; mod file; mod filetype; +pub mod nix; mod node; mod special; mod stream; diff --git a/src/nix/mod.rs b/src/nix/mod.rs new file mode 100644 index 0000000..d18627f --- /dev/null +++ b/src/nix/mod.rs @@ -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())) + } +} diff --git a/src/node.rs b/src/node.rs index edb99ca..8fc2374 100644 --- a/src/node.rs +++ b/src/node.rs @@ -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(()) + } }