#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2025 CTERA Networks. All Rights Reserved.
#
# FS QA Test No. 783
#
# Test overlayfs error cases with casefold enabled layers
#
# Overalyfs did not allow mounting layers with casefold capable fs
# until kernel v6.17 and with casefold enabled until kernel v6.18.
# Since kernel v6.17, overalyfs allows the mount, as long as casefolding
# is disabled on all directories.
# Since kernel v6.18, overalyfs allows the mount, as long as casefolding
# is consistent on all directories and encoding is consistent on all layers.
#
. ./common/preamble
_begin_fstest auto quick mount casefold

# Override the default cleanup function.
_cleanup()
{
	cd /
	_unmount $merge 2>/dev/null
	_unmount $MNT1
	_unmount $MNT2
	rm -r -f $tmp.*
}


# Import common functions.
. ./common/filter
. ./common/casefold

_exclude_fs overlay
_require_extra_fs overlay

_require_scratch_casefold

# Create casefold capable base fs
_scratch_mkfs_casefold >>$seqres.full 2>&1
_scratch_mount_casefold

# Create lowerdir, upperdir and workdir without casefold enabled
lowerdir="$SCRATCH_MNT/ovl-lower"
upperdir="$SCRATCH_MNT/ovl-upper"
workdir="$SCRATCH_MNT/ovl-work"
merge="$SCRATCH_MNT/ovl-merge"

mount_casefold_version()
{
	option="casefold=$1"
	_mount -t tmpfs -o $option tmpfs $2
}

mount_overlay()
{
	local lowerdirs=$1

	_mount -t overlay overlay $merge \
		-o lowerdir=$lowerdirs,upperdir=$upperdir,workdir=$workdir
}

unmount_overlay()
{
	_unmount $merge 2>/dev/null
}

# Try to mount an overlay with casefold enabled layers.
# On kernels older than v6.18 expect failure and skip the test
mkdir -p $merge $upperdir $workdir $lowerdir
_casefold_set_attr $upperdir >>$seqres.full
_casefold_set_attr $workdir >>$seqres.full
_casefold_set_attr $lowerdir >>$seqres.full
mount_overlay $lowerdir >>$seqres.full 2>&1 || \
	_notrun "overlayfs does not support casefold enabled layers"
unmount_overlay

# Re-create casefold disabled layers with lower subdir
casefolddir=$lowerdir/casefold
rm -rf $upperdir $workdir $lowerdir
mkdir -p $upperdir $workdir $lowerdir $casefolddir

# Try to mount an overlay with casefold capable but disabled layers.
# Since we already verified that overalyfs supports casefold enabled layers
# this is expected to succeed.
echo Casefold disabled
mount_overlay $lowerdir >>$seqres.full 2>&1 || \
	echo "Overlayfs mount with casefold disabled layers failed (1)"
ls $merge/casefold/ >>$seqres.full
unmount_overlay

# Use new upper/work dirs for each test to avoid ESTALE errors
# on mismatch lowerdir/upperdir (see test overlay/037)
rm -rf $upperdir $workdir
mkdir $upperdir $workdir

# Try to mount an overlay with casefold disabled layers and
# enable casefold on lowerdir root after mount - expect ESTALE error on lookup.
echo Casefold enabled after mount
mount_overlay $casefolddir >>$seqres.full || \
	echo "Overlayfs mount with casefold disabled layers failed (2)"
_casefold_set_attr $casefolddir >>$seqres.full
mkdir $casefolddir/subdir
ls $merge/subdir |& _filter_scratch
unmount_overlay

# Try to mount an overlay with casefold enabled lowerdir root - expect EINVAL.
# With libmount version >= v1.39, we expect the following descriptive error:
# mount: overlay: case-insensitive directory on .../ovl-lower/casefold not supported
# but we want the test to run with older libmount, so we so not expect this output
# we just expect a mount failure.
echo Casefold enabled lower dir
mount_overlay $casefolddir >>$seqres.full 2>&1 && \
	echo "Overlayfs mount with casefold enabled lowerdir should have failed" && \
	unmount_overlay

# Changing lower layer root again
rm -rf $upperdir $workdir
mkdir $upperdir $workdir

# Try to mount an overlay with casefold disabled layers, but with
# casefold enabled subdir in lowerdir - expect EREMOTE error on lookup.
echo Casefold enabled lower subdir
mount_overlay $lowerdir >>$seqres.full
ls $merge/casefold/subdir |& _filter_scratch
unmount_overlay

# workdir needs to be empty to set casefold attribute
rm -rf $workdir/*

_casefold_set_attr $upperdir >>$seqres.full
_casefold_set_attr $workdir >>$seqres.full

echo Casefold enabled upper dir
mount_overlay $lowerdir >>$seqres.full 2>&1 && \
	echo "Overlayfs mount with casefold enabled upperdir should have failed" && \
	unmount_overlay

# lowerdir needs to be empty to set casefold attribute
rm -rf $lowerdir/*
_casefold_set_attr $lowerdir >>$seqres.full
mkdir $casefolddir

# Try to mount an overlay with casefold enabled layers.
# On kernels older than v6.18 expect failure and skip the rest of the test
# On kernels v6.18 and newer, expect success and run the rest of the test cases.
echo Casefold enabled
mount_overlay $lowerdir >>$seqres.full 2>&1 || \
	echo "Overlayfs mount with casefold enabled layers failed (1)"
ls $merge/casefold/ >>$seqres.full
unmount_overlay

# Try to mount an overlayfs with casefold enabled layers. After the mount,
# disable casefold on the lower layer and try to lookup a file. Should return
# -ESTALE
echo Casefold disabled on lower after mount
mount_overlay $lowerdir >>$seqres.full 2>&1 || \
	echo "Overlayfs mount with casefold enabled layers failed (2)"
rm -rf $lowerdir/*
_casefold_unset_attr $lowerdir >>$seqres.full
mkdir $lowerdir/dir
ls $merge/dir/ |& _filter_scratch
unmount_overlay

# cleanup
rm -rf $lowerdir/*
_casefold_set_attr $lowerdir >>$seqres.full

# Try to mount an overlayfs with casefold enabled layers. After the mount,
# disable casefold on a subdir in  the lower layer and try to lookup it.
# Should return -EREMOTE
echo Casefold disabled on subdir after mount
mkdir $lowerdir/casefold/
mount_overlay $lowerdir >>$seqres.full 2>&1 || \
	echo "Overlayfs mount with casefold enabled layers failed (3)"
_casefold_unset_attr $lowerdir/casefold/
mkdir $lowerdir/casefold/subdir
ls $merge/casefold/subdir |& _filter_scratch
unmount_overlay

# cleanup
rm -rf $lowerdir/*

# Test strict enconding, but casefold not enabled. Should work
_scratch_umount_idmapped

_scratch_mkfs_casefold_strict >>$seqres.full 2>&1
_scratch_mount_casefold_strict

mkdir -p $merge $upperdir $workdir $lowerdir

mount_overlay $lowerdir >>$seqres.full 2>&1 || \
	echo "Overlayfs mount with strict casefold disabled layers failed"
unmount_overlay

# Test strict enconding, with casefold enabled. Should fail
# dmesg: overlayfs: strict encoding not supported
rm -rf $upperdir $workdir
mkdir $upperdir $workdir

_casefold_set_attr $upperdir >>$seqres.full
_casefold_set_attr $workdir >>$seqres.full
_casefold_set_attr $lowerdir >>$seqres.full

mount_overlay $lowerdir >>$seqres.full 2>&1 && \
	echo "Overlayfs mount with strict casefold enabled should have failed" && \
	unmount_overlay

# Test inconsistent casefold version. Should fail
# dmesg: overlayfs: all layers must have the same encoding

# use tmpfs to make easier to create two different mount points with different
# utf8 versions
testdir="$SCRATCH_MNT/newdir/"
mkdir $testdir

MNT1="$testdir/mnt1"
MNT2="$testdir/mnt2"

mkdir $MNT1 $MNT2 "$testdir/merge"

mount_casefold_version "utf8-12.1.0" $MNT1
mount_casefold_version "utf8-11.0.0" $MNT2

mkdir "$MNT1/dir" "$MNT2/dir"

_casefold_set_attr "$MNT1/dir"
_casefold_set_attr "$MNT2/dir"

mkdir "$MNT1/dir/lower" "$MNT2/dir/upper" "$MNT2/dir/work"

upperdir="$MNT2/dir/upper"
workdir="$MNT2/dir/work"
lowerdir="$MNT1/dir/lower"

mount_overlay $lowerdir >>$seqres.full 2>&1  && \
	echo "Overlayfs mount different unicode versions should have failed" && \
	unmount_overlay

# success, all done
status=0
exit
