summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-06-20 12:02:08 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-06-20 14:26:09 -0400
commita1490d98a1aebb3bfbd3873613977d0341ec7f98 (patch)
tree1320ab9e60b30621fedf29c50d42135dac8b4a2f
parent4889f3ebb59c926b8e53a2e12edd5009d7cd4cbe (diff)
downloadbfs-a1490d98a1aebb3bfbd3873613977d0341ec7f98.tar.xz
dir: Arena-allocate directories
-rw-r--r--src/bftw.c37
-rw-r--r--src/dir.c44
-rw-r--r--src/dir.h27
-rw-r--r--src/eval.c34
-rw-r--r--src/ioq.c15
-rw-r--r--src/ioq.h4
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 <sys/types.h>
@@ -62,17 +63,35 @@ struct bfs_dirent {
};
/**
+ * 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.