From bbf0f1cc1f2531ea042aeae6806ce38d26125499 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Fri, 28 Feb 2020 18:19:54 +0100 Subject: [PATCH] New implementation of broken-symlink handling --- src/fshelper/mod.rs | 4 +-- src/walk.rs | 60 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/fshelper/mod.rs b/src/fshelper/mod.rs index 01ec6ee..580bfd5 100644 --- a/src/fshelper/mod.rs +++ b/src/fshelper/mod.rs @@ -13,7 +13,7 @@ use std::io; use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; -use ignore::DirEntry; +use crate::walk; pub fn path_absolute_form(path: &Path) -> io::Result { if path.is_absolute() { @@ -55,7 +55,7 @@ pub fn is_executable(_: &fs::Metadata) -> bool { false } -pub fn is_empty(entry: &DirEntry) -> bool { +pub fn is_empty(entry: &walk::DirEntry) -> bool { if let Some(file_type) = entry.file_type() { if file_type.is_dir() { if let Ok(mut entries) = fs::read_dir(entry.path()) { diff --git a/src/walk.rs b/src/walk.rs index b3190e0..be389ca 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -15,8 +15,9 @@ use crate::output; use std::borrow::Cow; use std::error::Error; use std::ffi::OsStr; +use std::fs::{FileType, Metadata}; use std::io; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; @@ -253,6 +254,36 @@ fn spawn_receiver( }) } +pub enum DirEntry { + Normal(ignore::DirEntry), + BrokenSymlink(PathBuf), +} + +impl DirEntry { + pub fn path(&self) -> &Path { + match self { + DirEntry::Normal(e) => e.path(), + DirEntry::BrokenSymlink(pathbuf) => pathbuf.as_path(), + } + } + + pub fn file_type(&self) -> Option { + match self { + DirEntry::Normal(e) => e.file_type(), + DirEntry::BrokenSymlink(pathbuf) => { + pathbuf.symlink_metadata().map(|m| m.file_type()).ok() + } + } + } + + pub fn metadata(&self) -> Option { + match self { + DirEntry::Normal(e) => e.metadata().ok(), + DirEntry::BrokenSymlink(_) => None, + } + } +} + fn spawn_senders( config: &Arc, wants_to_quit: &Arc, @@ -272,17 +303,34 @@ fn spawn_senders( } let entry = match entry_o { - Ok(e) => e, + Ok(e) if e.depth() == 0 => { + // Skip the root directory entry. + return ignore::WalkState::Continue; + } + Ok(e) => DirEntry::Normal(e), + Err(ignore::Error::WithPath { + path, + err: inner_err, + }) => match inner_err.as_ref() { + ignore::Error::Io(io_error) if io_error.kind() == io::ErrorKind::NotFound => { + DirEntry::BrokenSymlink(path.to_owned()) + } + _ => { + tx_thread + .send(WorkerResult::Error(ignore::Error::WithPath { + path, + err: inner_err, + })) + .unwrap(); + return ignore::WalkState::Continue; + } + }, Err(err) => { tx_thread.send(WorkerResult::Error(err)).unwrap(); return ignore::WalkState::Continue; } }; - if entry.depth() == 0 { - return ignore::WalkState::Continue; - } - // Check the name first, since it doesn't require metadata let entry_path = entry.path();