summaryrefslogtreecommitdiffstats
path: root/crates/runner/src/util/fs.rs
blob: 099a339a40b91fe69520ebc1bacd197e5391363f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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<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<()> {
	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<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)| unsafe { (File::from_raw_fd(piper), File::from_raw_fd(pipew)) })
}