1*3147cac6SGuan-Chun Wu // SPDX-License-Identifier: GPL-2.0 2*3147cac6SGuan-Chun Wu /* 3*3147cac6SGuan-Chun Wu * KUnit tests for ext4 directory hash computation. 4*3147cac6SGuan-Chun Wu */ 5*3147cac6SGuan-Chun Wu 6*3147cac6SGuan-Chun Wu #include <kunit/test.h> 7*3147cac6SGuan-Chun Wu #include <kunit/resource.h> 8*3147cac6SGuan-Chun Wu #include <linux/fs.h> 9*3147cac6SGuan-Chun Wu #include <linux/stddef.h> 10*3147cac6SGuan-Chun Wu #include <linux/string.h> 11*3147cac6SGuan-Chun Wu #include <linux/unicode.h> 12*3147cac6SGuan-Chun Wu #include "ext4.h" 13*3147cac6SGuan-Chun Wu 14*3147cac6SGuan-Chun Wu static void ext4_hash_init_fake_dir(struct inode *dir, struct super_block *sb) 15*3147cac6SGuan-Chun Wu { 16*3147cac6SGuan-Chun Wu memset(sb, 0, sizeof(*sb)); 17*3147cac6SGuan-Chun Wu memset(dir, 0, sizeof(*dir)); 18*3147cac6SGuan-Chun Wu dir->i_sb = sb; 19*3147cac6SGuan-Chun Wu strscpy(sb->s_id, "kunit-ext4", sizeof(sb->s_id)); 20*3147cac6SGuan-Chun Wu } 21*3147cac6SGuan-Chun Wu 22*3147cac6SGuan-Chun Wu static void ext4_hash_init_fake_dir_with_sbi(struct inode *dir, 23*3147cac6SGuan-Chun Wu struct super_block *sb, 24*3147cac6SGuan-Chun Wu struct ext4_sb_info *sbi) 25*3147cac6SGuan-Chun Wu { 26*3147cac6SGuan-Chun Wu ext4_hash_init_fake_dir(dir, sb); 27*3147cac6SGuan-Chun Wu memset(sbi, 0, sizeof(*sbi)); 28*3147cac6SGuan-Chun Wu sb->s_fs_info = sbi; 29*3147cac6SGuan-Chun Wu sbi->s_sb = sb; 30*3147cac6SGuan-Chun Wu } 31*3147cac6SGuan-Chun Wu 32*3147cac6SGuan-Chun Wu #ifdef CONFIG_FS_ENCRYPTION 33*3147cac6SGuan-Chun Wu static const struct fscrypt_operations ext4_hash_test_cryptops = { 34*3147cac6SGuan-Chun Wu .inode_info_offs = 35*3147cac6SGuan-Chun Wu (int)offsetof(struct ext4_inode_info, i_crypt_info) - 36*3147cac6SGuan-Chun Wu (int)offsetof(struct ext4_inode_info, vfs_inode), 37*3147cac6SGuan-Chun Wu }; 38*3147cac6SGuan-Chun Wu #endif 39*3147cac6SGuan-Chun Wu 40*3147cac6SGuan-Chun Wu static void ext4_hash_init_fake_ext4_dir(struct ext4_inode_info *ei, 41*3147cac6SGuan-Chun Wu struct super_block *sb, 42*3147cac6SGuan-Chun Wu struct ext4_sb_info *sbi) 43*3147cac6SGuan-Chun Wu { 44*3147cac6SGuan-Chun Wu struct inode *dir = &ei->vfs_inode; 45*3147cac6SGuan-Chun Wu 46*3147cac6SGuan-Chun Wu memset(sb, 0, sizeof(*sb)); 47*3147cac6SGuan-Chun Wu memset(ei, 0, sizeof(*ei)); 48*3147cac6SGuan-Chun Wu memset(sbi, 0, sizeof(*sbi)); 49*3147cac6SGuan-Chun Wu 50*3147cac6SGuan-Chun Wu strscpy(sb->s_id, "kunit-ext4", sizeof(sb->s_id)); 51*3147cac6SGuan-Chun Wu sb->s_fs_info = sbi; 52*3147cac6SGuan-Chun Wu sbi->s_sb = sb; 53*3147cac6SGuan-Chun Wu 54*3147cac6SGuan-Chun Wu dir->i_sb = sb; 55*3147cac6SGuan-Chun Wu dir->i_mode = S_IFDIR; 56*3147cac6SGuan-Chun Wu 57*3147cac6SGuan-Chun Wu #ifdef CONFIG_FS_ENCRYPTION 58*3147cac6SGuan-Chun Wu fscrypt_set_ops(sb, &ext4_hash_test_cryptops); 59*3147cac6SGuan-Chun Wu #endif 60*3147cac6SGuan-Chun Wu } 61*3147cac6SGuan-Chun Wu 62*3147cac6SGuan-Chun Wu struct ext4_dirhash_test_case { 63*3147cac6SGuan-Chun Wu const char *name; 64*3147cac6SGuan-Chun Wu u32 hash_version; 65*3147cac6SGuan-Chun Wu const char *input; 66*3147cac6SGuan-Chun Wu int len; 67*3147cac6SGuan-Chun Wu u32 seed[4]; 68*3147cac6SGuan-Chun Wu bool use_seed; 69*3147cac6SGuan-Chun Wu u32 expected_hash; 70*3147cac6SGuan-Chun Wu u32 expected_minor_hash; 71*3147cac6SGuan-Chun Wu }; 72*3147cac6SGuan-Chun Wu 73*3147cac6SGuan-Chun Wu static const struct ext4_dirhash_test_case ext4_dirhash_test_cases[] = { 74*3147cac6SGuan-Chun Wu { 75*3147cac6SGuan-Chun Wu .name = "legacy_abc", 76*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_LEGACY, 77*3147cac6SGuan-Chun Wu .input = "abc", 78*3147cac6SGuan-Chun Wu .len = 3, 79*3147cac6SGuan-Chun Wu .use_seed = false, 80*3147cac6SGuan-Chun Wu .expected_hash = 0x75afd992, 81*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x00000000, 82*3147cac6SGuan-Chun Wu }, 83*3147cac6SGuan-Chun Wu { 84*3147cac6SGuan-Chun Wu .name = "legacy_unsigned_abc", 85*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_LEGACY_UNSIGNED, 86*3147cac6SGuan-Chun Wu .input = "abc", 87*3147cac6SGuan-Chun Wu .len = 3, 88*3147cac6SGuan-Chun Wu .use_seed = false, 89*3147cac6SGuan-Chun Wu .expected_hash = 0x75afd992, 90*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x00000000, 91*3147cac6SGuan-Chun Wu }, 92*3147cac6SGuan-Chun Wu { 93*3147cac6SGuan-Chun Wu .name = "half_md4_abc", 94*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 95*3147cac6SGuan-Chun Wu .input = "abc", 96*3147cac6SGuan-Chun Wu .len = 3, 97*3147cac6SGuan-Chun Wu .use_seed = false, 98*3147cac6SGuan-Chun Wu .expected_hash = 0xd196a868, 99*3147cac6SGuan-Chun Wu .expected_minor_hash = 0xc420eb28, 100*3147cac6SGuan-Chun Wu }, 101*3147cac6SGuan-Chun Wu { 102*3147cac6SGuan-Chun Wu .name = "half_md4_unsigned_abc", 103*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4_UNSIGNED, 104*3147cac6SGuan-Chun Wu .input = "abc", 105*3147cac6SGuan-Chun Wu .len = 3, 106*3147cac6SGuan-Chun Wu .use_seed = false, 107*3147cac6SGuan-Chun Wu .expected_hash = 0xd196a868, 108*3147cac6SGuan-Chun Wu .expected_minor_hash = 0xc420eb28, 109*3147cac6SGuan-Chun Wu }, 110*3147cac6SGuan-Chun Wu { 111*3147cac6SGuan-Chun Wu .name = "tea_abc", 112*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA, 113*3147cac6SGuan-Chun Wu .input = "abc", 114*3147cac6SGuan-Chun Wu .len = 3, 115*3147cac6SGuan-Chun Wu .use_seed = false, 116*3147cac6SGuan-Chun Wu .expected_hash = 0xb1435ec4, 117*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x3f7eaa0e, 118*3147cac6SGuan-Chun Wu }, 119*3147cac6SGuan-Chun Wu { 120*3147cac6SGuan-Chun Wu .name = "tea_unsigned_abc", 121*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA_UNSIGNED, 122*3147cac6SGuan-Chun Wu .input = "abc", 123*3147cac6SGuan-Chun Wu .len = 3, 124*3147cac6SGuan-Chun Wu .use_seed = false, 125*3147cac6SGuan-Chun Wu .expected_hash = 0xb1435ec4, 126*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x3f7eaa0e, 127*3147cac6SGuan-Chun Wu }, 128*3147cac6SGuan-Chun Wu { 129*3147cac6SGuan-Chun Wu .name = "empty_half_md4", 130*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 131*3147cac6SGuan-Chun Wu .input = "", 132*3147cac6SGuan-Chun Wu .len = 0, 133*3147cac6SGuan-Chun Wu .use_seed = false, 134*3147cac6SGuan-Chun Wu .expected_hash = 0xefcdab88, 135*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x98badcfe, 136*3147cac6SGuan-Chun Wu }, 137*3147cac6SGuan-Chun Wu { 138*3147cac6SGuan-Chun Wu .name = "half_md4_31bytes", 139*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 140*3147cac6SGuan-Chun Wu .input = "1234567890123456789012345678901", 141*3147cac6SGuan-Chun Wu .len = 31, 142*3147cac6SGuan-Chun Wu .use_seed = false, 143*3147cac6SGuan-Chun Wu .expected_hash = 0xc4db1f78, 144*3147cac6SGuan-Chun Wu .expected_minor_hash = 0xea23921b, 145*3147cac6SGuan-Chun Wu }, 146*3147cac6SGuan-Chun Wu { 147*3147cac6SGuan-Chun Wu .name = "half_md4_32bytes", 148*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 149*3147cac6SGuan-Chun Wu .input = "12345678901234567890123456789012", 150*3147cac6SGuan-Chun Wu .len = 32, 151*3147cac6SGuan-Chun Wu .use_seed = false, 152*3147cac6SGuan-Chun Wu .expected_hash = 0xfa6cc63e, 153*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x2f77bd1c, 154*3147cac6SGuan-Chun Wu }, 155*3147cac6SGuan-Chun Wu { 156*3147cac6SGuan-Chun Wu .name = "half_md4_33bytes", 157*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 158*3147cac6SGuan-Chun Wu .input = "123456789012345678901234567890123", 159*3147cac6SGuan-Chun Wu .len = 33, 160*3147cac6SGuan-Chun Wu .use_seed = false, 161*3147cac6SGuan-Chun Wu .expected_hash = 0xdc0c2dec, 162*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x5ca23365, 163*3147cac6SGuan-Chun Wu }, 164*3147cac6SGuan-Chun Wu { 165*3147cac6SGuan-Chun Wu .name = "half_md4_unsigned_31bytes", 166*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4_UNSIGNED, 167*3147cac6SGuan-Chun Wu .input = "1234567890123456789012345678901", 168*3147cac6SGuan-Chun Wu .len = 31, 169*3147cac6SGuan-Chun Wu .use_seed = false, 170*3147cac6SGuan-Chun Wu .expected_hash = 0xc4db1f78, 171*3147cac6SGuan-Chun Wu .expected_minor_hash = 0xea23921b, 172*3147cac6SGuan-Chun Wu }, 173*3147cac6SGuan-Chun Wu { 174*3147cac6SGuan-Chun Wu .name = "half_md4_unsigned_32bytes", 175*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4_UNSIGNED, 176*3147cac6SGuan-Chun Wu .input = "12345678901234567890123456789012", 177*3147cac6SGuan-Chun Wu .len = 32, 178*3147cac6SGuan-Chun Wu .use_seed = false, 179*3147cac6SGuan-Chun Wu .expected_hash = 0xfa6cc63e, 180*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x2f77bd1c, 181*3147cac6SGuan-Chun Wu }, 182*3147cac6SGuan-Chun Wu { 183*3147cac6SGuan-Chun Wu .name = "half_md4_unsigned_33bytes", 184*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4_UNSIGNED, 185*3147cac6SGuan-Chun Wu .input = "123456789012345678901234567890123", 186*3147cac6SGuan-Chun Wu .len = 33, 187*3147cac6SGuan-Chun Wu .use_seed = false, 188*3147cac6SGuan-Chun Wu .expected_hash = 0xdc0c2dec, 189*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x5ca23365, 190*3147cac6SGuan-Chun Wu }, 191*3147cac6SGuan-Chun Wu { 192*3147cac6SGuan-Chun Wu .name = "tea_15bytes", 193*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA, 194*3147cac6SGuan-Chun Wu .input = "123456789abcdef", 195*3147cac6SGuan-Chun Wu .len = 15, 196*3147cac6SGuan-Chun Wu .use_seed = false, 197*3147cac6SGuan-Chun Wu .expected_hash = 0xa562903a, 198*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x6174a00f, 199*3147cac6SGuan-Chun Wu }, 200*3147cac6SGuan-Chun Wu { 201*3147cac6SGuan-Chun Wu .name = "tea_16bytes", 202*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA, 203*3147cac6SGuan-Chun Wu .input = "1234567890abcdef", 204*3147cac6SGuan-Chun Wu .len = 16, 205*3147cac6SGuan-Chun Wu .use_seed = false, 206*3147cac6SGuan-Chun Wu .expected_hash = 0x8449f258, 207*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x49a16d46, 208*3147cac6SGuan-Chun Wu }, 209*3147cac6SGuan-Chun Wu { 210*3147cac6SGuan-Chun Wu .name = "tea_17bytes", 211*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA, 212*3147cac6SGuan-Chun Wu .input = "123456789abcdefgh", 213*3147cac6SGuan-Chun Wu .len = 17, 214*3147cac6SGuan-Chun Wu .use_seed = false, 215*3147cac6SGuan-Chun Wu .expected_hash = 0xf32ec10c, 216*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x58ceae61, 217*3147cac6SGuan-Chun Wu }, 218*3147cac6SGuan-Chun Wu { 219*3147cac6SGuan-Chun Wu .name = "half_md4_seeded", 220*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 221*3147cac6SGuan-Chun Wu .input = "same-name", 222*3147cac6SGuan-Chun Wu .len = 9, 223*3147cac6SGuan-Chun Wu .seed = { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }, 224*3147cac6SGuan-Chun Wu .use_seed = true, 225*3147cac6SGuan-Chun Wu .expected_hash = 0x8aebf604, 226*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x66ce48fe, 227*3147cac6SGuan-Chun Wu }, 228*3147cac6SGuan-Chun Wu { 229*3147cac6SGuan-Chun Wu .name = "half_md4_non_ascii_signed", 230*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 231*3147cac6SGuan-Chun Wu .input = "\x80\x81\x82\x83\x84", 232*3147cac6SGuan-Chun Wu .len = 5, 233*3147cac6SGuan-Chun Wu .use_seed = false, 234*3147cac6SGuan-Chun Wu .expected_hash = 0x8bab0498, 235*3147cac6SGuan-Chun Wu .expected_minor_hash = 0xc326632d, 236*3147cac6SGuan-Chun Wu }, 237*3147cac6SGuan-Chun Wu { 238*3147cac6SGuan-Chun Wu .name = "half_md4_non_ascii_unsigned", 239*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4_UNSIGNED, 240*3147cac6SGuan-Chun Wu .input = "\x80\x81\x82\x83\x84", 241*3147cac6SGuan-Chun Wu .len = 5, 242*3147cac6SGuan-Chun Wu .use_seed = false, 243*3147cac6SGuan-Chun Wu .expected_hash = 0xbc48596e, 244*3147cac6SGuan-Chun Wu .expected_minor_hash = 0xde0fad41, 245*3147cac6SGuan-Chun Wu }, 246*3147cac6SGuan-Chun Wu { 247*3147cac6SGuan-Chun Wu .name = "tea_non_ascii_signed", 248*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA, 249*3147cac6SGuan-Chun Wu .input = "\x80\x81\x82\x83\x84", 250*3147cac6SGuan-Chun Wu .len = 5, 251*3147cac6SGuan-Chun Wu .use_seed = false, 252*3147cac6SGuan-Chun Wu .expected_hash = 0x21e3a154, 253*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x90112c3d, 254*3147cac6SGuan-Chun Wu }, 255*3147cac6SGuan-Chun Wu { 256*3147cac6SGuan-Chun Wu .name = "tea_non_ascii_unsigned", 257*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA_UNSIGNED, 258*3147cac6SGuan-Chun Wu .input = "\x80\x81\x82\x83\x84", 259*3147cac6SGuan-Chun Wu .len = 5, 260*3147cac6SGuan-Chun Wu .use_seed = false, 261*3147cac6SGuan-Chun Wu .expected_hash = 0x9b648616, 262*3147cac6SGuan-Chun Wu .expected_minor_hash = 0x011dd507, 263*3147cac6SGuan-Chun Wu }, 264*3147cac6SGuan-Chun Wu }; 265*3147cac6SGuan-Chun Wu 266*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_vectors(struct kunit *test) 267*3147cac6SGuan-Chun Wu { 268*3147cac6SGuan-Chun Wu struct super_block *sb; 269*3147cac6SGuan-Chun Wu struct inode *dir; 270*3147cac6SGuan-Chun Wu int i; 271*3147cac6SGuan-Chun Wu 272*3147cac6SGuan-Chun Wu sb = kunit_kzalloc(test, sizeof(*sb), GFP_KERNEL); 273*3147cac6SGuan-Chun Wu dir = kunit_kzalloc(test, sizeof(*dir), GFP_KERNEL); 274*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb); 275*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, dir); 276*3147cac6SGuan-Chun Wu 277*3147cac6SGuan-Chun Wu ext4_hash_init_fake_dir(dir, sb); 278*3147cac6SGuan-Chun Wu 279*3147cac6SGuan-Chun Wu for (i = 0; i < ARRAY_SIZE(ext4_dirhash_test_cases); i++) { 280*3147cac6SGuan-Chun Wu const struct ext4_dirhash_test_case *tc = 281*3147cac6SGuan-Chun Wu &ext4_dirhash_test_cases[i]; 282*3147cac6SGuan-Chun Wu struct dx_hash_info hinfo; 283*3147cac6SGuan-Chun Wu int ret; 284*3147cac6SGuan-Chun Wu 285*3147cac6SGuan-Chun Wu memset(&hinfo, 0, sizeof(hinfo)); 286*3147cac6SGuan-Chun Wu hinfo.hash_version = tc->hash_version; 287*3147cac6SGuan-Chun Wu hinfo.seed = tc->use_seed ? (u32 *)tc->seed : NULL; 288*3147cac6SGuan-Chun Wu 289*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, tc->input, tc->len, &hinfo); 290*3147cac6SGuan-Chun Wu 291*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ_MSG(test, ret, 0, "case=%s", tc->name); 292*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ_MSG(test, hinfo.hash, tc->expected_hash, 293*3147cac6SGuan-Chun Wu "case=%s", tc->name); 294*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ_MSG(test, hinfo.minor_hash, 295*3147cac6SGuan-Chun Wu tc->expected_minor_hash, 296*3147cac6SGuan-Chun Wu "case=%s", tc->name); 297*3147cac6SGuan-Chun Wu } 298*3147cac6SGuan-Chun Wu } 299*3147cac6SGuan-Chun Wu 300*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_seed_changes_result(struct kunit *test) 301*3147cac6SGuan-Chun Wu { 302*3147cac6SGuan-Chun Wu struct super_block *sb; 303*3147cac6SGuan-Chun Wu struct inode *dir; 304*3147cac6SGuan-Chun Wu u32 seed[4] = { 0x11111111, 0x22222222, 0x33333333, 0x44444444 }; 305*3147cac6SGuan-Chun Wu struct dx_hash_info plain = { 306*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 307*3147cac6SGuan-Chun Wu }; 308*3147cac6SGuan-Chun Wu struct dx_hash_info seeded = { 309*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 310*3147cac6SGuan-Chun Wu .seed = seed, 311*3147cac6SGuan-Chun Wu }; 312*3147cac6SGuan-Chun Wu int ret_plain, ret_seeded; 313*3147cac6SGuan-Chun Wu 314*3147cac6SGuan-Chun Wu sb = kunit_kzalloc(test, sizeof(*sb), GFP_KERNEL); 315*3147cac6SGuan-Chun Wu dir = kunit_kzalloc(test, sizeof(*dir), GFP_KERNEL); 316*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb); 317*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, dir); 318*3147cac6SGuan-Chun Wu 319*3147cac6SGuan-Chun Wu ext4_hash_init_fake_dir(dir, sb); 320*3147cac6SGuan-Chun Wu 321*3147cac6SGuan-Chun Wu ret_plain = ext4fs_dirhash(dir, "same-name", 9, &plain); 322*3147cac6SGuan-Chun Wu ret_seeded = ext4fs_dirhash(dir, "same-name", 9, &seeded); 323*3147cac6SGuan-Chun Wu 324*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret_plain, 0); 325*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret_seeded, 0); 326*3147cac6SGuan-Chun Wu 327*3147cac6SGuan-Chun Wu KUNIT_EXPECT_TRUE(test, 328*3147cac6SGuan-Chun Wu plain.hash != seeded.hash || 329*3147cac6SGuan-Chun Wu plain.minor_hash != seeded.minor_hash); 330*3147cac6SGuan-Chun Wu } 331*3147cac6SGuan-Chun Wu 332*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_invalid_version_returns_einval(struct kunit *test) 333*3147cac6SGuan-Chun Wu { 334*3147cac6SGuan-Chun Wu struct super_block *sb; 335*3147cac6SGuan-Chun Wu struct inode *dir; 336*3147cac6SGuan-Chun Wu struct ext4_sb_info *sbi; 337*3147cac6SGuan-Chun Wu struct dx_hash_info hinfo = { 338*3147cac6SGuan-Chun Wu .hash = 0xdeadbeef, 339*3147cac6SGuan-Chun Wu .minor_hash = 0xcafebabe, 340*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_LAST + 1, 341*3147cac6SGuan-Chun Wu }; 342*3147cac6SGuan-Chun Wu int ret; 343*3147cac6SGuan-Chun Wu 344*3147cac6SGuan-Chun Wu sb = kunit_kzalloc(test, sizeof(*sb), GFP_KERNEL); 345*3147cac6SGuan-Chun Wu dir = kunit_kzalloc(test, sizeof(*dir), GFP_KERNEL); 346*3147cac6SGuan-Chun Wu sbi = kunit_kzalloc(test, sizeof(*sbi), GFP_KERNEL); 347*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb); 348*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, dir); 349*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sbi); 350*3147cac6SGuan-Chun Wu 351*3147cac6SGuan-Chun Wu ext4_hash_init_fake_dir_with_sbi(dir, sb, sbi); 352*3147cac6SGuan-Chun Wu 353*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, "abc", 3, &hinfo); 354*3147cac6SGuan-Chun Wu 355*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, ret, -EINVAL); 356*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, hinfo.hash, 0); 357*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, hinfo.minor_hash, 0); 358*3147cac6SGuan-Chun Wu } 359*3147cac6SGuan-Chun Wu 360*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_siphash_without_key_returns_einval(struct kunit *test) 361*3147cac6SGuan-Chun Wu { 362*3147cac6SGuan-Chun Wu struct super_block *sb; 363*3147cac6SGuan-Chun Wu struct ext4_inode_info *ei; 364*3147cac6SGuan-Chun Wu struct inode *dir; 365*3147cac6SGuan-Chun Wu struct ext4_sb_info *sbi; 366*3147cac6SGuan-Chun Wu struct dx_hash_info hinfo = { 367*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_SIPHASH, 368*3147cac6SGuan-Chun Wu }; 369*3147cac6SGuan-Chun Wu int ret; 370*3147cac6SGuan-Chun Wu 371*3147cac6SGuan-Chun Wu sb = kunit_kzalloc(test, sizeof(*sb), GFP_KERNEL); 372*3147cac6SGuan-Chun Wu ei = kunit_kzalloc(test, sizeof(*ei), GFP_KERNEL); 373*3147cac6SGuan-Chun Wu sbi = kunit_kzalloc(test, sizeof(*sbi), GFP_KERNEL); 374*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb); 375*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, ei); 376*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sbi); 377*3147cac6SGuan-Chun Wu 378*3147cac6SGuan-Chun Wu ext4_hash_init_fake_ext4_dir(ei, sb, sbi); 379*3147cac6SGuan-Chun Wu dir = &ei->vfs_inode; 380*3147cac6SGuan-Chun Wu 381*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, "name", strlen("name"), &hinfo); 382*3147cac6SGuan-Chun Wu 383*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, ret, -EINVAL); 384*3147cac6SGuan-Chun Wu } 385*3147cac6SGuan-Chun Wu 386*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_signed_unsigned_differ_on_nonascii(struct kunit *test) 387*3147cac6SGuan-Chun Wu { 388*3147cac6SGuan-Chun Wu struct super_block *sb; 389*3147cac6SGuan-Chun Wu struct inode *dir; 390*3147cac6SGuan-Chun Wu static const char input[] = "\x80\xff\x81\xfe\101bc"; 391*3147cac6SGuan-Chun Wu struct dx_hash_info legacy_signed = { 392*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_LEGACY, 393*3147cac6SGuan-Chun Wu }; 394*3147cac6SGuan-Chun Wu struct dx_hash_info legacy_unsigned = { 395*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_LEGACY_UNSIGNED, 396*3147cac6SGuan-Chun Wu }; 397*3147cac6SGuan-Chun Wu struct dx_hash_info md4_signed = { 398*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 399*3147cac6SGuan-Chun Wu }; 400*3147cac6SGuan-Chun Wu struct dx_hash_info md4_unsigned = { 401*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4_UNSIGNED, 402*3147cac6SGuan-Chun Wu }; 403*3147cac6SGuan-Chun Wu struct dx_hash_info tea_signed = { 404*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA, 405*3147cac6SGuan-Chun Wu }; 406*3147cac6SGuan-Chun Wu struct dx_hash_info tea_unsigned = { 407*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_TEA_UNSIGNED, 408*3147cac6SGuan-Chun Wu }; 409*3147cac6SGuan-Chun Wu int ret; 410*3147cac6SGuan-Chun Wu 411*3147cac6SGuan-Chun Wu sb = kunit_kzalloc(test, sizeof(*sb), GFP_KERNEL); 412*3147cac6SGuan-Chun Wu dir = kunit_kzalloc(test, sizeof(*dir), GFP_KERNEL); 413*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb); 414*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, dir); 415*3147cac6SGuan-Chun Wu 416*3147cac6SGuan-Chun Wu ext4_hash_init_fake_dir(dir, sb); 417*3147cac6SGuan-Chun Wu 418*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, input, sizeof(input) - 1, &legacy_signed); 419*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 420*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, input, sizeof(input) - 1, &legacy_unsigned); 421*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 422*3147cac6SGuan-Chun Wu KUNIT_EXPECT_NE(test, legacy_signed.hash, legacy_unsigned.hash); 423*3147cac6SGuan-Chun Wu 424*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, input, sizeof(input) - 1, &md4_signed); 425*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 426*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, input, sizeof(input) - 1, &md4_unsigned); 427*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 428*3147cac6SGuan-Chun Wu KUNIT_EXPECT_TRUE(test, 429*3147cac6SGuan-Chun Wu md4_signed.hash != md4_unsigned.hash || 430*3147cac6SGuan-Chun Wu md4_signed.minor_hash != md4_unsigned.minor_hash); 431*3147cac6SGuan-Chun Wu 432*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, input, sizeof(input) - 1, &tea_signed); 433*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 434*3147cac6SGuan-Chun Wu ret = ext4fs_dirhash(dir, input, sizeof(input) - 1, &tea_unsigned); 435*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 436*3147cac6SGuan-Chun Wu KUNIT_EXPECT_TRUE(test, 437*3147cac6SGuan-Chun Wu tea_signed.hash != tea_unsigned.hash || 438*3147cac6SGuan-Chun Wu tea_signed.minor_hash != tea_unsigned.minor_hash); 439*3147cac6SGuan-Chun Wu } 440*3147cac6SGuan-Chun Wu 441*3147cac6SGuan-Chun Wu #if IS_ENABLED(CONFIG_UNICODE) 442*3147cac6SGuan-Chun Wu KUNIT_DEFINE_ACTION_WRAPPER(utf8_unload_action, utf8_unload, 443*3147cac6SGuan-Chun Wu struct unicode_map *); 444*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_casefolded_names_hash_consistently(struct kunit *test) 445*3147cac6SGuan-Chun Wu { 446*3147cac6SGuan-Chun Wu struct super_block *sb; 447*3147cac6SGuan-Chun Wu struct ext4_inode_info *ei; 448*3147cac6SGuan-Chun Wu struct ext4_sb_info *sbi; 449*3147cac6SGuan-Chun Wu struct unicode_map *um; 450*3147cac6SGuan-Chun Wu struct dx_hash_info h1 = { 451*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 452*3147cac6SGuan-Chun Wu }; 453*3147cac6SGuan-Chun Wu struct dx_hash_info h2 = { 454*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 455*3147cac6SGuan-Chun Wu }; 456*3147cac6SGuan-Chun Wu int ret, ret1, ret2; 457*3147cac6SGuan-Chun Wu 458*3147cac6SGuan-Chun Wu sb = kunit_kzalloc(test, sizeof(*sb), GFP_KERNEL); 459*3147cac6SGuan-Chun Wu ei = kunit_kzalloc(test, sizeof(*ei), GFP_KERNEL); 460*3147cac6SGuan-Chun Wu sbi = kunit_kzalloc(test, sizeof(*sbi), GFP_KERNEL); 461*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb); 462*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, ei); 463*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sbi); 464*3147cac6SGuan-Chun Wu 465*3147cac6SGuan-Chun Wu um = utf8_load(UTF8_LATEST); 466*3147cac6SGuan-Chun Wu if (IS_ERR(um)) { 467*3147cac6SGuan-Chun Wu kunit_skip(test, "utf8_load(UTF8_LATEST) failed: %pe", 468*3147cac6SGuan-Chun Wu um); 469*3147cac6SGuan-Chun Wu return; 470*3147cac6SGuan-Chun Wu } 471*3147cac6SGuan-Chun Wu 472*3147cac6SGuan-Chun Wu ret = kunit_add_action_or_reset(test, utf8_unload_action, um); 473*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 474*3147cac6SGuan-Chun Wu 475*3147cac6SGuan-Chun Wu ext4_hash_init_fake_ext4_dir(ei, sb, sbi); 476*3147cac6SGuan-Chun Wu sb->s_encoding = um; 477*3147cac6SGuan-Chun Wu ei->vfs_inode.i_flags |= S_CASEFOLD; 478*3147cac6SGuan-Chun Wu 479*3147cac6SGuan-Chun Wu KUNIT_ASSERT_TRUE(test, IS_CASEFOLDED(&ei->vfs_inode)); 480*3147cac6SGuan-Chun Wu 481*3147cac6SGuan-Chun Wu ret1 = ext4fs_dirhash(&ei->vfs_inode, "Alpha", 5, &h1); 482*3147cac6SGuan-Chun Wu ret2 = ext4fs_dirhash(&ei->vfs_inode, "aLPHa", 5, &h2); 483*3147cac6SGuan-Chun Wu 484*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret1, 0); 485*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret2, 0); 486*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, h1.hash, h2.hash); 487*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, h1.minor_hash, h2.minor_hash); 488*3147cac6SGuan-Chun Wu } 489*3147cac6SGuan-Chun Wu 490*3147cac6SGuan-Chun Wu static void test_ext4fs_dirhash_casefold_fallback(struct kunit *test) 491*3147cac6SGuan-Chun Wu { 492*3147cac6SGuan-Chun Wu struct super_block *sb_cf, *sb_plain; 493*3147cac6SGuan-Chun Wu struct ext4_inode_info *ei; 494*3147cac6SGuan-Chun Wu struct ext4_sb_info *sbi; 495*3147cac6SGuan-Chun Wu struct inode *plain_dir; 496*3147cac6SGuan-Chun Wu struct unicode_map *um; 497*3147cac6SGuan-Chun Wu static const char invalid_utf8[] = "\xc3\x28"; 498*3147cac6SGuan-Chun Wu struct dx_hash_info folded_dir = { 499*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 500*3147cac6SGuan-Chun Wu }; 501*3147cac6SGuan-Chun Wu struct dx_hash_info plain = { 502*3147cac6SGuan-Chun Wu .hash_version = DX_HASH_HALF_MD4, 503*3147cac6SGuan-Chun Wu }; 504*3147cac6SGuan-Chun Wu int ret, ret_cf, ret_plain; 505*3147cac6SGuan-Chun Wu 506*3147cac6SGuan-Chun Wu sb_cf = kunit_kzalloc(test, sizeof(*sb_cf), GFP_KERNEL); 507*3147cac6SGuan-Chun Wu sb_plain = kunit_kzalloc(test, sizeof(*sb_plain), GFP_KERNEL); 508*3147cac6SGuan-Chun Wu ei = kunit_kzalloc(test, sizeof(*ei), GFP_KERNEL); 509*3147cac6SGuan-Chun Wu sbi = kunit_kzalloc(test, sizeof(*sbi), GFP_KERNEL); 510*3147cac6SGuan-Chun Wu plain_dir = kunit_kzalloc(test, sizeof(*plain_dir), GFP_KERNEL); 511*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb_cf); 512*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sb_plain); 513*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, ei); 514*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, sbi); 515*3147cac6SGuan-Chun Wu KUNIT_ASSERT_NOT_NULL(test, plain_dir); 516*3147cac6SGuan-Chun Wu 517*3147cac6SGuan-Chun Wu um = utf8_load(UTF8_LATEST); 518*3147cac6SGuan-Chun Wu if (IS_ERR(um)) { 519*3147cac6SGuan-Chun Wu kunit_skip(test, "utf8_load(UTF8_LATEST) failed: %pe", 520*3147cac6SGuan-Chun Wu um); 521*3147cac6SGuan-Chun Wu return; 522*3147cac6SGuan-Chun Wu } 523*3147cac6SGuan-Chun Wu 524*3147cac6SGuan-Chun Wu ret = kunit_add_action_or_reset(test, utf8_unload_action, um); 525*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret, 0); 526*3147cac6SGuan-Chun Wu 527*3147cac6SGuan-Chun Wu ext4_hash_init_fake_ext4_dir(ei, sb_cf, sbi); 528*3147cac6SGuan-Chun Wu sb_cf->s_encoding = um; 529*3147cac6SGuan-Chun Wu ei->vfs_inode.i_flags |= S_CASEFOLD; 530*3147cac6SGuan-Chun Wu 531*3147cac6SGuan-Chun Wu KUNIT_ASSERT_TRUE(test, IS_CASEFOLDED(&ei->vfs_inode)); 532*3147cac6SGuan-Chun Wu 533*3147cac6SGuan-Chun Wu ext4_hash_init_fake_dir(plain_dir, sb_plain); 534*3147cac6SGuan-Chun Wu 535*3147cac6SGuan-Chun Wu ret_cf = ext4fs_dirhash(&ei->vfs_inode, invalid_utf8, 536*3147cac6SGuan-Chun Wu sizeof(invalid_utf8) - 1, &folded_dir); 537*3147cac6SGuan-Chun Wu ret_plain = ext4fs_dirhash(plain_dir, invalid_utf8, 538*3147cac6SGuan-Chun Wu sizeof(invalid_utf8) - 1, &plain); 539*3147cac6SGuan-Chun Wu 540*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret_cf, 0); 541*3147cac6SGuan-Chun Wu KUNIT_ASSERT_EQ(test, ret_plain, 0); 542*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, folded_dir.hash, plain.hash); 543*3147cac6SGuan-Chun Wu KUNIT_EXPECT_EQ(test, folded_dir.minor_hash, plain.minor_hash); 544*3147cac6SGuan-Chun Wu } 545*3147cac6SGuan-Chun Wu #endif 546*3147cac6SGuan-Chun Wu 547*3147cac6SGuan-Chun Wu static struct kunit_case ext4_hash_test_cases[] = { 548*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_vectors), 549*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_seed_changes_result), 550*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_invalid_version_returns_einval), 551*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_siphash_without_key_returns_einval), 552*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_signed_unsigned_differ_on_nonascii), 553*3147cac6SGuan-Chun Wu #if IS_ENABLED(CONFIG_UNICODE) 554*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_casefolded_names_hash_consistently), 555*3147cac6SGuan-Chun Wu KUNIT_CASE(test_ext4fs_dirhash_casefold_fallback), 556*3147cac6SGuan-Chun Wu #endif 557*3147cac6SGuan-Chun Wu {} 558*3147cac6SGuan-Chun Wu }; 559*3147cac6SGuan-Chun Wu 560*3147cac6SGuan-Chun Wu static struct kunit_suite ext4_hash_test_suite = { 561*3147cac6SGuan-Chun Wu .name = "ext4_hash", 562*3147cac6SGuan-Chun Wu .test_cases = ext4_hash_test_cases, 563*3147cac6SGuan-Chun Wu }; 564*3147cac6SGuan-Chun Wu 565*3147cac6SGuan-Chun Wu kunit_test_suites(&ext4_hash_test_suite); 566*3147cac6SGuan-Chun Wu 567*3147cac6SGuan-Chun Wu MODULE_LICENSE("GPL"); 568