xref: /freebsd/usr.sbin/makefs/tests/makefs_zfs_tests.sh (revision c4fde87a3236b500246d4fce76dbca2a3d64f73a)
1#-
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2022-2023 The FreeBSD Foundation
5#
6# This software was developed by Mark Johnston under sponsorship from
7# the FreeBSD Foundation.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions are
11# met:
12# 1. Redistributions of source code must retain the above copyright
13#    notice, this list of conditions and the following disclaimer.
14# 2. Redistributions in binary form must reproduce the above copyright
15#    notice, this list of conditions and the following disclaimer in
16#    the documentation and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28# SUCH DAMAGE.
29#
30
31MAKEFS="makefs -t zfs -o verify-txgs=true -o poolguid=$$"
32ZFS_POOL_NAME="makefstest$$"
33TEST_ZFS_POOL_NAME="$TMPDIR/poolname"
34
35. "$(dirname "$0")/makefs_tests_common.sh"
36
37common_cleanup()
38{
39	local pool md
40
41	# Try to force a TXG, this can help catch bugs by triggering a panic.
42	sync
43
44	if [ -f "$TEST_ZFS_POOL_NAME" ]; then
45		pool=$(cat $TEST_ZFS_POOL_NAME)
46		if zpool list "$pool" >/dev/null; then
47			zpool destroy "$pool"
48		fi
49	fi
50
51	if [ -f "$TEST_MD_DEVICE_FILE" ]; then
52		md=$(cat $TEST_MD_DEVICE_FILE)
53		if [ -c /dev/"$md" ]; then
54			mdconfig -o force -d -u "$md"
55		fi
56	fi
57}
58
59import_image()
60{
61	atf_check -o save:$TEST_MD_DEVICE_FILE mdconfig -a -f $TEST_IMAGE
62	atf_check -o ignore \
63	    zdb -e -p /dev/$(cat $TEST_MD_DEVICE_FILE) -mmm -ddddd $ZFS_POOL_NAME
64	atf_check zpool import -R $TEST_MOUNT_DIR $ZFS_POOL_NAME
65	echo "$ZFS_POOL_NAME" > $TEST_ZFS_POOL_NAME
66}
67
68#
69# Test autoexpansion of the vdev.
70#
71# The pool is initially 10GB, so we get 10GB minus one metaslab's worth of
72# usable space for data.  Then the pool is expanded to 50GB, and the amount of
73# usable space is 50GB minus one metaslab.
74#
75atf_test_case autoexpand cleanup
76autoexpand_body()
77{
78	local mssize poolsize poolsize1 newpoolsize
79
80	create_test_inputs
81
82	mssize=$((128 * 1024 * 1024))
83	poolsize=$((10 * 1024 * 1024 * 1024))
84	atf_check $MAKEFS -s $poolsize -o mssize=$mssize -o rootpath=/ \
85	    -o poolname=$ZFS_POOL_NAME \
86	    $TEST_IMAGE $TEST_INPUTS_DIR
87
88	newpoolsize=$((50 * 1024 * 1024 * 1024))
89	truncate -s $newpoolsize $TEST_IMAGE
90
91	import_image
92
93	check_image_contents
94
95	poolsize1=$(zpool list -Hp -o size $ZFS_POOL_NAME)
96	atf_check [ $((poolsize1 + $mssize)) -eq $poolsize ]
97
98	atf_check zpool online -e $ZFS_POOL_NAME /dev/$(cat $TEST_MD_DEVICE_FILE)
99
100	check_image_contents
101
102	poolsize1=$(zpool list -Hp -o size $ZFS_POOL_NAME)
103	atf_check [ $((poolsize1 + $mssize)) -eq $newpoolsize ]
104}
105autoexpand_cleanup()
106{
107	common_cleanup
108}
109
110#
111# Test setting the autotrim pool property.
112#
113atf_test_case autotrim cleanup
114autotrim_body()
115{
116	local src val
117
118	create_test_inputs
119
120	atf_check -s exit:1 -e match:"invalid value .foo. for autotrim" \
121	    $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
122	    -o autotrim=foo $TEST_IMAGE $TEST_INPUTS_DIR
123
124	for val in on off; do
125		atf_check $MAKEFS -s 1g -o rootpath=/ \
126		    -o poolname=$ZFS_POOL_NAME \
127		    -o autotrim=$val \
128		    $TEST_IMAGE $TEST_INPUTS_DIR
129
130		import_image
131
132		check_image_contents
133
134		# OpenZFS reports a pool property whose value equals the
135		# default as coming from the default, even if it was set.
136		if [ $val = off ]; then
137			src=default
138		else
139			src=local
140		fi
141		atf_check -o inline:$val\\n \
142		    zpool get -H -o value autotrim $ZFS_POOL_NAME
143		atf_check -o inline:$src\\n \
144		    zpool get -H -o source autotrim $ZFS_POOL_NAME
145
146		atf_check zpool destroy ${ZFS_POOL_NAME}
147		atf_check rm -f ${TEST_ZFS_POOL_NAME}
148		atf_check mdconfig -d -u $(cat ${TEST_MD_DEVICE_FILE})
149		atf_check rm -f ${TEST_MD_DEVICE_FILE}
150	done
151
152	# Without the option the property keeps its default value.
153	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
154	    $TEST_IMAGE $TEST_INPUTS_DIR
155
156	import_image
157
158	atf_check -o inline:off\\n \
159	    zpool get -H -o value autotrim $ZFS_POOL_NAME
160	atf_check -o inline:default\\n \
161	    zpool get -H -o source autotrim $ZFS_POOL_NAME
162}
163autotrim_cleanup()
164{
165	common_cleanup
166}
167
168#
169# Test with some default layout defined by the common code.
170#
171atf_test_case basic cleanup
172basic_body()
173{
174	create_test_inputs
175
176	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
177	    $TEST_IMAGE $TEST_INPUTS_DIR
178
179	import_image
180
181	check_image_contents
182}
183basic_cleanup()
184{
185	common_cleanup
186}
187
188#
189# Try configuring various compression algorithms.
190#
191atf_test_case compression cleanup
192compression_head()
193{
194	# Double the default timeout to make it pass on emulated architectures
195	# on ci.freebsd.org
196	atf_set "timeout" 600
197}
198compression_body()
199{
200	create_test_inputs
201
202	cd $TEST_INPUTS_DIR
203	mkdir dir
204	mkdir dir2
205	cd -
206
207	for alg in off on lzjb gzip gzip-1 gzip-2 gzip-3 gzip-4 \
208	    gzip-5 gzip-6 gzip-7 gzip-8 gzip-9 zle lz4 zstd; do
209		atf_check $MAKEFS -s 1g -o rootpath=/ \
210		    -o poolname=$ZFS_POOL_NAME \
211		    -o fs=${ZFS_POOL_NAME}\;compression=$alg \
212		    -o fs=${ZFS_POOL_NAME}/dir \
213		    -o fs=${ZFS_POOL_NAME}/dir2\;compression=off \
214		    $TEST_IMAGE $TEST_INPUTS_DIR
215
216		import_image
217
218		check_image_contents
219
220		if [ $alg = gzip-6 ]; then
221			# ZFS reports gzip-6 as just gzip since it uses
222			# a default compression level of 6.
223			alg=gzip
224		fi
225		# The "dir" dataset's compression algorithm should be
226		# inherited from the root dataset.
227		atf_check -o inline:$alg\\n \
228		    zfs get -H -o value compression ${ZFS_POOL_NAME}
229		atf_check -o inline:$alg\\n \
230		    zfs get -H -o value compression ${ZFS_POOL_NAME}/dir
231		atf_check -o inline:off\\n \
232		    zfs get -H -o value compression ${ZFS_POOL_NAME}/dir2
233
234		atf_check -e ignore dd if=/dev/random \
235		    of=${TEST_MOUNT_DIR}/dir/random bs=1M count=10
236		atf_check -e ignore dd if=/dev/zero \
237		    of=${TEST_MOUNT_DIR}/dir/zero bs=1M count=10
238		atf_check -e ignore dd if=/dev/zero \
239		    of=${TEST_MOUNT_DIR}/dir2/zero bs=1M count=10
240
241		# Export and reimport to ensure that everything is
242		# flushed to disk.
243		atf_check zpool export ${ZFS_POOL_NAME}
244		atf_check -o ignore \
245		    zdb -e -p /dev/$(cat $TEST_MD_DEVICE_FILE) -mmm -ddddd \
246		    $ZFS_POOL_NAME
247		atf_check zpool import -R $TEST_MOUNT_DIR $ZFS_POOL_NAME
248
249		if [ $alg = off ]; then
250			# If compression is off, the files should be the
251			# same size as the input.
252			atf_check -o match:"^11[[:space:]]+${TEST_MOUNT_DIR}/dir/random" \
253			    du -m ${TEST_MOUNT_DIR}/dir/random
254			atf_check -o match:"^11[[:space:]]+${TEST_MOUNT_DIR}/dir/zero" \
255			    du -m ${TEST_MOUNT_DIR}/dir/zero
256			atf_check -o match:"^11[[:space:]]+${TEST_MOUNT_DIR}/dir2/zero" \
257			    du -m ${TEST_MOUNT_DIR}/dir2/zero
258		else
259			# If compression is on, the dir/zero file ought
260			# to be smaller.
261			atf_check -o match:"^1[[:space:]]+${TEST_MOUNT_DIR}/dir/zero" \
262			    du -m ${TEST_MOUNT_DIR}/dir/zero
263			atf_check -o match:"^11[[:space:]]+${TEST_MOUNT_DIR}/dir/random" \
264			    du -m ${TEST_MOUNT_DIR}/dir/random
265			atf_check -o match:"^11[[:space:]]+${TEST_MOUNT_DIR}/dir2/zero" \
266			    du -m ${TEST_MOUNT_DIR}/dir2/zero
267		fi
268
269		atf_check zpool destroy ${ZFS_POOL_NAME}
270		atf_check rm -f ${TEST_ZFS_POOL_NAME}
271		atf_check mdconfig -o force -d -u $(cat ${TEST_MD_DEVICE_FILE})
272		atf_check rm -f ${TEST_MD_DEVICE_FILE}
273	done
274}
275compression_cleanup()
276{
277	common_cleanup
278}
279
280#
281# Make sure that the link count and the size of the directories which are the
282# roots of datasets count "." and the directories which are the mountpoints
283# of other datasets.  fts(3) trusts the link count of the directories of ZFS
284# and skips the subdirectories that it does not account for.
285#
286atf_test_case dataset_link_counts cleanup
287dataset_link_counts_body()
288{
289	local dir entries subdirs
290
291	create_test_dirs
292	cd $TEST_INPUTS_DIR
293
294	mkdir -p dir1/sub1 dir1/sub2 dir1/dir2/sub3 dir1/dir2/dir3 dir4
295	echo a > dir1/a
296	echo b > dir1/dir2/b
297	echo c > dir1/sub1/c
298	echo d > dir1/sub2/d
299	echo e > dir1/dir2/sub3/e
300
301	cd -
302
303	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
304	    -o fs=${ZFS_POOL_NAME}/dir1 -o fs=${ZFS_POOL_NAME}/dir1/dir2 \
305	    -o fs=${ZFS_POOL_NAME}/dir1/dir2/dir3 -o fs=${ZFS_POOL_NAME}/dir4 \
306	    -o fs=${ZFS_POOL_NAME}/dir5 \
307	    $TEST_IMAGE $TEST_INPUTS_DIR
308
309	import_image
310
311	# find(1) relies on the link counts to decide which entries may be
312	# directories, so it must see the whole tree.
313	(cd $TEST_INPUTS_DIR && find dir1 dir4 | sort) > ./expected
314	atf_check -o file:./expected -x \
315	    "cd $TEST_MOUNT_DIR && find dir1 dir4 | sort"
316
317	# The link count is the number of directories in the directory plus
318	# "." and "..", and the size is the number of entries plus the same.
319	for dir in . dir1 dir1/sub1 dir1/dir2 dir1/dir2/dir3 dir4 dir5; do
320		subdirs=$(ls -d $TEST_MOUNT_DIR/$dir/*/ 2>/dev/null | wc -l)
321		entries=$(ls -A $TEST_MOUNT_DIR/$dir | wc -l)
322		atf_check -o inline:$((subdirs + 2))\\n \
323		    stat -f %l $TEST_MOUNT_DIR/$dir
324		atf_check -o inline:$((entries + 2))\\n \
325		    stat -f %z $TEST_MOUNT_DIR/$dir
326	done
327}
328dataset_link_counts_cleanup()
329{
330	common_cleanup
331}
332
333#
334# Try destroying a dataset that was created by makefs.
335#
336atf_test_case dataset_removal cleanup
337dataset_removal_body()
338{
339	create_test_dirs
340
341	cd $TEST_INPUTS_DIR
342	mkdir dir
343	cd -
344
345	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
346	    -o fs=${ZFS_POOL_NAME}/dir \
347	    $TEST_IMAGE $TEST_INPUTS_DIR
348
349	import_image
350
351	check_image_contents
352
353	atf_check zfs destroy ${ZFS_POOL_NAME}/dir
354}
355dataset_removal_cleanup()
356{
357	common_cleanup
358}
359
360#
361# Make sure that we can handle some special file types.  Anything other than
362# regular files, symlinks and directories are ignored.
363#
364atf_test_case devfs cleanup
365devfs_body()
366{
367	atf_check mkdir dev
368	atf_check mount -t devfs none ./dev
369
370	atf_check -e match:"skipping unhandled" $MAKEFS -s 1g -o rootpath=/ \
371	    -o poolname=$ZFS_POOL_NAME $TEST_IMAGE ./dev
372
373	import_image
374}
375devfs_cleanup()
376{
377	common_cleanup
378	umount -f ./dev
379}
380
381#
382# Make sure that we can create and remove an empty directory.
383#
384atf_test_case empty_dir cleanup
385empty_dir_body()
386{
387	create_test_dirs
388
389	cd $TEST_INPUTS_DIR
390	mkdir dir
391	cd -
392
393	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
394	    $TEST_IMAGE $TEST_INPUTS_DIR
395
396	import_image
397
398	check_image_contents
399
400	atf_check rmdir ${TEST_MOUNT_DIR}/dir
401}
402empty_dir_cleanup()
403{
404	common_cleanup
405}
406
407atf_test_case empty_fs cleanup
408empty_fs_body()
409{
410	create_test_dirs
411
412	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
413	    $TEST_IMAGE $TEST_INPUTS_DIR
414
415	import_image
416
417	check_image_contents
418}
419empty_fs_cleanup()
420{
421	common_cleanup
422}
423
424atf_test_case file_extend cleanup
425file_extend_head()
426{
427	# Double the default timeout to make it pass on emulated architectures
428	# on ci.freebsd.org
429	atf_set "timeout" 600
430}
431file_extend_body()
432{
433	local i start
434
435	create_test_dirs
436
437	# Create a file slightly longer than the maximum block size.
438	start=132
439	dd if=/dev/random of=${TEST_INPUTS_DIR}/foo bs=1k count=$start
440	md5 -q ${TEST_INPUTS_DIR}/foo > foo.md5
441
442	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
443	    $TEST_IMAGE $TEST_INPUTS_DIR
444
445	import_image
446
447	check_image_contents
448
449	i=0
450	while [ $i -lt 1000 ]; do
451		dd if=/dev/random of=${TEST_MOUNT_DIR}/foo bs=1k count=1 \
452		    seek=$(($i + $start)) conv=notrunc
453		# Make sure that the first $start blocks are unmodified.
454		dd if=${TEST_MOUNT_DIR}/foo bs=1k count=$start of=foo.copy
455		atf_check -o file:foo.md5 md5 -q foo.copy
456		i=$(($i + 1))
457	done
458}
459file_extend_cleanup()
460{
461	common_cleanup
462}
463
464atf_test_case file_sizes cleanup
465file_sizes_body()
466{
467	local i
468
469	create_test_dirs
470	cd $TEST_INPUTS_DIR
471
472	i=1
473	while [ $i -lt $((1 << 20)) ]; do
474		truncate -s $i ${i}.1
475		truncate -s $(($i - 1)) ${i}.2
476		truncate -s $(($i + 1)) ${i}.3
477		i=$(($i << 1))
478	done
479
480	cd -
481
482	# XXXMJ this creates sparse files, make sure makefs doesn't
483	#       preserve the sparseness.
484	# XXXMJ need to test with larger files (at least 128MB for L2 indirs)
485	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
486	    $TEST_IMAGE $TEST_INPUTS_DIR
487
488	import_image
489
490	check_image_contents
491}
492file_sizes_cleanup()
493{
494	common_cleanup
495}
496
497atf_test_case hard_links cleanup
498hard_links_body()
499{
500	local f
501
502	create_test_dirs
503	cd $TEST_INPUTS_DIR
504
505	mkdir dir
506	echo "hello" > 1
507	ln 1 2
508	ln 1 dir/1
509
510	echo "goodbye" > dir/a
511	ln dir/a dir/b
512	ln dir/a a
513
514	cd -
515
516	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
517	    $TEST_IMAGE $TEST_INPUTS_DIR
518
519	import_image
520
521	check_image_contents
522
523	stat -f '%i' ${TEST_MOUNT_DIR}/1 > ./ino
524	stat -f '%l' ${TEST_MOUNT_DIR}/1 > ./nlink
525	for f in 1 2 dir/1; do
526		atf_check -o file:./nlink stat -f '%l' ${TEST_MOUNT_DIR}/${f}
527		atf_check -o file:./ino stat -f '%i' ${TEST_MOUNT_DIR}/${f}
528		atf_check cmp -s ${TEST_INPUTS_DIR}/1 ${TEST_MOUNT_DIR}/${f}
529	done
530
531	stat -f '%i' ${TEST_MOUNT_DIR}/dir/a > ./ino
532	stat -f '%l' ${TEST_MOUNT_DIR}/dir/a > ./nlink
533	for f in dir/a dir/b a; do
534		atf_check -o file:./nlink stat -f '%l' ${TEST_MOUNT_DIR}/${f}
535		atf_check -o file:./ino stat -f '%i' ${TEST_MOUNT_DIR}/${f}
536		atf_check cmp -s ${TEST_INPUTS_DIR}/dir/a ${TEST_MOUNT_DIR}/${f}
537	done
538}
539hard_links_cleanup()
540{
541	common_cleanup
542}
543
544# Allocate enough dnodes from an object set that the meta dnode needs to use
545# indirect blocks.
546atf_test_case indirect_dnode_array cleanup
547indirect_dnode_array_body()
548{
549	local count i
550
551	# How many dnodes do we need to allocate?  Well, the data block size
552	# for meta dnodes is always 16KB, so with a dnode size of 512B we get
553	# 32 dnodes per direct block.  The maximum indirect block size is 128KB
554	# and that can fit 1024 block pointers, so we need at least 32 * 1024
555	# files to force the use of two levels of indirection.
556	#
557	# Unfortunately that number of files makes the test run quite slowly,
558	# so we settle for a single indirect block for now...
559	count=$(jot -r 1 32 1024)
560
561	create_test_dirs
562	cd $TEST_INPUTS_DIR
563	for i in $(seq 1 $count); do
564		touch $i
565	done
566	cd -
567
568	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
569	    $TEST_IMAGE $TEST_INPUTS_DIR
570
571	import_image
572
573	check_image_contents
574}
575indirect_dnode_array_cleanup()
576{
577	common_cleanup
578}
579
580#
581# Create some files with long names, so as to test fat ZAP handling.
582#
583atf_test_case long_file_name cleanup
584long_file_name_body()
585{
586	local dir i
587
588	create_test_dirs
589	cd $TEST_INPUTS_DIR
590
591	# micro ZAP keys can be at most 50 bytes.
592	for i in $(seq 1 60); do
593		touch $(jot -s '' $i 1 1)
594	done
595	dir=$(jot -s '' 61 1 1)
596	mkdir $dir
597	for i in $(seq 1 60); do
598		touch ${dir}/$(jot -s '' $i 1 1)
599	done
600
601	cd -
602
603	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
604	    $TEST_IMAGE $TEST_INPUTS_DIR
605
606	import_image
607
608	check_image_contents
609
610	# Add a directory entry in the hope that OpenZFS might catch a bug
611	# in makefs' fat ZAP encoding.
612	touch ${TEST_MOUNT_DIR}/foo
613}
614long_file_name_cleanup()
615{
616	common_cleanup
617}
618
619#
620# Exercise handling of multiple datasets.
621#
622atf_test_case multi_dataset_1 cleanup
623multi_dataset_1_body()
624{
625	create_test_dirs
626	cd $TEST_INPUTS_DIR
627
628	mkdir dir1
629	echo a > dir1/a
630	mkdir dir2
631	echo b > dir2/b
632
633	cd -
634
635	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
636	    -o fs=${ZFS_POOL_NAME}/dir1 -o fs=${ZFS_POOL_NAME}/dir2 \
637	    $TEST_IMAGE $TEST_INPUTS_DIR
638
639	import_image
640
641	check_image_contents
642
643	# Make sure that we have three datasets with the expected mount points.
644	atf_check -o inline:${ZFS_POOL_NAME}\\n \
645	    zfs list -H -o name ${ZFS_POOL_NAME}
646	atf_check -o inline:${TEST_MOUNT_DIR}\\n \
647	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}
648
649	atf_check -o inline:${ZFS_POOL_NAME}/dir1\\n \
650	    zfs list -H -o name ${ZFS_POOL_NAME}/dir1
651	atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n \
652	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
653
654	atf_check -o inline:${ZFS_POOL_NAME}/dir2\\n \
655	    zfs list -H -o name ${ZFS_POOL_NAME}/dir2
656	atf_check -o inline:${TEST_MOUNT_DIR}/dir2\\n \
657	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir2
658}
659multi_dataset_1_cleanup()
660{
661	common_cleanup
662}
663
664#
665# Create a pool with two datasets, where the root dataset is mounted below
666# the child dataset.
667#
668atf_test_case multi_dataset_2 cleanup
669multi_dataset_2_body()
670{
671	create_test_dirs
672	cd $TEST_INPUTS_DIR
673
674	mkdir dir1
675	echo a > dir1/a
676	mkdir dir2
677	echo b > dir2/b
678
679	cd -
680
681	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
682	    -o fs=${ZFS_POOL_NAME}/dir1\;mountpoint=/ \
683	    -o fs=${ZFS_POOL_NAME}\;mountpoint=/dir1 \
684	    $TEST_IMAGE $TEST_INPUTS_DIR
685
686	import_image
687
688	check_image_contents
689}
690multi_dataset_2_cleanup()
691{
692	common_cleanup
693}
694
695#
696# Create a dataset with a non-existent mount point.
697#
698atf_test_case multi_dataset_3 cleanup
699multi_dataset_3_body()
700{
701	create_test_dirs
702	cd $TEST_INPUTS_DIR
703
704	mkdir dir1
705	echo a > dir1/a
706
707	cd -
708
709	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
710	    -o fs=${ZFS_POOL_NAME}/dir1 \
711	    -o fs=${ZFS_POOL_NAME}/dir2 \
712	    $TEST_IMAGE $TEST_INPUTS_DIR
713
714	import_image
715
716	atf_check -o inline:${TEST_MOUNT_DIR}/dir2\\n \
717	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir2
718
719	# Mounting dir2 should have created a directory called dir2.  Go
720	# back and create it in the staging tree before comparing.
721	atf_check mkdir ${TEST_INPUTS_DIR}/dir2
722
723	check_image_contents
724}
725multi_dataset_3_cleanup()
726{
727	common_cleanup
728}
729
730#
731# Create an unmounted dataset.
732#
733atf_test_case multi_dataset_4 cleanup
734multi_dataset_4_body()
735{
736	create_test_dirs
737	cd $TEST_INPUTS_DIR
738
739	mkdir dir1
740	echo a > dir1/a
741
742	cd -
743
744	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
745	    -o fs=${ZFS_POOL_NAME}/dir1\;canmount=noauto\;mountpoint=none \
746	    $TEST_IMAGE $TEST_INPUTS_DIR
747
748	import_image
749
750	atf_check -o inline:none\\n \
751	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
752
753	check_image_contents
754
755	atf_check zfs set mountpoint=/dir1 ${ZFS_POOL_NAME}/dir1
756	atf_check zfs mount ${ZFS_POOL_NAME}/dir1
757	atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n \
758	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
759
760	# dir1/a should be part of the root dataset, not dir1.
761	atf_check -s not-exit:0 -e not-empty stat ${TEST_MOUNT_DIR}/dir1/a
762}
763multi_dataset_4_cleanup()
764{
765	common_cleanup
766}
767
768#
769# Create a legacy dataset.
770#
771atf_test_case multi_dataset_5 cleanup
772multi_dataset_5_body()
773{
774	create_test_dirs
775	cd $TEST_INPUTS_DIR
776
777	mkdir dir1
778	echo a > dir1/a
779
780	cd -
781
782	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
783	    -o fs=${ZFS_POOL_NAME}/dir1\;canmount=noauto\;mountpoint=legacy \
784	    $TEST_IMAGE $TEST_INPUTS_DIR
785
786	import_image
787
788	atf_check -o inline:legacy\\n \
789	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
790
791	check_image_contents
792
793	atf_check zfs set mountpoint=/dir1 ${ZFS_POOL_NAME}/dir1
794	atf_check zfs mount ${ZFS_POOL_NAME}/dir1
795	atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n \
796	    zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
797
798	# dir1/a should be part of the root dataset, not dir1.
799	atf_check -s not-exit:0 -e not-empty stat ${TEST_MOUNT_DIR}/dir1/a
800}
801multi_dataset_5_cleanup()
802{
803	common_cleanup
804}
805
806#
807# Validate handling of multiple staging directories.
808#
809atf_test_case multi_staging_1 cleanup
810multi_staging_1_body()
811{
812	local tmpdir
813
814	create_test_dirs
815	cd $TEST_INPUTS_DIR
816
817	mkdir dir1
818	echo a > a
819	echo a > dir1/a
820	echo z > z
821
822	cd -
823
824	tmpdir=$(mktemp -d)
825	cd $tmpdir
826
827	mkdir dir2 dir2/dir3
828	echo b > dir2/b
829	echo c > dir2/dir3/c
830	ln -s dir2/dir3c s
831
832	cd -
833
834	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
835	    $TEST_IMAGE ${TEST_INPUTS_DIR} $tmpdir
836
837	import_image
838
839	check_image_contents -d $tmpdir
840}
841multi_staging_1_cleanup()
842{
843	common_cleanup
844}
845
846atf_test_case multi_staging_2 cleanup
847multi_staging_2_body()
848{
849	local tmpdir
850
851	create_test_dirs
852	cd $TEST_INPUTS_DIR
853
854	mkdir dir
855	echo a > dir/foo
856	echo b > dir/bar
857
858	cd -
859
860	tmpdir=$(mktemp -d)
861	cd $tmpdir
862
863	mkdir dir
864	echo c > dir/baz
865
866	cd -
867
868	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
869	    $TEST_IMAGE ${TEST_INPUTS_DIR} $tmpdir
870
871	import_image
872
873	# check_image_contents can't easily handle merged directories, so
874	# just check that the merged directory contains the files we expect.
875	atf_check -o not-empty stat ${TEST_MOUNT_DIR}/dir/foo
876	atf_check -o not-empty stat ${TEST_MOUNT_DIR}/dir/bar
877	atf_check -o not-empty stat ${TEST_MOUNT_DIR}/dir/baz
878
879	if [ "$(ls ${TEST_MOUNT_DIR}/dir | wc -l)" -ne 3 ]; then
880		atf_fail "Expected 3 files in ${TEST_MOUNT_DIR}/dir"
881	fi
882}
883multi_staging_2_cleanup()
884{
885	common_cleanup
886}
887
888#
889# Rudimentary test to verify that two ZFS images created using the same
890# parameters and input hierarchy are byte-identical.  In particular, makefs(1)
891# does not preserve file access times.
892#
893atf_test_case reproducible cleanup
894reproducible_body()
895{
896	create_test_inputs
897
898	atf_check $MAKEFS -s 512m -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
899	    ${TEST_IMAGE}.1 $TEST_INPUTS_DIR
900
901	atf_check $MAKEFS -s 512m -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
902	    ${TEST_IMAGE}.2 $TEST_INPUTS_DIR
903
904	# XXX-MJ cmp(1) is really slow
905	atf_check cmp ${TEST_IMAGE}.1 ${TEST_IMAGE}.2
906}
907reproducible_cleanup()
908{
909}
910
911#
912# Verify that we can take a snapshot of a generated dataset.
913#
914atf_test_case snapshot cleanup
915snapshot_body()
916{
917	create_test_dirs
918	cd $TEST_INPUTS_DIR
919
920	mkdir dir
921	echo "hello" > dir/hello
922	echo "goodbye" > goodbye
923
924	cd -
925
926	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
927	    $TEST_IMAGE $TEST_INPUTS_DIR
928
929	import_image
930
931	atf_check zfs snapshot ${ZFS_POOL_NAME}@1
932}
933snapshot_cleanup()
934{
935	common_cleanup
936}
937
938#
939# Check handling of symbolic links.
940#
941atf_test_case soft_links cleanup
942soft_links_body()
943{
944	create_test_dirs
945	cd $TEST_INPUTS_DIR
946
947	mkdir dir
948	ln -s a a
949	ln -s dir/../a a
950	ln -s dir/b b
951	echo 'c' > dir
952	ln -s dir/c c
953	# XXX-MJ overflows bonus buffer ln -s $(jot -s '' 320 1 1) 1
954
955	cd -
956
957	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
958	    $TEST_IMAGE $TEST_INPUTS_DIR
959
960	import_image
961
962	check_image_contents
963}
964soft_links_cleanup()
965{
966	common_cleanup
967}
968
969#
970# Verify that we can set properties on the root dataset.
971#
972atf_test_case root_props cleanup
973root_props_body()
974{
975	create_test_inputs
976
977	atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
978	    -o fs=${ZFS_POOL_NAME}\;atime=off\;setuid=off \
979	    $TEST_IMAGE $TEST_INPUTS_DIR
980
981	import_image
982
983	check_image_contents
984
985	atf_check -o inline:off\\n zfs get -H -o value atime $ZFS_POOL_NAME
986	atf_check -o inline:local\\n zfs get -H -o source atime $ZFS_POOL_NAME
987	atf_check -o inline:off\\n zfs get -H -o value setuid $ZFS_POOL_NAME
988	atf_check -o inline:local\\n zfs get -H -o source setuid $ZFS_POOL_NAME
989}
990root_props_cleanup()
991{
992	common_cleanup
993}
994
995#
996# Verify that usedds and usedchild props are set properly.
997#
998atf_test_case used_space_props cleanup
999used_space_props_body()
1000{
1001	local used usedds usedchild
1002	local rootmb childmb totalmb fudge
1003	local status
1004
1005	create_test_dirs
1006	cd $TEST_INPUTS_DIR
1007	mkdir dir
1008
1009	rootmb=17
1010	childmb=39
1011	totalmb=$(($rootmb + $childmb))
1012	fudge=$((2 * 1024 * 1024))
1013
1014	atf_check -e ignore dd if=/dev/random of=foo bs=1M count=$rootmb
1015	atf_check -e ignore dd if=/dev/random of=dir/bar bs=1M count=$childmb
1016
1017	cd -
1018
1019	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
1020	    -o fs=${ZFS_POOL_NAME}/dir \
1021	    $TEST_IMAGE $TEST_INPUTS_DIR
1022
1023	import_image
1024
1025	# Make sure that each dataset's space usage is no more than 2MB larger
1026	# than their files.  This number is magic and might need to change
1027	# someday.
1028	usedds=$(zfs list -o usedds -Hp ${ZFS_POOL_NAME})
1029	atf_check test $usedds -gt $(($rootmb * 1024 * 1024)) -a \
1030	    $usedds -le $(($rootmb * 1024 * 1024 + $fudge))
1031	usedds=$(zfs list -o usedds -Hp ${ZFS_POOL_NAME}/dir)
1032	atf_check test $usedds -gt $(($childmb * 1024 * 1024)) -a \
1033	    $usedds -le $(($childmb * 1024 * 1024 + $fudge))
1034
1035	# Make sure that the usedchild property value makes sense: the parent's
1036	# value corresponds to the size of the child, and the child has no
1037	# children.
1038	usedchild=$(zfs list -o usedchild -Hp ${ZFS_POOL_NAME})
1039	atf_check test $usedchild -gt $(($childmb * 1024 * 1024)) -a \
1040	    $usedchild -le $(($childmb * 1024 * 1024 + $fudge))
1041	atf_check -o inline:'0\n' zfs list -Hp -o usedchild ${ZFS_POOL_NAME}/dir
1042
1043	# Make sure that the used property value makes sense: the parent's
1044	# value is the sum of the two sizes, and the child's value is the
1045	# same as its usedds value, which has already been checked.
1046	used=$(zfs list -o used -Hp ${ZFS_POOL_NAME})
1047	atf_check test $used -gt $(($totalmb * 1024 * 1024)) -a \
1048	    $used -le $(($totalmb * 1024 * 1024 + 2 * $fudge))
1049	used=$(zfs list -o used -Hp ${ZFS_POOL_NAME}/dir)
1050	atf_check -o inline:$used'\n' zfs list -Hp -o usedds ${ZFS_POOL_NAME}/dir
1051
1052	# Both datasets do not have snapshots.
1053	atf_check -o inline:'0\n' zfs list -Hp -o usedsnap ${ZFS_POOL_NAME}
1054	atf_check -o inline:'0\n' zfs list -Hp -o usedsnap ${ZFS_POOL_NAME}/dir
1055}
1056used_space_props_cleanup()
1057{
1058	common_cleanup
1059}
1060
1061#
1062# Test setting the path of the vdev.
1063#
1064# The pool is associated to /dev/md0 by default, i.e., when no path is specified.
1065# When the path vdevprops(7) is specified, verify it gets set.
1066#
1067atf_test_case path_vdev_props cleanup
1068path_vdev_props_body()
1069{
1070	local md zdb_path
1071	local vdev_path="/dev/gpt/testdisk"
1072
1073	create_test_inputs
1074
1075	atf_check $MAKEFS -s 1g -o rootpath=/ \
1076	    -o poolname=$ZFS_POOL_NAME \
1077	    -o path=${vdev_path} \
1078	    $TEST_IMAGE $TEST_INPUTS_DIR
1079
1080	# Check the raw path property before import_image.
1081	atf_check -o save:$TEST_MD_DEVICE_FILE mdconfig -a -f $TEST_IMAGE
1082	zdb_path=$(zdb -C -e -p /dev/$(cat $TEST_MD_DEVICE_FILE) \
1083	    $ZFS_POOL_NAME 2>/dev/null | awk -F"'" '/path:/ {print $2; exit}')
1084	atf_check -o inline:"${vdev_path}\n" echo "$zdb_path"
1085
1086	# Cleanup before import_image.
1087	if [ -f "$TEST_MD_DEVICE_FILE" ]; then
1088		md=$(cat $TEST_MD_DEVICE_FILE)
1089		if [ -c /dev/"$md" ]; then
1090			mdconfig -o force -d -u "$md"
1091		fi
1092	fi
1093
1094	# Once imported, the path will be /dev/$(cat $TEST_MD_DEVICE_FILE),
1095	# as /dev/gpt/testdisk does not really exist.
1096	import_image
1097
1098	check_image_contents
1099}
1100path_vdev_props_cleanup()
1101{
1102	common_cleanup
1103}
1104
1105# Verify that file permissions are set properly.  Make sure that non-executable
1106# files can't be executed.
1107atf_test_case perms cleanup
1108perms_body()
1109{
1110	local mode
1111
1112	create_test_dirs
1113	cd $TEST_INPUTS_DIR
1114
1115	for mode in $(seq 0 511); do
1116		mode=$(printf "%04o\n" $mode)
1117		echo 'echo a' > $mode
1118		atf_check chmod $mode $mode
1119	done
1120
1121	cd -
1122
1123	atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
1124	    $TEST_IMAGE $TEST_INPUTS_DIR
1125
1126	import_image
1127
1128	check_image_contents
1129
1130	for mode in $(seq 0 511); do
1131		mode=$(printf "%04o\n" $mode)
1132		if [ $(($mode & 0111)) -eq 0 ]; then
1133			atf_check -s not-exit:0 -e match:"Permission denied" \
1134			    ${TEST_INPUTS_DIR}/$mode
1135		fi
1136		if [ $(($mode & 0001)) -eq 0 ]; then
1137			atf_check -s not-exit:0 -e match:"Permission denied" \
1138			    su -m tests -c ${TEST_INPUTS_DIR}/$mode
1139		fi
1140	done
1141}
1142perms_cleanup()
1143{
1144	common_cleanup
1145}
1146
1147#
1148# Verify that -T timestamps are honored.
1149#
1150atf_test_case T_flag_dir cleanup
1151T_flag_dir_body()
1152{
1153	timestamp=1742574909
1154	create_test_dirs
1155	mkdir -p $TEST_INPUTS_DIR/dir1
1156
1157	atf_check $MAKEFS -T $timestamp -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
1158	    $TEST_IMAGE $TEST_INPUTS_DIR
1159
1160	import_image
1161	eval $(stat -s  $TEST_MOUNT_DIR/dir1)
1162	atf_check_equal $st_atime $timestamp
1163	atf_check_equal $st_mtime $timestamp
1164	atf_check_equal $st_ctime $timestamp
1165}
1166
1167T_flag_dir_cleanup()
1168{
1169	common_cleanup
1170}
1171
1172atf_test_case T_flag_F_flag cleanup
1173T_flag_F_flag_body()
1174{
1175	timestamp_F=1742574909
1176	timestamp_T=1742574910
1177	create_test_dirs
1178	mkdir -p $TEST_INPUTS_DIR/dir1
1179
1180	atf_check -o save:$TEST_SPEC_FILE $MTREE -c -p $TEST_INPUTS_DIR
1181	change_mtree_timestamp $TEST_SPEC_FILE $timestamp_F
1182	atf_check \
1183	    $MAKEFS -F $TEST_SPEC_FILE -T $timestamp_T -s 10g -o rootpath=/ \
1184	    -o poolname=$ZFS_POOL_NAME $TEST_IMAGE $TEST_INPUTS_DIR
1185
1186	import_image
1187	eval $(stat -s  $TEST_MOUNT_DIR/dir1)
1188	atf_check_equal $st_atime $timestamp_F
1189	atf_check_equal $st_mtime $timestamp_F
1190	# atf_check_equal $st_ctime $timestamp_F
1191}
1192
1193T_flag_F_flag_cleanup()
1194{
1195	common_cleanup
1196}
1197
1198atf_test_case T_flag_mtree cleanup
1199T_flag_mtree_body()
1200{
1201	timestamp=1742574909
1202	create_test_dirs
1203	mkdir -p $TEST_INPUTS_DIR/dir1
1204
1205	atf_check -o save:$TEST_SPEC_FILE $MTREE -c -p $TEST_INPUTS_DIR
1206	atf_check $MAKEFS -T $timestamp -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
1207	    $TEST_IMAGE $TEST_SPEC_FILE
1208
1209	import_image
1210	eval $(stat -s  $TEST_MOUNT_DIR/dir1)
1211	atf_check_equal $st_atime $timestamp
1212	atf_check_equal $st_mtime $timestamp
1213	atf_check_equal $st_ctime $timestamp
1214}
1215
1216T_flag_mtree_cleanup()
1217{
1218	common_cleanup
1219}
1220
1221atf_init_test_cases()
1222{
1223	atf_add_test_case autoexpand
1224	atf_add_test_case autotrim
1225	atf_add_test_case basic
1226	atf_add_test_case compression
1227	atf_add_test_case dataset_link_counts
1228	atf_add_test_case dataset_removal
1229	atf_add_test_case devfs
1230	atf_add_test_case empty_dir
1231	atf_add_test_case empty_fs
1232	atf_add_test_case file_extend
1233	atf_add_test_case file_sizes
1234	atf_add_test_case hard_links
1235	atf_add_test_case indirect_dnode_array
1236	atf_add_test_case long_file_name
1237	atf_add_test_case multi_dataset_1
1238	atf_add_test_case multi_dataset_2
1239	atf_add_test_case multi_dataset_3
1240	atf_add_test_case multi_dataset_4
1241	atf_add_test_case multi_dataset_5
1242	atf_add_test_case multi_staging_1
1243	atf_add_test_case multi_staging_2
1244	atf_add_test_case reproducible
1245	atf_add_test_case snapshot
1246	atf_add_test_case soft_links
1247	atf_add_test_case root_props
1248	atf_add_test_case used_space_props
1249	atf_add_test_case path_vdev_props
1250	atf_add_test_case perms
1251	atf_add_test_case T_flag_dir
1252	atf_add_test_case T_flag_F_flag
1253	atf_add_test_case T_flag_mtree
1254
1255	# XXXMJ tests:
1256	# - test with different ashifts (at least, 9 and 12), different image sizes
1257	# - create datasets in imported pool
1258}
1259