From 46e32a2c62f6500ff0b278f93a7286660ee0a008 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 2 Oct 2016 14:29:05 -0400 Subject: bftw: Handle errors from readdir(). --- bftw.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 25 deletions(-) (limited to 'bftw.c') diff --git a/bftw.c b/bftw.c index a969cf4..ac99a42 100644 --- a/bftw.c +++ b/bftw.c @@ -633,6 +633,24 @@ static int bftw_path_concat(struct bftw_state *state, const char *subpath) { return dstrcat(&state->path, subpath); } +/** + * Trim the path to just the current directory. + */ +static void bftw_path_trim(struct bftw_state *state) { + struct dircache_entry *current = state->current; + + size_t length = current->nameoff + current->namelen; + if (current->namelen > 1) { + // Trim the trailing slash + --length; + } + dstresize(&state->path, length); + + if (state->status == BFTW_CHILD) { + state->status = BFTW_CURRENT; + } +} + /** * Record an error. */ @@ -778,6 +796,19 @@ static struct dircache_entry *bftw_add(struct bftw_state *state, const char *nam return entry; } +/** + * readdir() wrapper that makes error handling cleaner. + */ +static int bftw_readdir(DIR *dir, struct dirent **de) { + errno = 0; + *de = readdir(dir); + if (!*de && errno != 0) { + return -1; + } else { + return 0; + } +} + /** * Push a new entry onto the queue. */ @@ -820,14 +851,8 @@ static int bftw_pop(struct bftw_state *state, bool invoke_callback) { } if (invoke_callback) { - size_t length = current->nameoff + current->namelen; - if (current->namelen > 1) { - // Trim the trailing slash - --length; - } - dstresize(&state->path, length); - state->current = current; + bftw_path_trim(state); bftw_init_buffers(state, NULL); int action = bftw_handle_path(state); @@ -915,27 +940,18 @@ int bftw(const char *path, bftw_fn *fn, int nopenfd, enum bftw_flags flags, void DIR *dir = dircache_entry_open(&state.cache, state.current, state.path); if (!dir) { - int error = errno; - - 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: - goto done; + goto dir_error; + } - case BFTW_FAIL: - goto fail; + while (true) { + struct dirent *de; + if (bftw_readdir(dir, &de) != 0) { + goto dir_error; + } + if (!de) { + break; } - } - struct dirent *de; - while ((de = readdir(dir)) != NULL) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) { continue; } @@ -996,6 +1012,31 @@ int bftw(const char *path, bftw_fn *fn, int nopenfd, enum bftw_flags flags, void case BFTW_FAIL: goto fail; } + continue; + + dir_error: + state.error = errno; + + if (dir) { + closedir(dir); + } + + bftw_path_trim(&state); + bftw_init_buffers(&state, NULL); + bftw_set_error(&state, state.error); + + switch (bftw_handle_path(&state)) { + case BFTW_CONTINUE: + case BFTW_SKIP_SIBLINGS: + case BFTW_SKIP_SUBTREE: + goto next; + + case BFTW_STOP: + goto done; + + case BFTW_FAIL: + goto fail; + } } while (state.current); done: -- cgit v1.2.3