opnsense-src/usr.sbin/makefs/tests/makefs_zfs_tests.sh
Mark Johnston 8502144d7a makefs tests: Add test cases for handling of multiple staging dirs
Sponsored by:	The FreeBSD Foundation
2022-08-17 17:28:01 -04:00

718 lines
15 KiB
Bash

#-
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
#
# Copyright (c) 2022 The FreeBSD Foundation
#
# This software was developed by Mark Johnston under sponsorship from
# the FreeBSD Foundation.
#
# 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.
#
MAKEFS="makefs -t zfs -o nowarn=true"
ZFS_POOL_NAME="makefstest$$"
TEST_ZFS_POOL_NAME="$TMPDIR/poolname"
. "$(dirname "$0")/makefs_tests_common.sh"
common_cleanup()
{
local pool md
# Try to force a TXG, this can help catch bugs by triggering a panic.
sync
pool=$(cat $TEST_ZFS_POOL_NAME)
if zpool list "$pool" >/dev/null; then
zpool destroy "$pool"
fi
md=$(cat $TEST_MD_DEVICE_FILE)
if [ -c /dev/"$md" ]; then
mdconfig -d -u "$md"
fi
}
import_image()
{
atf_check -e empty -o save:$TEST_MD_DEVICE_FILE -s exit:0 \
mdconfig -a -f $TEST_IMAGE
atf_check zpool import -R $TEST_MOUNT_DIR $ZFS_POOL_NAME
echo "$ZFS_POOL_NAME" > $TEST_ZFS_POOL_NAME
}
#
# Test autoexpansion of the vdev.
#
# The pool is initially 10GB, so we get 10GB minus one metaslab's worth of
# usable space for data. Then the pool is expanded to 50GB, and the amount of
# usable space is 50GB minus one metaslab.
#
atf_test_case autoexpand cleanup
autoexpand_body()
{
local mssize poolsize poolsize1 newpoolsize
create_test_inputs
mssize=$((128 * 1024 * 1024))
poolsize=$((10 * 1024 * 1024 * 1024))
atf_check $MAKEFS -s $poolsize -o mssize=$mssize -o rootpath=/ \
-o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
newpoolsize=$((50 * 1024 * 1024 * 1024))
truncate -s $newpoolsize $TEST_IMAGE
import_image
check_image_contents
poolsize1=$(zpool list -Hp -o size $ZFS_POOL_NAME)
atf_check [ $((poolsize1 + $mssize)) -eq $poolsize ]
atf_check zpool online -e $ZFS_POOL_NAME /dev/$(cat $TEST_MD_DEVICE_FILE)
check_image_contents
poolsize1=$(zpool list -Hp -o size $ZFS_POOL_NAME)
atf_check [ $((poolsize1 + $mssize)) -eq $newpoolsize ]
}
autoexpand_cleanup()
{
common_cleanup
}
#
# Test with some default layout defined by the common code.
#
atf_test_case basic cleanup
basic_body()
{
create_test_inputs
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
}
basic_cleanup()
{
common_cleanup
}
atf_test_case dataset_removal cleanup
dataset_removal_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
-o fs=${ZFS_POOL_NAME}/dir \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
atf_check zfs destroy ${ZFS_POOL_NAME}/dir
}
dataset_removal_cleanup()
{
common_cleanup
}
#
# Make sure that we can create and remove an empty directory.
#
atf_test_case empty_dir cleanup
empty_dir_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir
cd -
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
atf_check rmdir ${TEST_MOUNT_DIR}/dir
}
empty_dir_cleanup()
{
common_cleanup
}
atf_test_case empty_fs cleanup
empty_fs_body()
{
create_test_dirs
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
}
empty_fs_cleanup()
{
common_cleanup
}
atf_test_case file_sizes cleanup
file_sizes_body()
{
local i
create_test_dirs
cd $TEST_INPUTS_DIR
i=1
while [ $i -lt $((1 << 20)) ]; do
truncate -s $i ${i}.1
truncate -s $(($i - 1)) ${i}.2
truncate -s $(($i + 1)) ${i}.3
i=$(($i << 1))
done
cd -
# XXXMJ this creates sparse files, make sure makefs doesn't
# preserve the sparseness.
# XXXMJ need to test with larger files (at least 128MB for L2 indirs)
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
}
file_sizes_cleanup()
{
common_cleanup
}
atf_test_case hard_links cleanup
hard_links_body()
{
local f
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir
echo "hello" > 1
ln 1 2
ln 1 dir/1
echo "goodbye" > dir/a
ln dir/a dir/b
ln dir/a a
cd -
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
stat -f '%i' ${TEST_MOUNT_DIR}/1 > ./ino
stat -f '%l' ${TEST_MOUNT_DIR}/1 > ./nlink
for f in 1 2 dir/1; do
atf_check -o file:./nlink -e empty -s exit:0 \
stat -f '%l' ${TEST_MOUNT_DIR}/${f}
atf_check -o file:./ino -e empty -s exit:0 \
stat -f '%i' ${TEST_MOUNT_DIR}/${f}
atf_check cmp -s ${TEST_INPUTS_DIR}/1 ${TEST_MOUNT_DIR}/${f}
done
stat -f '%i' ${TEST_MOUNT_DIR}/dir/a > ./ino
stat -f '%l' ${TEST_MOUNT_DIR}/dir/a > ./nlink
for f in dir/a dir/b a; do
atf_check -o file:./nlink -e empty -s exit:0 \
stat -f '%l' ${TEST_MOUNT_DIR}/${f}
atf_check -o file:./ino -e empty -s exit:0 \
stat -f '%i' ${TEST_MOUNT_DIR}/${f}
atf_check cmp -s ${TEST_INPUTS_DIR}/dir/a ${TEST_MOUNT_DIR}/${f}
done
}
hard_links_cleanup()
{
common_cleanup
}
# Allocate enough dnodes from an object set that the meta dnode needs to use
# indirect blocks.
atf_test_case indirect_dnode_array cleanup
indirect_dnode_array_body()
{
local count i
# How many dnodes do we need to allocate? Well, the data block size
# for meta dnodes is always 16KB, so with a dnode size of 512B we get
# 32 dnodes per direct block. The maximum indirect block size is 128KB
# and that can fit 1024 block pointers, so we need at least 32 * 1024
# files to force the use of two levels of indirection.
#
# Unfortunately that number of files makes the test run quite slowly,
# so we settle for a single indirect block for now...
count=$(jot -r 1 32 1024)
create_test_dirs
cd $TEST_INPUTS_DIR
for i in $(seq 1 $count); do
touch $i
done
cd -
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
}
indirect_dnode_array_cleanup()
{
common_cleanup
}
#
# Create some files with long names, so as to test fat ZAP handling.
#
atf_test_case long_file_name cleanup
long_file_name_body()
{
local dir i
create_test_dirs
cd $TEST_INPUTS_DIR
# micro ZAP keys can be at most 50 bytes.
for i in $(seq 1 60); do
touch $(jot -s '' $i 1 1)
done
dir=$(jot -s '' 61 1 1)
mkdir $dir
for i in $(seq 1 60); do
touch ${dir}/$(jot -s '' $i 1 1)
done
cd -
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
# Add a directory entry in the hope that OpenZFS might catch a bug
# in makefs' fat ZAP encoding.
touch ${TEST_MOUNT_DIR}/foo
}
long_file_name_cleanup()
{
common_cleanup
}
#
# Exercise handling of multiple datasets.
#
atf_test_case multi_dataset_1 cleanup
multi_dataset_1_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir1
echo a > dir1/a
mkdir dir2
echo b > dir2/b
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
-o fs=${ZFS_POOL_NAME}/dir1 -o fs=${ZFS_POOL_NAME}/dir2 \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
# Make sure that we have three datasets with the expected mount points.
atf_check -o inline:${ZFS_POOL_NAME}\\n -e empty -s exit:0 \
zfs list -H -o name ${ZFS_POOL_NAME}
atf_check -o inline:${TEST_MOUNT_DIR}\\n -e empty -s exit:0 \
zfs list -H -o mountpoint ${ZFS_POOL_NAME}
atf_check -o inline:${ZFS_POOL_NAME}/dir1\\n -e empty -s exit:0 \
zfs list -H -o name ${ZFS_POOL_NAME}/dir1
atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n -e empty -s exit:0 \
zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
atf_check -o inline:${ZFS_POOL_NAME}/dir2\\n -e empty -s exit:0 \
zfs list -H -o name ${ZFS_POOL_NAME}/dir2
atf_check -o inline:${TEST_MOUNT_DIR}/dir2\\n -e empty -s exit:0 \
zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir2
}
multi_dataset_1_cleanup()
{
common_cleanup
}
#
# Create a pool with two datasets, where the root dataset is mounted below
# the child dataset.
#
atf_test_case multi_dataset_2 cleanup
multi_dataset_2_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir1
echo a > dir1/a
mkdir dir2
echo b > dir2/b
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
-o fs=${ZFS_POOL_NAME}/dir1\;mountpoint=/ \
-o fs=${ZFS_POOL_NAME}\;mountpoint=/dir1 \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
}
multi_dataset_2_cleanup()
{
common_cleanup
}
#
# Create a dataset with a non-existent mount point.
#
atf_test_case multi_dataset_3 cleanup
multi_dataset_3_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir1
echo a > dir1/a
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
-o fs=${ZFS_POOL_NAME}/dir1 \
-o fs=${ZFS_POOL_NAME}/dir2 \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
atf_check -o inline:${TEST_MOUNT_DIR}/dir2\\n -e empty -s exit:0 \
zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir2
# Mounting dir2 should have created a directory called dir2. Go
# back and create it in the staging tree before comparing.
atf_check mkdir ${TEST_INPUTS_DIR}/dir2
check_image_contents
}
multi_dataset_3_cleanup()
{
common_cleanup
}
#
# Create an unmounted dataset.
#
atf_test_case multi_dataset_4 cleanup
multi_dataset_4_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir1
echo a > dir1/a
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
-o fs=${ZFS_POOL_NAME}/dir1\;canmount=noauto\;mountpoint=none \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
atf_check -o inline:none\\n -e empty -s exit:0 \
zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
check_image_contents
atf_check zfs set mountpoint=/dir1 ${ZFS_POOL_NAME}/dir1
atf_check zfs mount ${ZFS_POOL_NAME}/dir1
atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n -e empty -s exit:0 \
zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
# dir1/a should be part of the root dataset, not dir1.
atf_check -s not-exit:0 -e not-empty stat ${TEST_MOUNT_DIR}dir1/a
}
multi_dataset_4_cleanup()
{
common_cleanup
}
#
# Validate handling of multiple staging directories.
#
atf_test_case multi_staging_1 cleanup
multi_staging_1_body()
{
local tmpdir
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir1
echo a > a
echo a > dir1/a
echo z > z
cd -
tmpdir=$(mktemp -d)
cd $tmpdir
mkdir dir2 dir2/dir3
echo b > dir2/b
echo c > dir2/dir3/c
ln -s dir2/dir3c s
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE ${TEST_INPUTS_DIR} $tmpdir
import_image
check_image_contents -d $tmpdir
}
multi_staging_1_cleanup()
{
common_cleanup
}
atf_test_case multi_staging_2 cleanup
multi_staging_2_body()
{
local tmpdir
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir
echo a > dir/foo
echo b > dir/bar
cd -
tmpdir=$(mktemp -d)
cd $tmpdir
mkdir dir
echo c > dir/baz
cd -
atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE ${TEST_INPUTS_DIR} $tmpdir
import_image
# check_image_contents can't easily handle merged directories, so
# just check that the merged directory contains the files we expect.
atf_check -o not-empty stat ${TEST_MOUNT_DIR}/dir/foo
atf_check -o not-empty stat ${TEST_MOUNT_DIR}/dir/bar
atf_check -o not-empty stat ${TEST_MOUNT_DIR}/dir/baz
if [ "$(ls ${TEST_MOUNT_DIR}/dir | wc -l)" -ne 3 ]; then
atf_fail "Expected 3 files in ${TEST_MOUNT_DIR}/dir"
fi
}
multi_staging_2_cleanup()
{
common_cleanup
}
#
# Rudimentary test to verify that two ZFS images created using the same
# parameters and input hierarchy are byte-identical. In particular, makefs(1)
# does not preserve file access times.
#
atf_test_case reproducible cleanup
reproducible_body()
{
create_test_inputs
atf_check $MAKEFS -s 512m -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
${TEST_IMAGE}.1 $TEST_INPUTS_DIR
atf_check $MAKEFS -s 512m -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
${TEST_IMAGE}.2 $TEST_INPUTS_DIR
# XXX-MJ cmp(1) is really slow
atf_check cmp ${TEST_IMAGE}.1 ${TEST_IMAGE}.2
}
reproducible_cleanup()
{
}
#
# Verify that we can take a snapshot of a generated dataset.
#
atf_test_case snapshot cleanup
snapshot_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir
echo "hello" > dir/hello
echo "goodbye" > goodbye
cd -
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
atf_check zfs snapshot ${ZFS_POOL_NAME}@1
}
snapshot_cleanup()
{
common_cleanup
}
#
# Check handling of symbolic links.
#
atf_test_case soft_links cleanup
soft_links_body()
{
create_test_dirs
cd $TEST_INPUTS_DIR
mkdir dir
ln -s a a
ln -s dir/../a a
ln -s dir/b b
echo 'c' > dir
ln -s dir/c c
# XXX-MJ overflows bonus buffer ln -s $(jot -s '' 320 1 1) 1
cd -
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
}
soft_links_cleanup()
{
common_cleanup
}
#
# Verify that we can set properties on the root dataset.
#
atf_test_case root_props cleanup
root_props_body()
{
create_test_inputs
atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
-o fs=${ZFS_POOL_NAME}\;atime=off\;setuid=off \
$TEST_IMAGE $TEST_INPUTS_DIR
import_image
check_image_contents
atf_check -o inline:off\\n -e empty -s exit:0 \
zfs get -H -o value atime $ZFS_POOL_NAME
atf_check -o inline:local\\n -e empty -s exit:0 \
zfs get -H -o source atime $ZFS_POOL_NAME
atf_check -o inline:off\\n -e empty -s exit:0 \
zfs get -H -o value setuid $ZFS_POOL_NAME
atf_check -o inline:local\\n -e empty -s exit:0 \
zfs get -H -o source setuid $ZFS_POOL_NAME
}
root_props_cleanup()
{
common_cleanup
}
atf_init_test_cases()
{
atf_add_test_case autoexpand
atf_add_test_case basic
atf_add_test_case dataset_removal
atf_add_test_case empty_dir
atf_add_test_case empty_fs
atf_add_test_case file_sizes
atf_add_test_case hard_links
atf_add_test_case indirect_dnode_array
atf_add_test_case long_file_name
atf_add_test_case multi_dataset_1
atf_add_test_case multi_dataset_2
atf_add_test_case multi_dataset_3
atf_add_test_case multi_dataset_4
atf_add_test_case multi_staging_1
atf_add_test_case multi_staging_2
atf_add_test_case reproducible
atf_add_test_case snapshot
atf_add_test_case soft_links
atf_add_test_case root_props
# XXXMJ tests:
# - test with different ashifts (at least, 9 and 12), different image sizes
# - create datasets in imported pool
}