use std::ffi::CString;
use std::fmt::Write as FmtWrite;
use std::io;
use std::os::unix::fs::{FileTypeExt, MetadataExt};
pub struct StatConfig {
pub dereference: bool,
pub filesystem: bool,
pub format: Option<String>,
pub printf_format: Option<String>,
pub terse: bool,
}
fn extract_fsid(fsid: &libc::fsid_t) -> u64 {
let bytes: [u8; std::mem::size_of::<libc::fsid_t>()] =
unsafe { std::mem::transmute_copy(fsid) };
let val0 = u32::from_ne_bytes(bytes[0..4].try_into().unwrap()) as u64;
let val1 = u32::from_ne_bytes(bytes[4..8].try_into().unwrap()) as u64;
(val0 << 32) | val1
}
fn raw_stat(path: &str, dereference: bool) -> Result<libc::stat, io::Error> {
let c_path = CString::new(path)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?;
unsafe {
let mut st: libc::stat = std::mem::zeroed();
let rc = if dereference {
libc::stat(c_path.as_ptr(), &mut st)
} else {
libc::lstat(c_path.as_ptr(), &mut st)
};
if rc != 0 {
Err(io::Error::last_os_error())
} else {
Ok(st)
}
}
}
fn raw_fstat(fd: i32) -> Result<libc::stat, io::Error> {
unsafe {
let mut st: libc::stat = std::mem::zeroed();
let rc = libc::fstat(fd, &mut st);
if rc != 0 {
Err(io::Error::last_os_error())
} else {
Ok(st)
}
}
}
fn raw_statfs(path: &str) -> Result<libc::statfs, io::Error> {
let c_path = CString::new(path)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid path"))?;
unsafe {
let mut sfs: libc::statfs = std::mem::zeroed();
let rc = libc::statfs(c_path.as_ptr(), &mut sfs);
if rc != 0 {
Err(io::Error::last_os_error())
} else {
Ok(sfs)
}
}
}
pub fn stat_file(path: &str, config: &StatConfig) -> Result<String, io::Error> {
if path == "-" {
if config.filesystem {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"using '-' to denote standard input does not work in file system mode",
));
}
return stat_stdin(config);
}
if config.filesystem {
stat_filesystem(path, config)
} else {
stat_regular(path, config)
}
}
fn stat_stdin(config: &StatConfig) -> Result<String, io::Error> {
let st = raw_fstat(0)?;
let f = std::mem::ManuallyDrop::new(unsafe {
<std::fs::File as std::os::unix::io::FromRawFd>::from_raw_fd(0)
});
let meta = f.metadata()?;
let display_path = "-";
if let Some(ref fmt) = config.printf_format {
let expanded = expand_backslash_escapes(fmt);
return Ok(format_file_specifiers(
&expanded,
display_path,
&meta,
&st,
config.dereference,
));
}
if let Some(ref fmt) = config.format {
let result = format_file_specifiers(fmt, display_path, &meta, &st, config.dereference);
return Ok(result + "\n");
}
if config.terse {
return Ok(format_file_terse(
display_path,
&meta,
&st,
config.dereference,
));
}
Ok(format_file_default(
display_path,
&meta,
&st,
config.dereference,
))
}
fn stat_regular(path: &str, config: &StatConfig) -> Result<String, io::Error> {
let meta = if config.dereference {
std::fs::metadata(path)?
} else {
std::fs::symlink_metadata(path)?
};
let st = raw_stat(path, config.dereference)?;
if let Some(ref fmt) = config.printf_format {
let expanded = expand_backslash_escapes(fmt);
return Ok(format_file_specifiers(
&expanded,
path,
&meta,
&st,
config.dereference,
));
}
if let Some(ref fmt) = config.format {
let result = format_file_specifiers(fmt, path, &meta, &st, config.dereference);
return Ok(result + "\n");
}
if config.terse {
return Ok(format_file_terse(path, &meta, &st, config.dereference));
}
Ok(format_file_default(path, &meta, &st, config.dereference))
}
fn stat_filesystem(path: &str, config: &StatConfig) -> Result<String, io::Error> {
let sfs = raw_statfs(path)?;
if let Some(ref fmt) = config.printf_format {
let expanded = expand_backslash_escapes(fmt);
return Ok(format_fs_specifiers(&expanded, path, &sfs));
}
if let Some(ref fmt) = config.format {
let result = format_fs_specifiers(fmt, path, &sfs);
return Ok(result + "\n");
}
if config.terse {
return Ok(format_fs_terse(path, &sfs));
}
Ok(format_fs_default(path, &sfs))
}
fn format_file_default(
path: &str,
meta: &std::fs::Metadata,
st: &libc::stat,
dereference: bool,
) -> String {
let mode = meta.mode();
let file_type_str = file_type_label(mode);
let perms_str = mode_to_human(mode);
let uid = meta.uid();
let gid = meta.gid();
let uname = lookup_username(uid);
let gname = lookup_groupname(gid);
let dev = meta.dev();
let dev_major = major(dev);
let dev_minor = minor(dev);
let name_display = if meta.file_type().is_symlink() {
match std::fs::read_link(path) {
Ok(target) => format!("{} -> {}", path, target.display()),
Err(_) => path.to_string(),
}
} else {
path.to_string()
};
let size_line = if meta.file_type().is_block_device() || meta.file_type().is_char_device() {
let rdev = meta.rdev();
let rmaj = major(rdev);
let rmin = minor(rdev);
format!(
" Size: {:<10}\tBlocks: {:<10} IO Block: {:<6} {}",
format!("{}, {}", rmaj, rmin),
meta.blocks(),
meta.blksize(),
file_type_str
)
} else {
format!(
" Size: {:<10}\tBlocks: {:<10} IO Block: {:<6} {}",
meta.size(),
meta.blocks(),
meta.blksize(),
file_type_str
)
};
let device_line = format!(
"Device: {},{}\tInode: {:<11} Links: {}",
dev_major,
dev_minor,
meta.ino(),
meta.nlink()
);
let access_line = format!(
"Access: ({:04o}/{}) Uid: ({:5}/{:>8}) Gid: ({:5}/{:>8})",
mode & 0o7777,
perms_str,
uid,
uname,
gid,
gname
);
let atime = format_timestamp(st.st_atime, st.st_atime_nsec);
let mtime = format_timestamp(st.st_mtime, st.st_mtime_nsec);
let ctime = format_timestamp(st.st_ctime, st.st_ctime_nsec);
let birth = format_birth_time_for_path(path, dereference);
format!(
" File: {}\n{}\n{}\n{}\nAccess: {}\nModify: {}\nChange: {}\n Birth: {}\n",
name_display, size_line, device_line, access_line, atime, mtime, ctime, birth
)
}
fn format_file_terse(
path: &str,
meta: &std::fs::Metadata,
st: &libc::stat,
dereference: bool,
) -> String {
let dev = meta.dev();
let birth_secs = get_birth_time(path, dereference)
.map(|(s, _)| s)
.unwrap_or(0);
format!(
"{} {} {} {:x} {} {} {:x} {} {} {:x} {:x} {} {} {} {} {}\n",
path,
meta.size(),
meta.blocks(),
meta.mode(),
meta.uid(),
meta.gid(),
dev,
meta.ino(),
meta.nlink(),
major(meta.rdev()),
minor(meta.rdev()),
st.st_atime,
st.st_mtime,
st.st_ctime,
birth_secs,
meta.blksize()
)
}
fn format_fs_default(path: &str, sfs: &libc::statfs) -> String {
#[cfg(target_os = "linux")]
let fs_type = sfs.f_type;
#[cfg(not(target_os = "linux"))]
let fs_type = 0u32;
let fs_type_name = fs_type_name(fs_type as u64);
let fsid = sfs.f_fsid;
let fsid_val = extract_fsid(&fsid);
#[cfg(target_os = "linux")]
let namelen = sfs.f_namelen;
#[cfg(not(target_os = "linux"))]
let namelen = 255i64;
#[cfg(target_os = "linux")]
let frsize = sfs.f_frsize;
#[cfg(not(target_os = "linux"))]
let frsize = sfs.f_bsize as u64;
format!(
" File: \"{}\"\n ID: {:x} Namelen: {} Type: {}\nBlock size: {:<10} Fundamental block size: {}\nBlocks: Total: {:<10} Free: {:<10} Available: {}\nInodes: Total: {:<10} Free: {}\n",
path,
fsid_val,
namelen,
fs_type_name,
sfs.f_bsize,
frsize,
sfs.f_blocks,
sfs.f_bfree,
sfs.f_bavail,
sfs.f_files,
sfs.f_ffree
)
}
fn format_fs_terse(path: &str, sfs: &libc::statfs) -> String {
let fsid = sfs.f_fsid;
let fsid_val = extract_fsid(&fsid);
#[cfg(target_os = "linux")]
let namelen = sfs.f_namelen;
#[cfg(not(target_os = "linux"))]
let namelen = 255i64;
#[cfg(target_os = "linux")]
let fs_type = sfs.f_type;
#[cfg(not(target_os = "linux"))]
let fs_type = 0u32;
#[cfg(target_os = "linux")]
let frsize = sfs.f_frsize;
#[cfg(not(target_os = "linux"))]
let frsize = sfs.f_bsize as u64;
format!(
"{} {} {} {} {} {} {} {} {} {} {} {}\n",
path,
fsid_val,
namelen,
fs_type,
sfs.f_bsize,
frsize,
sfs.f_blocks,
sfs.f_bfree,
sfs.f_bavail,
sfs.f_files,
sfs.f_ffree,
0 )
}
fn format_file_specifiers(
fmt: &str,
path: &str,
meta: &std::fs::Metadata,
st: &libc::stat,
dereference: bool,
) -> String {
let mut result = String::new();
let bytes = fmt.as_bytes();
let len = bytes.len();
let mut i = 0;
while i < len {
if bytes[i] == b'%' && i + 1 < len {
i += 1;
if bytes[i] == b'H' || bytes[i] == b'L' {
if i + 1 >= len {
result.push('?');
i += 1;
continue;
}
let modifier = bytes[i];
let spec = bytes[i + 1];
match (modifier, spec) {
(b'H', b'd') => {
let _ = write!(result, "{}", major(meta.dev()));
i += 2;
continue;
}
(b'L', b'd') => {
let _ = write!(result, "{}", minor(meta.dev()));
i += 2;
continue;
}
(b'H', b'r') => {
let _ = write!(result, "{}", major(meta.rdev()));
i += 2;
continue;
}
(b'L', b'r') => {
let _ = write!(result, "{}", minor(meta.rdev()));
i += 2;
continue;
}
(_, _) => {
result.push('?');
result.push(spec as char);
i += 2;
continue;
}
}
}
match bytes[i] {
b'a' => {
let _ = write!(result, "{:o}", meta.mode() & 0o7777);
}
b'A' => {
result.push_str(&mode_to_human(meta.mode()));
}
b'b' => {
let _ = write!(result, "{}", meta.blocks());
}
b'B' => {
result.push_str("512");
}
b'd' => {
let _ = write!(result, "{}", meta.dev());
}
b'D' => {
let _ = write!(result, "{:x}", meta.dev());
}
b'f' => {
let _ = write!(result, "{:x}", meta.mode());
}
b'F' => {
result.push_str(file_type_label(meta.mode()));
}
b'g' => {
let _ = write!(result, "{}", meta.gid());
}
b'G' => {
result.push_str(&lookup_groupname(meta.gid()));
}
b'h' => {
let _ = write!(result, "{}", meta.nlink());
}
b'i' => {
let _ = write!(result, "{}", meta.ino());
}
b'm' => {
result.push_str(&find_mount_point(path));
}
b'n' => {
result.push_str(path);
}
b'N' => {
if meta.file_type().is_symlink() {
match std::fs::read_link(path) {
Ok(target) => {
result.push('\'');
result.push_str(path);
result.push_str("' -> '");
let _ = write!(result, "{}", target.display());
result.push('\'');
}
Err(_) => {
result.push('\'');
result.push_str(path);
result.push('\'');
}
}
} else {
result.push('\'');
result.push_str(path);
result.push('\'');
}
}
b'o' => {
let _ = write!(result, "{}", meta.blksize());
}
b's' => {
let _ = write!(result, "{}", meta.size());
}
b't' => {
let _ = write!(result, "{:x}", major(meta.rdev()));
}
b'T' => {
let _ = write!(result, "{:x}", minor(meta.rdev()));
}
b'u' => {
let _ = write!(result, "{}", meta.uid());
}
b'U' => {
result.push_str(&lookup_username(meta.uid()));
}
b'w' => {
result.push_str(&format_birth_time_for_path(path, dereference));
}
b'W' => {
result.push_str(&format_birth_seconds_for_path(path, dereference));
}
b'x' => {
result.push_str(&format_timestamp(st.st_atime, st.st_atime_nsec));
}
b'X' => {
let _ = write!(result, "{}", st.st_atime);
}
b'y' => {
result.push_str(&format_timestamp(st.st_mtime, st.st_mtime_nsec));
}
b'Y' => {
let _ = write!(result, "{}", st.st_mtime);
}
b'z' => {
result.push_str(&format_timestamp(st.st_ctime, st.st_ctime_nsec));
}
b'Z' => {
let _ = write!(result, "{}", st.st_ctime);
}
b'%' => {
result.push('%');
}
other => {
result.push('%');
result.push(other as char);
}
}
} else {
let b = bytes[i];
if b < 0x80 {
result.push(b as char);
} else {
let char_len = if b < 0xE0 {
2
} else if b < 0xF0 {
3
} else {
4
};
let end = (i + char_len).min(len);
if let Ok(s) = std::str::from_utf8(&bytes[i..end]) {
result.push_str(s);
i = end;
continue;
} else {
result.push(b as char);
}
}
}
i += 1;
}
result
}
fn format_fs_specifiers(fmt: &str, path: &str, sfs: &libc::statfs) -> String {
let mut result = String::new();
let bytes = fmt.as_bytes();
let len = bytes.len();
let mut i = 0;
let fsid_val = extract_fsid(&sfs.f_fsid);
while i < len {
if bytes[i] == b'%' && i + 1 < len {
i += 1;
match bytes[i] {
b'a' => {
let _ = write!(result, "{}", sfs.f_bavail);
}
b'b' => {
let _ = write!(result, "{}", sfs.f_blocks);
}
b'c' => {
let _ = write!(result, "{}", sfs.f_files);
}
b'd' => {
let _ = write!(result, "{}", sfs.f_ffree);
}
b'f' => {
let _ = write!(result, "{}", sfs.f_bfree);
}
b'i' => {
let _ = write!(result, "{:x}", fsid_val);
}
b'l' => {
#[cfg(target_os = "linux")]
let _ = write!(result, "{}", sfs.f_namelen);
#[cfg(not(target_os = "linux"))]
result.push_str("255");
}
b'n' => {
result.push_str(path);
}
b's' => {
let _ = write!(result, "{}", sfs.f_bsize);
}
b'S' => {
#[cfg(target_os = "linux")]
let _ = write!(result, "{}", sfs.f_frsize);
#[cfg(not(target_os = "linux"))]
let _ = write!(result, "{}", sfs.f_bsize);
}
b't' => {
#[cfg(target_os = "linux")]
let _ = write!(result, "{:x}", sfs.f_type);
#[cfg(not(target_os = "linux"))]
result.push('0');
}
b'T' => {
#[cfg(target_os = "linux")]
result.push_str(fs_type_name(sfs.f_type as u64));
#[cfg(not(target_os = "linux"))]
result.push_str("unknown");
}
b'%' => {
result.push('%');
}
other => {
result.push('%');
result.push(other as char);
}
}
} else {
let b = bytes[i];
if b < 0x80 {
result.push(b as char);
} else {
let char_len = if b < 0xE0 {
2
} else if b < 0xF0 {
3
} else {
4
};
let end = (i + char_len).min(len);
if let Ok(s) = std::str::from_utf8(&bytes[i..end]) {
result.push_str(s);
i = end;
continue;
} else {
result.push(b as char);
}
}
}
i += 1;
}
result
}
pub fn mode_to_human(mode: u32) -> String {
let file_char = match mode & (libc::S_IFMT as u32) {
m if m == libc::S_IFREG as u32 => '-',
m if m == libc::S_IFDIR as u32 => 'd',
m if m == libc::S_IFLNK as u32 => 'l',
m if m == libc::S_IFBLK as u32 => 'b',
m if m == libc::S_IFCHR as u32 => 'c',
m if m == libc::S_IFIFO as u32 => 'p',
m if m == libc::S_IFSOCK as u32 => 's',
_ => '?',
};
let mut s = String::with_capacity(10);
s.push(file_char);
s.push(if mode & 0o400 != 0 { 'r' } else { '-' });
s.push(if mode & 0o200 != 0 { 'w' } else { '-' });
s.push(if mode & (libc::S_ISUID as u32) != 0 {
if mode & 0o100 != 0 { 's' } else { 'S' }
} else if mode & 0o100 != 0 {
'x'
} else {
'-'
});
s.push(if mode & 0o040 != 0 { 'r' } else { '-' });
s.push(if mode & 0o020 != 0 { 'w' } else { '-' });
s.push(if mode & (libc::S_ISGID as u32) != 0 {
if mode & 0o010 != 0 { 's' } else { 'S' }
} else if mode & 0o010 != 0 {
'x'
} else {
'-'
});
s.push(if mode & 0o004 != 0 { 'r' } else { '-' });
s.push(if mode & 0o002 != 0 { 'w' } else { '-' });
s.push(if mode & (libc::S_ISVTX as u32) != 0 {
if mode & 0o001 != 0 { 't' } else { 'T' }
} else if mode & 0o001 != 0 {
'x'
} else {
'-'
});
s
}
pub fn file_type_label(mode: u32) -> &'static str {
match mode & (libc::S_IFMT as u32) {
m if m == libc::S_IFREG as u32 => "regular file",
m if m == libc::S_IFDIR as u32 => "directory",
m if m == libc::S_IFLNK as u32 => "symbolic link",
m if m == libc::S_IFBLK as u32 => "block special file",
m if m == libc::S_IFCHR as u32 => "character special file",
m if m == libc::S_IFIFO as u32 => "fifo",
m if m == libc::S_IFSOCK as u32 => "socket",
_ => "unknown",
}
}
fn format_timestamp(secs: i64, nsec: i64) -> String {
let t = secs as libc::time_t;
let mut tm: libc::tm = unsafe { std::mem::zeroed() };
unsafe {
libc::localtime_r(&t, &mut tm);
}
let offset_secs = tm.tm_gmtoff;
let offset_sign = if offset_secs >= 0 { '+' } else { '-' };
let offset_abs = offset_secs.unsigned_abs();
let offset_hours = offset_abs / 3600;
let offset_mins = (offset_abs % 3600) / 60;
let mut s = String::with_capacity(36);
let _ = write!(
s,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09} {}{:02}{:02}",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
nsec,
offset_sign,
offset_hours,
offset_mins
);
s
}
#[cfg(target_os = "linux")]
fn get_birth_time(path: &str, dereference: bool) -> Option<(i64, i64)> {
use std::mem::MaybeUninit;
let c_path = CString::new(path).ok()?;
unsafe {
let mut statx_buf: libc::statx = MaybeUninit::zeroed().assume_init();
let flags = if dereference {
0
} else {
libc::AT_SYMLINK_NOFOLLOW
};
let rc = libc::statx(
libc::AT_FDCWD,
c_path.as_ptr(),
flags,
libc::STATX_BTIME,
&mut statx_buf,
);
if rc == 0 && (statx_buf.stx_mask & libc::STATX_BTIME) != 0 {
Some((
statx_buf.stx_btime.tv_sec,
statx_buf.stx_btime.tv_nsec as i64,
))
} else {
None
}
}
}
#[cfg(not(target_os = "linux"))]
fn get_birth_time(_path: &str, _dereference: bool) -> Option<(i64, i64)> {
None
}
fn format_birth_time_for_path(path: &str, dereference: bool) -> String {
if let Some((secs, nsec)) = get_birth_time(path, dereference) {
format_timestamp(secs, nsec)
} else {
"-".to_string()
}
}
fn format_birth_seconds_for_path(path: &str, dereference: bool) -> String {
if let Some((secs, _nsec)) = get_birth_time(path, dereference) {
secs.to_string()
} else {
"0".to_string()
}
}
fn major(dev: u64) -> u64 {
((dev >> 8) & 0xff) | ((dev >> 32) & !0xffu64)
}
fn minor(dev: u64) -> u64 {
(dev & 0xff) | ((dev >> 12) & !0xffu64)
}
fn lookup_username(uid: u32) -> String {
unsafe {
let pw = libc::getpwuid(uid);
if pw.is_null() {
return uid.to_string();
}
let name = std::ffi::CStr::from_ptr((*pw).pw_name);
name.to_string_lossy().into_owned()
}
}
fn lookup_groupname(gid: u32) -> String {
unsafe {
let gr = libc::getgrgid(gid);
if gr.is_null() {
return gid.to_string();
}
let name = std::ffi::CStr::from_ptr((*gr).gr_name);
name.to_string_lossy().into_owned()
}
}
fn find_mount_point(path: &str) -> String {
use std::path::PathBuf;
let abs = match std::fs::canonicalize(path) {
Ok(p) => p,
Err(_) => PathBuf::from(path),
};
let mut current = abs.as_path();
let dev = match std::fs::metadata(current) {
Ok(m) => m.dev(),
Err(_) => return "/".to_string(),
};
loop {
match current.parent() {
Some(parent) => {
match std::fs::metadata(parent) {
Ok(pm) => {
if pm.dev() != dev {
return current.to_string_lossy().into_owned();
}
}
Err(_) => {
return current.to_string_lossy().into_owned();
}
}
current = parent;
}
None => {
return current.to_string_lossy().into_owned();
}
}
}
}
pub fn expand_backslash_escapes(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let bytes = s.as_bytes();
let len = bytes.len();
let mut i = 0;
while i < len {
if bytes[i] == b'\\' && i + 1 < len {
i += 1;
match bytes[i] {
b'n' => result.push('\n'),
b't' => result.push('\t'),
b'r' => result.push('\r'),
b'a' => result.push('\x07'),
b'b' => result.push('\x08'),
b'f' => result.push('\x0C'),
b'v' => result.push('\x0B'),
b'\\' => result.push('\\'),
b'"' => result.push('"'),
b'0' => {
let mut val: u32 = 0;
let mut count = 0;
while i + 1 < len && count < 3 {
let next = bytes[i + 1];
if next >= b'0' && next <= b'7' {
val = val * 8 + (next - b'0') as u32;
i += 1;
count += 1;
} else {
break;
}
}
if let Some(c) = char::from_u32(val) {
result.push(c);
}
}
other => {
result.push('\\');
result.push(other as char);
}
}
} else {
let b = bytes[i];
if b < 0x80 {
result.push(b as char);
} else {
let char_len = if b < 0xE0 {
2
} else if b < 0xF0 {
3
} else {
4
};
let end = (i + char_len).min(len);
if let Ok(s) = std::str::from_utf8(&bytes[i..end]) {
result.push_str(s);
i = end;
continue;
} else {
result.push(b as char);
}
}
}
i += 1;
}
result
}
fn fs_type_name(fs_type: u64) -> &'static str {
match fs_type {
0xEF53 => "ext2/ext3",
0x6969 => "nfs",
0x58465342 => "xfs",
0x2FC12FC1 => "zfs",
0x9123683E => "btrfs",
0x01021994 => "tmpfs",
0x28cd3d45 => "cramfs",
0x3153464a => "jfs",
0x52654973 => "reiserfs",
0x7275 => "romfs",
0x858458f6 => "ramfs",
0x73717368 => "squashfs",
0x62646576 => "devfs",
0x64626720 => "debugfs",
0x1cd1 => "devpts",
0xf15f => "ecryptfs",
0x794c7630 => "overlayfs",
0xFF534D42 => "cifs",
0xfe534d42 => "smb2",
0x137F => "minix",
0x4d44 => "msdos",
0x4006 => "fat",
0x65735546 => "fuse",
0x65735543 => "fusectl",
0x9fa0 => "proc",
0x62656572 => "sysfs",
0x27e0eb => "cgroup",
0x63677270 => "cgroup2",
0x19800202 => "mqueue",
0x50495045 => "pipefs",
0x74726163 => "tracefs",
0x68756773 => "hugetlbfs",
0xBAD1DEA => "futexfs",
0x5346544e => "ntfs",
0x00011954 => "ufs",
_ => "UNKNOWN",
}
}