blob: f69b046b43ed6ef10ffa472a31f84a2f12e95316 [file] [log] [blame]
use std::fmt;
use std::ffi::{CStr, CString, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use nix::mount::{MsFlags, mount};
use {OSError, Error};
use util::{path_to_cstring, as_path};
use explain::{Explainable, exists, user};
use remount::Remount;
/// A mount bind definition
///
/// By default bind mount is recursive (it's what you want most of the time).
///
/// Also recursive mounts can be used in user namespaces.
#[derive(Debug, Clone)]
pub struct BindMount {
source: CString,
target: CString,
recursive: bool,
readonly: bool,
}
impl BindMount {
/// Create a new, recursive bind mount
///
/// You can disable recursion with a `non_recursive()` method
pub fn new<A: AsRef<Path>, B: AsRef<Path>>(source: A, target: B)
-> BindMount
{
BindMount {
source: path_to_cstring(source.as_ref()),
target: path_to_cstring(target.as_ref()),
recursive: true,
readonly: false,
}
}
/// Toggle recursion
pub fn recursive(mut self, flag: bool) -> BindMount {
self.recursive = flag;
self
}
/// If set to `true` makes bind-mount readonly
///
/// Few notes:
///
/// 1. This makes additional `mount` call (`Remount().readonly()`)
/// 2. If remount fails mount bind is left on the filesystem, no cleanup
/// is done
/// 3. If set to `false` is option is no-op (does **not** remount `rw`)
pub fn readonly(mut self, flag: bool) -> BindMount {
self.readonly = flag;
self
}
/// Execute a bind mount
pub fn bare_mount(self) -> Result<(), OSError> {
let mut flags = MsFlags::MS_BIND;
if self.recursive {
flags = flags | MsFlags::MS_REC;
}
if let Err(err) = mount(
Some(&*self.source),
&*self.target,
None::<&CStr>,
flags,
None::<&CStr>,
) {
return Err(OSError::from_nix(err, Box::new(self)));
}
if self.readonly {
try!(Remount::new(OsStr::from_bytes(self.target.as_bytes()))
.bind(true)
.readonly(true)
.bare_remount());
}
Ok(())
}
/// Execute a bind mount and explain the error immediately
pub fn mount(self) -> Result<(), Error> {
self.bare_mount().map_err(OSError::explain)
}
}
impl fmt::Display for BindMount {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if self.recursive {
try!(write!(fmt, "recursive "));
}
write!(fmt, "bind mount {:?} -> {:?}",
as_path(&self.source), as_path(&self.target))
}
}
impl Explainable for BindMount {
fn explain(&self) -> String {
[
format!("source: {}", exists(as_path(&self.source))),
format!("target: {}", exists(as_path(&self.target))),
format!("{}", user()),
].join(", ")
}
}