summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-02-06 23:48:40 -0500
committerTavian Barnes <tavianator@tavianator.com>2017-02-06 23:48:40 -0500
commitaba099bbca9d73215affd49895a391e7eded0a4a (patch)
tree695f68f4d9dd390f539ab85c0b17b1334c3a44a8
parent3fd6439f571a56771c6bbc21fa638cb94c929cc3 (diff)
downloadbfs-aba099bbca9d73215affd49895a391e7eded0a4a.tar.xz
bftw: Plug a leak if dirqueue_push() fails
If bftw_add() succeeds but dirqueue_push() fails, we need to clean up the just-added dircache_entry. Otherwise it will leak, and we'll also fail the cache->size == 0 assertion. Fix it by extracting the dircache-related parts of bftw_pop() into a new helper function bftw_gc(), and call it from bftw_pop() as well as the bftw_push() failure path.
-rw-r--r--bftw.c44
1 files changed, 28 insertions, 16 deletions
diff --git a/bftw.c b/bftw.c
index 4ac1240..ab16b67 100644
--- a/bftw.c
+++ b/bftw.c
@@ -881,23 +881,10 @@ static struct dircache_entry *bftw_add(struct bftw_state *state, const char *nam
}
/**
- * Push a new entry onto the queue.
- */
-static int bftw_push(struct bftw_state *state, const char *name) {
- struct dircache_entry *entry = bftw_add(state, name);
- if (!entry) {
- return -1;
- }
-
- return dirqueue_push(&state->queue, entry);
-}
-
-/**
- * Pop an entry off the queue.
+ * Garbage-collect a dircache entry.
*/
-static int bftw_pop(struct bftw_state *state, bool invoke_callback) {
+static int bftw_gc(struct bftw_state *state, struct dircache_entry *entry, bool invoke_callback) {
int ret = BFTW_CONTINUE;
- struct dircache_entry *entry = state->current;
if (!(state->flags & BFTW_DEPTH)) {
invoke_callback = false;
@@ -944,9 +931,34 @@ static int bftw_pop(struct bftw_state *state, bool invoke_callback) {
dircache_entry_free(&state->cache, current);
}
+ return ret;
+}
+
+/**
+ * Push a new entry onto the queue.
+ */
+static int bftw_push(struct bftw_state *state, const char *name) {
+ struct dircache_entry *entry = bftw_add(state, name);
+ if (!entry) {
+ return -1;
+ }
+
+ if (dirqueue_push(&state->queue, entry) != 0) {
+ state->error = errno;
+ bftw_gc(state, entry, false);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Pop an entry off the queue.
+ */
+static int bftw_pop(struct bftw_state *state, bool invoke_callback) {
+ int ret = bftw_gc(state, state->current, invoke_callback);
state->current = dirqueue_pop(&state->queue);
state->status = BFTW_CURRENT;
-
return ret;
}