fix: introduce own CWD instead of chdir to avoid potential race conditions (#2008)

This commit is contained in:
三咲雅 · Misaki Masa 2024-12-07 22:07:18 +08:00 committed by GitHub
parent adb1426539
commit 8ec944dbd3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 106 additions and 47 deletions

45
Cargo.lock generated
View file

@ -127,6 +127,12 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
@ -327,9 +333,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.2"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc"
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
dependencies = [
"jobserver",
"libc",
@ -1349,9 +1355,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.74"
version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [
"once_cell",
"wasm-bindgen",
@ -1882,9 +1888,9 @@ dependencies = [
[[package]]
name = "png"
version = "0.17.14"
version = "0.17.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
@ -2766,9 +2772,9 @@ dependencies = [
[[package]]
name = "trash"
version = "5.2.0"
version = "5.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defe1fdd4232e407b312377885a2c5396764972bddad87baf304753374a1bfc8"
checksum = "a8e5ca62c20366b4685e3e41fba17bc7c9bbdcb82e65a89d6fda2ceea5fffd2f"
dependencies = [
"chrono",
"libc",
@ -2776,6 +2782,7 @@ dependencies = [
"objc2",
"objc2-foundation",
"once_cell",
"percent-encoding",
"scopeguard",
"urlencoding",
"windows",
@ -2989,9 +2996,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.97"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [
"cfg-if",
"once_cell",
@ -3000,13 +3007,12 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.97"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@ -3015,9 +3021,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.97"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -3025,9 +3031,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.97"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
@ -3038,9 +3044,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.97"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]]
name = "weezl"
@ -3515,6 +3521,7 @@ name = "yazi-fs"
version = "0.3.3"
dependencies = [
"anyhow",
"arc-swap",
"bitflags 2.6.0",
"dirs",
"futures",

View file

@ -12,7 +12,7 @@ pub(crate) struct Chafa;
impl Chafa {
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let output = Command::new("chafa")
let child = Command::new("chafa")
.args([
"-f",
"symbols",
@ -32,9 +32,9 @@ impl Chafa {
.stdout(Stdio::piped())
.stderr(Stdio::null())
.kill_on_drop(true)
.output()
.await?;
.spawn()?;
let output = child.wait_with_output().await?;
if !output.status.success() {
bail!("chafa failed with status: {}", output.status);
} else if output.stdout.is_empty() {

View file

@ -1,7 +1,7 @@
use std::{collections::HashSet, ffi::OsString, path::PathBuf};
use serde::Serialize;
use yazi_fs::{Xdg, current_cwd, expand_path};
use yazi_fs::{CWD, Xdg, expand_path};
#[derive(Debug, Default, Serialize)]
pub struct Boot {
@ -20,7 +20,7 @@ pub struct Boot {
impl Boot {
fn parse_entries(entries: &[PathBuf]) -> (Vec<PathBuf>, Vec<OsString>) {
if entries.is_empty() {
return (vec![current_cwd().unwrap()], vec![OsString::new()]);
return (vec![CWD.load().to_path_buf()], vec![OsString::new()]);
}
let mut cwds = Vec::with_capacity(entries.len());

View file

@ -7,6 +7,7 @@ use clap::Parser;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
yazi_shared::init();
yazi_fs::init();
if std::env::args_os().nth(1).is_some_and(|s| s == "-V" || s == "--version") {
println!(

View file

@ -12,11 +12,11 @@ impl Git {
}
pub(super) async fn fetch(path: &Path) -> Result<()> {
Self::exec(|c| c.current_dir(path).arg("fetch")).await
Self::exec(|c| c.arg("fetch").current_dir(path)).await
}
pub(super) async fn checkout(path: &Path, rev: &str) -> Result<()> {
Self::exec(|c| c.current_dir(path).args(["checkout", rev])).await
Self::exec(|c| c.args(["checkout", rev]).current_dir(path)).await
}
pub(super) async fn pull(path: &Path) -> Result<()> {
@ -27,8 +27,8 @@ impl Git {
pub(super) async fn hash(path: &Path) -> Result<String> {
let output = Command::new("git")
.current_dir(path)
.args(["rev-parse", "--short", "HEAD"])
.current_dir(path)
.output()
.await
.context("Failed to get current revision")?;

View file

@ -1,16 +1,15 @@
use std::{env, path::MAIN_SEPARATOR};
use std::path::MAIN_SEPARATOR;
use crossterm::{execute, terminal::SetTitle};
use yazi_config::MANAGER;
use yazi_fs::CWD;
use yazi_shared::event::CmdCow;
use crate::{manager::Manager, tasks::Tasks};
impl Manager {
pub fn refresh(&mut self, _: CmdCow, tasks: &Tasks) {
env::set_current_dir(self.cwd()).ok();
env::set_var("PWD", self.cwd());
CWD.set(self.cwd());
if !MANAGER.title_format.is_empty() {
execute!(std::io::stderr(), SetTitle(self.title())).ok();
}

View file

@ -18,6 +18,8 @@ async fn main() -> anyhow::Result<()> {
yazi_shared::init();
yazi_fs::init();
yazi_config::init()?;
yazi_adapter::init()?;

View file

@ -14,12 +14,13 @@ yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
# External dependencies
anyhow = { workspace = true }
arc-swap = "1.7.1"
bitflags = { workspace = true }
dirs = { workspace = true }
futures = { workspace = true }
regex = { workspace = true }
tokio = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
[target."cfg(unix)".dependencies]
libc = { workspace = true }

35
yazi-fs/src/cwd.rs Normal file
View file

@ -0,0 +1,35 @@
use std::{ops::Deref, path::PathBuf, sync::Arc};
use arc_swap::ArcSwap;
use yazi_shared::{RoCell, url::Url};
pub static CWD: RoCell<Cwd> = RoCell::new();
pub struct Cwd {
inner: ArcSwap<Url>,
}
impl Deref for Cwd {
type Target = ArcSwap<Url>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Default for Cwd {
fn default() -> Self {
let p = std::env::var_os("PWD")
.map(PathBuf::from)
.filter(|p| p.is_absolute())
.or_else(|| std::env::current_dir().ok())
.expect("failed to get current working directory");
Self { inner: ArcSwap::new(Arc::new(Url::from(p))) }
}
}
impl Cwd {
pub fn set(&self, url: &Url) {
self.inner.store(Arc::new(url.clone()));
std::env::set_var("PWD", self.inner.load().as_ref());
}
}

View file

@ -1,3 +1,5 @@
#![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)]
yazi_macro::mod_flat!(cha file files filter fns op path sorter sorting stage step xdg);
yazi_macro::mod_flat!(cha cwd file files filter fns op path sorter sorting stage step xdg);
pub fn init() { CWD.init(<_>::default()); }

View file

@ -3,12 +3,7 @@ use std::{borrow::Cow, env, ffi::OsString, future::Future, io, path::{Component,
use tokio::fs;
use yazi_shared::url::{Loc, Url};
pub fn current_cwd() -> Option<PathBuf> {
env::var_os("PWD")
.map(PathBuf::from)
.filter(|p| p.is_absolute())
.or_else(|| env::current_dir().ok())
}
use crate::CWD;
#[inline]
pub fn clean_path(path: impl AsRef<Path>) -> PathBuf { _clean_path(path.as_ref()) }
@ -65,10 +60,8 @@ fn _expand_path(p: &Path) -> PathBuf {
clean_path(dirs::home_dir().unwrap_or_default().join(rest))
} else if p.is_absolute() {
clean_path(p)
} else if let Some(cwd) = current_cwd() {
clean_path(cwd.join(p))
} else {
clean_path(p)
clean_path(CWD.load().join(p))
}
}

View file

@ -16,6 +16,7 @@ impl Clipboard {
use std::os::unix::prelude::OsStringExt;
use tokio::process::Command;
use yazi_fs::CWD;
use yazi_shared::in_ssh_connection;
if in_ssh_connection() {
@ -30,7 +31,13 @@ impl Clipboard {
];
for (bin, args) in all {
let Ok(output) = Command::new(bin).args(args).kill_on_drop(true).output().await else {
let Ok(output) = Command::new(bin)
.args(args)
.current_dir(CWD.load().as_ref())
.kill_on_drop(true)
.output()
.await
else {
continue;
};
if output.status.success() {
@ -58,6 +65,7 @@ impl Clipboard {
use crossterm::execute;
use tokio::{io::AsyncWriteExt, process::Command};
use yazi_fs::CWD;
s.as_ref().clone_into(&mut self.content.lock());
execute!(BufWriter::new(stderr()), osc52::SetClipboard::new(s.as_ref())).ok();
@ -72,6 +80,7 @@ impl Clipboard {
for (bin, args) in all {
let cmd = Command::new(bin)
.args(args)
.current_dir(CWD.load().as_ref())
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null())

View file

@ -2,7 +2,7 @@ use std::process::Stdio;
use anyhow::Result;
use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}};
use yazi_fs::File;
use yazi_fs::{CWD, File};
use yazi_shared::url::Url;
pub struct FdOpt {
@ -37,6 +37,7 @@ fn spawn(program: &str, opt: &FdOpt) -> std::io::Result<Child> {
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
.args(&opt.args)
.arg(&opt.subject)
.current_dir(CWD.load().as_ref())
.kill_on_drop(true)
.stdout(Stdio::piped())
.stderr(Stdio::null())

View file

@ -14,11 +14,11 @@ pub struct RgOpt {
pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
let mut child = Command::new("rg")
.current_dir(&opt.cwd)
.args(["--color=never", "--files-with-matches", "--smart-case"])
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
.args(opt.args)
.arg(opt.subject)
.current_dir(&opt.cwd)
.kill_on_drop(true)
.stdout(Stdio::piped())
.stderr(Stdio::null())

View file

@ -2,6 +2,7 @@ use std::process::Stdio;
use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, Table, UserData, Value};
use tokio::process::{ChildStderr, ChildStdin, ChildStdout};
use yazi_fs::CWD;
use super::{Child, output::Output};
use crate::{Error, process::Status};
@ -18,7 +19,12 @@ impl Command {
pub fn install(lua: &Lua) -> mlua::Result<()> {
let new = lua.create_function(|_, (_, program): (Table, String)| {
let mut inner = tokio::process::Command::new(program);
inner.kill_on_drop(true).stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
inner
.current_dir(CWD.load().as_ref())
.kill_on_drop(true)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
Ok(Self { inner })
})?;

View file

@ -30,4 +30,4 @@ tracing = { workspace = true }
libc = { workspace = true }
[target.'cfg(not(target_os = "android"))'.dependencies]
trash = "5.2.0"
trash = "5.2.1"

View file

@ -2,6 +2,7 @@ use std::{ffi::OsString, process::Stdio};
use anyhow::Result;
use tokio::process::{Child, Command};
use yazi_fs::CWD;
#[derive(Default)]
pub struct ShellOpt {
@ -29,6 +30,7 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
return Ok(unsafe {
Command::new("sh")
.arg("-c")
.current_dir(CWD.load().as_ref())
.stdin(opt.stdio())
.stdout(opt.stdio())
.stderr(opt.stdio())
@ -50,6 +52,7 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
Command::new("cmd.exe")
.raw_arg("/C")
.raw_arg(parser::parse(&opt.cmd, &opt.args))
.current_dir(CWD.load().as_ref())
.stdin(opt.stdio())
.stdout(opt.stdio())
.stderr(opt.stdio())