summaryrefslogtreecommitdiffstats
path: root/crates/rebel-runner/src/util/fs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/rebel-runner/src/util/fs.rs')
-rw-r--r--crates/rebel-runner/src/util/fs.rs127
1 files changed, 127 insertions, 0 deletions
diff --git a/crates/rebel-runner/src/util/fs.rs b/crates/rebel-runner/src/util/fs.rs
new file mode 100644
index 0000000..9e33eb7
--- /dev/null
+++ b/crates/rebel-runner/src/util/fs.rs
@@ -0,0 +1,127 @@
+use std::{
+ fs::{self, File},
+ io,
+ os::unix::prelude::*,
+ path::{Path, PathBuf},
+};
+
+use nix::{
+ fcntl::OFlag,
+ mount::{self, MsFlags},
+ unistd,
+};
+
+use rebel_common::error::*;
+
+pub fn open<P: AsRef<Path>>(path: P) -> Result<fs::File> {
+ fs::File::open(path.as_ref())
+ .with_context(|| format!("Failed to open file {:?} for reading", path.as_ref()))
+}
+
+pub fn create<P: AsRef<Path>>(path: P) -> Result<fs::File> {
+ fs::File::create(path.as_ref())
+ .with_context(|| format!("Failed to open file {:?} for writing", path.as_ref()))
+}
+
+pub fn rename<P1: AsRef<Path>, P2: AsRef<Path>>(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<P1: AsRef<Path>, P2: AsRef<Path>>(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<P: AsRef<Path>>(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<P: AsRef<Path>>(path: P) -> Result<()> {
+ let result = if path.as_ref().is_dir() {
+ fs::remove_dir_all(path.as_ref())
+ } else {
+ fs::remove_file(path.as_ref())
+ };
+ result
+ .or_else(|err| match err.kind() {
+ io::ErrorKind::NotFound => Ok(()),
+ _ => Err(err),
+ })
+ .with_context(|| format!("Failed to delete {:?}", path.as_ref()))
+}
+
+pub fn is_dir_empty<P: AsRef<Path>>(path: P) -> Result<bool> {
+ Ok(fs::read_dir(path)?.next().is_none())
+}
+
+/// Fixes up weirdness of set-group-ID or bsdgroups
+pub fn fixup_permissions<P: AsRef<Path>>(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<P1: AsRef<Path>, P2: AsRef<Path>>(
+ source: P1,
+ target: P2,
+ fstype: Option<&str>,
+ flags: MsFlags,
+ data: Option<&str>,
+) -> Result<Mount> {
+ 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)| (File::from(piper), File::from(pipew)))
+}