From d3e64d6e1f905cd67264845c38b38fc6d1bdb088 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 4 Jul 2019 17:07:50 -0400 Subject: Make -mount and -xdev do different things POSIX now says -mount should skip the whole mount point, while -xdev should only skip its descendents. C.f. http://austingroupbugs.net/view.php?id=1133 C.f. https://savannah.gnu.org/bugs/?42318 C.f. https://savannah.gnu.org/bugs/?54745 --- bfs.1 | 7 +++---- bftw.c | 39 +++++++++++++++++++++++++-------------- bftw.h | 6 ++++-- eval.c | 1 + parse.c | 51 ++++++++++++++++++++++++++++++++++++++------------- tests/test_mount.out | 1 - 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/bfs.1 b/bfs.1 index 3f6ad50..277aeb7 100644 --- a/bfs.1 +++ b/bfs.1 @@ -244,13 +244,12 @@ detects that the file tree is modified during the search (default: Ignore files deeper/shallower than .IR N . .RE -.PP +.TP .B \-mount -.br +Skip mount points entirely. +.TP .B \-xdev -.RS Don't descend into other mount points. -.RE .TP .B \-noleaf Ignored; for compatibility with GNU find. diff --git a/bftw.c b/bftw.c index 444310c..370ab12 100644 --- a/bftw.c +++ b/bftw.c @@ -901,7 +901,7 @@ static bool bftw_need_stat(const struct bftw_state *state) { } if (ftwbuf->typeflag == BFTW_DIR) { - if (state->flags & (BFTW_DETECT_CYCLES | BFTW_XDEV)) { + if (state->flags & (BFTW_DETECT_CYCLES | BFTW_MOUNT | BFTW_XDEV)) { return true; } #if __linux__ @@ -1041,6 +1041,23 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) { } } +/** Check if the current file is a mount point. */ +static bool bftw_is_mount(struct bftw_state *state, const char *name) { + const struct bftw_file *file = state->file; + if (!file) { + return false; + } + + const struct bftw_file *parent = name ? file : file->parent; + if (!parent) { + return false; + } + + const struct BFTW *ftwbuf = &state->ftwbuf; + const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); + return statbuf && statbuf->dev != parent->dev; +} + /** Fill file identity information from an ftwbuf. */ static void bftw_fill_id(struct bftw_file *file, const struct BFTW *ftwbuf) { const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf; @@ -1071,6 +1088,10 @@ static enum bftw_action bftw_visit(struct bftw_state *state, const char *name, e return BFTW_STOP; } + if ((state->flags & BFTW_MOUNT) && bftw_is_mount(state, name)) { + return BFTW_PRUNE; + } + enum bftw_action ret = state->callback(ftwbuf, state->ptr); switch (ret) { case BFTW_CONTINUE: @@ -1088,19 +1109,9 @@ static enum bftw_action bftw_visit(struct bftw_state *state, const char *name, e goto done; } - if (state->flags & BFTW_XDEV) { - const struct bftw_file *parent = state->file; - if (parent && !name) { - parent = parent->parent; - } - - if (parent) { - const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); - if (statbuf && statbuf->dev != parent->dev) { - ret = BFTW_PRUNE; - goto done; - } - } + if ((state->flags & BFTW_XDEV) && bftw_is_mount(state, name)) { + ret = BFTW_PRUNE; + goto done; } done: diff --git a/bftw.h b/bftw.h index 573c5a9..06fdedb 100644 --- a/bftw.h +++ b/bftw.h @@ -184,8 +184,10 @@ enum bftw_flags { BFTW_LOGICAL = 1 << 4, /** Detect directory cycles. */ BFTW_DETECT_CYCLES = 1 << 5, - /** Stay on the same filesystem. */ - BFTW_XDEV = 1 << 6, + /** Skip mount points and their descendents. */ + BFTW_MOUNT = 1 << 6, + /** Skip the descendents of mount points. */ + BFTW_XDEV = 1 << 7, }; /** diff --git a/eval.c b/eval.c index d89e62a..e2690c0 100644 --- a/eval.c +++ b/eval.c @@ -1300,6 +1300,7 @@ static void dump_bftw_flags(enum bftw_flags flags) { DEBUG_FLAG(flags, BFTW_COMFOLLOW); DEBUG_FLAG(flags, BFTW_LOGICAL); DEBUG_FLAG(flags, BFTW_DETECT_CYCLES); + DEBUG_FLAG(flags, BFTW_MOUNT); DEBUG_FLAG(flags, BFTW_XDEV); assert(!flags); diff --git a/parse.c b/parse.c index fbc987c..70cf13d 100644 --- a/parse.c +++ b/parse.c @@ -346,6 +346,10 @@ struct parser_state { const char *depth_arg; /** A "-prune"-type argument if any. */ const char *prune_arg; + /** A "-mount"-type argument if any. */ + const char *mount_arg; + /** An "-xdev"-type argument if any. */ + const char *xdev_arg; /** The current time. */ struct timespec now; @@ -1546,10 +1550,11 @@ static struct expr *parse_ls(struct parser_state *state, int arg1, int arg2) { } /** - * Parse -mount, -xdev. + * Parse -mount. */ static struct expr *parse_mount(struct parser_state *state, int arg1, int arg2) { - state->cmdline->flags |= BFTW_XDEV; + state->cmdline->flags |= BFTW_MOUNT; + state->mount_arg = state->argv[0]; return parse_nullary_option(state); } @@ -2408,6 +2413,15 @@ static struct expr *parse_xattr(struct parser_state *state, int arg1, int arg2) #endif } +/** + * Parse -xdev. + */ +static struct expr *parse_xdev(struct parser_state *state, int arg1, int arg2) { + state->cmdline->flags |= BFTW_XDEV; + state->xdev_arg = state->argv[0]; + return parse_nullary_option(state); +} + /** * Launch a pager for the help output. */ @@ -2594,6 +2608,7 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " ${blu}-mindepth${rs} ${bld}N${rs}\n"); cfprintf(cout, " Ignore files deeper/shallower than ${bld}N${rs}\n"); cfprintf(cout, " ${blu}-mount${rs}\n"); + cfprintf(cout, " Skip mount points entirely\n"); cfprintf(cout, " ${blu}-xdev${rs}\n"); cfprintf(cout, " Don't descend into other mount points\n"); cfprintf(cout, " ${blu}-noleaf${rs}\n"); @@ -2888,9 +2903,9 @@ static const struct table_entry parse_table[] = { {"-warn", parse_warn, true}, {"-wholename", parse_path, false}, {"-writable", parse_access, W_OK}, - {"-x", parse_mount}, + {"-x", parse_xdev}, {"-xattr", parse_xattr}, - {"-xdev", parse_mount}, + {"-xdev", parse_xdev}, {"-xtype", parse_type, true}, {0}, }; @@ -3183,17 +3198,23 @@ static struct expr *parse_whole_expr(struct parser_state *state) { } } - if (state->warn && state->depth_arg && state->prune_arg) { - parse_warning(state, "%s does not work in the presence of %s.\n", state->prune_arg, state->depth_arg); + if (state->warn) { + if (state->mount_arg && state->xdev_arg) { + parse_warning(state, "%s is redundant in the presence of %s.\n\n", state->xdev_arg, state->mount_arg); + } + + if (state->depth_arg && state->prune_arg) { + parse_warning(state, "%s does not work in the presence of %s.\n", state->prune_arg, state->depth_arg); - if (state->interactive) { - fprintf(stderr, "Do you want to continue? "); - if (ynprompt() == 0) { - goto fail; + if (state->interactive) { + fprintf(stderr, "Do you want to continue? "); + if (ynprompt() == 0) { + goto fail; + } } - } - fprintf(stderr, "\n"); + fprintf(stderr, "\n"); + } } return expr; @@ -3275,8 +3296,10 @@ void dump_cmdline(const struct cmdline *cmdline, bool verbose) { if (cmdline->ignore_races) { cfprintf(cerr, "${blu}-ignore_readdir_race${rs} "); } - if (cmdline->flags & BFTW_XDEV) { + if (cmdline->flags & BFTW_MOUNT) { cfprintf(cerr, "${blu}-mount${rs} "); + } else if (cmdline->flags & BFTW_XDEV) { + cfprintf(cerr, "${blu}-xdev{rs} "); } if (cmdline->mindepth != 0) { cfprintf(cerr, "${blu}-mindepth${rs} ${bld}%d${rs} ", cmdline->mindepth); @@ -3411,6 +3434,8 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { .last_arg = NULL, .depth_arg = NULL, .prune_arg = NULL, + .mount_arg = NULL, + .xdev_arg = NULL, }; if (strcmp(xbasename(state.command), "find") == 0) { diff --git a/tests/test_mount.out b/tests/test_mount.out index 005bdcf..2425675 100644 --- a/tests/test_mount.out +++ b/tests/test_mount.out @@ -1,4 +1,3 @@ scratch scratch/foo -scratch/mnt scratch/foo/bar -- cgit v1.2.3