mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
fix: paste --force doesn't work on existing read-only files with the same name (#3894)
This commit is contained in:
parent
463150848d
commit
ae4c138f49
10 changed files with 73 additions and 20 deletions
|
|
@ -54,6 +54,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Chafa v1.18.1 causes random ghost keypresses when previewing images ([#3678])
|
- Chafa v1.18.1 causes random ghost keypresses when previewing images ([#3678])
|
||||||
|
- `paste --force` doesn't work on existing read-only files with the same name ([#3894])
|
||||||
- Be a little defensive while parsing the output of `7zz -ba` ([#3744])
|
- Be a little defensive while parsing the output of `7zz -ba` ([#3744])
|
||||||
- Make `ya pkg` ignore default remote name in user Git config ([#3648])
|
- Make `ya pkg` ignore default remote name in user Git config ([#3648])
|
||||||
- Archive extraction fails for target paths with non-ASCII characters on Windows ([#3607])
|
- Archive extraction fails for target paths with non-ASCII characters on Windows ([#3607])
|
||||||
|
|
@ -1701,3 +1702,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
||||||
[#3854]: https://github.com/sxyazi/yazi/pull/3854
|
[#3854]: https://github.com/sxyazi/yazi/pull/3854
|
||||||
[#3862]: https://github.com/sxyazi/yazi/pull/3862
|
[#3862]: https://github.com/sxyazi/yazi/pull/3862
|
||||||
[#3891]: https://github.com/sxyazi/yazi/pull/3891
|
[#3891]: https://github.com/sxyazi/yazi/pull/3891
|
||||||
|
[#3894]: https://github.com/sxyazi/yazi/pull/3894
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ impl Actor for Shell {
|
||||||
|
|
||||||
TasksProxy::open_shell_compat(ProcessOpt {
|
TasksProxy::open_shell_compat(ProcessOpt {
|
||||||
cwd,
|
cwd,
|
||||||
cmd: form.run.to_string().into(),
|
cmd: form.run.to_string().into(),
|
||||||
args: selected,
|
args: selected,
|
||||||
block: form.block,
|
block: form.block,
|
||||||
orphan: form.orphan,
|
orphan: form.orphan,
|
||||||
spread: true,
|
spread: true,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ dirs = { workspace = true }
|
||||||
either = { workspace = true }
|
either = { workspace = true }
|
||||||
foldhash = { workspace = true }
|
foldhash = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
|
libc = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
percent-encoding = { workspace = true }
|
percent-encoding = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
|
@ -38,7 +39,6 @@ tracing = { workspace = true }
|
||||||
typed-path = { workspace = true }
|
typed-path = { workspace = true }
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
libc = { workspace = true }
|
|
||||||
uzers = { workspace = true }
|
uzers = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
|
|
||||||
|
|
@ -81,17 +81,19 @@ impl Cha {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let mode = {
|
let mut mode = if m.is_file() {
|
||||||
if m.is_file() {
|
ChaMode::T_FILE
|
||||||
ChaMode::T_FILE
|
} else if m.is_dir() {
|
||||||
} else if m.is_dir() {
|
ChaMode::T_DIR
|
||||||
ChaMode::T_DIR
|
} else if m.is_symlink() {
|
||||||
} else if m.is_symlink() {
|
ChaMode::T_LINK
|
||||||
ChaMode::T_LINK
|
} else {
|
||||||
} else {
|
ChaMode::empty()
|
||||||
ChaMode::empty()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
#[cfg(windows)]
|
||||||
|
if !m.permissions().readonly() {
|
||||||
|
mode |= ChaMode::U_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
kind: ChaKind::empty(),
|
kind: ChaKind::empty(),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{io, path::Path, sync::Arc};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use yazi_shared::{path::{AsPath, PathBufDyn}, scheme::SchemeKind, strand::AsStrand, url::{Url, UrlBuf, UrlCow}};
|
use yazi_shared::{path::{AsPath, PathBufDyn}, scheme::SchemeKind, strand::AsStrand, url::{Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use crate::{cha::Cha, provider::{Attrs, Capabilities, Provider}};
|
use crate::{cha::{Cha, ChaMode}, provider::{Attrs, Capabilities, Provider}};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Local<'a> {
|
pub struct Local<'a> {
|
||||||
|
|
@ -125,6 +125,26 @@ impl<'a> Provider for Local<'a> {
|
||||||
tokio::fs::rename(self.path, to).await
|
tokio::fs::rename(self.path, to).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_mode(&self, mode: ChaMode) -> io::Result<()> {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
return tokio::fs::set_permissions(self.path, mode.into()).await;
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
|
||||||
|
let path: Vec<u16> = self.path.as_os_str().encode_wide().chain(Some(0)).collect();
|
||||||
|
let perm = if mode.contains(ChaMode::U_WRITE) { libc::S_IWRITE } else { libc::S_IREAD };
|
||||||
|
|
||||||
|
return tokio::task::spawn_blocking(move || {
|
||||||
|
let result = unsafe { libc::wchmod(path.as_ptr(), perm) };
|
||||||
|
if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn symlink<S, F>(&self, original: S, _is_dir: F) -> io::Result<()>
|
async fn symlink<S, F>(&self, original: S, _is_dir: F) -> io::Result<()>
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use tokio::{io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt}, sync::mpsc};
|
||||||
use yazi_macro::ok_or_not_found;
|
use yazi_macro::ok_or_not_found;
|
||||||
use yazi_shared::{path::{AsPath, PathBufDyn}, strand::{AsStrand, StrandCow}, url::{AsUrl, Url, UrlBuf}};
|
use yazi_shared::{path::{AsPath, PathBufDyn}, strand::{AsStrand, StrandCow}, url::{AsUrl, Url, UrlBuf}};
|
||||||
|
|
||||||
use crate::{cha::{Cha, ChaType}, provider::{Attrs, Capabilities}};
|
use crate::{cha::{Cha, ChaMode, ChaType}, provider::{Attrs, Capabilities}};
|
||||||
|
|
||||||
pub trait Provider: Sized {
|
pub trait Provider: Sized {
|
||||||
type File: AsyncRead + AsyncSeek + AsyncWrite + Unpin;
|
type File: AsyncRead + AsyncSeek + AsyncWrite + Unpin;
|
||||||
|
|
@ -163,6 +163,8 @@ pub trait Provider: Sized {
|
||||||
where
|
where
|
||||||
P: AsPath;
|
P: AsPath;
|
||||||
|
|
||||||
|
fn set_mode(&self, mode: ChaMode) -> impl Future<Output = io::Result<()>>;
|
||||||
|
|
||||||
fn symlink<S, F>(&self, original: S, _is_dir: F) -> impl Future<Output = io::Result<()>>
|
fn symlink<S, F>(&self, original: S, _is_dir: F) -> impl Future<Output = io::Result<()>>
|
||||||
where
|
where
|
||||||
S: AsStrand,
|
S: AsStrand,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{hash::{BuildHasher, Hash, Hasher}, io};
|
use std::{hash::{BuildHasher, Hash, Hasher}, io};
|
||||||
|
|
||||||
|
use yazi_fs::cha::ChaMode;
|
||||||
use yazi_macro::ok_or_not_found;
|
use yazi_macro::ok_or_not_found;
|
||||||
use yazi_shared::{timestamp_us, url::{AsUrl, Url, UrlBuf}};
|
use yazi_shared::{timestamp_us, url::{AsUrl, Url, UrlBuf}};
|
||||||
use yazi_vfs::{provider, unique_file};
|
use yazi_vfs::{provider, unique_file};
|
||||||
|
|
@ -31,8 +32,12 @@ impl Transaction {
|
||||||
U: AsUrl,
|
U: AsUrl,
|
||||||
{
|
{
|
||||||
let url = url.as_url();
|
let url = url.as_url();
|
||||||
if ok_or_not_found!(provider::symlink_metadata(url).await, return Ok(())).is_link() {
|
|
||||||
|
let cha = ok_or_not_found!(provider::symlink_metadata(url).await, return Ok(()));
|
||||||
|
if cha.is_link() {
|
||||||
provider::rename(Self::tmp(url).await?, url).await?;
|
provider::rename(Self::tmp(url).await?, url).await?;
|
||||||
|
} else if !cha.contains(ChaMode::U_WRITE) {
|
||||||
|
provider::set_mode(url, cha.mode | ChaMode::U_WRITE).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use yazi_fs::{cha::Cha, provider::{Attrs, Capabilities, Provider, local::Local}};
|
use yazi_fs::{cha::{Cha, ChaMode}, provider::{Attrs, Capabilities, Provider, local::Local}};
|
||||||
use yazi_shared::{path::PathBufDyn, strand::AsStrand, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
use yazi_shared::{path::PathBufDyn, strand::AsStrand, url::{AsUrl, Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use super::{Providers, ReadDir, RwFile};
|
use super::{Providers, ReadDir, RwFile};
|
||||||
|
|
@ -215,6 +215,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_mode<U>(url: U, mode: ChaMode) -> io::Result<()>
|
||||||
|
where
|
||||||
|
U: AsUrl,
|
||||||
|
{
|
||||||
|
Providers::new(url.as_url()).await?.set_mode(mode).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn symlink<U, S, F>(link: U, original: S, is_dir: F) -> io::Result<()>
|
pub async fn symlink<U, S, F>(link: U, original: S, is_dir: F) -> io::Result<()>
|
||||||
where
|
where
|
||||||
U: AsUrl,
|
U: AsUrl,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use yazi_fs::{cha::Cha, provider::{Attrs, Capabilities, Provider}};
|
use yazi_fs::{cha::{Cha, ChaMode}, provider::{Attrs, Capabilities, Provider}};
|
||||||
use yazi_shared::{path::{AsPath, PathBufDyn}, strand::AsStrand, url::{Url, UrlBuf, UrlCow}};
|
use yazi_shared::{path::{AsPath, PathBufDyn}, strand::AsStrand, url::{Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -175,6 +175,13 @@ impl<'a> Provider for Providers<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_mode(&self, mode: ChaMode) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Local(p) => p.set_mode(mode).await,
|
||||||
|
Self::Sftp(p) => p.set_mode(mode).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn symlink<S, F>(&self, original: S, is_dir: F) -> io::Result<()>
|
async fn symlink<S, F>(&self, original: S, is_dir: F) -> io::Result<()>
|
||||||
where
|
where
|
||||||
S: AsStrand,
|
S: AsStrand,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{io, sync::Arc};
|
||||||
|
|
||||||
use tokio::{io::{AsyncWriteExt, BufReader, BufWriter}, sync::mpsc::Receiver};
|
use tokio::{io::{AsyncWriteExt, BufReader, BufWriter}, sync::mpsc::Receiver};
|
||||||
use yazi_config::vfs::{ServiceSftp, Vfs};
|
use yazi_config::vfs::{ServiceSftp, Vfs};
|
||||||
use yazi_fs::provider::{Capabilities, DirReader, FileHolder, Provider};
|
use yazi_fs::{cha::ChaMode, provider::{Capabilities, DirReader, FileHolder, Provider}};
|
||||||
use yazi_sftp::fs::{Attrs, Flags};
|
use yazi_sftp::fs::{Attrs, Flags};
|
||||||
use yazi_shared::{loc::LocBuf, path::{AsPath, PathBufDyn}, pool::InternStr, scheme::SchemeKind, strand::AsStrand, url::{Url, UrlBuf, UrlCow, UrlLike}};
|
use yazi_shared::{loc::LocBuf, path::{AsPath, PathBufDyn}, pool::InternStr, scheme::SchemeKind, strand::AsStrand, url::{Url, UrlBuf, UrlCow, UrlLike}};
|
||||||
|
|
||||||
|
|
@ -193,6 +193,14 @@ impl<'a> Provider for Sftp<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_mode(&self, mode: ChaMode) -> io::Result<()> {
|
||||||
|
let attrs = super::Attrs(yazi_fs::provider::Attrs { mode: Some(mode), ..Default::default() })
|
||||||
|
.try_into()
|
||||||
|
.map_err(|()| io::Error::new(io::ErrorKind::InvalidInput, "Cannot convert mode"))?;
|
||||||
|
|
||||||
|
Ok(self.op().await?.setstat(self.path, attrs).await?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn symlink<S, F>(&self, original: S, _is_dir: F) -> io::Result<()>
|
async fn symlink<S, F>(&self, original: S, _is_dir: F) -> io::Result<()>
|
||||||
where
|
where
|
||||||
S: AsStrand,
|
S: AsStrand,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue