From 2c9ab2d930c2740cb5304f256caa7c71b8f674f4 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 23 Jun 2024 22:34:48 +0800 Subject: [PATCH] Try another way --- Cargo.lock | 7 -- cspell.json | 2 +- yazi-core/src/manager/commands/bulk_rename.rs | 11 +-- yazi-core/src/manager/commands/rename.rs | 33 +++------ yazi-shared/Cargo.toml | 5 +- yazi-shared/src/fs/fns.rs | 70 ++++++++++--------- 6 files changed, 52 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05245f1..5706c970 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2474,12 +2474,6 @@ dependencies = [ "winsafe", ] -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - [[package]] name = "winapi" version = "0.3.9" @@ -2970,7 +2964,6 @@ dependencies = [ "serde", "shell-words", "tokio", - "widestring", "windows-sys 0.52.0", ] diff --git a/cspell.json b/cspell.json index a3d8a050..4eb16cfb 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","widestring"],"language":"en"} \ No newline at end of file +{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE"],"version":"0.2"} \ No newline at end of file diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index 7f98c7ad..e79c0e26 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -84,16 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - let overwrite_safe; - #[cfg(windows)] - { - overwrite_safe = yazi_shared::fs::rename_without_overwriting(&old, &new).await.is_ok(); - } - #[cfg(unix)] - { - overwrite_safe = yazi_shared::fs::are_paths_equal(&old, &new).await; - } - if maybe_exists(&new).await && !overwrite_safe { + if maybe_exists(&new).await { failed.push((o, n, anyhow!("Destination already exists"))); } else { #[cfg(unix)] diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 0241af90..6b13878d 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -62,46 +62,31 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - let overwrite_safe; - #[cfg(windows)] - { - overwrite_safe = yazi_shared::fs::rename_without_overwriting(&hovered, &new).await.is_ok(); - } - #[cfg(unix)] - { - overwrite_safe = yazi_shared::fs::are_paths_equal(&hovered, &new).await; - } - if opt.force || !maybe_exists(&new).await || overwrite_safe { - Self::rename_do(tab, hovered, Url::from(new), overwrite_safe).await.ok(); + if opt.force || !maybe_exists(&new).await { + Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } let mut result = InputProxy::show(InputCfg::overwrite()); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { - Self::rename_do(tab, hovered, Url::from(new), false).await.ok(); + Self::rename_do(tab, hovered, Url::from(new)).await.ok(); } }; }); } - async fn rename_do(tab: usize, old: Url, new: Url, overwrite_safe: bool) -> Result<()> { + async fn rename_do(tab: usize, old: Url, new: Url) -> Result<()> { let Some(p_old) = old.parent_url() else { return Ok(()) }; let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); - if overwrite_safe { - if !cfg!(windows) { - fs::rename(&old, &new).await?; - } - } else { - let overwritten = symlink_realpath(&new).await; - fs::rename(&old, &new).await?; + let overwritten = symlink_realpath(&new).await; + fs::rename(&old, &new).await?; - if let Ok(p) = overwritten { - ok_or_not_found(fs::rename(&p, &new).await)?; - FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); - } + if let Ok(p) = overwritten { + ok_or_not_found(fs::rename(&p, &new).await)?; + FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); } Pubsub::pub_from_rename(tab, &old, &new); diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index ba31f15c..bb9afa54 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -22,8 +22,9 @@ regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } -widestring = "1.1.0" -windows-sys = { version = "0.52.0", features = ["Win32_Foundation"] } [target."cfg(unix)".dependencies] libc = "0.2.155" + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.52.0", features = [ "Win32_Storage_FileSystem" ] } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 7d17466e..342b791f 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -23,42 +23,48 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } } -#[inline] #[cfg(unix)] -pub async fn are_paths_equal(old: impl AsRef, new: impl AsRef) -> bool { - if let (Ok(canonical_old), Ok(canonical_new)) = - (fs::canonicalize(&old).await, fs::canonicalize(&new).await) - { - if let (Ok(old), Ok(new)) = - (fs::metadata(&canonical_old).await, fs::metadata(&canonical_new).await) - { - use std::os::unix::fs::MetadataExt; - old.ino() == new.ino() && old.dev() == new.dev() - } else { - false - } - } else { - false - } +pub async fn paths_to_same_file(a: &Path, b: &Path) -> io::Result { + use std::os::unix::fs::MetadataExt; + + let (a, b) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?); + Ok(a.ino() == b.ino() && a.dev() == b.dev()) } -#[inline] #[cfg(windows)] -pub async fn rename_without_overwriting( - old: impl AsRef, - new: impl AsRef, -) -> io::Result<()> { - use widestring::U16CString; - use windows_sys::Win32::Storage::FileSystem::MoveFileExW; - let old = U16CString::from_os_str(old.as_ref().as_os_str()) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; - let new = U16CString::from_os_str(new.as_ref().as_os_str()) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?; - tokio::task::spawn_blocking(move || { - let result = unsafe { MoveFileExW(old.as_ptr(), new.as_ptr(), 0) }; - if result != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } - }) - .await? +pub async fn paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { + use std::{ffi::OsString, os::windows::{ffi::OsStringExt, io::AsRawHandle}, path::{Path, PathBuf}}; + + use windows_sys::Win32::{Foundation::{HANDLE, MAX_PATH}, Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, VOLUME_NAME_DOS}}; + + async fn final_name(p: &Path) -> std::io::Result { + let file = tokio::fs::OpenOptions::new() + .access_mode(0) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT) + .open(p) + .await?; + + let mut buf = [0u16; MAX_PATH as usize]; + tokio::task::spawn_blocking(move || { + let len = unsafe { + GetFinalPathNameByHandleW( + file.as_raw_handle() as HANDLE, + buf.as_mut_ptr(), + buf.len() as u32, + VOLUME_NAME_DOS, + ) + }; + + if len == 0 { + Err(std::io::Error::last_os_error()) + } else { + Ok(PathBuf::from(OsString::from_wide(&buf[0..len as usize]))) + } + }) + .await? + } + + Ok(final_name(a).await? == final_name(b).await?) } pub async fn symlink_realpath(path: &Path) -> Result {