diff options
Diffstat (limited to 'usr.sbin/extattr')
| -rw-r--r-- | usr.sbin/extattr/Makefile | 19 | ||||
| -rw-r--r-- | usr.sbin/extattr/Makefile.depend | 17 | ||||
| -rw-r--r-- | usr.sbin/extattr/rmextattr.8 | 141 | ||||
| -rw-r--r-- | usr.sbin/extattr/rmextattr.c | 314 | ||||
| -rw-r--r-- | usr.sbin/extattr/tests/Makefile | 3 | ||||
| -rw-r--r-- | usr.sbin/extattr/tests/Makefile.depend | 10 | ||||
| -rwxr-xr-x | usr.sbin/extattr/tests/extattr_test.sh | 367 | 
7 files changed, 871 insertions, 0 deletions
| diff --git a/usr.sbin/extattr/Makefile b/usr.sbin/extattr/Makefile new file mode 100644 index 000000000000..b2951dabf935 --- /dev/null +++ b/usr.sbin/extattr/Makefile @@ -0,0 +1,19 @@ +PROG=	rmextattr +MAN=	rmextattr.8 + +LIBADD=	sbuf + +LINKS+=	${BINDIR}/rmextattr ${BINDIR}/getextattr +LINKS+=	${BINDIR}/rmextattr ${BINDIR}/setextattr +LINKS+=	${BINDIR}/rmextattr ${BINDIR}/lsextattr + +MLINKS+=	rmextattr.8 setextattr.8 +MLINKS+=	rmextattr.8 getextattr.8 +MLINKS+=	rmextattr.8 lsextattr.8 + +.include <src.opts.mk> + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + +.include <bsd.prog.mk> diff --git a/usr.sbin/extattr/Makefile.depend b/usr.sbin/extattr/Makefile.depend new file mode 100644 index 000000000000..3c711d830978 --- /dev/null +++ b/usr.sbin/extattr/Makefile.depend @@ -0,0 +1,17 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ +	include \ +	include/xlocale \ +	lib/${CSU_DIR} \ +	lib/libc \ +	lib/libcompiler_rt \ +	lib/libsbuf \ +	lib/libutil \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/extattr/rmextattr.8 b/usr.sbin/extattr/rmextattr.8 new file mode 100644 index 000000000000..dee62ed73872 --- /dev/null +++ b/usr.sbin/extattr/rmextattr.8 @@ -0,0 +1,141 @@ +.\"- +.\" Copyright (c) 2000, 2001 Robert N. M. Watson +.\" Copyright (c) 2002 Networks Associates Technology, Inc. +.\" All rights reserved. +.\" +.\" This software was developed for the FreeBSD Project by Poul-Henning +.\" Kamp and Network Associates Laboratories, the Security Research Division +.\" of Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 +.\" ("CBOSS"), as part of the DARPA CHATS research program +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 27, 2016 +.Dt RMEXTATTR 8 +.Os +.Sh NAME +.Nm getextattr , +.Nm lsextattr , +.Nm rmextattr , +.Nm setextattr +.Nd manipulate extended attributes +.Sh SYNOPSIS +.Nm getextattr +.Op Fl fhqsx +.Ar attrnamespace +.Ar attrname +.Ar filename ... +.Nm lsextattr +.Op Fl fhq +.Ar attrnamespace +.Ar filename ... +.Nm rmextattr +.Op Fl fhq +.Ar attrnamespace +.Ar attrname +.Ar filename ... +.Nm setextattr +.Op Fl fhnq +.Ar attrnamespace +.Ar attrname +.Ar attrvalue +.Ar filename ... +.Nm setextattr +.Fl i +.Op Fl fhnq +.Ar attrnamespace +.Ar attrname +.Ar filename ... +.Sh DESCRIPTION +These +utilities +are user tools to manipulate the named extended attributes on files and +directories. +The +.Ar attrnamespace +argument should be the namespace of the attribute to retrieve: legal +values are +.Cm user +and +.Cm system . +The +.Ar attrname +argument should be the name of the attribute, +.Ar filename +the name of the target file or directory, +.Ar attrvalue +a string to store in the attribute. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f +(Force.) +Ignore errors on individual filenames and continue with +the remaining arguments. +.It Fl h +(No follow.) +If the file is a symbolic link, perform the operation on the +link itself rather than the file that the link points to. +.It Fl i +(From stdin.) +Read attribute data from stdin instead of as an argument. +.It Fl n +.Dv ( NUL Ns +-terminate.) +.Dv NUL Ns +-terminate the extent content written out. +.It Fl q +(Quiet.) +Do not print out the pathname and suppress error messages. +When given twice, print only the attribute value, with no trailing newline. +.It Fl s +(Stringify.) +Escape nonprinting characters and put quotes around the output. +.It Fl x +(Hex.) +Print the output in hexadecimal. +.El +.Sh EXAMPLES +.Bd -literal +setextattr system md5 `md5 -q /boot/kernel/kernel` /boot/kernel/kernel +md5 -q /boot/kernel/kernel | setextattr -i system md5 /boot/kernel/kernel +getextattr system md5 /boot/kernel/kernel +getextattr -qq system md5 /boot/kernel/kernel | od -x +lsextattr system /boot/kernel/kernel +rmextattr system md5 /boot/kernel/kernel +.Ed +.Sh SEE ALSO +.Xr extattr 2 , +.Xr extattr 3 , +.Xr extattrctl 8 , +.Xr extattr 9 +.Sh HISTORY +Extended attribute support was developed as part of the +.Tn TrustedBSD +Project, +and introduced in +.Fx 5.0 . +It was developed to support security extensions requiring additional labels +to be associated with each file or directory. +.Sh AUTHORS +.An Robert N M Watson +.An Poul-Henning Kamp diff --git a/usr.sbin/extattr/rmextattr.c b/usr.sbin/extattr/rmextattr.c new file mode 100644 index 000000000000..305feb2b8f21 --- /dev/null +++ b/usr.sbin/extattr/rmextattr.c @@ -0,0 +1,314 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. + * Copyright (c) 2002 Poul-Henning Kamp. + * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Poul-Henning + * Kamp and Network Associates Laboratories, the Security Research Division + * of Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. The names of the authors may not be used to endorse or promote + *    products derived from this software without specific prior written + *    permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/sbuf.h> +#include <sys/uio.h> +#include <sys/extattr.h> + +#include <libgen.h> +#include <libutil.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <vis.h> +#include <err.h> +#include <errno.h> + +static enum { EADUNNO, EAGET, EASET, EARM, EALS } what = EADUNNO; + +static void __dead2 +usage(void)  +{ + +	switch (what) { +	case EAGET: +		fprintf(stderr, "usage: getextattr [-fhqsx] attrnamespace"); +		fprintf(stderr, " attrname filename ...\n"); +		exit(-1); +	case EASET: +		fprintf(stderr, "usage: setextattr [-fhnq] attrnamespace"); +		fprintf(stderr, " attrname attrvalue filename ...\n"); +		fprintf(stderr, "   or  setextattr -i [-fhnq] attrnamespace"); +		fprintf(stderr, " attrname filename ...\n"); +		exit(-1); +	case EARM: +		fprintf(stderr, "usage: rmextattr [-fhq] attrnamespace"); +		fprintf(stderr, " attrname filename ...\n"); +		exit(-1); +	case EALS: +		fprintf(stderr, "usage: lsextattr [-fhq] attrnamespace"); +		fprintf(stderr, " filename ...\n"); +		exit(-1); +	case EADUNNO: +	default: +		fprintf(stderr, "usage: (getextattr|lsextattr|rmextattr"); +		fprintf(stderr, "|setextattr)\n"); +		exit (-1); +	} +} + +static void +mkbuf(char **buf, int *oldlen, int newlen) +{ + +	if (*oldlen >= newlen) +		return; +	if (*buf != NULL) +		free(*buf); +	*buf = malloc(newlen); +	if (*buf == NULL) +		err(1, "malloc"); +	*oldlen = newlen; +} + +int +main(int argc, char *argv[]) +{ +#define STDIN_BUF_SZ 4096 +	char	 stdin_data[STDIN_BUF_SZ]; +	char	*p; + +	const char *options, *attrname; +	size_t	len; +	ssize_t	ret; +	int	 ch, error, i, arg_counter, attrnamespace, minargc; + +	char   *visbuf = NULL; +	int	visbuflen = 0; +	char   *buf = NULL; +	int	buflen = 0; +	struct	sbuf *attrvalue = NULL; +	int	flag_force = 0; +	int	flag_nofollow = 0; +	int	flag_null = 0; +	int	count_quiet = 0; +	int	flag_from_stdin = 0; +	int	flag_string = 0; +	int	flag_hex = 0; + +	p = basename(argv[0]); +	if (p == NULL) +		p = argv[0]; +	if (!strcmp(p, "getextattr")) { +		what = EAGET; +		options = "fhqsx"; +		minargc = 3; +	} else if (!strcmp(p, "setextattr")) { +		what = EASET; +		options = "fhinq"; +		minargc = 3; +	} else if (!strcmp(p, "rmextattr")) { +		what = EARM; +		options = "fhq"; +		minargc = 3; +	} else if (!strcmp(p, "lsextattr")) { +		what = EALS; +		options = "fhq"; +		minargc = 2; +	} else { +		usage(); +	} + +	while ((ch = getopt(argc, argv, options)) != -1) { +		switch (ch) { +		case 'f': +			flag_force = 1; +			break; +		case 'h': +			flag_nofollow = 1; +			break; +		case 'i': +			flag_from_stdin = 1; +			break; +		case 'n': +			flag_null = 1; +			break; +		case 'q': +			count_quiet += 1; +			break; +		case 's': +			flag_string = 1; +			break; +		case 'x': +			flag_hex = 1; +			break; +		case '?': +		default: +			usage(); +		} +	} + +	argc -= optind; +	argv += optind; + +	if (what == EASET && flag_from_stdin == 0) +		minargc++; + +	if (argc < minargc) +		usage(); + +	error = extattr_string_to_namespace(argv[0], &attrnamespace); +	if (error) +		err(-1, "%s", argv[0]); +	argc--; argv++; + +	if (what != EALS) { +		attrname = argv[0]; +		argc--; argv++; +	} else +		attrname = NULL; + +	if (what == EASET) { +		attrvalue = sbuf_new_auto(); +		if (flag_from_stdin) { +			while ((error = read(0, stdin_data, STDIN_BUF_SZ)) > 0) +				sbuf_bcat(attrvalue, stdin_data, error); +		} else { +			sbuf_cpy(attrvalue, argv[0]); +			argc--; argv++; +		} +		sbuf_finish(attrvalue); +	} + +	for (arg_counter = 0; arg_counter < argc; arg_counter++) { +		switch (what) { +		case EARM: +			if (flag_nofollow) +				error = extattr_delete_link(argv[arg_counter], +				    attrnamespace, attrname); +			else +				error = extattr_delete_file(argv[arg_counter], +				    attrnamespace, attrname); +			if (error >= 0) +				continue; +			break; +		case EASET: +			len = sbuf_len(attrvalue) + flag_null; +			if (flag_nofollow) +				ret = extattr_set_link(argv[arg_counter], +				    attrnamespace, attrname, +				    sbuf_data(attrvalue), len); +			else +				ret = extattr_set_file(argv[arg_counter], +				    attrnamespace, attrname, +				    sbuf_data(attrvalue), len); +			if (ret >= 0) { +				if ((size_t)ret != len && !count_quiet) { +					warnx("Set %zd bytes of %zu for %s", +					    ret, len, attrname); +				} +				continue; +			} +			break; +		case EALS: +			if (flag_nofollow) +				ret = extattr_list_link(argv[arg_counter], +				    attrnamespace, NULL, 0); +			else +				ret = extattr_list_file(argv[arg_counter], +				    attrnamespace, NULL, 0); +			if (ret < 0) +				break; +			mkbuf(&buf, &buflen, ret); +			if (flag_nofollow) +				ret = extattr_list_link(argv[arg_counter], +				    attrnamespace, buf, buflen); +			else +				ret = extattr_list_file(argv[arg_counter], +				    attrnamespace, buf, buflen); +			if (ret < 0) +				break; +			if (!count_quiet) +				printf("%s\t", argv[arg_counter]); +			for (i = 0; i < ret; i += ch + 1) { +			    /* The attribute name length is unsigned. */ +			    ch = (unsigned char)buf[i]; +			    printf("%s%*.*s", i ? "\t" : "", +				ch, ch, buf + i + 1); +			} +			if (!count_quiet || ret > 0) +				printf("\n"); +			continue; +		case EAGET: +			if (flag_nofollow) +				ret = extattr_get_link(argv[arg_counter], +				    attrnamespace, attrname, NULL, 0); +			else +				ret = extattr_get_file(argv[arg_counter], +				    attrnamespace, attrname, NULL, 0); +			if (ret < 0) +				break; +			mkbuf(&buf, &buflen, ret); +			if (flag_nofollow) +				ret = extattr_get_link(argv[arg_counter], +				    attrnamespace, attrname, buf, buflen); +			else +				ret = extattr_get_file(argv[arg_counter], +				    attrnamespace, attrname, buf, buflen); +			if (ret < 0) +				break; +			if (!count_quiet) +				printf("%s\t", argv[arg_counter]); +			if (flag_string) { +				mkbuf(&visbuf, &visbuflen, ret * 4 + 1); +				strvisx(visbuf, buf, ret, +				    VIS_SAFE | VIS_WHITE); +				printf("\"%s\"", visbuf); +			} else if (flag_hex) { +				for (i = 0; i < ret; i++) +					printf("%s%02x", i ? " " : "", +							(unsigned char)buf[i]); +			} else { +				fwrite(buf, ret, 1, stdout); +			} +			if (count_quiet < 2) +				printf("\n"); +			continue; +		default: +			break; +		} +		if (!count_quiet)  +			warn("%s: failed", argv[arg_counter]); +		if (flag_force) +			continue; +		return(1); +	} +	return (0); +} diff --git a/usr.sbin/extattr/tests/Makefile b/usr.sbin/extattr/tests/Makefile new file mode 100644 index 000000000000..a39bf21c7863 --- /dev/null +++ b/usr.sbin/extattr/tests/Makefile @@ -0,0 +1,3 @@ +ATF_TESTS_SH=	extattr_test + +.include <bsd.test.mk> diff --git a/usr.sbin/extattr/tests/Makefile.depend b/usr.sbin/extattr/tests/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/usr.sbin/extattr/tests/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/extattr/tests/extattr_test.sh b/usr.sbin/extattr/tests/extattr_test.sh new file mode 100755 index 000000000000..0b16350a958a --- /dev/null +++ b/usr.sbin/extattr/tests/extattr_test.sh @@ -0,0 +1,367 @@ +# +# Copyright (c) 2016 Spectra Logic Corp +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +#    notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +#    notice, this list of conditions and the following disclaimer in the +#    documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +atf_test_case bad_namespace +bad_namespace_head() { +	atf_set "descr" "Can't set attributes for nonexistent namespaces" +} +bad_namespace_body() { +	check_fs +	touch foo +	atf_check -s not-exit:0 -e match:"Invalid argument" \ +		setextattr badnamespace myattr X foo +	atf_check -s not-exit:0 -e match:"Invalid argument" \ +		lsextattr -q badnamespace foo +} + +atf_test_case hex +hex_head() { +	atf_set "descr" "Set and get attribute values in hexadecimal" +} +hex_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr user myattr XYZ foo +	atf_check -s exit:0 -o inline:"58 59 5a\n" \ +		getextattr -qx user myattr foo +} + +atf_test_case hex_nonascii +hex_nonascii_head() { +	atf_set "descr" "Get binary attribute values in hexadecimal" +} +hex_nonascii_body() { +	check_fs +	touch foo +	BINSTUFF=`echo $'\x20\x30\x40\x55\x66\x70\x81\xa2\xb3\xee\xff'` +	atf_check -s exit:0 -o empty setextattr user myattr "$BINSTUFF" foo +	getextattr user myattr foo +	atf_check -s exit:0 -o inline:"20 30 40 55 66 70 81 a2 b3 ee ff\n" \ +		getextattr -qx user myattr foo +} + +atf_test_case long_name +long_name_head() { +	atf_set "descr" "A maximum length attribute name" +} +long_name_body() { +	check_fs + +	if ! NAME_MAX=$(getconf NAME_MAX .); then +		atf_skip "Filesystem not reporting NAME_MAX; skipping testcase" +	fi + +	ATTRNAME=`jot -b X -s "" $NAME_MAX 0` +	touch foo +	atf_check -s exit:0 -o empty setextattr user $ATTRNAME myvalue foo +	atf_check -s exit:0 -o inline:"${ATTRNAME}\n" lsextattr -q user foo +	atf_check -s exit:0 -o inline:"myvalue\n" \ +		getextattr -q user ${ATTRNAME} foo +	atf_check -s exit:0 -o empty rmextattr user ${ATTRNAME} foo +	atf_check -s exit:0 -o empty lsextattr -q user foo +} + +atf_test_case loud +loud_head() { +	atf_set "descr" "Loud (non -q) output for each command" +} +loud_body() { +	check_fs +	touch foo +	# setextattr(8) and friends print hard tabs.  Use printf to convert +	# them to spaces before checking the output. +	atf_check -s exit:0 -o empty setextattr user myattr myvalue foo +	atf_check -s exit:0 -o inline:"foo myattr" \ +		printf "%s %s" $(lsextattr user foo) +	atf_check -s exit:0 -o inline:"foo myvalue" \ +		printf "%s %s" $(getextattr user myattr foo) +	atf_check -s exit:0 -o empty rmextattr user myattr foo +	atf_check -s exit:0 -o inline:"foo" printf %s $(lsextattr user foo) +} + +atf_test_case noattrs +noattrs_head() { +	atf_set "descr" "A file with no extended attributes" +} +noattrs_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty lsextattr -q user foo +} + +atf_test_case nonexistent_file +nonexistent_file_head() { +	atf_set "descr" "A file that does not exist" +} +nonexistent_file_body() { +	check_fs +	atf_check -s exit:1 -e match:"No such file or directory" \ +		lsextattr user foo +	atf_check -s exit:1 -e match:"No such file or directory" \ +		setextattr user myattr myvalue foo +	atf_check -s exit:1 -e match:"No such file or directory" \ +		getextattr user myattr foo +	atf_check -s exit:1 -e match:"No such file or directory" \ +		rmextattr user myattr foo +} + +atf_test_case null +null_head() { +	atf_set "descr" "NUL-terminate an attribute value" +} +null_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr -n user myattr myvalue foo +	atf_check -s exit:0 -o inline:"myvalue\0\n" getextattr -q user myattr foo +} + +atf_test_case one_user_attr +one_user_attr_head() { +	atf_set "descr" "A file with one extended attribute" +} +one_user_attr_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr user myattr myvalue foo +	atf_check -s exit:0 -o inline:"myattr\n" lsextattr -q user foo +	atf_check -s exit:0 -o inline:"myvalue\n" getextattr -q user myattr foo +	atf_check -s exit:0 -o empty rmextattr user myattr foo +	atf_check -s exit:0 -o empty lsextattr -q user foo +} + +atf_test_case one_system_attr +one_system_attr_head() { +	atf_set "descr" "A file with one extended attribute" +	atf_set "require.user" "root" +} +one_system_attr_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr system myattr myvalue foo +	atf_check -s exit:0 -o inline:"myattr\n" lsextattr -q system foo +	atf_check -s exit:0 -o inline:"myvalue\n" getextattr -q system myattr foo +	atf_check -s exit:0 -o empty rmextattr system myattr foo +	atf_check -s exit:0 -o empty lsextattr -q system foo +} + +atf_test_case stdin +stdin_head() { +	atf_set "descr" "Set attribute value from stdin" +} +stdin_body() { +	check_fs +	dd if=/dev/random of=infile bs=1k count=8 +	touch foo +	setextattr -i user myattr foo < infile || atf_fail "setextattr failed" +	atf_check -s exit:0 -o inline:"myattr\n" lsextattr -q user foo +	getextattr -qq user myattr foo > outfile || atf_fail "getextattr failed" +	atf_check -s exit:0 cmp -s infile outfile +} + +atf_test_case stringify +stringify_head() { +	atf_set "descr" "Stringify the output of getextattr" +} +stringify_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr user myattr "my value" foo +	atf_check -s exit:0 -o inline:"\"my\\\040value\"\n" \ +		getextattr -qs user myattr foo +} + +atf_test_case symlink +symlink_head() { +	atf_set "descr" "A symlink to an ordinary file" +} +symlink_body() { +	check_fs +	touch foo +	ln -s foo foolink +	atf_check -s exit:0 -o empty setextattr user myattr myvalue foolink +	atf_check -s exit:0 -o inline:"myvalue\n" \ +		getextattr -q user myattr foolink +	atf_check -s exit:0 -o inline:"myvalue\n" getextattr -q user myattr foo +} + +atf_test_case symlink_nofollow +symlink_nofollow_head() { +	atf_set "descr" "Operating directly on a symlink" +} +symlink_nofollow_body() { +	check_fs +	touch foo +	ln -s foo foolink +	# Check that with -h we can operate directly on the link +	atf_check -s exit:0 -o empty setextattr -h user myattr myvalue foolink +	atf_check -s exit:0 -o inline:"myvalue\n" \ +		getextattr -qh user myattr foolink +	atf_check -s exit:1 -e match:"Attribute not found" \ +		getextattr user myattr foolink +	atf_check -s exit:1 -e match:"Attribute not found" \ +		getextattr user myattr foo + +	# Check that with -h we cannot operate on the destination file +	atf_check -s exit:0 -o empty setextattr user otherattr othervalue foo +	atf_check -s exit:1 getextattr -qh user otherattr foolink +} + +atf_test_case system_and_user_attrs +system_and_user_attrs_head() { +	atf_set "descr" "A file with both system and user extended attributes" +	atf_set "require.user" "root" +} +system_and_user_attrs_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr user userattr userval foo +	atf_check -s exit:0 -o empty setextattr system sysattr sysval foo +	atf_check -s exit:0 -o inline:"userattr\n" lsextattr -q user foo +	atf_check -s exit:0 -o inline:"sysattr\n" lsextattr -q system foo + +	atf_check -s exit:0 -o inline:"userval\n" getextattr -q user userattr foo +	atf_check -s exit:0 -o inline:"sysval\n" getextattr -q system sysattr foo +	atf_check -s exit:0 -o empty rmextattr user userattr foo +	atf_check -s exit:0 -o empty rmextattr system sysattr foo +	atf_check -s exit:0 -o empty lsextattr -q user foo +	atf_check -s exit:0 -o empty lsextattr -q system foo +} + +atf_test_case two_files +two_files_head() { +	atf_set "descr" "Manipulate two files" +} +two_files_body() { +	check_fs +	touch foo bar +	atf_check -s exit:0 -o empty setextattr user myattr myvalue foo bar +	atf_check -s exit:0 -o inline:"foo\tmyattr\nbar\tmyattr\n" \ +		lsextattr user foo bar +	atf_check -s exit:0 \ +		-o inline:"foo\tmyvalue\nbar\tmyvalue\n" \ +		getextattr user myattr foo bar +	atf_check -s exit:0 -o empty rmextattr user myattr foo bar +	atf_check -s exit:0 -o empty lsextattr -q user foo bar +} + +atf_test_case two_files_force +two_files_force_head() { +	atf_set "descr" "Manipulate two files.  The first does not exist" +} +two_files_force_body() { +	check_fs +	touch bar +	atf_check -s exit:1 -e match:"No such file or directory" \ +		setextattr user myattr myvalue foo bar +	atf_check -s exit:0 -e ignore setextattr -f user myattr myvalue foo bar +	atf_check -s exit:1 -e match:"No such file or directory" \ +		lsextattr user foo bar +	atf_check -s exit:0 -e ignore -o inline:"bar\tmyattr\n" \ +		lsextattr -f user foo bar +	atf_check -s exit:1 -e match:"No such file or directory" \ +		getextattr user myattr foo bar +	atf_check -s exit:0 -e ignore \ +		-o inline:"bar\tmyvalue\n" \ +		getextattr -f user myattr foo bar +	atf_check -s exit:1 -e match:"No such file or directory" \ +		rmextattr user myattr foo bar +	atf_check -s exit:0 -e ignore \ +		rmextattr -f user myattr foo bar +	atf_check -s exit:0 -o empty lsextattr -q user bar +} + +atf_test_case two_user_attrs +two_user_attrs_head() { +	atf_set "descr" "A file with two extended attributes" +} +two_user_attrs_body() { +	check_fs +	touch foo +	atf_check -s exit:0 -o empty setextattr user myattr1 myvalue1 foo +	atf_check -s exit:0 -o empty setextattr user myattr2 myvalue2 foo +	# lsextattr could return the attributes in any order, so we must be +	# careful how we compare them. +	raw_output=`lsextattr -q user foo` || atf_fail "lsextattr failed" +	tabless_output=`printf "%s %s" ${raw_output}` +	if [ "myattr1 myattr2" != "${tabless_output}" -a \ +	     "myattr2 myattr1" != "${tabless_output}" ]; then +		atf_fail "lsextattr printed ${tabless_output}" +	fi +	atf_check -s exit:0 -o inline:"myvalue1\n" getextattr -q user myattr1 foo +	atf_check -s exit:0 -o inline:"myvalue2\n" getextattr -q user myattr2 foo +	atf_check -s exit:0 -o empty rmextattr user myattr2 foo +	atf_check -s exit:0 -o empty rmextattr user myattr1 foo +	atf_check -s exit:0 -o empty lsextattr -q user foo +} + +atf_test_case unprivileged_user_cannot_set_system_attr +unprivileged_user_cannot_set_system_attr_head() { +	atf_set "descr" "Unprivileged users can't set system attributes" +        atf_set "require.user" "unprivileged" +} +unprivileged_user_cannot_set_system_attr_body() { +	check_fs +	touch foo +	atf_check -s exit:1 -e match:"Operation not permitted" \ +		setextattr system myattr myvalue foo +} + + +atf_init_test_cases() { +	atf_add_test_case bad_namespace +	atf_add_test_case hex +	atf_add_test_case hex_nonascii +	atf_add_test_case long_name +	atf_add_test_case loud +	atf_add_test_case noattrs +	atf_add_test_case nonexistent_file +	atf_add_test_case null +	atf_add_test_case symlink_nofollow +	atf_add_test_case one_user_attr +	atf_add_test_case one_system_attr +	atf_add_test_case stdin +	atf_add_test_case stringify +	atf_add_test_case symlink +	atf_add_test_case symlink_nofollow +	atf_add_test_case system_and_user_attrs +	atf_add_test_case two_files +	atf_add_test_case two_files_force +	atf_add_test_case two_user_attrs +	atf_add_test_case unprivileged_user_cannot_set_system_attr +} + +check_fs() { +	case `df -T . | tail -n 1 | cut -wf 2` in +		"ufs") +		case `dumpfs . | head -1 | awk -F'[()]' '{print $2}'` in +			"UFS1") atf_skip "UFS1 is not supported by this test";; +			"UFS2") ;; # UFS2 is fine +		esac ;; +		"zfs") ;; # ZFS is fine +		"tmpfs") atf_skip "tmpfs does not support extended attributes";; +	esac +} | 
