summaryrefslogtreecommitdiffstats
path: root/eval.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2019-05-04 11:51:56 -0400
committerTavian Barnes <tavianator@tavianator.com>2019-05-04 11:55:07 -0400
commit09119a3e8fa5f206eb4254144a1c8e77bc6e587e (patch)
tree924146ec43e912f2271ab90a470cd09492919db7 /eval.c
parentd8e10d648b12b8595e9f177ec8f1a71d24aecea5 (diff)
downloadbfs-09119a3e8fa5f206eb4254144a1c8e77bc6e587e.tar.xz
bftw: Add a caching stat() API to struct BFTW
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c209
1 files changed, 75 insertions, 134 deletions
diff --git a/eval.c b/eval.c
index 1c4ff80..3bcdfeb 100644
--- a/eval.c
+++ b/eval.c
@@ -59,79 +59,8 @@ struct eval_state {
int *ret;
/** Whether to quit immediately. */
bool *quit;
- /** A bfs_stat() buffer, if necessary. */
- struct bfs_stat statbuf;
- /** A bfs_stat() buffer for -xtype style tests. */
- struct bfs_stat xstatbuf;
};
-#define DEBUG_FLAG(flags, flag) \
- do { \
- if ((flags & flag) || flags == flag) { \
- fputs(#flag, stderr); \
- flags ^= flag; \
- if (flags) { \
- fputs(" | ", stderr); \
- } \
- } \
- } while (0)
-
-/**
- * Debug stat() calls.
- */
-static void debug_stat(const struct eval_state *state, enum bfs_stat_flag flags) {
- if (!(state->cmdline->debug & DEBUG_STAT)) {
- return;
- }
-
- struct BFTW *ftwbuf = state->ftwbuf;
-
- fprintf(stderr, "bfs_stat(");
- if (ftwbuf->at_fd == AT_FDCWD) {
- fprintf(stderr, "AT_FDCWD");
- } else {
- size_t baselen = strlen(ftwbuf->path) - strlen(ftwbuf->at_path);
- fprintf(stderr, "\"");
- fwrite(ftwbuf->path, 1, baselen, stderr);
- fprintf(stderr, "\"");
- }
-
- fprintf(stderr, ", \"%s\", ", ftwbuf->at_path);
-
- DEBUG_FLAG(flags, BFS_STAT_FOLLOW);
- DEBUG_FLAG(flags, BFS_STAT_NOFOLLOW);
- DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW);
-
- fprintf(stderr, ")\n");
-}
-
-/**
- * Perform a bfs_stat() call if necessary.
- */
-static const struct bfs_stat *eval_try_stat(struct eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
-
- if (ftwbuf->statbuf) {
- goto done;
- }
-
- if (ftwbuf->error) {
- errno = ftwbuf->error;
- goto done;
- }
-
- debug_stat(state, ftwbuf->stat_flags);
-
- if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, &state->statbuf) == 0) {
- ftwbuf->statbuf = &state->statbuf;
- } else {
- ftwbuf->error = errno;
- }
-
-done:
- return ftwbuf->statbuf;
-}
-
/**
* Print an error message.
*/
@@ -141,10 +70,6 @@ static void eval_error(struct eval_state *state, const char *format, ...) {
const struct cmdline *cmdline = state->cmdline;
CFILE *cerr = cmdline->cerr;
- if (cerr->colors) {
- eval_try_stat(state);
- }
-
bfs_error(cmdline, "%pP: ", state->ftwbuf);
va_list args;
@@ -177,7 +102,8 @@ static void eval_report_error(struct eval_state *state) {
* Perform a bfs_stat() call if necessary.
*/
static const struct bfs_stat *eval_stat(struct eval_state *state) {
- const struct bfs_stat *ret = eval_try_stat(state);
+ struct BFTW *ftwbuf = state->ftwbuf;
+ const struct bfs_stat *ret = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!ret) {
eval_report_error(state);
}
@@ -185,22 +111,6 @@ static const struct bfs_stat *eval_stat(struct eval_state *state) {
}
/**
- * Perform a bfs_stat() call for tests that flip the follow flag, like -xtype.
- */
-static const struct bfs_stat *eval_xstat(struct eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
- struct bfs_stat *statbuf = &state->xstatbuf;
- if (!statbuf->mask) {
- enum bfs_stat_flag flags = ftwbuf->stat_flags ^ (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW);
- debug_stat(state, flags);
- if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, flags, statbuf) != 0) {
- return NULL;
- }
- }
- return statbuf;
-}
-
-/**
* Get the difference (in seconds) between two struct timespecs.
*/
static time_t timespec_diff(const struct timespec *lhs, const struct timespec *rhs) {
@@ -415,21 +325,14 @@ bool eval_delete(const struct expr *expr, struct eval_state *state) {
}
int flag = 0;
- if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) {
- if (ftwbuf->typeflag == BFTW_DIR) {
- flag |= AT_REMOVEDIR;
- }
- } else if (ftwbuf->typeflag != BFTW_LNK) {
- // We need to know the actual type of the path, not what it points to
- const struct bfs_stat *statbuf = eval_xstat(state);
- if (statbuf) {
- if (S_ISDIR(statbuf->mode)) {
- flag |= AT_REMOVEDIR;
- }
- } else {
- eval_report_error(state);
- return false;
- }
+
+ // We need to know the actual type of the path, not what it points to
+ enum bftw_typeflag type = bftw_typeflag(ftwbuf, BFS_STAT_NOFOLLOW);
+ if (type == BFTW_DIR) {
+ flag |= AT_REMOVEDIR;
+ } else if (type == BFTW_ERROR) {
+ eval_report_error(state);
+ return false;
}
if (unlinkat(ftwbuf->at_fd, ftwbuf->at_path, flag) != 0) {
@@ -698,7 +601,7 @@ bool eval_perm(const struct expr *expr, struct eval_state *state) {
bool eval_fls(const struct expr *expr, struct eval_state *state) {
CFILE *cfile = expr->cfile;
FILE *file = cfile->file;
- const struct BFTW *ftwbuf = state->ftwbuf;
+ struct BFTW *ftwbuf = state->ftwbuf;
const struct bfs_stat *statbuf = eval_stat(state);
if (!statbuf) {
goto done;
@@ -825,17 +728,10 @@ bool eval_fprint0(const struct expr *expr, struct eval_state *state) {
* -f?printf action.
*/
bool eval_fprintf(const struct expr *expr, struct eval_state *state) {
- if (expr->printf->needs_stat) {
- if (!eval_stat(state)) {
- goto done;
- }
- }
-
if (bfs_printf(expr->cfile->file, expr->printf, state->ftwbuf) != 0) {
eval_report_error(state);
}
-done:
return true;
}
@@ -989,22 +885,13 @@ bool eval_type(const struct expr *expr, struct eval_state *state) {
*/
bool eval_xtype(const struct expr *expr, struct eval_state *state) {
struct BFTW *ftwbuf = state->ftwbuf;
-
- bool follow = !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW);
- bool is_link = ftwbuf->typeflag == BFTW_LNK;
- if (follow == is_link) {
- return eval_type(expr, state);
- }
-
- const struct bfs_stat *statbuf = eval_xstat(state);
- if (statbuf) {
- return bftw_mode_typeflag(statbuf->mode) & expr->idata;
- } else if (!follow && is_nonexistence_error(errno)) {
- // Broken symlink
- return eval_type(expr, state);
- } else {
+ enum bfs_stat_flag flags = ftwbuf->stat_flags ^ (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW);
+ enum bftw_typeflag type = bftw_typeflag(ftwbuf, flags);
+ if (type == BFTW_ERROR) {
eval_report_error(state);
return false;
+ } else {
+ return type & expr->idata;
}
}
@@ -1156,6 +1043,61 @@ static bool eval_file_unique(struct eval_state *state, struct trie *seen) {
}
}
+#define DEBUG_FLAG(flags, flag) \
+ do { \
+ if ((flags & flag) || flags == flag) { \
+ fputs(#flag, stderr); \
+ flags ^= flag; \
+ if (flags) { \
+ fputs(" | ", stderr); \
+ } \
+ } \
+ } while (0)
+
+/**
+ * Log a stat() call.
+ */
+static void debug_stat(const struct BFTW *ftwbuf, const struct bftw_stat *cache, enum bfs_stat_flag flags) {
+ fprintf(stderr, "bfs_stat(");
+ if (ftwbuf->at_fd == AT_FDCWD) {
+ fprintf(stderr, "AT_FDCWD");
+ } else {
+ size_t baselen = strlen(ftwbuf->path) - strlen(ftwbuf->at_path);
+ fprintf(stderr, "\"");
+ fwrite(ftwbuf->path, 1, baselen, stderr);
+ fprintf(stderr, "\"");
+ }
+
+ fprintf(stderr, ", \"%s\", ", ftwbuf->at_path);
+
+ DEBUG_FLAG(flags, BFS_STAT_FOLLOW);
+ DEBUG_FLAG(flags, BFS_STAT_NOFOLLOW);
+ DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW);
+
+ fprintf(stderr, ") == %d", cache->buf ? 0 : -1);
+
+ if (cache->error) {
+ fprintf(stderr, " [%d]", cache->error);
+ }
+
+ fprintf(stderr, "\n");
+}
+
+/**
+ * Log any stat() calls that happened.
+ */
+static void debug_stats(const struct BFTW *ftwbuf) {
+ const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf;
+ if (statbuf || ftwbuf->stat_cache.error) {
+ debug_stat(ftwbuf, &ftwbuf->stat_cache, BFS_STAT_FOLLOW);
+ }
+
+ const struct bfs_stat *lstatbuf = ftwbuf->lstat_cache.buf;
+ if ((lstatbuf && lstatbuf != statbuf) || ftwbuf->lstat_cache.error) {
+ debug_stat(ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW);
+ }
+}
+
/**
* Dump the bftw_typeflag for -D search.
*/
@@ -1237,11 +1179,6 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) {
state.action = BFTW_CONTINUE;
state.ret = &args->ret;
state.quit = &args->quit;
- state.xstatbuf.mask = 0;
-
- if (ftwbuf->statbuf) {
- debug_stat(&state, ftwbuf->stat_flags);
- }
if (ftwbuf->typeflag == BFTW_ERROR) {
if (!eval_should_ignore(&state, ftwbuf->error)) {
@@ -1284,6 +1221,10 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) {
}
done:
+ if (cmdline->debug & DEBUG_STAT) {
+ debug_stats(ftwbuf);
+ }
+
if (cmdline->debug & DEBUG_SEARCH) {
fprintf(stderr,
"cmdline_callback({ "