summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--RELEASES.md2
-rw-r--r--bfs.14
-rw-r--r--eval.c13
-rw-r--r--eval.h1
-rw-r--r--fsade.c44
-rw-r--r--fsade.h12
-rw-r--r--parse.c20
-rwxr-xr-xtests.sh77
-rw-r--r--tests/test_L_xattr.out1
-rw-r--r--tests/test_L_xattrname.out2
-rw-r--r--tests/test_xattr.out1
-rw-r--r--tests/test_xattrname.out2
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
diff --git a/bfs.1 b/bfs.1
index 4c78bde..abb4189 100644
--- a/bfs.1
+++ b/bfs.1
@@ -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
diff --git a/eval.c b/eval.c
index dc859b7..f8b9a6d 100644
--- a/eval.c
+++ b/eval.c
@@ -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) {
diff --git a/eval.h b/eval.h
index b31c0d5..a95c3ac 100644
--- a/eval.h
+++ b/eval.h
@@ -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);
diff --git a/fsade.c b/fsade.c
index acd39bb..efe5092 100644
--- a/fsade.c
+++ b/fsade.c
@@ -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
diff --git a/fsade.h b/fsade.h
index fedc8ed..3136608 100644
--- a/fsade.h
+++ b/fsade.h
@@ -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
diff --git a/parse.c b/parse.c
index 42dfc2e..155f10b 100644
--- a/parse.c
+++ b/parse.c
@@ -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},
diff --git a/tests.sh b/tests.sh
index 88b6988..bbc823d 100755
--- a/tests.sh
+++ b/tests.sh
@@ -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