From 2f0ca715d368f499a6ccb1df0bc8e62a09ab3261 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sun, 9 Mar 2025 09:19:38 +0100 Subject: Rewrite file traversal logic to not suffer from stack overflow problem See also here: https://github.com/ririsoft/async-walkdir/issues/13 At the same time, this now also ignores permission denied errors gracefully and just ignores those files. --- src/file_op.rs | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/file_op.rs b/src/file_op.rs index 5ae9c32..9a06aea 100644 --- a/src/file_op.rs +++ b/src/file_op.rs @@ -14,7 +14,7 @@ use std::path::{Component, Path, PathBuf}; use std::sync::Arc; use actix_web::{HttpRequest, HttpResponse, http::header, web}; -use async_walkdir::{Filtering, WalkDir}; +use async_walkdir::WalkDir; use futures::{StreamExt, TryStreamExt}; use log::{info, warn}; use serde::Deserialize; @@ -60,11 +60,13 @@ impl FileHash { pub async fn recursive_dir_size(dir: &Path) -> Result { #[cfg(target_family = "unix")] let seen_inodes = Arc::new(RwLock::new(HashSet::new())); - let mut entries = WalkDir::new(dir).filter(move |entry| { - { - #[cfg(target_family = "unix")] - let seen = seen_inodes.clone(); - async move { + + let mut entries = WalkDir::new(dir); + + let mut total_size = 0; + loop { + match entries.next().await { + Some(Ok(entry)) => { if let Ok(metadata) = entry.metadata().await { if metadata.is_file() { // On Unix, we want to filter inodes that we've already seen so we get a more @@ -74,31 +76,25 @@ pub async fn recursive_dir_size(dir: &Path) -> Result { let (device_id, inode) = (metadata.dev(), metadata.ino()); // Check if this file has been seen before based on its device ID and inode number - if seen.read().await.contains(&(device_id, inode)) { - return Filtering::Ignore; + if seen_inodes.read().await.contains(&(device_id, inode)) { + continue; } else { - seen.write().await.insert((device_id, inode)); + seen_inodes.write().await.insert((device_id, inode)); } } - return Filtering::Continue; + total_size += metadata.len(); } } - Filtering::Ignore - } - } - }); - - let mut total_size = 0; - loop { - match entries.next().await { - Some(Ok(entry)) => { - if let Ok(metadata) = entry.metadata().await { - total_size += metadata.len(); - } } Some(Err(e)) => { - warn!("Error trying to read file when calculating dir size: {e}"); - return Err(RuntimeError::InvalidPathError(e.to_string())); + if let Some(io_err) = e.into_io() { + match io_err.kind() { + ErrorKind::PermissionDenied => warn!( + "Error trying to read file when calculating dir size: {io_err}, ignoring" + ), + _ => return Err(RuntimeError::InvalidPathError(io_err.to_string())), + } + } } None => break, } -- cgit v1.2.3