From: Lennart Poettering Date: Fri, 22 Oct 2021 22:30:14 +0000 (+0200) Subject: stat-util: optimize dir_is_empty_at() a bit, by using getdents64() X-Git-Tag: v250-rc1~426^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a068aceafbffcba85398cce636c25d659265087a;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index 768cc1de61e..a6272f891f5 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -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 diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 56789b48785..900f06ad1c7 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; }