sys_mount/
mount.rs

1// Copyright 2018-2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::umount::{unmount_, Unmount, UnmountDrop};
5use crate::{MountBuilder, UnmountFlags};
6use std::{
7    ffi::{CString, OsStr},
8    io,
9    os::unix::ffi::OsStrExt,
10    path::Path,
11};
12
13/// Handle for managing a mounted file system.
14#[derive(Debug)]
15pub struct Mount {
16    pub(crate) target: CString,
17    pub(crate) fstype: String,
18    #[cfg(feature = "loop")]
19    pub(crate) loopback: Option<loopdev::LoopDevice>,
20    pub(crate) loop_path: Option<std::path::PathBuf>,
21}
22
23impl Unmount for Mount {
24    fn unmount(&self, flags: UnmountFlags) -> io::Result<()> {
25        unsafe {
26            unmount_(self.target.as_ptr(), flags)?;
27        }
28
29        #[cfg(feature = "loop")]
30        if let Some(ref loopback) = self.loopback {
31            loopback.detach()?;
32        }
33
34        Ok(())
35    }
36}
37
38impl Mount {
39    /// Creates a [`MountBuilder`] for configuring a new mount.
40    ///
41    /// ```no_run
42    /// use sys_mount::*;
43    ///
44    /// fn main() -> std::io::Result<()> {
45    ///     let _mount = Mount::builder()
46    ///         .fstype("btrfs")
47    ///         .data("subvol=@home")
48    ///         .mount("/dev/sda1", "/home")?;
49    ///     Ok(())
50    /// }
51    /// ```
52    #[inline]
53    #[must_use]
54    pub fn builder<'a>() -> MountBuilder<'a> {
55        MountBuilder::default()
56    }
57
58    /// Mounts the source device to the target path.
59    ///
60    /// Attempts to automatically detect the filesystem of the source device.
61    ///
62    /// For more flexibility, use [`Mount::builder`] instead.
63    ///
64    /// # Errors
65    ///
66    /// Errors if supported filesystems cannot be detected, or the mount fails.
67    #[inline]
68    pub fn new(source: impl AsRef<Path>, target: impl AsRef<Path>) -> io::Result<Mount> {
69        let supported = crate::SupportedFilesystems::new()?;
70        MountBuilder::default()
71            .fstype(&supported)
72            .mount(source, target)
73    }
74
75    /// If the device was associated with a loopback device, that device's path
76    /// can be retrieved here.
77    #[inline]
78    #[must_use]
79    pub fn backing_loop_device(&self) -> Option<&Path> {
80        self.loop_path.as_deref()
81    }
82
83    /// Describes the file system which this mount was mounted with.
84    ///
85    /// This is useful in the event that the mounted device was mounted automatically.
86    #[inline]
87    #[must_use]
88    pub fn get_fstype(&self) -> &str {
89        &self.fstype
90    }
91
92    /// Return the path this mount was mounted on.
93    #[inline]
94    #[must_use]
95    pub fn target_path(&self) -> &Path {
96        Path::new(OsStr::from_bytes(self.target.as_bytes()))
97    }
98
99    #[inline]
100    pub(crate) fn from_target_and_fstype(target: CString, fstype: String) -> Self {
101        Mount {
102            target,
103            fstype,
104            #[cfg(feature = "loop")]
105            loopback: None,
106            loop_path: None,
107        }
108    }
109}
110
111/// An abstraction that will ensure that temporary mounts are dropped in reverse.
112pub struct Mounts(pub Vec<UnmountDrop<Mount>>);
113
114impl Mounts {
115    /// Unmounts all mounts, with the option to do so lazily.
116    ///
117    /// # Errors
118    ///
119    /// Returns on the first error when unmounting.
120    pub fn unmount(&mut self, lazy: bool) -> io::Result<()> {
121        let flags = if lazy {
122            UnmountFlags::DETACH
123        } else {
124            UnmountFlags::empty()
125        };
126        self.0
127            .iter_mut()
128            .rev()
129            .try_for_each(|mount| mount.unmount(flags))
130    }
131}
132
133impl Drop for Mounts {
134    fn drop(&mut self) {
135        for mount in self.0.drain(..).rev() {
136            drop(mount);
137        }
138    }
139}