1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:07:45 +00:00

Userland: Also cache d_type in find(1)

Since readdir() actually gives us the file type, we don't have
to stat it again to know whether it's a directory! This means that
'find /' can process the majority of files without ever calling
stat() on them, simply by reading directories.

In addition, the TypeCommand (-t) can make use of this info too,
so, for instance, a 'find / -t d' does not need to stat anything
either.

This gives us a final huge speedup :^)
This commit is contained in:
Sergey Bugaev 2021-08-18 15:23:21 +03:00 committed by Andreas Kling
parent ed5b3f8495
commit 6c9343e262

View file

@ -47,6 +47,8 @@ struct FileData {
struct stat stat { struct stat stat {
}; };
bool stat_is_valid : 1 { false }; bool stat_is_valid : 1 { false };
// File type as returned from readdir(), or DT_UNKNOWN.
unsigned char d_type { DT_UNKNOWN };
const struct stat* ensure_stat() const struct stat* ensure_stat()
{ {
@ -62,6 +64,24 @@ struct FileData {
} }
stat_is_valid = true; stat_is_valid = true;
if (S_ISREG(stat.st_mode))
d_type = DT_REG;
else if (S_ISDIR(stat.st_mode))
d_type = DT_DIR;
else if (S_ISCHR(stat.st_mode))
d_type = DT_CHR;
else if (S_ISBLK(stat.st_mode))
d_type = DT_BLK;
else if (S_ISFIFO(stat.st_mode))
d_type = DT_FIFO;
else if (S_ISLNK(stat.st_mode))
d_type = DT_LNK;
else if (S_ISSOCK(stat.st_mode))
d_type = DT_SOCK;
else
VERIFY_NOT_REACHED();
return &stat; return &stat;
} }
}; };
@ -86,7 +106,7 @@ private:
} }
}; };
class TypeCommand final : public StatCommand { class TypeCommand final : public Command {
public: public:
TypeCommand(const char* arg) TypeCommand(const char* arg)
{ {
@ -97,24 +117,31 @@ public:
} }
private: private:
virtual bool evaluate(const struct stat& stat) const override virtual bool evaluate(FileData& file_data) const override
{ {
auto type = stat.st_mode; // First, make sure we have a type, but avoid calling
// sys$stat() unless we need to.
if (file_data.d_type == DT_UNKNOWN) {
if (file_data.ensure_stat() == nullptr)
return false;
}
auto type = file_data.d_type;
switch (m_type) { switch (m_type) {
case 'b': case 'b':
return S_ISBLK(type); return type == DT_BLK;
case 'c': case 'c':
return S_ISCHR(type); return type == DT_CHR;
case 'd': case 'd':
return S_ISDIR(type); return type == DT_DIR;
case 'l': case 'l':
return S_ISLNK(type); return type == DT_LNK;
case 'p': case 'p':
return S_ISFIFO(type); return type == DT_FIFO;
case 'f': case 'f':
return S_ISREG(type); return type == DT_REG;
case 's': case 's':
return S_ISSOCK(type); return type == DT_SOCK;
default: default:
// We've verified this is a correct character before. // We've verified this is a correct character before.
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
@ -481,12 +508,18 @@ static void walk_tree(FileData& root_data, Command& command)
// * This is a directory. // * This is a directory.
// * This is a symlink (that could point to a directory), // * This is a symlink (that could point to a directory),
// and we're following symlinks. // and we're following symlinks.
struct stat stat; // * The type is unknown, so it could be a directory.
int rc = fstatat(root_data.dirfd, root_data.basename, &stat, AT_SYMLINK_NOFOLLOW); switch (root_data.d_type) {
if (rc < 0) case DT_DIR:
case DT_UNKNOWN:
break;
case DT_LNK:
if (g_follow_symlinks)
break;
return; return;
if (!S_ISDIR(stat.st_mode) && (!g_follow_symlinks || !S_ISLNK(stat.st_mode))) default:
return; return;
}
int dirfd = openat(root_data.dirfd, root_data.basename, O_RDONLY | O_DIRECTORY | O_CLOEXEC); int dirfd = openat(root_data.dirfd, root_data.basename, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (dirfd < 0) { if (dirfd < 0) {
@ -517,6 +550,7 @@ static void walk_tree(FileData& root_data, Command& command)
dirent->d_name, dirent->d_name,
(struct stat) {}, (struct stat) {},
false, false,
dirent->d_type,
}; };
walk_tree(file_data, command); walk_tree(file_data, command);
} }
@ -547,6 +581,7 @@ int main(int argc, char* argv[])
basename.characters(), basename.characters(),
(struct stat) {}, (struct stat) {},
false, false,
DT_UNKNOWN,
}; };
auto command = parse_all_commands(argv); auto command = parse_all_commands(argv);
walk_tree(file_data, *command); walk_tree(file_data, *command);