From ad15d0c880e18d9a783f7e623569696bd10a2bac Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 18 Aug 2021 14:52:18 +0300 Subject: [PATCH] Userland: Ditch Core::DirIterator for readdir() in find(1) While Core::DirIterator is nice C++ API, we want more low-level control here. In particular, we want to open the directory using openat(), to also not pay the cost of re-traversing the path components we have already resolved previously. This gives us another nice speedup :^) Also, in the future commits this will allow us to make use of more data from the returned struct dirent, to speed things up even further. --- Userland/Utilities/find.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/Userland/Utilities/find.cpp b/Userland/Utilities/find.cpp index 7979efd447..7c26e77a79 100644 --- a/Userland/Utilities/find.cpp +++ b/Userland/Utilities/find.cpp @@ -10,13 +10,15 @@ #include #include #include -#include +#include #include #include #include #include #include +#include #include +#include #include #include @@ -459,28 +461,39 @@ static void walk_tree(const FileData& root_data, Command& command) { command.evaluate(root_data); - Core::DirIterator dir_iterator(root_data.full_path.string(), Core::DirIterator::SkipParentAndBaseDir); - if (dir_iterator.has_error() && dir_iterator.error() == ENOTDIR) + int dirfd = openat(root_data.dirfd, root_data.basename, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dirfd < 0 && errno == ENOTDIR) return; - while (dir_iterator.has_next()) { - String basename = dir_iterator.next_path(); + DIR* dir = fdopendir(dirfd); + + while (true) { + errno = 0; + auto* dirent = readdir(dir); + if (!dirent) + break; + + if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) + continue; + FileData file_data { - root_data.full_path.append(basename), - dir_iterator.fd(), - basename.characters(), + root_data.full_path.append(dirent->d_name), + dirfd, + dirent->d_name, }; struct stat stat; - if (g_follow_symlinks || fstatat(dir_iterator.fd(), basename.characters(), &stat, AT_SYMLINK_NOFOLLOW) < 0 || !S_ISLNK(stat.st_mode)) + if (g_follow_symlinks || fstatat(dirfd, dirent->d_name, &stat, AT_SYMLINK_NOFOLLOW) < 0 || !S_ISLNK(stat.st_mode)) walk_tree(file_data, command); else command.evaluate(file_data); } - if (dir_iterator.has_error()) { - warnln("{}: {}", root_data.full_path, dir_iterator.error_string()); + if (errno != 0) { + perror(root_data.full_path.string().characters()); g_there_was_an_error = true; } + + closedir(dir); } int main(int argc, char* argv[])