summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bftw.c8
-rw-r--r--bftw.h6
-rw-r--r--color.c10
-rw-r--r--eval.c28
-rw-r--r--mtab.c6
-rw-r--r--parse.c4
-rw-r--r--posix1e.c2
-rw-r--r--printf.c2
-rw-r--r--stat.c24
-rw-r--r--stat.h21
10 files changed, 66 insertions, 45 deletions
diff --git a/bftw.c b/bftw.c
index 106bd41..8229612 100644
--- a/bftw.c
+++ b/bftw.c
@@ -751,7 +751,7 @@ static enum bftw_typeflag bftw_dirent_typeflag(const struct dirent *de) {
/** Call stat() and use the results. */
static int bftw_stat(struct BFTW *ftwbuf, struct bfs_stat *sb) {
- int ret = bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags, BFS_STAT_BROKEN_OK, sb);
+ int ret = bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, sb);
if (ret == 0) {
ftwbuf->statbuf = sb;
ftwbuf->typeflag = bftw_mode_typeflag(sb->mode);
@@ -877,7 +877,7 @@ static bool bftw_need_stat(struct bftw_state *state) {
return true;
}
- if (ftwbuf->typeflag == BFTW_LNK && !(ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW)) {
+ if (ftwbuf->typeflag == BFTW_LNK && !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) {
return true;
}
@@ -918,7 +918,7 @@ static void bftw_prepare_visit(struct bftw_state *state) {
ftwbuf->statbuf = NULL;
ftwbuf->at_fd = AT_FDCWD;
ftwbuf->at_path = ftwbuf->path;
- ftwbuf->at_flags = AT_SYMLINK_NOFOLLOW;
+ ftwbuf->stat_flags = BFS_STAT_NOFOLLOW;
if (dir) {
ftwbuf->nameoff = dir->nameoff;
@@ -957,7 +957,7 @@ static void bftw_prepare_visit(struct bftw_state *state) {
}
bool follow = state->flags & follow_flags;
if (follow) {
- ftwbuf->at_flags = 0;
+ ftwbuf->stat_flags = BFS_STAT_TRYFOLLOW;
}
if (bftw_need_stat(state)) {
diff --git a/bftw.h b/bftw.h
index e822523..e0f02d9 100644
--- a/bftw.h
+++ b/bftw.h
@@ -96,10 +96,10 @@ struct BFTW {
/** A parent file descriptor for the *at() family of calls. */
int at_fd;
- /** The path relative to atfd for the *at() family of calls. */
+ /** The path relative to at_fd for the *at() family of calls. */
const char *at_path;
- /** Appropriate flags (such as AT_SYMLINK_NOFOLLOW) for the *at() family of calls. */
- int at_flags;
+ /** Flags for bfs_stat(). */
+ enum bfs_stat_flag stat_flags;
};
/**
diff --git a/color.c b/color.c
index 17d59e6..726c40f 100644
--- a/color.c
+++ b/color.c
@@ -542,7 +542,7 @@ int cfclose(CFILE *cfile) {
/** Check if a symlink is broken. */
static bool is_link_broken(const struct BFTW *ftwbuf) {
- if (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW) {
+ if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) {
return xfaccessat(ftwbuf->at_fd, ftwbuf->at_path, F_OK) != 0;
} else {
return true;
@@ -710,7 +710,7 @@ static int print_path_colored(CFILE *cfile, const struct BFTW *ftwbuf) {
/** Call stat() again to resolve a link target. */
static void restat(struct BFTW *ftwbuf, struct bfs_stat *statbuf) {
- if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags, 0, statbuf) == 0) {
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, statbuf) == 0) {
ftwbuf->statbuf = statbuf;
}
}
@@ -723,9 +723,9 @@ static int print_path(CFILE *cfile, const struct BFTW *ftwbuf) {
}
if (colors && colors->link_as_target) {
- if (ftwbuf->typeflag == BFTW_LNK && (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW)) {
+ if (ftwbuf->typeflag == BFTW_LNK && (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) {
struct BFTW altbuf = *ftwbuf;
- altbuf.at_flags = 0;
+ altbuf.stat_flags = BFS_STAT_FOLLOW;
struct bfs_stat statbuf;
restat(&altbuf, &statbuf);
return print_path_colored(cfile, &altbuf);
@@ -752,7 +752,7 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) {
struct BFTW altbuf = *ftwbuf;
altbuf.path = target;
altbuf.nameoff = xbasename(target) - target;
- altbuf.at_flags = 0;
+ altbuf.stat_flags = BFS_STAT_FOLLOW;
altbuf.statbuf = NULL;
struct bfs_stat statbuf;
diff --git a/eval.c b/eval.c
index 283b778..1c4ff80 100644
--- a/eval.c
+++ b/eval.c
@@ -79,7 +79,7 @@ struct eval_state {
/**
* Debug stat() calls.
*/
-static void debug_stat(const struct eval_state *state, int at_flags, enum bfs_stat_flag flags) {
+static void debug_stat(const struct eval_state *state, enum bfs_stat_flag flags) {
if (!(state->cmdline->debug & DEBUG_STAT)) {
return;
}
@@ -98,13 +98,9 @@ static void debug_stat(const struct eval_state *state, int at_flags, enum bfs_st
fprintf(stderr, ", \"%s\", ", ftwbuf->at_path);
- DEBUG_FLAG(at_flags, 0);
- DEBUG_FLAG(at_flags, AT_SYMLINK_NOFOLLOW);
-
- fprintf(stderr, ", ");
-
- DEBUG_FLAG(flags, 0);
- DEBUG_FLAG(flags, BFS_STAT_BROKEN_OK);
+ DEBUG_FLAG(flags, BFS_STAT_FOLLOW);
+ DEBUG_FLAG(flags, BFS_STAT_NOFOLLOW);
+ DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW);
fprintf(stderr, ")\n");
}
@@ -124,9 +120,9 @@ static const struct bfs_stat *eval_try_stat(struct eval_state *state) {
goto done;
}
- debug_stat(state, ftwbuf->at_flags, BFS_STAT_BROKEN_OK);
+ debug_stat(state, ftwbuf->stat_flags);
- if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->at_flags, BFS_STAT_BROKEN_OK, &state->statbuf) == 0) {
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, ftwbuf->stat_flags, &state->statbuf) == 0) {
ftwbuf->statbuf = &state->statbuf;
} else {
ftwbuf->error = errno;
@@ -195,9 +191,9 @@ 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) {
- int at_flags = ftwbuf->at_flags ^ AT_SYMLINK_NOFOLLOW;
- debug_stat(state, at_flags, 0);
- if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, at_flags, 0, statbuf) != 0) {
+ 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;
}
}
@@ -419,7 +415,7 @@ bool eval_delete(const struct expr *expr, struct eval_state *state) {
}
int flag = 0;
- if (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW) {
+ if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) {
if (ftwbuf->typeflag == BFTW_DIR) {
flag |= AT_REMOVEDIR;
}
@@ -994,7 +990,7 @@ 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->at_flags & AT_SYMLINK_NOFOLLOW);
+ bool follow = !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW);
bool is_link = ftwbuf->typeflag == BFTW_LNK;
if (follow == is_link) {
return eval_type(expr, state);
@@ -1244,7 +1240,7 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) {
state.xstatbuf.mask = 0;
if (ftwbuf->statbuf) {
- debug_stat(&state, ftwbuf->at_flags, BFS_STAT_BROKEN_OK);
+ debug_stat(&state, ftwbuf->stat_flags);
}
if (ftwbuf->typeflag == BFTW_ERROR) {
diff --git a/mtab.c b/mtab.c
index 63c04ac..c21cf0b 100644
--- a/mtab.c
+++ b/mtab.c
@@ -104,7 +104,7 @@ struct bfs_mtab *parse_bfs_mtab() {
struct mntent *mnt;
while ((mnt = getmntent(file))) {
struct bfs_stat sb;
- if (bfs_stat(AT_FDCWD, mnt->mnt_dir, 0, 0, &sb) != 0) {
+ if (bfs_stat(AT_FDCWD, mnt->mnt_dir, BFS_STAT_NOFOLLOW, &sb) != 0) {
continue;
}
@@ -140,7 +140,7 @@ fail:
for (struct statfs *mnt = mntbuf; mnt < mntbuf + size; ++mnt) {
struct bfs_stat sb;
- if (bfs_stat(AT_FDCWD, mnt->f_mntonname, 0, 0, &sb) != 0) {
+ if (bfs_stat(AT_FDCWD, mnt->f_mntonname, BFS_STAT_NOFOLLOW, &sb) != 0) {
continue;
}
@@ -173,7 +173,7 @@ fail:
struct mnttab mnt;
while (getmntent(file, &mnt) == 0) {
struct bfs_stat sb;
- if (bfs_stat(AT_FDCWD, mnt.mnt_mountp, 0, 0, &sb) != 0) {
+ if (bfs_stat(AT_FDCWD, mnt.mnt_mountp, BFS_STAT_NOFOLLOW, &sb) != 0) {
continue;
}
diff --git a/parse.c b/parse.c
index 6367cd6..10a7300 100644
--- a/parse.c
+++ b/parse.c
@@ -465,9 +465,9 @@ static int stat_arg(const struct parser_state *state, struct expr *expr, struct
const struct cmdline *cmdline = state->cmdline;
bool follow = cmdline->flags & (BFTW_COMFOLLOW | BFTW_LOGICAL);
- int at_flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
+ enum bfs_stat_flag flags = follow ? BFS_STAT_TRYFOLLOW : BFS_STAT_NOFOLLOW;
- int ret = bfs_stat(AT_FDCWD, expr->sdata, at_flags, BFS_STAT_BROKEN_OK, sb);
+ int ret = bfs_stat(AT_FDCWD, expr->sdata, flags, sb);
if (ret != 0) {
parse_error(state, "'%s': %m.\n", expr->sdata);
}
diff --git a/posix1e.c b/posix1e.c
index 00bff10..a898b39 100644
--- a/posix1e.c
+++ b/posix1e.c
@@ -44,7 +44,7 @@ static const char *open_path(const struct BFTW *ftwbuf, int *fd) {
// /proc/self/fd/<fd> to cap_get_path(). Inspired by
// https://android.googlesource.com/platform/bionic/+/2825f10b7f61558c264231a536cf3affc0d84204
int flags = O_PATH;
- if (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW) {
+ if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) {
flags |= O_NOFOLLOW;
}
diff --git a/printf.c b/printf.c
index 0b4b0c8..b2463e1 100644
--- a/printf.c
+++ b/printf.c
@@ -381,7 +381,7 @@ static int bfs_printf_Y(FILE *file, const struct bfs_printf_directive *directive
const char *type = "U";
struct bfs_stat sb;
- if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, 0, 0, &sb) == 0) {
+ if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, BFS_STAT_FOLLOW, &sb) == 0) {
type = bfs_printf_type(bftw_mode_typeflag(sb.mode));
} else {
switch (errno) {
diff --git a/stat.c b/stat.c
index 14a4f8e..d96717a 100644
--- a/stat.c
+++ b/stat.c
@@ -82,10 +82,9 @@ const char *bfs_stat_field_name(enum bfs_stat_field field) {
/**
* Check if we should retry a failed stat() due to a potentially broken link.
*/
-static bool bfs_stat_retry(int ret, int at_flags, enum bfs_stat_flag flags) {
+static bool bfs_stat_retry(int ret, enum bfs_stat_flag flags) {
return ret != 0
- && !(at_flags & AT_SYMLINK_NOFOLLOW)
- && (flags & BFS_STAT_BROKEN_OK)
+ && (flags & (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW)) == BFS_STAT_TRYFOLLOW
&& is_nonexistence_error(errno);
}
@@ -144,7 +143,7 @@ static int bfs_stat_impl(int at_fd, const char *at_path, int at_flags, enum bfs_
struct stat statbuf;
int ret = fstatat(at_fd, at_path, &statbuf, at_flags);
- if (bfs_stat_retry(ret, at_flags, flags)) {
+ if (bfs_stat_retry(ret, flags)) {
at_flags |= AT_SYMLINK_NOFOLLOW;
ret = fstatat(at_fd, at_path, &statbuf, at_flags);
}
@@ -183,7 +182,7 @@ static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, enum bfs
struct statx xbuf;
int ret = bfs_statx(at_fd, at_path, at_flags, mask, &xbuf);
- if (bfs_stat_retry(ret, at_flags, flags)) {
+ if (bfs_stat_retry(ret, flags)) {
at_flags |= AT_SYMLINK_NOFOLLOW;
ret = bfs_statx(at_fd, at_path, at_flags, mask, &xbuf);
}
@@ -274,7 +273,10 @@ static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, enum bfs
#endif // HAVE_BFS_STATX
-int bfs_stat(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf) {
+/**
+ * Allows calling stat with custom at_flags.
+ */
+static int bfs_stat_explicit(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf) {
#if HAVE_BFS_STATX
static bool has_statx = true;
@@ -291,12 +293,20 @@ int bfs_stat(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag fl
return bfs_stat_impl(at_fd, at_path, at_flags, flags, buf);
}
+int bfs_stat(int at_fd, const char *at_path, enum bfs_stat_flag flags, struct bfs_stat *buf) {
+ int at_flags = 0;
+ if (flags & BFS_STAT_NOFOLLOW) {
+ at_flags |= AT_SYMLINK_NOFOLLOW;
+ }
+ return bfs_stat_explicit(at_fd, at_path, at_flags, flags, buf);
+}
+
int bfs_fstat(int fd, struct bfs_stat *buf) {
#ifdef AT_EMPTY_PATH
static bool has_at_ep = true;
if (has_at_ep) {
- int ret = bfs_stat(fd, "", AT_EMPTY_PATH, 0, buf);
+ int ret = bfs_stat_explicit(fd, "", AT_EMPTY_PATH, 0, buf);
if (ret != 0 && errno == EINVAL) {
has_at_ep = false;
} else {
diff --git a/stat.h b/stat.h
index 59a9712..6c915da 100644
--- a/stat.h
+++ b/stat.h
@@ -63,8 +63,12 @@ const char *bfs_stat_field_name(enum bfs_stat_field field);
* bfs_stat() flags.
*/
enum bfs_stat_flag {
- /** Fall back to the link itself on broken symlinks. */
- BFS_STAT_BROKEN_OK = 1 << 0,
+ /** Follow symlinks (the default). */
+ BFS_STAT_FOLLOW = 0,
+ /** Never follow symlinks. */
+ BFS_STAT_NOFOLLOW = 1 << 0,
+ /** Try to follow symlinks, but fall back to the link itself if broken. */
+ BFS_STAT_TRYFOLLOW = 1 << 1,
};
#ifdef DEV_BSIZE
@@ -113,8 +117,19 @@ struct bfs_stat {
/**
* Facade over fstatat().
+ *
+ * @param at_fd
+ * The base file descriptor for the lookup.
+ * @param at_path
+ * The path to stat, relative to at_fd.
+ * @param flags
+ * Flags that affect the lookup.
+ * @param[out] buf
+ * A place to store the stat buffer, if successful.
+ * @return
+ * 0 on success, -1 on error.
*/
-int bfs_stat(int at_fd, const char *at_path, int at_flags, enum bfs_stat_flag flags, struct bfs_stat *buf);
+int bfs_stat(int at_fd, const char *at_path, enum bfs_stat_flag flags, struct bfs_stat *buf);
/**
* Facade over fstat().