xref: /freebsd/tests/sys/fs/tarfs/tarfs_test.sh (revision aa1a8ff2d6dbc51ef058f46f3db5a8bb77967145)
1#!/bin/sh
2#-
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2023-2024 Klara, Inc.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28
29mnt="$(realpath ${TMPDIR:-/tmp})/mnt"
30
31# expected SHA256 checksum of file contained in test tarball
32sum=4da2143234486307bb44eaa610375301781a577d1172f362b88bb4b1643dee62
33
34tar() {
35	if [ -n "${TARFS_USE_GNU_TAR}" ] ; then
36		gtar --posix --absolute-names "$@"
37	else
38		bsdtar "$@"
39	fi
40}
41
42mktar() {
43	"$(atf_get_srcdir)"/mktar ${TARFS_USE_GNU_TAR+-g} "$@"
44}
45
46tarsum() {
47	"$(atf_get_srcdir)"/tarsum
48}
49
50tarfs_setup() {
51	kldload -n tarfs || atf_skip "This test requires tarfs and could not load it"
52	mkdir "${mnt}"
53}
54
55tarfs_cleanup() {
56	umount -f "${mnt}" 2>/dev/null || true
57}
58
59atf_test_case tarfs_basic cleanup
60tarfs_basic_head() {
61	atf_set "descr" "Basic function test"
62	atf_set "require.user" "root"
63}
64tarfs_basic_body() {
65	tarfs_setup
66	local tarball="${PWD}/tarfs_test.tar.zst"
67	mktar "${tarball}"
68	atf_check mount -rt tarfs "${tarball}" "${mnt}"
69	atf_check -o match:"^${tarball} on ${mnt} \(tarfs," mount
70	atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -f%d,%i "${mnt}"/hard_link)"
71	atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/short_link)"
72	atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/long_link)"
73	atf_check -o inline:"${sum}\n" sha256 -q "${mnt}"/sparse_file
74	atf_check -o inline:"2,40755\n" stat -f%l,%p "${mnt}"/directory
75	atf_check -o inline:"1,100644\n" stat -f%l,%p "${mnt}"/file
76	atf_check -o inline:"2,100644\n" stat -f%l,%p "${mnt}"/hard_link
77	atf_check -o inline:"1,120755\n" stat -f%l,%p "${mnt}"/long_link
78	atf_check -o inline:"1,120755\n" stat -f%l,%p "${mnt}"/short_link
79	atf_check -o inline:"2,100644\n" stat -f%l,%p "${mnt}"/sparse_file
80	atf_check -o inline:"3,40755\n" stat -f%l,%p "${mnt}"
81}
82tarfs_basic_cleanup() {
83	tarfs_cleanup
84}
85
86atf_test_case tarfs_basic_gnu cleanup
87tarfs_basic_gnu_head() {
88	atf_set "descr" "Basic function test using GNU tar"
89	atf_set "require.user" "root"
90	atf_set "require.progs" "gtar"
91}
92tarfs_basic_gnu_body() {
93	TARFS_USE_GNU_TAR=true
94	tarfs_basic_body
95}
96tarfs_basic_gnu_cleanup() {
97	tarfs_basic_cleanup
98}
99
100atf_test_case tarfs_notdir_device cleanup
101tarfs_notdir_device_head() {
102	atf_set "descr" "Regression test for PR 269519 and 269561"
103	atf_set "require.user" "root"
104}
105tarfs_notdir_device_body() {
106	tarfs_setup
107	atf_check mknod d b 0xdead 0xbeef
108	tar -cf tarfs_notdir.tar d
109	rm d
110	mkdir d
111	echo "boom" >d/f
112	tar -rf tarfs_notdir.tar d/f
113	atf_check -s not-exit:0 -e match:"Invalid" \
114	    mount -rt tarfs tarfs_notdir.tar "${mnt}"
115}
116tarfs_notdir_device_cleanup() {
117	tarfs_cleanup
118}
119
120atf_test_case tarfs_notdir_device_gnu cleanup
121tarfs_notdir_device_gnu_head() {
122	atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
123	atf_set "require.user" "root"
124	atf_set "require.progs" "gtar"
125}
126tarfs_notdir_device_gnu_body() {
127	TARFS_USE_GNU_TAR=true
128	tarfs_notdir_device_body
129}
130tarfs_notdir_device_gnu_cleanup() {
131	tarfs_notdir_device_cleanup
132}
133
134atf_test_case tarfs_notdir_dot cleanup
135tarfs_notdir_dot_head() {
136	atf_set "descr" "Regression test for PR 269519 and 269561"
137	atf_set "require.user" "root"
138}
139tarfs_notdir_dot_body() {
140	tarfs_setup
141	echo "hello" >d
142	tar -cf tarfs_notdir.tar d
143	rm d
144	mkdir d
145	echo "world" >d/f
146	tar -rf tarfs_notdir.tar d/./f
147	atf_check -s not-exit:0 -e match:"Invalid" \
148	    mount -rt tarfs tarfs_notdir.tar "${mnt}"
149}
150tarfs_notdir_dot_cleanup() {
151	tarfs_cleanup
152}
153
154atf_test_case tarfs_notdir_dot_gnu cleanup
155tarfs_notdir_dot_gnu_head() {
156	atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
157	atf_set "require.user" "root"
158	atf_set "require.progs" "gtar"
159}
160tarfs_notdir_dot_gnu_body() {
161	TARFS_USE_GNU_TAR=true
162	tarfs_notdir_dot_body
163}
164tarfs_notdir_dot_gnu_cleanup() {
165	tarfs_notdir_dot_cleanup
166}
167
168atf_test_case tarfs_notdir_dotdot cleanup
169tarfs_notdir_dotdot_head() {
170	atf_set "descr" "Regression test for PR 269519 and 269561"
171	atf_set "require.user" "root"
172}
173tarfs_notdir_dotdot_body() {
174	tarfs_setup
175	echo "hello" >d
176	tar -cf tarfs_notdir.tar d
177	rm d
178	mkdir d
179	echo "world" >f
180	tar -rf tarfs_notdir.tar d/../f
181	atf_check -s not-exit:0 -e match:"Invalid" \
182	    mount -rt tarfs tarfs_notdir.tar "${mnt}"
183}
184tarfs_notdir_dotdot_cleanup() {
185	tarfs_cleanup
186}
187
188atf_test_case tarfs_notdir_dotdot_gnu cleanup
189tarfs_notdir_dotdot_gnu_head() {
190	atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
191	atf_set "require.user" "root"
192	atf_set "require.progs" "gtar"
193}
194tarfs_notdir_dotdot_gnu_body() {
195	TARFS_USE_GNU_TAR=true
196	tarfs_notdir_dotdot_body
197}
198tarfs_notdir_dotdot_gnu_cleanup() {
199	tarfs_notdir_dotdot_cleanup
200}
201
202atf_test_case tarfs_notdir_file cleanup
203tarfs_notdir_file_head() {
204	atf_set "descr" "Regression test for PR 269519 and 269561"
205	atf_set "require.user" "root"
206}
207tarfs_notdir_file_body() {
208	tarfs_setup
209	echo "hello" >d
210	tar -cf tarfs_notdir.tar d
211	rm d
212	mkdir d
213	echo "world" >d/f
214	tar -rf tarfs_notdir.tar d/f
215	atf_check -s not-exit:0 -e match:"Invalid" \
216	    mount -rt tarfs tarfs_notdir.tar "${mnt}"
217}
218tarfs_notdir_file_cleanup() {
219	tarfs_cleanup
220}
221
222atf_test_case tarfs_notdir_file_gnu cleanup
223tarfs_notdir_file_gnu_head() {
224	atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
225	atf_set "require.user" "root"
226	atf_set "require.progs" "gtar"
227}
228tarfs_notdir_file_gnu_body() {
229	TARFS_USE_GNU_TAR=true
230	tarfs_notdir_file_body
231}
232tarfs_notdir_file_gnu_cleanup() {
233	tarfs_notdir_file_cleanup
234}
235
236atf_test_case tarfs_emptylink cleanup
237tarfs_emptylink_head() {
238	atf_set "descr" "Regression test for PR 277360: empty link target"
239	atf_set "require.user" "root"
240}
241tarfs_emptylink_body() {
242	tarfs_setup
243	touch z
244	ln -f z hard
245	ln -fs z soft
246	tar -cf - z hard soft | dd bs=512 skip=1 | tr z '\0' | \
247		tarsum >> tarfs_emptylink.tar
248	atf_check -s not-exit:0 -e match:"Invalid" \
249		  mount -rt tarfs tarfs_emptylink.tar "${mnt}"
250}
251tarfs_emptylink_cleanup() {
252	tarfs_cleanup
253}
254
255atf_test_case tarfs_linktodir cleanup
256tarfs_linktodir_head() {
257	atf_set "descr" "Regression test for PR 277360: link to directory"
258	atf_set "require.user" "root"
259}
260tarfs_linktodir_body() {
261	tarfs_setup
262	mkdir d
263	tar -cf - d | dd bs=512 count=1 > tarfs_linktodir.tar
264	rmdir d
265	touch d
266	ln -f d link
267	tar -cf - d link | dd bs=512 skip=1 >> tarfs_linktodir.tar
268	atf_check -s not-exit:0 -e match:"Invalid" \
269		  mount -rt tarfs tarfs_linktodir.tar "${mnt}"
270}
271tarfs_linktodir_cleanup() {
272	tarfs_cleanup
273}
274
275atf_test_case tarfs_linktononexistent cleanup
276tarfs_linktononexistent_head() {
277	atf_set "descr" "Regression test for PR 277360: link to nonexistent target"
278	atf_set "require.user" "root"
279}
280tarfs_linktononexistent_body() {
281	tarfs_setup
282	touch f
283	ln -f f link
284	tar -cf - f link | dd bs=512 skip=1 >> tarfs_linktononexistent.tar
285	atf_check -s not-exit:0 -e match:"Invalid" \
286		  mount -rt tarfs tarfs_linktononexistent.tar "${mnt}"
287}
288tarfs_linktononexistent_cleanup() {
289	tarfs_cleanup
290}
291
292atf_test_case tarfs_checksum cleanup
293tarfs_checksum_head() {
294	atf_set "descr" "Verify that the checksum covers header padding"
295	atf_set "require.user" "root"
296}
297tarfs_checksum_body() {
298	tarfs_setup
299	touch f
300	tar -cf tarfs_checksum.tar f
301	truncate -s 500 tarfs_checksum.tar
302	printf "\1\1\1\1\1\1\1\1\1\1\1\1" >> tarfs_checksum.tar
303	dd if=/dev/zero bs=512 count=2 >> tarfs_checksum.tar
304	hexdump -C tarfs_checksum.tar
305	atf_check -s not-exit:0 -e match:"Invalid" \
306		  mount -rt tarfs tarfs_checksum.tar "${mnt}"
307}
308tarfs_checksum_cleanup() {
309	tarfs_cleanup
310}
311
312atf_test_case tarfs_long_names cleanup
313tarfs_long_names_head() {
314	atf_set "descr" "Verify that tarfs supports long file names"
315	atf_set "require.user" "root"
316}
317tarfs_long_names_body() {
318	tarfs_setup
319	local a b c d e
320	a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
321	b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
322	c="cccccccccccccccccccccccccccccccccccccccc"
323	d="dddddddddddddddddddddddddddddddddddddddd"
324	e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
325	mkdir -p "${a}"
326	touch "${a}/${b}_${c}_${d}_${e}_foo"
327	ln "${a}/${b}_${c}_${d}_${e}_foo" "${a}/${b}_${c}_${d}_${e}_bar"
328	ln -s "${b}_${c}_${d}_${e}_bar" "${a}/${b}_${c}_${d}_${e}_baz"
329	tar -cf tarfs_long_names.tar "${a}"
330	atf_check mount -rt tarfs tarfs_long_names.tar "${mnt}"
331}
332tarfs_long_names_cleanup() {
333	tarfs_cleanup
334}
335
336atf_test_case tarfs_long_paths cleanup
337tarfs_long_paths_head() {
338	atf_set "descr" "Verify that tarfs supports long paths"
339	atf_set "require.user" "root"
340}
341tarfs_long_paths_body() {
342	tarfs_setup
343	local a b c d e
344	a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
345	b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
346	c="cccccccccccccccccccccccccccccccccccccccc"
347	d="dddddddddddddddddddddddddddddddddddddddd"
348	e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
349	mkdir -p "${a}/${b}/${c}/${d}/${e}"
350	touch "${a}/${b}/${c}/${d}/${e}/foo"
351	ln "${a}/${b}/${c}/${d}/${e}/foo" "${a}/${b}/${c}/${d}/${e}/bar"
352	ln -s "${b}/${c}/${d}/${e}/bar" "${a}/baz"
353	tar -cf tarfs_long_paths.tar "${a}"
354	atf_check mount -rt tarfs tarfs_long_paths.tar "${mnt}"
355}
356tarfs_long_paths_cleanup() {
357	tarfs_cleanup
358}
359
360atf_test_case tarfs_git_archive cleanup
361tarfs_git_archive_head() {
362	atf_set "descr" "Verify that tarfs supports archives created by git"
363	atf_set "require.user" "root"
364	atf_set "require.progs" "git"
365}
366tarfs_git_archive_body() {
367	tarfs_setup
368	mkdir foo
369	echo "Hello, world!" >foo/bar
370	git -C foo init --initial-branch=tarfs
371	git -C foo config user.name "File System"
372	git -C foo config user.email fs@freebsd.org
373	git -C foo add bar
374	git -C foo commit -m bar
375	git -C foo archive --output=../tarfs_git_archive.tar HEAD
376	atf_check mount -rt tarfs tarfs_git_archive.tar "${mnt}"
377	atf_check -o file:foo/bar cat "${mnt}"/bar
378}
379tarfs_git_archive_cleanup() {
380	tarfs_cleanup
381}
382
383atf_init_test_cases() {
384	atf_add_test_case tarfs_basic
385	atf_add_test_case tarfs_basic_gnu
386	atf_add_test_case tarfs_notdir_device
387	atf_add_test_case tarfs_notdir_device_gnu
388	atf_add_test_case tarfs_notdir_dot
389	atf_add_test_case tarfs_notdir_dot_gnu
390	atf_add_test_case tarfs_notdir_dotdot
391	atf_add_test_case tarfs_notdir_dotdot_gnu
392	atf_add_test_case tarfs_notdir_file
393	atf_add_test_case tarfs_notdir_file_gnu
394	atf_add_test_case tarfs_emptylink
395	atf_add_test_case tarfs_linktodir
396	atf_add_test_case tarfs_linktononexistent
397	atf_add_test_case tarfs_checksum
398	atf_add_test_case tarfs_long_names
399	atf_add_test_case tarfs_long_paths
400	atf_add_test_case tarfs_git_archive
401}
402