sys_mount/
umount.rs

1// Copyright 2018-2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::UnmountFlags;
5use libc::{c_char, umount2};
6use std::{ffi::CString, io, ops::Deref, os::unix::ffi::OsStrExt, path::Path, ptr};
7
8/// Unmount trait which enables any type that implements it to be upgraded into an `UnmountDrop`.
9pub trait Unmount {
10    /// Unmount this mount with the given `flags`.
11    ///
12    /// This will also detach the loopback device that the mount is assigned to, if
13    /// it was associated with a loopback device.
14    ///
15    /// # Errors
16    ///
17    /// On failure to unmount
18    fn unmount(&self, flags: UnmountFlags) -> io::Result<()>;
19
20    /// Upgrades `Self` into an `UnmountDrop`, which will unmount the mount when it is dropped.
21    fn into_unmount_drop(self, flags: UnmountFlags) -> UnmountDrop<Self>
22    where
23        Self: Sized,
24    {
25        UnmountDrop { mount: self, flags }
26    }
27}
28
29/// Unmounts the underlying mounted device upon drop.
30pub struct UnmountDrop<T: Unmount> {
31    pub(crate) mount: T,
32    pub(crate) flags: UnmountFlags,
33}
34
35impl<T: Unmount> UnmountDrop<T> {
36    /// Modify the previously-set unmount flags.
37    pub fn set_unmount_flags(&mut self, flags: UnmountFlags) {
38        self.flags = flags;
39    }
40}
41
42impl<T: Unmount> Deref for UnmountDrop<T> {
43    type Target = T;
44
45    fn deref(&self) -> &T {
46        &self.mount
47    }
48}
49
50impl<T: Unmount> Drop for UnmountDrop<T> {
51    fn drop(&mut self) {
52        let _res = self.mount.unmount(self.flags);
53    }
54}
55
56/// Unmounts the device at `path` using the provided `UnmountFlags`.
57///
58/// This will not detach a loopback device if the mount was attached to one. This behavior may
59/// change in the future, once the [loopdev](https://crates.io/crates/loopdev) crate supports
60/// querying loopback device info.
61///
62/// # Errors
63///
64/// - If the path is not a valid C String
65/// - Or the unmount function fails
66///
67/// # Example
68///
69/// ```rust,no_run
70/// extern crate sys_mount;
71///
72/// use sys_mount::{unmount, UnmountFlags};
73///
74/// fn main() {
75///     // Unmount device at `/target/path` lazily.
76///     let result = unmount("/target/path", UnmountFlags::DETACH);
77/// }
78/// ```
79pub fn unmount<P: AsRef<Path>>(path: P, flags: UnmountFlags) -> io::Result<()> {
80    let mount = CString::new(path.as_ref().as_os_str().as_bytes().to_owned());
81    let mount_ptr = mount
82        .as_ref()
83        .ok()
84        .map_or(ptr::null(), |cstr| cstr.as_ptr());
85
86    unsafe { unmount_(mount_ptr, flags) }
87}
88
89#[inline]
90pub(crate) unsafe fn unmount_(mount_ptr: *const c_char, flags: UnmountFlags) -> io::Result<()> {
91    match umount2(mount_ptr, flags.bits()) {
92        0 => Ok(()),
93        _err => Err(io::Error::last_os_error()),
94    }
95}