Skip to content

Commit

Permalink
Show broken files with unavailable metadata
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Ape committed Jul 9, 2024
1 parent afa5936 commit 11ab1c0
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 29 deletions.
5 changes: 5 additions & 0 deletions yazi-config/preset/theme.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down Expand Up @@ -765,6 +769,7 @@ exts = [
conds = [
# Special files
{ if = "orphan", text = "" },
{ if = "inaccessible", text = "" },
{ if = "link" , text = "" },
{ if = "block" , text = "" },
{ if = "char" , text = "" },
Expand Down
1 change: 1 addition & 0 deletions yazi-config/src/theme/icons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions yazi-config/src/theme/is.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum Is {
Orphan,
Sock,
Sticky,
Inaccessible,
}

impl FromStr for Is {
Expand All @@ -32,6 +33,7 @@ impl FromStr for Is {
"orphan" => Self::Orphan,
"sock" => Self::Sock,
"sticky" => Self::Sticky,
"inaccessible" => Self::Inaccessible,
_ => bail!("invalid filetype: {s}"),
})
}
Expand All @@ -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,
}
}
}
6 changes: 6 additions & 0 deletions yazi-core/src/folder/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
Expand All @@ -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
Expand Down
3 changes: 1 addition & 2 deletions yazi-plugin/preset/components/folder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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(" ")
Expand Down
11 changes: 11 additions & 0 deletions yazi-plugin/preset/ya.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 5 additions & 4 deletions yazi-plugin/src/cha/cha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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::<String>,
)
Expand Down Expand Up @@ -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)]
Expand Down
24 changes: 18 additions & 6 deletions yazi-plugin/src/utils/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,32 @@ impl Utils {

ya.raw_set(
"user_name",
lua.create_function(|lua, uid: Option<u32>| {
lua.create_function(|lua, uid: Option<Option<u32>>| {
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()))
.map(|s| lua.create_string(s.name().as_encoded_bytes()))
.get_user_by_uid(user_id)
.map(|user| lua.create_string(user.name().as_encoded_bytes()))
.transpose()
})?,
)?;

ya.raw_set(
"group_name",
lua.create_function(|lua, gid: Option<u32>| {
lua.create_function(|lua, gid: Option<Option<u32>>| {
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()))
.map(|s| lua.create_string(s.name().as_encoded_bytes()))
.get_group_by_gid(group_id)
.map(|group| lua.create_string(group.name().as_encoded_bytes()))
.transpose()
})?,
)?;
Expand Down
72 changes: 57 additions & 15 deletions yazi-shared/src/fs/cha.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{fs::Metadata, time::SystemTime};
use std::{fs::{FileType, Metadata}, time::SystemTime};

use bitflags::bitflags;

Expand All @@ -20,19 +20,20 @@ bitflags! {

#[derive(Clone, Copy, Debug, Default)]
pub struct Cha {
pub kind: ChaKind,
pub len: u64,
pub accessed: Option<SystemTime>,
pub created: Option<SystemTime>,
pub modified: Option<SystemTime>,
pub kind: ChaKind,
pub len: u64,
pub accessed: Option<SystemTime>,
pub created: Option<SystemTime>,
pub modified: Option<SystemTime>,
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<Metadata> for Cha {
Expand Down Expand Up @@ -60,11 +61,12 @@ impl From<Metadata> 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: {
Expand All @@ -90,12 +92,52 @@ impl From<Metadata> for Cha {
}
}

impl From<FileType> 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 {
Expand Down
10 changes: 9 additions & 1 deletion yazi-shared/src/fs/file.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<FileType>) -> 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() } }
}
Expand Down
10 changes: 9 additions & 1 deletion yazi-shared/src/fs/fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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 { '-' });
Expand Down

0 comments on commit 11ab1c0

Please sign in to comment.