| # SPDX-License-Identifier: GPL-2.0+ |
| #!/bin/bash |
| # FS QA Test No. 556 |
| # |
| # Test the basic functionality of filesystems with case-insensitive |
| # support. |
| |
| . ./common/preamble |
| _begin_fstest auto quick casefold |
| |
| . ./common/filter |
| . ./common/casefold |
| . ./common/attr |
| |
| _supported_fs generic |
| _require_scratch_nocheck |
| _require_scratch_casefold |
| _require_symlinks |
| _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} |
| |
| touch ${basedir}/rename |
| |
| # 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 |