Skip to content
Permalink
Browse files

Prevent infinite loop with -aaR

This happened because exa would recurse into `.` over and over again. There was nothing distinguishing the pseudo-entry for `.` that was being added by `--a` from a `.` passed in on the command-line, so it was looping forever.

It gets fixed by having the File value keep track of whether it’s an --all --all entry, and not recursing into directories with this field set.

Fixes #515
  • Loading branch information...
ogham committed Jul 13, 2019
1 parent 7dada93 commit e936d7e09f100044251f1f059175f075182a362e
Showing with 34 additions and 5 deletions.
  1. +1 −1 src/exa.rs
  2. +2 −2 src/fs/dir.rs
  3. +31 −2 src/fs/file.rs
@@ -191,7 +191,7 @@ impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {

let mut child_dirs = Vec::new();
for child_dir in children.iter().filter(|f| f.is_directory()) {
for child_dir in children.iter().filter(|f| f.is_directory() && !f.is_all_all) {
match child_dir.to_dir() {
Ok(d) => child_dirs.push(d),
Err(e) => writeln!(stderr(), "{}: {}", child_dir.path.display(), e)?,
@@ -140,12 +140,12 @@ impl<'dir, 'ig> Iterator for Files<'dir, 'ig> {
fn next(&mut self) -> Option<Self::Item> {
if let Dots::DotNext = self.dots {
self.dots = Dots::DotDotNext;
Some(File::new(self.dir.path.to_path_buf(), self.dir, String::from("."))
Some(File::new_aa_current(self.dir)
.map_err(|e| (Path::new(".").to_path_buf(), e)))
}
else if let Dots::DotDotNext = self.dots {
self.dots = Dots::FilesNext;
Some(File::new(self.parent(), self.dir, String::from(".."))
Some(File::new_aa_parent(self.parent(), self.dir)
.map_err(|e| (self.parent(), e)))
}
else {
@@ -55,6 +55,13 @@ pub struct File<'dir> {
/// contain a reference to it, which is used in certain operations (such
/// as looking up compiled files).
pub parent_dir: Option<&'dir Dir>,

/// Whether this is one of the two `--all all` directories, `.` and `..`.
///
/// Unlike all other entries, these are not returned as part of the
/// directory's children, and are in fact added specifically by exa; this
/// means that they should be skipped when recursing.
pub is_all_all: bool,
}

impl<'dir> File<'dir> {
@@ -68,8 +75,30 @@ impl<'dir> File<'dir> {

debug!("Statting file {:?}", &path);
let metadata = fs::symlink_metadata(&path)?;
let is_all_all = false;

Ok(File { path, parent_dir, metadata, ext, name, is_all_all })
}

pub fn new_aa_current(parent_dir: &'dir Dir) -> IOResult<File<'dir>> {
let path = parent_dir.path.to_path_buf();
let ext = File::ext(&path);

debug!("Statting file {:?}", &path);
let metadata = fs::symlink_metadata(&path)?;
let is_all_all = true;

Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: ".".to_string(), is_all_all })
}

pub fn new_aa_parent(path: PathBuf, parent_dir: &'dir Dir) -> IOResult<File<'dir>> {
let ext = File::ext(&path);

debug!("Statting file {:?}", &path);
let metadata = fs::symlink_metadata(&path)?;
let is_all_all = true;

Ok(File { path, parent_dir, metadata, ext, name })
Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: "..".to_string(), is_all_all })
}

/// A file’s name is derived from its string. This needs to handle directories
@@ -219,7 +248,7 @@ impl<'dir> File<'dir> {
Ok(metadata) => {
let ext = File::ext(&path);
let name = File::filename(&path);
FileTarget::Ok(Box::new(File { parent_dir: None, path, ext, metadata, name }))
FileTarget::Ok(Box::new(File { parent_dir: None, path, ext, metadata, name, is_all_all: false }))
}
Err(e) => {
error!("Error following link {:?}: {:#?}", &path, e);

0 comments on commit e936d7e

Please sign in to comment.
You can’t perform that action at this time.