diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | bftw.c | 188 | ||||
-rw-r--r-- | bftw.h | 40 | ||||
-rw-r--r-- | color.c | 25 | ||||
-rw-r--r-- | dir.c | 200 | ||||
-rw-r--r-- | dir.h | 124 | ||||
-rw-r--r-- | eval.c | 108 | ||||
-rw-r--r-- | fsade.c | 19 | ||||
-rw-r--r-- | parse.c | 21 | ||||
-rw-r--r-- | printf.c | 25 | ||||
-rw-r--r-- | util.c | 18 | ||||
-rw-r--r-- | util.h | 6 |
12 files changed, 473 insertions, 302 deletions
@@ -142,6 +142,7 @@ bfs: \ ctx.o \ darray.o \ diag.o \ + dir.o \ dstring.o \ eval.o \ exec.o \ @@ -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; @@ -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. @@ -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; } @@ -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; +} @@ -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 @@ -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) { eval_status(&state, args->bar, &args->last_status, args->count); } - if (ftwbuf->type == BFTW_ERROR) { + if (ftwbuf->type == BFS_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { args->ret = EXIT_FAILURE; eval_error(&state, "%s.\n", strerror(ftwbuf->error)); @@ -1361,7 +1354,7 @@ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { // In -depth mode, only handle directories on the BFTW_POST visit enum bftw_visit expected_visit = BFTW_PRE; if ((ctx->flags & BFTW_POST_ORDER) - && (ctx->strategy == BFTW_IDS || ftwbuf->type == BFTW_DIR) + && (ctx->strategy == BFTW_IDS || ftwbuf->type == BFS_DIR) && ftwbuf->depth < (size_t)ctx->maxdepth) { expected_visit = BFTW_POST; } @@ -1380,7 +1373,7 @@ done: fprintf(stderr, "\t.root = \"%s\",\n", ftwbuf->root); fprintf(stderr, "\t.depth = %zu,\n", ftwbuf->depth); fprintf(stderr, "\t.visit = %s,\n", dump_bftw_visit(ftwbuf->visit)); - fprintf(stderr, "\t.type = %s,\n", dump_bftw_type(ftwbuf->type)); + fprintf(stderr, "\t.type = %s,\n", dump_bfs_type(ftwbuf->type)); fprintf(stderr, "\t.error = %d,\n", ftwbuf->error); fprintf(stderr, "}) == %s\n", dump_bftw_action(state.action)); } @@ -1406,20 +1399,19 @@ static int infer_fdlimit(const struct bfs_ctx *ctx) { // Check /proc/self/fd for the current number of open fds, if possible // (we may have inherited more than just the standard ones) - DIR *dir = opendir("/proc/self/fd"); + struct bfs_dir *dir = bfs_opendir(AT_FDCWD, "/proc/self/fd"); if (!dir) { - dir = opendir("/dev/fd"); + dir = bfs_opendir(AT_FDCWD, "/dev/fd"); } if (dir) { // Account for 'dir' itself nopen = -1; - struct dirent *de; - while (xreaddir(dir, &de) == 0 && de) { + while (bfs_readdir(dir, NULL) > 0) { ++nopen; } - closedir(dir); + bfs_closedir(dir); } ret -= nopen; @@ -1457,10 +1449,10 @@ static void dump_bftw_flags(enum bftw_flags flags) { */ static const char *dump_bftw_strategy(enum bftw_strategy strategy) { static const char *strategies[] = { - DUMP_BFTW_MAP(BFTW_BFS), - DUMP_BFTW_MAP(BFTW_DFS), - DUMP_BFTW_MAP(BFTW_IDS), - DUMP_BFTW_MAP(BFTW_EDS), + DUMP_MAP(BFTW_BFS), + DUMP_MAP(BFTW_DFS), + DUMP_MAP(BFTW_IDS), + DUMP_MAP(BFTW_EDS), }; return strategies[strategy]; } @@ -16,6 +16,7 @@ #include "fsade.h" #include "bftw.h" +#include "dir.h" #include "dstring.h" #include "util.h" #include <errno.h> @@ -200,7 +201,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { }; static const size_t n_acl_types = sizeof(acl_types)/sizeof(acl_types[0]); - if (ftwbuf->type == BFTW_LNK) { + if (ftwbuf->type == BFS_LNK) { return 0; } @@ -210,7 +211,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { for (size_t i = 0; i < n_acl_types && ret <= 0; ++i) { acl_type_t type = acl_types[i]; - if (type == ACL_TYPE_DEFAULT && ftwbuf->type != BFTW_DIR) { + if (type == ACL_TYPE_DEFAULT && ftwbuf->type != BFS_DIR) { // ACL_TYPE_DEFAULT is supported only for directories, // otherwise acl_get_file() gives EACCESS continue; @@ -247,7 +248,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { #if BFS_CAN_CHECK_CAPABILITIES int bfs_check_capabilities(const struct BFTW *ftwbuf) { - if (ftwbuf->type == BFTW_LNK) { + if (ftwbuf->type == BFS_LNK) { return 0; } @@ -298,17 +299,17 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf) { #if BFS_HAS_SYS_EXTATTR ssize_t (*extattr_list)(const char *, int, void*, size_t) = - ftwbuf->type == BFTW_LNK ? extattr_list_link : extattr_list_file; + ftwbuf->type == BFS_LNK ? extattr_list_link : extattr_list_file; len = extattr_list(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); if (len <= 0) { len = extattr_list(path, EXTATTR_NAMESPACE_USER, NULL, 0); } #elif __APPLE__ - int options = ftwbuf->type == BFTW_LNK ? XATTR_NOFOLLOW : 0; + int options = ftwbuf->type == BFS_LNK ? XATTR_NOFOLLOW : 0; len = listxattr(path, NULL, 0, options); #else - if (ftwbuf->type == BFTW_LNK) { + if (ftwbuf->type == BFS_LNK) { len = llistxattr(path, NULL, 0); } else { len = listxattr(path, NULL, 0); @@ -337,17 +338,17 @@ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { #if BFS_HAS_SYS_EXTATTR ssize_t (*extattr_get)(const char *, int, const char *, void*, size_t) = - ftwbuf->type == BFTW_LNK ? extattr_get_link : extattr_get_file; + ftwbuf->type == BFS_LNK ? extattr_get_link : extattr_get_file; len = extattr_get(path, EXTATTR_NAMESPACE_SYSTEM, name, NULL, 0); if (len < 0) { len = extattr_get(path, EXTATTR_NAMESPACE_USER, name, NULL, 0); } #elif __APPLE__ - int options = ftwbuf->type == BFTW_LNK ? XATTR_NOFOLLOW : 0; + int options = ftwbuf->type == BFS_LNK ? XATTR_NOFOLLOW : 0; len = getxattr(path, name, NULL, 0, 0, options); #else - if (ftwbuf->type == BFTW_LNK) { + if (ftwbuf->type == BFS_LNK) { len = lgetxattr(path, name, NULL, 0); } else { len = getxattr(path, name, NULL, 0); @@ -28,6 +28,7 @@ #include "ctx.h" #include "darray.h" #include "diag.h" +#include "dir.h" #include "eval.h" #include "exec.h" #include "expr.h" @@ -2314,44 +2315,44 @@ static struct expr *parse_type(struct parser_state *state, int x, int arg2) { const char *c = expr->sdata; while (true) { - enum bftw_type type; + enum bfs_type type; double type_prob; switch (*c) { case 'b': - type = BFTW_BLK; + type = BFS_BLK; type_prob = 0.00000721183; break; case 'c': - type = BFTW_CHR; + type = BFS_CHR; type_prob = 0.0000499855; break; case 'd': - type = BFTW_DIR; + type = BFS_DIR; type_prob = 0.114475; break; case 'D': - type = BFTW_DOOR; + type = BFS_DOOR; type_prob = 0.000001; break; case 'p': - type = BFTW_FIFO; + type = BFS_FIFO; type_prob = 0.00000248684; break; case 'f': - type = BFTW_REG; + type = BFS_REG; type_prob = 0.859772; break; case 'l': - type = BFTW_LNK; + type = BFS_LNK; type_prob = 0.0256816; break; case 's': - type = BFTW_SOCK; + type = BFS_SOCK; type_prob = 0.0000116881; break; case 'w': - type = BFTW_WHT; + type = BFS_WHT; type_prob = 0.000001; break; @@ -18,6 +18,7 @@ #include "bftw.h" #include "ctx.h" #include "diag.h" +#include "dir.h" #include "dstring.h" #include "mtab.h" #include "pwcache.h" @@ -309,7 +310,7 @@ static int bfs_printf_l(FILE *file, const struct bfs_printf *directive, const st char *buf = NULL; const char *target = ""; - if (ftwbuf->type == BFTW_LNK) { + if (ftwbuf->type == BFS_LNK) { const struct bfs_stat *statbuf = bftw_cached_stat(ftwbuf, BFS_STAT_NOFOLLOW); size_t len = statbuf ? statbuf->size : 0; @@ -425,23 +426,23 @@ static int bfs_printf_u(FILE *file, const struct bfs_printf *directive, const st return fprintf(file, directive->str, pwd->pw_name); } -static const char *bfs_printf_type(enum bftw_type type) { +static const char *bfs_printf_type(enum bfs_type type) { switch (type) { - case BFTW_BLK: + case BFS_BLK: return "b"; - case BFTW_CHR: + case BFS_CHR: return "c"; - case BFTW_DIR: + case BFS_DIR: return "d"; - case BFTW_DOOR: + case BFS_DOOR: return "D"; - case BFTW_FIFO: + case BFS_FIFO: return "p"; - case BFTW_LNK: + case BFS_LNK: return "l"; - case BFTW_REG: + case BFS_REG: return "f"; - case BFTW_SOCK: + case BFS_SOCK: return "s"; default: return "U"; @@ -458,7 +459,7 @@ static int bfs_printf_y(FILE *file, const struct bfs_printf *directive, const st static int bfs_printf_Y(FILE *file, const struct bfs_printf *directive, const struct BFTW *ftwbuf) { int error = 0; - if (ftwbuf->type != BFTW_LNK) { + if (ftwbuf->type != BFS_LNK) { return bfs_printf_y(file, directive, ftwbuf); } @@ -466,7 +467,7 @@ static int bfs_printf_Y(FILE *file, const struct bfs_printf *directive, const st const struct bfs_stat *statbuf = bftw_stat(ftwbuf, BFS_STAT_FOLLOW); if (statbuf) { - type = bfs_printf_type(bftw_mode_to_type(statbuf->mode)); + type = bfs_printf_type(bfs_mode_to_type(statbuf->mode)); } else { switch (errno) { case ELOOP: @@ -39,24 +39,6 @@ # include <sys/mkdev.h> #endif -int xreaddir(DIR *dir, struct dirent **de) { - while (true) { - errno = 0; - *de = readdir(dir); - - if (*de) { - const char *name = (*de)->d_name; - if (name[0] != '.' || (name[1] != '\0' && (name[1] != '.' || name[2] != '\0'))) { - return 0; - } - } else if (errno != 0) { - return -1; - } else { - return 0; - } - } -} - char *xreadlinkat(int fd, const char *path, size_t size) { ssize_t len; char *name = NULL; @@ -21,7 +21,6 @@ #ifndef BFS_UTIL_H #define BFS_UTIL_H -#include <dirent.h> #include <fcntl.h> #include <fnmatch.h> #include <regex.h> @@ -115,11 +114,6 @@ : BFS_FLEX_MAX(sizeof(type), BFS_FLEX_LB(type, member, length))) /** - * readdir() wrapper that makes error handling cleaner. - */ -int xreaddir(DIR *dir, struct dirent **de); - -/** * readlinkat() wrapper that dynamically allocates the result. * * @param fd |