feat: new external and removable fields in the fs.partitions() API (#2343)

This commit is contained in:
三咲雅 · Misaki Masa 2025-02-15 23:15:31 +08:00 committed by GitHub
parent 38e45c647b
commit 22c8f370dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 68 additions and 24 deletions

View file

@ -27,6 +27,12 @@ impl CFDict {
Ok(value)
}
pub fn bool(&self, key: &str) -> Result<bool> {
let value = self.value(key)?;
#[allow(unexpected_cfgs)]
Ok(unsafe { msg_send![value as *const Object, boolValue] })
}
pub fn integer(&self, key: &str) -> Result<i64> {
let value = self.value(key)?;
#[allow(unexpected_cfgs)]

View file

@ -59,6 +59,21 @@ impl Partitions {
});
}
fn partitions() -> Result<HashSet<String>> {
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::<u16>()) else { continue };
let Some(Ok(_minor)) = it.next().map(|s| s.parse::<u16>()) else { continue };
let Some(Ok(_blocks)) = it.next().map(|s| s.parse::<u32>()) 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<OsString, Option<bool>> =
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<HashSet<String>> {
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::<u16>()) else { continue };
let Some(Ok(_minor)) = it.next().map(|s| s.parse::<u16>()) else { continue };
let Some(Ok(_blocks)) = it.next().map(|s| s.parse::<u32>()) 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 {

View file

@ -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
});
}

View file

@ -2,13 +2,15 @@ use std::{ffi::OsString, path::PathBuf};
#[derive(Debug, Default)]
pub struct Partition {
pub src: OsString,
pub dist: Option<PathBuf>,
pub src: OsString,
pub dist: Option<PathBuf>,
#[cfg(unix)]
pub rdev: Option<libc::dev_t>,
pub label: Option<OsString>,
pub fstype: Option<OsString>,
pub capacity: u64,
pub rdev: Option<libc::dev_t>,
pub label: Option<OsString>,
pub fstype: Option<OsString>,
pub capacity: u64,
pub external: Option<bool>,
pub removable: Option<bool>,
}
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
}
}
}

View file

@ -162,6 +162,8 @@ fn partitions(lua: &Lua) -> mlua::Result<Function> {
("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::<mlua::Result<Vec<Table>>>()