1#!/bin/ksh
2
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# source.  A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/license/CDDL.
12#
13
14#
15# Copyright (c) 2020 The FreeBSD Foundation [1]
16#
17# [1] Portions of this software were developed by Allan Jude
18#     under sponsorship from the FreeBSD Foundation.
19
20. $STF_SUITE/include/libtest.shlib
21
22#
23# Description:
24# zdb -Z pool <objid> will display the ZSTD compression header
25#     This will contain the actual length of the compressed data, as well as
26#     the version of ZSTD used to compress the block, and the compression level
27#
28# Strategy:
29# 1. Create a pool, set compression to zstd-<random level>
30# 2. Write some identifiable data to a file
31# 3. Run zdb -Zddddddbbbbbb against the file
32# 4. Record the DVA, lsize, and psize, and ZSTD header of L0 block 0
33# 5. Check that the ZSTD length is less than psize
34# 6. Check that the ZSTD level matches the level we requested
35# 7. Run zdb -R with :dr flags and confirm the size and content match
36#
37
38function cleanup
39{
40	datasetexists $TESTPOOL && destroy_pool $TESTPOOL
41}
42
43log_assert "Verify zdb -Z (read ZSTD header) works as expected"
44log_onexit cleanup
45src_data="$STF_SUITE/tests/functional/cli_root/zfs_receive/zstd_test_data.txt"
46init_data=$TESTDIR/file1
47write_count=128
48blksize=131072
49verify_runnable "global"
50verify_disk_count "$DISKS" 2
51random_level=$((RANDOM%19 + 1))
52
53default_mirror_setup_noexit $DISKS
54log_must zfs set recordsize=$blksize $TESTPOOL/$TESTFS
55log_must zfs set compression=zstd-$random_level $TESTPOOL/$TESTFS
56
57# write the 1k of text 128 times
58for i in {1..$write_count}
59do
60	cat $src_data >> $init_data
61done
62
63sync_pool $TESTPOOL true
64
65# get object number of file
66listing=$(ls -i $init_data)
67set -A array $listing
68obj=${array[0]}
69log_note "file $init_data has object number $obj"
70
71output=$(zdb -Zddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \
72    |grep -m 1 "L0 DVA" |head -n1)
73dva=$(sed -Ene 's/^.+DVA\[0\]=<([^>]+)>.*$/\1/p' <<< "$output")
74log_note "block 0 of $init_data has a DVA of $dva"
75
76# use the length reported by zdb -ddddddbbbbbb
77size_str=$(sed -Ene 's/^.+ size=([^ ]+) .*$/\1/p' <<< "$output")
78# convert sizes to decimal
79lsize=$(echo $size_str |awk '{split($0,array,"/")} END{print array[1]}')
80lsize_orig=$lsize
81lsize=${lsize%?}
82lsize_bytes=$((16#$lsize))
83psize=$(echo $size_str |awk '{split($0,array,"/")} END{print array[2]}')
84psize_orig=$psize
85psize=${psize%?}
86psize_bytes=$((16#$psize))
87log_note "block size $size_str"
88
89# Get the ZSTD header reported by zdb -Z
90zstd_str=$(sed -Ene 's/^.+ ZSTD:size=([^:]+):version=([^:]+):level=([^:]+):.*$/\1:\2:\3/p' <<< "$output")
91zstd_size=$(echo "$zstd_str" |awk '{split($0,array,":")} END{print array[1]}')
92log_note "ZSTD compressed size $zstd_size"
93(( $psize_bytes < $zstd_size )) && log_fail \
94"zdb -Z failed: physical block size was less than header content length ($psize_bytes < $zstd_size)"
95
96zstd_version=$(echo "$zstd_str" |awk '{split($0,array,":")} END{print array[2]}')
97log_note "ZSTD version $zstd_version"
98
99zstd_level=$(echo "$zstd_str" |awk '{split($0,array,":")} END{print array[3]}')
100log_note "ZSTD level $zstd_level"
101(( $zstd_level != $random_level )) && log_fail \
102"zdb -Z failed: compression level did not match header level ($zstd_level < $random_level)"
103
104vdev=$(echo "$dva" |awk '{split($0,array,":")} END{print array[1]}')
105offset=$(echo "$dva" |awk '{split($0,array,":")} END{print array[2]}')
106# Check the first 1024 bytes
107output=$(ZDB_NO_ZLE="true" zdb -R $TESTPOOL $vdev:$offset:$size_str:dr 2> /dev/null)
108outsize=$(wc -c <<< "$output")
109(( $outsize != $blksize )) && log_fail \
110"zdb -Z failed to decompress the data to the expected length ($outsize != $lsize_bytes)"
111cmp $init_data - <<< "$output"
112(( $? != 0 )) && log_fail "zdb -R :dr failed to decompress the data properly"
113
114log_pass "zdb -Z flag (ZSTD compression header) works as expected"
115