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(-) 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