summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2016-10-22 21:05:47 -0400
committerTavian Barnes <tavianator@tavianator.com>2016-10-24 11:19:24 -0400
commit3c8233869d34713860c48b1230c3ea06b8767b88 (patch)
treed2c95dd2370cfd8b1255c08c83b953c29ccd91f2
parent087b29c53e13299e195942e48ae309817f4f1d93 (diff)
downloadbfs-3c8233869d34713860c48b1230c3ea06b8767b88.tar.xz
Implement -ignore_readdir_race.
-rw-r--r--bfs.h2
-rw-r--r--bftw.c5
-rw-r--r--eval.c42
-rw-r--r--parse.c24
-rwxr-xr-xtests.sh30
-rwxr-xr-xtests/remove-sibling.sh10
6 files changed, 82 insertions, 31 deletions
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
@@ -52,12 +52,23 @@ struct eval_state {
};
/**
+ * 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
@@ -1035,6 +1035,14 @@ static struct expr *parse_hidden(struct parser_state *state, int arg1, int arg2)
}
/**
+ * 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.
*/
static struct expr *parse_inum(struct parser_state *state, int arg1, int arg2) {
@@ -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