From: "Lakshmipathi.G" <lakshmipathi.ganapathi@xxxxxxxxxxxxxxx> This new test implements verification for the per-directory case-insensitive feature, as supported by the reference implementation in Ext4. Signed-off-by: Lakshmipathi.G <lakshmipathi.ganapathi@xxxxxxxxxxxxxxx> Signed-off-by: Gabriel Krisman Bertazi <krisman@xxxxxxxxxxxxx> [Rewrite to support feature design] [Refactor to simplify implementation] --- tests/shared/012 | 508 +++++++++++++++++++++++++++++++++++++++++++ tests/shared/012.out | 16 ++ tests/shared/group | 1 + 3 files changed, 525 insertions(+) create mode 100755 tests/shared/012 create mode 100644 tests/shared/012.out diff --git a/tests/shared/012 b/tests/shared/012 new file mode 100755 index 000000000000..d7e9cb43f524 --- /dev/null +++ b/tests/shared/012 @@ -0,0 +1,508 @@ +# SPDX-License-Identifier: GPL-2.0+ +#!/bin/bash +# FSQA Test No. 012 +# +# Test the basic functionality of filesystems with case-insensitive +# support. + +seq=`basename $0` +seqres=$RESULT_DIR/$seq +echo "QA output created by $seq" +status=1 # failure is thea default + +. ./common/rc +. ./common/filter +. ./common/casefold +. ./common/attr + +_supported_os Linux +_require_scratch_nocheck +_require_scratch_casefold +_require_check_dmesg +_require_attrs + +sdev=$(_short_dev ${SCRATCH_DEV}) + +filename1="file.txt" +filename2="FILE.TXT" + +pt_file1=$(echo -e "coração") +pt_file2=$(echo -e "corac\xcc\xa7\xc3\xa3o" | tr a-z A-Z) + +fr_file2=$(echo -e "french_caf\xc3\xa9.txt") +fr_file1=$(echo -e "french_cafe\xcc\x81.txt") + +ar_file1=$(echo -e "arabic_\xdb\x92\xd9\x94.txt") +ar_file2=$(echo -e "arabic_\xdb\x93.txt" | tr a-z A-Z) + +jp_file1=$(echo -e "japanese_\xe3\x82\xb2.txt") +jp_file2=$(echo -e "japanese_\xe3\x82\xb1\xe3\x82\x99.txt") + +# '\xc3\x00' is an invalid sequence. Despite that, the sequences +# below could match, if we ignored the error. But we don't want +# to be greedy at normalization, so at the first error we treat +# the entire sequence as an opaque blob. Therefore, these two +# must NOT match. +blob_file1=$(echo -e "corac\xcc\xa7\xc3") +blob_file2=$(echo -e "coraç\xc3") + +# Test helpers +basic_create_lookup() +{ + local basedir=${1} + local exact=${2} + local lookup=${3} + + touch "${basedir}/${exact}" + [ -f "${basedir}/${lookup}" ] || \ + echo "lookup of ${exact} using ${lookup} failed" + _casefold_check_exact_name "${basedir}" "${exact}" || \ + echo "Created file ${exact} with wrong name." +} + +# CI search should fail. +bad_basic_create_lookup() +{ + local basedir=${1} + local exact=${2} + local lookup=${3} + + touch "${basedir}/${exact}" + [ -f "${basedir}/${lookup}" ] && \ + echo "Lookup of ${exact} using ${lookup} should fail" +} + +# Testcases +test_casefold_lookup() +{ + local basedir=${SCRATCH_MNT}/casefold_lookup + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + basic_create_lookup "${basedir}" "${filename1}" "${filename2}" + basic_create_lookup "${basedir}" "${pt_file1}" "${pt_file2}" + basic_create_lookup "${basedir}" "${fr_file1}" "${fr_file2}" + basic_create_lookup "${basedir}" "${ar_file1}" "${ar_file2}" + basic_create_lookup "${basedir}" "${jp_file1}" "${jp_file2}" +} + +test_bad_casefold_lookup() +{ + local basedir=${SCRATCH_MNT}/casefold_lookup + + mkdir -p ${basedir} + + bad_basic_create_lookup ${basedir} ${blob_file1} ${blob_file2} +} + +do_create_and_remove() +{ + local basedir=${1} + local exact=${2} + local casefold=${3} + + basic_create_lookup ${basedir} ${exact} ${casefold} + rm -f ${basedir}/${exact} + [ -f ${basedir}/${exact} ] && \ + echo "File ${exact} was not removed using exact name" + + basic_create_lookup ${basedir} ${exact} ${casefold} + rm -f ${basedir}/${casefold} + [ -f ${basedir}/${exact} ] && \ + echo "File ${exact} was not removed using inexact name" +} + +# remove and recreate +test_create_and_remove() +{ + local basedir=${SCRATCH_MNT}/create_and_remove + mkdir -p ${basedir} + + _casefold_set_attr ${basedir} + do_create_and_remove "${basedir}" "${pt_file1}" "${pt_file2}" + do_create_and_remove "${basedir}" "${jp_file1}" "${jp_file2}" + do_create_and_remove "${basedir}" "${ar_file1}" "${ar_file2}" + do_create_and_remove "${basedir}" "${fr_file1}" "${fr_file2}" +} + +test_casefold_flag_basic() +{ + local basedir=${SCRATCH_MNT}/basic + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + _casefold_lsattr_dir ${basedir} | _filter_scratch + + _casefold_unset_attr ${basedir} + _casefold_lsattr_dir ${basedir} | _filter_scratch +} + +test_casefold_flag_removal() +{ + local basedir=${SCRATCH_MNT}/casefold_flag_removal + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + _casefold_lsattr_dir ${basedir} | _filter_scratch + + # Try to remove +F attribute on non empty directory + touch ${basedir}/${filename1} + _casefold_unset_attr ${basedir} &>/dev/null + _casefold_lsattr_dir ${basedir} | _filter_scratch +} + +# Test Inheritance of casefold flag +test_casefold_flag_inheritance() +{ + local basedir=${SCRATCH_MNT}/flag_inheritance + local dirpath1="d1/d2/d3" + local dirpath2="D1/D2/D3" + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + mkdir -p ${basedir}/${dirpath1} + _casefold_lsattr_dir ${basedir}/${dirpath1} | _filter_scratch + + [ -d ${basedir}/${dirpath2} ] || \ + echo "Directory CI Lookup failed." + _casefold_check_exact_name "${basedir}" "${dirpath1}" || \ + echo "Created directory with wrong name." + + touch ${basedir}/${dirpath2}/${filename1} + [ -f ${basedir}/${dirpath1}/${filename2} ] || \ + echo "Couldn't create file on casefolded parent." +} + +# Test nesting of sensitive directory inside insensitive directory. +test_nesting_sensitive_insensitive_tree_simple() +{ + local basedir=${SCRATCH_MNT}/sd1 + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + mkdir -p ${basedir}/sd1 + _casefold_set_attr ${basedir}/sd1 + + mkdir ${basedir}/sd1/sd2 + _casefold_unset_attr ${basedir}/sd1/sd2 + + touch ${basedir}/sd1/sd2/${filename1} + [ -f ${basedir}/sd1/sd2/${filename1} ] || \ + echo "Exact nested file lookup failed." + [ -f ${basedir}/sd1/SD2/${filename1} ] || \ + echo "Nested file lookup failed." + [ -f ${basedir}/sd1/SD2/${filename2} ] && \ + echo "Wrong file lookup passed, should have fail." +} + +test_nesting_sensitive_insensitive_tree_complex() +{ + # Test nested-directories + local basedir=${SCRATCH_MNT}/nesting + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + mkdir ${basedir}/nd1 + _casefold_set_attr ${basedir}/nd1 + mkdir ${basedir}/nd1/nd2 + _casefold_unset_attr ${basedir}/nd1/nd2 + mkdir ${basedir}/nd1/nd2/nd3 + _casefold_set_attr ${basedir}/nd1/nd2/nd3 + mkdir ${basedir}/nd1/nd2/nd3/nd4 + _casefold_unset_attr ${basedir}/nd1/nd2/nd3/nd4 + mkdir ${basedir}/nd1/nd2/nd3/nd4/nd5 + _casefold_set_attr ${basedir}/nd1/nd2/nd3/nd4/nd5 + + [ -d ${basedir}/ND1/ND2/nd3/ND4/nd5 ] || \ + echo "Nest-dir Lookup failed." + [ -d ${basedir}/nd1/nd2/nd3/nd4/ND5 ] && \ + echo "ND5: Nest-dir Lookup passed, it should fail." + [ -d ${basedir}/nd1/nd2/nd3/ND4/nd5 ] || \ + echo "Nest-dir Lookup failed." + [ -d ${basedir}/nd1/nd2/ND3/nd4/ND5 ] && \ + echo "ND3: Nest-dir Lookup passed, it should fail." +} + +test_symlink_with_inexact_name() +{ + local basedir=${SCRATCH_MNT}/symlink + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + mkdir ${basedir}/ind1 + mkdir ${basedir}/ind2 + _casefold_set_attr ${basedir}/ind1 + touch ${basedir}/ind1/target + + ln -s ${basedir}/ind1/TARGET ${basedir}/ind2/link + [ -L ${basedir}/ind2/link ] || echo "Not a symlink." + readlink -e ${basedir}/ind2/link | _filter_scratch +} + +do_test_name_preserve() +{ + local basedir=${1} + local exact=${2} + local casefold=${3} + + touch ${basedir}/${exact} + rm ${basedir}/${exact} + + touch ${basedir}/${casefold} + _casefold_check_exact_name ${basedir} ${casefold} || + echo "${casefold} was not created with exact name" +} + +# Name-preserving tests +# We create a file with a name, delete it and create again with an +# equivalent name. If the negative dentry wasn't invalidated, the +# file might be created using $1 instead of $2. +test_name_preserve() +{ + local basedir=${SCRATCH_MNT}/test_name_preserve + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + do_test_name_preserve "${basedir}" "${pt_file1}" "${pt_file2}" + do_test_name_preserve "${basedir}" "${jp_file1}" "${jp_file2}" + do_test_name_preserve "${basedir}" "${ar_file1}" "${ar_file2}" + do_test_name_preserve "${basedir}" "${fr_file1}" "${fr_file2}" +} + +do_test_dir_name_preserve() +{ + local basedir=${1} + local exact=${2} + local casefold=${3} + + mkdir ${basedir}/${exact} + rmdir ${basedir}/${exact} + + mkdir ${basedir}/${casefold} + _casefold_check_exact_name ${basedir} ${casefold} || + echo "${casefold} was not created with exact name" +} + +test_dir_name_preserve() +{ + local basedir=${SCRATCH_MNT}/"dir-test_name_preserve" + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + do_test_dir_name_preserve "${basedir}" "${pt_file1}" "${pt_file2}" + do_test_dir_name_preserve "${basedir}" "${jp_file1}" "${jp_file2}" + do_test_dir_name_preserve "${basedir}" "${ar_file1}" "${ar_file2}" + do_test_dir_name_preserve "${basedir}" "${fr_file1}" "${fr_file2}" +} + +test_name_reuse() +{ + local basedir=${SCRATCH_MNT}/reuse + local reuse1=fileX + local reuse2=FILEX + + mkdir ${basedir} + _casefold_set_attr ${basedir} + + touch ${basedir}/${reuse1} + rm -f ${basedir}/${reuse1} || echo "File lookup failed." + touch ${basedir}/${reuse2} + _casefold_check_exact_name "${basedir}" "${reuse2}" || \ + echo "File created with wrong name" + _casefold_check_exact_name "${basedir}" "${reuse1}" && \ + echo "File created with the old name" +} + +test_create_with_same_name() +{ + local basedir=${SCRATCH_MNT}/same_name + + mkdir ${basedir} + _casefold_set_attr ${basedir} + + mkdir -p ${basedir}/same1/same1 + touch ${basedir}/SAME1/sAME1/sAMe1 + touch -c ${basedir}/SAME1/sAME1/same1 || + echo "Would create a new file instead of using old one" +} + +test_file_rename() +{ + local basedir=${SCRATCH_MNT}/rename + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + # Move to an equivalent name should not work + mv ${basedir}/rename ${basedir}/rename 2>&1 | \ + _filter_scratch + + _casefold_check_exact_name ${basedir} "rename" || \ + echo "Name shouldn't change." +} + +# Test openfd with casefold. +# 1. Delete a file after gettings its fd. +# 2. Then create new dir with same name +test_casefold_openfd() +{ + local basedir=${SCRATCH_MNT}/openfd + local ofd1="openfd" + local ofd2="OPENFD" + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + exec 3<> ${basedir}/${ofd1} + rm -rf ${basedir}/${ofd1} + mkdir ${basedir}/${ofd2} + [ -d ${basedir}/${ofd2} ] || echo "Not a directory" + _casefold_check_exact_name ${basedir} "${ofd2}" || + echo "openfd file was created using old name" + rm -rf ${basedir}/${ofd2} + exec 3>&- +} + +# Test openfd with casefold. +# 1. Delete a file after gettings its fd. +# 2. Then create new file with same name +# 3. Read from open-fd and write into new file. +test_casefold_openfd2() +{ + local basedir=${SCRATCH_MNT}/openfd2 + local ofd1="openfd" + local ofd2="OPENFD" + + mkdir ${basedir} + _casefold_set_attr ${basedir} + + date > ${basedir}/${ofd1} + exec 3<> ${basedir}/${ofd1} + rm -rf ${basedir}/${ofd1} + touch ${basedir}/${ofd1} + [ -f ${basedir}/${ofd2} ] || echo "Not a file" + read data <&3 + echo $data >> ${basedir}/${ofd1} + exec 3>&- +} + +test_hard_link_lookups() +{ + local basedir=${SCRATCH_MNT}/hard_link + + mkdir ${basedir} + _casefold_set_attr ${basedir} + + touch ${basedir}/h1 + ln ${basedir}/H1 ${SCRATCH_MNT}/h1 + cnt=`stat -c %h ${basedir}/h1` + [ $cnt -eq 1 ] && echo "Unable to create hardlink" + + # Create hardlink for casefold dir file and inside regular dir. + touch ${SCRATCH_MNT}/h2 + ln ${SCRATCH_MNT}/h2 ${basedir}/H2 + cnt=`stat -c %h ${basedir}/h2` + [ $cnt -eq 1 ] && echo "Unable to create hardlink" +} + +test_xattrs_lookups() +{ + local basedir=${SCRATCH_MNT}/xattrs + + mkdir ${basedir} + _casefold_set_attr ${basedir} + + mkdir -p ${basedir}/x + + ${SETFATTR_PROG} -n user.foo -v bar ${basedir}/x + ${GETFATTR_PROG} --absolute-names -n user.foo \ + ${basedir}/x | _filter_scratch + + touch ${basedir}/x/f1 + ${SETFATTR_PROG} -n user.foo -v bar ${basedir}/x/f1 + ${GETFATTR_PROG} --absolute-names -n user.foo \ + ${basedir}/x/f1 | _filter_scratch +} + +test_lookup_large_directory() +{ + local basedir=${SCRATCH_MNT}/large + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + touch $(seq -f "${basedir}/file%g" 0 2000) + + # We really want to spawn a single process here, to speed up the + # test, but we don't want the output of 2k files, except for + # errors. + cat $(seq -f "${basedir}/FILE%g" 0 2000) || \ + echo "Case on large dir failed" +} + +test_strict_mode_invalid_filename() +{ + local basedir=${SCRATCH_MNT}/strict + + mkdir -p ${basedir} + _casefold_set_attr ${basedir} + + # These creation commands should fail, since we are on strict + # mode. + touch "${basedir}/${blob_file1}" 2>&1 | _filter_scratch + touch "${basedir}/${blob_file2}" 2>&1 | _filter_scratch +} + +############# +# Run tests # +############# + +_scratch_mkfs_casefold >>$seqres.full 2>&1 + +_scratch_mount + +_check_dmesg_for \ + "\(${sdev}\): Using encoding defined by superblock: utf8" || \ + _fail "Could not mount with encoding: utf8" + +test_casefold_flag_basic +test_casefold_lookup +test_bad_casefold_lookup +test_create_and_remove +test_casefold_flag_removal +test_casefold_flag_inheritance +test_nesting_sensitive_insensitive_tree_simple +test_nesting_sensitive_insensitive_tree_complex +test_symlink_with_inexact_name +test_name_preserve +test_dir_name_preserve +test_name_reuse +test_create_with_same_name +test_file_rename +test_casefold_openfd +test_casefold_openfd2 +test_hard_link_lookups +test_xattrs_lookups +test_lookup_large_directory + +_scratch_unmount +_check_scratch_fs + +# Test Strict Mode +_scratch_mkfs_casefold_strict >>$seqres.full 2>&1 +_scratch_mount + +test_strict_mode_invalid_filename + +_scratch_unmount +_check_scratch_fs + +status=0 +exit diff --git a/tests/shared/012.out b/tests/shared/012.out new file mode 100644 index 000000000000..2951be901f7c --- /dev/null +++ b/tests/shared/012.out @@ -0,0 +1,16 @@ +QA output created by 012 +SCRATCH_MNT/basic Extents, Casefold +SCRATCH_MNT/basic Extents +SCRATCH_MNT/casefold_flag_removal Extents, Casefold +SCRATCH_MNT/casefold_flag_removal Extents, Casefold +SCRATCH_MNT/flag_inheritance/d1/d2/d3 Extents, Casefold +SCRATCH_MNT/symlink/ind1/TARGET +mv: cannot stat 'SCRATCH_MNT/rename/rename': No such file or directory +# file: SCRATCH_MNT/xattrs/x +user.foo="bar" + +# file: SCRATCH_MNT/xattrs/x/f1 +user.foo="bar" + +touch: setting times of 'SCRATCH_MNT/strict/corac'$'\314\247\303': Invalid argument +touch: setting times of 'SCRATCH_MNT/strict/cora'$'\303\247\303': Invalid argument diff --git a/tests/shared/group b/tests/shared/group index b091d9111359..53b4a356695d 100644 --- a/tests/shared/group +++ b/tests/shared/group @@ -14,6 +14,7 @@ 009 auto stress dedupe 010 auto stress dedupe 011 auto quick +012 auto quick casefold 032 mkfs auto quick 272 auto enospc rw 289 auto quick -- 2.20.1