summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bftw.c18
-rw-r--r--bftw.h8
-rw-r--r--parse.c20
-rwxr-xr-xtests.sh20
4 files changed, 57 insertions, 9 deletions
diff --git a/bftw.c b/bftw.c
index 99e26e1..a49cbed 100644
--- a/bftw.c
+++ b/bftw.c
@@ -471,8 +471,8 @@ static void ftwbuf_use_dirent(struct BFTW *ftwbuf, const struct dirent *de) {
#endif
}
-static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb) {
- int ret = fstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, AT_SYMLINK_NOFOLLOW);
+static int ftwbuf_stat(struct BFTW *ftwbuf, struct stat *sb, int flags) {
+ int ret = fstatat(ftwbuf->at_fd, ftwbuf->at_path, sb, flags);
if (ret != 0) {
return ret;
}
@@ -620,8 +620,18 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de)
ftwbuf->typeflag = BFTW_UNKNOWN;
}
- if ((state->flags & BFTW_STAT) || ftwbuf->typeflag == BFTW_UNKNOWN) {
- if (ftwbuf_stat(ftwbuf, &state->statbuf) != 0) {
+ bool follow;
+ if (state->flags & BFTW_FOLLOW_ROOT) {
+ follow = !state->current;
+ } else {
+ follow = false;
+ }
+
+ if ((state->flags & BFTW_STAT)
+ || ftwbuf->typeflag == BFTW_UNKNOWN
+ || (ftwbuf->typeflag == BFTW_LNK && follow)) {
+ int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
+ if (ftwbuf_stat(ftwbuf, &state->statbuf, flags) != 0) {
state->error = errno;
ftwbuf_set_error(ftwbuf, state->error);
}
diff --git a/bftw.h b/bftw.h
index 1375594..4b81123 100644
--- a/bftw.h
+++ b/bftw.h
@@ -105,11 +105,13 @@ typedef enum bftw_action bftw_fn(struct BFTW *ftwbuf, void *ptr);
*/
enum bftw_flags {
/** stat() each encountered file. */
- BFTW_STAT = 1 << 0,
+ BFTW_STAT = 1 << 0,
/** Attempt to recover from encountered errors. */
- BFTW_RECOVER = 1 << 1,
+ BFTW_RECOVER = 1 << 1,
/** Visit directories in post-order as well as pre-order. */
- BFTW_DEPTH = 1 << 2,
+ BFTW_DEPTH = 1 << 2,
+ /** If the initial path is a symbolic link, follow it. */
+ BFTW_FOLLOW_ROOT = 1 << 3,
};
/**
diff --git a/parse.c b/parse.c
index 413282a..93657c8 100644
--- a/parse.c
+++ b/parse.c
@@ -344,7 +344,11 @@ static struct expr *parse_acnewer(struct parser_state *state, const char *option
}
struct stat sb;
- if (fstatat(AT_FDCWD, expr->sdata, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
+
+ bool follow = state->cl->flags & BFTW_FOLLOW_ROOT;
+ int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
+
+ if (fstatat(AT_FDCWD, expr->sdata, &sb, flags) != 0) {
print_error(NULL, expr->sdata, errno);
free_expr(expr);
return NULL;
@@ -465,6 +469,20 @@ static struct expr *parse_literal(struct parser_state *state) {
}
switch (arg[1]) {
+ case 'P':
+ if (strcmp(arg, "-P") == 0) {
+ state->cl->flags &= ~BFTW_FOLLOW_ROOT;
+ return new_option(state, arg);
+ }
+ break;
+
+ case 'H':
+ if (strcmp(arg, "-H") == 0) {
+ state->cl->flags |= BFTW_FOLLOW_ROOT;
+ return new_option(state, arg);
+ }
+ break;
+
case 'a':
if (strcmp(arg, "-amin") == 0) {
return parse_acmtime(state, arg, ATIME, MINUTES);
diff --git a/tests.sh b/tests.sh
index ffb110d..0a2f60a 100755
--- a/tests.sh
+++ b/tests.sh
@@ -35,6 +35,7 @@ function links_structure() {
ln "$1/a" "$1/c"
mkdir -p "$1/d/e"
ln -s ../../d "$1/d/e/f"
+ touchp "$1/d/e/g"
}
# Checks for any (order-independent) differences between bfs and find
@@ -150,7 +151,24 @@ function test_0020() {
find_diff "$1" -links +1
}
-for i in {1..20}; do
+function test_0021() {
+ links_structure "$1"
+ find_diff -P "$1/d/e/f" && \
+ find_diff -P "$1/d/e/f/"
+}
+
+function test_0022() {
+ links_structure "$1"
+ find_diff -H "$1/d/e/f" && \
+ find_diff -H "$1/d/e/f/"
+}
+
+function test_0023() {
+ links_structure "$1"
+ find_diff -H "$1" -newer "$1/d/e/f"
+}
+
+for i in {1..23}; do
dir="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX)"
test="test_$(printf '%04d' $i)"
"$test" "$dir"