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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
// Copyright 2018-2022 System76 <info@system76.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::umount::{unmount_, Unmount, UnmountDrop};
use crate::{MountBuilder, UnmountFlags};
use std::{
ffi::{CString, OsStr},
io,
os::unix::ffi::OsStrExt,
path::Path,
};
/// Handle for managing a mounted file system.
#[derive(Debug)]
pub struct Mount {
pub(crate) target: CString,
pub(crate) fstype: String,
#[cfg(feature = "loop")]
pub(crate) loopback: Option<loopdev::LoopDevice>,
pub(crate) loop_path: Option<std::path::PathBuf>,
}
impl Unmount for Mount {
fn unmount(&self, flags: UnmountFlags) -> io::Result<()> {
unsafe {
unmount_(self.target.as_ptr(), flags)?;
}
#[cfg(feature = "loop")]
if let Some(ref loopback) = self.loopback {
loopback.detach()?;
}
Ok(())
}
}
impl Mount {
/// Creates a [`MountBuilder`] for configuring a new mount.
///
/// ```no_run
/// use sys_mount::*;
///
/// fn main() -> std::io::Result<()> {
/// let _mount = Mount::builder()
/// .fstype("btrfs")
/// .data("subvol=@home")
/// .mount("/dev/sda1", "/home")?;
/// Ok(())
/// }
/// ```
#[inline]
#[must_use]
pub fn builder<'a>() -> MountBuilder<'a> {
MountBuilder::default()
}
/// Mounts the source device to the target path.
///
/// Attempts to automatically detect the filesystem of the source device.
///
/// For more flexibility, use [`Mount::builder`] instead.
///
/// # Errors
///
/// Errors if supported filesystems cannot be detected, or the mount fails.
#[inline]
pub fn new(source: impl AsRef<Path>, target: impl AsRef<Path>) -> io::Result<Mount> {
let supported = crate::SupportedFilesystems::new()?;
MountBuilder::default()
.fstype(&supported)
.mount(source, target)
}
/// If the device was associated with a loopback device, that device's path
/// can be retrieved here.
#[inline]
#[must_use]
pub fn backing_loop_device(&self) -> Option<&Path> {
self.loop_path.as_deref()
}
/// Describes the file system which this mount was mounted with.
///
/// This is useful in the event that the mounted device was mounted automatically.
#[inline]
#[must_use]
pub fn get_fstype(&self) -> &str {
&self.fstype
}
/// Return the path this mount was mounted on.
#[inline]
#[must_use]
pub fn target_path(&self) -> &Path {
Path::new(OsStr::from_bytes(self.target.as_bytes()))
}
#[inline]
pub(crate) fn from_target_and_fstype(target: CString, fstype: String) -> Self {
Mount {
target,
fstype,
#[cfg(feature = "loop")]
loopback: None,
loop_path: None,
}
}
}
/// An abstraction that will ensure that temporary mounts are dropped in reverse.
pub struct Mounts(pub Vec<UnmountDrop<Mount>>);
impl Mounts {
/// Unmounts all mounts, with the option to do so lazily.
///
/// # Errors
///
/// Returns on the first error when unmounting.
pub fn unmount(&mut self, lazy: bool) -> io::Result<()> {
let flags = if lazy {
UnmountFlags::DETACH
} else {
UnmountFlags::empty()
};
self.0
.iter_mut()
.rev()
.try_for_each(|mount| mount.unmount(flags))
}
}
impl Drop for Mounts {
fn drop(&mut self) {
for mount in self.0.drain(..).rev() {
drop(mount);
}
}
}