summaryrefslogtreecommitdiff
path: root/src/basic/stat-util.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-10-23 00:30:14 +0200
committerLennart Poettering <lennart@poettering.net>2021-10-25 21:51:37 +0200
commita068aceafbffcba85398cce636c25d659265087a (patch)
tree49fd3b3bb3de7030bade9b43752688d44e9c361b /src/basic/stat-util.c
parenta4e70ef7ba3b5e2c050cce5ed2bbf58807505596 (diff)
downloadsystemd-a068aceafbffcba85398cce636c25d659265087a.tar.gz
stat-util: optimize dir_is_empty_at() a bit, by using getdents64()
That way we have a single syscall only for it, instead of the multiple readdir() and friends do. And we can operate entirely on the stack, no malloc() implicit.
Diffstat (limited to 'src/basic/stat-util.c')
-rw-r--r--src/basic/stat-util.c20
1 files changed, 13 insertions, 7 deletions
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 56789b4878..900f06ad1c 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -73,27 +73,33 @@ int is_device_node(const char *path) {
int dir_is_empty_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -1;
- _cleanup_closedir_ DIR *d = NULL;
+ /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
+ * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
+ DEFINE_DIRENT_BUFFER(buffer, 3);
struct dirent *de;
+ ssize_t n;
if (path) {
fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else {
- /* Note that DUPing is not enough, as the internal pointer
- * would still be shared and moved by FOREACH_DIRENT. */
+ /* Note that DUPing is not enough, as the internal pointer would still be shared and moved
+ * getedents64(). */
+ assert(dir_fd >= 0);
+
fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return fd;
}
- d = take_fdopendir(&fd);
- if (!d)
+ n = getdents64(fd, &buffer, sizeof(buffer));
+ if (n < 0)
return -errno;
- FOREACH_DIRENT(de, d, return -errno)
- return 0;
+ FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
+ if (!dot_or_dot_dot(de->d_name))
+ return 0;
return 1;
}