summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2016-10-02 14:29:05 -0400
committerTavian Barnes <tavianator@tavianator.com>2016-10-02 14:29:38 -0400
commit46e32a2c62f6500ff0b278f93a7286660ee0a008 (patch)
tree48ca7fa5db18c289a3de221a8087434d282ea58f
parent94a804972d9e2099bb38461161e82277e5ab1747 (diff)
downloadbfs-46e32a2c62f6500ff0b278f93a7286660ee0a008.tar.xz
bftw: Handle errors from readdir().
-rw-r--r--bftw.c91
1 files changed, 66 insertions, 25 deletions
diff --git a/bftw.c b/bftw.c
index a969cf4..ac99a42 100644
--- a/bftw.c
+++ b/bftw.c
@@ -634,6 +634,24 @@ static int bftw_path_concat(struct bftw_state *state, const char *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.
*/
static void bftw_set_error(struct bftw_state *state, int error) {
@@ -779,6 +797,19 @@ static struct dircache_entry *bftw_add(struct bftw_state *state, const char *nam
}
/**
+ * 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.
*/
static int bftw_push(struct bftw_state *state, const char *name) {
@@ -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: