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 either::Either;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use crate::mounts::Partition;
|
||||||
use crate::cha::Cha;
|
use crate::cha::Cha;
|
||||||
|
|
||||||
type Task = Either<PathBuf, std::fs::ReadDir>;
|
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 {
|
pub enum SizeCalculator {
|
||||||
Idle((VecDeque<Task>, Option<u64>), Cha),
|
Idle((VecDeque<Task>, Option<u64>, SystemicMounts), Cha),
|
||||||
Pending(JoinHandle<(VecDeque<Task>, Option<u64>)>, Cha),
|
Pending(JoinHandle<(VecDeque<Task>, Option<u64>, SystemicMounts)>, Cha),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SizeCalculator {
|
impl SizeCalculator {
|
||||||
|
|
@ -18,12 +24,17 @@ impl SizeCalculator {
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let cha = Cha::new(p.file_name().unwrap_or_default(), std::fs::symlink_metadata(&p)?);
|
let cha = Cha::new(p.file_name().unwrap_or_default(), std::fs::symlink_metadata(&p)?);
|
||||||
if !cha.is_dir() {
|
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 mut buf = VecDeque::from([Either::Right(std::fs::read_dir(&p)?)]);
|
||||||
let size = Self::next_chunk(&mut buf);
|
let size = Self::next_chunk(&mut buf, &systemic);
|
||||||
Ok(Self::Idle((buf, size), cha))
|
Ok(Self::Idle((buf, size, systemic), cha))
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +58,7 @@ impl SizeCalculator {
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
loop {
|
loop {
|
||||||
match self {
|
match self {
|
||||||
Self::Idle((buf, size), cha) => {
|
Self::Idle((buf, size, systemic), cha) => {
|
||||||
if let Some(s) = size.take() {
|
if let Some(s) = size.take() {
|
||||||
return Poll::Ready(Ok(Some(s)));
|
return Poll::Ready(Ok(Some(s)));
|
||||||
} else if buf.is_empty() {
|
} else if buf.is_empty() {
|
||||||
|
|
@ -55,10 +66,11 @@ impl SizeCalculator {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf = mem::take(buf);
|
let mut buf = mem::take(buf);
|
||||||
|
let systemic = mem::take(systemic);
|
||||||
*self = Self::Pending(
|
*self = Self::Pending(
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let size = Self::next_chunk(&mut buf);
|
let size = Self::next_chunk(&mut buf, &systemic);
|
||||||
(buf, size)
|
(buf, size, systemic)
|
||||||
}),
|
}),
|
||||||
*cha,
|
*cha,
|
||||||
);
|
);
|
||||||
|
|
@ -72,7 +84,10 @@ impl SizeCalculator {
|
||||||
.await
|
.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());
|
let (mut i, mut size, now) = (0, 0, Instant::now());
|
||||||
macro_rules! pop_and_continue {
|
macro_rules! pop_and_continue {
|
||||||
() => {{
|
() => {{
|
||||||
|
|
@ -102,7 +117,10 @@ impl SizeCalculator {
|
||||||
let Ok(ent) = next else { continue };
|
let Ok(ent) = next else { continue };
|
||||||
let Ok(ft) = ent.file_type() else { continue };
|
let Ok(ft) = ent.file_type() else { continue };
|
||||||
if ft.is_dir() {
|
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() {
|
} else if let Ok(meta) = ent.metadata() {
|
||||||
size += meta.len();
|
size += meta.len();
|
||||||
}
|
}
|
||||||
|
|
@ -110,3 +128,86 @@ impl SizeCalculator {
|
||||||
Some(size)
|
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