diff --git a/src/nix/mod.rs b/src/nix/mod.rs index d18627f..af1afe9 100644 --- a/src/nix/mod.rs +++ b/src/nix/mod.rs @@ -17,6 +17,37 @@ pub fn chown(path: &str, uid: u32, gid: u32) -> Result<(), Error> { } } +#[cfg(target_os = "linux")] +pub fn chmod(path: &str, mode: u32) -> Result<(), Error> { + let ret = unsafe { libc::chmod(CString::new(path)?.as_ptr(), mode) }; + if ret == 0 { + Ok(()) + } else { + Err(Error::Io(io::Error::last_os_error())) + } +} + +#[cfg(target_os = "freebsd")] +pub fn chmod(path: &str, mode: u32) -> Result<(), Error> { + let ret = unsafe { libc::chmod(CString::new(path)?.as_ptr(), mode.try_into()?) }; + if ret == 0 { + Ok(()) + } else { + Err(Error::Io(io::Error::last_os_error())) + } +} + +#[cfg(target_os = "linux")] +pub fn mkfifo(path: &str, mode: u32) -> Result<(), Error> { + let ret = unsafe { libc::mkfifo(CString::new(path)?.as_ptr(), mode) }; + if ret == 0 { + Ok(()) + } else { + Err(Error::Io(io::Error::last_os_error())) + } +} + +#[cfg(target_os = "freebsd")] 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 { @@ -26,6 +57,18 @@ pub fn mkfifo(path: &str, mode: u32) -> Result<(), Error> { } } +#[cfg(target_os = "linux")] +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, dev) }; + if res == 0 { + Ok(()) + } else { + Err(Error::Io(io::Error::last_os_error())) + } +} + +#[cfg(target_os = "freebsd")] 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) }; diff --git a/src/node.rs b/src/node.rs index 8fc2374..cfa6a3d 100644 --- a/src/node.rs +++ b/src/node.rs @@ -6,7 +6,7 @@ use { std::{ collections::HashMap, fs, - io::{BufReader, Read, Write}, + io::{BufReader, BufWriter, Read, Write}, os::unix::fs::{symlink, DirBuilderExt, MetadataExt}, path::{Path, PathBuf}, sync::Mutex, @@ -156,10 +156,10 @@ impl Node { } let kind = Kind::from(mode); if kind == Kind::Char { - let device = Special::from_rdev(meta.rdev().try_into()?); + let device = Special::from_rdev(meta.rdev()); break 'blk FileType::Character(device); } else if kind == Kind::Block { - let device = Special::from_rdev(meta.rdev().try_into()?); + let device = Special::from_rdev(meta.rdev()); break 'blk FileType::Block(device); } else if kind == Kind::Pipe { break 'blk FileType::Fifo; @@ -225,10 +225,38 @@ impl Node { 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::Block(ref b) => { + if euid == 0 { + nix::mknod(&path, self.mode, b.major, b.minor)?; + } + } + FileType::Character(ref c) => { + if euid == 0 { + nix::mknod(&path, self.mode, c.major, c.minor)?; + } + } + FileType::Normal(ref n) => { + let n_path = PathBuf::from(&path); + if let Some(p) = n_path.parent() { + if !p.exists() { + self.mkdir(&p)?; + } + } + { + let fd = fs::OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(&n_path)?; + let mut writer = BufWriter::new(fd); + writer.write_all(&n.data)?; + } + if euid == 0 { + nix::chown(&path, self.uid, self.gid)?; + } + nix::chmod(&path, self.mode)?; + } FileType::HardLink(ref t) => { let target = if let Some(prefix) = prefix { format!("{prefix}/{t}") @@ -236,7 +264,7 @@ impl Node { t.to_string() }; fs::hard_link(&target, &path)?; - }, + } FileType::SoftLink(ref t) => { symlink(&self.name, t)?; } @@ -253,7 +281,7 @@ impl Node { self.mkdir(&p)?; } } - if !dir.exists () { + if !dir.exists() { fs::DirBuilder::new().mode(self.mode).create(dir)?; } if nix::geteuid() == 0 { diff --git a/src/special.rs b/src/special.rs index 108ff63..56368f9 100644 --- a/src/special.rs +++ b/src/special.rs @@ -27,10 +27,25 @@ impl Special { Ok(()) } - pub(super) fn from_rdev(rdev: u32) -> Self { + #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] + pub(super) fn from_rdev(rdev: u64) -> Self { Self { - major: rdev >> 8, - minor: rdev & 0xff, + major: ((rdev >> 8) & 0xff) as u32, + minor: (rdev & 0xffff00ff) as u32, + } + } + + #[cfg(target_os = "linux")] + pub(super) fn from_rdev(rdev: u64) -> Self { + let mut major = 0; + major |= (rdev & 0x00000000000fff00) >> 8; + major |= (rdev & 0xfffff00000000000) >> 32; + let mut minor = 0; + minor |= (rdev & 0x00000000000000ff) >> 0; + minor |= (rdev & 0x00000ffffff00000) >> 12; + Self { + major: major as u32, + minor: minor as u32, } } }