diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 5a0b86246..ee4c7ed44 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -189,6 +189,10 @@ rules = [ { name = "*", is = "orphan", bg = "red" }, { name = "*", is = "exec" , fg = "green" }, + # Inaccessible files + { name = "*", is = "inaccessible", bg = "red" }, + { name = "*/", is = "inaccessible", bg = "red" }, + # Fallback # { name = "*", fg = "white" }, { name = "*/", fg = "blue" } @@ -765,6 +769,7 @@ exts = [ conds = [ # Special files { if = "orphan", text = "" }, + { if = "inaccessible", text = "" }, { if = "link" , text = "" }, { if = "block" , text = "" }, { if = "char" , text = "" }, diff --git a/yazi-config/src/theme/icons.rs b/yazi-config/src/theme/icons.rs index 04c1e81b6..937316241 100644 --- a/yazi-config/src/theme/icons.rs +++ b/yazi-config/src/theme/icons.rs @@ -35,6 +35,7 @@ impl Icons { "sock" => file.is_sock(), "exec" => file.is_exec(), "sticky" => file.is_sticky(), + "inaccessible" => file.inaccessible, _ => false, }; self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i) diff --git a/yazi-config/src/theme/is.rs b/yazi-config/src/theme/is.rs index 89a9ad7f4..491bd72dd 100644 --- a/yazi-config/src/theme/is.rs +++ b/yazi-config/src/theme/is.rs @@ -17,6 +17,7 @@ pub enum Is { Orphan, Sock, Sticky, + Inaccessible, } impl FromStr for Is { @@ -32,6 +33,7 @@ impl FromStr for Is { "orphan" => Self::Orphan, "sock" => Self::Sock, "sticky" => Self::Sticky, + "inaccessible" => Self::Inaccessible, _ => bail!("invalid filetype: {s}"), }) } @@ -56,6 +58,7 @@ impl Is { Self::Orphan => cha.is_orphan(), Self::Sock => cha.is_sock(), Self::Sticky => cha.is_sticky(), + Self::Inaccessible => cha.inaccessible, } } } diff --git a/yazi-core/src/folder/files.rs b/yazi-core/src/folder/files.rs index f1297a7b8..c917dd219 100644 --- a/yazi-core/src/folder/files.rs +++ b/yazi-core/src/folder/files.rs @@ -56,6 +56,9 @@ impl Files { result = item.metadata() => { if let Ok(meta) = result { tx.send(File::from_meta(Url::from(item.path()), meta).await).ok(); + } else { + let file_type = item.file_type().await.ok(); + tx.send(File::from_no_meta(Url::from(item.path()), file_type).await).ok(); } } } @@ -78,6 +81,9 @@ impl Files { for entry in entities { if let Ok(meta) = entry.metadata().await { files.push(File::from_meta(Url::from(entry.path()), meta).await); + } else { + let file_type = entry.file_type().await.ok(); + files.push(File::from_no_meta(Url::from(entry.path()), file_type).await); } } files diff --git a/yazi-plugin/preset/components/folder.lua b/yazi-plugin/preset/components/folder.lua index b172d4199..28490c312 100644 --- a/yazi-plugin/preset/components/folder.lua +++ b/yazi-plugin/preset/components/folder.lua @@ -22,8 +22,7 @@ function Folder:linemode(area, files) elseif mode == "permissions" then spans[#spans + 1] = ui.Span(f.cha:permissions() or "") elseif mode == "owner" then - spans[#spans + 1] = ya.user_name and ui.Span(ya.user_name(f.cha.uid) .. ":" .. ya.group_name(f.cha.gid)) - or ui.Span("") + spans[#spans + 1] = ui.Span(ya.owner(f.cha) or "") end spans[#spans + 1] = ui.Span(" ") diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 8713c0a15..23ea6548c 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -50,3 +50,14 @@ function ya.readable_path(path) return path end end + +function ya.owner(cha) + if not cha.uid or not cha.gid then + return nil + end + + user = ya.user_name(cha.uid) or cha.uid + group = ya.group_name(cha.gid) or cha.gid + + return user .. ":" .. group +end diff --git a/yazi-plugin/src/cha/cha.rs b/yazi-plugin/src/cha/cha.rs index 96830beec..09d996d95 100644 --- a/yazi-plugin/src/cha/cha.rs +++ b/yazi-plugin/src/cha/cha.rs @@ -25,9 +25,9 @@ impl Cha { #[cfg(unix)] { - reg.add_field_method_get("uid", |_, me| Ok(me.uid)); - reg.add_field_method_get("gid", |_, me| Ok(me.gid)); - reg.add_field_method_get("nlink", |_, me| Ok(me.nlink)); + reg.add_field_method_get("uid", |_, me| Ok((!me.inaccessible).then_some(me.uid))); + reg.add_field_method_get("gid", |_, me| Ok((!me.inaccessible).then_some(me.gid))); + reg.add_field_method_get("nlink", |_, me| Ok((!me.inaccessible).then_some(me.nlink))); } reg.add_field_method_get("length", |_, me| Ok(me.len)); @@ -43,7 +43,7 @@ impl Cha { reg.add_method("permissions", |_, me, ()| { Ok( #[cfg(unix)] - Some(yazi_shared::fs::permissions(me.permissions)), + Some(yazi_shared::fs::permissions(me.permissions, me.inaccessible)), #[cfg(windows)] None::, ) @@ -75,6 +75,7 @@ impl Cha { accessed: parse_time(t.raw_get("atime").ok())?, created: parse_time(t.raw_get("ctime").ok())?, modified: parse_time(t.raw_get("mtime").ok())?, + inaccessible: t.raw_get("inaccessible").unwrap_or_default(), #[cfg(unix)] permissions: t.raw_get("permissions").unwrap_or_default(), #[cfg(unix)] diff --git a/yazi-plugin/src/utils/user.rs b/yazi-plugin/src/utils/user.rs index 0679e405d..037a59077 100644 --- a/yazi-plugin/src/utils/user.rs +++ b/yazi-plugin/src/utils/user.rs @@ -17,9 +17,15 @@ impl Utils { ya.raw_set( "user_name", - lua.create_function(|lua, uid: Option| { + lua.create_function(|lua, uid: Option>| { + let user_id = match uid { + None => USERS_CACHE.get_current_uid(), + Some(None) => return Ok(None), + Some(Some(u)) => u, + }; + USERS_CACHE - .get_user_by_uid(uid.unwrap_or_else(|| USERS_CACHE.get_current_uid())) + .get_user_by_uid(user_id) .map(|s| lua.create_string(s.name().as_encoded_bytes())) .transpose() })?, @@ -27,9 +33,15 @@ impl Utils { ya.raw_set( "group_name", - lua.create_function(|lua, gid: Option| { + lua.create_function(|lua, gid: Option>| { + let group_id = match gid { + None => USERS_CACHE.get_current_gid(), + Some(None) => return Ok(None), + Some(Some(g)) => g, + }; + USERS_CACHE - .get_group_by_gid(gid.unwrap_or_else(|| USERS_CACHE.get_current_gid())) + .get_group_by_gid(group_id) .map(|s| lua.create_string(s.name().as_encoded_bytes())) .transpose() })?, diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 310ec706a..18b8a387f 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -1,4 +1,4 @@ -use std::{fs::Metadata, time::SystemTime}; +use std::{fs::{FileType, Metadata}, time::SystemTime}; use bitflags::bitflags; @@ -20,19 +20,20 @@ bitflags! { #[derive(Clone, Copy, Debug, Default)] pub struct Cha { - pub kind: ChaKind, - pub len: u64, - pub accessed: Option, - pub created: Option, - pub modified: Option, + pub kind: ChaKind, + pub len: u64, + pub accessed: Option, + pub created: Option, + pub modified: Option, + pub inaccessible: bool, #[cfg(unix)] - pub permissions: libc::mode_t, + pub permissions: libc::mode_t, #[cfg(unix)] - pub uid: libc::uid_t, + pub uid: libc::uid_t, #[cfg(unix)] - pub gid: libc::gid_t, + pub gid: libc::gid_t, #[cfg(unix)] - pub nlink: libc::nlink_t, + pub nlink: libc::nlink_t, } impl From for Cha { @@ -60,11 +61,12 @@ impl From for Cha { } Self { - kind: ck, - len: m.len(), - accessed: m.accessed().ok(), - created: m.created().ok(), - modified: m.modified().ok(), + kind: ck, + len: m.len(), + accessed: m.accessed().ok(), + created: m.created().ok(), + modified: m.modified().ok(), + inaccessible: false, #[cfg(unix)] permissions: { @@ -90,12 +92,52 @@ impl From for Cha { } } +impl From for Cha { + fn from(file_type: FileType) -> Self { + let mut kind = ChaKind::empty(); + let mut permissions = 0; + + if file_type.is_dir() { + kind |= ChaKind::DIR; + + #[cfg(unix)] + { + permissions |= libc::S_IFDIR; + } + } + + if file_type.is_symlink() { + kind |= ChaKind::LINK; + + #[cfg(unix)] + { + permissions |= libc::S_IFLNK; + } + } + + Self { + kind, + + #[cfg(unix)] + permissions, + + ..Default::default() + } + } +} + impl Cha { #[inline] pub fn with_kind(mut self, kind: ChaKind) -> Self { self.kind |= kind; self } + + #[inline] + pub fn inaccessible(mut self) -> Self { + self.inaccessible = true; + self + } } impl Cha { diff --git a/yazi-shared/src/fs/file.rs b/yazi-shared/src/fs/file.rs index eb3eebf57..ef189e82c 100644 --- a/yazi-shared/src/fs/file.rs +++ b/yazi-shared/src/fs/file.rs @@ -1,4 +1,4 @@ -use std::{cell::Cell, ffi::OsStr, fs::Metadata, ops::Deref}; +use std::{cell::Cell, ffi::OsStr, fs::{FileType, Metadata}, ops::Deref}; use anyhow::Result; use tokio::fs; @@ -62,6 +62,14 @@ impl File { Self { url, cha: Cha::from(meta).with_kind(ck), link_to, icon: Default::default() } } + pub async fn from_no_meta(url: Url, file_type: Option) -> Self { + Self { + url, + cha: file_type.map_or_else(Cha::default, Cha::from).inaccessible(), + ..Default::default() + } + } + #[inline] pub fn from_dummy(url: &Url) -> Self { Self { url: url.to_owned(), ..Default::default() } } } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index e201807be..0cdd9a30f 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -264,7 +264,7 @@ pub fn copy_with_progress( // Convert a file mode to a string representation #[cfg(unix)] #[allow(clippy::collapsible_else_if)] -pub fn permissions(m: libc::mode_t) -> String { +pub fn permissions(m: libc::mode_t, inaccessible: bool) -> String { use libc::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR}; let mut s = String::with_capacity(10); @@ -279,6 +279,14 @@ pub fn permissions(m: libc::mode_t) -> String { _ => '-', }); + if inaccessible { + for _ in 0..9 { + s.push('?'); + } + + return s; + } + // Owner s.push(if m & S_IRUSR != 0 { 'r' } else { '-' }); s.push(if m & S_IWUSR != 0 { 'w' } else { '-' });