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;