use std::{ fs::{self, File}, io, os::unix::prelude::*, path::{Path, PathBuf}, }; use nix::{ fcntl::OFlag, mount::{self, MsFlags}, unistd, }; use common::error::*; pub fn open>(path: P) -> Result { fs::File::open(path.as_ref()) .with_context(|| format!("Failed to open file {:?} for reading", path.as_ref())) } pub fn create>(path: P) -> Result { fs::File::create(path.as_ref()) .with_context(|| format!("Failed to open file {:?} for writing", path.as_ref())) } pub fn rename, P2: AsRef>(from: P1, to: P2) -> Result<()> { fs::rename(from.as_ref(), to.as_ref()) .with_context(|| format!("Failed to rename {:?} to {:?}", from.as_ref(), to.as_ref())) } // Unlike fs::copy, this doesn't preserve file flags pub fn copy, P2: AsRef>(from: P1, to: P2) -> Result<()> { (|| -> Result<()> { let mut src = open(from.as_ref())?; let mut dest = create(to.as_ref())?; io::copy(&mut src, &mut dest)?; dest.sync_all()?; Ok(()) })() .with_context(|| format!("Failed to copy {:?} to {:?}", from.as_ref(), to.as_ref())) } pub fn mkdir>(path: P) -> Result<()> { let mut builder = fs::DirBuilder::new(); builder.recursive(true); builder .create(path.as_ref()) .with_context(|| format!("Failed to create directory {:?}", path.as_ref())) } pub fn ensure_removed>(path: P) -> Result<()> { fs::remove_dir_all(path.as_ref()) .or_else(|err| match err.kind() { io::ErrorKind::NotFound => Ok(()), _ => Err(err), }) .with_context(|| format!("Failed to delete directory {:?}", path.as_ref())) } pub fn is_dir_empty>(path: P) -> Result { Ok(fs::read_dir(path)?.next().is_none()) } /// Fixes up weirdness of set-group-ID or bsdgroups pub fn fixup_permissions>(path: P) -> Result<()> { let path = path.as_ref(); let gid = unistd::getegid(); let metadata = path .metadata() .with_context(|| format!("Failed to get metadata of {:?}", path))?; if metadata.gid() != gid.as_raw() { unistd::chown(path, None, Some(gid)) .with_context(|| format!("Failed to set group of {:?}", path))?; } let mut perms = metadata.permissions(); let mode = perms.mode(); if (mode & 0o777) != mode { perms.set_mode(mode & 0o777); std::fs::set_permissions(path, perms) .with_context(|| format!("Failed to set mode of {:?}", path))?; } Ok(()) } #[must_use] pub struct Mount(PathBuf); impl Drop for Mount { fn drop(&mut self) { mount::umount(&self.0) .with_context(|| format!("Failed to unmount {:?}", self.0)) .unwrap(); } } pub fn mount, P2: AsRef>( source: P1, target: P2, fstype: Option<&str>, flags: MsFlags, data: Option<&str>, ) -> Result { mkdir(target.as_ref()).with_context(|| format!("Failed to create {:?}", target.as_ref()))?; let canon_target = target .as_ref() .canonicalize() .with_context(|| format!("Failed to get absolute path for {:?}", target.as_ref()))?; mount::mount(Some(source.as_ref()), &canon_target, fstype, flags, data) .with_context(|| format!("Failed to mount {:?}", canon_target))?; Ok(Mount(canon_target)) } pub fn pipe() -> Result<(File, File)> { unistd::pipe2(OFlag::O_CLOEXEC) .context("pipe2()") .map(|(piper, pipew)| unsafe { (File::from_raw_fd(piper), File::from_raw_fd(pipew)) }) }