fix: respect hidden status of directory junctions and symlinks themselves on Windows (#2471)

This commit is contained in:
三咲雅 · Misaki Masa 2025-03-11 12:57:47 +08:00 committed by sxyazi
parent 76adb97e22
commit a0c6842626
No known key found for this signature in database
21 changed files with 93 additions and 77 deletions

20
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -1,5 +1,5 @@
use serde::Deserialize;
use yazi_fs::Cha;
use yazi_fs::cha::Cha;
#[derive(Default, Deserialize)]
#[serde(rename_all = "kebab-case")]

View file

@ -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};

View file

@ -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}};

View file

@ -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}};

View file

@ -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};

View file

@ -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<Metadata> 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<Metadata> for Cha {
}
}
impl From<Metadata> for Cha {
fn from(m: Metadata) -> Self { Self::from(&m) }
}
impl From<FileType> for Cha {
fn from(t: FileType) -> Self {
let mut kind = ChaKind::DUMMY;
@ -134,9 +124,8 @@ impl From<FileType> 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]

44
yazi-fs/src/cha/kind.rs Normal file
View file

@ -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
}
}

1
yazi-fs/src/cha/mod.rs Normal file
View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(cha kind);

View file

@ -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 {

View file

@ -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 {

View file

@ -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<Path>) -> bool { fs::symlink_metadata(p).await.is_ok() }

View file

@ -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());

View file

@ -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<RwLock<Partitions>>;

View file

@ -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();

View file

@ -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<T: Into<yazi_fs::Cha>> From<T> for Cha {
impl<T: Into<yazi_fs::cha::Cha>> From<T> 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())?,

View file

@ -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,

View file

@ -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,

View file

@ -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};

View file

@ -1,4 +1,4 @@
use yazi_fs::Cha;
use yazi_fs::cha::Cha;
use yazi_shared::url::Url;
#[derive(Debug)]