Skip to content

Commit

Permalink
feat: show Btrfs subvolumes
Browse files Browse the repository at this point in the history
  • Loading branch information
horror-proton committed Oct 12, 2024
1 parent d1df86e commit e68311e
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 1 deletion.
39 changes: 39 additions & 0 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,45 @@ impl<'dir> File<'dir> {
.is_some_and(|p| all_mounts().contains_key(p))
}

/// Whether the directory represents a Btrfs subvolume
#[cfg(target_os = "linux")]
pub fn is_btrfs_subvolume(&self) -> bool {
// Listing all subvolumes with ioctl(BTRFS_IOC_TREE_SEARCH) requires CAP_SYS_ADMIN, normal users can't do that.
// So we test the inode number, directory representing a subvolume has always inode number 256
const BTRFS_FIRST_FREE_OBJECTID: u64 = 256;
self.metadata.file_type().is_dir()
&& self.metadata.ino() == BTRFS_FIRST_FREE_OBJECTID
&& self.is_btrfs().unwrap_or(false)
}

#[cfg(not(target_os = "linux"))]
pub fn is_btrfs_subvolume(&self) -> bool {
false
}

#[cfg(target_os = "linux")]
fn is_btrfs(&self) -> io::Result<bool> {
use std::os::unix::ffi::OsStrExt;

const BTRFS_FSTYPE_NAME: &str = "btrfs";

for part in self.absolute_path().unwrap_or(&self.path).ancestors() {
if let Some(mount) = all_mounts().get(part) {
return Ok(mount.fstype == BTRFS_FSTYPE_NAME);
}
}

// /proc/mounts not working? fallback to statfs
let file_path = &self.path;
let mut out = std::mem::MaybeUninit::<libc::statfs>::uninit();
let path = std::ffi::CString::new(file_path.as_os_str().as_bytes()).unwrap();
match unsafe { libc::statfs(path.as_ptr(), out.as_mut_ptr()) } {
0 => Ok(unsafe { out.assume_init() }.f_type == libc::BTRFS_SUPER_MAGIC),
_ => Err(io::Error::last_os_error()),
// eprintln!("eza: statfs {:?}: {}", self.path, os_err);
}
}

/// The filesystem device and type for a mount point
pub fn mount_point_info(&self) -> Option<&MountedFs> {
if cfg!(any(target_os = "linux", target_os = "macos")) {
Expand Down
2 changes: 2 additions & 0 deletions src/options/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ pub struct FileKindsOverride {
pub special: Option<StyleOverride>, // sp
pub executable: Option<StyleOverride>, // ex
pub mount_point: Option<StyleOverride>, // mp
pub btrfs_subvol: Option<StyleOverride>, // sv
}

impl FromOverride<FileKindsOverride> for FileKinds {
Expand All @@ -281,6 +282,7 @@ impl FromOverride<FileKindsOverride> for FileKinds {
special: FromOverride::from(value.special, default.special),
executable: FromOverride::from(value.executable, default.executable),
mount_point: FromOverride::from(value.mount_point, default.mount_point),
btrfs_subvol: FromOverride::from(value.btrfs_subvol, default.btrfs_subvol),
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/output/file_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
#[rustfmt::skip]
return match self.file {
f if f.is_mount_point() => self.colours.mount_point(),
f if f.is_btrfs_subvolume() => self.colours.btrfs_subvol(),
f if f.is_directory() => self.colours.directory(),
#[cfg(unix)]
f if f.is_executable_file() => self.colours.executable_file(),
Expand Down Expand Up @@ -541,6 +542,9 @@ pub trait Colours: FiletypeColours {
/// The style to paint a directory that has a filesystem mounted on it.
fn mount_point(&self) -> Style;

/// The style to paint a directory representing a Btrfs subvolume.
fn btrfs_subvol(&self) -> Style;

fn colour_file(&self, file: &File<'_>) -> Style;

fn style_override(&self, file: &File<'_>) -> Option<FileNameStyle>;
Expand Down
1 change: 1 addition & 0 deletions src/theme/default_theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl Default for UiStyles {
special: Some(Yellow.normal()),
executable: Some(Green.bold()),
mount_point: Some(Blue.bold().underline()),
btrfs_subvol: Some(Blue.underline()),
}),

#[rustfmt::skip]
Expand Down
4 changes: 4 additions & 0 deletions src/theme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,9 @@ impl FileNameColours for Theme {
fn broken_control_char(&self) -> Style { apply_overlay(self.ui.control_char(), self.ui.broken_path_overlay()) }
fn executable_file(&self) -> Style { self.ui.filekinds.unwrap_or_default().executable() }
fn mount_point(&self) -> Style { self.ui.filekinds.unwrap_or_default().mount_point() }
fn btrfs_subvol(&self) -> Style {
self.ui.filekinds.unwrap_or_default().btrfs_subvol()
}

fn colour_file(&self, file: &File<'_>) -> Style {
self.exts
Expand Down Expand Up @@ -643,6 +646,7 @@ mod customs_test {
test!(exa_bo: ls "", exa "bO=4" => colours c -> { c.broken_path_overlay = Some(Style::default().underline()); });

test!(exa_mp: ls "", exa "mp=1;34;4" => colours c -> { c.filekinds().mount_point = Some(Blue.bold().underline()); });
test!(exa_sv: ls "", exa "sv=0;34;4" => colours c -> { c.filekinds().btrfs_subvol = Some(Blue.underline()); });
test!(exa_sp: ls "", exa "sp=1;35;4" => colours c -> { c.filekinds().special = Some(Purple.bold().underline()); });

test!(exa_im: ls "", exa "im=38;5;128" => colours c -> { c.file_type().image = Some(Fixed(128).normal()); });
Expand Down
7 changes: 6 additions & 1 deletion src/theme/ui_styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub struct FileKinds {
pub special: Option<Style>, // sp
pub executable: Option<Style>, // ex
pub mount_point: Option<Style>, // mp
pub btrfs_subvol: Option<Style>, // sv
}

impl Default for FileKinds {
Expand All @@ -140,6 +141,7 @@ impl Default for FileKinds {
special: Some(Yellow.normal()),
executable: Some(Green.bold()),
mount_point: Some(Blue.bold().underline()),
btrfs_subvol: Some(Blue.underline()),
}
}
}
Expand All @@ -154,7 +156,8 @@ field_accessors!(
socket: Option<Style>,
special: Option<Style>,
executable: Option<Style>,
mount_point: Option<Style>
mount_point: Option<Style>,
btrfs_subvol: Option<Style>
);

#[rustfmt::skip]
Expand Down Expand Up @@ -397,6 +400,7 @@ impl UiStyles {
special: Some(Style::default()),
executable: Some(Style::default()),
mount_point: Some(Style::default()),
btrfs_subvol: Some(Style::default()),
}),

#[rustfmt::skip]
Expand Down Expand Up @@ -596,6 +600,7 @@ impl UiStyles {
"bO" => self.broken_path_overlay = Some(pair.to_style()),

"mp" => self.filekinds().mount_point = Some(pair.to_style()),
"sv" => self.filekinds().btrfs_subvol = Some(pair.to_style()),
"sp" => self.filekinds().special = Some(pair.to_style()), // Catch-all for unrecognized file kind

"im" => self.file_type().image = Some(pair.to_style()),
Expand Down

0 comments on commit e68311e

Please sign in to comment.