From 9463fdd30d392c98de7b5712d30dfbaeada40e99 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 25 Jan 2023 16:14:11 -0500 Subject: Replace license boilerplate with SPDX tags And while I'm at it, remove years from copyright declarations. Link: https://spdx.dev/about/ Link: https://daniel.haxx.se/blog/2023/01/08/copyright-without-years/ --- src/dir.h | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'src/dir.h') diff --git a/src/dir.h b/src/dir.h index 69344c6..01eaaba 100644 --- a/src/dir.h +++ b/src/dir.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Directories and their contents. -- cgit v1.2.3 From 0c10a1e984bc2bed1fe3a7dadbd94a9c24139a91 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 11 Apr 2023 22:06:42 -0400 Subject: dir: Add a flag to bfs_freedir() to force the fd to stay the same --- src/bftw.c | 4 ++-- src/dir.c | 18 +++++++++++++----- src/dir.h | 10 ++++++++-- src/ioq.c | 1 + 4 files changed, 24 insertions(+), 9 deletions(-) (limited to 'src/dir.h') diff --git a/src/bftw.c b/src/bftw.c index 916a56b..a3e8d9b 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -853,13 +853,13 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { if (file->refcount > 1) { // Keep the fd around if any subdirectories exist - file->fd = bfs_freedir(state->dir); + file->fd = bfs_freedir(state->dir, false); } else { - bfs_closedir(state->dir); file->fd = -1; } if (file->fd < 0) { + bfs_closedir(state->dir); bftw_cache_remove(&state->cache, file); } } diff --git a/src/dir.c b/src/dir.c index 01d26db..126f473 100644 --- a/src/dir.c +++ b/src/dir.c @@ -243,13 +243,15 @@ int bfs_closedir(struct bfs_dir *dir) { int ret = xclose(dir->fd); #else int ret = closedir(dir->dir); - bfs_verify(ret == 0 || errno != EBADF); + if (ret != 0) { + bfs_verify(errno != EBADF); + } #endif free(dir); return ret; } -int bfs_freedir(struct bfs_dir *dir) { +int bfs_freedir(struct bfs_dir *dir, bool same_fd) { #if BFS_GETDENTS int ret = dir->fd; free(dir); @@ -257,10 +259,16 @@ int bfs_freedir(struct bfs_dir *dir) { int ret = fdclosedir(dir->dir); free(dir); #else + if (same_fd) { + errno = ENOTSUP; + return -1; + } + int ret = dup_cloexec(dirfd(dir->dir)); - int error = errno; - bfs_closedir(dir); - errno = error; + if (ret >= 0) { + bfs_closedir(dir); + } #endif + return ret; } diff --git a/src/dir.h b/src/dir.h index 01eaaba..e0fe913 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,6 +8,7 @@ #ifndef BFS_DIR_H #define BFS_DIR_H +#include "config.h" #include /** @@ -103,9 +104,14 @@ int bfs_closedir(struct bfs_dir *dir); * * @param dir * The directory to free. + * @param same_fd + * If true, require that the returned file descriptor is the same one + * that bfs_dirfd() would have returned. Otherwise, it may be a new + * file descriptor for the same directory. * @return - * The file descriptor on success, or -1 on failure. + * On success, a file descriptor for the directory is returned. On + * failure, -1 is returned, and the directory remains open. */ -int bfs_freedir(struct bfs_dir *dir); +int bfs_freedir(struct bfs_dir *dir, bool same_fd); #endif // BFS_DIR_H diff --git a/src/ioq.c b/src/ioq.c index e09c2a9..1b61fb0 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -155,6 +155,7 @@ static void *ioq_work(void *ptr) { sanitize_uninit(cmd); struct ioq_res *res = &cmd->res; + res->ptr = req.ptr; res->dir = bfs_opendir(req.dfd, req.path); res->error = errno; ioqq_push(ioq->ready, cmd); -- cgit v1.2.3 From 6719107e95a41b9ef241bdaa272eed505865710c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 Mar 2023 13:59:48 -0400 Subject: dir: New bfs_polldir() function for directory readahead --- src/dir.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++---------------- src/dir.h | 10 ++++++++ src/ioq.c | 4 +++ 3 files changed, 80 insertions(+), 22 deletions(-) (limited to 'src/dir.h') diff --git a/src/dir.c b/src/dir.c index 126f473..b9fd74d 100644 --- a/src/dir.c +++ b/src/dir.c @@ -107,7 +107,10 @@ struct bfs_dir { // sys_dirent buf[]; #else DIR *dir; + struct dirent *de; #endif + + bool eof; }; #if BFS_GETDENTS @@ -152,8 +155,10 @@ struct bfs_dir *bfs_opendir(int at_fd, const char *at_path) { free(dir); return NULL; } + dir->de = NULL; #endif + dir->eof = false; return dir; } @@ -165,38 +170,52 @@ int bfs_dirfd(const struct bfs_dir *dir) { #endif } -/** Convert de->d_type to a bfs_type, if it exists. */ -static enum bfs_type bfs_d_type(const sys_dirent *de) { -#ifdef DTTOIF - return bfs_mode_to_type(DTTOIF(de->d_type)); -#else - return BFS_UNKNOWN; -#endif -} - -/** Read a single directory entry. */ -static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { +int bfs_polldir(struct bfs_dir *dir) { #if BFS_GETDENTS + if (dir->pos < dir->size) { + return 1; + } else if (dir->eof) { + return 0; + } + char *buf = (char *)(dir + 1); + ssize_t size = bfs_getdents(dir->fd, buf, BUF_SIZE); + if (size == 0) { + dir->eof = true; + return 0; + } else if (size < 0) { + return -1; + } - if (dir->pos >= dir->size) { - ssize_t ret = bfs_getdents(dir->fd, buf, BUF_SIZE); - if (ret <= 0) { - return ret; + dir->pos = 0; + dir->size = size; + + // Like read(), getdents() doesn't indicate EOF until another call returns zero. + // Check that eagerly here to hopefully avoid a syscall in the last bfs_readdir(). + size_t rest = BUF_SIZE - size; + if (rest >= sizeof(sys_dirent)) { + size = bfs_getdents(dir->fd, buf + size, rest); + if (size > 0) { + dir->size += size; + } else if (size == 0) { + dir->eof = true; } - dir->pos = 0; - dir->size = ret; } - *de = (void *)(buf + dir->pos); - dir->pos += (*de)->d_reclen; return 1; -#else +#else // !BFS_GETDENTS + if (dir->de) { + return 1; + } else if (dir->eof) { + return 0; + } + errno = 0; - *de = readdir(dir->dir); - if (*de) { + dir->de = readdir(dir->dir); + if (dir->de) { return 1; } else if (errno == 0) { + dir->eof = true; return 0; } else { return -1; @@ -204,6 +223,22 @@ static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { #endif } +/** Read a single directory entry. */ +static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { + int ret = bfs_polldir(dir); + if (ret > 0) { +#if BFS_GETDENTS + char *buf = (char *)(dir + 1); + *de = (const sys_dirent *)(buf + dir->pos); + dir->pos += (*de)->d_reclen; +#else + *de = dir->de; + dir->de = NULL; +#endif + } + return ret; +} + /** Skip ".", "..", and deleted/empty dirents. */ static bool skip_dirent(const sys_dirent *de) { #if __FreeBSD__ @@ -217,6 +252,15 @@ static bool skip_dirent(const sys_dirent *de) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } +/** Convert de->d_type to a bfs_type, if it exists. */ +static enum bfs_type bfs_d_type(const sys_dirent *de) { +#ifdef DTTOIF + return bfs_mode_to_type(DTTOIF(de->d_type)); +#else + return BFS_UNKNOWN; +#endif +} + int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de) { while (true) { const sys_dirent *sysde; diff --git a/src/dir.h b/src/dir.h index e0fe913..6fe7ae2 100644 --- a/src/dir.h +++ b/src/dir.h @@ -79,6 +79,16 @@ struct bfs_dir *bfs_opendir(int at_fd, const char *at_path); */ int bfs_dirfd(const struct bfs_dir *dir); +/** + * Performs any I/O necessary for the next bfs_readdir() call. + * + * @param dir + * The directory to poll. + * @return + * 1 on success, 0 on EOF, or -1 on failure. + */ +int bfs_polldir(struct bfs_dir *dir); + /** * Read a directory entry. * diff --git a/src/ioq.c b/src/ioq.c index 1b61fb0..74d2e09 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -158,6 +158,10 @@ static void *ioq_work(void *ptr) { res->ptr = req.ptr; res->dir = bfs_opendir(req.dfd, req.path); res->error = errno; + if (res->dir) { + bfs_polldir(res->dir); + } + ioqq_push(ioq->ready, cmd); } -- cgit v1.2.3 From a1490d98a1aebb3bfbd3873613977d0341ec7f98 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 20 Jun 2023 12:02:08 -0400 Subject: dir: Arena-allocate directories --- src/bftw.c | 37 +++++++++++++++++++++++++++++-------- src/dir.c | 44 +++++++++++++++++++++++--------------------- src/dir.h | 27 +++++++++++++++++++++++---- src/eval.c | 34 ++++++++++++++++++++++------------ src/ioq.c | 15 ++++++++++----- src/ioq.h | 4 +++- 6 files changed, 110 insertions(+), 51 deletions(-) (limited to 'src/dir.h') diff --git a/src/bftw.c b/src/bftw.c index d0491e0..69e41a2 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -103,6 +103,8 @@ struct bftw_cache { /** bftw_file arena. */ struct varena files; + /** bfs_dir arena. */ + struct arena dirs; }; /** Initialize a cache. */ @@ -111,6 +113,7 @@ static void bftw_cache_init(struct bftw_cache *cache, size_t capacity) { cache->target = NULL; cache->capacity = capacity; VARENA_INIT(&cache->files, struct bftw_file, name); + bfs_dir_arena(&cache->dirs); } /** Remove a bftw_file from the LRU list. */ @@ -136,6 +139,7 @@ static void bftw_file_close(struct bftw_cache *cache, struct bftw_file *file) { if (file->dir) { bfs_assert(file->fd == bfs_dirfd(file->dir)); bfs_closedir(file->dir); + arena_free(&cache->dirs, file->dir); file->dir = NULL; } else { xclose(file->fd); @@ -157,9 +161,10 @@ static void bftw_file_freedir(struct bftw_cache *cache, struct bftw_file *file) bool pinned = file->pincount > 0; if (reffed || pinned) { - int fd = bfs_freedir(file->dir, pinned); + int fd = bfs_fdclosedir(file->dir, pinned); if (fd >= 0) { file->fd = fd; + arena_free(&cache->dirs, file->dir); file->dir = NULL; } } else { @@ -243,6 +248,7 @@ static void bftw_cache_destroy(struct bftw_cache *cache) { bfs_assert(!cache->target); varena_destroy(&cache->files); + arena_destroy(&cache->dirs); } /** Create a new bftw_file. */ @@ -410,10 +416,17 @@ static struct bfs_dir *bftw_file_opendir(struct bftw_cache *cache, struct bftw_f return NULL; } - struct bfs_dir *dir = bfs_opendir(fd, NULL); - if (dir) { - bftw_file_set_dir(cache, file, dir); + struct bfs_dir *dir = arena_alloc(&cache->dirs); + if (!dir) { + return NULL; + } + + if (bfs_opendir(dir, fd, NULL) != 0) { + arena_free(&cache->dirs, dir); + return NULL; } + + bftw_file_set_dir(cache, file, dir); return dir; } @@ -900,14 +913,18 @@ static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) { goto unpin; } } - --cache->capacity; - if (ioq_opendir(state->ioq, dfd, file->name, file) != 0) { - ++cache->capacity; + struct bfs_dir *dir = arena_alloc(&state->cache.dirs); + if (!dir) { goto unpin; } + if (ioq_opendir(state->ioq, dir, dfd, file->name, file) != 0) { + goto free; + } + file->ioqueued = true; + --cache->capacity; if (state->flags & BFTW_SORT) { goto append; @@ -915,6 +932,8 @@ static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) { return; } +free: + arena_free(&state->cache.dirs, dir); unpin: if (file->parent) { bftw_cache_unpin(cache, file->parent); @@ -950,7 +969,9 @@ static int bftw_ioq_pop(struct bftw_state *state, bool block) { bftw_cache_unpin(cache, file->parent); } - if (res->dir) { + if (res->error) { + arena_free(&state->cache.dirs, res->dir); + } else { bftw_file_set_dir(cache, file, res->dir); } diff --git a/src/dir.c b/src/dir.c index b9fd74d..a24b572 100644 --- a/src/dir.c +++ b/src/dir.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "dir.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "diag.h" @@ -120,26 +121,26 @@ struct bfs_dir { # define DIR_SIZE sizeof(struct bfs_dir) #endif -struct bfs_dir *bfs_opendir(int at_fd, const char *at_path) { - struct bfs_dir *dir = malloc(DIR_SIZE); - if (!dir) { - return NULL; - } +struct bfs_dir *bfs_allocdir(void) { + return malloc(DIR_SIZE); +} + +void bfs_dir_arena(struct arena *arena) { + arena_init(arena, alignof(struct bfs_dir), DIR_SIZE); +} +int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path) { int fd; if (at_path) { fd = openat(at_fd, at_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (fd < 0) { + return -1; + } } else if (at_fd >= 0) { fd = at_fd; } else { - free(dir); errno = EBADF; - return NULL; - } - - if (fd < 0) { - free(dir); - return NULL; + return -1; } #if BFS_GETDENTS @@ -152,14 +153,13 @@ struct bfs_dir *bfs_opendir(int at_fd, const char *at_path) { if (at_path) { close_quietly(fd); } - free(dir); - return NULL; + return -1; } dir->de = NULL; #endif dir->eof = false; - return dir; + return 0; } int bfs_dirfd(const struct bfs_dir *dir) { @@ -291,17 +291,16 @@ int bfs_closedir(struct bfs_dir *dir) { bfs_verify(errno != EBADF); } #endif - free(dir); + + sanitize_uninit(dir, DIR_SIZE); return ret; } -int bfs_freedir(struct bfs_dir *dir, bool same_fd) { +int bfs_fdclosedir(struct bfs_dir *dir, bool same_fd) { #if BFS_GETDENTS int ret = dir->fd; - free(dir); #elif __FreeBSD__ int ret = fdclosedir(dir->dir); - free(dir); #else if (same_fd) { errno = ENOTSUP; @@ -309,10 +308,13 @@ int bfs_freedir(struct bfs_dir *dir, bool same_fd) { } int ret = dup_cloexec(dirfd(dir->dir)); - if (ret >= 0) { - bfs_closedir(dir); + if (ret < 0) { + return -1; } + + bfs_closedir(dir); #endif + sanitize_uninit(dir, DIR_SIZE); return ret; } diff --git a/src/dir.h b/src/dir.h index 6fe7ae2..16f592e 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,6 +8,7 @@ #ifndef BFS_DIR_H #define BFS_DIR_H +#include "alloc.h" #include "config.h" #include @@ -61,18 +62,36 @@ struct bfs_dirent { const char *name; }; +/** + * Allocate space for a directory. + * + * @return + * An allocated, unopen directory, or NULL on failure. + */ +struct bfs_dir *bfs_allocdir(void); + +/** + * Initialize an arena for directories. + * + * @param arena + * The arena to initialize. + */ +void bfs_dir_arena(struct arena *arena); + /** * Open a directory. * + * @param dir + * The allocated directory. * @param at_fd * The base directory for path resolution. * @param at_path * The path of the directory to open, relative to at_fd. Pass NULL to * open at_fd itself. * @return - * The opened directory, or NULL on failure. + * 0 on success, or -1 on failure. */ -struct bfs_dir *bfs_opendir(int at_fd, const char *at_path); +int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path); /** * Get the file descriptor for a directory. @@ -110,7 +129,7 @@ int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de); int bfs_closedir(struct bfs_dir *dir); /** - * Free a directory, keeping an open file descriptor to it. + * Extract the file descriptor from an open directory. * * @param dir * The directory to free. @@ -122,6 +141,6 @@ int bfs_closedir(struct bfs_dir *dir); * On success, a file descriptor for the directory is returned. On * failure, -1 is returned, and the directory remains open. */ -int bfs_freedir(struct bfs_dir *dir, bool same_fd); +int bfs_fdclosedir(struct bfs_dir *dir, bool same_fd); #endif // BFS_DIR_H diff --git a/src/eval.c b/src/eval.c index f16821e..5f27681 100644 --- a/src/eval.c +++ b/src/eval.c @@ -421,10 +421,15 @@ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { const struct BFTW *ftwbuf = state->ftwbuf; if (ftwbuf->type == BFS_DIR) { - struct bfs_dir *dir = bfs_opendir(ftwbuf->at_fd, ftwbuf->at_path); + struct bfs_dir *dir = bfs_allocdir(); if (!dir) { eval_report_error(state); - goto done; + return ret; + } + + if (bfs_opendir(dir, ftwbuf->at_fd, ftwbuf->at_path) != 0) { + eval_report_error(state); + return ret; } int did_read = bfs_readdir(dir, NULL); @@ -435,6 +440,7 @@ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { } bfs_closedir(dir); + free(dir); } else if (ftwbuf->type == BFS_REG) { const struct bfs_stat *statbuf = eval_stat(state); if (statbuf) { @@ -442,7 +448,6 @@ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { } } -done: return ret; } @@ -1495,20 +1500,25 @@ static int infer_fdlimit(const struct bfs_ctx *ctx, int limit) { // Check /proc/self/fd for the current number of open fds, if possible // (we may have inherited more than just the standard ones) - struct bfs_dir *dir = bfs_opendir(AT_FDCWD, "/proc/self/fd"); + struct bfs_dir *dir = bfs_allocdir(); if (!dir) { - dir = bfs_opendir(AT_FDCWD, "/dev/fd"); + goto done; } - if (dir) { - // Account for 'dir' itself - nopen = -1; - while (bfs_readdir(dir, NULL) > 0) { - ++nopen; - } + if (bfs_opendir(dir, AT_FDCWD, "/proc/self/fd") != 0 + && bfs_opendir(dir, AT_FDCWD, "/dev/fd") != 0) { + goto done; + } - bfs_closedir(dir); + // Account for 'dir' itself + nopen = -1; + + while (bfs_readdir(dir, NULL) > 0) { + ++nopen; } + bfs_closedir(dir); +done: + free(dir); int ret = limit - nopen; ret -= ctx->expr->persistent_fds; diff --git a/src/ioq.c b/src/ioq.c index 33316fa..47b082a 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -20,9 +20,11 @@ * An I/O queue request. */ struct ioq_req { + /** Directory allocation. */ + struct bfs_dir *dir; /** Base file descriptor for openat(). */ int dfd; - /** Relative path to dfd. */ + /** Path to open, relative to dfd. */ const char *path; /** Arbitrary user data. */ @@ -295,10 +297,12 @@ static void *ioq_work(void *ptr) { struct ioq_res *res = &cmd->res; res->ptr = req.ptr; - res->dir = bfs_opendir(req.dfd, req.path); - res->error = errno; - if (res->dir) { + res->dir = req.dir; + res->error = 0; + if (bfs_opendir(req.dir, req.dfd, req.path) == 0) { bfs_polldir(res->dir); + } else { + res->error = errno; } ioqq_push(ioq->ready, cmd); @@ -344,7 +348,7 @@ fail: return NULL; } -int ioq_opendir(struct ioq *ioq, int dfd, const char *path, void *ptr) { +int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, void *ptr) { if (ioq->size >= ioq->depth) { return -1; } @@ -355,6 +359,7 @@ int ioq_opendir(struct ioq *ioq, int dfd, const char *path, void *ptr) { } struct ioq_req *req = &cmd->req; + req->dir = dir; req->dfd = dfd; req->path = path; req->ptr = ptr; diff --git a/src/ioq.h b/src/ioq.h index 0af5779..50e02b1 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -45,6 +45,8 @@ struct ioq *ioq_create(size_t depth, size_t nthreads); * * @param ioq * The I/O queue. + * @param dir + * The allocated directory. * @param dfd * The base file descriptor. * @param path @@ -54,7 +56,7 @@ struct ioq *ioq_create(size_t depth, size_t nthreads); * @return * 0 on success, or -1 on failure. */ -int ioq_opendir(struct ioq *ioq, int dfd, const char *path, void *ptr); +int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, void *ptr); /** * Pop a response from the queue. -- cgit v1.2.3 From 739317470873422c1023eab22c570c374a10f498 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 4 Jul 2023 15:37:54 -0400 Subject: bftw: Try to close files asynchronously --- src/bftw.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++------------- src/dir.c | 20 ++----- src/dir.h | 30 +++++++---- 3 files changed, 163 insertions(+), 62 deletions(-) (limited to 'src/dir.h') diff --git a/src/bftw.c b/src/bftw.c index 64f221b..aec804b 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -149,29 +149,6 @@ static void bftw_file_close(struct bftw_cache *cache, struct bftw_file *file) { bftw_cache_remove(cache, file); } -/** Free an open directory. */ -static void bftw_file_freedir(struct bftw_cache *cache, struct bftw_file *file) { - if (!file->dir) { - return; - } - - // Try to keep an open fd if any children exist - bool reffed = file->refcount > 1; - // Keep the fd the same if it's pinned - bool pinned = file->pincount > 0; - - if (reffed || pinned) { - int fd = bfs_fdclosedir(file->dir, pinned); - if (fd >= 0) { - file->fd = fd; - arena_free(&cache->dirs, file->dir); - file->dir = NULL; - } - } else { - bftw_file_close(cache, file); - } -} - /** Pop the least recently used directory from the cache. */ static int bftw_cache_pop(struct bftw_cache *cache) { struct bftw_file *file = cache->tail; @@ -228,7 +205,6 @@ static void bftw_cache_unpin(struct bftw_cache *cache, struct bftw_file *file) { if (--file->pincount == 0) { bftw_lru_add(cache, file); - bftw_file_freedir(cache, file); } } @@ -911,7 +887,18 @@ static int bftw_ioq_pop(struct bftw_state *state, bool block) { struct bfs_dir *dir; enum ioq_op op = ent->op; - if (op == IOQ_OPENDIR) { + switch (op) { + case IOQ_CLOSE: + ++cache->capacity; + break; + + case IOQ_CLOSEDIR: + ++cache->capacity; + dir = ent->closedir.dir; + arena_free(&cache->dirs, dir); + break; + + case IOQ_OPENDIR: file = ent->ptr; file->ioqueued = false; @@ -930,6 +917,7 @@ static int bftw_ioq_pop(struct bftw_state *state, bool block) { if (!(state->flags & BFTW_SORT)) { SLIST_PREPEND(&state->dirs, file); } + break; } ioq_free(ioq, ent); @@ -964,6 +952,12 @@ static int bftw_cache_reserve(struct bftw_state *state) { return 0; } + while (bftw_ioq_pop(state, false) >= 0) { + if (cache->capacity > 0) { + return 0; + } + } + return bftw_cache_pop(cache); } @@ -1022,13 +1016,106 @@ append: SLIST_APPEND(&state->dirs, file); } +/** Close a directory, asynchronously if possible. */ +static int bftw_ioq_closedir(struct bftw_state *state, struct bfs_dir *dir) { + if (bftw_ioq_reserve(state) == 0) { + if (ioq_closedir(state->ioq, dir, NULL) == 0) { + return 0; + } + } + + struct bftw_cache *cache = &state->cache; + int ret = bfs_closedir(dir); + arena_free(&cache->dirs, dir); + ++cache->capacity; + return ret; +} + +/** Close a file descriptor, asynchronously if possible. */ +static int bftw_ioq_close(struct bftw_state *state, int fd) { + if (bftw_ioq_reserve(state) == 0) { + if (ioq_close(state->ioq, fd, NULL) == 0) { + return 0; + } + } + + struct bftw_cache *cache = &state->cache; + int ret = xclose(fd); + ++cache->capacity; + return ret; +} + +/** Close a file, asynchronously if possible. */ +static int bftw_close(struct bftw_state *state, struct bftw_file *file) { + bfs_assert(file->fd >= 0); + bfs_assert(file->pincount == 0); + + struct bfs_dir *dir = file->dir; + int fd = file->fd; + + bftw_lru_remove(&state->cache, file); + file->dir = NULL; + file->fd = -1; + + if (dir) { + return bftw_ioq_closedir(state, dir); + } else { + return bftw_ioq_close(state, fd); + } +} + +/** Free an open directory. */ +static int bftw_unwrapdir(struct bftw_state *state, struct bftw_file *file) { + struct bfs_dir *dir = file->dir; + if (!dir) { + return 0; + } + + struct bftw_cache *cache = &state->cache; + + // Try to keep an open fd if any children exist + bool reffed = file->refcount > 1; + // Keep the fd the same if it's pinned + bool pinned = file->pincount > 0; + +#if BFS_USE_UNWRAPDIR + if (reffed || pinned) { + bfs_unwrapdir(dir); + arena_free(&cache->dirs, dir); + file->dir = NULL; + return 0; + } +#else + if (pinned) { + return -1; + } +#endif + + if (!reffed) { + return bftw_close(state, file); + } + + if (bftw_cache_reserve(state) != 0) { + return -1; + } + + int fd = dup_cloexec(file->fd); + if (fd < 0) { + return -1; + } + --cache->capacity; + + file->dir = NULL; + file->fd = fd; + return bftw_ioq_closedir(state, dir); +} + /** Pop a directory to read from the queue. */ static bool bftw_pop_dir(struct bftw_state *state) { bfs_assert(!state->file); - bool have_dirs = state->dirs.head; + struct bftw_file *dir = state->dirs.head; bool have_files = state->files.head; - bool have_room = state->cache.capacity > 0; if (state->flags & BFTW_SORT) { // Keep strict breadth-first order when sorting @@ -1036,16 +1123,25 @@ static bool bftw_pop_dir(struct bftw_state *state) { return false; } } else { - // Block if we have no other files/dirs to visit, or no room in the cache - bool block = !(have_dirs || have_files) || !have_room; - bftw_ioq_pop(state, block); + while (!dir || !dir->dir) { + // Block if we have no other files/dirs to visit, or no room in the cache + bool have_room = state->cache.capacity > 0; + bool block = !(dir || have_files) || !have_room; + + if (bftw_ioq_pop(state, block) < 0) { + break; + } + dir = state->dirs.head; + } } - struct bftw_file *dir = state->file = SLIST_POP(&state->dirs); if (!dir) { return false; } + SLIST_POP(&state->dirs); + state->file = dir; + while (dir->ioqueued) { bftw_ioq_pop(state, true); } @@ -1132,7 +1228,7 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { if (state->dir) { bftw_cache_unpin(&state->cache, state->file); - bftw_file_freedir(&state->cache, state->file); + bftw_unwrapdir(state, state->file); } state->dir = NULL; state->de = NULL; @@ -1169,8 +1265,12 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { if (state->previous == file) { state->previous = parent; } - bftw_file_free(&state->cache, file); state->file = parent; + + if (file->fd >= 0) { + bftw_close(state, file); + } + bftw_file_free(&state->cache, file); } return ret; @@ -1185,8 +1285,11 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { static int bftw_state_destroy(struct bftw_state *state) { dstrfree(state->path); - if (state->ioq) { - ioq_cancel(state->ioq); + struct ioq *ioq = state->ioq; + if (ioq) { + ioq_cancel(ioq); + while (bftw_ioq_pop(state, true) >= 0); + state->ioq = NULL; } SLIST_EXTEND(&state->files, &state->batch); @@ -1194,7 +1297,7 @@ static int bftw_state_destroy(struct bftw_state *state) { bftw_gc(state, BFTW_VISIT_NONE); } while (bftw_pop_dir(state) || bftw_pop_file(state)); - ioq_destroy(state->ioq); + ioq_destroy(ioq); bftw_cache_destroy(&state->cache); diff --git a/src/dir.c b/src/dir.c index 685bac5..0304674 100644 --- a/src/dir.c +++ b/src/dir.c @@ -15,10 +15,6 @@ #include #include -#ifndef BFS_USE_GETDENTS -# define BFS_USE_GETDENTS (__linux__ || __FreeBSD__) -#endif - #if BFS_USE_GETDENTS # if __linux__ # include @@ -296,25 +292,15 @@ int bfs_closedir(struct bfs_dir *dir) { return ret; } -int bfs_fdclosedir(struct bfs_dir *dir, bool same_fd) { +#if BFS_USE_UNWRAPDIR +int bfs_unwrapdir(struct bfs_dir *dir) { #if BFS_USE_GETDENTS int ret = dir->fd; #elif __FreeBSD__ int ret = fdclosedir(dir->dir); -#else - if (same_fd) { - errno = ENOTSUP; - return -1; - } - - int ret = dup_cloexec(dirfd(dir->dir)); - if (ret < 0) { - return -1; - } - - bfs_closedir(dir); #endif sanitize_uninit(dir, DIR_SIZE); return ret; } +#endif diff --git a/src/dir.h b/src/dir.h index 16f592e..1137ff5 100644 --- a/src/dir.h +++ b/src/dir.h @@ -12,6 +12,14 @@ #include "config.h" #include +/** + * Whether the implementation uses the getdents() syscall directly, rather than + * libc's readdir(). + */ +#ifndef BFS_USE_GETDENTS +# define BFS_USE_GETDENTS (__linux__ || __FreeBSD__) +#endif + /** * A directory. */ @@ -129,18 +137,22 @@ int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de); int bfs_closedir(struct bfs_dir *dir); /** - * Extract the file descriptor from an open directory. + * Whether the bfs_unwrapdir() function is supported. + */ +#ifndef BFS_USE_UNWRAPDIR +# define BFS_USE_UNWRAPDIR (BFS_USE_GETDENTS || __FreeBSD__) +#endif + +#if BFS_USE_UNWRAPDIR +/** + * Detach the file descriptor from an open directory. * * @param dir - * The directory to free. - * @param same_fd - * If true, require that the returned file descriptor is the same one - * that bfs_dirfd() would have returned. Otherwise, it may be a new - * file descriptor for the same directory. + * The directory to detach. * @return - * On success, a file descriptor for the directory is returned. On - * failure, -1 is returned, and the directory remains open. + * The file descriptor of the directory. */ -int bfs_fdclosedir(struct bfs_dir *dir, bool same_fd); +int bfs_unwrapdir(struct bfs_dir *dir); +#endif #endif // BFS_DIR_H -- cgit v1.2.3 From 214a1f9215d33d4b9f34a3d258da1e1f4e3eb01f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 16 Oct 2023 16:44:46 -0400 Subject: dir: Add a flags parameter to bfs_opendir() --- src/bftw.c | 4 ++-- src/dir.c | 31 ++++++++++++++++++++----------- src/dir.h | 12 +++++++++++- src/eval.c | 6 +++--- src/ioq.c | 7 ++++--- src/ioq.h | 6 +++++- 6 files changed, 45 insertions(+), 21 deletions(-) (limited to 'src/dir.h') diff --git a/src/bftw.c b/src/bftw.c index 06a0085..e2f1a66 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -856,7 +856,7 @@ static int bftw_ioq_opendir(struct bftw_state *state, struct bftw_file *file) { goto unpin; } - if (ioq_opendir(state->ioq, dir, dfd, file->name, file) != 0) { + if (ioq_opendir(state->ioq, dir, dfd, file->name, 0, file) != 0) { goto free; } @@ -1018,7 +1018,7 @@ static struct bfs_dir *bftw_file_opendir(struct bftw_state *state, struct bftw_f return NULL; } - if (bfs_opendir(dir, fd, NULL) != 0) { + if (bfs_opendir(dir, fd, NULL, 0) != 0) { bftw_freedir(cache, dir); return NULL; } diff --git a/src/dir.c b/src/dir.c index a7423e9..dee02e5 100644 --- a/src/dir.c +++ b/src/dir.c @@ -96,18 +96,26 @@ enum bfs_type bfs_mode_to_type(mode_t mode) { } } +/** + * Private directory flags. + */ +enum { + /** We've reached the end of the directory. */ + BFS_DIR_EOF = BFS_DIR_PRIVATE << 0, +}; + struct bfs_dir { + unsigned int flags; + #if BFS_USE_GETDENTS - alignas(sys_dirent) int fd; + int fd; unsigned short pos; unsigned short size; - // sys_dirent buf[]; + alignas(sys_dirent) char buf[]; #else DIR *dir; struct dirent *de; #endif - - bool eof; }; #if BFS_USE_GETDENTS @@ -125,7 +133,7 @@ void bfs_dir_arena(struct arena *arena) { arena_init(arena, alignof(struct bfs_dir), DIR_SIZE); } -int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path) { +int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path, enum bfs_dir_flags flags) { int fd; if (at_path) { fd = openat(at_fd, at_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY); @@ -139,6 +147,8 @@ int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path) { return -1; } + dir->flags = flags; + #if BFS_USE_GETDENTS dir->fd = fd; dir->pos = 0; @@ -154,7 +164,6 @@ int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path) { dir->de = NULL; #endif - dir->eof = false; return 0; } @@ -170,14 +179,14 @@ int bfs_polldir(struct bfs_dir *dir) { #if BFS_USE_GETDENTS if (dir->pos < dir->size) { return 1; - } else if (dir->eof) { + } else if (dir->flags & BFS_DIR_EOF) { return 0; } char *buf = (char *)(dir + 1); ssize_t size = bfs_getdents(dir->fd, buf, BUF_SIZE); if (size == 0) { - dir->eof = true; + dir->flags |= BFS_DIR_EOF; return 0; } else if (size < 0) { return -1; @@ -194,7 +203,7 @@ int bfs_polldir(struct bfs_dir *dir) { if (size > 0) { dir->size += size; } else if (size == 0) { - dir->eof = true; + dir->flags |= BFS_DIR_EOF; } } @@ -202,7 +211,7 @@ int bfs_polldir(struct bfs_dir *dir) { #else // !BFS_USE_GETDENTS if (dir->de) { return 1; - } else if (dir->eof) { + } else if (dir->flags & BFS_DIR_EOF) { return 0; } @@ -211,7 +220,7 @@ int bfs_polldir(struct bfs_dir *dir) { if (dir->de) { return 1; } else if (errno == 0) { - dir->eof = true; + dir->flags |= BFS_DIR_EOF; return 0; } else { return -1; diff --git a/src/dir.h b/src/dir.h index 1137ff5..7e0cbba 100644 --- a/src/dir.h +++ b/src/dir.h @@ -86,6 +86,14 @@ struct bfs_dir *bfs_allocdir(void); */ void bfs_dir_arena(struct arena *arena); +/** + * bfs_opendir() flags. + */ +enum bfs_dir_flags { + /** @internal Start of private flags. */ + BFS_DIR_PRIVATE = 1 << 0, +}; + /** * Open a directory. * @@ -96,10 +104,12 @@ void bfs_dir_arena(struct arena *arena); * @param at_path * The path of the directory to open, relative to at_fd. Pass NULL to * open at_fd itself. + * @param flags + * Flags that control which directory entries are listed. * @return * 0 on success, or -1 on failure. */ -int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path); +int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path, enum bfs_dir_flags flags); /** * Get the file descriptor for a directory. diff --git a/src/eval.c b/src/eval.c index e0dd97b..adf7a0b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -427,7 +427,7 @@ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { return ret; } - if (bfs_opendir(dir, ftwbuf->at_fd, ftwbuf->at_path) != 0) { + if (bfs_opendir(dir, ftwbuf->at_fd, ftwbuf->at_path, 0) != 0) { eval_report_error(state); return ret; } @@ -1517,8 +1517,8 @@ static int infer_fdlimit(const struct bfs_ctx *ctx, int limit) { goto done; } - if (bfs_opendir(dir, AT_FDCWD, "/proc/self/fd") != 0 - && bfs_opendir(dir, AT_FDCWD, "/dev/fd") != 0) { + if (bfs_opendir(dir, AT_FDCWD, "/proc/self/fd", 0) != 0 + && bfs_opendir(dir, AT_FDCWD, "/dev/fd", 0) != 0) { goto done; } diff --git a/src/ioq.c b/src/ioq.c index 04b9c0d..8c1bdbe 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -348,7 +348,7 @@ static void ioq_handle(struct ioq *ioq, struct ioq_ent *ent) { break; case IOQ_OPENDIR: - ret = bfs_opendir(ent->opendir.dir, ent->opendir.dfd, ent->opendir.path); + ret = bfs_opendir(ent->opendir.dir, ent->opendir.dfd, ent->opendir.path, ent->opendir.flags); if (ret == 0) { bfs_polldir(ent->opendir.dir); } @@ -505,7 +505,7 @@ static void ioq_ring_reap(struct ioq_ring_state *state) { continue; } - ent->ret = bfs_opendir(ent->opendir.dir, fd, NULL); + ent->ret = bfs_opendir(ent->opendir.dir, fd, NULL, ent->opendir.flags); if (ent->ret == 0) { // TODO: io_uring_prep_getdents() bfs_polldir(ent->opendir.dir); @@ -659,7 +659,7 @@ int ioq_close(struct ioq *ioq, int fd, void *ptr) { return 0; } -int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, void *ptr) { +int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, enum bfs_dir_flags flags, void *ptr) { struct ioq_ent *ent = ioq_request(ioq, IOQ_OPENDIR, ptr); if (!ent) { return -1; @@ -669,6 +669,7 @@ int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, args->dir = dir; args->dfd = dfd; args->path = path; + args->flags = flags; ioqq_push(ioq->pending, ent); return 0; diff --git a/src/ioq.h b/src/ioq.h index 99c18c2..eab89ec 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -8,6 +8,7 @@ #ifndef BFS_IOQ_H #define BFS_IOQ_H +#include "dir.h" #include /** @@ -53,6 +54,7 @@ struct ioq_ent { struct bfs_dir *dir; int dfd; const char *path; + enum bfs_dir_flags flags; } opendir; /** ioq_closedir() args. */ struct ioq_closedir { @@ -103,12 +105,14 @@ int ioq_close(struct ioq *ioq, int fd, void *ptr); * The base file descriptor. * @param path * The path to open, relative to dfd. + * @param flags + * Flags that control which directory entries are listed. * @param ptr * An arbitrary pointer to associate with the request. * @return * 0 on success, or -1 on failure. */ -int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, void *ptr); +int ioq_opendir(struct ioq *ioq, struct bfs_dir *dir, int dfd, const char *path, enum bfs_dir_flags flags, void *ptr); /** * Asynchronous bfs_closedir(). -- cgit v1.2.3 From 332f38aff0e6b7bc1a3a648eb66437d2d043ad21 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 17 Oct 2023 11:43:43 -0400 Subject: dir: New flag to control whiteout visibility --- src/dir.c | 12 ++++++++++-- src/dir.h | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src/dir.h') diff --git a/src/dir.c b/src/dir.c index 371696f..98518f2 100644 --- a/src/dir.c +++ b/src/dir.c @@ -259,7 +259,8 @@ static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { /** Skip ".", "..", and deleted/empty dirents. */ static int bfs_skipdent(struct bfs_dir *dir, const sys_dirent *de) { -#if BFS_USE_GETDENTS && __FreeBSD__ +#if BFS_USE_GETDENTS +# if __FreeBSD__ // Union mounts on FreeBSD have to be de-duplicated in userspace if (dir->flags & BFS_DIR_UNION) { struct trie_leaf *leaf = trie_insert_str(&dir->trie, de->d_name); @@ -276,7 +277,14 @@ static int bfs_skipdent(struct bfs_dir *dir, const sys_dirent *de) { if (de->d_ino == 0) { return 1; } -#endif +# endif + +# ifdef DT_WHT + if (de->d_type == DT_WHT && !(dir->flags & BFS_DIR_WHITEOUTS)) { + return 1; + } +# endif +#endif // BFS_USE_GETDENTS const char *name = de->d_name; return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); diff --git a/src/dir.h b/src/dir.h index 7e0cbba..b11d454 100644 --- a/src/dir.h +++ b/src/dir.h @@ -90,8 +90,10 @@ void bfs_dir_arena(struct arena *arena); * bfs_opendir() flags. */ enum bfs_dir_flags { + /** Include whiteouts in the results. */ + BFS_DIR_WHITEOUTS = 1 << 0, /** @internal Start of private flags. */ - BFS_DIR_PRIVATE = 1 << 0, + BFS_DIR_PRIVATE = 1 << 1, }; /** -- cgit v1.2.3 From 2c3ef3a06ee1f951f6d68be6d0d3f6a1822b05b7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 11 Mar 2024 09:51:03 -0400 Subject: Re-run include-what-you-use --- src/alloc.h | 1 + src/bfstd.c | 6 +++--- src/bftw.c | 1 + src/color.h | 1 - src/ctx.c | 1 + src/ctx.h | 2 ++ src/diag.c | 2 +- src/dir.h | 4 ++-- src/dstring.c | 2 ++ src/eval.c | 4 ++-- src/expr.c | 4 ++-- src/expr.h | 1 - src/ioq.c | 5 +++-- src/main.c | 1 + src/opt.c | 3 ++- src/parse.c | 3 +-- src/printf.c | 3 ++- src/trie.c | 2 -- src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 1 - tests/alloc.c | 1 + tests/bfstd.c | 3 --- tests/bit.c | 2 +- tests/ioq.c | 2 ++ tests/main.c | 3 --- tests/xtime.c | 4 ++-- tests/xtouch.c | 1 + 28 files changed, 33 insertions(+), 32 deletions(-) (limited to 'src/dir.h') diff --git a/src/alloc.h b/src/alloc.h index 60dd738..ae055bc 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include /** Check if a size is properly aligned. */ diff --git a/src/bfstd.c b/src/bfstd.c index ce4aa49..c6c2e7f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -2,18 +2,19 @@ // SPDX-License-Identifier: 0BSD #include "bfstd.h" -#include "bit.h" #include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" #include "xregex.h" -#include #include #include #include +#include #include #include +#include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #if BFS_USE_SYS_SYSMACROS_H # include diff --git a/src/bftw.c b/src/bftw.c index 6f52299..50b8b02 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -35,6 +35,7 @@ #include #include #include +#include /** Initialize a bftw_stat cache. */ static void bftw_stat_init(struct bftw_stat *bufs, struct bfs_stat *stat_buf, struct bfs_stat *lstat_buf) { diff --git a/src/color.h b/src/color.h index 85633a4..e3e7973 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include "dstring.h" -#include #include /** diff --git a/src/ctx.c b/src/ctx.c index 6c84f75..f5b28c7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -6,6 +6,7 @@ #include "color.h" #include "diag.h" #include "expr.h" +#include "list.h" #include "mtab.h" #include "pwcache.h" #include "stat.h" diff --git a/src/ctx.h b/src/ctx.h index aa91f2c..e14db21 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -18,6 +18,8 @@ #include #include +struct CFILE; + /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index 656fa89..cb27b92 100644 --- a/src/diag.c +++ b/src/diag.c @@ -11,8 +11,8 @@ #include "expr.h" #include #include +#include #include -#include /** bfs_diagf() implementation. */ attr(printf(2, 0)) diff --git a/src/dir.h b/src/dir.h index b11d454..18d907e 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,8 +8,6 @@ #ifndef BFS_DIR_H #define BFS_DIR_H -#include "alloc.h" -#include "config.h" #include /** @@ -78,6 +76,8 @@ struct bfs_dirent { */ struct bfs_dir *bfs_allocdir(void); +struct arena; + /** * Initialize an arena for directories. * diff --git a/src/dstring.c b/src/dstring.c index bc18308..10b0fad 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -7,6 +7,8 @@ #include "config.h" #include "diag.h" #include +#include +#include #include #include #include diff --git a/src/eval.c b/src/eval.c index 1711001..9e55964 100644 --- a/src/eval.c +++ b/src/eval.c @@ -25,7 +25,6 @@ #include "stat.h" #include "trie.h" #include "xregex.h" -#include "xtime.h" #include #include #include @@ -36,8 +35,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/src/expr.c b/src/expr.c index 3e0033f..5784220 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4,12 +4,12 @@ #include "expr.h" #include "alloc.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "exec.h" +#include "list.h" #include "printf.h" #include "xregex.h" -#include -#include #include struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { diff --git a/src/expr.h b/src/expr.h index 957b04a..75cb5fd 100644 --- a/src/expr.h +++ b/src/expr.h @@ -12,7 +12,6 @@ #include "config.h" #include "eval.h" #include "stat.h" -#include #include #include diff --git a/src/ioq.c b/src/ioq.c index 00c3b86..37eed7d 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -126,13 +126,14 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "sanity.h" #include "stat.h" #include "thread.h" -#include #include +#include #include +#include #include +#include #if BFS_USE_LIBURING # include diff --git a/src/main.c b/src/main.c index 16a2576..e120f03 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,7 @@ #include "bfstd.h" #include "config.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "parse.h" #include diff --git a/src/opt.c b/src/opt.c index 74145ac..76965de 100644 --- a/src/opt.c +++ b/src/opt.c @@ -26,11 +26,13 @@ */ #include "opt.h" +#include "bftw.h" #include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" #include "diag.h" +#include "dir.h" #include "eval.h" #include "exec.h" #include "expr.h" @@ -40,7 +42,6 @@ #include #include #include -#include #include static char *fake_and_arg = "-and"; diff --git a/src/parse.c b/src/parse.c index b26a50f..3b7386d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -42,8 +42,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/printf.c b/src/printf.c index 34ed606..487f039 100644 --- a/src/printf.c +++ b/src/printf.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "printf.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -14,10 +15,10 @@ #include "mtab.h" #include "pwcache.h" #include "stat.h" -#include "xtime.h" #include #include #include +#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bd5300d..1ffb23a 100644 --- a/src/trie.c +++ b/src/trie.c @@ -87,9 +87,7 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include -#include #include bfs_static_assert(CHAR_WIDTH == 8); diff --git a/src/trie.h b/src/trie.h index 02088f1..4288d76 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "alloc.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 8d6108b..6a94d3d 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #if BFS_USE_PATHS_H diff --git a/src/xtime.c b/src/xtime.c index 05f0e1a..5b259ab 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -7,7 +7,6 @@ #include "diag.h" #include #include -#include #include #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 4ce23d4..9f08111 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "tests.h" #include "../src/alloc.h" +#include "../src/config.h" #include "../src/diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 0ded5de..26abdb6 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,9 +7,6 @@ #include "../src/diag.h" #include #include -#include -#include -#include #include #include diff --git a/tests/bit.c b/tests/bit.c index b944748..3d66ce3 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -3,10 +3,10 @@ #include "tests.h" #include "../src/bit.h" +#include "../src/config.h" #include "../src/diag.h" #include #include -#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); diff --git a/tests/ioq.c b/tests/ioq.c index 56e1886..1ce8f75 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -4,10 +4,12 @@ #include "tests.h" #include "../src/ioq.h" #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/diag.h" #include "../src/dir.h" #include #include +#include /** * Test for blocking within ioq_slot_push(). diff --git a/tests/main.c b/tests/main.c index 38438b2..8849e8c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,11 +6,8 @@ */ #include "tests.h" -#include "../src/bfstd.h" #include "../src/color.h" #include "../src/config.h" -#include "../src/diag.h" -#include #include #include #include diff --git a/tests/xtime.c b/tests/xtime.c index 3f1fec2..c8dc00b 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -5,10 +5,10 @@ #include "../src/xtime.h" #include "../src/bfstd.h" #include "../src/config.h" +#include "../src/diag.h" #include +#include #include -#include -#include #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { diff --git a/tests/xtouch.c b/tests/xtouch.c index 8c5c5f3..fad272f 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 1207086b5a6341412c647480eea68c870b18bba1 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 Apr 2024 16:16:18 -0400 Subject: config: Check for getdents{,64}() --- config/flags.mk | 1 - config/getdents.c | 10 ++++++++++ config/getdents64-syscall.c | 12 ++++++++++++ config/getdents64.c | 10 ++++++++++ config/header.mk | 3 +++ src/dir.c | 14 ++++++++------ src/dir.h | 4 ++-- 7 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 config/getdents.c create mode 100644 config/getdents64-syscall.c create mode 100644 config/getdents64.c (limited to 'src/dir.h') diff --git a/config/flags.mk b/config/flags.mk index 9959d10..8a2e50e 100644 --- a/config/flags.mk +++ b/config/flags.mk @@ -36,7 +36,6 @@ export BFS_CPPFLAGS= \ -D_DARWIN_C_SOURCE \ -D_DEFAULT_SOURCE \ -D_GNU_SOURCE \ - -D_LARGEFILE64_SOURCE \ -D_POSIX_PTHREAD_SEMANTICS \ -D_FILE_OFFSET_BITS=64 \ -D_TIME_BITS=64 diff --git a/config/getdents.c b/config/getdents.c new file mode 100644 index 0000000..d0d4228 --- /dev/null +++ b/config/getdents.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct dirent de; + getdents(3, &de, 1024); + return 0; +} diff --git a/config/getdents64-syscall.c b/config/getdents64-syscall.c new file mode 100644 index 0000000..4838c14 --- /dev/null +++ b/config/getdents64-syscall.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + struct dirent64 de; + syscall(SYS_getdents64, 3, &de, 1024); + return 0; +} diff --git a/config/getdents64.c b/config/getdents64.c new file mode 100644 index 0000000..1abf36d --- /dev/null +++ b/config/getdents64.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct dirent64 de; + getdents64(3, &de, 1024); + return 0; +} diff --git a/config/header.mk b/config/header.mk index 8867200..cc4ae05 100644 --- a/config/header.mk +++ b/config/header.mk @@ -9,6 +9,9 @@ include config/exports.mk # All header fragments we generate HEADERS := \ + ${GEN}/getdents.h \ + ${GEN}/getdents64.h \ + ${GEN}/getdents64-syscall.h \ ${GEN}/getprogname.h \ ${GEN}/getprogname-gnu.h \ ${GEN}/posix-spawn-addfchdir.h \ diff --git a/src/dir.c b/src/dir.c index 53c9be3..8b1bee0 100644 --- a/src/dir.c +++ b/src/dir.c @@ -17,7 +17,7 @@ #include #if BFS_USE_GETDENTS -# if __linux__ +# if BFS_HAS_GETDENTS64_SYSCALL # include # endif @@ -25,12 +25,14 @@ static ssize_t bfs_getdents(int fd, void *buf, size_t size) { sanitize_uninit(buf, size); -#if (__linux__ && __GLIBC__ && !__GLIBC_PREREQ(2, 30)) || __ANDROID__ - ssize_t ret = syscall(SYS_getdents64, fd, buf, size); -#elif __linux__ +#if BFS_HAS_GETDENTS + ssize_t ret = getdents(fd, buf, size); +#elif BFS_HAS_GETDENTS64 ssize_t ret = getdents64(fd, buf, size); +#elif BFS_HAS_GETDENTS64_SYSCALL + ssize_t ret = syscall(SYS_getdents64, fd, buf, size); #else - ssize_t ret = getdents(fd, buf, size); +# error "No getdents() implementation" #endif if (ret > 0) { @@ -42,7 +44,7 @@ static ssize_t bfs_getdents(int fd, void *buf, size_t size) { #endif // BFS_USE_GETDENTS -#if BFS_USE_GETDENTS && __linux__ +#if BFS_USE_GETDENTS && !BFS_HAS_GETDENTS /** Directory entry type for bfs_getdents() */ typedef struct dirent64 sys_dirent; #else diff --git a/src/dir.h b/src/dir.h index 18d907e..19dfa98 100644 --- a/src/dir.h +++ b/src/dir.h @@ -14,8 +14,8 @@ * Whether the implementation uses the getdents() syscall directly, rather than * libc's readdir(). */ -#ifndef BFS_USE_GETDENTS -# define BFS_USE_GETDENTS (__linux__ || __FreeBSD__) +#if !defined(BFS_USE_GETDENTS) && (__linux__ || __FreeBSD__) +# define BFS_USE_GETDENTS (BFS_HAS_GETDENTS || BFS_HAS_GETDENTS64 | BFS_HAS_GETDENTS64_SYSCALL) #endif /** -- cgit v1.2.3 From 17da9c855a49925333433b4308c1b3384c65c50a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 19 Apr 2024 14:23:16 -0400 Subject: config: Check for fdclosedir() --- config/fdclosedir.c | 8 ++++++++ config/header.mk | 1 + src/dir.c | 2 +- src/dir.h | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 config/fdclosedir.c (limited to 'src/dir.h') diff --git a/config/fdclosedir.c b/config/fdclosedir.c new file mode 100644 index 0000000..f4ad1f5 --- /dev/null +++ b/config/fdclosedir.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return fdclosedir(opendir(".")); +} diff --git a/config/header.mk b/config/header.mk index 36ad98e..04fbdec 100644 --- a/config/header.mk +++ b/config/header.mk @@ -11,6 +11,7 @@ include config/exports.mk HEADERS := \ ${GEN}/acl-is-trivial-np.h \ ${GEN}/confstr.h \ + ${GEN}/fdclosedir.h \ ${GEN}/getdents.h \ ${GEN}/getdents64.h \ ${GEN}/getdents64-syscall.h \ diff --git a/src/dir.c b/src/dir.c index 8b1bee0..cfbbca4 100644 --- a/src/dir.c +++ b/src/dir.c @@ -353,7 +353,7 @@ int bfs_closedir(struct bfs_dir *dir) { int bfs_unwrapdir(struct bfs_dir *dir) { #if BFS_USE_GETDENTS int ret = dir->fd; -#elif __FreeBSD__ +#elif BFS_HAS_FDCLOSEDIR int ret = fdclosedir(dir->dir); #endif diff --git a/src/dir.h b/src/dir.h index 19dfa98..bc6c4ed 100644 --- a/src/dir.h +++ b/src/dir.h @@ -152,7 +152,7 @@ int bfs_closedir(struct bfs_dir *dir); * Whether the bfs_unwrapdir() function is supported. */ #ifndef BFS_USE_UNWRAPDIR -# define BFS_USE_UNWRAPDIR (BFS_USE_GETDENTS || __FreeBSD__) +# define BFS_USE_UNWRAPDIR (BFS_USE_GETDENTS || BFS_HAS_FDCLOSEDIR) #endif #if BFS_USE_UNWRAPDIR -- cgit v1.2.3 From 339c20aea74d5b4c6b8b178b478cc5530bc9af79 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 25 Apr 2024 12:01:40 -0400 Subject: config: Add BFS_USE_LIB* to config.h instead of CPPFLAGS --- config/config.mk | 23 ++++------------------- config/deps.mk | 2 +- config/header.mk | 1 + config/pkg.mk | 23 ----------------------- config/pkg.sh | 26 -------------------------- config/pkgconf.sh | 12 ++++++++++++ config/pkgs.mk | 35 ++++++++++++++++++++++++++++++----- config/prelude.mk | 2 +- src/dir.h | 1 + 9 files changed, 50 insertions(+), 75 deletions(-) delete mode 100644 config/pkg.mk delete mode 100755 config/pkg.sh (limited to 'src/dir.h') diff --git a/config/config.mk b/config/config.mk index 5750b49..280c6ac 100644 --- a/config/config.mk +++ b/config/config.mk @@ -44,34 +44,19 @@ ${GEN}/vars.mk:: # Sets the build flags. This depends on vars.mk and uses a recursive make so # that the default flags can depend on variables like ${OS}. ${GEN}/flags.mk: ${GEN}/vars.mk - @+${MAKE} -sf config/flags.mk + @+${MAKE} -sf config/flags.mk $@ .PHONY: ${GEN}/flags.mk # Check for dependency generation support ${GEN}/deps.mk: ${GEN}/flags.mk - @+${MAKE} -sf config/deps.mk + @+${MAKE} -sf config/deps.mk $@ .PHONY: ${GEN}/deps.mk -# External dependencies -PKG_MKS := \ - ${GEN}/libacl.mk \ - ${GEN}/libcap.mk \ - ${GEN}/libselinux.mk \ - ${GEN}/liburing.mk \ - ${GEN}/oniguruma.mk - # Auto-detect dependencies and their build flags -${GEN}/pkgs.mk: ${PKG_MKS} - @printf '# %s\n' "${TGT}" >$@ - @printf 'include $${GEN}/%s\n' ${.ALLSRC:${GEN}/%=%} >>$@ - @+${MAKE} -sf config/pkgs.mk +${GEN}/pkgs.mk: ${GEN}/flags.mk + @+${MAKE} -sf config/pkgs.mk $@ .PHONY: ${GEN}/pkgs.mk -# Auto-detect dependencies -${PKG_MKS}: ${GEN}/flags.mk - @+${MAKE} -sf config/pkg.mk TARGET=$@ -.PHONY: ${PKG_MKS} - # Compile-time feature detection ${GEN}/config.h: ${CONFIG} @+${MAKE} -sf config/header.mk $@ diff --git a/config/deps.mk b/config/deps.mk index 2201f06..ac394a5 100644 --- a/config/deps.mk +++ b/config/deps.mk @@ -15,4 +15,4 @@ ${GEN}/deps.mk:: printf 'DEPFLAGS = -MD -MP\n'; \ fi >>$@ 2>$@.log ${VCAT} $@ - @printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ + printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ diff --git a/config/header.mk b/config/header.mk index 7fd2f78..ccc36d3 100644 --- a/config/header.mk +++ b/config/header.mk @@ -48,6 +48,7 @@ ${GEN}/config.h: ${HEADERS} printf '// %s\n' "${TGT}" >$@ printf '#ifndef BFS_CONFIG_H\n' >>$@ printf '#define BFS_CONFIG_H\n' >>$@ + printf '#define BFS_USE_%s true\n' $$(printf '%s\n' ${PKGS} | tr 'a-z-' 'A-Z_') >>$@ cat ${.ALLSRC} >>$@ printf '#endif // BFS_CONFIG_H\n' >>$@ cat ${.ALLSRC:%=%.log} >$@.log diff --git a/config/pkg.mk b/config/pkg.mk deleted file mode 100644 index fafe562..0000000 --- a/config/pkg.mk +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/lib*.mk - -include config/prelude.mk -include ${GEN}/vars.mk -include ${GEN}/flags.mk -include config/exports.mk - -# Like ${TGT} but for ${TARGET}, not $@ -SHORT = ${TARGET:${BUILDDIR}/%=%} - -default:: - @printf '# %s\n' "${SHORT}" >${TARGET} - config/pkg.sh ${TARGET:${GEN}/%.mk=%} >>${TARGET} 2>${TARGET}.log - @if [ "${IS_V}" ]; then \ - cat ${TARGET}; \ - elif grep -q PKGS ${TARGET}; then \ - printf '[ GEN] %-${MSG_WIDTH}s ✔\n' ${SHORT}; \ - else \ - printf '[ GEN] %-${MSG_WIDTH}s ✘\n' ${SHORT}; \ - fi diff --git a/config/pkg.sh b/config/pkg.sh deleted file mode 100755 index 4ebea64..0000000 --- a/config/pkg.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# pkg-config wrapper that outputs a makefile fragment - -set -eu - -NAME=$(printf '%s' "$1" | tr 'a-z' 'A-Z') -eval "XUSE=\"\${USE_$NAME:-}\"" - -if [ "$XUSE" ]; then - USE="$XUSE" -elif config/pkgconf.sh "$1"; then - USE=y -else - USE=n -fi - -if [ "$USE" = y ]; then - printf 'PKGS += %s\n' "$1" - printf 'CPPFLAGS += -DBFS_USE_%s=1\n' "$NAME" -else - printf 'CPPFLAGS += -DBFS_USE_%s=0\n' "$NAME" -fi diff --git a/config/pkgconf.sh b/config/pkgconf.sh index 2dbad76..2fb2f1e 100755 --- a/config/pkgconf.sh +++ b/config/pkgconf.sh @@ -26,6 +26,18 @@ esac if [ -z "$MODE" ]; then # Check whether the libraries exist at all for LIB; do + # Check ${USE_$LIB} + USE_LIB="USE_$(printf '%s' "$LIB" | tr 'a-z-' 'A-Z_')" + eval "USE=\"\${$USE_LIB:-}\"" + case "$USE" in + y|1) + continue + ;; + n|0) + exit 1 + ;; + esac + CFLAGS=$("$0" --cflags "$LIB") || exit 1 LDFLAGS=$("$0" --ldflags "$LIB") || exit 1 LDLIBS=$("$0" --ldlibs "$LIB") || exit 1 diff --git a/config/pkgs.mk b/config/pkgs.mk index 3a18289..2c100ab 100644 --- a/config/pkgs.mk +++ b/config/pkgs.mk @@ -6,12 +6,37 @@ include config/prelude.mk include ${GEN}/vars.mk include ${GEN}/flags.mk -include ${GEN}/pkgs.mk include config/exports.mk -${GEN}/pkgs.mk:: +# External dependencies +USE_PKGS := \ + ${GEN}/libacl.use \ + ${GEN}/libcap.use \ + ${GEN}/libselinux.use \ + ${GEN}/liburing.use \ + ${GEN}/oniguruma.use + +${GEN}/pkgs.mk: ${USE_PKGS} ${MSG} "[ GEN] ${TGT}" - printf 'CFLAGS += %s\n' "$$(config/pkgconf.sh --cflags ${PKGS})" >>$@ 2>>$@.log - printf 'LDFLAGS += %s\n' "$$(config/pkgconf.sh --ldflags ${PKGS})" >>$@ 2>>$@.log - printf 'LDLIBS := %s $${LDLIBS}\n' "$$(config/pkgconf.sh --ldlibs ${PKGS})" >>$@ 2>>$@.log + printf '# %s\n' "${TGT}" >$@ + gen() { \ + printf 'PKGS := %s\n' "$$*"; \ + printf 'CFLAGS += %s\n' "$$(config/pkgconf.sh --cflags "$$@")"; \ + printf 'LDFLAGS += %s\n' "$$(config/pkgconf.sh --ldflags "$$@")"; \ + printf 'LDLIBS := %s $${LDLIBS}\n' "$$(config/pkgconf.sh --ldlibs "$$@")"; \ + }; \ + gen $$(cat ${.ALLSRC}) >>$@ ${VCAT} $@ +.PHONY: ${GEN}/pkgs.mk + +# Convert ${GEN}/foo.use to foo +PKG = ${@:${GEN}/%.use=%} + +${USE_PKGS}:: + if config/pkgconf.sh ${PKG} 2>$@.log; then \ + printf '%s\n' ${PKG} >$@; \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' config/${PKG}.c; \ + else \ + : >$@; \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' config/${PKG}.c; \ + fi diff --git a/config/prelude.mk b/config/prelude.mk index b9bc61b..e1e7a4d 100644 --- a/config/prelude.mk +++ b/config/prelude.mk @@ -109,7 +109,7 @@ MSG = @msg() { \ }; \ msg -# Maximum width of a short message, to align the [X] +# Maximum width of a short message, to align the ✔/✘ MSG_WIDTH := 33 # cat a file if V=1 diff --git a/src/dir.h b/src/dir.h index bc6c4ed..6d5c9c5 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,6 +8,7 @@ #ifndef BFS_DIR_H #define BFS_DIR_H +#include "prelude.h" #include /** -- cgit v1.2.3