diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2020-08-13 10:02:29 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2020-08-13 10:14:29 -0400 |
commit | 564142a029fda86b2d87f8f39c12acea34241098 (patch) | |
tree | 802a8225cacfc1d9f8ba80c0959651353f728325 | |
parent | 6a4c2677075adca143fa8501096a2a59499503b5 (diff) | |
download | bfs-564142a029fda86b2d87f8f39c12acea34241098.tar.xz |
Implement -xattrname
From macOS find.
-rw-r--r-- | RELEASES.md | 2 | ||||
-rw-r--r-- | bfs.1 | 4 | ||||
-rw-r--r-- | eval.c | 13 | ||||
-rw-r--r-- | eval.h | 1 | ||||
-rw-r--r-- | fsade.c | 44 | ||||
-rw-r--r-- | fsade.h | 12 | ||||
-rw-r--r-- | parse.c | 20 | ||||
-rwxr-xr-x | tests.sh | 77 | ||||
-rw-r--r-- | tests/test_L_xattr.out | 1 | ||||
-rw-r--r-- | tests/test_L_xattrname.out | 2 | ||||
-rw-r--r-- | tests/test_xattr.out | 1 | ||||
-rw-r--r-- | tests/test_xattrname.out | 2 |
12 files changed, 159 insertions, 20 deletions
diff --git a/RELEASES.md b/RELEASES.md index e8e0b71..d80f1ee 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -38,6 +38,8 @@ - Fixed an optimizer bug that could skip `-empty`/`-xtype` if they didn't always lead to an action +- Implemented `-xattrname` to find files with a particular extended attribute (from macOS find) + [#8]: https://github.com/tavianator/bfs/issues/8 [#30]: https://github.com/tavianator/bfs/issues/30 @@ -576,6 +576,10 @@ days after they were changed. Find files with extended attributes .RB ( xattr (7)). .TP +\fB\-xattrname\fR \fINAME\fR +Find files with the extended attribute +.IR NAME . +.TP \fB\-xtype\fR [\fIbcdlpfswD\fR] Find files of the given type, following links when .B \-type @@ -864,6 +864,19 @@ bool eval_xattr(const struct expr *expr, struct eval_state *state) { } /** + * -xattr test. + */ +bool eval_xattrname(const struct expr *expr, struct eval_state *state) { + int ret = bfs_check_xattr_named(state->ftwbuf, expr->sdata); + if (ret >= 0) { + return ret; + } else { + eval_report_error(state); + return false; + } +} + +/** * -xtype test. */ bool eval_xtype(const struct expr *expr, struct eval_state *state) { @@ -33,6 +33,7 @@ bool eval_acl(const struct expr *expr, struct eval_state *state); bool eval_capable(const struct expr *expr, struct eval_state *state); bool eval_perm(const struct expr *expr, struct eval_state *state); bool eval_xattr(const struct expr *expr, struct eval_state *state); +bool eval_xattrname(const struct expr *expr, struct eval_state *state); bool eval_newer(const struct expr *expr, struct eval_state *state); bool eval_time(const struct expr *expr, struct eval_state *state); @@ -330,6 +330,45 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf) { } } +int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { + const char *path = fake_at(ftwbuf); + ssize_t len; + +#if BFS_HAS_SYS_EXTATTR + ssize_t (*extattr_get)(const char *, int, const char *, void*, size_t) = + ftwbuf->type == BFTW_LNK ? extattr_get_link : extattr_get_file; + + len = extattr_get(path, EXTATTR_NAMESPACE_SYSTEM, name, NULL, 0); + if (len < 0) { + len = extattr_get(path, EXTATTR_NAMESPACE_USER, name, NULL, 0); + } +#elif __APPLE__ + int options = ftwbuf->type == BFTW_LNK ? XATTR_NOFOLLOW : 0; + len = getxattr(path, name, NULL, 0, 0, options); +#else + if (ftwbuf->type == BFTW_LNK) { + len = lgetxattr(path, name, NULL, 0); + } else { + len = getxattr(path, name, NULL, 0); + } +#endif + + int error = errno; + + free_fake_at(ftwbuf, path); + + if (len >= 0) { + return 1; + } else if (is_absence_error(error)) { + return 0; + } else if (error == E2BIG) { + return 1; + } else { + errno = error; + return -1; + } +} + #else // !BFS_CAN_CHECK_XATTRS int bfs_check_xattrs(const struct BFTW *ftwbuf) { @@ -337,4 +376,9 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf) { return -1; } +int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { + errno = ENOTSUP; + return -1; +} + #endif @@ -67,4 +67,16 @@ int bfs_check_capabilities(const struct BFTW *ftwbuf); */ int bfs_check_xattrs(const struct BFTW *ftwbuf); +/** + * Check if a file has an extended attribute with the given name. + * + * @param ftwbuf + * The file to check. + * @param name + * The name of the xattr to check. + * @return + * 1 if it does, 0 if it doesn't, or -1 if an error occurred. + */ +int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name); + #endif // BFS_FSADE_H @@ -2505,6 +2505,23 @@ static struct expr *parse_xattr(struct parser_state *state, int arg1, int arg2) } /** + * Parse -xattrname. + */ +static struct expr *parse_xattrname(struct parser_state *state, int arg1, int arg2) { +#if BFS_CAN_CHECK_XATTRS + struct expr *expr = parse_unary_test(state, eval_xattrname); + if (expr) { + expr->cost = STAT_COST; + expr->probability = 0.01; + } + return expr; +#else + parse_error(state, "${blu}%s${rs} is missing platform support.\n", state->argv[0]); + return NULL; +#endif +} + +/** * Parse -xdev. */ static struct expr *parse_xdev(struct parser_state *state, int arg1, int arg2) { @@ -2814,6 +2831,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { #if BFS_CAN_CHECK_XATTRS cfprintf(cout, " ${blu}-xattr${rs}\n"); cfprintf(cout, " Find files with extended attributes\n"); + cfprintf(cout, " ${blu}-xattrname${rs} ${bld}NAME${rs}\n"); + cfprintf(cout, " Find files with the extended attribute ${bld}NAME${rs}\n"); #endif cfprintf(cout, " ${blu}-xtype${rs} ${bld}[bcdlpfswD]${rs}\n"); cfprintf(cout, " Find files of the given type, following links when ${blu}-type${rs} would not, and\n"); @@ -3017,6 +3036,7 @@ static const struct table_entry parse_table[] = { {"-writable", T_TEST, parse_access, W_OK}, {"-x", T_FLAG, parse_xdev}, {"-xattr", T_TEST, parse_xattr}, + {"-xattrname", T_TEST, parse_xattrname}, {"-xdev", T_OPTION, parse_xdev}, {"-xtype", T_TEST, parse_type, true}, {0}, @@ -695,12 +695,18 @@ case "$UNAME" in bsd_tests+=( test_xattr test_L_xattr + + test_xattrname + test_L_xattrname ) ;; *) sudo_tests+=( test_xattr test_L_xattr + + test_xattrname + test_L_xattrname ) ;; esac @@ -2562,53 +2568,84 @@ function test_L_capable() { bfs_diff -L scratch -capable } -function set_xattr() { +function make_xattrs() { + rm -rf scratch/* + + touch scratch/{normal,xattr,xattr_2} + ln -s xattr scratch/link + ln -s normal scratch/xattr_link + case "$UNAME" in Darwin) - xattr -w bfs_test true "$1" - xattr -s -w bfs_test true "$2" + xattr -w bfs_test true scratch/xattr + xattr -w bfs_test_2 true scratch/xattr_2 + xattr -s -w bfs_test true scratch/xattr_link ;; FreeBSD) - setextattr user bfs_test true "$1" - setextattr -h user bfs_test true "$2" + setextattr user bfs_test true scratch/xattr + setextattr user bfs_test_2 true scratch/xattr_2 + setextattr -h user bfs_test true scratch/xattr_link ;; *) # Linux tmpfs doesn't support the user.* namespace, so we use the security.* # namespace, which is writable by root and readable by others - sudo setfattr -n security.bfs_test "$1" - sudo setfattr -h -n security.bfs_test "$2" + sudo setfattr -n security.bfs_test scratch/xattr + sudo setfattr -n security.bfs_test_2 scratch/xattr_2 + sudo setfattr -h -n security.bfs_test scratch/xattr_link ;; esac } function test_xattr() { - rm -rf scratch/* - if ! invoke_bfs scratch -quit -xattr 2>/dev/null; then return 0 fi - touch scratch/{normal,xattr} - ln -s xattr scratch/link - ln -s normal scratch/xattr_link - set_xattr scratch/xattr scratch/xattr_link - + make_xattrs bfs_diff scratch -xattr } function test_L_xattr() { - rm -rf scratch/* + if ! invoke_bfs scratch -quit -xattr 2>/dev/null; then + return 0 + fi + make_xattrs + bfs_diff -L scratch -xattr +} + +function test_xattrname() { if ! invoke_bfs scratch -quit -xattr 2>/dev/null; then return 0 fi - touch scratch/{normal,xattr} - ln -s xattr scratch/link - ln -s normal scratch/xattr_link - set_xattr scratch/xattr scratch/xattr_link + make_xattrs - bfs_diff -L scratch -xattr + case "$UNAME" in + Darwin|FreeBSD) + bfs_diff scratch -xattrname bfs_test + ;; + *) + bfs_diff scratch -xattrname security.bfs_test + ;; + esac +} + +function test_L_xattrname() { + if ! invoke_bfs scratch -quit -xattr 2>/dev/null; then + return 0 + fi + + make_xattrs + + case "$UNAME" in + Darwin|FreeBSD) + bfs_diff -L scratch -xattrname bfs_test + ;; + *) + bfs_diff -L scratch -xattrname security.bfs_test + ;; + esac } function test_help() { diff --git a/tests/test_L_xattr.out b/tests/test_L_xattr.out index 4dc4836..12fac95 100644 --- a/tests/test_L_xattr.out +++ b/tests/test_L_xattr.out @@ -1,2 +1,3 @@ scratch/link scratch/xattr +scratch/xattr_2 diff --git a/tests/test_L_xattrname.out b/tests/test_L_xattrname.out new file mode 100644 index 0000000..4dc4836 --- /dev/null +++ b/tests/test_L_xattrname.out @@ -0,0 +1,2 @@ +scratch/link +scratch/xattr diff --git a/tests/test_xattr.out b/tests/test_xattr.out index 0285ac1..109e7c9 100644 --- a/tests/test_xattr.out +++ b/tests/test_xattr.out @@ -1,2 +1,3 @@ scratch/xattr +scratch/xattr_2 scratch/xattr_link diff --git a/tests/test_xattrname.out b/tests/test_xattrname.out new file mode 100644 index 0000000..0285ac1 --- /dev/null +++ b/tests/test_xattrname.out @@ -0,0 +1,2 @@ +scratch/xattr +scratch/xattr_link |