From a0c6842626fec1d6e0d29f1f733f27d91277579e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Tue, 11 Mar 2025 12:57:47 +0800 Subject: [PATCH] fix: respect hidden status of directory junctions and symlinks themselves on Windows (#2471) --- Cargo.lock | 20 +++++----- Cargo.toml | 4 +- yazi-config/src/theme/is.rs | 2 +- yazi-core/src/mgr/watcher.rs | 2 +- yazi-core/src/tab/commands/search.rs | 2 +- yazi-core/src/tab/folder.rs | 2 +- yazi-core/src/tab/preview.rs | 2 +- yazi-fs/src/{ => cha}/cha.rs | 58 +++++++--------------------- yazi-fs/src/cha/kind.rs | 44 +++++++++++++++++++++ yazi-fs/src/cha/mod.rs | 1 + yazi-fs/src/file.rs | 2 +- yazi-fs/src/files.rs | 2 +- yazi-fs/src/fns.rs | 2 +- yazi-fs/src/lib.rs | 4 +- yazi-fs/src/mounts/partitions.rs | 2 +- yazi-fs/src/op.rs | 3 +- yazi-plugin/src/bindings/cha.rs | 10 ++--- yazi-plugin/src/utils/preview.rs | 2 +- yazi-plugin/src/utils/spot.rs | 2 +- yazi-scheduler/src/file/file.rs | 2 +- yazi-scheduler/src/file/op.rs | 2 +- 21 files changed, 93 insertions(+), 77 deletions(-) rename yazi-fs/src/{ => cha}/cha.rs (79%) create mode 100644 yazi-fs/src/cha/kind.rs create mode 100644 yazi-fs/src/cha/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 61caa028..6f32328e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -418,9 +418,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -1231,9 +1231,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -1767,9 +1767,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" [[package]] name = "onig" diff --git a/Cargo.toml b/Cargo.toml index 357b80be..5205919e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,14 @@ ansi-to-tui = "7.0.0" anyhow = "1.0.97" base64 = "0.22.1" bitflags = "2.9.0" -clap = { version = "4.5.31", features = [ "derive" ] } +clap = { version = "4.5.32", features = [ "derive" ] } core-foundation-sys = "0.8.7" crossterm = { version = "0.28.1", features = [ "event-stream" ] } dirs = "6.0.0" foldhash = "0.1.4" futures = "0.3.31" globset = "0.4.16" -indexmap = { version = "2.7.1", features = [ "serde" ] } +indexmap = { version = "2.8.0", features = [ "serde" ] } libc = "0.2.170" lru = "0.13.0" md-5 = "0.10.6" diff --git a/yazi-config/src/theme/is.rs b/yazi-config/src/theme/is.rs index 61f18b9b..9af86250 100644 --- a/yazi-config/src/theme/is.rs +++ b/yazi-config/src/theme/is.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use yazi_fs::Cha; +use yazi_fs::cha::Cha; #[derive(Default, Deserialize)] #[serde(rename_all = "kebab-case")] diff --git a/yazi-core/src/mgr/watcher.rs b/yazi-core/src/mgr/watcher.rs index 6afc46d9..757ed98f 100644 --- a/yazi-core/src/mgr/watcher.rs +++ b/yazi-core/src/mgr/watcher.rs @@ -6,7 +6,7 @@ use parking_lot::RwLock; use tokio::{fs, pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tracing::error; -use yazi_fs::{Cha, File, Files, FilesOp, realname_unchecked}; +use yazi_fs::{File, Files, FilesOp, cha::Cha, realname_unchecked}; use yazi_proxy::WATCHER; use yazi_shared::{RoCell, url::Url}; diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index 907227c2..09ac84fd 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -5,7 +5,7 @@ use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tracing::error; use yazi_config::popup::InputCfg; -use yazi_fs::{Cha, FilesOp}; +use yazi_fs::{FilesOp, cha::Cha}; use yazi_plugin::external; use yazi_proxy::{AppProxy, InputProxy, MgrProxy, TabProxy, options::{SearchOpt, SearchOptVia}}; diff --git a/yazi-core/src/tab/folder.rs b/yazi-core/src/tab/folder.rs index b7aa287a..459e5b8c 100644 --- a/yazi-core/src/tab/folder.rs +++ b/yazi-core/src/tab/folder.rs @@ -2,7 +2,7 @@ use std::mem; use yazi_config::{LAYOUT, MGR}; use yazi_dds::Pubsub; -use yazi_fs::{Cha, File, Files, FilesOp, FolderStage, Step}; +use yazi_fs::{File, Files, FilesOp, FolderStage, Step, cha::Cha}; use yazi_proxy::MgrProxy; use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index a55ac3c8..65eccd6a 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -5,7 +5,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tokio_util::sync::CancellationToken; use yazi_adapter::ADAPTOR; use yazi_config::PLUGIN; -use yazi_fs::{Cha, File, Files, FilesOp}; +use yazi_fs::{File, Files, FilesOp, cha::Cha}; use yazi_macro::render; use yazi_plugin::{external::Highlighter, isolate, utils::PreviewLock}; use yazi_shared::{MIME_DIR, url::Url}; diff --git a/yazi-fs/src/cha.rs b/yazi-fs/src/cha/cha.rs similarity index 79% rename from yazi-fs/src/cha.rs rename to yazi-fs/src/cha/cha.rs index 9ac97980..37732a78 100644 --- a/yazi-fs/src/cha.rs +++ b/yazi-fs/src/cha/cha.rs @@ -1,22 +1,8 @@ use std::{fs::{FileType, Metadata}, path::Path, time::SystemTime}; -use bitflags::bitflags; use yazi_macro::{unix_either, win_either}; -bitflags! { - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] - pub struct ChaKind: u8 { - const DIR = 0b00000001; - - const HIDDEN = 0b00000010; - const LINK = 0b00000100; - const ORPHAN = 0b00001000; - - const DUMMY = 0b00010000; - #[cfg(windows)] - const SYSTEM = 0b00100000; - } -} +use super::ChaKind; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct Cha { @@ -39,8 +25,8 @@ pub struct Cha { pub nlink: libc::nlink_t, } -impl From for Cha { - fn from(m: Metadata) -> Self { +impl From<&Metadata> for Cha { + fn from(m: &Metadata) -> Self { let mut kind = ChaKind::empty(); if m.is_dir() { kind |= ChaKind::DIR; @@ -89,6 +75,10 @@ impl From for Cha { } } +impl From for Cha { + fn from(m: Metadata) -> Self { Self::from(&m) } +} + impl From for Cha { fn from(t: FileType) -> Self { let mut kind = ChaKind::DUMMY; @@ -134,9 +124,8 @@ impl From for Cha { } impl Cha { - #[inline] pub async fn new(path: &Path, mut meta: Metadata) -> Self { - let mut attached = ChaKind::empty(); + let mut attached = ChaKind::hidden(path, &meta); if meta.is_symlink() { attached |= ChaKind::LINK; @@ -146,35 +135,16 @@ impl Cha { attached |= ChaKind::ORPHAN; } - let mut cha = Self::new_nofollow(path, meta); - cha.kind |= attached; - cha + let mut me = Self::from(meta); + me.kind |= attached; + me } #[inline] pub fn new_nofollow(_path: &Path, meta: Metadata) -> Self { - let mut attached = ChaKind::empty(); - - #[cfg(unix)] - if yazi_shared::url::Urn::new(_path).is_hidden() { - attached |= ChaKind::HIDDEN; - } - #[cfg(windows)] - { - use std::os::windows::fs::MetadataExt; - - use windows_sys::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM}; - if meta.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0 { - attached |= ChaKind::HIDDEN; - } - if meta.file_attributes() & FILE_ATTRIBUTE_SYSTEM != 0 { - attached |= ChaKind::SYSTEM; - } - } - - let mut cha = Self::from(meta); - cha.kind |= attached; - cha + let mut me = Self::from(&meta); + me.kind |= ChaKind::hidden(_path, &meta); + me } #[inline] diff --git a/yazi-fs/src/cha/kind.rs b/yazi-fs/src/cha/kind.rs new file mode 100644 index 00000000..8b71aa41 --- /dev/null +++ b/yazi-fs/src/cha/kind.rs @@ -0,0 +1,44 @@ +use std::{fs::Metadata, path::Path}; + +use bitflags::bitflags; + +bitflags! { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] + pub struct ChaKind: u8 { + const DIR = 0b00000001; + + const HIDDEN = 0b00000010; + const LINK = 0b00000100; + const ORPHAN = 0b00001000; + + const DUMMY = 0b00010000; + #[cfg(windows)] + const SYSTEM = 0b00100000; + } +} + +impl ChaKind { + #[inline] + pub(super) fn hidden(_path: &Path, _meta: &Metadata) -> Self { + let mut me = Self::empty(); + + #[cfg(unix)] + if yazi_shared::url::Urn::new(_path).is_hidden() { + me |= Self::HIDDEN; + } + #[cfg(windows)] + { + use std::os::windows::fs::MetadataExt; + + use windows_sys::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM}; + if _meta.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0 { + me |= Self::HIDDEN; + } + if _meta.file_attributes() & FILE_ATTRIBUTE_SYSTEM != 0 { + me |= Self::SYSTEM; + } + } + + me + } +} diff --git a/yazi-fs/src/cha/mod.rs b/yazi-fs/src/cha/mod.rs new file mode 100644 index 00000000..aa8136b3 --- /dev/null +++ b/yazi-fs/src/cha/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(cha kind); diff --git a/yazi-fs/src/file.rs b/yazi-fs/src/file.rs index 2c1467ee..18d7ba77 100644 --- a/yazi-fs/src/file.rs +++ b/yazi-fs/src/file.rs @@ -4,7 +4,7 @@ use anyhow::Result; use tokio::fs; use yazi_shared::{SyncCell, theme::IconCache, url::{Url, Urn, UrnBuf}}; -use crate::Cha; +use crate::cha::Cha; #[derive(Clone, Debug, Default)] pub struct File { diff --git a/yazi-fs/src/files.rs b/yazi-fs/src/files.rs index bed8bc7b..6d195c61 100644 --- a/yazi-fs/src/files.rs +++ b/yazi-fs/src/files.rs @@ -4,7 +4,7 @@ use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}} use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; use super::{FilesSorter, Filter}; -use crate::{Cha, FILES_TICKET, File, FilesOp, SortBy, maybe_exists, mounts::PARTITIONS}; +use crate::{FILES_TICKET, File, FilesOp, SortBy, cha::Cha, maybe_exists, mounts::PARTITIONS}; #[derive(Default)] pub struct Files { diff --git a/yazi-fs/src/fns.rs b/yazi-fs/src/fns.rs index 45206338..cf73a577 100644 --- a/yazi-fs/src/fns.rs +++ b/yazi-fs/src/fns.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, collections::{HashMap, HashSet, VecDeque}, ffi::{OsStr, O use anyhow::{Result, bail}; use tokio::{fs, io::{self, AsyncWriteExt}, select, sync::{mpsc, oneshot}, time}; -use super::Cha; +use crate::cha::Cha; #[inline] pub async fn must_exists(p: impl AsRef) -> bool { fs::symlink_metadata(p).await.is_ok() } diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs index e028bc45..22488e75 100644 --- a/yazi-fs/src/lib.rs +++ b/yazi-fs/src/lib.rs @@ -1,8 +1,8 @@ #![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)] -yazi_macro::mod_pub!(mounts); +yazi_macro::mod_pub!(cha mounts); -yazi_macro::mod_flat!(cha cwd file files filter fns op path sorter sorting stage step xdg); +yazi_macro::mod_flat!(cwd file files filter fns op path sorter sorting stage step xdg); pub fn init() { CWD.init(<_>::default()); diff --git a/yazi-fs/src/mounts/partitions.rs b/yazi-fs/src/mounts/partitions.rs index ef589eec..eacee155 100644 --- a/yazi-fs/src/mounts/partitions.rs +++ b/yazi-fs/src/mounts/partitions.rs @@ -4,7 +4,7 @@ use parking_lot::RwLock; use yazi_shared::RoCell; use super::Partition; -use crate::Cha; +use crate::cha::Cha; pub(super) type Locked = Arc>; diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index d1f4cd70..fe22c95e 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -2,7 +2,8 @@ use std::collections::{HashMap, HashSet}; use yazi_shared::{Id, Ids, event::Cmd, url::{Url, UrnBuf}}; -use super::{Cha, File}; +use super::File; +use crate::cha::Cha; pub static FILES_TICKET: Ids = Ids::new(); diff --git a/yazi-plugin/src/bindings/cha.rs b/yazi-plugin/src/bindings/cha.rs index 4329be9c..dca7ef50 100644 --- a/yazi-plugin/src/bindings/cha.rs +++ b/yazi-plugin/src/bindings/cha.rs @@ -1,18 +1,18 @@ use std::{ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods}; -use yazi_fs::ChaKind; +use yazi_fs::cha::ChaKind; #[derive(Clone, Copy, FromLua)] -pub struct Cha(yazi_fs::Cha); +pub struct Cha(yazi_fs::cha::Cha); impl Deref for Cha { - type Target = yazi_fs::Cha; + type Target = yazi_fs::cha::Cha; fn deref(&self) -> &Self::Target { &self.0 } } -impl> From for Cha { +impl> From for Cha { fn from(cha: T) -> Self { Self(cha.into()) } } @@ -33,7 +33,7 @@ impl Cha { let kind = ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?; - Self::from(yazi_fs::Cha { + Self::from(yazi_fs::cha::Cha { kind, len: t.raw_get("len").unwrap_or_default(), atime: parse_time(t.raw_get("atime").ok())?, diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 19915519..175d94d7 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -9,7 +9,7 @@ use crate::{elements::{Area, Rect, Renderable, Text, WRAP, WRAP_NO}, external::H #[derive(Debug, Default)] pub struct PreviewLock { pub url: yazi_shared::url::Url, - pub cha: yazi_fs::Cha, + pub cha: yazi_fs::cha::Cha, pub mime: String, pub skip: usize, diff --git a/yazi-plugin/src/utils/spot.rs b/yazi-plugin/src/utils/spot.rs index 70e39368..26a43e90 100644 --- a/yazi-plugin/src/utils/spot.rs +++ b/yazi-plugin/src/utils/spot.rs @@ -8,7 +8,7 @@ use crate::{elements::Renderable, file::FileRef}; pub struct SpotLock { pub url: yazi_shared::url::Url, - pub cha: yazi_fs::Cha, + pub cha: yazi_fs::cha::Cha, pub mime: String, pub skip: usize, diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 16b7ed0f..329ab4ec 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -4,7 +4,7 @@ use anyhow::{Result, anyhow}; use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::TASKS; -use yazi_fs::{Cha, calculate_size, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path}; +use yazi_fs::{calculate_size, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path}; use yazi_shared::url::Url; use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}; diff --git a/yazi-scheduler/src/file/op.rs b/yazi-scheduler/src/file/op.rs index dd0c474c..0d87fde8 100644 --- a/yazi-scheduler/src/file/op.rs +++ b/yazi-scheduler/src/file/op.rs @@ -1,4 +1,4 @@ -use yazi_fs::Cha; +use yazi_fs::cha::Cha; use yazi_shared::url::Url; #[derive(Debug)]