From 29fb3a1a6074f68fd126536581c210796fe64a3e Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 5 Aug 2023 03:43:25 +0300 Subject: [PATCH] Utilities/ls: Add an option to print raw inode numbers This is only possible if listing an entire directory, because the LibC readdir function will return the raw inode number in each struct dirent, therefore allowing to print it as well. --- Base/usr/share/man/man1/ls.md | 11 +++++++ Userland/Utilities/ls.cpp | 62 ++++++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/Base/usr/share/man/man1/ls.md b/Base/usr/share/man/man1/ls.md index 9e003be630..64de02e068 100644 --- a/Base/usr/share/man/man1/ls.md +++ b/Base/usr/share/man/man1/ls.md @@ -27,6 +27,7 @@ If no *path* argument is provided the current working directory is used. * `-r`, `--reverse`: Reverse sort order * `-G`: Use pretty colors * `-i`, `--inode`: Show inode ids +* `-I`, `--raw-inode`: Show raw inode ids if possible (see Notes to understand when this will not work) * `-n`, `--numeric-uid-gid`: In long format, display numeric UID/GID * `-o`, In long format, do not show group information * `-h`, `--human-readable`: Print human-readable sizes @@ -54,5 +55,15 @@ $ ls /etc $ ls -la /etc ``` +## Notes + +Printing raw inode numbers is only possible when listing an entire directory. +This happens because the program uses the LibC `readdir` function, which +will provide the raw inode numbers as they're appearing "on disk". +In other cases, when strictly using the LibC `lstat` function the kernel +will resolve the inode number with respect to the mount table, so if there +is a mounted filesystem on a directory entry, `lstat` will give the root +inode number for that filesystem. + ## See also * [`tree`(1)](help://man/1/tree) to show the contents of the directory and subdirectories in a tree visualization diff --git a/Userland/Utilities/ls.cpp b/Userland/Utilities/ls.cpp index d4d3ff87a8..7b4834f266 100644 --- a/Userland/Utilities/ls.cpp +++ b/Userland/Utilities/ls.cpp @@ -38,6 +38,7 @@ struct FileMetadata { DeprecatedString name; DeprecatedString path; + ino_t raw_inode_number; struct stat stat { }; }; @@ -57,6 +58,7 @@ static bool flag_show_almost_all_dotfiles = false; static bool flag_ignore_backups = false; static bool flag_list_directories_only = false; static bool flag_show_inode = false; +static bool flag_show_raw_inode = false; static bool flag_print_numeric = false; static bool flag_hide_group = false; static bool flag_human_readable = false; @@ -111,6 +113,7 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(flag_classify, "Append a file type indicator to entries", "classify", 'F'); args_parser.add_option(flag_colorize, "Use pretty colors", nullptr, 'G'); args_parser.add_option(flag_show_inode, "Show inode ids", "inode", 'i'); + args_parser.add_option(flag_show_raw_inode, "Show raw inode ids if possible", "raw-inode", 'I'); args_parser.add_option(flag_print_numeric, "In long format, display numeric UID/GID", "numeric-uid-gid", 'n'); args_parser.add_option(flag_hide_group, "In long format, do not show group information", nullptr, 'o'); args_parser.add_option(flag_human_readable, "Print human-readable sizes", "human-readable", 'h'); @@ -146,7 +149,7 @@ ErrorOr serenity_main(Main::Arguments arguments) Vector files; for (auto& path : paths) { - FileMetadata metadata; + FileMetadata metadata {}; metadata.name = path; int rc = lstat(DeprecatedString(path).characters(), &metadata.stat); @@ -300,10 +303,16 @@ static size_t print_name(const struct stat& st, DeprecatedString const& name, Op return nprinted; } -static bool print_filesystem_object(DeprecatedString const& path, DeprecatedString const& name, const struct stat& st) +static bool print_filesystem_object(DeprecatedString const& path, DeprecatedString const& name, const struct stat& st, Optional raw_inode_number) { - if (flag_show_inode) + if (flag_show_inode) { printf("%s ", DeprecatedString::formatted("{}", st.st_ino).characters()); + } else if (flag_show_raw_inode) { + if (raw_inode_number.has_value()) + printf("%s ", DeprecatedString::formatted("{}", raw_inode_number.value()).characters()); + else + printf("n/a "); + } if (S_ISDIR(st.st_mode)) printf("d"); @@ -375,6 +384,11 @@ static bool print_filesystem_object(DeprecatedString const& path, DeprecatedStri return true; } +static bool print_filesystem_metadata_object(FileMetadata const& file) +{ + return print_filesystem_object(file.path, file.name, file.stat, file.raw_inode_number); +} + static int do_file_system_object_long(DeprecatedString const& path) { if (flag_list_directories_only) { @@ -383,7 +397,9 @@ static int do_file_system_object_long(DeprecatedString const& path) int rc = lstat(path.characters(), &stat); if (rc < 0) perror("lstat"); - if (print_filesystem_object(path, path, stat)) + if (flag_show_raw_inode) + fprintf(stderr, "warning: can't print raw inode numbers\n"); + if (print_filesystem_object(path, path, stat, {})) return 0; return 2; } @@ -404,7 +420,9 @@ static int do_file_system_object_long(DeprecatedString const& path) int rc = lstat(path.characters(), &stat); if (rc < 0) perror("lstat"); - if (print_filesystem_object(path, path, stat)) + if (flag_show_raw_inode) + fprintf(stderr, "warning: can't print raw inode numbers\n"); + if (print_filesystem_object(path, path, stat, {})) return 0; return 2; } @@ -414,8 +432,10 @@ static int do_file_system_object_long(DeprecatedString const& path) Vector files; while (di.has_next()) { - FileMetadata metadata; - metadata.name = di.next_path(); + auto dirent = di.next().value(); + FileMetadata metadata {}; + metadata.name = dirent.name; + metadata.raw_inode_number = dirent.inode_number; VERIFY(!metadata.name.is_empty()); if (metadata.name.ends_with('~') && flag_ignore_backups && metadata.name != path) @@ -437,13 +457,13 @@ static int do_file_system_object_long(DeprecatedString const& path) quick_sort(files, filemetadata_comparator); for (auto& file : files) { - if (!print_filesystem_object(file.path, file.name, file.stat)) + if (!print_filesystem_metadata_object(file)) return 2; } return 0; } -static bool print_filesystem_object_short(DeprecatedString const& path, char const* name, size_t* nprinted) +static bool print_filesystem_object_short(DeprecatedString const& path, char const* name, Optional raw_inode_number, size_t* nprinted) { struct stat st; int rc = lstat(path.characters(), &st); @@ -452,8 +472,14 @@ static bool print_filesystem_object_short(DeprecatedString const& path, char con return false; } - if (flag_show_inode) + if (flag_show_inode) { printf("%s ", DeprecatedString::formatted("{}", st.st_ino).characters()); + } else if (flag_show_raw_inode) { + if (raw_inode_number.has_value()) + printf("%s ", DeprecatedString::formatted("{}", raw_inode_number.value()).characters()); + else + printf("n/a "); + } *nprinted = print_name(st, name, {}, path); return true; @@ -469,7 +495,7 @@ static bool print_names(char const* path, size_t longest_name, Vector longest_name) @@ -496,8 +522,10 @@ static bool print_names(char const* path, size_t longest_name, Vector files; size_t longest_name = 0; while (di.has_next()) { - FileMetadata metadata; - metadata.name = di.next_path(); + auto dirent = di.next().value(); + FileMetadata metadata {}; + metadata.name = dirent.name; + metadata.raw_inode_number = dirent.inode_number; if (metadata.name.ends_with('~') && flag_ignore_backups && metadata.name != path) continue;