Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/vfs-notify/src/lib.rs')
-rw-r--r--crates/vfs-notify/src/lib.rs28
1 files changed, 27 insertions, 1 deletions
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index 4cfdec2b5c..45bb777d4d 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -9,7 +9,10 @@
#![warn(rust_2018_idioms, unused_lifetimes)]
-use std::fs;
+use std::{
+ fs,
+ path::{Component, Path},
+};
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
@@ -206,6 +209,11 @@ impl NotifyActor {
return true;
}
let path = entry.path();
+
+ if path_is_parent_symlink(path) {
+ return false;
+ }
+
root == path
|| dirs.exclude.iter().chain(&dirs.include).all(|it| it != path)
});
@@ -258,3 +266,21 @@ fn read(path: &AbsPath) -> Option<Vec<u8>> {
fn log_notify_error<T>(res: notify::Result<T>) -> Option<T> {
res.map_err(|err| tracing::warn!("notify error: {}", err)).ok()
}
+
+/// Is `path` a symlink to a parent directory?
+///
+/// Including this path is guaranteed to cause an infinite loop. This
+/// heuristic is not sufficient to catch all symlink cycles (it's
+/// possible to construct cycle using two or more symlinks), but it
+/// catches common cases.
+fn path_is_parent_symlink(path: &Path) -> bool {
+ let Ok(destination) = std::fs::read_link(path) else {
+ return false;
+ };
+
+ // If the symlink is of the form "../..", it's a parent symlink.
+ let is_relative_parent =
+ destination.components().all(|c| matches!(c, Component::CurDir | Component::ParentDir));
+
+ is_relative_parent || path.starts_with(destination)
+}