From f1fb3158d3f242f1884d8d8a7473ab0719e93e8c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 May 2019 17:13:39 -0400 Subject: Implement -xattr predicate --- .travis.yml | 6 ++++++ Makefile | 6 +++--- eval.c | 13 +++++++++++++ eval.h | 1 + fsade.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- fsade.h | 12 ++++++++++++ parse.c | 18 ++++++++++++++++++ tests.sh | 27 +++++++++++++++++++++++++++ tests/test_L_xattr.out | 2 ++ tests/test_xattr.out | 2 ++ util.h | 4 ++++ 11 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 tests/test_L_xattr.out create mode 100644 tests/test_xattr.out 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 @@ -886,6 +886,19 @@ bool eval_type(const struct expr *expr, struct eval_state *state) { return state->ftwbuf->typeflag & expr->idata; } +/** + * -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. */ 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 #endif -#if BFS_CAN_CHECK_ACL || BFS_CAN_CHECK_CAPABILITIES +#if BFS_CAN_CHECK_XATTRS +# include +#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 @@ -2337,6 +2337,23 @@ static struct expr *parse_warn(struct parser_state *state, int warn, int arg2) { return parse_nullary_positional_option(state); } +/** + * 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. */ @@ -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(, __GLIBC__) #endif +#ifndef BFS_HAS_SYS_XATTR +# define BFS_HAS_SYS_XATTR BFS_HAS_INCLUDE(, __linux__) +#endif + #if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE) # define FNM_CASEFOLD FNM_IGNORECASE #endif -- cgit v1.2.3