Index: head/usr.sbin/extattr/Makefile =================================================================== --- head/usr.sbin/extattr/Makefile +++ head/usr.sbin/extattr/Makefile @@ -3,6 +3,8 @@ PROG= rmextattr MAN= rmextattr.8 +LIBADD= sbuf + LINKS+= ${BINDIR}/rmextattr ${BINDIR}/getextattr LINKS+= ${BINDIR}/rmextattr ${BINDIR}/setextattr LINKS+= ${BINDIR}/rmextattr ${BINDIR}/lsextattr Index: head/usr.sbin/extattr/rmextattr.8 =================================================================== --- head/usr.sbin/extattr/rmextattr.8 +++ head/usr.sbin/extattr/rmextattr.8 @@ -31,7 +31,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 30, 2000 +.Dd April 27, 2016 .Dt RMEXTATTR 8 .Os .Sh NAME @@ -61,6 +61,12 @@ .Ar attrname .Ar attrvalue .Ar filename ... +.Nm setextattr +.Fl i +.Op Fl fhnq +.Ar attrnamespace +.Ar attrname +.Ar filename ... .Sh DESCRIPTION These utilities @@ -91,6 +97,9 @@ (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.) @@ -99,6 +108,7 @@ .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. @@ -109,6 +119,7 @@ .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 @@ -129,7 +140,3 @@ .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 +++ head/usr.sbin/extattr/rmextattr.c @@ -37,6 +37,7 @@ */ #include +#include #include #include @@ -64,6 +65,8 @@ 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"); @@ -99,24 +102,28 @@ 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]; @@ -126,8 +133,8 @@ 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"; @@ -148,11 +155,14 @@ 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; @@ -169,6 +179,9 @@ argc -= optind; argv += optind; + if (what == EASET && flag_from_stdin == 0) + minargc++; + if (argc < minargc) usage(); @@ -184,9 +197,15 @@ 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++) { @@ -202,15 +221,17 @@ 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); } @@ -235,7 +256,7 @@ 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. */ @@ -243,7 +264,7 @@ 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: @@ -264,29 +285,26 @@ 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; Index: head/usr.sbin/extattr/tests/extattr_test.sh =================================================================== --- head/usr.sbin/extattr/tests/extattr_test.sh +++ head/usr.sbin/extattr/tests/extattr_test.sh @@ -66,6 +66,23 @@ 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" @@ -127,6 +144,19 @@ 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" @@ -273,12 +303,14 @@ 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