summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--Makefile6
-rw-r--r--eval.c13
-rw-r--r--eval.h1
-rw-r--r--fsade.c50
-rw-r--r--fsade.h12
-rw-r--r--parse.c18
-rwxr-xr-xtests.sh27
-rw-r--r--tests/test_L_xattr.out2
-rw-r--r--tests/test_xattr.out2
-rw-r--r--util.h4
11 files changed, 136 insertions, 5 deletions
diff --git a/.travis.yml b/.travis.yml
index 1acc520..8aeb402 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,13 @@ addons:
apt:
packages:
- gcc-multilib
+ - acl
- libacl1-dev
- libacl1:i386
+ - attr
+ - libattr1-dev
+ - libattr1:i386
+ - libcap2-bin
- libcap-dev
- libcap2:i386
@@ -19,6 +24,7 @@ matrix:
# Ubuntu doesn't let you install the -dev packages for both amd64 and
# i386 at once, so we make our own symlinks to fix -m32 -lacl -lcap
- sudo ln -s libacl.so.1 /lib/i386-linux-gnu/libacl.so
+ - sudo ln -s libattr.so.1 /lib/i386-linux-gnu/libattr.so
- sudo ln -s libcap.so.2 /lib/i386-linux-gnu/libcap.so
- os: osx
diff --git a/Makefile b/Makefile
index 868a011..f37c4ba 100644
--- a/Makefile
+++ b/Makefile
@@ -53,10 +53,10 @@ LOCAL_LDLIBS :=
ifeq ($(OS),Linux)
LOCAL_LDFLAGS += -Wl,--as-needed
-LOCAL_LDLIBS += -lacl -lcap -lrt
+LOCAL_LDLIBS += -lacl -lcap -lattr -lrt
-# These libraries are not build with msan, so disable them
-MSAN_CFLAGS := -DBFS_HAS_SYS_ACL=0 -DBFS_HAS_SYS_CAPABILITY=0
+# These libraries are not built with msan, so disable them
+MSAN_CFLAGS := -DBFS_HAS_SYS_ACL=0 -DBFS_HAS_SYS_CAPABILITY=0 -DBFS_HAS_SYS_XATTRS=0
endif
ALL_CPPFLAGS = $(LOCAL_CPPFLAGS) $(CPPFLAGS)
diff --git a/eval.c b/eval.c
index f07b380..bd69b67 100644
--- a/eval.c
+++ b/eval.c
@@ -887,6 +887,19 @@ bool eval_type(const struct expr *expr, struct eval_state *state) {
}
/**
+ * -xattr test.
+ */
+bool eval_xattr(const struct expr *expr, struct eval_state *state) {
+ int ret = bfs_check_xattrs(state->ftwbuf);
+ 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 91cc944..7038bb9 100644
--- a/eval.h
+++ b/eval.h
@@ -32,6 +32,7 @@ bool eval_access(const struct expr *expr, struct eval_state *state);
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_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 7d796e0..822fbd1 100644
--- a/fsade.c
+++ b/fsade.c
@@ -30,7 +30,11 @@
# include <sys/capability.h>
#endif
-#if BFS_CAN_CHECK_ACL || BFS_CAN_CHECK_CAPABILITIES
+#if BFS_CAN_CHECK_XATTRS
+# include <sys/xattr.h>
+#endif
+
+#if BFS_CAN_CHECK_ACL || BFS_CAN_CHECK_CAPABILITIES || BFS_CAN_CHECK_XATTRS
/**
* Many of the APIs used here don't have *at() variants, but we can try to
@@ -103,7 +107,7 @@ static bool is_absence_error(int error) {
return false;
}
-#endif // BFS_CAN_CHECK_ACL || BFS_CAN_CHECK_CAPABILITIES
+#endif // BFS_CAN_CHECK_ACL || BFS_CAN_CHECK_CAPABILITIES || BFS_CAN_CHECK_XATTRS
#if BFS_CAN_CHECK_ACL
@@ -230,3 +234,45 @@ int bfs_check_capabilities(const struct BFTW *ftwbuf) {
}
#endif
+
+#if BFS_CAN_CHECK_XATTRS
+
+int bfs_check_xattrs(const struct BFTW *ftwbuf) {
+ const char *path = fake_at(ftwbuf);
+ ssize_t len;
+
+#if __APPLE__
+ int options = ftwbuf->typeflag == BFTW_LNK ? XATTR_NOFOLLOW : 0;
+ len = listxattr(path, NULL, 0, options);
+#else
+ if (ftwbuf->typeflag == BFTW_LNK) {
+ len = llistxattr(path, NULL, 0);
+ } else {
+ len = listxattr(path, NULL, 0);
+ }
+#endif
+
+ int error = errno;
+
+ free_fake_at(ftwbuf, path);
+
+ if (len > 0) {
+ return 1;
+ } else if (len == 0 || 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) {
+ errno = ENOTSUP;
+ return -1;
+}
+
+#endif
diff --git a/fsade.h b/fsade.h
index d4e97c0..c9dcfc9 100644
--- a/fsade.h
+++ b/fsade.h
@@ -35,6 +35,8 @@
# endif
#endif
+#define BFS_CAN_CHECK_XATTRS BFS_HAS_SYS_XATTR
+
/**
* Check if a file has a non-trvial Access Control List.
*
@@ -55,4 +57,14 @@ int bfs_check_acl(const struct BFTW *ftwbuf);
*/
int bfs_check_capabilities(const struct BFTW *ftwbuf);
+/**
+ * Check if a file has any extended attributes set.
+ *
+ * @param ftwbuf
+ * The file to check.
+ * @return
+ * 1 if it does, 0 if it doesn't, or -1 if an error occurred.
+ */
+int bfs_check_xattrs(const struct BFTW *ftwbuf);
+
#endif // BFS_FSADE_H
diff --git a/parse.c b/parse.c
index dbca7a9..4379e7a 100644
--- a/parse.c
+++ b/parse.c
@@ -2338,6 +2338,23 @@ static struct expr *parse_warn(struct parser_state *state, int warn, int arg2) {
}
/**
+ * Parse -xattr.
+ */
+static struct expr *parse_xattr(struct parser_state *state, int arg1, int arg2) {
+#if BFS_CAN_CHECK_XATTRS
+ struct expr *expr = parse_nullary_test(state, eval_xattr);
+ if (expr) {
+ expr->cost = STAT_COST;
+ expr->probability = 0.01;
+ }
+ return expr;
+#else
+ parse_error(state, "%s is missing platform support.\n", state->argv[0]);
+ return NULL;
+#endif
+}
+
+/**
* "Parse" -help.
*/
static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) {
@@ -2692,6 +2709,7 @@ static const struct table_entry parse_table[] = {
{"-wholename", false, parse_path, false},
{"-writable", false, parse_access, W_OK},
{"-x", false, parse_mount},
+ {"-xattr", false, parse_xattr},
{"-xdev", false, parse_mount},
{"-xtype", false, parse_type, true},
{"--"},
diff --git a/tests.sh b/tests.sh
index c776ba4..bec7022 100755
--- a/tests.sh
+++ b/tests.sh
@@ -648,6 +648,9 @@ sudo_tests=(
test_capable
test_L_capable
+ test_xattr
+ test_L_xattr
+
test_mount
test_xdev
@@ -2351,6 +2354,30 @@ function test_L_capable() {
bfs_diff -L scratch -capable
}
+function test_xattr() {
+ rm -rf scratch/*
+ touch scratch/{normal,xattr}
+ # 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 scratch/xattr
+ ln -s xattr scratch/link
+ ln -s normal scratch/xattr_link
+ sudo setfattr -h -n security.bfs_test scratch/xattr_link
+
+ bfs_diff scratch -xattr
+}
+
+function test_L_xattr() {
+ rm -rf scratch/*
+ touch scratch/{normal,xattr}
+ sudo setfattr -n security.bfs_test scratch/xattr
+ ln -s xattr scratch/link
+ ln -s normal scratch/xattr_link
+ sudo setfattr -h -n security.bfs_test scratch/xattr_link
+
+ bfs_diff -L scratch -xattr
+}
+
BOL=
EOL='\n'
diff --git a/tests/test_L_xattr.out b/tests/test_L_xattr.out
new file mode 100644
index 0000000..4dc4836
--- /dev/null
+++ b/tests/test_L_xattr.out
@@ -0,0 +1,2 @@
+scratch/link
+scratch/xattr
diff --git a/tests/test_xattr.out b/tests/test_xattr.out
new file mode 100644
index 0000000..0285ac1
--- /dev/null
+++ b/tests/test_xattr.out
@@ -0,0 +1,2 @@
+scratch/xattr
+scratch/xattr_link
diff --git a/util.h b/util.h
index 0a4779f..73258b9 100644
--- a/util.h
+++ b/util.h
@@ -67,6 +67,10 @@
# define BFS_HAS_SYS_SYSMACROS BFS_HAS_INCLUDE(<sys/sysmacros.h>, __GLIBC__)
#endif
+#ifndef BFS_HAS_SYS_XATTR
+# define BFS_HAS_SYS_XATTR BFS_HAS_INCLUDE(<sys/xattr.h>, __linux__)
+#endif
+
#if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE)
# define FNM_CASEFOLD FNM_IGNORECASE
#endif