From f2b26479754ffa4e899e5aa670e6c107b8384aca Mon Sep 17 00:00:00 2001 From: oxalica Date: Sat, 29 Apr 2023 11:12:41 +0800 Subject: [PATCH] Fix stuck on non-regular files and report loading errors --- Cargo.lock | 14 +++++------ crates/nil/Cargo.toml | 4 +-- crates/nil/src/server.rs | 53 +++++++++++++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23b4a0c..cb9d4a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,15 +388,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "linux-raw-sys" -version = "0.3.1" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" [[package]] name = "lock_api" @@ -476,10 +476,10 @@ dependencies = [ "atty", "codespan-reporting", "ide", - "libc", "log", "lsp-types", "nix-interop", + "rustix", "serde_json", "slab", "syntax", @@ -710,9 +710,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.11" +version = "0.37.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +checksum = "bc809f704c03a812ac71f22456c857be34185cac691a4316f27ab0f633bb9009" dependencies = [ "bitflags", "errno", diff --git a/crates/nil/Cargo.toml b/crates/nil/Cargo.toml index bed693a..d158d5a 100644 --- a/crates/nil/Cargo.toml +++ b/crates/nil/Cargo.toml @@ -34,5 +34,5 @@ features = [ "tracing-log", ] -[target.'cfg(target_os = "linux")'.dependencies] -libc = "0.2.137" +[target.'cfg(unix)'.dependencies] +rustix = { version = "0.37.17", default-features = false, features = ["fs"] } diff --git a/crates/nil/src/server.rs b/crates/nil/src/server.rs index 1ada182..e420667 100644 --- a/crates/nil/src/server.rs +++ b/crates/nil/src/server.rs @@ -26,6 +26,7 @@ use std::borrow::BorrowMut; use std::cell::Cell; use std::collections::HashMap; use std::future::{ready, Future}; +use std::io::{ErrorKind, Read}; use std::ops::ControlFlow; use std::panic::UnwindSafe; use std::path::Path; @@ -352,23 +353,57 @@ impl Server { tracing::debug!("Watched files changed: {params:?}"); let mut flake_files_changed = true; - for FileEvent { uri, typ } in ¶ms.changes { + for &FileEvent { ref uri, mut typ } in ¶ms.changes { // Don't reload files maintained by the client. if self.opened_files.contains_key(uri) { continue; } let Ok(path) = uri.to_file_path() else { continue }; - match *typ { - FileChangeType::CREATED | FileChangeType::CHANGED => { - if let Ok(text) = std::fs::read_to_string(&path) { - self.set_vfs_file_content(uri, text); + + if matches!(typ, FileChangeType::CREATED | FileChangeType::CHANGED) { + match (|| -> std::io::Result<_> { + #[cfg(unix)] + use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags, OpenOptionsExt}; + + // Rule out non-regular files which may block `open()` infinitely + // (eg. FIFO). We open it with `O_NONBLOCK` and check it before reading. + let mut options = std::fs::File::options(); + options.read(true); + #[cfg(unix)] + options.custom_flags(OFlags::NONBLOCK.bits() as _); + + let mut file = options.open(&path)?; + let ft = file.metadata()?.file_type(); + if !ft.is_file() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("non-regular file type: {ft:?}"), + )); } + + // Remove the O_NONBLOCK flag for blocking read. + #[cfg(unix)] + { + let flags = fcntl_getfl(&file)? - OFlags::NONBLOCK; + fcntl_setfl(&file, flags)?; + } + + let mut buf = String::new(); + file.read_to_string(&mut buf)?; + Ok(buf) + })() { + Ok(text) => self.set_vfs_file_content(uri, text), + Err(err) if matches!(err.kind(), ErrorKind::NotFound) => { + // File gets removed at the time calling `open()`. + typ = FileChangeType::DELETED; + } + Err(err) => tracing::error!("Ignore file {path:?}: {err}"), } - FileChangeType::DELETED => { - let _: Result<_> = self.vfs.write().unwrap().remove_uri(uri); - } - _ => continue, } + if typ == FileChangeType::DELETED { + let _: Result<_> = self.vfs.write().unwrap().remove_uri(uri); + } + if let Ok(relative) = path.strip_prefix(&self.config.root_path) { if relative == Path::new(FLAKE_FILE) || relative == Path::new(FLAKE_LOCK_FILE) { flake_files_changed = true;