Просмотр исходного кода

feat: Support --mount option on Mac

Robert Minsk 2 лет назад
Родитель
Сommit
e2260b7c2c
8 измененных файлов с 114 добавлено и 33 удалено
  1. 2 2
      src/fs/file.rs
  2. 1 1
      src/fs/mod.rs
  3. 0 6
      src/fs/mounts.rs
  4. 16 0
      src/fs/mounts/linux.rs
  5. 43 0
      src/fs/mounts/macos.rs
  6. 39 0
      src/fs/mounts/mod.rs
  7. 12 23
      src/main.rs
  8. 1 1
      src/options/help.rs

+ 2 - 2
src/fs/file.rs

@@ -253,7 +253,7 @@ impl<'dir> File<'dir> {
 
 
     /// Whether this file is a mount point
     /// Whether this file is a mount point
     pub fn is_mount_point(&self) -> bool {
     pub fn is_mount_point(&self) -> bool {
-        if cfg!(target_os = "linux") && self.is_directory() {
+        if cfg!(any(target_os = "linux", target_os = "macos")) && self.is_directory() {
             return match self.absolute_path.as_ref() {
             return match self.absolute_path.as_ref() {
                 Some(path) => ALL_MOUNTS.contains_key(path),
                 Some(path) => ALL_MOUNTS.contains_key(path),
                 None => false,
                 None => false,
@@ -264,7 +264,7 @@ impl<'dir> File<'dir> {
 
 
     /// The filesystem device and type for a mount point
     /// The filesystem device and type for a mount point
     pub fn mount_point_info(&self) -> Option<&MountedFs> {
     pub fn mount_point_info(&self) -> Option<&MountedFs> {
-        if cfg!(target_os = "linux") {
+        if cfg!(any(target_os = "linux",target_os = "macos")) {
             return self.absolute_path.as_ref().and_then(|p|ALL_MOUNTS.get(p));
             return self.absolute_path.as_ref().and_then(|p|ALL_MOUNTS.get(p));
         }
         }
         None
         None

+ 1 - 1
src/fs/mod.rs

@@ -8,4 +8,4 @@ pub mod dir_action;
 pub mod feature;
 pub mod feature;
 pub mod fields;
 pub mod fields;
 pub mod filter;
 pub mod filter;
-pub mod mounts;
+pub mod mounts;

+ 0 - 6
src/fs/mounts.rs

@@ -1,6 +0,0 @@
-/// Details of a mounted filesystem.
-pub struct MountedFs {
-    pub dest: String,
-    pub fstype: String,
-    pub source: String,
-}

+ 16 - 0
src/fs/mounts/linux.rs

@@ -0,0 +1,16 @@
+use proc_mounts::MountList;
+use crate::fs::mounts::{Error, MountedFs};
+
+/// Get a list of all mounted filesystems
+pub fn mounts() -> Result<Vec<MountedFs>, Error> {
+    Ok(MountList::new()
+           .map_err(Error::IOError)?
+           .0.iter()
+           .map(|mount| MountedFs {
+               dest: mount.dest.clone(),
+               fstype: mount.fstype.clone(),
+               source: mount.source.to_string_lossy().into()
+           })
+           .collect()
+    )
+}

+ 43 - 0
src/fs/mounts/macos.rs

@@ -0,0 +1,43 @@
+use std::{mem, ptr};
+use std::ffi::{CStr, OsStr};
+use std::os::raw::{c_char, c_int};
+use std::os::unix::ffi::OsStrExt;
+use std::path::PathBuf;
+use libc::{__error, getfsstat, MNT_NOWAIT, statfs};
+use crate::fs::mounts::{Error, MountedFs};
+
+/// Get a list of all mounted filesystem
+pub fn mounts() -> Result<Vec<MountedFs>, Error> {
+    // Passing a null pointer and zero bufsize will return the number of mounts
+    let mut count: i32 = unsafe { getfsstat(ptr::null_mut(), 0, MNT_NOWAIT) };
+    let mut mntbuf = Vec::<statfs>::new();
+    if count > 0 {
+        mntbuf.resize_with(count as usize, || unsafe { mem::zeroed() });
+        let bufsize = mntbuf.len() * mem::size_of::<statfs>();
+        count = unsafe { getfsstat(mntbuf.as_mut_ptr(), bufsize as c_int, MNT_NOWAIT) };
+        // Resize if the mount table has shrunk since last call
+        if count >= 0 {
+            mntbuf.truncate(count as usize);
+        }
+    }
+    if count < 0 {
+        return Err(Error::GetFSStatError(unsafe { *__error() }));
+    }
+
+    let mut mounts = Vec::with_capacity(count as usize);
+    for mnt in &mntbuf {
+        let mount_point = OsStr::from_bytes(
+            unsafe { CStr::from_ptr(mnt.f_mntonname.as_ptr().cast::<c_char>()) }.to_bytes()
+        );
+        let dest = PathBuf::from(mount_point);
+        let fstype = unsafe { CStr::from_ptr(mnt.f_fstypename.as_ptr().cast::<c_char>()) }
+            .to_string_lossy()
+            .into();
+        let source = unsafe { CStr::from_ptr(mnt.f_mntfromname.as_ptr().cast::<c_char>()) }
+            .to_string_lossy()
+            .into();
+        mounts.push(MountedFs { dest, fstype, source });
+    }
+
+    Ok(mounts)
+}

+ 39 - 0
src/fs/mounts/mod.rs

@@ -0,0 +1,39 @@
+#[cfg(target_os = "linux")]
+mod linux;
+#[cfg(target_os = "macos")]
+mod macos;
+
+#[cfg(target_os = "linux")]
+pub use linux::mounts;
+#[cfg(target_os = "macos")]
+pub use macos::mounts;
+
+/// Details of a mounted filesystem.
+#[derive(Clone)]
+pub struct MountedFs {
+    pub dest: std::path::PathBuf,
+    pub fstype: String,
+    pub source: String,
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Error {
+    #[cfg(target_os = "macos")]
+    GetFSStatError(i32),
+    #[cfg(target_os = "linux")]
+    IOError(std::io::Error)
+}
+
+impl std::error::Error for Error {}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            #[cfg(target_os = "macos")]
+            Error::GetFSStatError(err) => write!(f, "getfsstat failed: {err}"),
+            #[cfg(target_os = "linux")]
+            Error::IOError(err) => write!(f, "failed to read /proc/mounts: {err}")
+        }
+    }
+}

+ 12 - 23
src/main.rs

@@ -32,13 +32,10 @@ use ansiterm::{ANSIStrings, Style};
 
 
 use log::*;
 use log::*;
 
 
-#[cfg(target_os = "linux")]
-use proc_mounts::MountList;
-
 #[macro_use]
 #[macro_use]
 extern crate lazy_static;
 extern crate lazy_static;
 
 
-use crate::fs::mounts::MountedFs;
+use crate::fs::mounts;
 use crate::fs::{Dir, File};
 use crate::fs::{Dir, File};
 use crate::fs::feature::git::GitCache;
 use crate::fs::feature::git::GitCache;
 use crate::fs::filter::GitIgnore;
 use crate::fs::filter::GitIgnore;
@@ -56,33 +53,25 @@ mod theme;
 // A lazily initialised static map of all mounted file systems.
 // A lazily initialised static map of all mounted file systems.
 //
 //
 // The map contains a mapping from the mounted directory path to the
 // The map contains a mapping from the mounted directory path to the
-// corresponding mount information. On Linux systems, this map is populated
-// using the `proc-mounts` crate. If there's an error retrieving the mount
-// list or if we're not running on Linux, the map will be empty.
+// corresponding mount information. If there's an error retrieving the mount
+// list or if we're not running on Linux or Mac, the map will be empty.
 //
 //
 // Initialise this at application start so we don't have to look the details
 // Initialise this at application start so we don't have to look the details
 // up for every directory. Ideally this would only be done if the --mounts
 // up for every directory. Ideally this would only be done if the --mounts
 // option is specified which will be significantly easier once the move
 // option is specified which will be significantly easier once the move
 // to `clap` is complete.
 // to `clap` is complete.
 lazy_static! {
 lazy_static! {
-    static ref ALL_MOUNTS: HashMap<PathBuf, MountedFs> = {
-        #[cfg(target_os = "linux")]
-        match MountList::new() {
-            Ok(mount_list) => {
-                let mut m = HashMap::new();
-                mount_list.0.iter().for_each(|mount| {
-                    m.insert(mount.dest.clone(), MountedFs {
-                        dest: mount.dest.to_string_lossy().into_owned(),
-                        fstype: mount.fstype.clone(),
-                        source: mount.source.to_string_lossy().into(),
-                    });
-                });
-                m
+    static ref ALL_MOUNTS: HashMap<PathBuf, mounts::MountedFs> = {
+        let mut mount_map: HashMap<PathBuf, mounts::MountedFs> = HashMap::new();
+
+        #[cfg(any(target_os = "linux", target_os = "macos"))]
+        if let Ok(mounts)  = mounts::mounts() {
+            for mount in mounts {
+                mount_map.insert(mount.dest.clone(), mount);
             }
             }
-            Err(_) => HashMap::new()
         }
         }
-        #[cfg(not(target_os = "linux"))]
-        HashMap::new()
+
+        mount_map
     };
     };
 }
 }
 
 

+ 1 - 1
src/options/help.rs

@@ -53,7 +53,7 @@ LONG VIEW OPTIONS
   -H, --links              list each file's number of hard links
   -H, --links              list each file's number of hard links
   -i, --inode              list each file's inode number
   -i, --inode              list each file's inode number
   -m, --modified           use the modified timestamp field
   -m, --modified           use the modified timestamp field
-  -M, --mounts             show mount details (Linux only)
+  -M, --mounts             show mount details (Linux and MacOS only)
   -n, --numeric            list numeric user and group IDs
   -n, --numeric            list numeric user and group IDs
   -S, --blocksize          show size of allocated file system blocks
   -S, --blocksize          show size of allocated file system blocks
   -t, --time FIELD         which timestamp field to list (modified, accessed, created)
   -t, --time FIELD         which timestamp field to list (modified, accessed, created)