From aba099bbca9d73215affd49895a391e7eded0a4a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 6 Feb 2017 23:48:40 -0500 Subject: 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. --- bftw.c | 44 ++++++++++++++++++++++++++++---------------- 1 file 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; } -- cgit v1.2.3