aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/extattr
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/extattr')
-rw-r--r--usr.sbin/extattr/Makefile19
-rw-r--r--usr.sbin/extattr/Makefile.depend17
-rw-r--r--usr.sbin/extattr/rmextattr.8141
-rw-r--r--usr.sbin/extattr/rmextattr.c314
-rw-r--r--usr.sbin/extattr/tests/Makefile3
-rw-r--r--usr.sbin/extattr/tests/Makefile.depend10
-rwxr-xr-xusr.sbin/extattr/tests/extattr_test.sh367
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
+}