diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2017-02-06 23:48:40 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2017-02-06 23:48:40 -0500 |
commit | aba099bbca9d73215affd49895a391e7eded0a4a (patch) | |
tree | 695f68f4d9dd390f539ab85c0b17b1334c3a44a8 | |
parent | 3fd6439f571a56771c6bbc21fa638cb94c929cc3 (diff) | |
download | bfs-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.c | 44 |
1 files changed, 28 insertions, 16 deletions
@@ -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; } |