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
use libc::*; use std::{ffi::CString, io, ops::Deref, os::unix::ffi::OsStrExt, path::Path, ptr}; /// Unmount trait which enables any type that implements it to be upgraded into an `UnmountDrop`. pub trait Unmount { /// Unmount this mount with the given `flags`. /// /// This will also detach the loopback device that the mount is assigned to, if /// it was associated with a loopback device. fn unmount(&self, flags: UnmountFlags) -> io::Result<()>; /// Upgrades `Self` into an `UnmountDrop`, which will unmount the mount when it is dropped. fn into_unmount_drop(self, flags: UnmountFlags) -> UnmountDrop<Self> where Self: Sized, { UnmountDrop { mount: self, flags } } } /// Unmounts the underlying mounted device upon drop. pub struct UnmountDrop<T: Unmount> { pub(crate) mount: T, pub(crate) flags: UnmountFlags, } impl<T: Unmount> UnmountDrop<T> { /// Modify the previously-set unmount flags. pub fn set_unmount_flags(&mut self, flags: UnmountFlags) { self.flags = flags; } } impl<T: Unmount> Deref for UnmountDrop<T> { type Target = T; fn deref(&self) -> &T { &self.mount } } impl<T: Unmount> Drop for UnmountDrop<T> { fn drop(&mut self) { let _ = self.mount.unmount(self.flags); } } bitflags! { /// Flags which may be specified when unmounting a file system. pub struct UnmountFlags: c_int { /// Force unmount even if busy. This can cause data loss. (Only for NFS mounts.) const FORCE = MNT_FORCE; /// Perform a lazy unmount: make the mount point unavailable for new accesses, /// and actually perform the unmount when the mount point ceases to be busy. const DETACH = MNT_DETACH; /// Mark the mount point as expired. If a mount point is not currently in use, /// then an initial call to umount2() with this flag fails with the error EAGAIN, /// but marks the mount point as expired. The mount point remains expired as /// long as it isn't accessed by any process. A second umount2() call specifying /// MNT_EXPIRE unmounts an expired mount point. This flag cannot be specified with /// either MNT_FORCE or MNT_DETACH. const EXPIRE = MNT_EXPIRE; /// Don't dereference target if it is a symbolic link. This flag allows security /// problems to be avoided in set-user-ID-root programs that allow unprivileged /// users to unmount file systems. const NOFOLLOW = O_NOFOLLOW; } } /// Unmounts the device at `path` using the provided `UnmountFlags`. /// /// This will not detach a loopback device if the mount was attached to one. This behavior may /// change in the future, once the [loopdev](https://crates.io/crates/loopdev) crate supports /// querying loopback device info. /// /// ```rust,no_run /// extern crate sys_mount; /// /// use sys_mount::{unmount, UnmountFlags}; /// /// fn main() { /// // Unmount device at `/target/path` lazily. /// let result = unmount("/target/path", UnmountFlags::DETACH); /// } /// ``` pub fn unmount<P: AsRef<Path>>(path: P, flags: UnmountFlags) -> io::Result<()> { let mount = CString::new(path.as_ref().as_os_str().as_bytes().to_owned()); let mount_ptr = mount.as_ref().ok().map_or(ptr::null(), |cstr| cstr.as_ptr()); unsafe { unmount_(mount_ptr, flags) } } pub(crate) unsafe fn unmount_(mount_ptr: *const c_char, flags: UnmountFlags) -> io::Result<()> { match umount2(mount_ptr, flags.bits()) { 0 => Ok(()), _err => Err(io::Error::last_os_error()), } }