From 68ae5d07e06cf8b91b4277ca25bb74e437cf3ca0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 21 Jun 2018 22:49:23 -0400 Subject: bftw: Introduce bftw_reader type --- bftw.c | 703 +++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 336 insertions(+), 367 deletions(-) (limited to 'bftw.c') diff --git a/bftw.c b/bftw.c index 4ff74a0..11baf91 100644 --- a/bftw.c +++ b/bftw.c @@ -252,16 +252,34 @@ static void bftw_cache_pop(struct bftw_cache *cache) { bftw_dir_close(cache, cache->heap[0]); } -/** Pop a directory other than 'saved' from the cache. */ -static void bftw_cache_pop_other(struct bftw_cache *cache, const struct bftw_dir *saved) { - assert(cache->size > 1); +/** + * Shrink the cache, to recover from EMFILE. + * + * @param cache + * The cache in question. + * @param saved + * A bftw_dir that must be preserved. + * @return + * 0 if successfully shrunk, otherwise -1. + */ +static int bftw_cache_shrink(struct bftw_cache *cache, const struct bftw_dir *saved) { + int ret = -1; + struct bftw_dir *dir = NULL; + + if (cache->size >= 1) { + dir = cache->heap[0]; + if (dir == saved && cache->size >= 2) { + dir = cache->heap[1]; + } + } - struct bftw_dir *dir = cache->heap[0]; - if (dir == saved) { - dir = cache->heap[1]; + if (dir && dir != saved) { + bftw_dir_close(cache, dir); + ret = 0; } - bftw_dir_close(cache, dir); + cache->capacity = cache->size; + return ret; } /** Create a new bftw_dir. */ @@ -309,6 +327,25 @@ static struct bftw_dir *bftw_dir_new(struct bftw_cache *cache, struct bftw_dir * return dir; } +/** + * Compute the path to a bftw_dir. + */ +static int bftw_dir_path(const struct bftw_dir *dir, char **path) { + size_t pathlen = dir->nameoff + dir->namelen; + if (dstresize(path, pathlen) != 0) { + return -1; + } + char *dest = *path; + + // Build the path backwards + while (dir) { + memcpy(dest + dir->nameoff, dir->name, dir->namelen); + dir = dir->parent; + } + + return 0; +} + /** * Get the appropriate (fd, path) pair for the *at() family of functions. * @@ -335,25 +372,6 @@ static struct bftw_dir *bftw_dir_base(struct bftw_dir *dir, int *at_fd, const ch return base; } -/** - * Check if we should retry an operation due to EMFILE. - * - * @param cache - * The cache in question. - * @param saved - * A bftw_dir that must be preserved. - */ -static bool bftw_should_retry(struct bftw_cache *cache, const struct bftw_dir *saved) { - if (errno == EMFILE && cache->size > 1) { - // Too many open files, shrink the cache - bftw_cache_pop_other(cache, saved); - cache->capacity = cache->size; - return true; - } else { - return false; - } -} - /** * Open a bftw_dir relative to another one. * @@ -364,7 +382,7 @@ static bool bftw_should_retry(struct bftw_cache *cache, const struct bftw_dir *s * @param base * The base directory for the relative path (may be NULL). * @param at_fd - * The base file descriptor, AT_CWDFD if base == NULL. + * The base file descriptor, AT_FDCWD if base == NULL. * @param at_path * The relative path to the dir. * @return @@ -376,8 +394,10 @@ static int bftw_dir_openat(struct bftw_cache *cache, struct bftw_dir *dir, const int flags = O_RDONLY | O_CLOEXEC | O_DIRECTORY; int fd = openat(at_fd, at_path, flags); - if (fd < 0 && bftw_should_retry(cache, base)) { - fd = openat(at_fd, at_path, flags); + if (fd < 0 && errno == EMFILE) { + if (bftw_cache_shrink(cache, base) == 0) { + fd = openat(base->fd, at_path, flags); + } } if (fd >= 0) { @@ -473,17 +493,23 @@ static DIR *bftw_dir_opendir(struct bftw_cache *cache, struct bftw_dir *dir, con int dfd = dup_cloexec(fd); - if (dfd < 0 && bftw_should_retry(cache, dir)) { - dfd = dup_cloexec(fd); + if (dfd < 0 && errno == EMFILE) { + if (bftw_cache_shrink(cache, dir) == 0) { + dfd = dup_cloexec(fd); + } } + if (dfd < 0) { return NULL; } DIR *ret = fdopendir(dfd); if (!ret) { + int error = errno; close(dfd); + errno = error; } + return ret; } @@ -498,43 +524,136 @@ static void bftw_dir_free(struct bftw_cache *cache, struct bftw_dir *dir) { free(dir); } +/** + * A directory reader. + */ +struct bftw_reader { + /** The directory object. */ + struct bftw_dir *dir; + /** The path to the directory. */ + char *path; + /** The open handle to the directory. */ + DIR *handle; + /** The current directory entry. */ + struct dirent *de; + /** Any error code that has occurred. */ + int error; +}; + +/** Initialize a reader. */ +static int bftw_reader_init(struct bftw_reader *reader) { + reader->path = dstralloc(0); + if (!reader->path) { + return -1; + } + + reader->dir = NULL; + reader->handle = NULL; + reader->de = NULL; + reader->error = 0; + return 0; +} + +/** Read a directory entry. */ +static int bftw_reader_read(struct bftw_reader *reader) { + if (xreaddir(reader->handle, &reader->de) != 0) { + reader->error = errno; + return -1; + } + + return 0; +} + +/** Open a directory for reading. */ +static int bftw_reader_open(struct bftw_reader *reader, struct bftw_cache *cache, struct bftw_dir *dir) { + assert(!reader->handle); + assert(!reader->de); + + reader->dir = dir; + reader->error = 0; + + if (bftw_dir_path(dir, &reader->path) != 0) { + reader->error = errno; + dstresize(&reader->path, 0); + return -1; + } + + reader->handle = bftw_dir_opendir(cache, dir, reader->path); + if (!reader->handle) { + reader->error = errno; + return -1; + } + + return bftw_reader_read(reader); +} + +/** Close a directory. */ +static int bftw_reader_close(struct bftw_reader *reader) { + assert(reader->handle); + + int ret = 0; + if (closedir(reader->handle) != 0) { + reader->error = errno; + ret = -1; + } + + reader->handle = NULL; + reader->de = NULL; + + return ret; +} + +/** Destroy a reader. */ +static void bftw_reader_destroy(struct bftw_reader *reader) { + if (reader->handle) { + bftw_reader_close(reader); + } + + dstrfree(reader->path); +} + /** * A queue of bftw_dir's to examine. */ struct bftw_queue { + /** The head of the queue. */ struct bftw_dir *head; - struct bftw_dir **tail; + /** The tail of the queue. */ + struct bftw_dir *tail; }; /** Initialize a bftw_queue. */ static void bftw_queue_init(struct bftw_queue *queue) { queue->head = NULL; - queue->tail = &queue->head; + queue->tail = NULL; } /** Add a directory to the bftw_queue. */ static void bftw_queue_push(struct bftw_queue *queue, struct bftw_dir *dir) { - *queue->tail = dir; - queue->tail = &dir->next; + assert(dir->next == NULL); + + if (!queue->head) { + queue->head = dir; + } + if (queue->tail) { + queue->tail->next = dir; + } + queue->tail = dir; } -/** Remove a directory from the bftw_queue. */ +/** Pop the next directory from the queue. */ static struct bftw_dir *bftw_queue_pop(struct bftw_queue *queue) { struct bftw_dir *dir = queue->head; - - if (dir) { - queue->head = dir->next; - if (!queue->head) { - queue->tail = &queue->head; - } - dir->next = NULL; + queue->head = dir->next; + if (queue->tail == dir) { + queue->tail = NULL; } - + dir->next = NULL; return dir; } /** Call stat() and use the results. */ -static int ftwbuf_stat(struct BFTW *ftwbuf, struct bfs_stat *sb) { +static int bftw_stat(struct BFTW *ftwbuf, struct bfs_stat *sb) { int ret = bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags, BFS_STAT_BROKEN_OK, sb); if (ret == 0) { ftwbuf->statbuf = sb; @@ -543,18 +662,6 @@ static int ftwbuf_stat(struct BFTW *ftwbuf, struct bfs_stat *sb) { return ret; } -/** - * Possible bftw() traversal statuses. - */ -enum bftw_status { - /** The current path is state.current. */ - BFTW_CURRENT, - /** The current path is a child of state.current. */ - BFTW_CHILD, - /** bftw_dir's are being garbage collected. */ - BFTW_GC, -}; - /** * Holds the current state of the bftw() traversal. */ @@ -571,22 +678,16 @@ struct bftw_state { /** The cache of open directories. */ struct bftw_cache cache; - /** The queue of directories left to explore. */ struct bftw_queue queue; - /** The current directory. */ - struct bftw_dir *current; - /** The previous directory. */ - struct bftw_dir *previous; - /** The currently open directory. */ - DIR *dir; - /** The current traversal status. */ - enum bftw_status status; + + /** The reader for the current directory. */ + struct bftw_reader reader; + /** Whether the current visit is pre- or post-order. */ + enum bftw_visit visit; /** The root path of the walk. */ const char *root; - /** The current path being explored. */ - char *path; /** Extra data about the current file. */ struct BFTW ftwbuf; @@ -607,7 +708,7 @@ static int bftw_state_init(struct bftw_state *state, const char *root, bftw_fn * if (nopenfd < 2) { errno = EMFILE; - return -1; + goto err; } // -1 to account for dup() @@ -617,16 +718,12 @@ static int bftw_state_init(struct bftw_state *state, const char *root, bftw_fn * bftw_queue_init(&state->queue); - state->current = NULL; - state->previous = NULL; - state->dir = NULL; - state->status = BFTW_CURRENT; - - state->path = dstralloc(0); - if (!state->path) { + if (bftw_reader_init(&state->reader) != 0) { goto err_cache; } + state->visit = BFTW_PRE; + return 0; err_cache: @@ -636,154 +733,71 @@ err: } /** - * Compute the path to the current bftw_dir. - */ -static int bftw_build_path(struct bftw_state *state) { - const struct bftw_dir *dir = state->current; - size_t namelen = dir->namelen; - size_t pathlen = dir->nameoff + namelen; - - if (dstresize(&state->path, pathlen) != 0) { - return -1; - } - - // Only rebuild the part of the path that changes - const struct bftw_dir *base = state->previous; - while (base && base->depth > dir->depth) { - base = base->parent; - } - - // Build the path backwards - char *path = state->path; - while (dir != base) { - char *segment = path + dir->nameoff; - namelen = dir->namelen; - memcpy(segment, dir->name, namelen); - - if (base && base->depth == dir->depth) { - base = base->parent; - } - dir = dir->parent; - } - - state->previous = state->current; - - return 0; -} - -/** - * Concatenate a subpath to the current path. - */ -static int bftw_path_concat(struct bftw_state *state, const char *subpath) { - size_t nameoff = 0; - - struct bftw_dir *current = state->current; - if (current) { - nameoff = current->nameoff + current->namelen; - } - - state->status = BFTW_CHILD; - - if (dstresize(&state->path, nameoff) != 0) { - return -1; - } - return dstrcat(&state->path, subpath); -} - -/** - * Trim the path to just the current directory. + * Update the path for the current file. */ -static void bftw_path_trim(struct bftw_state *state) { - struct bftw_dir *current = state->current; +static int bftw_update_path(struct bftw_state *state) { + struct bftw_reader *reader = &state->reader; + struct bftw_dir *dir = reader->dir; + struct dirent *de = reader->de; size_t length; - if (current->depth == 0) { - // Use exactly the string passed to bftw(), including any - // trailing slashes + if (de) { + length = dir->nameoff + dir->namelen; + } else if (dir->depth == 0) { + // Use exactly the string passed to bftw(), including any trailing slashes length = strlen(state->root); } else { - length = current->nameoff + current->namelen; - if (current->namelen > 1) { - // Trim the trailing slash - --length; - state->previous = current->parent; - } + // -1 to trim the trailing slash + length = dir->nameoff + dir->namelen - 1; } - dstresize(&state->path, length); - - if (state->status == BFTW_CHILD) { - state->status = BFTW_CURRENT; - } -} - -/** - * Open the current directory. - */ -static int bftw_opendir(struct bftw_state *state) { - assert(!state->dir); - state->dir = bftw_dir_opendir(&state->cache, state->current, state->path); - if (state->dir) { - return 0; - } else { - return 1; + if (dstrlen(reader->path) < length) { + errno = reader->error; + return -1; } -} + dstresize(&reader->path, length); -/** - * Close the current directory. - */ -static int bftw_closedir(struct bftw_state *state) { - DIR *dir = state->dir; - state->dir = NULL; - if (dir) { - return closedir(dir); - } else { - return 0; + if (de) { + if (dstrcat(&reader->path, reader->de->d_name) != 0) { + return -1; + } } -} - -/** - * Record an error. - */ -static void bftw_set_error(struct bftw_state *state, int error) { - state->ftwbuf.error = error; - state->ftwbuf.typeflag = BFTW_ERROR; - if (!(state->flags & BFTW_RECOVER)) { - state->error = error; - } + return 0; } /** * Initialize the buffers with data about the current path. */ -static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de) { +static void bftw_prepare_visit(struct bftw_state *state) { + struct bftw_reader *reader = &state->reader; + struct bftw_dir *dir = reader->dir; + struct dirent *de = reader->de; + struct BFTW *ftwbuf = &state->ftwbuf; - ftwbuf->path = state->path; + ftwbuf->path = dir ? reader->path : state->root; ftwbuf->root = state->root; - ftwbuf->error = 0; - ftwbuf->visit = (state->status == BFTW_GC ? BFTW_POST : BFTW_PRE); + ftwbuf->depth = 0; + ftwbuf->error = reader->error; + ftwbuf->visit = state->visit; ftwbuf->statbuf = NULL; ftwbuf->at_fd = AT_FDCWD; ftwbuf->at_path = ftwbuf->path; + ftwbuf->at_flags = AT_SYMLINK_NOFOLLOW; - struct bftw_dir *current = state->current; - if (current) { - ftwbuf->nameoff = current->nameoff; - ftwbuf->depth = current->depth; + if (dir) { + ftwbuf->nameoff = dir->nameoff; + ftwbuf->depth = dir->depth; - if (state->status == BFTW_CHILD) { - ftwbuf->nameoff += current->namelen; + if (de) { + ftwbuf->nameoff += dir->namelen; ++ftwbuf->depth; - ftwbuf->at_fd = current->fd; + ftwbuf->at_fd = dir->fd; ftwbuf->at_path += ftwbuf->nameoff; } else { - bftw_dir_base(current, &ftwbuf->at_fd, &ftwbuf->at_path); + bftw_dir_base(dir, &ftwbuf->at_fd, &ftwbuf->at_path); } - } else { - ftwbuf->depth = 0; } if (ftwbuf->depth == 0) { @@ -791,11 +805,15 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de) ftwbuf->nameoff = xbasename(ftwbuf->path) - ftwbuf->path; } - ftwbuf->typeflag = BFTW_UNKNOWN; - if (de) { + if (ftwbuf->error != 0) { + ftwbuf->typeflag = BFTW_ERROR; + return; + } else if (de) { ftwbuf->typeflag = dirent_to_typeflag(de); - } else if (state->status != BFTW_CHILD) { + } else if (ftwbuf->depth > 0) { ftwbuf->typeflag = BFTW_DIR; + } else { + ftwbuf->typeflag = BFTW_UNKNOWN; } int follow_flags = BFTW_LOGICAL; @@ -803,10 +821,11 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de) follow_flags |= BFTW_COMFOLLOW; } bool follow = state->flags & follow_flags; - ftwbuf->at_flags = follow ? 0 : AT_SYMLINK_NOFOLLOW; + if (follow) { + ftwbuf->at_flags = 0; + } - bool detect_cycles = (state->flags & BFTW_DETECT_CYCLES) - && state->status == BFTW_CHILD; + bool detect_cycles = (state->flags & BFTW_DETECT_CYCLES) && de; bool xdev = state->flags & BFTW_XDEV; @@ -814,17 +833,19 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de) || ftwbuf->typeflag == BFTW_UNKNOWN || (ftwbuf->typeflag == BFTW_LNK && follow) || (ftwbuf->typeflag == BFTW_DIR && (detect_cycles || xdev))) { - if (ftwbuf_stat(ftwbuf, &state->statbuf) != 0) { - bftw_set_error(state, errno); + if (bftw_stat(ftwbuf, &state->statbuf) != 0) { + ftwbuf->error = errno; + ftwbuf->typeflag = BFTW_ERROR; return; } if (ftwbuf->typeflag == BFTW_DIR && detect_cycles) { dev_t dev = ftwbuf->statbuf->dev; ino_t ino = ftwbuf->statbuf->ino; - for (const struct bftw_dir *dir = current; dir; dir = dir->parent) { - if (dev == dir->dev && ino == dir->ino) { - bftw_set_error(state, ELOOP); + for (const struct bftw_dir *parent = dir; parent; parent = parent->parent) { + if (dev == parent->dev && ino == parent->ino) { + ftwbuf->error = ELOOP; + ftwbuf->typeflag = BFTW_ERROR; return; } } @@ -832,16 +853,23 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de) } } -/** internal action: Abort the traversal. */ -#define BFTW_FAIL (-1) - /** - * Invoke the callback on the current file. + * Invoke the callback. */ -static int bftw_handle_path(struct bftw_state *state) { +static int bftw_visit_path(struct bftw_state *state) { + if (state->reader.dir) { + if (bftw_update_path(state) != 0) { + state->error = errno; + return BFTW_STOP; + } + } + + bftw_prepare_visit(state); + // Never give the callback BFTW_ERROR unless BFTW_RECOVER is specified if (state->ftwbuf.typeflag == BFTW_ERROR && !(state->flags & BFTW_RECOVER)) { - return BFTW_FAIL; + state->error = state->ftwbuf.error; + return BFTW_STOP; } // Defensive copy @@ -857,17 +885,19 @@ static int bftw_handle_path(struct bftw_state *state) { default: state->error = EINVAL; - return BFTW_FAIL; + return BFTW_STOP; } } /** - * Create a new bftw_dir for the current file. + * Push a new directory onto the queue. */ -static struct bftw_dir *bftw_add(struct bftw_state *state, const char *name) { - struct bftw_dir *dir = bftw_dir_new(&state->cache, state->current, name); +static int bftw_push(struct bftw_state *state, const char *name) { + struct bftw_dir *parent = state->reader.dir; + struct bftw_dir *dir = bftw_dir_new(&state->cache, parent, name); if (!dir) { - return NULL; + state->error = errno; + return -1; } const struct bfs_stat *statbuf = state->ftwbuf.statbuf; @@ -876,41 +906,31 @@ static struct bftw_dir *bftw_add(struct bftw_state *state, const char *name) { dir->ino = statbuf->ino; } - return dir; + bftw_queue_push(&state->queue, dir); + return 0; } /** - * Push a new directory onto the queue. + * Pop a directory from the queue and start reading it. */ -static int bftw_push(struct bftw_state *state, const char *name) { - struct bftw_dir *dir = bftw_add(state, name); - if (!dir) { - return -1; - } - - bftw_queue_push(&state->queue, dir); - return 0; +static struct bftw_reader *bftw_pop(struct bftw_state *state) { + struct bftw_reader *reader = &state->reader; + struct bftw_dir *dir = bftw_queue_pop(&state->queue); + bftw_reader_open(reader, &state->cache, dir); + return reader; } /** - * Pop a directory off the queue. + * Finalize and free a directory we're done with. */ -static int bftw_pop(struct bftw_state *state, bool invoke_callback) { +static int bftw_release_dir(struct bftw_state *state, struct bftw_dir *dir, bool do_visit) { int ret = BFTW_CONTINUE; - struct bftw_dir *dir = state->current; if (!(state->flags & BFTW_DEPTH)) { - invoke_callback = false; - } - - if (invoke_callback) { - if (bftw_build_path(state) != 0) { - ret = BFTW_FAIL; - invoke_callback = false; - } + do_visit = false; } - state->status = BFTW_GC; + state->visit = BFTW_POST; while (dir) { bftw_dir_decref(&state->cache, dir); @@ -918,23 +938,11 @@ static int bftw_pop(struct bftw_state *state, bool invoke_callback) { break; } - if (invoke_callback) { - state->current = dir; - bftw_path_trim(state); - bftw_init_buffers(state, NULL); - - int action = bftw_handle_path(state); - switch (action) { - case BFTW_CONTINUE: - case BFTW_SKIP_SIBLINGS: - case BFTW_SKIP_SUBTREE: - break; - - case BFTW_STOP: - case BFTW_FAIL: - ret = action; - invoke_callback = false; - break; + if (do_visit) { + state->reader.dir = dir; + if (bftw_visit_path(state) == BFTW_STOP) { + ret = BFTW_STOP; + do_visit = false; } } @@ -943,54 +951,82 @@ static int bftw_pop(struct bftw_state *state, bool invoke_callback) { dir = parent; } - state->previous = dir; - state->current = bftw_queue_pop(&state->queue); - state->status = BFTW_CURRENT; + state->visit = BFTW_PRE; + return ret; +} + +/** + * Close and release the reader. + */ +static int bftw_release_reader(struct bftw_state *state, bool do_visit) { + int ret = BFTW_CONTINUE; + + struct bftw_reader *reader = &state->reader; + if (reader->handle) { + bftw_reader_close(reader); + } + + if (reader->error != 0) { + if (do_visit) { + if (bftw_visit_path(state) == BFTW_STOP) { + ret = BFTW_STOP; + do_visit = false; + } + } else { + state->error = reader->error; + } + reader->error = 0; + } + + if (bftw_release_dir(state, reader->dir, do_visit) == BFTW_STOP) { + ret = BFTW_STOP; + } + + reader->dir = NULL; + return ret; } /** * Dispose of the bftw() state. + * + * @return + * The bftw() return value. */ -static void bftw_state_destroy(struct bftw_state *state) { - bftw_closedir(state); +static int bftw_state_destroy(struct bftw_state *state) { + struct bftw_reader *reader = &state->reader; + if (reader->dir) { + bftw_release_reader(state, false); + } + bftw_reader_destroy(reader); - while (state->current) { - bftw_pop(state, false); + struct bftw_queue *queue = &state->queue; + while (queue->head) { + struct bftw_dir *dir = bftw_queue_pop(queue); + bftw_release_dir(state, dir, false); } bftw_cache_destroy(&state->cache); - dstrfree(state->path); + errno = state->error; + if (state->error == 0) { + return 0; + } else { + return -1; + } } int bftw(const char *path, bftw_fn *fn, int nopenfd, enum bftw_flags flags, void *ptr) { - int ret = -1, error; - struct bftw_state state; if (bftw_state_init(&state, path, fn, nopenfd, flags, ptr) != 0) { return -1; } // Handle 'path' itself first - - if (bftw_path_concat(&state, path) != 0) { - goto fail; - } - - bftw_init_buffers(&state, NULL); - - switch (bftw_handle_path(&state)) { - case BFTW_CONTINUE: - case BFTW_SKIP_SIBLINGS: - break; - + switch (bftw_visit_path(&state)) { case BFTW_SKIP_SUBTREE: case BFTW_STOP: goto done; - - case BFTW_FAIL: - goto fail; } if (state.ftwbuf.typeflag != BFTW_DIR) { @@ -999,41 +1035,20 @@ int bftw(const char *path, bftw_fn *fn, int nopenfd, enum bftw_flags flags, void // Now start the breadth-first search - state.current = bftw_add(&state, path); - if (!state.current) { - goto fail; + if (bftw_push(&state, path) != 0) { + goto done; } - do { - if (bftw_build_path(&state) != 0) { - goto fail; - } - - if (bftw_opendir(&state) != 0) { - goto dir_error; - } - - while (true) { - struct dirent *de; - if (xreaddir(state.dir, &de) != 0) { - goto dir_error; - } - if (!de) { - break; - } + while (state.queue.head) { + struct bftw_reader *reader = bftw_pop(&state); - const char *name = de->d_name; + while (reader->de) { + const char *name = reader->de->d_name; if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) { - continue; - } - - if (bftw_path_concat(&state, name) != 0) { - goto fail; + goto read; } - bftw_init_buffers(&state, de); - - switch (bftw_handle_path(&state)) { + switch (bftw_visit_path(&state)) { case BFTW_CONTINUE: break; @@ -1041,81 +1056,35 @@ int bftw(const char *path, bftw_fn *fn, int nopenfd, enum bftw_flags flags, void goto next; case BFTW_SKIP_SUBTREE: - continue; + goto read; case BFTW_STOP: goto done; - - case BFTW_FAIL: - goto fail; } if (state.ftwbuf.typeflag == BFTW_DIR) { const struct bfs_stat *statbuf = state.ftwbuf.statbuf; if ((flags & BFTW_XDEV) && statbuf - && statbuf->dev != state.current->dev) { - continue; + && statbuf->dev != reader->dir->dev) { + goto read; } if (bftw_push(&state, name) != 0) { - goto fail; + goto done; } } - } - next: - if (bftw_closedir(&state) != 0) { - goto dir_error; + read: + bftw_reader_read(reader); } - switch (bftw_pop(&state, true)) { - case BFTW_CONTINUE: - case BFTW_SKIP_SIBLINGS: - case BFTW_SKIP_SUBTREE: - break; - - case BFTW_STOP: - goto done; - - case BFTW_FAIL: - goto fail; - } - continue; - - dir_error: - error = errno; - bftw_closedir(&state); - bftw_path_trim(&state); - bftw_init_buffers(&state, NULL); - bftw_set_error(&state, error); - - switch (bftw_handle_path(&state)) { - case BFTW_CONTINUE: - case BFTW_SKIP_SIBLINGS: - case BFTW_SKIP_SUBTREE: - goto next; - - case BFTW_STOP: + next: + if (bftw_release_reader(&state, true) == BFTW_STOP) { goto done; - - case BFTW_FAIL: - goto fail; } - } while (state.current); - -done: - if (state.error == 0) { - ret = 0; - } - -fail: - if (state.error == 0) { - state.error = errno; } - bftw_state_destroy(&state); - - errno = state.error; - return ret; +done: + return bftw_state_destroy(&state); } -- cgit v1.2.3