walk: catch ignore NotFound error in the case of a broken symlink
@@ -13,8 +13,10 @@ use crate::internal::{opts::FdOptions, osstr_to_bytes, MAX_BUFFER_LENGTH};
use crate::output;
use std::borrow::Cow;
+use std::convert::TryFrom;
use std::error::Error;
use std::ffi::OsStr;
+use std::fs;
use std::io;
use std::path::PathBuf;
use std::process;
@@ -44,6 +46,40 @@ pub enum WorkerResult {
Error(ignore::Error),
}
+/// Converts a WorkerResult to a PathBuf. This is required as in some cases,
+/// the underlying `ignore` libary will return an error when we actually
+/// still want to show the path.
+///
+/// Currently, the cases supported are:
+/// - a broken symlink should still be found and the path returned
+impl TryFrom<WorkerResult> for PathBuf {
+ type Error = ignore::Error;
+
+ fn try_from(worker_result: WorkerResult) -> Result<PathBuf, Self::Error> {
+ match worker_result {
+ WorkerResult::Entry(value) => return Ok(value),
+ WorkerResult::Error(err) => {
+ if let ignore::Error::WithPath {
+ path,
+ err: path_err,
+ } = &err
+ {
+ if let ignore::Error::Io(io_err) = &**path_err {
+ if io_err.kind() == io::ErrorKind::NotFound {
+ if let Ok(metadata) = fs::symlink_metadata(&path) {
+ if metadata.file_type().is_symlink() {
+ return Ok(path.to_path_buf());
+ }
+ }
+ }
+ }
+ }
+ return Err(err);
+ }
+ }
+ }
+}
+
/// Recursively scan the given search path for files / pathnames matching the pattern.
///
/// If the `--exec` argument was supplied, this will create a thread pool for executing
@@ -201,8 +237,8 @@ fn spawn_receiver(
let mut stdout = stdout.lock();
for worker_result in rx {
- match worker_result {
- WorkerResult::Entry(value) => {
+ match PathBuf::try_from(worker_result) {
+ Ok(value) => {
match mode {
ReceiverMode::Buffering => {
buffer.push(value);
@@ -231,7 +267,7 @@ fn spawn_receiver(
}
}
}
- WorkerResult::Error(err) => {
+ Err(err) => {
if show_filesystem_errors {
print_error!("{}", err);
}
