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.
This commit is contained in:
Sergey Bugaev 2021-08-18 14:52:18 +03:00 committed by Andreas Kling
parent 4c126ea908
commit ad15d0c880
Notes: sideshowbarker 2024-07-18 05:31:52 +09:00

View file

@ -10,13 +10,15 @@
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
#include <LibCore/DirIterator.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@ -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[])