Index: head/usr.sbin/extattr/Makefile =================================================================== --- head/usr.sbin/extattr/Makefile (revision 299084) +++ head/usr.sbin/extattr/Makefile (revision 299085) @@ -1,20 +1,22 @@ # $FreeBSD$ 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 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: head/usr.sbin/extattr/rmextattr.8 =================================================================== --- head/usr.sbin/extattr/rmextattr.8 (revision 299084) +++ head/usr.sbin/extattr/rmextattr.8 (revision 299085) @@ -1,135 +1,142 @@ .\"- .\" 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. .\" .\" $FreeBSD$ .\" -.Dd August 30, 2000 +.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, do not print a 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 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 -.Sh BUGS -The -.Nm setextattr -utility can only be used to set attributes to strings. Index: head/usr.sbin/extattr/rmextattr.c =================================================================== --- head/usr.sbin/extattr/rmextattr.c (revision 299084) +++ head/usr.sbin/extattr/rmextattr.c (revision 299085) @@ -1,296 +1,314 @@ /*- * 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. * * $FreeBSD$ */ #include +#include #include #include #include #include #include #include #include #include #include #include #include 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; return; } int main(int argc, char *argv[]) { - char *buf, *visbuf, *p; +#define STDIN_BUF_SZ 1024 + char stdin_data[STDIN_BUF_SZ]; + char *p; const char *options, *attrname; size_t len; ssize_t ret; - int buflen, visbuflen, ch, error, i, arg_counter, attrnamespace, - minargc; + 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 flag_quiet = 0; + int count_quiet = 0; + int flag_from_stdin = 0; int flag_string = 0; int flag_hex = 0; - visbuflen = buflen = 0; - visbuf = buf = NULL; - 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 = "fhnq"; - minargc = 4; + 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': - flag_quiet = 1; + 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) { - mkbuf(&buf, &buflen, strlen(argv[0]) + 1); - strcpy(buf, argv[0]); - argc--; argv++; + 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 = strlen(buf) + flag_null; + len = sbuf_len(attrvalue) + flag_null; if (flag_nofollow) ret = extattr_set_link(argv[arg_counter], - attrnamespace, attrname, buf, len); + attrnamespace, attrname, + sbuf_data(attrvalue), len); else ret = extattr_set_file(argv[arg_counter], - attrnamespace, attrname, buf, len); + attrnamespace, attrname, + sbuf_data(attrvalue), len); if (ret >= 0) { - if ((size_t)ret != len && !flag_quiet) { + 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 (!flag_quiet) + 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 (!flag_quiet || ret > 0) + 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 (!flag_quiet) + 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\"\n", visbuf); - continue; + printf("\"%s\"", visbuf); } else if (flag_hex) { for (i = 0; i < ret; i++) - printf("%s%02x", i ? " " : "", - buf[i]); - printf("\n"); - continue; + printf("%s%02x", i ? " " : "", buf[i]); } else { fwrite(buf, ret, 1, stdout); - printf("\n"); - continue; } + if (count_quiet < 2) + printf("\n"); + continue; default: break; } - if (!flag_quiet) + if (!count_quiet) warn("%s: failed", argv[arg_counter]); if (flag_force) continue; return(1); } return (0); } Index: head/usr.sbin/extattr/tests/extattr_test.sh =================================================================== --- head/usr.sbin/extattr/tests/extattr_test.sh (revision 299084) +++ head/usr.sbin/extattr/tests/extattr_test.sh (revision 299085) @@ -1,290 +1,322 @@ # # 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. # # $FreeBSD$ atf_test_case bad_namespace bad_namespace_head() { atf_set "descr" "Can't set attributes for nonexistent namespaces" } bad_namespace_body() { 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() { touch foo atf_check -s exit:0 -o empty setextattr user myattr1 XYZ foo atf_check -s exit:0 -o inline:"58 59 5a\n" \ getextattr -qx user myattr1 foo } atf_test_case long_name long_name_head() { atf_set "descr" "A maximum length attribute name" } long_name_body() { # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208965 atf_expect_fail "BUG 208965 extattr(2) doesn't allow maxlen attr names" ATTRNAME=`jot -b X -s "" 255 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() { + 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() { 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() { 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() { 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() { 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() { 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() { + 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() { 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() { 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() { 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() { 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() { 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() { 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() { 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() { touch foo atf_check -s exit:1 -e match:"Operation not permitted" \ setextattr system myattr myvalue foo } atf_init_test_cases() { # TODO: add test cases for verbose output (without -q) atf_add_test_case bad_namespace atf_add_test_case hex 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 }