From d548b86e120db7e1327eea957a698d4bb874a1fb Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 22 Aug 2017 18:34:36 -0400 Subject: Avoid multiple extra stat()s of broken symlinks for -xtype --- bftw.c | 2 +- eval.c | 13 +++++++++---- parse.c | 2 +- util.c | 10 +++++----- util.h | 4 ++-- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/bftw.c b/bftw.c index 02e7adb..d21c202 100644 --- a/bftw.c +++ b/bftw.c @@ -551,7 +551,7 @@ static void bftw_queue_destroy(struct bftw_queue *queue) { /** Call stat() and use the results. */ static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb) { - int ret = xfstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, &ftwbuf->at_flags); + int ret = xfstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, ftwbuf->at_flags); if (ret == 0) { ftwbuf->statbuf = sb; ftwbuf->typeflag = mode_to_typeflag(sb->st_mode); diff --git a/eval.c b/eval.c index a3c55d5..21204d8 100644 --- a/eval.c +++ b/eval.c @@ -76,7 +76,7 @@ static void eval_error(struct eval_state *state) { static const struct stat *fill_statbuf(struct eval_state *state) { struct BFTW *ftwbuf = state->ftwbuf; if (!ftwbuf->statbuf) { - if (xfstatat(ftwbuf->at_fd, ftwbuf->at_path, &state->statbuf, &ftwbuf->at_flags) == 0) { + if (xfstatat(ftwbuf->at_fd, ftwbuf->at_path, &state->statbuf, ftwbuf->at_flags) == 0) { ftwbuf->statbuf = &state->statbuf; } else { eval_error(state); @@ -831,9 +831,14 @@ bool eval_xtype(const struct expr *expr, struct eval_state *state) { int at_flags = ftwbuf->at_flags ^ AT_SYMLINK_NOFOLLOW; struct stat sb; - if (xfstatat(ftwbuf->at_fd, ftwbuf->at_path, &sb, &at_flags) != 0) { - eval_error(state); - return false; + if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &sb, at_flags) != 0) { + if (!follow && (errno == ENOENT || errno == ENOTDIR)) { + // Broken symlink + return eval_type(expr, state); + } else { + eval_error(state); + return false; + } } return mode_to_typeflag(sb.st_mode) & expr->idata; diff --git a/parse.c b/parse.c index ef252fb..4624e7e 100644 --- a/parse.c +++ b/parse.c @@ -345,7 +345,7 @@ static int stat_arg(const struct parser_state *state, struct expr *expr, struct bool follow = cmdline->flags & (BFTW_COMFOLLOW | BFTW_LOGICAL); int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW; - int ret = xfstatat(AT_FDCWD, expr->sdata, sb, &flags); + int ret = xfstatat(AT_FDCWD, expr->sdata, sb, flags); if (ret != 0) { cfprintf(cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", expr->sdata, strerror(errno)); } diff --git a/util.c b/util.c index d825554..5b2b3a9 100644 --- a/util.c +++ b/util.c @@ -226,12 +226,12 @@ const char *xbasename(const char *path) { return i; } -int xfstatat(int fd, const char *path, struct stat *buf, int *flags) { - int ret = fstatat(fd, path, buf, *flags); +int xfstatat(int fd, const char *path, struct stat *buf, int flags) { + int ret = fstatat(fd, path, buf, flags); - if (ret != 0 && !(*flags & AT_SYMLINK_NOFOLLOW) && (errno == ENOENT || errno == ENOTDIR)) { - *flags |= AT_SYMLINK_NOFOLLOW; - ret = fstatat(fd, path, buf, *flags); + if (ret != 0 && !(flags & AT_SYMLINK_NOFOLLOW) && (errno == ENOENT || errno == ENOTDIR)) { + flags |= AT_SYMLINK_NOFOLLOW; + ret = fstatat(fd, path, buf, flags); } return ret; diff --git a/util.h b/util.h index 6798fb9..e23acdc 100644 --- a/util.h +++ b/util.h @@ -152,10 +152,10 @@ const char *xbasename(const char *path); * @param buf * The stat buffer to fill. * @param flags - * AT_* flags for this call. Will be updated if a fallback happens. + * AT_* flags for this call. * @return 0 on success, -1 on failure. */ -int xfstatat(int fd, const char *path, struct stat *buf, int *flags); +int xfstatat(int fd, const char *path, struct stat *buf, int flags); /** * Convert a stat() st_mode to a bftw() typeflag. -- cgit v1.2.3