mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
fix: VFS should not walk circular symlinks
As of #6246, rust-analyzer follows symlinks. This can introduce an infinite loop if symlinks point to parent directories. Considering that #6246 was added in 2020 without many bug reports, this is clearly a rare occurrence. However, I am observing rust-analyzer hang on projects that have symlinks of the form: ``` test/a_symlink -> ../../ ``` Ignore symlinks that only point to the parent directories, as this is more robust but still allows typical symlink usage patterns.
This commit is contained in:
parent
46702ffc1a
commit
bd133eecda
1 changed files with 27 additions and 1 deletions
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue