sys_mount/
lib.rs

1// Copyright 2018-2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! High level abstraction over the `mount` and `umount2` system calls.
5//!
6//! If the `loop` feature is enabled (default), additionally supports creating loopback devices
7//! automatically when mounting an iso or squashfs file.
8//!
9//! # Example
10//!
11//! ```rust,no_run
12//! extern crate sys_mount;
13//!
14//! use std::process::exit;
15//! use sys_mount::{
16//!     Mount,
17//!     MountFlags,
18//!     SupportedFilesystems,
19//!     Unmount,
20//!     UnmountFlags
21//! };
22//!
23//! fn main() {
24//!     // Fetch a list of supported file systems.
25//!     // When mounting, a file system will be selected from this.
26//!     let supported = SupportedFilesystems::new().unwrap();
27//!
28//!     // Attempt to mount the src device to the dest directory.
29//!     let mount_result = Mount::builder()
30//!         .fstype("btrfs")
31//!         .data("subvol=@home")
32//!         .mount("/dev/sda1", "/home");
33//!
34//!     match mount_result {
35//!         Ok(mount) => {
36//!             // Make the mount temporary, so that it will be unmounted on drop.
37//!             let mount = mount.into_unmount_drop(UnmountFlags::DETACH);
38//!             // Do thing with the mounted device.
39//!         }
40//!         Err(why) => {
41//!             eprintln!("failed to mount device: {}", why);
42//!             exit(1);
43//!         }
44//!     }
45//! }
46
47extern crate libc;
48#[macro_use]
49extern crate bitflags;
50#[macro_use]
51extern crate thiserror;
52
53mod builder;
54mod flags;
55mod fstype;
56mod mount;
57mod supported;
58mod umount;
59
60pub use self::{builder::*, flags::*, fstype::*, mount::*, supported::*, umount::*};
61
62use libc::swapoff as c_swapoff;
63use std::{
64    ffi::CString,
65    io::{self, Error, ErrorKind},
66    os::unix::ffi::OsStrExt,
67    path::Path,
68};
69
70#[derive(Debug, Error)]
71pub enum ScopedMountError {
72    #[error("cannot get list of supported file systems")]
73    Supported(#[source] io::Error),
74    #[error("could not mount partition")]
75    Mount(#[source] io::Error),
76}
77
78/// Mount a partition temporarily for the duration of the scoped block within.
79///
80/// # Errors
81///
82/// - Fails if the supported file systems cannot be found.
83/// - Or if it fails to unmount
84pub fn scoped_mount<T, S: FnOnce() -> T>(
85    source: &Path,
86    mount_at: &Path,
87    scope: S,
88) -> Result<T, ScopedMountError> {
89    let supported = SupportedFilesystems::new().map_err(ScopedMountError::Supported)?;
90
91    Mount::builder()
92        .fstype(&supported)
93        .mount(source, mount_at)
94        .map_err(ScopedMountError::Mount)?;
95
96    let result = scope();
97
98    if let Err(why) = unmount(mount_at, UnmountFlags::empty()) {
99        tracing::warn!("{}: failed to unmount: {}", mount_at.display(), why);
100    }
101
102    Ok(result)
103}
104
105/// Unmounts a swap partition using `libc::swapoff`
106///
107/// # Errors
108///
109/// - If the destination path is not a valid C String
110/// - Or the swapoff function fails
111pub fn swapoff<P: AsRef<Path>>(dest: P) -> io::Result<()> {
112    let Ok(swap) = CString::new(dest.as_ref().as_os_str().as_bytes().to_owned()) else {
113        return Err(Error::new(
114            ErrorKind::Other,
115            format!(
116                "swap path is not a valid c string: '{}'",
117                dest.as_ref().display()
118            )
119        ))
120    };
121
122    match unsafe { c_swapoff(swap.as_ptr()) } {
123        0 => Ok(()),
124
125        _err => Err(Error::new(
126            ErrorKind::Other,
127            format!(
128                "failed to swapoff {}: {}",
129                dest.as_ref().display(),
130                Error::last_os_error()
131            ),
132        )),
133    }
134}
135
136#[inline]
137fn to_cstring(data: &[u8]) -> io::Result<CString> {
138    CString::new(data).map_err(|why| {
139        io::Error::new(
140            io::ErrorKind::InvalidData,
141            format!("failed to create `CString`: {}", why),
142        )
143    })
144}