]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stat-util: optimize dir_is_empty_at() a bit, by using getdents64()
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Oct 2021 22:30:14 +0000 (00:30 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 25 Oct 2021 19:51:37 +0000 (21:51 +0200)
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.

src/basic/dirent-util.h
src/basic/stat-util.c

index 768cc1de61e1ffd9c9b95748bdb889c7bb109f65..a6272f891f5582d1da2303f67badec28cd70394f 100644 (file)
@@ -56,3 +56,9 @@ assert_cc(sizeof_field(struct dirent, d_name) == sizeof_field(struct dirent64, d
         for (void *_end = (uint8_t*) ({ (de) = (buf); }) + (sz);        \
              (uint8_t*) (de) < (uint8_t*) _end;                         \
              (de) = (struct dirent*) ((uint8_t*) (de) + (de)->d_reclen))
+
+#define DEFINE_DIRENT_BUFFER(name, sz)                                  \
+        union {                                                         \
+                struct dirent de;                                       \
+                uint8_t data[(sz) * DIRENT_SIZE_MAX];                   \
+        } name
index 56789b4878555d62beed4ad7b3b9884f0015af94..900f06ad1c74acd3121ab1ee27cd540987c90ce9 100644 (file)
@@ -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;
 }