diff options
-rw-r--r-- | bftw.c | 8 | ||||
-rw-r--r-- | bftw.h | 6 | ||||
-rw-r--r-- | color.c | 10 | ||||
-rw-r--r-- | eval.c | 28 | ||||
-rw-r--r-- | mtab.c | 6 | ||||
-rw-r--r-- | parse.c | 4 | ||||
-rw-r--r-- | posix1e.c | 2 | ||||
-rw-r--r-- | printf.c | 2 | ||||
-rw-r--r-- | stat.c | 24 | ||||
-rw-r--r-- | stat.h | 21 |
10 files changed, 66 insertions, 45 deletions
@@ -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)) { @@ -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; }; /** @@ -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; @@ -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) { @@ -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; } @@ -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); } @@ -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; } @@ -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) { @@ -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 { @@ -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(). |