summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-08-22 18:34:36 -0400
committerTavian Barnes <tavianator@tavianator.com>2017-08-22 18:37:23 -0400
commitd548b86e120db7e1327eea957a698d4bb874a1fb (patch)
tree874e34926ee12fbbf076a834d6822ab751cf378b
parent2a561b66f48ab4deafb0a031cad3ad541ea79449 (diff)
downloadbfs-d548b86e120db7e1327eea957a698d4bb874a1fb.tar.xz
Avoid multiple extra stat()s of broken symlinks for -xtype
-rw-r--r--bftw.c2
-rw-r--r--eval.c13
-rw-r--r--parse.c2
-rw-r--r--util.c10
-rw-r--r--util.h4
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.