From 4c126ea9085448a290a75ec5143fa21639c37987 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 18 Aug 2021 14:42:45 +0300 Subject: [PATCH] Userland: Use fstatat() in find(1) This speeds things up noticeably :^) The idea here is that a directory fd is a way to hold onto the results of path resolution that the kernel has already done for us. This way we don't ask the kernel to resolve the same parent directories over and over. --- Userland/Utilities/find.cpp | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/Userland/Utilities/find.cpp b/Userland/Utilities/find.cpp index 8afaebe47e..7979efd447 100644 --- a/Userland/Utilities/find.cpp +++ b/Userland/Utilities/find.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,10 @@ template struct FileData { // Full path to the file; either absolute or relative to cwd. LexicalPath full_path; + // The parent directory of the file. + int dirfd { -1 }; + // The file's basename, relative to the directory. + const char* basename { nullptr }; }; class Command { @@ -52,8 +57,8 @@ private: virtual bool evaluate(const FileData& file_data) const override { struct stat stat; - auto stat_func = g_follow_symlinks ? ::stat : ::lstat; - int rc = stat_func(file_data.full_path.string().characters(), &stat); + int flags = g_follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; + int rc = fstatat(file_data.dirfd, file_data.basename, &stat, flags); if (rc < 0) { perror(file_data.full_path.string().characters()); g_there_was_an_error = true; @@ -459,10 +464,14 @@ static void walk_tree(const FileData& root_data, Command& command) return; while (dir_iterator.has_next()) { - LexicalPath path { dir_iterator.next_full_path() }; - FileData file_data { path }; + String basename = dir_iterator.next_path(); + FileData file_data { + root_data.full_path.append(basename), + dir_iterator.fd(), + basename.characters(), + }; struct stat stat; - if (g_follow_symlinks || ::lstat(path.string().characters(), &stat) < 0 || !S_ISLNK(stat.st_mode)) + if (g_follow_symlinks || fstatat(dir_iterator.fd(), basename.characters(), &stat, AT_SYMLINK_NOFOLLOW) < 0 || !S_ISLNK(stat.st_mode)) walk_tree(file_data, command); else command.evaluate(file_data); @@ -477,8 +486,22 @@ static void walk_tree(const FileData& root_data, Command& command) int main(int argc, char* argv[]) { LexicalPath root_path(parse_options(argc, argv)); - FileData file_data { root_path }; + String dirname = root_path.dirname(); + String basename = root_path.basename(); + + int dirfd = open(dirname.characters(), O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dirfd < 0) { + perror(dirname.characters()); + return 1; + } + + FileData file_data { + root_path, + dirfd, + basename.characters(), + }; auto command = parse_all_commands(argv); walk_tree(file_data, *command); + close(dirfd); return g_there_was_an_error ? 1 : 0; }