#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2022 Meta, Inc.  All Rights Reserved.
#
# FS QA Test 692
#
# fs-verity requires the filesystem to decide how it stores the Merkle tree,
# which can be quite large.
# It is convenient to treat the Merkle tree as past EOF, and ext4, f2fs, and
# btrfs do so in at least some fashion. This leads to an edge case where a
# large file can be under the file system file size limit, but trigger EFBIG
# on enabling fs-verity. Test enabling verity on some large files to exercise
# EFBIG logic for filesystems with fs-verity specific limits.
#
. ./common/preamble
_begin_fstest auto quick verity


# Import common functions.
. ./common/filter
. ./common/verity

# real QA test starts here
_supported_fs generic
_require_test
_require_math
_require_scratch_verity
_require_fsverity_max_file_size_limit

_scratch_mkfs_verity &>> $seqres.full
_scratch_mount

fsv_file=$SCRATCH_MNT/file.fsv

max_sz=$(_get_max_file_size)
_fsv_scratch_begin_subtest "way too big: fail on first merkle block"
truncate -s $max_sz $fsv_file
_fsv_enable $fsv_file |& _filter_scratch

# The goal of this second test is to make a big enough file that we trip the
# EFBIG codepath, but not so big that we hit it immediately when writing the
# first Merkle leaf.
#
# The Merkle tree is stored with the leaf node level (L0) last, but it is
# written first.  To get an interesting overflow, we need the maximum file size
# (MAX) to be in the middle of L0 -- ideally near the beginning of L0 so that we
# don't have to write many blocks before getting an error.
#
# With SHA-256 and 4K blocks, there are 128 hashes per block.  Thus, ignoring
# padding, L0 is 1/128 of the file size while the other levels in total are
# 1/128**2 + 1/128**3 + 1/128**4 + ... = 1/16256 of the file size.  So still
# ignoring padding, for L0 start exactly at MAX, the file size must be s such
# that s + s/16256 = MAX, i.e. s = MAX * (16256/16257).  Then to get a file size
# where MAX occurs *near* the start of L0 rather than *at* the start, we can
# just subtract an overestimate of the padding: 64K after the file contents,
# then 4K per level, where the consideration of 8 levels is sufficient.
sz=$(echo "scale=20; $max_sz * (16256/16257) - 65536 - 4096*8" | $BC -q | cut -d. -f1)
_fsv_scratch_begin_subtest "still too big: fail on first invalid merkle block"
truncate -s $sz $fsv_file
_fsv_enable $fsv_file |& _filter_scratch

# success, all done
status=0
exit
