ogham / exa

A modern replacement for ‘ls’.
https://the.exa.website/
MIT License
23.68k stars 659 forks source link

Display the right major and minor device IDs when encountering block or char devices #1126

Open SteveLauC opened 2 years ago

SteveLauC commented 2 years ago

I noticed that the device ID returned by exa is wrong:

$ bat src/main.rs
───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: src/main.rs
───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ use nix::sys::stat::{major, minor, stat};
   2   │ fn main() {
   3   │     let s = stat("/dev/nvme0n1p3").unwrap();
   4   │
   5   │     println!("major: {}", major(s.st_rdev));
   6   │     println!("minor: {}", minor(s.st_rdev));
   7   │ }
───────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

$ cargo r -q
major: 259
minor: 3

$ exa -l /dev/nvme0n1p3
brw-rw----@ 3,3 root  2 Oct 07:22 /dev/nvme0n1p3

And this behavior is actually documented in the source code: https://github.com/ogham/exa/blob/master/src/fs/file.rs#L321~L332

Any reason why it is not correctly decomposed and returned?

SteveLauC commented 2 years ago

Seems that I found the reason, DeviceID is defined as following:

pub struct DeviceIDs {
    pub major: u8,
    pub minor: u8,
}

u8 is apparently not sufficient for those IDs...

SteveLauC commented 2 years ago

For someone who is interested in implementing this, you need major() and minor() functions from libc. These two functions for apple platforms are added in libc#2937, and included in release 0.2.135.

The impl is basically something like this:

https://github.com/SteveLauC/exa/blob/7bf29e8927c7747aacc963cb0ed273d3cc2241c9/src/fs/file.rs#L325-L328

            f::Size::DeviceIDs(f::DeviceIDs {
                major: unsafe{major(device_ids as dev_t)},
                minor: unsafe{minor(device_ids as dev_t)},
            })

https://github.com/SteveLauC/exa/blob/7bf29e8927c7747aacc963cb0ed273d3cc2241c9/src/fs/fields.rs#L181-L185

#[derive(Copy, Clone)]
pub struct DeviceIDs {
    pub major: u32,
    pub minor: u32,
}

What you need to fight is the difference between Linux's major()/minor()` definitions and the macOS one:

# Linux
pub type dev_t = u64;
pub fn major(dev: ::dev_t) -> ::c_uint;
pub fn minor(dev: ::dev_t) -> ::c_uint;

# macOS
pub type dev_t = i32;
pub fn major(dev: dev_t) -> i32;
pub fn minor(dev: dev_t) -> i32;