summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2020-09-18 17:34:03 -0400
committerTavian Barnes <tavianator@tavianator.com>2020-09-18 17:34:03 -0400
commitccf75c74bdac06eec97a2a6a5228c2e706c121bd (patch)
tree0e9b682bbdf15f9da668f7fe343a19ed17371b74
parent0aa71f890777d2aaddeca53384b94742e4b2678b (diff)
downloadbfs-ccf75c74bdac06eec97a2a6a5228c2e706c121bd.tar.xz
Don't call stat() just to determine symbolic lengths
The new bftw_cached_stat() helper gets us stat info if we already have it, but doesn't call stat() if we don't. In that case we just take a guess for the initial length to readlinkat(). This lets us avoid stat() entirely in many cases for -lname and -printf %l.
-rw-r--r--bftw.c8
-rw-r--r--bftw.h13
-rw-r--r--color.c7
-rw-r--r--eval.c8
-rw-r--r--printf.c5
5 files changed, 30 insertions, 11 deletions
diff --git a/bftw.c b/bftw.c
index ff4797d..1296360 100644
--- a/bftw.c
+++ b/bftw.c
@@ -886,6 +886,14 @@ const struct bfs_stat *bftw_stat(const struct BFTW *ftwbuf, enum bfs_stat_flag f
return ret;
}
+const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat_flag flags) {
+ if (flags & BFS_STAT_NOFOLLOW) {
+ return ftwbuf->lstat_cache.buf;
+ } else {
+ return ftwbuf->stat_cache.buf;
+ }
+}
+
enum bftw_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flag flags) {
if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) {
if ((flags & BFS_STAT_NOFOLLOW) || ftwbuf->type != BFTW_LNK) {
diff --git a/bftw.h b/bftw.h
index 0a4f2aa..2198b52 100644
--- a/bftw.h
+++ b/bftw.h
@@ -131,6 +131,19 @@ struct BFTW {
const struct bfs_stat *bftw_stat(const struct BFTW *ftwbuf, enum bfs_stat_flag flags);
/**
+ * Get bfs_stat() info for a file encountered during bftw(), if it has already
+ * been cached.
+ *
+ * @param ftwbuf
+ * bftw() data for the file to stat.
+ * @param flags
+ * flags for bfs_stat(). Pass ftwbuf->stat_flags for the default flags.
+ * @return
+ * A pointer to a bfs_stat() buffer, or NULL if no stat info is cached.
+ */
+const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat_flag flags);
+
+/**
* Get the type of a file encountered during bftw(), with flags controlling
* whether to follow links. This function will avoid calling bfs_stat() if
* possible.
diff --git a/color.c b/color.c
index 11684ef..4b70e19 100644
--- a/color.c
+++ b/color.c
@@ -841,11 +841,8 @@ static int print_path(CFILE *cfile, const struct BFTW *ftwbuf) {
static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) {
int ret = -1;
- size_t len = 0;
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, BFS_STAT_NOFOLLOW);
- if (statbuf) {
- len = statbuf->size;
- }
+ const struct bfs_stat *statbuf = bftw_cached_stat(ftwbuf, BFS_STAT_NOFOLLOW);
+ size_t len = statbuf ? statbuf->size : 0;
char *target = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, len);
if (!target) {
diff --git a/eval.c b/eval.c
index f8b9a6d..b50efd7 100644
--- a/eval.c
+++ b/eval.c
@@ -484,12 +484,10 @@ bool eval_lname(const struct expr *expr, struct eval_state *state) {
goto done;
}
- const struct bfs_stat *statbuf = eval_stat(state);
- if (!statbuf) {
- goto done;
- }
+ const struct bfs_stat *statbuf = bftw_cached_stat(ftwbuf, BFS_STAT_NOFOLLOW);
+ size_t len = statbuf ? statbuf->size : 0;
- name = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, statbuf->size);
+ name = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, len);
if (!name) {
eval_report_error(state);
goto done;
diff --git a/printf.c b/printf.c
index b9149f3..6eb59dd 100644
--- a/printf.c
+++ b/printf.c
@@ -310,7 +310,10 @@ static int bfs_printf_l(FILE *file, const struct bfs_printf *directive, const st
return 0;
}
- char *target = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, 0);
+ const struct bfs_stat *statbuf = bftw_cached_stat(ftwbuf, BFS_STAT_NOFOLLOW);
+ size_t len = statbuf ? statbuf->size : 0;
+
+ char *target = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, len);
if (!target) {
return -1;
}