mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Skip systemic mounts during local size calculation
Agent-Logs-Url: https://github.com/sxyazi/yazi/sessions/89d44d10-de98-42c8-bc7b-dc68c41459bb Co-authored-by: sxyazi <17523360+sxyazi@users.noreply.github.com>
This commit is contained in:
parent
62862d2fc4
commit
9711d6aa25
1 changed files with 111 additions and 10 deletions
|
|
@ -3,13 +3,19 @@ use std::{collections::VecDeque, future::poll_fn, io, mem, path::{Path, PathBuf}
|
|||
use either::Either;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::mounts::Partition;
|
||||
use crate::cha::Cha;
|
||||
|
||||
type Task = Either<PathBuf, std::fs::ReadDir>;
|
||||
#[cfg(target_os = "linux")]
|
||||
type SystemicMounts = std::collections::HashSet<PathBuf>;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
type SystemicMounts = ();
|
||||
|
||||
pub enum SizeCalculator {
|
||||
Idle((VecDeque<Task>, Option<u64>), Cha),
|
||||
Pending(JoinHandle<(VecDeque<Task>, Option<u64>)>, Cha),
|
||||
Idle((VecDeque<Task>, Option<u64>, SystemicMounts), Cha),
|
||||
Pending(JoinHandle<(VecDeque<Task>, Option<u64>, SystemicMounts)>, Cha),
|
||||
}
|
||||
|
||||
impl SizeCalculator {
|
||||
|
|
@ -18,12 +24,17 @@ impl SizeCalculator {
|
|||
tokio::task::spawn_blocking(move || {
|
||||
let cha = Cha::new(p.file_name().unwrap_or_default(), std::fs::symlink_metadata(&p)?);
|
||||
if !cha.is_dir() {
|
||||
return Ok(Self::Idle((VecDeque::new(), Some(cha.len)), cha));
|
||||
return Ok(Self::Idle((VecDeque::new(), Some(cha.len), systemic_mounts(&p)), cha));
|
||||
}
|
||||
|
||||
let systemic = systemic_mounts(&p);
|
||||
if is_systemic_mount(&p, &systemic) {
|
||||
return Ok(Self::Idle((VecDeque::new(), Some(0), systemic), cha));
|
||||
}
|
||||
|
||||
let mut buf = VecDeque::from([Either::Right(std::fs::read_dir(&p)?)]);
|
||||
let size = Self::next_chunk(&mut buf);
|
||||
Ok(Self::Idle((buf, size), cha))
|
||||
let size = Self::next_chunk(&mut buf, &systemic);
|
||||
Ok(Self::Idle((buf, size, systemic), cha))
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
|
@ -47,7 +58,7 @@ impl SizeCalculator {
|
|||
poll_fn(|cx| {
|
||||
loop {
|
||||
match self {
|
||||
Self::Idle((buf, size), cha) => {
|
||||
Self::Idle((buf, size, systemic), cha) => {
|
||||
if let Some(s) = size.take() {
|
||||
return Poll::Ready(Ok(Some(s)));
|
||||
} else if buf.is_empty() {
|
||||
|
|
@ -55,10 +66,11 @@ impl SizeCalculator {
|
|||
}
|
||||
|
||||
let mut buf = mem::take(buf);
|
||||
let systemic = mem::take(systemic);
|
||||
*self = Self::Pending(
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let size = Self::next_chunk(&mut buf);
|
||||
(buf, size)
|
||||
let size = Self::next_chunk(&mut buf, &systemic);
|
||||
(buf, size, systemic)
|
||||
}),
|
||||
*cha,
|
||||
);
|
||||
|
|
@ -72,7 +84,10 @@ impl SizeCalculator {
|
|||
.await
|
||||
}
|
||||
|
||||
fn next_chunk(buf: &mut VecDeque<Either<PathBuf, std::fs::ReadDir>>) -> Option<u64> {
|
||||
fn next_chunk(
|
||||
buf: &mut VecDeque<Either<PathBuf, std::fs::ReadDir>>,
|
||||
systemic: &SystemicMounts,
|
||||
) -> Option<u64> {
|
||||
let (mut i, mut size, now) = (0, 0, Instant::now());
|
||||
macro_rules! pop_and_continue {
|
||||
() => {{
|
||||
|
|
@ -102,7 +117,10 @@ impl SizeCalculator {
|
|||
let Ok(ent) = next else { continue };
|
||||
let Ok(ft) = ent.file_type() else { continue };
|
||||
if ft.is_dir() {
|
||||
buf.push_back(Either::Left(ent.path()));
|
||||
let path = ent.path();
|
||||
if !is_systemic_mount(&path, systemic) {
|
||||
buf.push_back(Either::Left(path));
|
||||
}
|
||||
} else if let Ok(meta) = ent.metadata() {
|
||||
size += meta.len();
|
||||
}
|
||||
|
|
@ -110,3 +128,86 @@ impl SizeCalculator {
|
|||
Some(size)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn systemic_mounts(root: &Path) -> SystemicMounts {
|
||||
systemic_mounts_from(root, &std::fs::read_to_string("/proc/mounts").unwrap_or_default())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn systemic_mounts(_: &Path) -> SystemicMounts {}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_systemic_mount(path: &Path, systemic: &SystemicMounts) -> bool { systemic.contains(path) }
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn is_systemic_mount(_: &Path, _: &SystemicMounts) -> bool { false }
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn systemic_mounts_from(root: &Path, mounts: &str) -> SystemicMounts {
|
||||
mounts
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
let mut it = line.split_whitespace();
|
||||
let _src = it.next()?;
|
||||
let dist = unmangle_octal(it.next()?);
|
||||
let fstype = unmangle_octal(it.next()?);
|
||||
let dist = PathBuf::from(dist.as_ref());
|
||||
(Partition { fstype: Some(fstype.into_owned().into()), ..Default::default() }.systemic()
|
||||
&& (dist == root || dist.starts_with(root)))
|
||||
.then_some(dist)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn unmangle_octal(s: &str) -> std::borrow::Cow<'_, str> {
|
||||
use yazi_shared::replace_cow;
|
||||
|
||||
let mut s = std::borrow::Cow::Borrowed(s);
|
||||
for (a, b) in
|
||||
[(r"\011", "\t"), (r"\012", "\n"), (r"\040", " "), (r"\043", "#"), (r"\134", r"\")]
|
||||
{
|
||||
s = replace_cow(s, a, b);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{path::Path, sync::OnceLock};
|
||||
|
||||
use super::SizeCalculator;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use super::systemic_mounts_from;
|
||||
|
||||
fn init() {
|
||||
static INIT: OnceLock<()> = OnceLock::new();
|
||||
INIT.get_or_init(crate::init);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn systemic_mounts_only_include_systemic_descendants() {
|
||||
let mounts = systemic_mounts_from(
|
||||
Path::new("/"),
|
||||
"rootfs / ext4 rw 0 0\nproc /proc proc rw 0 0\nsysfs /sys sysfs rw 0 0\ntmpfs /tmp tmpfs rw 0 0\n/dev/sda1 /home ext4 rw 0 0\n",
|
||||
);
|
||||
|
||||
assert!(mounts.contains(Path::new("/proc")));
|
||||
assert!(mounts.contains(Path::new("/sys")));
|
||||
assert!(mounts.contains(Path::new("/tmp")));
|
||||
assert!(!mounts.contains(Path::new("/home")));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[tokio::test]
|
||||
async fn proc_size_ignores_systemic_pseudo_files() {
|
||||
init();
|
||||
|
||||
let mut it = SizeCalculator::new(Path::new("/proc")).await.unwrap();
|
||||
assert_eq!(it.next().await.unwrap(), Some(0));
|
||||
assert_eq!(it.next().await.unwrap(), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue