From 3c8233869d34713860c48b1230c3ea06b8767b88 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 22 Oct 2016 21:05:47 -0400 Subject: Implement -ignore_readdir_race. --- bfs.h | 2 ++ bftw.c | 5 ++++- eval.c | 42 +++++++++++++++++++++++++----------------- parse.c | 24 +++++++++++++++++++----- tests.sh | 30 ++++++++++++++++++++++-------- tests/remove-sibling.sh | 10 ++++++++++ 6 files changed, 82 insertions(+), 31 deletions(-) create mode 100755 tests/remove-sibling.sh diff --git a/bfs.h b/bfs.h index e3090c9..b504f0c 100644 --- a/bfs.h +++ b/bfs.h @@ -106,6 +106,8 @@ struct cmdline { int optlevel; /** Debugging flags. */ enum debug_flags debug; + /** Whether to ignore deletions that race with bfs. */ + bool ignore_races; /** The command line expression. */ struct expr *expr; diff --git a/bftw.c b/bftw.c index 45d2d27..f48d3f8 100644 --- a/bftw.c +++ b/bftw.c @@ -713,9 +713,12 @@ static void bftw_path_trim(struct bftw_state *state) { * Record an error. */ static void bftw_set_error(struct bftw_state *state, int error) { - state->error = error; state->ftwbuf.error = error; state->ftwbuf.typeflag = BFTW_ERROR; + + if (!(state->flags & BFTW_RECOVER)) { + state->error = error; + } } /** diff --git a/eval.c b/eval.c index 4778b5d..f930bac 100644 --- a/eval.c +++ b/eval.c @@ -51,13 +51,24 @@ struct eval_state { struct stat statbuf; }; +/** + * Check if an error should be ignored. + */ +static bool eval_should_ignore(const struct eval_state *state, int error) { + return state->cmdline->ignore_races + && error == ENOENT + && state->ftwbuf->depth > 0; +} + /** * Report an error that occurs during evaluation. */ static void eval_error(struct eval_state *state) { - pretty_error(state->cmdline->stderr_colors, - "'%s': %s\n", state->ftwbuf->path, strerror(errno)); - state->ret = -1; + if (!eval_should_ignore(state, errno)) { + pretty_error(state->cmdline->stderr_colors, + "'%s': %s\n", state->ftwbuf->path, strerror(errno)); + state->ret = -1; + } } /** @@ -905,8 +916,6 @@ struct callback_args { const struct cmdline *cmdline; /** Eventual return value from eval_cmdline(). */ int ret; - /** The last error code seen. */ - int last_error; }; /** @@ -917,12 +926,6 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { const struct cmdline *cmdline = args->cmdline; - if (ftwbuf->typeflag == BFTW_ERROR) { - args->last_error = ftwbuf->error; - pretty_error(cmdline->stderr_colors, "'%s': %s\n", ftwbuf->path, strerror(ftwbuf->error)); - return BFTW_SKIP_SUBTREE; - } - struct eval_state state = { .ftwbuf = ftwbuf, .cmdline = cmdline, @@ -930,6 +933,15 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { .ret = args->ret, }; + if (ftwbuf->typeflag == BFTW_ERROR) { + if (!eval_should_ignore(&state, ftwbuf->error)) { + state.ret = -1; + pretty_error(cmdline->stderr_colors, "'%s': %s\n", ftwbuf->path, strerror(ftwbuf->error)); + } + state.action = BFTW_SKIP_SUBTREE; + goto done; + } + if (ftwbuf->depth >= cmdline->maxdepth) { state.action = BFTW_SKIP_SUBTREE; } @@ -948,6 +960,7 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { eval_expr(cmdline->expr, &state); } +done: if ((cmdline->debug & DEBUG_STAT) && ftwbuf->statbuf) { debug_stat(&state); } @@ -1024,14 +1037,9 @@ int eval_cmdline(const struct cmdline *cmdline) { }; for (struct root *root = cmdline->roots; root; root = root->next) { - args.last_error = 0; - if (bftw(root->path, cmdline_callback, nopenfd, cmdline->flags, &args) != 0) { args.ret = -1; - - if (errno != args.last_error) { - perror("bftw()"); - } + perror("bftw()"); } } diff --git a/parse.c b/parse.c index 023d414..b17192e 100644 --- a/parse.c +++ b/parse.c @@ -1034,6 +1034,14 @@ static struct expr *parse_hidden(struct parser_state *state, int arg1, int arg2) return parse_nullary_test(state, eval_hidden); } +/** + * Parse -(no)?ignore_readdir_race. + */ +static struct expr *parse_ignore_races(struct parser_state *state, int ignore, int arg2) { + state->cmdline->ignore_races = ignore; + return parse_nullary_option(state); +} + /** * Parse -inum N. */ @@ -1443,6 +1451,7 @@ static const struct table_entry parse_table[] = { {"group", false, parse_group}, {"help", false, parse_help}, {"hidden", false, parse_hidden}, + {"ignore_readdir_race", false, parse_ignore_races, true}, {"ilname", false, parse_lname, true}, {"iname", false, parse_name, true}, {"inum", false, parse_inum}, @@ -1460,6 +1469,7 @@ static const struct table_entry parse_table[] = { {"newer", true, parse_newerxy}, {"nocolor", false, parse_color, false}, {"nohidden", false, parse_nohidden}, + {"noignore_readdir_race", false, parse_ignore_races, false}, {"noleaf", false, parse_noleaf}, {"not"}, {"nowarn", false, parse_warn, false}, @@ -1932,9 +1942,17 @@ void dump_cmdline(const struct cmdline *cmdline, bool verbose) { fprintf(stderr, "%s ", root->path); } + if (cmdline->stdout_colors) { + fputs("-color ", stderr); + } else { + fputs("-nocolor ", stderr); + } if (cmdline->flags & BFTW_DEPTH) { fputs("-depth ", stderr); } + if (cmdline->ignore_races) { + fputs("-ignore_readdir_race ", stderr); + } if (cmdline->flags & BFTW_MOUNT) { fputs("-mount ", stderr); } @@ -1944,11 +1962,6 @@ void dump_cmdline(const struct cmdline *cmdline, bool verbose) { if (cmdline->maxdepth != INT_MAX) { fprintf(stderr, "-maxdepth %d ", cmdline->maxdepth); } - if (cmdline->stdout_colors) { - fputs("-color ", stderr); - } else { - fputs("-nocolor ", stderr); - } dump_expr(cmdline->expr, verbose); @@ -1993,6 +2006,7 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { cmdline->flags = BFTW_RECOVER; cmdline->optlevel = 3; cmdline->debug = 0; + cmdline->ignore_races = false; cmdline->expr = &expr_true; cmdline->nopen_files = 0; diff --git a/tests.sh b/tests.sh index 63de3ee..2206877 100755 --- a/tests.sh +++ b/tests.sh @@ -62,11 +62,12 @@ function make_weirdnames() { weirdnames="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.weirdnames.XXXXXXXXXX)" make_weirdnames "$weirdnames" -out="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.out.XXXXXXXXXX)" +# Create a scratch directory that tests can modify +scratch="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.weirdnames.XXXXXXXXXX)" # Clean up temporary directories on exit function cleanup() { - rm -rf "$out" + rm -rf "$scratch" rm -rf "$weirdnames" rm -rf "$links" rm -rf "$perms" @@ -359,12 +360,12 @@ function test_0064() { } function test_0065() { - find "$basic" -fprint "$out/out.find" - "$BFS" "$basic" -fprint "$out/out.bfs" + find "$basic" -fprint "$scratch/out.find" + "$BFS" "$basic" -fprint "$scratch/out.bfs" - sort -o "$out/out.find" "$out/out.find" - sort -o "$out/out.bfs" "$out/out.bfs" - diff -u "$out/out.find" "$out/out.bfs" + sort -o "$scratch/out.find" "$scratch/out.find" + sort -o "$scratch/out.bfs" "$scratch/out.bfs" + diff -u "$scratch/out.find" "$scratch/out.bfs" } function test_0066() { @@ -377,7 +378,20 @@ function test_0067() { find_diff -L -- -type f } -for i in {1..67}; do +function test_0068() { + # Make sure -ignore_readdir_race doesn't suppress ENOENT at the root + ! "$BFS" "$basic/nonexistent" -ignore_readdir_race 2>/dev/null +} + +function test_0069() { + rm -rf "$scratch"/* + touch "$scratch"/{foo,bar} + + # -links 1 forces a stat() call, which will fail for the second file + "$BFS" "$scratch" -ignore_readdir_race -links 1 -exec ./tests/remove-sibling.sh '{}' ';' +} + +for i in {1..69}; do test="test_$(printf '%04d' $i)" ("$test" "$dir") status=$? diff --git a/tests/remove-sibling.sh b/tests/remove-sibling.sh new file mode 100755 index 0000000..ddf81d7 --- /dev/null +++ b/tests/remove-sibling.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +for file in "${1%/*}"/*; do + if [ "$file" != "$1" ]; then + rm "$file" + exit $? + fi +done + +exit 1 -- cgit v1.2.3