summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2021-01-27 15:27:56 -0500
committerTavian Barnes <tavianator@tavianator.com>2021-01-28 17:13:26 -0500
commit9bf75cee19b8d60d2b4f6260a8c2322cc8ca11db (patch)
tree0bc819909cb616b05ff013fbf1144dae7a13ec23
parent3fcd7b010fd4f477582997d688cc415f76e70411 (diff)
dir: New DIR* facade
-rw-r--r--Makefile1
-rw-r--r--bftw.c188
-rw-r--r--bftw.h40
-rw-r--r--color.c25
-rw-r--r--dir.c200
-rw-r--r--dir.h124
-rw-r--r--eval.c108
-rw-r--r--fsade.c19
-rw-r--r--parse.c21
-rw-r--r--printf.c25
-rw-r--r--util.c18
-rw-r--r--util.h6
12 files changed, 473 insertions, 302 deletions
diff --git a/Makefile b/Makefile
index 5df2812..e78605e 100644
--- a/Makefile
+++ b/Makefile
@@ -142,6 +142,7 @@ bfs: \
ctx.o \
darray.o \
diag.o \
+ dir.o \
dstring.o \
eval.o \
exec.o \
diff --git a/bftw.c b/bftw.c
index aac6799..2e35594 100644
--- a/bftw.c
+++ b/bftw.c
@@ -33,13 +33,13 @@
*/
#include "bftw.h"
+#include "dir.h"
#include "dstring.h"
#include "mtab.h"
#include "stat.h"
#include "trie.h"
#include "util.h"
#include <assert.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -70,7 +70,7 @@ struct bftw_file {
int fd;
/** This file's type, if known. */
- enum bftw_type type;
+ enum bfs_type type;
/** The device number, for cycle detection. */
dev_t dev;
/** The inode number, for cycle detection. */
@@ -322,7 +322,7 @@ static struct bftw_file *bftw_file_new(struct bftw_cache *cache, struct bftw_fil
file->refcount = 1;
file->fd = -1;
- file->type = BFTW_UNKNOWN;
+ file->type = BFS_UNKNOWN;
file->dev = -1;
file->ino = -1;
@@ -433,7 +433,7 @@ static int bftw_file_open(struct bftw_cache *cache, struct bftw_file *file, cons
}
/**
- * Open a DIR* for a bftw_file.
+ * Open a bftw_file as a directory.
*
* @param cache
* The cache to hold the file.
@@ -442,16 +442,16 @@ static int bftw_file_open(struct bftw_cache *cache, struct bftw_file *file, cons
* @param path
* The full path to the directory.
* @return
- * The opened DIR *, or NULL on error.
+ * The opened directory, or NULL on error.
*/
-static DIR *bftw_file_opendir(struct bftw_cache *cache, struct bftw_file *file, const char *path) {
+static struct bfs_dir *bftw_file_opendir(struct bftw_cache *cache, struct bftw_file *file, const char *path) {
int fd = bftw_file_open(cache, file, path);
if (fd < 0) {
return NULL;
}
// Now we dup() the fd and pass it to fdopendir(). This way we can
- // close the DIR* as soon as we're done with it, reducing the memory
+ // close the bfs_dir as soon as we're done with it, reducing the memory
// footprint significantly, while keeping the fd around for future
// openat() calls.
@@ -467,7 +467,7 @@ static DIR *bftw_file_opendir(struct bftw_cache *cache, struct bftw_file *file,
return NULL;
}
- DIR *ret = fdopendir(dfd);
+ struct bfs_dir *ret = bfs_opendir(dfd, NULL);
if (!ret) {
int error = errno;
close(dfd);
@@ -618,9 +618,11 @@ struct bftw_state {
struct bftw_file *previous;
/** The currently open directory. */
- DIR *dir;
+ struct bfs_dir *dir;
/** The current directory entry. */
- struct dirent *de;
+ struct bfs_dirent *de;
+ /** Storage for the directory entry. */
+ struct bfs_dirent de_storage;
/** Any error encountered while reading the directory. */
int direrror;
@@ -645,7 +647,7 @@ static int bftw_state_init(struct bftw_state *state, const struct bftw_args *arg
goto err;
}
- // Reserve 1 fd for the open DIR *
+ // Reserve 1 fd for the open bfs_dir
if (bftw_cache_init(&state->cache, args->nopenfd - 1) != 0) {
goto err;
}
@@ -673,103 +675,6 @@ err:
return -1;
}
-enum bftw_type bftw_mode_to_type(mode_t mode) {
- switch (mode & S_IFMT) {
-#ifdef S_IFBLK
- case S_IFBLK:
- return BFTW_BLK;
-#endif
-#ifdef S_IFCHR
- case S_IFCHR:
- return BFTW_CHR;
-#endif
-#ifdef S_IFDIR
- case S_IFDIR:
- return BFTW_DIR;
-#endif
-#ifdef S_IFDOOR
- case S_IFDOOR:
- return BFTW_DOOR;
-#endif
-#ifdef S_IFIFO
- case S_IFIFO:
- return BFTW_FIFO;
-#endif
-#ifdef S_IFLNK
- case S_IFLNK:
- return BFTW_LNK;
-#endif
-#ifdef S_IFPORT
- case S_IFPORT:
- return BFTW_PORT;
-#endif
-#ifdef S_IFREG
- case S_IFREG:
- return BFTW_REG;
-#endif
-#ifdef S_IFSOCK
- case S_IFSOCK:
- return BFTW_SOCK;
-#endif
-#ifdef S_IFWHT
- case S_IFWHT:
- return BFTW_WHT;
-#endif
-
- default:
- return BFTW_UNKNOWN;
- }
-}
-
-static enum bftw_type bftw_dirent_type(const struct dirent *de) {
-#if defined(_DIRENT_HAVE_D_TYPE) || defined(DT_UNKNOWN)
- switch (de->d_type) {
-#ifdef DT_BLK
- case DT_BLK:
- return BFTW_BLK;
-#endif
-#ifdef DT_CHR
- case DT_CHR:
- return BFTW_CHR;
-#endif
-#ifdef DT_DIR
- case DT_DIR:
- return BFTW_DIR;
-#endif
-#ifdef DT_DOOR
- case DT_DOOR:
- return BFTW_DOOR;
-#endif
-#ifdef DT_FIFO
- case DT_FIFO:
- return BFTW_FIFO;
-#endif
-#ifdef DT_LNK
- case DT_LNK:
- return BFTW_LNK;
-#endif
-#ifdef DT_PORT
- case DT_PORT:
- return BFTW_PORT;
-#endif
-#ifdef DT_REG
- case DT_REG:
- return BFTW_REG;
-#endif
-#ifdef DT_SOCK
- case DT_SOCK:
- return BFTW_SOCK;
-#endif
-#ifdef DT_WHT
- case DT_WHT:
- return BFTW_WHT;
-#endif
- }
-#endif
-
- return BFTW_UNKNOWN;
-}
-
/** Cached bfs_stat(). */
static const struct bfs_stat *bftw_stat_impl(struct BFTW *ftwbuf, struct bftw_stat *cache, enum bfs_stat_flags flags) {
if (!cache->buf) {
@@ -817,20 +722,20 @@ const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat
}
}
-enum bftw_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flags flags) {
+enum bfs_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flags flags) {
if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) {
- if ((flags & BFS_STAT_NOFOLLOW) || ftwbuf->type != BFTW_LNK) {
+ if ((flags & BFS_STAT_NOFOLLOW) || ftwbuf->type != BFS_LNK) {
return ftwbuf->type;
}
- } else if ((flags & (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW)) == BFS_STAT_TRYFOLLOW || ftwbuf->type == BFTW_LNK) {
+ } else if ((flags & (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW)) == BFS_STAT_TRYFOLLOW || ftwbuf->type == BFS_LNK) {
return ftwbuf->type;
}
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, flags);
if (statbuf) {
- return bftw_mode_to_type(statbuf->mode);
+ return bfs_mode_to_type(statbuf->mode);
} else {
- return BFTW_ERROR;
+ return BFS_ERROR;
}
}
@@ -865,15 +770,15 @@ static bool bftw_need_stat(const struct bftw_state *state) {
}
const struct BFTW *ftwbuf = &state->ftwbuf;
- if (ftwbuf->type == BFTW_UNKNOWN) {
+ if (ftwbuf->type == BFS_UNKNOWN) {
return true;
}
- if (ftwbuf->type == BFTW_LNK && !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) {
+ if (ftwbuf->type == BFS_LNK && !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) {
return true;
}
- if (ftwbuf->type == BFTW_DIR) {
+ if (ftwbuf->type == BFS_DIR) {
if (state->flags & (BFTW_DETECT_CYCLES | BFTW_SKIP_MOUNTS | BFTW_PRUNE_MOUNTS)) {
return true;
}
@@ -930,14 +835,14 @@ static int bftw_ensure_open(struct bftw_cache *cache, struct bftw_file *file, co
*/
static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) {
struct bftw_file *file = state->file;
- const struct dirent *de = state->de;
+ const struct bfs_dirent *de = state->de;
struct BFTW *ftwbuf = &state->ftwbuf;
ftwbuf->path = state->path;
ftwbuf->root = file ? file->root->name : ftwbuf->path;
ftwbuf->depth = 0;
ftwbuf->visit = visit;
- ftwbuf->type = BFTW_UNKNOWN;
+ ftwbuf->type = BFS_UNKNOWN;
ftwbuf->error = state->direrror;
ftwbuf->at_fd = AT_FDCWD;
ftwbuf->at_path = ftwbuf->path;
@@ -949,7 +854,7 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) {
if (de) {
parent = file;
ftwbuf->depth = file->depth + 1;
- ftwbuf->type = bftw_dirent_type(de);
+ ftwbuf->type = de->type;
ftwbuf->nameoff = bftw_child_nameoff(file);
} else if (file) {
parent = file->parent;
@@ -974,7 +879,7 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) {
}
if (ftwbuf->error != 0) {
- ftwbuf->type = BFTW_ERROR;
+ ftwbuf->type = BFS_ERROR;
return;
}
@@ -991,18 +896,18 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) {
if (bftw_need_stat(state)) {
statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (statbuf) {
- ftwbuf->type = bftw_mode_to_type(statbuf->mode);
+ ftwbuf->type = bfs_mode_to_type(statbuf->mode);
} else {
- ftwbuf->type = BFTW_ERROR;
+ ftwbuf->type = BFS_ERROR;
ftwbuf->error = errno;
return;
}
}
- if (ftwbuf->type == BFTW_DIR && (state->flags & BFTW_DETECT_CYCLES)) {
+ if (ftwbuf->type == BFS_DIR && (state->flags & BFTW_DETECT_CYCLES)) {
for (const struct bftw_file *ancestor = parent; ancestor; ancestor = ancestor->parent) {
if (ancestor->dev == statbuf->dev && ancestor->ino == statbuf->ino) {
- ftwbuf->type = BFTW_ERROR;
+ ftwbuf->type = BFS_ERROR;
ftwbuf->error = ELOOP;
return;
}
@@ -1051,8 +956,8 @@ static enum bftw_action bftw_visit(struct bftw_state *state, const char *name, e
const struct BFTW *ftwbuf = &state->ftwbuf;
bftw_init_ftwbuf(state, visit);
- // Never give the callback BFTW_ERROR unless BFTW_RECOVER is specified
- if (ftwbuf->type == BFTW_ERROR && !(state->flags & BFTW_RECOVER)) {
+ // Never give the callback BFS_ERROR unless BFTW_RECOVER is specified
+ if (ftwbuf->type == BFS_ERROR && !(state->flags & BFTW_RECOVER)) {
state->error = ftwbuf->error;
return BFTW_STOP;
}
@@ -1073,7 +978,7 @@ static enum bftw_action bftw_visit(struct bftw_state *state, const char *name, e
return BFTW_STOP;
}
- if (visit != BFTW_PRE || ftwbuf->type != BFTW_DIR) {
+ if (visit != BFTW_PRE || ftwbuf->type != BFS_DIR) {
ret = BFTW_PRUNE;
goto done;
}
@@ -1103,7 +1008,7 @@ static int bftw_push(struct bftw_state *state, const char *name, bool fill_id) {
}
if (state->de) {
- file->type = bftw_dirent_type(state->de);
+ file->type = state->de->type;
}
if (fill_id) {
@@ -1190,14 +1095,17 @@ static int bftw_readdir(struct bftw_state *state) {
return -1;
}
- if (xreaddir(state->dir, &state->de) != 0) {
- state->direrror = errno;
- return -1;
- } else if (state->de) {
- return 1;
+ int ret = bfs_readdir(state->dir, &state->de_storage);
+ if (ret > 0) {
+ state->de = &state->de_storage;
+ } else if (ret == 0) {
+ state->de = NULL;
} else {
- return 0;
+ state->de = NULL;
+ state->direrror = errno;
}
+
+ return ret;
}
/**
@@ -1220,7 +1128,7 @@ enum bftw_release_flags {
static enum bftw_action bftw_closedir(struct bftw_state *state, enum bftw_release_flags flags) {
enum bftw_action ret = BFTW_CONTINUE;
- if (state->dir && closedir(state->dir) != 0) {
+ if (state->dir && bfs_closedir(state->dir) != 0) {
state->direrror = errno;
}
state->de = NULL;
@@ -1352,7 +1260,7 @@ static int bftw_stream(const struct bftw_args *args) {
bftw_batch_start(&state);
while (bftw_readdir(&state) > 0) {
- const char *name = state.de->d_name;
+ const char *name = state.de->name;
switch (bftw_visit(&state, name, BFTW_PRE)) {
case BFTW_CONTINUE:
@@ -1415,7 +1323,7 @@ static int bftw_batch(const struct bftw_args *args) {
bftw_batch_start(&state);
while (bftw_readdir(&state) > 0) {
- if (bftw_push(&state, state.de->d_name, false) != 0) {
+ if (bftw_push(&state, state.de->name, false) != 0) {
goto done;
}
}
@@ -1479,7 +1387,7 @@ static enum bftw_action bftw_ids_callback(const struct BFTW *ftwbuf, void *ptr)
mutbuf->visit = state->visit;
}
- if (ftwbuf->type == BFTW_ERROR) {
+ if (ftwbuf->type == BFS_ERROR) {
if (ftwbuf->depth + 1 >= state->min_depth) {
return state->delegate(ftwbuf, state->ptr);
} else {
@@ -1506,13 +1414,13 @@ static enum bftw_action bftw_ids_callback(const struct BFTW *ftwbuf, void *ptr)
switch (ret) {
case BFTW_CONTINUE:
- if (ftwbuf->type == BFTW_DIR && ftwbuf->depth + 1 >= state->max_depth) {
+ if (ftwbuf->type == BFS_DIR && ftwbuf->depth + 1 >= state->max_depth) {
state->bottom = false;
ret = BFTW_PRUNE;
}
break;
case BFTW_PRUNE:
- if (ftwbuf->type == BFTW_DIR) {
+ if (ftwbuf->type == BFS_DIR) {
if (!trie_insert_str(&state->pruned, ftwbuf->path)) {
state->error = errno;
state->quit = true;
diff --git a/bftw.h b/bftw.h
index 098d12c..30fb39b 100644
--- a/bftw.h
+++ b/bftw.h
@@ -21,46 +21,12 @@
#ifndef BFS_BFTW_H
#define BFS_BFTW_H
+#include "dir.h"
#include "stat.h"
#include <stddef.h>
#include <sys/types.h>
/**
- * Possible file types.
- */
-enum bftw_type {
- /** An error occurred for this file. */
- BFTW_ERROR = -1,
- /** Unknown type. */
- BFTW_UNKNOWN,
- /** Block device. */
- BFTW_BLK,
- /** Character device. */
- BFTW_CHR,
- /** Directory. */
- BFTW_DIR,
- /** Solaris door. */
- BFTW_DOOR,
- /** Pipe. */
- BFTW_FIFO,
- /** Symbolic link. */
- BFTW_LNK,
- /** Solaris event port. */
- BFTW_PORT,
- /** Regular file. */
- BFTW_REG,
- /** Socket. */
- BFTW_SOCK,
- /** BSD whiteout. */
- BFTW_WHT,
-};
-
-/**
- * Convert a bfs_stat() mode to a bftw_type.
- */
-enum bftw_type bftw_mode_to_type(mode_t mode);
-
-/**
* Possible visit occurrences.
*/
enum bftw_visit {
@@ -99,7 +65,7 @@ struct BFTW {
enum bftw_visit visit;
/** The file type. */
- enum bftw_type type;
+ enum bfs_type type;
/** The errno that occurred, if type == BFTW_ERROR. */
int error;
@@ -154,7 +120,7 @@ const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat
* @return
* The type of the file, or BFTW_ERROR if an error occurred.
*/
-enum bftw_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flags flags);
+enum bfs_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flags flags);
/**
* Walk actions returned by the bftw() callback.
diff --git a/color.c b/color.c
index 1d06e51..cfedba1 100644
--- a/color.c
+++ b/color.c
@@ -16,6 +16,7 @@
#include "color.h"
#include "bftw.h"
+#include "dir.h"
#include "dstring.h"
#include "expr.h"
#include "fsade.h"
@@ -581,8 +582,8 @@ static bool is_link_broken(const struct BFTW *ftwbuf) {
/** Get the color for a file. */
static const char *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) {
- enum bftw_type type = bftw_type(ftwbuf, flags);
- if (type == BFTW_ERROR) {
+ enum bfs_type type = bftw_type(ftwbuf, flags);
+ if (type == BFS_ERROR) {
goto error;
}
@@ -590,7 +591,7 @@ static const char *file_color(const struct colors *colors, const char *filename,
const char *color = NULL;
switch (type) {
- case BFTW_REG:
+ case BFS_REG:
if (colors->setuid || colors->setgid || colors->executable || colors->multi_hard) {
statbuf = bftw_stat(ftwbuf, flags);
if (!statbuf) {
@@ -620,7 +621,7 @@ static const char *file_color(const struct colors *colors, const char *filename,
break;
- case BFTW_DIR:
+ case BFS_DIR:
if (colors->sticky_other_writable || colors->other_writable || colors->sticky) {
statbuf = bftw_stat(ftwbuf, flags);
if (!statbuf) {
@@ -640,7 +641,7 @@ static const char *file_color(const struct colors *colors, const char *filename,
break;
- case BFTW_LNK:
+ case BFS_LNK:
if (colors->orphan && is_link_broken(ftwbuf)) {
color = colors->orphan;
} else {
@@ -648,19 +649,19 @@ static const char *file_color(const struct colors *colors, const char *filename,
}
break;
- case BFTW_BLK:
+ case BFS_BLK:
color = colors->blockdev;
break;
- case BFTW_CHR:
+ case BFS_CHR:
color = colors->chardev;
break;
- case BFTW_FIFO:
+ case BFS_FIFO:
color = colors->pipe;
break;
- case BFTW_SOCK:
+ case BFS_SOCK:
color = colors->socket;
break;
- case BFTW_DOOR:
+ case BFS_DOOR:
color = colors->door;
break;
@@ -733,7 +734,7 @@ static int print_colored(CFILE *cfile, const char *esc, const char *str, size_t
static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags, size_t max) {
ssize_t ret = max;
- if (bftw_type(ftwbuf, flags) != BFTW_ERROR) {
+ if (bftw_type(ftwbuf, flags) != BFS_ERROR) {
goto out;
}
@@ -840,7 +841,7 @@ static int print_path(CFILE *cfile, const struct BFTW *ftwbuf) {
}
enum bfs_stat_flags flags = ftwbuf->stat_flags;
- if (colors && colors->link_as_target && ftwbuf->type == BFTW_LNK) {
+ if (colors && colors->link_as_target && ftwbuf->type == BFS_LNK) {
flags = BFS_STAT_TRYFOLLOW;
}
diff --git a/dir.c b/dir.c
new file mode 100644
index 0000000..23aa62d
--- /dev/null
+++ b/dir.c
@@ -0,0 +1,200 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2021 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * 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. *
+ ****************************************************************************/
+
+#include "dir.h"
+#include "util.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+enum bfs_type bfs_mode_to_type(mode_t mode) {
+ switch (mode & S_IFMT) {
+#ifdef S_IFBLK
+ case S_IFBLK:
+ return BFS_BLK;
+#endif
+#ifdef S_IFCHR
+ case S_IFCHR:
+ return BFS_CHR;
+#endif
+#ifdef S_IFDIR
+ case S_IFDIR:
+ return BFS_DIR;
+#endif
+#ifdef S_IFDOOR
+ case S_IFDOOR:
+ return BFS_DOOR;
+#endif
+#ifdef S_IFIFO
+ case S_IFIFO:
+ return BFS_FIFO;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK:
+ return BFS_LNK;
+#endif
+#ifdef S_IFPORT
+ case S_IFPORT:
+ return BFS_PORT;
+#endif
+#ifdef S_IFREG
+ case S_IFREG:
+ return BFS_REG;
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ return BFS_SOCK;
+#endif
+#ifdef S_IFWHT
+ case S_IFWHT:
+ return BFS_WHT;
+#endif
+
+ default:
+ return BFS_UNKNOWN;
+ }
+}
+
+struct bfs_dir {
+ DIR *dir;
+ struct dirent *ent;
+};
+
+struct bfs_dir *bfs_opendir(int at_fd, const char *at_path) {
+ struct bfs_dir *dir = malloc(sizeof(*dir));
+ if (!dir) {
+ return NULL;
+ }
+
+ int fd;
+ if (at_path) {
+ fd = openat(at_fd, at_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+ } else if (at_fd >= 0) {
+ fd = at_fd;
+ } else {
+ free(dir);
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (fd < 0) {
+ free(dir);
+ return NULL;
+ }
+
+ dir->dir = fdopendir(fd);
+ if (!dir->dir) {
+ int error = errno;
+ close(fd);
+ free(dir);
+ errno = error;
+ return NULL;
+ }
+
+ dir->ent = NULL;
+
+ return dir;
+}
+
+int bfs_dirfd(const struct bfs_dir *dir) {
+ return dirfd(dir->dir);
+}
+
+static enum bfs_type dirent_type(const struct dirent *de) {
+#if defined(_DIRENT_HAVE_D_TYPE) || defined(DT_UNKNOWN)
+ switch (de->d_type) {
+#ifdef DT_BLK
+ case DT_BLK:
+ return BFS_BLK;
+#endif
+#ifdef DT_CHR
+ case DT_CHR:
+ return BFS_CHR;
+#endif
+#ifdef DT_DIR
+ case DT_DIR:
+ return BFS_DIR;
+#endif
+#ifdef DT_DOOR
+ case DT_DOOR:
+ return BFS_DOOR;
+#endif
+#ifdef DT_FIFO
+ case DT_FIFO:
+ return BFS_FIFO;
+#endif
+#ifdef DT_LNK
+ case DT_LNK:
+ return BFS_LNK;
+#endif
+#ifdef DT_PORT
+ case DT_PORT:
+ return BFS_PORT;
+#endif
+#ifdef DT_REG
+ case DT_REG:
+ return BFS_REG;
+#endif
+#ifdef DT_SOCK
+ case DT_SOCK:
+ return BFS_SOCK;
+#endif
+#ifdef DT_WHT
+ case DT_WHT:
+ return BFS_WHT;
+#endif
+ }
+#endif
+
+ return BFS_UNKNOWN;
+}
+
+int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *dirent) {
+ while (true) {
+ errno = 0;
+ dir->ent = readdir(dir->dir);
+ if (dir->ent) {
+ const char *name = dir->ent->d_name;
+ if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) {
+ continue;
+ }
+ if (dirent) {
+ dirent->type = dirent_type(dir->ent);
+ dirent->name = name;
+ }
+ return 1;
+ } else if (errno != 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+int bfs_closedir(struct bfs_dir *dir) {
+ int ret = closedir(dir->dir);
+ free(dir);
+ return ret;
+}
+
+int bfs_freedir(struct bfs_dir *dir) {
+ int ret = dup_cloexec(dirfd(dir->dir));
+ bfs_closedir(dir);
+ return ret;
+}
diff --git a/dir.h b/dir.h
new file mode 100644
index 0000000..5817013
--- /dev/null
+++ b/dir.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+ * bfs *
+ * Copyright (C) 2021 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * 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. *
+ ****************************************************************************/
+
+/**
+ * Directories and their contents.
+ */
+
+#ifndef BFS_DIR_H
+#define BFS_DIR_H
+
+#include <sys/types.h>
+
+/**
+ * A directory.
+ */
+struct bfs_dir;
+
+/**
+ * File types.
+ */
+enum bfs_type {
+ /** An error occurred for this file. */
+ BFS_ERROR = -1,
+ /** Unknown type. */
+ BFS_UNKNOWN,
+ /** Block device. */
+ BFS_BLK,
+ /** Character device. */
+ BFS_CHR,
+ /** Directory. */
+ BFS_DIR,
+ /** Solaris door. */
+ BFS_DOOR,
+ /** Pipe. */
+ BFS_FIFO,
+ /** Symbolic link. */
+ BFS_LNK,
+ /** Solaris event port. */
+ BFS_PORT,
+ /** Regular file. */
+ BFS_REG,
+ /** Socket. */
+ BFS_SOCK,
+ /** BSD whiteout. */
+ BFS_WHT,
+};
+
+/**
+ * Convert a bfs_stat() mode to a bfs_type.
+ */
+enum bfs_type bfs_mode_to_type(mode_t mode);
+
+/**
+ * A directory entry.
+ */
+struct bfs_dirent {
+ /** The type of this file (possibly unknown). */
+ enum bfs_type type;
+ /** The name of this file. */
+ const char *name;
+};
+
+/**
+ * Open a 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.
+ */
+struct bfs_dir *bfs_opendir(int at_fd, const char *at_path);
+
+/**
+ * Get the file descriptor for a directory.
+ */
+int bfs_dirfd(const struct bfs_dir *dir);
+
+/**
+ * Read a directory entry.
+ *
+ * @param dir
+ * The directory to read.
+ * @param[out] dirent
+ * The directory entry to populate.
+ * @return
+ * 1 on success, 0 on EOF, or -1 on failure.
+ */
+int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *dirent);
+
+/**
+ * Close a directory.
+ *
+ * @return
+ * 0 on success, -1 on failure.
+ */
+int bfs_closedir(struct bfs_dir *dir);
+
+/**
+ * Free a directory, keeping an open file descriptor to it.
+ *
+ * @param dir
+ * The directory to free.
+ * @return
+ * The file descriptor on success, or -1 on failure.
+ */
+int bfs_freedir(struct bfs_dir *dir);
+
+#endif // BFS_DIR_H
diff --git a/eval.c b/eval.c
index 2bcfa8a..9e8b0b6 100644
--- a/eval.c
+++ b/eval.c
@@ -25,6 +25,7 @@
#include "ctx.h"
#include "darray.h"
#include "diag.h"
+#include "dir.h"
#include "dstring.h"
#include "exec.h"
#include "expr.h"
@@ -37,7 +38,6 @@
#include "trie.h"
#include "util.h"
#include <assert.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
@@ -345,10 +345,10 @@ bool eval_delete(const struct expr *expr, struct eval_state *state) {
int flag = 0;
// We need to know the actual type of the path, not what it points to
- enum bftw_type type = bftw_type(ftwbuf, BFS_STAT_NOFOLLOW);
- if (type == BFTW_DIR) {
+ enum bfs_type type = bftw_type(ftwbuf, BFS_STAT_NOFOLLOW);
+ if (type == BFS_DIR) {
flag |= AT_REMOVEDIR;
- } else if (type == BFTW_ERROR) {
+ } else if (type == BFS_ERROR) {
eval_report_error(state);
return false;
}
@@ -415,29 +415,22 @@ bool eval_empty(const struct expr *expr, struct eval_state *state) {
bool ret = false;
const struct BFTW *ftwbuf = state->ftwbuf;
- if (ftwbuf->type == BFTW_DIR) {
- int dfd = openat(ftwbuf->at_fd, ftwbuf->at_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
- if (dfd < 0) {
- eval_report_error(state);
- goto done;
- }
-
- DIR *dir = fdopendir(dfd);
+ if (ftwbuf->type == BFS_DIR) {
+ struct bfs_dir *dir = bfs_opendir(ftwbuf->at_fd, ftwbuf->at_path);
if (!dir) {
eval_report_error(state);
- close(dfd);
goto done;
}
- struct dirent *de;
- if (xreaddir(dir, &de) == 0) {
- ret = !de;
- } else {
+ int did_read = bfs_readdir(dir, NULL);
+ if (did_read < 0) {
eval_report_error(state);
+ } else {
+ ret = !did_read;
}
- closedir(dir);
- } else if (ftwbuf->type == BFTW_REG) {
+ bfs_closedir(dir);
+ } else if (ftwbuf->type == BFS_REG) {
const struct bfs_stat *statbuf = eval_stat(state);
if (statbuf) {
ret = statbuf->size == 0;
@@ -514,7 +507,7 @@ bool eval_lname(const struct expr *expr, struct eval_state *state) {
char *name = NULL;
const struct BFTW *ftwbuf = state->ftwbuf;
- if (ftwbuf->type != BFTW_LNK) {
+ if (ftwbuf->type != BFS_LNK) {
goto done;
}
@@ -580,7 +573,7 @@ bool eval_perm(const struct expr *expr, struct eval_state *state) {
mode_t mode = statbuf->mode;
mode_t target;
- if (state->ftwbuf->type == BFTW_DIR) {
+ if (state->ftwbuf->type == BFS_DIR) {
target = expr->dir_mode;
} else {
target = expr->file_mode;
@@ -648,7 +641,7 @@ bool eval_fls(const struct expr *expr, struct eval_state *state) {
}
}
- if (ftwbuf->type == BFTW_BLK || ftwbuf->type == BFTW_CHR) {
+ if (ftwbuf->type == BFS_BLK || ftwbuf->type == BFS_CHR) {
int ma = bfs_major(statbuf->rdev);
int mi = bfs_minor(statbuf->rdev);
if (fprintf(file, " %3d, %3d", ma, mi) < 0) {
@@ -686,7 +679,7 @@ bool eval_fls(const struct expr *expr, struct eval_state *state) {
goto error;
}
- if (ftwbuf->type == BFTW_LNK) {
+ if (ftwbuf->type == BFS_LNK) {
if (cfprintf(cfile, " -> %pL", ftwbuf) < 0) {
goto error;
}
@@ -914,8 +907,8 @@ bool eval_xattrname(const struct expr *expr, struct eval_state *state) {
bool eval_xtype(const struct expr *expr, struct eval_state *state) {
const struct BFTW *ftwbuf = state->ftwbuf;
enum bfs_stat_flags flags = ftwbuf->stat_flags ^ (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW);
- enum bftw_type type = bftw_type(ftwbuf, flags);
- if (type == BFTW_ERROR) {
+ enum bfs_type type = bftw_type(ftwbuf, flags);
+ if (type == BFS_ERROR) {
eval_report_error(state);
return false;
} else {
@@ -1236,28 +1229,28 @@ static void debug_stats(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf) {
}
}
-#define DUMP_BFTW_MAP(value) [value] = #value
+#define DUMP_MAP(value) [value] = #value
/**
- * Dump the bftw_type for -D search.
+ * Dump the bfs_type for -D search.
*/
-static const char *dump_bftw_type(enum bftw_type type) {
+static const char *dump_bfs_type(enum bfs_type type) {
static const char *types[] = {
- DUMP_BFTW_MAP(BFTW_UNKNOWN),
- DUMP_BFTW_MAP(BFTW_BLK),
- DUMP_BFTW_MAP(BFTW_CHR),
- DUMP_BFTW_MAP(BFTW_DIR),
- DUMP_BFTW_MAP(BFTW_DOOR),
- DUMP_BFTW_MAP(BFTW_FIFO),
- DUMP_BFTW_MAP(BFTW_LNK),
- DUMP_BFTW_MAP(BFTW_PORT),
- DUMP_BFTW_MAP(BFTW_REG),
- DUMP_BFTW_MAP(BFTW_SOCK),
- DUMP_BFTW_MAP(BFTW_WHT),
+ DUMP_MAP(BFS_UNKNOWN),
+ DUMP_MAP(BFS_BLK),
+ DUMP_MAP(BFS_CHR),
+ DUMP_MAP(BFS_DIR),
+ DUMP_MAP(BFS_DOOR),
+ DUMP_MAP(BFS_FIFO),
+ DUMP_MAP(BFS_LNK),
+ DUMP_MAP(BFS_PORT),
+ DUMP_MAP(BFS_REG),
+ DUMP_MAP(BFS_SOCK),
+ DUMP_MAP(BFS_WHT),
};
- if (type == BFTW_ERROR) {
- return "BFTW_ERROR";
+ if (type == BFS_ERROR) {
+ return "BFS_ERROR";
} else {
return types[type];
}
@@ -1268,8 +1261,8 @@ static const char *dump_bftw_type(enum bftw_type type) {
*/
static const char *dump_bftw_visit(enum bftw_visit visit) {
static const char *visits[] = {
- DUMP_BFTW_MAP(BFTW_PRE),
- DUMP_BFTW_MAP(BFTW_POST),
+ DUMP_MAP(BFTW_PRE),
+ DUMP_MAP(BFTW_POST),
};
return visits[visit];
}
@@ -1279,9 +1272,9 @@ static const char *dump_bftw_visit(enum bftw_visit visit) {
*/
static const char *dump_bftw_action(enum bftw_action action) {
static const char *actions[] = {
- DUMP_BFTW_MAP(BFTW_CONTINUE),
- DUMP_BFTW_MAP(BFTW_PRUNE),
- DUMP_BFTW_MAP(BFTW_STOP),
+ DUMP_MAP(BFTW_CONTINUE),
+ DUMP_MAP(BFTW_PRUNE),
+ DUMP_MAP(BFTW_STOP),
};
return actions[action];
}
@@ -1327,7 +1320,7 @@ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) {