various low level system utils moved into system module
This commit is contained in:
parent
4bd8c3626f
commit
4b4e5f31e7
@ -1,64 +0,0 @@
|
|||||||
|
|
||||||
use std::path::{PathBuf,Path};
|
|
||||||
use std::fs;
|
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
pub struct Mount {
|
|
||||||
source: String,
|
|
||||||
target: PathBuf,
|
|
||||||
fstype: String,
|
|
||||||
options: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mount {
|
|
||||||
///
|
|
||||||
/// Returns `true` if `path` matches the source field (first field)
|
|
||||||
/// of any of the mount lines listed in /proc/mounts
|
|
||||||
///
|
|
||||||
pub fn is_source_mounted<P: AsRef<Path>>(path: P) -> Result<bool> {
|
|
||||||
let path_str = path.as_ref().to_string_lossy();
|
|
||||||
let mounts = Mount::all_mounts()?;
|
|
||||||
Ok(mounts.into_iter().any(|m| m.source == path_str))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_target_mounted<P: AsRef<Path>>(path: P) -> Result<bool> {
|
|
||||||
let mounts = Mount::all_mounts()?;
|
|
||||||
Ok(mounts.into_iter().any(|m| m.target == path.as_ref()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all_mounts() -> Result<Vec<Mount>> {
|
|
||||||
let s = fs::read_to_string("/proc/mounts")?;
|
|
||||||
Ok(s.lines().flat_map(Mount::parse_mount_line).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_mount_line(line: &str) -> Option<Mount> {
|
|
||||||
let parts = line.split_whitespace().collect::<Vec<_>>();
|
|
||||||
if parts.len() < 4 {
|
|
||||||
warn!("Failed to parse mount line: {}", line);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Mount{
|
|
||||||
source: parts[0].to_string(),
|
|
||||||
target: PathBuf::from(parts[1]),
|
|
||||||
fstype: parts[2].to_string(),
|
|
||||||
options: parts[3].to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn source(&self) -> &str {
|
|
||||||
&self.source
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn target(&self) -> &Path {
|
|
||||||
&self.target
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fstype(&self) -> &str {
|
|
||||||
&self.fstype
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn options(&self) -> &str {
|
|
||||||
&self.options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
78
libcitadel/src/system/lock.rs
Normal file
78
libcitadel/src/system/lock.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use std::fs::{self,File,OpenOptions};
|
||||||
|
use std::io::{Error,ErrorKind};
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::{Path,PathBuf};
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
pub struct FileLock {
|
||||||
|
file: File,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileLock {
|
||||||
|
|
||||||
|
pub fn acquire<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
|
let path = path.as_ref().to_path_buf();
|
||||||
|
let file = Self::open_lockfile(&path)?;
|
||||||
|
let flock = FileLock { file, path };
|
||||||
|
flock.lock()?;
|
||||||
|
Ok(flock)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_lockfile(path: &Path) -> Result<File> {
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
if !parent.exists() {
|
||||||
|
fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..3 {
|
||||||
|
if let Some(file) = Self::try_create_lockfile(path)? {
|
||||||
|
return Ok(file);
|
||||||
|
}
|
||||||
|
if let Some(file) = Self::try_open_lockfile(path)? {
|
||||||
|
return Ok(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(format_err!("unable to acquire lockfile {}", path.display() ))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_create_lockfile(path: &Path) -> Result<Option<File>> {
|
||||||
|
match OpenOptions::new().write(true).create_new(true).open(path) {
|
||||||
|
Ok(file) => Ok(Some(file)),
|
||||||
|
Err(ref e) if e.kind() == ErrorKind::AlreadyExists => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_open_lockfile(path: &Path) -> Result<Option<File>> {
|
||||||
|
match File::open(path) {
|
||||||
|
Ok(file) => Ok(Some(file)),
|
||||||
|
Err(ref e) if e.kind() == ErrorKind::NotFound => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock(&self) -> Result<()> {
|
||||||
|
self.flock(libc::LOCK_UN)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lock(&self) -> Result<()> {
|
||||||
|
self.flock(libc::LOCK_EX)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flock(&self, flag: libc::c_int) -> Result<()> {
|
||||||
|
if unsafe { libc::flock(self.file.as_raw_fd(), flag) } < 0 {
|
||||||
|
return Err(Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for FileLock {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = fs::remove_file(&self.path);
|
||||||
|
let _ = self.unlock();
|
||||||
|
}
|
||||||
|
}
|
120
libcitadel/src/system/loopdev.rs
Normal file
120
libcitadel/src/system/loopdev.rs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::path::{Path,PathBuf};
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
use super::mounts::Mounts;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoopDevice(PathBuf);
|
||||||
|
|
||||||
|
impl LoopDevice {
|
||||||
|
const LOSETUP: &'static str = "/usr/sbin/losetup";
|
||||||
|
const MOUNT: &'static str = "/usr/bin/mount";
|
||||||
|
|
||||||
|
fn new<P: AsRef<Path>>(device: P) -> LoopDevice {
|
||||||
|
let device = device.as_ref().to_path_buf();
|
||||||
|
LoopDevice(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create<P: AsRef<Path>>(image: P, offset: Option<usize>, read_only: bool) -> Result<LoopDevice> {
|
||||||
|
let image = image.as_ref();
|
||||||
|
let mut args = String::new();
|
||||||
|
if let Some(offset) = offset {
|
||||||
|
args += &format!("--offset {} ", offset);
|
||||||
|
}
|
||||||
|
if read_only {
|
||||||
|
args += &format!("--read-only ");
|
||||||
|
}
|
||||||
|
args += &format!("-f --show {}", image.display());
|
||||||
|
let output = cmd_with_output!(Self::LOSETUP, args)?;
|
||||||
|
Ok(LoopDevice::new(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_loop<P,F,R>(image: P, offset: Option<usize>, read_only: bool, f: F) -> Result<R>
|
||||||
|
where P: AsRef<Path>,
|
||||||
|
F: FnOnce(&LoopDevice) -> Result<R>,
|
||||||
|
{
|
||||||
|
let loopdev = Self::create(image, offset, read_only)?;
|
||||||
|
let result = f(&loopdev);
|
||||||
|
let detach_result = loopdev.detach();
|
||||||
|
let r = result?;
|
||||||
|
detach_result.map_err(|e| format_err!("error detaching loop device: {}", e))?;
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search for an entry in /proc/mounts for a loop device which is mounted on the
|
||||||
|
/// specified mountpoint.
|
||||||
|
/// The relevant lines look like this:
|
||||||
|
///
|
||||||
|
/// /dev/loop3 /run/citadel/realmfs/realmfs-name-rw.mountpoint ext4 rw,noatime,data=ordered 0 0
|
||||||
|
///
|
||||||
|
pub fn find_mounted_loop<P: AsRef<Path>>(mount_target: P) -> Option<LoopDevice> {
|
||||||
|
let mount_target = mount_target.as_ref();
|
||||||
|
Mounts::load().ok()
|
||||||
|
.and_then(|mounts| mounts.mounts()
|
||||||
|
.find(|m| m.target_path() == mount_target &&
|
||||||
|
m.source().starts_with("/dev/loop"))
|
||||||
|
.map(|m| LoopDevice::new(m.source_path())) )
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_devices_for<P: AsRef<Path>>(image: P) -> Result<Vec<LoopDevice>> {
|
||||||
|
let image = image.as_ref();
|
||||||
|
// Output from losetup -j looks like this:
|
||||||
|
// /dev/loop1: [0036]:64845938 (/storage/resources/dev/citadel-extra-dev-001.img), offset 4096
|
||||||
|
let output:String = cmd_with_output!(Self::LOSETUP, "-j {}", image.display())?;
|
||||||
|
Ok(output.lines()
|
||||||
|
.flat_map(|line| line.splitn(2, ":").next())
|
||||||
|
.map(|s| LoopDevice::new(s))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detach(&self) -> Result<()> {
|
||||||
|
cmd!(Self::LOSETUP, format!("-d {}", self.0.display()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&self) -> Result<()> {
|
||||||
|
cmd!(Self::LOSETUP, format!("-c {}", self.0.display()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device(&self) -> &Path {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_str(&self) -> &str {
|
||||||
|
self.device().to_str().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_ro<P: AsRef<Path>>(&self, target: P) -> Result<()> {
|
||||||
|
let target = target.as_ref();
|
||||||
|
cmd!(Self::MOUNT, "-oro,noatime {} {}", self, target.display())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount<P: AsRef<Path>>(&self, target: P) -> Result<()> {
|
||||||
|
let target = target.as_ref();
|
||||||
|
cmd!(Self::MOUNT, "-orw,noatime {} {}", self, target.display())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_pair<P,Q>(&self, rw_target: P, ro_target: Q) -> Result<()>
|
||||||
|
where P: AsRef<Path>,
|
||||||
|
Q: AsRef<Path>
|
||||||
|
{
|
||||||
|
let rw = rw_target.as_ref();
|
||||||
|
let ro = ro_target.as_ref();
|
||||||
|
|
||||||
|
self.mount(rw)?;
|
||||||
|
// From mount(8):
|
||||||
|
//
|
||||||
|
// mount --bind olddir newdir
|
||||||
|
// mount -o remount,bind,ro olddir newdir
|
||||||
|
cmd!(Self::MOUNT, "--bind {} {}", rw.display(), ro.display())?;
|
||||||
|
cmd!(Self::MOUNT, "-o remount,bind,ro {} {}", rw.display(), ro.display())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LoopDevice {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.device().display())
|
||||||
|
}
|
||||||
|
}
|
9
libcitadel/src/system/mod.rs
Normal file
9
libcitadel/src/system/mod.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
mod lock;
|
||||||
|
mod loopdev;
|
||||||
|
mod mounts;
|
||||||
|
mod uname;
|
||||||
|
|
||||||
|
pub use self::uname::UtsName;
|
||||||
|
pub use self::loopdev::LoopDevice;
|
||||||
|
pub use self::mounts::{Mounts,MountLine};
|
||||||
|
pub use self::lock::FileLock;
|
96
libcitadel/src/system/mounts.rs
Normal file
96
libcitadel/src/system/mounts.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
pub struct Mounts {
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mounts {
|
||||||
|
///
|
||||||
|
/// Returns `true` if `path` matches the source field (first field)
|
||||||
|
/// of any of the mount lines listed in /proc/mounts
|
||||||
|
///
|
||||||
|
pub fn is_source_mounted<P: AsRef<Path>>(path: P) -> Result<bool> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let mounted = Self::load()?
|
||||||
|
.mounts()
|
||||||
|
.any(|m| m.source_path() == path);
|
||||||
|
|
||||||
|
Ok(mounted)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_target_mounted<P: AsRef<Path>>(path: P) -> Result<bool> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let mounted = Self::load()?
|
||||||
|
.mounts()
|
||||||
|
.any(|m| m.target_path() == path);
|
||||||
|
|
||||||
|
Ok(mounted)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Result<Mounts> {
|
||||||
|
let content = fs::read_to_string("/proc/mounts")?;
|
||||||
|
Ok(Mounts { content })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mounts(&self) -> impl Iterator<Item=MountLine> {
|
||||||
|
self.content.lines().flat_map(MountLine::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MountLine<'a> {
|
||||||
|
line: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'a> MountLine<'a> {
|
||||||
|
|
||||||
|
fn new(line: &str) -> Option<MountLine> {
|
||||||
|
if line.split_whitespace().count() >= 4 {
|
||||||
|
Some(MountLine { line })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field(&self, n: usize) -> &str {
|
||||||
|
self.line.split_whitespace().nth(n).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> &str {
|
||||||
|
self.field(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_path(&self) -> &Path {
|
||||||
|
Path::new(self.source())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target(&self) -> &str {
|
||||||
|
self.field(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_path(&self) -> &Path {
|
||||||
|
Path::new(self.target())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fstype(&self) -> &str {
|
||||||
|
self.field(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(&self) -> HashMap<&str,&str> {
|
||||||
|
self.field(3).split(',').map(Self::parse_key_val).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_key_val(option: &str) -> (&str,&str) {
|
||||||
|
let kv: Vec<&str> = option.splitn(2, '=').collect();
|
||||||
|
if kv.len() == 2 {
|
||||||
|
(kv[0], kv[1])
|
||||||
|
} else {
|
||||||
|
(kv[0], "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
libcitadel/src/system/uname.rs
Normal file
48
libcitadel/src/system/uname.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::ffi::CStr;
|
||||||
|
use std::mem;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use libc::c_char;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct UtsName(libc::utsname);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl UtsName {
|
||||||
|
pub fn uname() -> UtsName {
|
||||||
|
unsafe {
|
||||||
|
let mut ret: UtsName = mem::uninitialized();
|
||||||
|
libc::uname(&mut ret.0);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sysname(&self) -> &str {
|
||||||
|
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodename(&self) -> &str {
|
||||||
|
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(&self) -> &str {
|
||||||
|
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(&self) -> &str {
|
||||||
|
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn machine(&self) -> &str {
|
||||||
|
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_str<'a>(s: *const *const c_char) -> &'a str {
|
||||||
|
unsafe {
|
||||||
|
let res = CStr::from_ptr(*s).to_bytes();
|
||||||
|
str::from_utf8_unchecked(res)
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
use std::path::{Path,PathBuf};
|
use std::path::{Path,PathBuf};
|
||||||
use std::process::{Command,ExitStatus,Stdio};
|
use std::process::{Command,Stdio};
|
||||||
use std::mem;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use libc::{self, c_char};
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::str::from_utf8_unchecked;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::{self,File};
|
||||||
use std::io::{self,Seek,Read,BufReader,SeekFrom};
|
use std::ffi::CString;
|
||||||
|
use std::io::{self, Seek, Read, BufReader, SeekFrom};
|
||||||
|
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
use libc;
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
@ -56,55 +57,11 @@ pub fn ensure_command_exists(cmd: &str) -> Result<()> {
|
|||||||
Err(format_err!("Cannot execute '{}': command does not exist", cmd))
|
Err(format_err!("Cannot execute '{}': command does not exist", cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_cmdline<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<()> {
|
|
||||||
ensure_command_exists(cmd_path)?;
|
|
||||||
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
|
||||||
let status = Command::new(cmd_path)
|
|
||||||
.args(args)
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
check_cmd_status(cmd_path, &status)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exec_cmdline_quiet<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<()> {
|
|
||||||
ensure_command_exists(cmd_path)?;
|
|
||||||
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
|
||||||
let status = Command::new(cmd_path)
|
|
||||||
.args(args)
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
check_cmd_status(cmd_path, &status)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exec_cmdline_with_output<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<String> {
|
|
||||||
ensure_command_exists(cmd_path)?;
|
|
||||||
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
|
|
||||||
let res = Command::new(cmd_path)
|
|
||||||
.args(args)
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.output()
|
|
||||||
.context(format!("unable to execute {}", cmd_path))?;
|
|
||||||
|
|
||||||
check_cmd_status(cmd_path, &res.status)?;
|
|
||||||
Ok(String::from_utf8(res.stdout).unwrap().trim().to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_cmd_status(cmd_path: &str, status: &ExitStatus) -> Result<()> {
|
|
||||||
if !status.success() {
|
|
||||||
match status.code() {
|
|
||||||
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
|
|
||||||
None => bail!("command {} failed with no exit code", cmd_path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sha256<P: AsRef<Path>>(path: P) -> Result<String> {
|
pub fn sha256<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||||
let output = exec_cmdline_with_output("/usr/bin/sha256sum", format!("{}", path.as_ref().display()))
|
let path = path.as_ref();
|
||||||
.context(format!("failed to calculate sha256 on {}", path.as_ref().display()))?;
|
let output = cmd_with_output!("/usr/bin/256sum", "{}", path.display())
|
||||||
|
.context(format!("failed to calculate sha256 on {}", path.display()))?;
|
||||||
|
|
||||||
let v: Vec<&str> = output.split_whitespace().collect();
|
let v: Vec<&str> = output.split_whitespace().collect();
|
||||||
Ok(v[0].trim().to_owned())
|
Ok(v[0].trim().to_owned())
|
||||||
@ -158,71 +115,96 @@ pub fn exec_cmdline_pipe_input<S,P>(cmd_path: &str, args: S, input: P, range: Fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn xz_compress<P: AsRef<Path>>(path: P) -> Result<()> {
|
pub fn xz_compress<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||||
exec_cmdline("/usr/bin/xz", format!("-T0 {}", path.as_ref().display()))
|
let path = path.as_ref();
|
||||||
.context(format!("failed to compress {}", path.as_ref().display()))?;
|
cmd!("/usr/bin/xz", "-T0 {}", path.display())
|
||||||
|
.context(format!("failed to compress {}", path.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xz_decompress<P: AsRef<Path>>(path: P) -> Result<()> {
|
pub fn xz_decompress<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||||
exec_cmdline("/usr/bin/xz", format!("-d {}", path.as_ref().display()))
|
let path = path.as_ref();
|
||||||
.context(format!("failed to decompress {}", path.as_ref().display()))?;
|
cmd!("/usr/bin/xz", "-d {}", path.display())
|
||||||
|
.context(format!("failed to decompress {}", path.display()))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount<P: AsRef<Path>>(source: &str, target: P, options: Option<&str>) -> Result<()> {
|
pub fn mount<P: AsRef<Path>>(source: impl AsRef<str>, target: P, options: Option<&str>) -> Result<()> {
|
||||||
let paths = format!("{} {}", source, target.as_ref().display());
|
let source = source.as_ref();
|
||||||
let args = match options {
|
let target = target.as_ref();
|
||||||
Some(s) => format!("{} {}", s, paths),
|
if let Some(options) = options {
|
||||||
None => paths,
|
cmd!("/usr/bin/mount", "{} {} {}", options, source, target.display())
|
||||||
};
|
} else {
|
||||||
exec_cmdline("/usr/bin/mount", args)
|
cmd!("/usr/bin/mount", "{} {}", source, target.display())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn umount<P: AsRef<Path>>(path: P) -> Result<()> {
|
pub fn umount<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||||
let args = format!("{}", path.as_ref().display());
|
let path = path.as_ref();
|
||||||
exec_cmdline("/usr/bin/umount", args)
|
cmd!("/usr/bin/umount", "{}", path.display())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chown_user<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||||
#[repr(C)]
|
chown(path.as_ref(), 1000, 1000)
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct UtsName(libc::utsname);
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl UtsName {
|
|
||||||
pub fn sysname(&self) -> &str {
|
|
||||||
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nodename(&self) -> &str {
|
|
||||||
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn release(&self) -> &str {
|
|
||||||
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version(&self) -> &str {
|
|
||||||
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn machine(&self) -> &str {
|
|
||||||
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uname() -> UtsName {
|
pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
|
||||||
|
let cstr = CString::new(path.as_os_str().as_bytes())?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut ret: UtsName = mem::uninitialized();
|
if libc::chown(cstr.as_ptr(), uid, gid) == -1 {
|
||||||
libc::uname(&mut ret.0);
|
return Err(io::Error::last_os_error());
|
||||||
ret
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn copy_path(from: &Path, to: &Path, chown_to: Option<(u32,u32)>) -> Result<()> {
|
||||||
fn to_str<'a>(s: *const *const c_char) -> &'a str {
|
if to.exists() {
|
||||||
unsafe {
|
bail!("destination path {} already exists which is not expected", to.display());
|
||||||
let res = CStr::from_ptr(*s).to_bytes();
|
|
||||||
from_utf8_unchecked(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let meta = from.metadata()?;
|
||||||
|
|
||||||
|
if from.is_dir() {
|
||||||
|
fs::create_dir(to)?;
|
||||||
|
} else {
|
||||||
|
fs::copy(&from, &to)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((uid,gid)) = chown_to {
|
||||||
|
chown(to, uid, gid)?;
|
||||||
|
} else {
|
||||||
|
chown(to, meta.uid(), meta.gid())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_tree(from_base: &Path, to_base: &Path) -> Result<()> {
|
||||||
|
_copy_tree(from_base, to_base, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_tree_with_chown(from_base: &Path, to_base: &Path, chown_to: (u32,u32)) -> Result<()> {
|
||||||
|
_copy_tree(from_base, to_base, Some(chown_to))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _copy_tree(from_base: &Path, to_base: &Path, chown_to: Option<(u32,u32)>) -> Result<()> {
|
||||||
|
for entry in WalkDir::new(from_base) {
|
||||||
|
let path = entry?.path().to_owned();
|
||||||
|
let to = to_base.join(path.strip_prefix(from_base)?);
|
||||||
|
if &to != to_base {
|
||||||
|
copy_path(&path, &to, chown_to)
|
||||||
|
.map_err(|e| format_err!("failed to copy {} to {}: {}", path.display(), to.display(), e))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chown_tree(base: &Path, chown_to: (u32,u32), include_base: bool) -> Result<()> {
|
||||||
|
for entry in WalkDir::new(base) {
|
||||||
|
let entry = entry?;
|
||||||
|
if entry.path() != base || include_base {
|
||||||
|
chown(entry.path(), chown_to.0, chown_to.1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user