From 92fc9eeaec95c09439bbd0038ceb219d8174ee66 Mon Sep 17 00:00:00 2001 From: Lauri Niskanen Date: Mon, 8 Jul 2024 19:41:22 +0300 Subject: [PATCH] Show broken files with unavailable metadata Some broken files cause tokio::fs::DirEntry::metadata to fail. The previous version would just hide those broken files with no way of viewing them. This commit shows those files, but marked as broken. File type information about being a directory or a symlink is used if available. --- yazi-config/preset/theme.toml | 5 ++ yazi-config/src/theme/icons.rs | 1 + yazi-config/src/theme/is.rs | 3 + yazi-core/src/folder/files.rs | 6 ++ yazi-plugin/preset/components/folder.lua | 3 +- yazi-plugin/preset/components/status.lua | 2 +- yazi-plugin/preset/ya.lua | 11 ++++ yazi-plugin/src/cha/cha.rs | 9 +-- yazi-plugin/src/utils/user.rs | 20 +++++-- yazi-shared/src/fs/cha.rs | 72 +++++++++++++++++++----- yazi-shared/src/fs/file.rs | 10 +++- yazi-shared/src/fs/fns.rs | 10 +++- 12 files changed, 124 insertions(+), 28 deletions(-) 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/components/status.lua b/yazi-plugin/preset/components/status.lua index 571b9469f..5d383024a 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -62,7 +62,7 @@ function Status:permissions() for i = 1, #perm do local c = perm:sub(i, i) local style = THEME.status.permissions_t - if c == "-" then + if c == "-" or c == "?" then style = THEME.status.permissions_s elseif c == "r" then style = THEME.status.permissions_r 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 { '-' });