diff --git a/yazi-ffi/src/cf_dict.rs b/yazi-ffi/src/cf_dict.rs index b535e2ed..d99abb9d 100644 --- a/yazi-ffi/src/cf_dict.rs +++ b/yazi-ffi/src/cf_dict.rs @@ -27,6 +27,12 @@ impl CFDict { Ok(value) } + pub fn bool(&self, key: &str) -> Result { + let value = self.value(key)?; + #[allow(unexpected_cfgs)] + Ok(unsafe { msg_send![value as *const Object, boolValue] }) + } + pub fn integer(&self, key: &str) -> Result { let value = self.value(key)?; #[allow(unexpected_cfgs)] diff --git a/yazi-fs/src/mounts/linux.rs b/yazi-fs/src/mounts/linux.rs index af983559..89b1c29d 100644 --- a/yazi-fs/src/mounts/linux.rs +++ b/yazi-fs/src/mounts/linux.rs @@ -59,6 +59,21 @@ impl Partitions { }); } + fn partitions() -> Result> { + let mut set = HashSet::new(); + let s = std::fs::read_to_string("/proc/partitions")?; + for line in s.lines().skip(2) { + let mut it = line.split_whitespace(); + let Some(Ok(_major)) = it.next().map(|s| s.parse::()) else { continue }; + let Some(Ok(_minor)) = it.next().map(|s| s.parse::()) else { continue }; + let Some(Ok(_blocks)) = it.next().map(|s| s.parse::()) else { continue }; + if let Some(name) = it.next() { + set.insert(Self::unmangle_octal(name).into_owned()); + } + } + Ok(set) + } + async fn update(me: Locked) { _ = tokio::task::spawn_blocking(move || { let mut guard = me.write(); @@ -75,11 +90,24 @@ impl Partitions { { let set = &self.linux_cache; let mut set: HashSet<&OsStr> = set.iter().map(AsRef::as_ref).collect(); - mounts.iter().filter_map(|p| p.dev_name()).for_each(|s| _ = set.remove(s)); + mounts.iter().filter_map(|p| p.dev_name(true)).for_each(|s| _ = set.remove(s)); mounts.extend(set.into_iter().map(Partition::new)); mounts.sort_unstable_by(|a, b| natsort(a.src.as_bytes(), b.src.as_bytes(), false)); }; + let mut removable: HashMap> = + mounts.iter().filter_map(|p| p.dev_name(false)).map(|s| (s.to_owned(), None)).collect(); + for (s, b) in &mut removable { + match std::fs::read(format!("/sys/block/{}/removable", s.to_string_lossy())) + .unwrap_or_default() + .trim_ascii() + { + b"0" => *b = Some(false), + b"1" => *b = Some(true), + _ => (), + } + } + let labels = Self::labels(); for mount in &mut mounts { if !mount.src.as_bytes().starts_with(b"/dev/") { @@ -88,6 +116,8 @@ impl Partitions { if let Ok(meta) = std::fs::metadata(&mount.src) { mount.rdev = Some(meta.rdev() as _); mount.label = labels.get(&(meta.dev(), meta.ino())).cloned(); + // TODO: mount.external + mount.removable = mount.dev_name(false).and_then(|s| removable.get(s).copied()).flatten(); } } Ok(mounts) @@ -111,21 +141,6 @@ impl Partitions { Ok(vec) } - fn partitions() -> Result> { - let mut set = HashSet::new(); - let s = std::fs::read_to_string("/proc/partitions")?; - for line in s.lines().skip(2) { - let mut it = line.split_whitespace(); - let Some(Ok(_major)) = it.next().map(|s| s.parse::()) else { continue }; - let Some(Ok(_minor)) = it.next().map(|s| s.parse::()) else { continue }; - let Some(Ok(_blocks)) = it.next().map(|s| s.parse::()) else { continue }; - if let Some(name) = it.next() { - set.insert(Self::unmangle_octal(name).into_owned()); - } - } - Ok(set) - } - fn labels() -> HashMap<(u64, u64), OsString> { let mut map = HashMap::new(); let Ok(it) = std::fs::read_dir("/dev/disk/by-label") else { diff --git a/yazi-fs/src/mounts/macos.rs b/yazi-fs/src/mounts/macos.rs index ae0e1fbb..9d080da4 100644 --- a/yazi-fs/src/mounts/macos.rs +++ b/yazi-fs/src/mounts/macos.rs @@ -115,6 +115,8 @@ impl Partitions { fstype: dict.os_string("DAVolumeKind").ok(), label: dict.os_string("DAVolumeName").ok(), capacity: dict.integer("DAMediaSize").unwrap_or_default() as u64, + external: dict.bool("DADeviceInternal").ok().map(|b| !b), + removable: dict.bool("DAMediaRemovable").ok(), ..partition }); } diff --git a/yazi-fs/src/mounts/partition.rs b/yazi-fs/src/mounts/partition.rs index d1eba779..6308f970 100644 --- a/yazi-fs/src/mounts/partition.rs +++ b/yazi-fs/src/mounts/partition.rs @@ -2,13 +2,15 @@ use std::{ffi::OsString, path::PathBuf}; #[derive(Debug, Default)] pub struct Partition { - pub src: OsString, - pub dist: Option, + pub src: OsString, + pub dist: Option, #[cfg(unix)] - pub rdev: Option, - pub label: Option, - pub fstype: Option, - pub capacity: u64, + pub rdev: Option, + pub label: Option, + pub fstype: Option, + pub capacity: u64, + pub external: Option, + pub removable: Option, } impl Partition { @@ -44,7 +46,24 @@ impl Partition { #[inline] #[cfg(target_os = "linux")] - pub(super) fn dev_name(&self) -> Option<&std::ffi::OsStr> { - std::path::Path::new(&self.src).strip_prefix("/dev/").ok().map(|p| p.as_os_str()) + pub(super) fn dev_name(&self, full: bool) -> Option<&std::ffi::OsStr> { + use std::os::unix::ffi::OsStrExt; + + let s = std::path::Path::new(&self.src).strip_prefix("/dev/").ok()?.as_os_str(); + if full { + return Some(s); + } + + let b = s.as_bytes(); + if b.len() < 3 { + None + } else if b.starts_with(b"sd") || b.starts_with(b"hd") || b.starts_with(b"vd") { + Some(std::ffi::OsStr::from_bytes(&b[..3])) + } else if b.starts_with(b"nvme") || b.starts_with(b"mmcblk") { + let n = b.iter().position(|&b| b == b'p').unwrap_or(b.len()); + Some(std::ffi::OsStr::from_bytes(&b[..n])) + } else { + None + } } } diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index c2543491..bae09a18 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -162,6 +162,8 @@ fn partitions(lua: &Lua) -> mlua::Result { ("dist", p.dist.clone().into_lua(&lua)?), ("label", p.label.clone().into_lua(&lua)?), ("fstype", p.fstype.clone().into_lua(&lua)?), + ("external", p.external.into_lua(&lua)?), + ("removable", p.removable.into_lua(&lua)?), ]) }) .collect::>>()