Detect cycles when scanning a directory

This commit is contained in:
Antonio Scandurra 2022-07-18 13:41:09 +02:00
parent df33556693
commit b48118830f

View file

@ -47,7 +47,7 @@ use std::{
task::Poll, task::Poll,
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap}; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
lazy_static! { lazy_static! {
@ -1491,6 +1491,16 @@ impl LocalSnapshot {
} }
} }
fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet<u64> {
let mut inodes = TreeSet::default();
for ancestor in path.ancestors().skip(1) {
if let Some(entry) = self.entry_for_path(ancestor) {
inodes.insert(entry.inode);
}
}
inodes
}
fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool) -> Arc<IgnoreStack> { fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool) -> Arc<IgnoreStack> {
let mut new_ignores = Vec::new(); let mut new_ignores = Vec::new();
for ancestor in abs_path.ancestors().skip(1) { for ancestor in abs_path.ancestors().skip(1) {
@ -2053,14 +2063,16 @@ impl BackgroundScanner {
async fn scan_dirs(&mut self) -> Result<()> { async fn scan_dirs(&mut self) -> Result<()> {
let root_char_bag; let root_char_bag;
let root_abs_path; let root_abs_path;
let next_entry_id; let root_inode;
let is_dir; let is_dir;
let next_entry_id;
{ {
let snapshot = self.snapshot.lock(); let snapshot = self.snapshot.lock();
root_char_bag = snapshot.root_char_bag; root_char_bag = snapshot.root_char_bag;
root_abs_path = snapshot.abs_path.clone(); root_abs_path = snapshot.abs_path.clone();
root_inode = snapshot.root_entry().map(|e| e.inode);
is_dir = snapshot.root_entry().map_or(false, |e| e.is_dir());
next_entry_id = snapshot.next_entry_id.clone(); next_entry_id = snapshot.next_entry_id.clone();
is_dir = snapshot.root_entry().map_or(false, |e| e.is_dir())
}; };
// Populate ignores above the root. // Populate ignores above the root.
@ -2088,12 +2100,18 @@ impl BackgroundScanner {
if is_dir { if is_dir {
let path: Arc<Path> = Arc::from(Path::new("")); let path: Arc<Path> = Arc::from(Path::new(""));
let mut ancestor_inodes = TreeSet::default();
if let Some(root_inode) = root_inode {
ancestor_inodes.insert(root_inode);
}
let (tx, rx) = channel::unbounded(); let (tx, rx) = channel::unbounded();
self.executor self.executor
.block(tx.send(ScanJob { .block(tx.send(ScanJob {
abs_path: root_abs_path.to_path_buf(), abs_path: root_abs_path.to_path_buf(),
path, path,
ignore_stack, ignore_stack,
ancestor_inodes,
scan_queue: tx.clone(), scan_queue: tx.clone(),
})) }))
.unwrap(); .unwrap();
@ -2195,24 +2213,30 @@ impl BackgroundScanner {
root_char_bag, root_char_bag,
); );
if child_metadata.is_dir { if child_entry.is_dir() {
let is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, true); let is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, true);
child_entry.is_ignored = is_ignored; child_entry.is_ignored = is_ignored;
new_entries.push(child_entry);
new_jobs.push(ScanJob { if !job.ancestor_inodes.contains(&child_entry.inode) {
abs_path: child_abs_path, let mut ancestor_inodes = job.ancestor_inodes.clone();
path: child_path, ancestor_inodes.insert(child_entry.inode);
ignore_stack: if is_ignored { new_jobs.push(ScanJob {
IgnoreStack::all() abs_path: child_abs_path,
} else { path: child_path,
ignore_stack.clone() ignore_stack: if is_ignored {
}, IgnoreStack::all()
scan_queue: job.scan_queue.clone(), } else {
}); ignore_stack.clone()
},
ancestor_inodes,
scan_queue: job.scan_queue.clone(),
});
}
} else { } else {
child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false); child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false);
new_entries.push(child_entry); }
};
new_entries.push(child_entry);
} }
self.snapshot self.snapshot
@ -2292,11 +2316,13 @@ impl BackgroundScanner {
fs_entry.is_ignored = ignore_stack.is_all(); fs_entry.is_ignored = ignore_stack.is_all();
snapshot.insert_entry(fs_entry, self.fs.as_ref()); snapshot.insert_entry(fs_entry, self.fs.as_ref());
if metadata.is_dir { if metadata.is_dir {
let ancestor_inodes = snapshot.ancestor_inodes_for_path(&path);
self.executor self.executor
.block(scan_queue_tx.send(ScanJob { .block(scan_queue_tx.send(ScanJob {
abs_path, abs_path,
path, path,
ignore_stack, ignore_stack,
ancestor_inodes,
scan_queue: scan_queue_tx.clone(), scan_queue: scan_queue_tx.clone(),
})) }))
.unwrap(); .unwrap();
@ -2458,6 +2484,7 @@ struct ScanJob {
path: Arc<Path>, path: Arc<Path>,
ignore_stack: Arc<IgnoreStack>, ignore_stack: Arc<IgnoreStack>,
scan_queue: Sender<ScanJob>, scan_queue: Sender<ScanJob>,
ancestor_inodes: TreeSet<u64>,
} }
struct UpdateIgnoreStatusJob { struct UpdateIgnoreStatusJob {