New implementation of broken-symlink handling

This commit is contained in:
sharkdp 2020-02-28 18:19:54 +01:00 committed by David Peter
parent bfc8c42444
commit bbf0f1cc1f
2 changed files with 56 additions and 8 deletions

View file

@ -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<PathBuf> {
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()) {

View file

@ -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<FileType> {
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<Metadata> {
match self {
DirEntry::Normal(e) => e.metadata().ok(),
DirEntry::BrokenSymlink(_) => None,
}
}
}
fn spawn_senders(
config: &Arc<FdOptions>,
wants_to_quit: &Arc<AtomicBool>,
@ -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();