xref: /freebsd/usr.sbin/extattr/tests/extattr_test.sh (revision 273c26a3c3bea87a241d6879abd4f991db180bf0)
1#
2# Copyright (c) 2016 Spectra Logic Corp
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8# 1. Redistributions of source code must retain the above copyright
9#    notice, this list of conditions and the following disclaimer.
10# 2. Redistributions in binary form must reproduce the above copyright
11#    notice, this list of conditions and the following disclaimer in the
12#    documentation and/or other materials provided with the distribution.
13#
14# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24# SUCH DAMAGE.
25#
26# $FreeBSD$
27
28atf_test_case bad_namespace
29bad_namespace_head() {
30	atf_set "descr" "Can't set attributes for nonexistent namespaces"
31}
32bad_namespace_body() {
33	check_fs
34	touch foo
35	atf_check -s not-exit:0 -e match:"Invalid argument" \
36		setextattr badnamespace myattr X foo
37	atf_check -s not-exit:0 -e match:"Invalid argument" \
38		lsextattr -q badnamespace foo
39}
40
41atf_test_case hex
42hex_head() {
43	atf_set "descr" "Set and get attribute values in hexadecimal"
44}
45hex_body() {
46	check_fs
47	touch foo
48	atf_check -s exit:0 -o empty setextattr user myattr XYZ foo
49	atf_check -s exit:0 -o inline:"58 59 5a\n" \
50		getextattr -qx user myattr foo
51}
52
53atf_test_case hex_nonascii
54hex_nonascii_head() {
55	atf_set "descr" "Get binary attribute values in hexadecimal"
56}
57hex_nonascii_body() {
58	check_fs
59	touch foo
60	BINSTUFF=`echo $'\x20\x30\x40\x55\x66\x70\x81\xa2\xb3\xee\xff'`
61	atf_check -s exit:0 -o empty setextattr user myattr "$BINSTUFF" foo
62	getextattr user myattr foo
63	atf_check -s exit:0 -o inline:"20 30 40 55 66 70 81 a2 b3 ee ff\n" \
64		getextattr -qx user myattr foo
65}
66
67atf_test_case long_name
68long_name_head() {
69	atf_set "descr" "A maximum length attribute name"
70}
71long_name_body() {
72	check_fs
73	# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208965
74	atf_expect_fail "BUG 208965 extattr(2) doesn't allow maxlen attr names"
75
76	ATTRNAME=`jot -b X -s "" 255 0`
77	touch foo
78	atf_check -s exit:0 -o empty setextattr user $ATTRNAME myvalue foo
79	atf_check -s exit:0 -o inline:"${ATTRNAME}\n" lsextattr -q user foo
80	atf_check -s exit:0 -o inline:"myvalue\n" \
81		getextattr -q user ${ATTRNAME} foo
82	atf_check -s exit:0 -o empty rmextattr user ${ATTRNAME} foo
83	atf_check -s exit:0 -o empty lsextattr -q user foo
84}
85
86atf_test_case loud
87loud_head() {
88	atf_set "descr" "Loud (non -q) output for each command"
89}
90loud_body() {
91	check_fs
92	touch foo
93	# setextattr(8) and friends print hard tabs.  Use printf to convert
94	# them to spaces before checking the output.
95	atf_check -s exit:0 -o empty setextattr user myattr myvalue foo
96	atf_check -s exit:0 -o inline:"foo myattr" \
97		printf "%s %s" $(lsextattr user foo)
98	atf_check -s exit:0 -o inline:"foo myvalue" \
99		printf "%s %s" $(getextattr user myattr foo)
100	atf_check -s exit:0 -o empty rmextattr user myattr foo
101	atf_check -s exit:0 -o inline:"foo" printf %s $(lsextattr user foo)
102}
103
104atf_test_case noattrs
105noattrs_head() {
106	atf_set "descr" "A file with no extended attributes"
107}
108noattrs_body() {
109	check_fs
110	touch foo
111	atf_check -s exit:0 -o empty lsextattr -q user foo
112}
113
114atf_test_case nonexistent_file
115nonexistent_file_head() {
116	atf_set "descr" "A file that does not exist"
117}
118nonexistent_file_body() {
119	check_fs
120	atf_check -s exit:1 -e match:"No such file or directory" \
121		lsextattr user foo
122	atf_check -s exit:1 -e match:"No such file or directory" \
123		setextattr user myattr myvalue foo
124	atf_check -s exit:1 -e match:"No such file or directory" \
125		getextattr user myattr foo
126	atf_check -s exit:1 -e match:"No such file or directory" \
127		rmextattr user myattr foo
128}
129
130atf_test_case null
131null_head() {
132	atf_set "descr" "NUL-terminate an attribute value"
133}
134null_body() {
135	check_fs
136	touch foo
137	atf_check -s exit:0 -o empty setextattr -n user myattr myvalue foo
138	atf_check -s exit:0 -o inline:"myvalue\0\n" getextattr -q user myattr foo
139}
140
141atf_test_case one_user_attr
142one_user_attr_head() {
143	atf_set "descr" "A file with one extended attribute"
144}
145one_user_attr_body() {
146	check_fs
147	touch foo
148	atf_check -s exit:0 -o empty setextattr user myattr myvalue foo
149	atf_check -s exit:0 -o inline:"myattr\n" lsextattr -q user foo
150	atf_check -s exit:0 -o inline:"myvalue\n" getextattr -q user myattr foo
151	atf_check -s exit:0 -o empty rmextattr user myattr foo
152	atf_check -s exit:0 -o empty lsextattr -q user foo
153}
154
155atf_test_case one_system_attr
156one_system_attr_head() {
157	atf_set "descr" "A file with one extended attribute"
158	atf_set "require.user" "root"
159}
160one_system_attr_body() {
161	check_fs
162	touch foo
163	atf_check -s exit:0 -o empty setextattr system myattr myvalue foo
164	atf_check -s exit:0 -o inline:"myattr\n" lsextattr -q system foo
165	atf_check -s exit:0 -o inline:"myvalue\n" getextattr -q system myattr foo
166	atf_check -s exit:0 -o empty rmextattr system myattr foo
167	atf_check -s exit:0 -o empty lsextattr -q system foo
168}
169
170atf_test_case stdin
171stdin_head() {
172	atf_set "descr" "Set attribute value from stdin"
173}
174stdin_body() {
175	check_fs
176	dd if=/dev/random of=infile bs=1k count=8
177	touch foo
178	setextattr -i user myattr foo < infile || atf_fail "setextattr failed"
179	atf_check -s exit:0 -o inline:"myattr\n" lsextattr -q user foo
180	getextattr -qq user myattr foo > outfile || atf_fail "getextattr failed"
181	atf_check -s exit:0 cmp -s infile outfile
182}
183
184atf_test_case stringify
185stringify_head() {
186	atf_set "descr" "Stringify the output of getextattr"
187}
188stringify_body() {
189	check_fs
190	touch foo
191	atf_check -s exit:0 -o empty setextattr user myattr "my value" foo
192	atf_check -s exit:0 -o inline:"\"my\\\040value\"\n" \
193		getextattr -qs user myattr foo
194}
195
196atf_test_case symlink
197symlink_head() {
198	atf_set "descr" "A symlink to an ordinary file"
199}
200symlink_body() {
201	check_fs
202	touch foo
203	ln -s foo foolink
204	atf_check -s exit:0 -o empty setextattr user myattr myvalue foolink
205	atf_check -s exit:0 -o inline:"myvalue\n" \
206		getextattr -q user myattr foolink
207	atf_check -s exit:0 -o inline:"myvalue\n" getextattr -q user myattr foo
208}
209
210atf_test_case symlink_nofollow
211symlink_nofollow_head() {
212	atf_set "descr" "Operating directly on a symlink"
213}
214symlink_nofollow_body() {
215	check_fs
216	touch foo
217	ln -s foo foolink
218	# Check that with -h we can operate directly on the link
219	atf_check -s exit:0 -o empty setextattr -h user myattr myvalue foolink
220	atf_check -s exit:0 -o inline:"myvalue\n" \
221		getextattr -qh user myattr foolink
222	atf_check -s exit:1 -e match:"Attribute not found" \
223		getextattr user myattr foolink
224	atf_check -s exit:1 -e match:"Attribute not found" \
225		getextattr user myattr foo
226
227	# Check that with -h we cannot operate on the destination file
228	atf_check -s exit:0 -o empty setextattr user otherattr othervalue foo
229	atf_check -s exit:1 getextattr -qh user otherattr foolink
230}
231
232atf_test_case system_and_user_attrs
233system_and_user_attrs_head() {
234	atf_set "descr" "A file with both system and user extended attributes"
235	atf_set "require.user" "root"
236}
237system_and_user_attrs_body() {
238	check_fs
239	touch foo
240	atf_check -s exit:0 -o empty setextattr user userattr userval foo
241	atf_check -s exit:0 -o empty setextattr system sysattr sysval foo
242	atf_check -s exit:0 -o inline:"userattr\n" lsextattr -q user foo
243	atf_check -s exit:0 -o inline:"sysattr\n" lsextattr -q system foo
244
245	atf_check -s exit:0 -o inline:"userval\n" getextattr -q user userattr foo
246	atf_check -s exit:0 -o inline:"sysval\n" getextattr -q system sysattr foo
247	atf_check -s exit:0 -o empty rmextattr user userattr foo
248	atf_check -s exit:0 -o empty rmextattr system sysattr foo
249	atf_check -s exit:0 -o empty lsextattr -q user foo
250	atf_check -s exit:0 -o empty lsextattr -q system foo
251}
252
253atf_test_case two_files
254two_files_head() {
255	atf_set "descr" "Manipulate two files"
256}
257two_files_body() {
258	check_fs
259	touch foo bar
260	atf_check -s exit:0 -o empty setextattr user myattr myvalue foo bar
261	atf_check -s exit:0 -o inline:"foo\tmyattr\nbar\tmyattr\n" \
262		lsextattr user foo bar
263	atf_check -s exit:0 \
264		-o inline:"foo\tmyvalue\nbar\tmyvalue\n" \
265		getextattr user myattr foo bar
266	atf_check -s exit:0 -o empty rmextattr user myattr foo bar
267	atf_check -s exit:0 -o empty lsextattr -q user foo bar
268}
269
270atf_test_case two_files_force
271two_files_force_head() {
272	atf_set "descr" "Manipulate two files.  The first does not exist"
273}
274two_files_force_body() {
275	check_fs
276	touch bar
277	atf_check -s exit:1 -e match:"No such file or directory" \
278		setextattr user myattr myvalue foo bar
279	atf_check -s exit:0 -e ignore setextattr -f user myattr myvalue foo bar
280	atf_check -s exit:1 -e match:"No such file or directory" \
281		lsextattr user foo bar
282	atf_check -s exit:0 -e ignore -o inline:"bar\tmyattr\n" \
283		lsextattr -f user foo bar
284	atf_check -s exit:1 -e match:"No such file or directory" \
285		getextattr user myattr foo bar
286	atf_check -s exit:0 -e ignore \
287		-o inline:"bar\tmyvalue\n" \
288		getextattr -f user myattr foo bar
289	atf_check -s exit:1 -e match:"No such file or directory" \
290		rmextattr user myattr foo bar
291	atf_check -s exit:0 -e ignore \
292		rmextattr -f user myattr foo bar
293	atf_check -s exit:0 -o empty lsextattr -q user bar
294}
295
296atf_test_case two_user_attrs
297two_user_attrs_head() {
298	atf_set "descr" "A file with two extended attributes"
299}
300two_user_attrs_body() {
301	check_fs
302	touch foo
303	atf_check -s exit:0 -o empty setextattr user myattr1 myvalue1 foo
304	atf_check -s exit:0 -o empty setextattr user myattr2 myvalue2 foo
305	# lsextattr could return the attributes in any order, so we must be
306	# careful how we compare them.
307	raw_output=`lsextattr -q user foo` || atf_fail "lsextattr failed"
308	tabless_output=`printf "%s %s" ${raw_output}`
309	if [ "myattr1 myattr2" != "${tabless_output}" -a \
310	     "myattr2 myattr1" != "${tabless_output}" ]; then
311		atf_fail "lsextattr printed ${tabless_output}"
312	fi
313	atf_check -s exit:0 -o inline:"myvalue1\n" getextattr -q user myattr1 foo
314	atf_check -s exit:0 -o inline:"myvalue2\n" getextattr -q user myattr2 foo
315	atf_check -s exit:0 -o empty rmextattr user myattr2 foo
316	atf_check -s exit:0 -o empty rmextattr user myattr1 foo
317	atf_check -s exit:0 -o empty lsextattr -q user foo
318}
319
320atf_test_case unprivileged_user_cannot_set_system_attr
321unprivileged_user_cannot_set_system_attr_head() {
322	atf_set "descr" "Unprivileged users can't set system attributes"
323        atf_set "require.user" "unprivileged"
324}
325unprivileged_user_cannot_set_system_attr_body() {
326	check_fs
327	touch foo
328	atf_check -s exit:1 -e match:"Operation not permitted" \
329		setextattr system myattr myvalue foo
330}
331
332
333atf_init_test_cases() {
334	atf_add_test_case bad_namespace
335	atf_add_test_case hex
336	atf_add_test_case hex_nonascii
337	atf_add_test_case long_name
338	atf_add_test_case loud
339	atf_add_test_case noattrs
340	atf_add_test_case nonexistent_file
341	atf_add_test_case null
342	atf_add_test_case symlink_nofollow
343	atf_add_test_case one_user_attr
344	atf_add_test_case one_system_attr
345	atf_add_test_case stdin
346	atf_add_test_case stringify
347	atf_add_test_case symlink
348	atf_add_test_case symlink_nofollow
349	atf_add_test_case system_and_user_attrs
350	atf_add_test_case two_files
351	atf_add_test_case two_files_force
352	atf_add_test_case two_user_attrs
353	atf_add_test_case unprivileged_user_cannot_set_system_attr
354}
355
356check_fs() {
357	case `df -T . | tail -n 1 | cut -wf 2` in
358		"ufs")
359		case `dumpfs . | head -1 | awk -F'[()]' '{print $2}'` in
360			"UFS1") atf_skip "UFS1 is not supported by this test";;
361			"UFS2") ;; # UFS2 is fine
362		esac ;;
363		"zfs") ;; # ZFS is fine
364		"tmpfs") atf_skip "tmpfs does not support extended attributes";;
365	esac
366}
367