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}