1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * KUnit tests for HFS+ Unicode string operations 4 * 5 * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com> 6 */ 7 8 #include <kunit/test.h> 9 #include <linux/nls.h> 10 #include <linux/dcache.h> 11 #include <linux/stringhash.h> 12 #include "hfsplus_fs.h" 13 #include "unicode.h" 14 15 struct test_mock_string_env { 16 struct hfsplus_unistr str1; 17 struct hfsplus_unistr str2; 18 char *buf; 19 u32 buf_size; 20 }; 21 22 static struct test_mock_string_env *setup_mock_str_env(u32 buf_size) 23 { 24 struct test_mock_string_env *env; 25 26 env = kzalloc_obj(struct test_mock_string_env); 27 if (!env) 28 return NULL; 29 30 env->buf = kzalloc(buf_size, GFP_KERNEL); 31 if (!env->buf) { 32 kfree(env); 33 return NULL; 34 } 35 36 env->buf_size = buf_size; 37 38 return env; 39 } 40 41 static void free_mock_str_env(struct test_mock_string_env *env) 42 { 43 kfree(env->buf); 44 kfree(env); 45 } 46 47 /* Helper function to create hfsplus_unistr */ 48 static void create_unistr(struct hfsplus_unistr *ustr, const char *ascii_str) 49 { 50 int len = strlen(ascii_str); 51 int i; 52 53 memset(ustr->unicode, 0, sizeof(ustr->unicode)); 54 55 ustr->length = cpu_to_be16(len); 56 for (i = 0; i < len && i < HFSPLUS_MAX_STRLEN; i++) 57 ustr->unicode[i] = cpu_to_be16((u16)ascii_str[i]); 58 } 59 60 static void corrupt_unistr(struct hfsplus_unistr *ustr) 61 { 62 ustr->length = cpu_to_be16(U16_MAX); 63 } 64 65 /* Test hfsplus_strcasecmp function */ 66 static void hfsplus_strcasecmp_test(struct kunit *test) 67 { 68 struct test_mock_string_env *mock_env; 69 70 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 71 KUNIT_ASSERT_NOT_NULL(test, mock_env); 72 73 /* Test identical strings */ 74 create_unistr(&mock_env->str1, "hello"); 75 create_unistr(&mock_env->str2, "hello"); 76 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 77 &mock_env->str2)); 78 79 /* Test case insensitive comparison */ 80 create_unistr(&mock_env->str1, "Hello"); 81 create_unistr(&mock_env->str2, "hello"); 82 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 83 &mock_env->str2)); 84 85 create_unistr(&mock_env->str1, "HELLO"); 86 create_unistr(&mock_env->str2, "hello"); 87 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 88 &mock_env->str2)); 89 90 /* Test different strings */ 91 create_unistr(&mock_env->str1, "apple"); 92 create_unistr(&mock_env->str2, "banana"); 93 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 94 &mock_env->str2), 0); 95 96 create_unistr(&mock_env->str1, "zebra"); 97 create_unistr(&mock_env->str2, "apple"); 98 KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 99 &mock_env->str2), 0); 100 101 /* Test different lengths */ 102 create_unistr(&mock_env->str1, "test"); 103 create_unistr(&mock_env->str2, "testing"); 104 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 105 &mock_env->str2), 0); 106 107 create_unistr(&mock_env->str1, "testing"); 108 create_unistr(&mock_env->str2, "test"); 109 KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 110 &mock_env->str2), 0); 111 112 /* Test empty strings */ 113 create_unistr(&mock_env->str1, ""); 114 create_unistr(&mock_env->str2, ""); 115 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 116 &mock_env->str2)); 117 118 create_unistr(&mock_env->str1, ""); 119 create_unistr(&mock_env->str2, "test"); 120 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 121 &mock_env->str2), 0); 122 123 /* Test single characters */ 124 create_unistr(&mock_env->str1, "A"); 125 create_unistr(&mock_env->str2, "a"); 126 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 127 &mock_env->str2)); 128 129 create_unistr(&mock_env->str1, "A"); 130 create_unistr(&mock_env->str2, "B"); 131 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 132 &mock_env->str2), 0); 133 134 /* Test maximum length strings */ 135 memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 136 mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0'; 137 create_unistr(&mock_env->str1, mock_env->buf); 138 create_unistr(&mock_env->str2, mock_env->buf); 139 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 140 &mock_env->str2)); 141 142 /* Change one character in the middle */ 143 mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b'; 144 create_unistr(&mock_env->str2, mock_env->buf); 145 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 146 &mock_env->str2), 0); 147 148 /* Test corrupted strings */ 149 create_unistr(&mock_env->str1, ""); 150 corrupt_unistr(&mock_env->str1); 151 create_unistr(&mock_env->str2, ""); 152 KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1, 153 &mock_env->str2)); 154 155 create_unistr(&mock_env->str1, ""); 156 create_unistr(&mock_env->str2, ""); 157 corrupt_unistr(&mock_env->str2); 158 KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1, 159 &mock_env->str2)); 160 161 create_unistr(&mock_env->str1, "test"); 162 corrupt_unistr(&mock_env->str1); 163 create_unistr(&mock_env->str2, "testing"); 164 KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 165 &mock_env->str2), 0); 166 167 create_unistr(&mock_env->str1, "test"); 168 create_unistr(&mock_env->str2, "testing"); 169 corrupt_unistr(&mock_env->str2); 170 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 171 &mock_env->str2), 0); 172 173 create_unistr(&mock_env->str1, "testing"); 174 corrupt_unistr(&mock_env->str1); 175 create_unistr(&mock_env->str2, "test"); 176 KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 177 &mock_env->str2), 0); 178 179 create_unistr(&mock_env->str1, "testing"); 180 create_unistr(&mock_env->str2, "test"); 181 corrupt_unistr(&mock_env->str2); 182 KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1, 183 &mock_env->str2), 0); 184 185 free_mock_str_env(mock_env); 186 } 187 188 /* Test hfsplus_strcmp function (case-sensitive) */ 189 static void hfsplus_strcmp_test(struct kunit *test) 190 { 191 struct test_mock_string_env *mock_env; 192 193 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 194 KUNIT_ASSERT_NOT_NULL(test, mock_env); 195 196 /* Test identical strings */ 197 create_unistr(&mock_env->str1, "hello"); 198 create_unistr(&mock_env->str2, "hello"); 199 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 200 &mock_env->str2)); 201 202 /* Test case sensitive comparison - should NOT be equal */ 203 create_unistr(&mock_env->str1, "Hello"); 204 create_unistr(&mock_env->str2, "hello"); 205 KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 206 &mock_env->str2)); 207 /* 'H' < 'h' in Unicode */ 208 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 209 &mock_env->str2), 0); 210 211 /* Test lexicographic ordering */ 212 create_unistr(&mock_env->str1, "apple"); 213 create_unistr(&mock_env->str2, "banana"); 214 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 215 &mock_env->str2), 0); 216 217 create_unistr(&mock_env->str1, "zebra"); 218 create_unistr(&mock_env->str2, "apple"); 219 KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 220 &mock_env->str2), 0); 221 222 /* Test different lengths with common prefix */ 223 create_unistr(&mock_env->str1, "test"); 224 create_unistr(&mock_env->str2, "testing"); 225 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 226 &mock_env->str2), 0); 227 228 create_unistr(&mock_env->str1, "testing"); 229 create_unistr(&mock_env->str2, "test"); 230 KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 231 &mock_env->str2), 0); 232 233 /* Test empty strings */ 234 create_unistr(&mock_env->str1, ""); 235 create_unistr(&mock_env->str2, ""); 236 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 237 &mock_env->str2)); 238 239 /* Test maximum length strings */ 240 memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 241 mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0'; 242 create_unistr(&mock_env->str1, mock_env->buf); 243 create_unistr(&mock_env->str2, mock_env->buf); 244 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 245 &mock_env->str2)); 246 247 /* Change one character in the middle */ 248 mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b'; 249 create_unistr(&mock_env->str2, mock_env->buf); 250 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 251 &mock_env->str2), 0); 252 253 /* Test corrupted strings */ 254 create_unistr(&mock_env->str1, ""); 255 corrupt_unistr(&mock_env->str1); 256 create_unistr(&mock_env->str2, ""); 257 KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 258 &mock_env->str2)); 259 260 create_unistr(&mock_env->str1, ""); 261 create_unistr(&mock_env->str2, ""); 262 corrupt_unistr(&mock_env->str2); 263 KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 264 &mock_env->str2)); 265 266 create_unistr(&mock_env->str1, "test"); 267 corrupt_unistr(&mock_env->str1); 268 create_unistr(&mock_env->str2, "testing"); 269 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 270 &mock_env->str2), 0); 271 272 create_unistr(&mock_env->str1, "test"); 273 create_unistr(&mock_env->str2, "testing"); 274 corrupt_unistr(&mock_env->str2); 275 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 276 &mock_env->str2), 0); 277 278 create_unistr(&mock_env->str1, "testing"); 279 corrupt_unistr(&mock_env->str1); 280 create_unistr(&mock_env->str2, "test"); 281 KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 282 &mock_env->str2), 0); 283 284 create_unistr(&mock_env->str1, "testing"); 285 create_unistr(&mock_env->str2, "test"); 286 corrupt_unistr(&mock_env->str2); 287 KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 288 &mock_env->str2), 0); 289 290 free_mock_str_env(mock_env); 291 } 292 293 /* Test Unicode edge cases */ 294 static void hfsplus_unicode_edge_cases_test(struct kunit *test) 295 { 296 struct test_mock_string_env *mock_env; 297 298 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 299 KUNIT_ASSERT_NOT_NULL(test, mock_env); 300 301 /* Test with special characters */ 302 mock_env->str1.length = cpu_to_be16(3); 303 mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */ 304 mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */ 305 mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 306 307 mock_env->str2.length = cpu_to_be16(3); 308 mock_env->str2.unicode[0] = cpu_to_be16(0x00E9); /* é */ 309 mock_env->str2.unicode[1] = cpu_to_be16(0x00F1); /* ñ */ 310 mock_env->str2.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 311 312 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 313 &mock_env->str2)); 314 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 315 &mock_env->str2)); 316 317 /* Test with different special characters */ 318 mock_env->str2.unicode[1] = cpu_to_be16(0x00F2); /* ò */ 319 KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1, 320 &mock_env->str2)); 321 322 /* Test null characters within string (should be handled correctly) */ 323 mock_env->str1.length = cpu_to_be16(3); 324 mock_env->str1.unicode[0] = cpu_to_be16('a'); 325 mock_env->str1.unicode[1] = cpu_to_be16(0x0000); /* null */ 326 mock_env->str1.unicode[2] = cpu_to_be16('b'); 327 328 mock_env->str2.length = cpu_to_be16(3); 329 mock_env->str2.unicode[0] = cpu_to_be16('a'); 330 mock_env->str2.unicode[1] = cpu_to_be16(0x0000); /* null */ 331 mock_env->str2.unicode[2] = cpu_to_be16('b'); 332 333 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 334 &mock_env->str2)); 335 336 free_mock_str_env(mock_env); 337 } 338 339 /* Test boundary conditions */ 340 static void hfsplus_unicode_boundary_test(struct kunit *test) 341 { 342 struct test_mock_string_env *mock_env; 343 int i; 344 345 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 346 KUNIT_ASSERT_NOT_NULL(test, mock_env); 347 348 /* Test maximum length boundary */ 349 mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 350 mock_env->str2.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 351 352 for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) { 353 mock_env->str1.unicode[i] = cpu_to_be16('A'); 354 mock_env->str2.unicode[i] = cpu_to_be16('A'); 355 } 356 357 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 358 &mock_env->str2)); 359 360 /* Change last character */ 361 mock_env->str2.unicode[HFSPLUS_MAX_STRLEN - 1] = cpu_to_be16('B'); 362 KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1, 363 &mock_env->str2), 0); 364 365 /* Test zero length strings */ 366 mock_env->str1.length = cpu_to_be16(0); 367 mock_env->str2.length = cpu_to_be16(0); 368 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1, 369 &mock_env->str2)); 370 KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1, 371 &mock_env->str2)); 372 373 /* Test one character vs empty */ 374 mock_env->str1.length = cpu_to_be16(1); 375 mock_env->str1.unicode[0] = cpu_to_be16('A'); 376 mock_env->str2.length = cpu_to_be16(0); 377 KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1, 378 &mock_env->str2), 0); 379 KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1, 380 &mock_env->str2), 0); 381 382 free_mock_str_env(mock_env); 383 } 384 385 /* Mock superblock and NLS table for testing hfsplus_uni2asc */ 386 struct test_mock_sb { 387 struct nls_table nls; 388 struct hfsplus_sb_info sb_info; 389 struct super_block sb; 390 }; 391 392 static struct test_mock_sb *setup_mock_sb(void) 393 { 394 struct test_mock_sb *ptr; 395 396 ptr = kzalloc_obj(struct test_mock_sb); 397 if (!ptr) 398 return NULL; 399 400 ptr->nls.charset = "utf8"; 401 ptr->nls.uni2char = NULL; /* Will use default behavior */ 402 ptr->sb_info.nls = &ptr->nls; 403 ptr->sb.s_fs_info = &ptr->sb_info; 404 405 /* Set default flags - no decomposition, no case folding */ 406 clear_bit(HFSPLUS_SB_NODECOMPOSE, &ptr->sb_info.flags); 407 clear_bit(HFSPLUS_SB_CASEFOLD, &ptr->sb_info.flags); 408 409 return ptr; 410 } 411 412 static void free_mock_sb(struct test_mock_sb *ptr) 413 { 414 kfree(ptr); 415 } 416 417 /* Simple uni2char implementation for testing */ 418 static int test_uni2char(wchar_t uni, unsigned char *out, int boundlen) 419 { 420 if (boundlen <= 0) 421 return -ENAMETOOLONG; 422 423 if (uni < 0x80) { 424 *out = (unsigned char)uni; 425 return 1; 426 } 427 428 /* For non-ASCII, just use '?' as fallback */ 429 *out = '?'; 430 return 1; 431 } 432 433 /* Test hfsplus_uni2asc basic functionality */ 434 static void hfsplus_uni2asc_basic_test(struct kunit *test) 435 { 436 struct test_mock_sb *mock_sb; 437 struct test_mock_string_env *mock_env; 438 int len, result; 439 440 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 441 KUNIT_ASSERT_NOT_NULL(test, mock_env); 442 443 mock_sb = setup_mock_sb(); 444 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 445 446 mock_sb->nls.uni2char = test_uni2char; 447 448 /* Test simple ASCII string conversion */ 449 create_unistr(&mock_env->str1, "hello"); 450 len = mock_env->buf_size; 451 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 452 mock_env->buf, &len); 453 454 KUNIT_EXPECT_EQ(test, 0, result); 455 KUNIT_EXPECT_EQ(test, 5, len); 456 KUNIT_EXPECT_STREQ(test, "hello", mock_env->buf); 457 458 /* Test empty string */ 459 create_unistr(&mock_env->str1, ""); 460 len = mock_env->buf_size; 461 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 462 mock_env->buf, &len); 463 464 KUNIT_EXPECT_EQ(test, 0, result); 465 KUNIT_EXPECT_EQ(test, 0, len); 466 467 /* Test single character */ 468 create_unistr(&mock_env->str1, "A"); 469 len = mock_env->buf_size; 470 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 471 mock_env->buf, &len); 472 473 KUNIT_EXPECT_EQ(test, 0, result); 474 KUNIT_EXPECT_EQ(test, 1, len); 475 KUNIT_EXPECT_EQ(test, 'A', mock_env->buf[0]); 476 477 free_mock_str_env(mock_env); 478 free_mock_sb(mock_sb); 479 } 480 481 /* Test special character handling */ 482 static void hfsplus_uni2asc_special_chars_test(struct kunit *test) 483 { 484 struct test_mock_sb *mock_sb; 485 struct test_mock_string_env *mock_env; 486 int len, result; 487 488 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 489 KUNIT_ASSERT_NOT_NULL(test, mock_env); 490 491 mock_sb = setup_mock_sb(); 492 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 493 494 mock_sb->nls.uni2char = test_uni2char; 495 496 /* Test null character conversion (should become 0x2400) */ 497 mock_env->str1.length = cpu_to_be16(1); 498 mock_env->str1.unicode[0] = cpu_to_be16(0x0000); 499 len = mock_env->buf_size; 500 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 501 mock_env->buf, &len); 502 503 KUNIT_EXPECT_EQ(test, 0, result); 504 KUNIT_EXPECT_EQ(test, 1, len); 505 /* Our test implementation returns '?' for non-ASCII */ 506 KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]); 507 508 /* Test forward slash conversion (should become colon) */ 509 mock_env->str1.length = cpu_to_be16(1); 510 mock_env->str1.unicode[0] = cpu_to_be16('/'); 511 len = mock_env->buf_size; 512 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 513 mock_env->buf, &len); 514 515 KUNIT_EXPECT_EQ(test, 0, result); 516 KUNIT_EXPECT_EQ(test, 1, len); 517 KUNIT_EXPECT_EQ(test, ':', mock_env->buf[0]); 518 519 /* Test string with mixed special characters */ 520 mock_env->str1.length = cpu_to_be16(3); 521 mock_env->str1.unicode[0] = cpu_to_be16('a'); 522 mock_env->str1.unicode[1] = cpu_to_be16('/'); 523 mock_env->str1.unicode[2] = cpu_to_be16('b'); 524 len = mock_env->buf_size; 525 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 526 mock_env->buf, &len); 527 528 KUNIT_EXPECT_EQ(test, 0, result); 529 KUNIT_EXPECT_EQ(test, 3, len); 530 KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[0]); 531 KUNIT_EXPECT_EQ(test, ':', mock_env->buf[1]); 532 KUNIT_EXPECT_EQ(test, 'b', mock_env->buf[2]); 533 534 free_mock_str_env(mock_env); 535 free_mock_sb(mock_sb); 536 } 537 538 /* Test buffer length handling */ 539 static void hfsplus_uni2asc_buffer_test(struct kunit *test) 540 { 541 struct test_mock_sb *mock_sb; 542 struct test_mock_string_env *mock_env; 543 int len, result; 544 545 mock_env = setup_mock_str_env(10); 546 KUNIT_ASSERT_NOT_NULL(test, mock_env); 547 548 mock_sb = setup_mock_sb(); 549 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 550 551 mock_sb->nls.uni2char = test_uni2char; 552 553 /* Test insufficient buffer space */ 554 create_unistr(&mock_env->str1, "toolongstring"); 555 len = 5; /* Buffer too small */ 556 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 557 mock_env->buf, &len); 558 559 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 560 KUNIT_EXPECT_EQ(test, 5, len); /* Should be set to consumed length */ 561 562 /* Test exact buffer size */ 563 create_unistr(&mock_env->str1, "exact"); 564 len = 5; 565 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 566 mock_env->buf, &len); 567 568 KUNIT_EXPECT_EQ(test, 0, result); 569 KUNIT_EXPECT_EQ(test, 5, len); 570 571 /* Test zero length buffer */ 572 create_unistr(&mock_env->str1, "test"); 573 len = 0; 574 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 575 mock_env->buf, &len); 576 577 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 578 KUNIT_EXPECT_EQ(test, 0, len); 579 580 free_mock_str_env(mock_env); 581 free_mock_sb(mock_sb); 582 } 583 584 /* Test corrupted unicode string handling */ 585 static void hfsplus_uni2asc_corrupted_test(struct kunit *test) 586 { 587 struct test_mock_sb *mock_sb; 588 struct test_mock_string_env *mock_env; 589 int len, result; 590 591 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 592 KUNIT_ASSERT_NOT_NULL(test, mock_env); 593 594 mock_sb = setup_mock_sb(); 595 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 596 597 mock_sb->nls.uni2char = test_uni2char; 598 599 /* Test corrupted length (too large) */ 600 create_unistr(&mock_env->str1, "test"); 601 corrupt_unistr(&mock_env->str1); /* Sets length to U16_MAX */ 602 len = mock_env->buf_size; 603 604 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 605 mock_env->buf, &len); 606 607 /* Should still work but with corrected length */ 608 KUNIT_EXPECT_EQ(test, 0, result); 609 /* 610 * Length should be corrected to HFSPLUS_MAX_STRLEN 611 * and processed accordingly 612 */ 613 KUNIT_EXPECT_GT(test, len, 0); 614 615 free_mock_str_env(mock_env); 616 free_mock_sb(mock_sb); 617 } 618 619 /* Test edge cases and boundary conditions */ 620 static void hfsplus_uni2asc_edge_cases_test(struct kunit *test) 621 { 622 struct test_mock_sb *mock_sb; 623 struct test_mock_string_env *mock_env; 624 int len, result; 625 int i; 626 627 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN * 2); 628 KUNIT_ASSERT_NOT_NULL(test, mock_env); 629 630 mock_sb = setup_mock_sb(); 631 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 632 633 mock_sb->nls.uni2char = test_uni2char; 634 635 /* Test maximum length string */ 636 mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 637 for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) 638 mock_env->str1.unicode[i] = cpu_to_be16('a'); 639 640 len = mock_env->buf_size; 641 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 642 mock_env->buf, &len); 643 644 KUNIT_EXPECT_EQ(test, 0, result); 645 KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, len); 646 647 /* Verify all characters are 'a' */ 648 for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) 649 KUNIT_EXPECT_EQ(test, 'a', mock_env->buf[i]); 650 651 /* Test string with high Unicode values (non-ASCII) */ 652 mock_env->str1.length = cpu_to_be16(3); 653 mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */ 654 mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */ 655 mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 656 len = mock_env->buf_size; 657 result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1, 658 mock_env->buf, &len); 659 660 KUNIT_EXPECT_EQ(test, 0, result); 661 KUNIT_EXPECT_EQ(test, 3, len); 662 /* Our test implementation converts non-ASCII to '?' */ 663 KUNIT_EXPECT_EQ(test, '?', mock_env->buf[0]); 664 KUNIT_EXPECT_EQ(test, '?', mock_env->buf[1]); 665 KUNIT_EXPECT_EQ(test, '?', mock_env->buf[2]); 666 667 free_mock_str_env(mock_env); 668 free_mock_sb(mock_sb); 669 } 670 671 /* Simple char2uni implementation for testing */ 672 static int test_char2uni(const unsigned char *rawstring, 673 int boundlen, wchar_t *uni) 674 { 675 if (boundlen <= 0) 676 return -EINVAL; 677 678 *uni = (wchar_t)*rawstring; 679 return 1; 680 } 681 682 /* Helper function to check unicode string contents */ 683 static void check_unistr_content(struct kunit *test, 684 struct hfsplus_unistr *ustr, 685 const char *expected_ascii) 686 { 687 int expected_len = strlen(expected_ascii); 688 int actual_len = be16_to_cpu(ustr->length); 689 int i; 690 691 KUNIT_EXPECT_EQ(test, expected_len, actual_len); 692 693 for (i = 0; i < expected_len && i < actual_len; i++) { 694 u16 expected_char = (u16)expected_ascii[i]; 695 u16 actual_char = be16_to_cpu(ustr->unicode[i]); 696 697 KUNIT_EXPECT_EQ(test, expected_char, actual_char); 698 } 699 } 700 701 /* Test hfsplus_asc2uni basic functionality */ 702 static void hfsplus_asc2uni_basic_test(struct kunit *test) 703 { 704 struct test_mock_sb *mock_sb; 705 struct test_mock_string_env *mock_env; 706 int result; 707 708 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 709 KUNIT_ASSERT_NOT_NULL(test, mock_env); 710 711 mock_sb = setup_mock_sb(); 712 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 713 714 mock_sb->nls.char2uni = test_char2uni; 715 716 /* Test simple ASCII string conversion */ 717 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 718 HFSPLUS_MAX_STRLEN, "hello", 5, 719 HFS_REGULAR_NAME); 720 721 KUNIT_EXPECT_EQ(test, 0, result); 722 check_unistr_content(test, &mock_env->str1, "hello"); 723 724 /* Test empty string */ 725 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 726 HFSPLUS_MAX_STRLEN, "", 0, 727 HFS_REGULAR_NAME); 728 729 KUNIT_EXPECT_EQ(test, 0, result); 730 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length)); 731 732 /* Test single character */ 733 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 734 HFSPLUS_MAX_STRLEN, "A", 1, 735 HFS_REGULAR_NAME); 736 737 KUNIT_EXPECT_EQ(test, 0, result); 738 check_unistr_content(test, &mock_env->str1, "A"); 739 740 /* Test null-terminated string with explicit length */ 741 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 742 HFSPLUS_MAX_STRLEN, "test\0extra", 4, 743 HFS_REGULAR_NAME); 744 745 KUNIT_EXPECT_EQ(test, 0, result); 746 check_unistr_content(test, &mock_env->str1, "test"); 747 748 free_mock_str_env(mock_env); 749 free_mock_sb(mock_sb); 750 } 751 752 /* Test special character handling in asc2uni */ 753 static void hfsplus_asc2uni_special_chars_test(struct kunit *test) 754 { 755 struct test_mock_sb *mock_sb; 756 struct test_mock_string_env *mock_env; 757 int result; 758 759 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 760 KUNIT_ASSERT_NOT_NULL(test, mock_env); 761 762 mock_sb = setup_mock_sb(); 763 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 764 765 mock_sb->nls.char2uni = test_char2uni; 766 767 /* Test colon conversion (should become forward slash) */ 768 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 769 HFSPLUS_MAX_STRLEN, ":", 1, 770 HFS_REGULAR_NAME); 771 772 KUNIT_EXPECT_EQ(test, 0, result); 773 KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length)); 774 KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0])); 775 776 /* Test string with mixed special characters */ 777 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 778 HFSPLUS_MAX_STRLEN, "a:b", 3, 779 HFS_REGULAR_NAME); 780 781 KUNIT_EXPECT_EQ(test, 0, result); 782 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length)); 783 KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(mock_env->str1.unicode[0])); 784 KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1])); 785 KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(mock_env->str1.unicode[2])); 786 787 /* Test multiple special characters */ 788 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 789 HFSPLUS_MAX_STRLEN, ":::", 3, 790 HFS_REGULAR_NAME); 791 792 KUNIT_EXPECT_EQ(test, 0, result); 793 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length)); 794 KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[0])); 795 KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[1])); 796 KUNIT_EXPECT_EQ(test, '/', be16_to_cpu(mock_env->str1.unicode[2])); 797 798 free_mock_str_env(mock_env); 799 free_mock_sb(mock_sb); 800 } 801 802 /* Test buffer length limits */ 803 static void hfsplus_asc2uni_buffer_limits_test(struct kunit *test) 804 { 805 struct test_mock_sb *mock_sb; 806 struct test_mock_string_env *mock_env; 807 int result; 808 809 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 10); 810 KUNIT_ASSERT_NOT_NULL(test, mock_env); 811 812 mock_sb = setup_mock_sb(); 813 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 814 815 mock_sb->nls.char2uni = test_char2uni; 816 817 /* Test exact maximum length */ 818 memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN); 819 result = hfsplus_asc2uni(&mock_sb->sb, 820 &mock_env->str1, HFSPLUS_MAX_STRLEN, 821 mock_env->buf, HFSPLUS_MAX_STRLEN, 822 HFS_REGULAR_NAME); 823 824 KUNIT_EXPECT_EQ(test, 0, result); 825 KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, 826 be16_to_cpu(mock_env->str1.length)); 827 828 /* Test exceeding maximum length */ 829 memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5); 830 result = hfsplus_asc2uni(&mock_sb->sb, 831 &mock_env->str1, HFSPLUS_MAX_STRLEN, 832 mock_env->buf, HFSPLUS_MAX_STRLEN + 5, 833 HFS_REGULAR_NAME); 834 835 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 836 KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN, 837 be16_to_cpu(mock_env->str1.length)); 838 839 /* Test with smaller max_unistr_len */ 840 result = hfsplus_asc2uni(&mock_sb->sb, 841 &mock_env->str1, 5, "toolongstring", 13, 842 HFS_REGULAR_NAME); 843 844 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 845 KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length)); 846 847 /* Test zero max length */ 848 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4, 849 HFS_REGULAR_NAME); 850 851 KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result); 852 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length)); 853 854 free_mock_str_env(mock_env); 855 free_mock_sb(mock_sb); 856 } 857 858 /* Test error handling and edge cases */ 859 static void hfsplus_asc2uni_edge_cases_test(struct kunit *test) 860 { 861 struct test_mock_sb *mock_sb; 862 struct hfsplus_unistr ustr; 863 char test_str[] = {'a', '\0', 'b'}; 864 int result; 865 866 mock_sb = setup_mock_sb(); 867 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 868 869 mock_sb->nls.char2uni = test_char2uni; 870 871 /* Test zero length input */ 872 result = hfsplus_asc2uni(&mock_sb->sb, 873 &ustr, HFSPLUS_MAX_STRLEN, "test", 0, 874 HFS_REGULAR_NAME); 875 876 KUNIT_EXPECT_EQ(test, 0, result); 877 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length)); 878 879 /* Test input with length mismatch */ 880 result = hfsplus_asc2uni(&mock_sb->sb, 881 &ustr, HFSPLUS_MAX_STRLEN, "hello", 3, 882 HFS_REGULAR_NAME); 883 884 KUNIT_EXPECT_EQ(test, 0, result); 885 check_unistr_content(test, &ustr, "hel"); 886 887 /* Test with various printable ASCII characters */ 888 result = hfsplus_asc2uni(&mock_sb->sb, 889 &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9, 890 HFS_REGULAR_NAME); 891 892 KUNIT_EXPECT_EQ(test, 0, result); 893 check_unistr_content(test, &ustr, "ABC123!@#"); 894 895 /* Test null character in the middle */ 896 result = hfsplus_asc2uni(&mock_sb->sb, 897 &ustr, HFSPLUS_MAX_STRLEN, test_str, 3, 898 HFS_REGULAR_NAME); 899 900 KUNIT_EXPECT_EQ(test, 0, result); 901 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length)); 902 KUNIT_EXPECT_EQ(test, 'a', be16_to_cpu(ustr.unicode[0])); 903 KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.unicode[1])); 904 KUNIT_EXPECT_EQ(test, 'b', be16_to_cpu(ustr.unicode[2])); 905 906 free_mock_sb(mock_sb); 907 } 908 909 /* Test decomposition flag behavior */ 910 static void hfsplus_asc2uni_decompose_test(struct kunit *test) 911 { 912 struct test_mock_sb *mock_sb; 913 struct test_mock_string_env *mock_env; 914 int result; 915 916 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 917 KUNIT_ASSERT_NOT_NULL(test, mock_env); 918 919 mock_sb = setup_mock_sb(); 920 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 921 922 mock_sb->nls.char2uni = test_char2uni; 923 924 /* Test with decomposition disabled (default) */ 925 clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 926 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 927 HFSPLUS_MAX_STRLEN, "test", 4, 928 HFS_REGULAR_NAME); 929 930 KUNIT_EXPECT_EQ(test, 0, result); 931 check_unistr_content(test, &mock_env->str1, "test"); 932 933 /* Test with decomposition enabled */ 934 set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 935 result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2, 936 HFSPLUS_MAX_STRLEN, "test", 4, 937 HFS_REGULAR_NAME); 938 939 KUNIT_EXPECT_EQ(test, 0, result); 940 check_unistr_content(test, &mock_env->str2, "test"); 941 942 /* For simple ASCII, both should produce the same result */ 943 KUNIT_EXPECT_EQ(test, 944 be16_to_cpu(mock_env->str1.length), 945 be16_to_cpu(mock_env->str2.length)); 946 947 free_mock_str_env(mock_env); 948 free_mock_sb(mock_sb); 949 } 950 951 /* 952 * Real UTF-8 <-> wchar_t conversion, mirroring what the "utf8" NLS table 953 * actually does (see char2uni()/uni2char() in fs/nls/nls_utf8.c). The 954 * test_char2uni()/test_uni2char() stand-ins above only handle one raw 955 * byte per character, which cannot represent the combining marks (e.g. 956 * U+0301, U+0323) the canonical-reordering tests below need. 957 */ 958 static int test_char2uni_utf8(const unsigned char *rawstring, int boundlen, 959 wchar_t *uni) 960 { 961 unicode_t u; 962 int n = utf8_to_utf32(rawstring, boundlen, &u); 963 964 if (n < 0 || u > MAX_WCHAR_T) { 965 *uni = 0x3f; /* ? */ 966 return -EINVAL; 967 } 968 *uni = (wchar_t)u; 969 return n; 970 } 971 972 static int test_uni2char_utf8(wchar_t uni, unsigned char *out, int boundlen) 973 { 974 int n = utf32_to_utf8(uni, out, boundlen); 975 976 if (n < 0) { 977 *out = '?'; 978 return -EINVAL; 979 } 980 return n; 981 } 982 983 /* 984 * Test that hfsplus_asc2uni() brings combining marks contributed by 985 * different source characters into Unicode canonical (combining-class) 986 * order, instead of just storing them in whatever order they were typed. 987 * 988 * U+0301 (COMBINING ACUTE ACCENT) has combining class 230; U+0323 989 * (COMBINING DOT BELOW) has combining class 220. Since 220 < 230, 990 * canonical order requires the dot-below before the acute. A name 991 * created with them typed in the "wrong" order must still end up stored 992 * in canonical order - this is exactly what macOS's own fsck_hfs 993 * (FixDecomps() in CatalogCheck.c) requires and flags as "Illegal name" 994 * when it isn't true. 995 */ 996 static void hfsplus_asc2uni_combining_reorder_test(struct kunit *test) 997 { 998 struct test_mock_sb *mock_sb; 999 struct hfsplus_unistr ustr; 1000 /* "e" + COMBINING ACUTE ACCENT (U+0301) + COMBINING DOT BELOW (U+0323) */ 1001 static const char wrong_order[] = "e\xcc\x81\xcc\xa3"; 1002 /* "e" + COMBINING DOT BELOW (U+0323) + COMBINING ACUTE ACCENT (U+0301) */ 1003 static const char canonical_order[] = "e\xcc\xa3\xcc\x81"; 1004 /* Two class-230 marks: GRAVE (U+0300) then ACUTE (U+0301) */ 1005 static const char same_class[] = "e\xcc\x80\xcc\x81"; 1006 int result; 1007 1008 mock_sb = setup_mock_sb(); 1009 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1010 1011 mock_sb->nls.char2uni = test_char2uni_utf8; 1012 1013 /* Typed out of canonical order: must be reordered on storage. */ 1014 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1015 wrong_order, strlen(wrong_order), 1016 HFS_REGULAR_NAME); 1017 1018 KUNIT_EXPECT_EQ(test, 0, result); 1019 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length)); 1020 KUNIT_EXPECT_EQ(test, 'e', be16_to_cpu(ustr.unicode[0])); 1021 KUNIT_EXPECT_EQ(test, 0x0323, be16_to_cpu(ustr.unicode[1])); 1022 KUNIT_EXPECT_EQ(test, 0x0301, be16_to_cpu(ustr.unicode[2])); 1023 1024 /* Already in canonical order: must come out unchanged. */ 1025 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1026 canonical_order, strlen(canonical_order), 1027 HFS_REGULAR_NAME); 1028 1029 KUNIT_EXPECT_EQ(test, 0, result); 1030 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length)); 1031 KUNIT_EXPECT_EQ(test, 'e', be16_to_cpu(ustr.unicode[0])); 1032 KUNIT_EXPECT_EQ(test, 0x0323, be16_to_cpu(ustr.unicode[1])); 1033 KUNIT_EXPECT_EQ(test, 0x0301, be16_to_cpu(ustr.unicode[2])); 1034 1035 /* Two marks of equal combining class must keep their relative 1036 * (input) order - the sort must be stable, not just "sorted". 1037 */ 1038 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1039 same_class, strlen(same_class), 1040 HFS_REGULAR_NAME); 1041 1042 KUNIT_EXPECT_EQ(test, 0, result); 1043 KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length)); 1044 KUNIT_EXPECT_EQ(test, 'e', be16_to_cpu(ustr.unicode[0])); 1045 KUNIT_EXPECT_EQ(test, 0x0300, be16_to_cpu(ustr.unicode[1])); 1046 KUNIT_EXPECT_EQ(test, 0x0301, be16_to_cpu(ustr.unicode[2])); 1047 1048 free_mock_sb(mock_sb); 1049 } 1050 1051 /* 1052 * Test that a name written with combining marks in non-canonical order 1053 * still reads back as valid, correct UTF-8 once reordered - i.e. that 1054 * hfsplus_asc2uni() and hfsplus_uni2asc_str() stay consistent with each 1055 * other across the reordering. 1056 * 1057 * NODECOMPOSE is set so hfsplus_uni2asc_str() doesn't also recompose 1058 * "e" + COMBINING DOT BELOW back into the precomposed U+1EB9 ("e"): that 1059 * composition behavior is real (and already covered elsewhere), but it 1060 * would obscure what this test is actually checking. 1061 */ 1062 static void hfsplus_unicode_combining_reorder_roundtrip_test(struct kunit *test) 1063 { 1064 struct test_mock_sb *mock_sb; 1065 struct hfsplus_unistr ustr; 1066 static const char wrong_order[] = "e\xcc\x81\xcc\xa3"; 1067 static const char expected[] = "e\xcc\xa3\xcc\x81"; /* canonical order */ 1068 char buf[32]; 1069 int len = sizeof(buf); 1070 int result; 1071 1072 mock_sb = setup_mock_sb(); 1073 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1074 1075 set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1076 mock_sb->nls.char2uni = test_char2uni_utf8; 1077 mock_sb->nls.uni2char = test_uni2char_utf8; 1078 1079 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1080 wrong_order, strlen(wrong_order), 1081 HFS_REGULAR_NAME); 1082 KUNIT_EXPECT_EQ(test, 0, result); 1083 1084 result = hfsplus_uni2asc_str(&mock_sb->sb, &ustr, buf, &len); 1085 1086 KUNIT_EXPECT_EQ(test, 0, result); 1087 KUNIT_EXPECT_EQ(test, (int)strlen(expected), len); 1088 KUNIT_EXPECT_MEMEQ(test, expected, buf, len); 1089 1090 free_mock_sb(mock_sb); 1091 } 1092 1093 /* 1094 * Test that hfsplus_asc2uni() decomposes a character using the corrected 1095 * (post-2002/"Jaguar") canonical decomposition even when Apple Technote 1096 * #1150's own decomposition table doesn't have an entry for it. 1097 * 1098 * U+01F8 (LATIN CAPITAL LETTER N WITH GRAVE) is exactly one of the 1099 * characters macOS's own fsck_hfs (FixDecomps() in CatalogCheck.c) has 1100 * flagged "Illegal name" for since 2002 when found stored undecomposed. 1101 */ 1102 static void hfsplus_asc2uni_legacy_decomp_test(struct kunit *test) 1103 { 1104 struct test_mock_sb *mock_sb; 1105 struct hfsplus_unistr ustr; 1106 static const char input[] = "\xc7\xb8"; /* U+01F8 */ 1107 int result; 1108 1109 mock_sb = setup_mock_sb(); 1110 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1111 1112 mock_sb->nls.char2uni = test_char2uni_utf8; 1113 1114 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1115 input, strlen(input), HFS_REGULAR_NAME); 1116 1117 KUNIT_EXPECT_EQ(test, 0, result); 1118 KUNIT_EXPECT_EQ(test, 2, be16_to_cpu(ustr.length)); 1119 KUNIT_EXPECT_EQ(test, 'N', be16_to_cpu(ustr.unicode[0])); 1120 KUNIT_EXPECT_EQ(test, 0x0300, be16_to_cpu(ustr.unicode[1])); 1121 1122 free_mock_sb(mock_sb); 1123 } 1124 1125 /* 1126 * Test that hfsplus_asc2uni() corrects two more of fsck_hfs's known 1127 * legacy decomposition sequences once combining marks from independently 1128 * typed characters end up adjacent: 1129 * 1130 * - GREEK SMALL LETTER ALPHA (U+03B1) + COMBINING VERTICAL LINE ABOVE 1131 * (U+030D) must become U+03B1 + COMBINING ACUTE ACCENT (U+0301). 1132 * - BENGALI LETTER BA (U+09AC) + BENGALI SIGN NUKTA (U+09BC) must become 1133 * the single character BENGALI LETTER RA WITH MIDDLE DIAGONAL (U+09B0). 1134 */ 1135 static void hfsplus_asc2uni_legacy_seq_fixup_test(struct kunit *test) 1136 { 1137 struct test_mock_sb *mock_sb; 1138 struct hfsplus_unistr ustr; 1139 static const char greek_input[] = "\xce\xb1\xcc\x8d"; /* U+03B1 U+030D */ 1140 static const char bengali_input[] = "\xe0\xa6\xac\xe0\xa6\xbc"; /* U+09AC U+09BC */ 1141 int result; 1142 1143 mock_sb = setup_mock_sb(); 1144 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1145 1146 mock_sb->nls.char2uni = test_char2uni_utf8; 1147 1148 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1149 greek_input, strlen(greek_input), 1150 HFS_REGULAR_NAME); 1151 1152 KUNIT_EXPECT_EQ(test, 0, result); 1153 KUNIT_EXPECT_EQ(test, 2, be16_to_cpu(ustr.length)); 1154 KUNIT_EXPECT_EQ(test, 0x03b1, be16_to_cpu(ustr.unicode[0])); 1155 KUNIT_EXPECT_EQ(test, 0x0301, be16_to_cpu(ustr.unicode[1])); 1156 1157 result = hfsplus_asc2uni(&mock_sb->sb, &ustr, HFSPLUS_MAX_STRLEN, 1158 bengali_input, strlen(bengali_input), 1159 HFS_REGULAR_NAME); 1160 1161 KUNIT_EXPECT_EQ(test, 0, result); 1162 KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(ustr.length)); 1163 KUNIT_EXPECT_EQ(test, 0x09b0, be16_to_cpu(ustr.unicode[0])); 1164 1165 free_mock_sb(mock_sb); 1166 } 1167 1168 /* Mock dentry for testing hfsplus_hash_dentry */ 1169 static struct dentry test_dentry; 1170 1171 static void setup_mock_dentry(struct super_block *sb) 1172 { 1173 memset(&test_dentry, 0, sizeof(test_dentry)); 1174 test_dentry.d_sb = sb; 1175 } 1176 1177 /* Helper function to create qstr */ 1178 static void create_qstr(struct qstr *str, const char *name) 1179 { 1180 str->name = name; 1181 str->len = strlen(name); 1182 str->hash = 0; /* Will be set by hash function */ 1183 } 1184 1185 /* Test hfsplus_hash_dentry basic functionality */ 1186 static void hfsplus_hash_dentry_basic_test(struct kunit *test) 1187 { 1188 struct test_mock_sb *mock_sb; 1189 struct qstr str1, str2; 1190 int result; 1191 1192 mock_sb = setup_mock_sb(); 1193 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1194 1195 setup_mock_dentry(&mock_sb->sb); 1196 mock_sb->nls.char2uni = test_char2uni; 1197 1198 /* Test basic string hashing */ 1199 create_qstr(&str1, "hello"); 1200 result = hfsplus_hash_dentry(&test_dentry, &str1); 1201 1202 KUNIT_EXPECT_EQ(test, 0, result); 1203 KUNIT_EXPECT_NE(test, 0, str1.hash); 1204 1205 /* Test that identical strings produce identical hashes */ 1206 create_qstr(&str2, "hello"); 1207 result = hfsplus_hash_dentry(&test_dentry, &str2); 1208 1209 KUNIT_EXPECT_EQ(test, 0, result); 1210 KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1211 1212 /* Test empty string */ 1213 create_qstr(&str1, ""); 1214 result = hfsplus_hash_dentry(&test_dentry, &str1); 1215 1216 /* Empty string should still produce a hash */ 1217 KUNIT_EXPECT_EQ(test, 0, result); 1218 1219 /* Test single character */ 1220 create_qstr(&str1, "A"); 1221 result = hfsplus_hash_dentry(&test_dentry, &str1); 1222 1223 KUNIT_EXPECT_EQ(test, 0, result); 1224 KUNIT_EXPECT_NE(test, 0, str1.hash); 1225 1226 free_mock_sb(mock_sb); 1227 } 1228 1229 /* Test case folding behavior in hash */ 1230 static void hfsplus_hash_dentry_casefold_test(struct kunit *test) 1231 { 1232 struct test_mock_sb *mock_sb; 1233 struct qstr str1, str2; 1234 int result; 1235 1236 mock_sb = setup_mock_sb(); 1237 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1238 1239 setup_mock_dentry(&mock_sb->sb); 1240 mock_sb->nls.char2uni = test_char2uni; 1241 1242 /* Test with case folding disabled (default) */ 1243 clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1244 1245 create_qstr(&str1, "Hello"); 1246 result = hfsplus_hash_dentry(&test_dentry, &str1); 1247 KUNIT_EXPECT_EQ(test, 0, result); 1248 1249 create_qstr(&str2, "hello"); 1250 result = hfsplus_hash_dentry(&test_dentry, &str2); 1251 KUNIT_EXPECT_EQ(test, 0, result); 1252 1253 /* 1254 * Without case folding, different cases 1255 * should produce different hashes 1256 */ 1257 KUNIT_EXPECT_NE(test, str1.hash, str2.hash); 1258 1259 /* Test with case folding enabled */ 1260 set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1261 1262 create_qstr(&str1, "Hello"); 1263 result = hfsplus_hash_dentry(&test_dentry, &str1); 1264 KUNIT_EXPECT_EQ(test, 0, result); 1265 1266 create_qstr(&str2, "hello"); 1267 result = hfsplus_hash_dentry(&test_dentry, &str2); 1268 KUNIT_EXPECT_EQ(test, 0, result); 1269 1270 /* With case folding, different cases should produce same hash */ 1271 KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1272 1273 /* Test mixed case */ 1274 create_qstr(&str1, "HeLLo"); 1275 result = hfsplus_hash_dentry(&test_dentry, &str1); 1276 KUNIT_EXPECT_EQ(test, 0, result); 1277 KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1278 1279 free_mock_sb(mock_sb); 1280 } 1281 1282 /* Test special character handling in hash */ 1283 static void hfsplus_hash_dentry_special_chars_test(struct kunit *test) 1284 { 1285 struct test_mock_sb *mock_sb; 1286 struct qstr str1, str2; 1287 int result; 1288 1289 mock_sb = setup_mock_sb(); 1290 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1291 1292 setup_mock_dentry(&mock_sb->sb); 1293 mock_sb->nls.char2uni = test_char2uni; 1294 1295 /* Test colon conversion (: becomes /) */ 1296 create_qstr(&str1, "file:name"); 1297 result = hfsplus_hash_dentry(&test_dentry, &str1); 1298 KUNIT_EXPECT_EQ(test, 0, result); 1299 1300 create_qstr(&str2, "file/name"); 1301 result = hfsplus_hash_dentry(&test_dentry, &str2); 1302 KUNIT_EXPECT_EQ(test, 0, result); 1303 1304 /* After conversion, these should produce the same hash */ 1305 KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1306 1307 /* Test multiple special characters */ 1308 create_qstr(&str1, ":::"); 1309 result = hfsplus_hash_dentry(&test_dentry, &str1); 1310 KUNIT_EXPECT_EQ(test, 0, result); 1311 1312 create_qstr(&str2, "///"); 1313 result = hfsplus_hash_dentry(&test_dentry, &str2); 1314 KUNIT_EXPECT_EQ(test, 0, result); 1315 1316 KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1317 1318 free_mock_sb(mock_sb); 1319 } 1320 1321 /* Test decomposition flag behavior in hash */ 1322 static void hfsplus_hash_dentry_decompose_test(struct kunit *test) 1323 { 1324 struct test_mock_sb *mock_sb; 1325 struct qstr str1, str2; 1326 int result; 1327 1328 mock_sb = setup_mock_sb(); 1329 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1330 1331 setup_mock_dentry(&mock_sb->sb); 1332 mock_sb->nls.char2uni = test_char2uni; 1333 1334 /* Test with decomposition disabled (default) */ 1335 clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1336 1337 create_qstr(&str1, "test"); 1338 result = hfsplus_hash_dentry(&test_dentry, &str1); 1339 KUNIT_EXPECT_EQ(test, 0, result); 1340 1341 /* Test with decomposition enabled */ 1342 set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1343 1344 create_qstr(&str2, "test"); 1345 result = hfsplus_hash_dentry(&test_dentry, &str2); 1346 KUNIT_EXPECT_EQ(test, 0, result); 1347 1348 /* 1349 * For simple ASCII, decomposition shouldn't change 1350 * the hash much but the function should still work correctly 1351 */ 1352 KUNIT_EXPECT_NE(test, 0, str2.hash); 1353 1354 free_mock_sb(mock_sb); 1355 } 1356 1357 /* Test hash consistency and distribution */ 1358 static void hfsplus_hash_dentry_consistency_test(struct kunit *test) 1359 { 1360 struct test_mock_sb *mock_sb; 1361 struct qstr str1, str2, str3; 1362 unsigned long hash1; 1363 int result; 1364 1365 mock_sb = setup_mock_sb(); 1366 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1367 1368 setup_mock_dentry(&mock_sb->sb); 1369 mock_sb->nls.char2uni = test_char2uni; 1370 1371 /* Test that same string always produces same hash */ 1372 create_qstr(&str1, "consistent"); 1373 result = hfsplus_hash_dentry(&test_dentry, &str1); 1374 KUNIT_EXPECT_EQ(test, 0, result); 1375 hash1 = str1.hash; 1376 1377 create_qstr(&str2, "consistent"); 1378 result = hfsplus_hash_dentry(&test_dentry, &str2); 1379 KUNIT_EXPECT_EQ(test, 0, result); 1380 1381 KUNIT_EXPECT_EQ(test, hash1, str2.hash); 1382 1383 /* Test that different strings produce different hashes */ 1384 create_qstr(&str3, "different"); 1385 result = hfsplus_hash_dentry(&test_dentry, &str3); 1386 KUNIT_EXPECT_EQ(test, 0, result); 1387 1388 KUNIT_EXPECT_NE(test, str1.hash, str3.hash); 1389 1390 /* Test similar strings should have different hashes */ 1391 create_qstr(&str1, "file1"); 1392 result = hfsplus_hash_dentry(&test_dentry, &str1); 1393 KUNIT_EXPECT_EQ(test, 0, result); 1394 1395 create_qstr(&str2, "file2"); 1396 result = hfsplus_hash_dentry(&test_dentry, &str2); 1397 KUNIT_EXPECT_EQ(test, 0, result); 1398 1399 KUNIT_EXPECT_NE(test, str1.hash, str2.hash); 1400 1401 free_mock_sb(mock_sb); 1402 } 1403 1404 /* Test edge cases and boundary conditions */ 1405 static void hfsplus_hash_dentry_edge_cases_test(struct kunit *test) 1406 { 1407 struct test_mock_sb *mock_sb; 1408 struct test_mock_string_env *mock_env; 1409 struct qstr str; 1410 int result; 1411 1412 mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1); 1413 KUNIT_ASSERT_NOT_NULL(test, mock_env); 1414 1415 mock_sb = setup_mock_sb(); 1416 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1417 1418 setup_mock_dentry(&mock_sb->sb); 1419 mock_sb->nls.char2uni = test_char2uni; 1420 1421 /* Test very long filename */ 1422 memset(mock_env->buf, 'a', mock_env->buf_size - 1); 1423 mock_env->buf[mock_env->buf_size - 1] = '\0'; 1424 1425 create_qstr(&str, mock_env->buf); 1426 result = hfsplus_hash_dentry(&test_dentry, &str); 1427 1428 KUNIT_EXPECT_EQ(test, 0, result); 1429 KUNIT_EXPECT_NE(test, 0, str.hash); 1430 1431 /* Test filename with all printable ASCII characters */ 1432 create_qstr(&str, "!@#$%^&*()_+-=[]{}|;':\",./<>?"); 1433 result = hfsplus_hash_dentry(&test_dentry, &str); 1434 1435 KUNIT_EXPECT_EQ(test, 0, result); 1436 KUNIT_EXPECT_NE(test, 0, str.hash); 1437 1438 /* Test with embedded null (though not typical for filenames) */ 1439 str.name = "file\0hidden"; 1440 str.len = 11; /* Include the null and text after it */ 1441 str.hash = 0; 1442 result = hfsplus_hash_dentry(&test_dentry, &str); 1443 1444 KUNIT_EXPECT_EQ(test, 0, result); 1445 KUNIT_EXPECT_NE(test, 0, str.hash); 1446 1447 free_mock_str_env(mock_env); 1448 free_mock_sb(mock_sb); 1449 } 1450 1451 /* 1452 * Test that hfsplus_hash_dentry() hashes two names identically when they 1453 * differ only in the (non-canonical) typed order of the same combining 1454 * marks - both must canonicalize to the same stored form, so they must 1455 * hash the same or dcache lookups would spuriously miss. 1456 */ 1457 static void hfsplus_hash_dentry_combining_reorder_test(struct kunit *test) 1458 { 1459 struct test_mock_sb *mock_sb; 1460 struct qstr str1, str2; 1461 int result; 1462 1463 mock_sb = setup_mock_sb(); 1464 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1465 1466 setup_mock_dentry(&mock_sb->sb); 1467 mock_sb->nls.char2uni = test_char2uni_utf8; 1468 1469 create_qstr(&str1, "e\xcc\x81\xcc\xa3"); /* acute, then dot-below */ 1470 result = hfsplus_hash_dentry(&test_dentry, &str1); 1471 KUNIT_EXPECT_EQ(test, 0, result); 1472 1473 create_qstr(&str2, "e\xcc\xa3\xcc\x81"); /* dot-below, then acute */ 1474 result = hfsplus_hash_dentry(&test_dentry, &str2); 1475 KUNIT_EXPECT_EQ(test, 0, result); 1476 1477 KUNIT_EXPECT_EQ(test, str1.hash, str2.hash); 1478 1479 free_mock_sb(mock_sb); 1480 } 1481 1482 /* Test hfsplus_compare_dentry basic functionality */ 1483 static void hfsplus_compare_dentry_basic_test(struct kunit *test) 1484 { 1485 struct test_mock_sb *mock_sb; 1486 struct qstr name; 1487 int result; 1488 1489 mock_sb = setup_mock_sb(); 1490 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1491 1492 setup_mock_dentry(&mock_sb->sb); 1493 mock_sb->nls.char2uni = test_char2uni; 1494 1495 /* Test identical strings */ 1496 create_qstr(&name, "hello"); 1497 result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1498 KUNIT_EXPECT_EQ(test, 0, result); 1499 1500 /* Test different strings - lexicographic order */ 1501 create_qstr(&name, "world"); 1502 result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1503 KUNIT_EXPECT_LT(test, result, 0); /* "hello" < "world" */ 1504 1505 result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name); 1506 KUNIT_EXPECT_EQ(test, 0, result); 1507 1508 create_qstr(&name, "hello"); 1509 result = hfsplus_compare_dentry(&test_dentry, 5, "world", &name); 1510 KUNIT_EXPECT_GT(test, result, 0); /* "world" > "hello" */ 1511 1512 /* Test empty strings */ 1513 create_qstr(&name, ""); 1514 result = hfsplus_compare_dentry(&test_dentry, 0, "", &name); 1515 KUNIT_EXPECT_EQ(test, 0, result); 1516 1517 /* Test one empty, one non-empty */ 1518 create_qstr(&name, "test"); 1519 result = hfsplus_compare_dentry(&test_dentry, 0, "", &name); 1520 KUNIT_EXPECT_LT(test, result, 0); /* "" < "test" */ 1521 1522 create_qstr(&name, ""); 1523 result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1524 KUNIT_EXPECT_GT(test, result, 0); /* "test" > "" */ 1525 1526 free_mock_sb(mock_sb); 1527 } 1528 1529 /* Test case folding behavior in comparison */ 1530 static void hfsplus_compare_dentry_casefold_test(struct kunit *test) 1531 { 1532 struct test_mock_sb *mock_sb; 1533 struct qstr name; 1534 int result; 1535 1536 mock_sb = setup_mock_sb(); 1537 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1538 1539 setup_mock_dentry(&mock_sb->sb); 1540 mock_sb->nls.char2uni = test_char2uni; 1541 1542 /* Test with case folding disabled (default) */ 1543 clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1544 1545 create_qstr(&name, "hello"); 1546 result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name); 1547 /* Case sensitive: "Hello" != "hello" */ 1548 KUNIT_EXPECT_NE(test, 0, result); 1549 1550 create_qstr(&name, "Hello"); 1551 result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1552 /* Case sensitive: "hello" != "Hello" */ 1553 KUNIT_EXPECT_NE(test, 0, result); 1554 1555 /* Test with case folding enabled */ 1556 set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1557 1558 create_qstr(&name, "hello"); 1559 result = hfsplus_compare_dentry(&test_dentry, 5, "Hello", &name); 1560 /* Case insensitive: "Hello" == "hello" */ 1561 KUNIT_EXPECT_EQ(test, 0, result); 1562 1563 create_qstr(&name, "Hello"); 1564 result = hfsplus_compare_dentry(&test_dentry, 5, "hello", &name); 1565 /* Case insensitive: "hello" == "Hello" */ 1566 KUNIT_EXPECT_EQ(test, 0, result); 1567 1568 /* Test mixed case */ 1569 create_qstr(&name, "TeSt"); 1570 result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1571 KUNIT_EXPECT_EQ(test, 0, result); 1572 1573 create_qstr(&name, "test"); 1574 result = hfsplus_compare_dentry(&test_dentry, 4, "TEST", &name); 1575 KUNIT_EXPECT_EQ(test, 0, result); 1576 1577 free_mock_sb(mock_sb); 1578 } 1579 1580 /* Test special character handling in comparison */ 1581 static void hfsplus_compare_dentry_special_chars_test(struct kunit *test) 1582 { 1583 struct test_mock_sb *mock_sb; 1584 struct qstr name; 1585 int result; 1586 1587 mock_sb = setup_mock_sb(); 1588 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1589 1590 setup_mock_dentry(&mock_sb->sb); 1591 mock_sb->nls.char2uni = test_char2uni; 1592 1593 /* Test colon conversion (: becomes /) */ 1594 create_qstr(&name, "file/name"); 1595 result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name); 1596 /* "file:name" == "file/name" after conversion */ 1597 KUNIT_EXPECT_EQ(test, 0, result); 1598 1599 create_qstr(&name, "file:name"); 1600 result = hfsplus_compare_dentry(&test_dentry, 9, "file/name", &name); 1601 /* "file/name" == "file:name" after conversion */ 1602 KUNIT_EXPECT_EQ(test, 0, result); 1603 1604 /* Test multiple special characters */ 1605 create_qstr(&name, "///"); 1606 result = hfsplus_compare_dentry(&test_dentry, 3, ":::", &name); 1607 KUNIT_EXPECT_EQ(test, 0, result); 1608 1609 /* Test mixed special and regular characters */ 1610 create_qstr(&name, "a/b:c"); 1611 result = hfsplus_compare_dentry(&test_dentry, 5, "a:b/c", &name); 1612 /* Both become "a/b/c" after conversion */ 1613 KUNIT_EXPECT_EQ(test, 0, result); 1614 1615 free_mock_sb(mock_sb); 1616 } 1617 1618 /* Test length differences */ 1619 static void hfsplus_compare_dentry_length_test(struct kunit *test) 1620 { 1621 struct test_mock_sb *mock_sb; 1622 struct qstr name; 1623 int result; 1624 1625 mock_sb = setup_mock_sb(); 1626 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1627 1628 setup_mock_dentry(&mock_sb->sb); 1629 mock_sb->nls.char2uni = test_char2uni; 1630 1631 /* Test different lengths with common prefix */ 1632 create_qstr(&name, "testing"); 1633 result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1634 KUNIT_EXPECT_LT(test, result, 0); /* "test" < "testing" */ 1635 1636 create_qstr(&name, "test"); 1637 result = hfsplus_compare_dentry(&test_dentry, 7, "testing", &name); 1638 KUNIT_EXPECT_GT(test, result, 0); /* "testing" > "test" */ 1639 1640 /* Test exact length match */ 1641 create_qstr(&name, "exact"); 1642 result = hfsplus_compare_dentry(&test_dentry, 5, "exact", &name); 1643 KUNIT_EXPECT_EQ(test, 0, result); 1644 1645 /* Test length parameter vs actual string content */ 1646 create_qstr(&name, "hello"); 1647 result = hfsplus_compare_dentry(&test_dentry, 3, "hel", &name); 1648 KUNIT_EXPECT_LT(test, result, 0); /* "hel" < "hello" */ 1649 1650 /* Test longer first string but shorter length parameter */ 1651 create_qstr(&name, "hi"); 1652 result = hfsplus_compare_dentry(&test_dentry, 2, "hello", &name); 1653 /* "he" < "hi" (only first 2 chars compared) */ 1654 KUNIT_EXPECT_LT(test, result, 0); 1655 1656 free_mock_sb(mock_sb); 1657 } 1658 1659 /* Test decomposition flag behavior */ 1660 static void hfsplus_compare_dentry_decompose_test(struct kunit *test) 1661 { 1662 struct test_mock_sb *mock_sb; 1663 struct qstr name; 1664 int result; 1665 1666 mock_sb = setup_mock_sb(); 1667 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1668 1669 setup_mock_dentry(&mock_sb->sb); 1670 mock_sb->nls.char2uni = test_char2uni; 1671 1672 /* Test with decomposition disabled (default) */ 1673 clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1674 1675 create_qstr(&name, "test"); 1676 result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1677 KUNIT_EXPECT_EQ(test, 0, result); 1678 1679 /* Test with decomposition enabled */ 1680 set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1681 1682 create_qstr(&name, "test"); 1683 result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1684 KUNIT_EXPECT_EQ(test, 0, result); 1685 1686 /* For simple ASCII, decomposition shouldn't affect the result */ 1687 create_qstr(&name, "different"); 1688 result = hfsplus_compare_dentry(&test_dentry, 4, "test", &name); 1689 KUNIT_EXPECT_NE(test, 0, result); 1690 1691 free_mock_sb(mock_sb); 1692 } 1693 1694 /* Test edge cases and boundary conditions */ 1695 static void hfsplus_compare_dentry_edge_cases_test(struct kunit *test) 1696 { 1697 struct test_mock_sb *mock_sb; 1698 struct qstr name; 1699 char *long_str; 1700 char *long_str2; 1701 u32 str_size = HFSPLUS_MAX_STRLEN + 1; 1702 struct qstr null_name = { 1703 .name = "a\0b", 1704 .len = 3, 1705 .hash = 0 1706 }; 1707 int result; 1708 1709 mock_sb = setup_mock_sb(); 1710 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1711 1712 setup_mock_dentry(&mock_sb->sb); 1713 mock_sb->nls.char2uni = test_char2uni; 1714 1715 long_str = kzalloc(str_size, GFP_KERNEL); 1716 KUNIT_ASSERT_NOT_NULL(test, long_str); 1717 1718 long_str2 = kzalloc(str_size, GFP_KERNEL); 1719 KUNIT_ASSERT_NOT_NULL(test, long_str2); 1720 1721 /* Test very long strings */ 1722 memset(long_str, 'a', str_size - 1); 1723 long_str[str_size - 1] = '\0'; 1724 1725 create_qstr(&name, long_str); 1726 result = hfsplus_compare_dentry(&test_dentry, str_size - 1, 1727 long_str, &name); 1728 KUNIT_EXPECT_EQ(test, 0, result); 1729 1730 /* Test with difference at the end of long strings */ 1731 memset(long_str2, 'a', str_size - 1); 1732 long_str2[str_size - 1] = '\0'; 1733 long_str2[str_size - 2] = 'b'; 1734 create_qstr(&name, long_str2); 1735 result = hfsplus_compare_dentry(&test_dentry, str_size - 1, 1736 long_str, &name); 1737 KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */ 1738 1739 /* Test single character differences */ 1740 create_qstr(&name, "b"); 1741 result = hfsplus_compare_dentry(&test_dentry, 1, "a", &name); 1742 KUNIT_EXPECT_LT(test, result, 0); /* 'a' < 'b' */ 1743 1744 create_qstr(&name, "a"); 1745 result = hfsplus_compare_dentry(&test_dentry, 1, "b", &name); 1746 KUNIT_EXPECT_GT(test, result, 0); /* 'b' > 'a' */ 1747 1748 /* Test with null characters in the middle */ 1749 result = hfsplus_compare_dentry(&test_dentry, 3, "a\0b", &null_name); 1750 KUNIT_EXPECT_EQ(test, 0, result); 1751 1752 /* Test all printable ASCII characters */ 1753 create_qstr(&name, "!@#$%^&*()"); 1754 result = hfsplus_compare_dentry(&test_dentry, 10, "!@#$%^&*()", &name); 1755 KUNIT_EXPECT_EQ(test, 0, result); 1756 1757 kfree(long_str); 1758 kfree(long_str2); 1759 free_mock_sb(mock_sb); 1760 } 1761 1762 /* Test combined flag behaviors */ 1763 static void hfsplus_compare_dentry_combined_flags_test(struct kunit *test) 1764 { 1765 struct test_mock_sb *mock_sb; 1766 struct qstr name; 1767 int result; 1768 1769 mock_sb = setup_mock_sb(); 1770 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1771 1772 setup_mock_dentry(&mock_sb->sb); 1773 mock_sb->nls.char2uni = test_char2uni; 1774 1775 /* Test with both casefold and decompose enabled */ 1776 set_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1777 set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1778 1779 create_qstr(&name, "hello"); 1780 result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name); 1781 KUNIT_EXPECT_EQ(test, 0, result); 1782 1783 /* Test special chars with case folding */ 1784 create_qstr(&name, "File/Name"); 1785 result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name); 1786 KUNIT_EXPECT_EQ(test, 0, result); 1787 1788 /* Test with both flags disabled */ 1789 clear_bit(HFSPLUS_SB_CASEFOLD, &mock_sb->sb_info.flags); 1790 clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags); 1791 1792 create_qstr(&name, "hello"); 1793 result = hfsplus_compare_dentry(&test_dentry, 5, "HELLO", &name); 1794 KUNIT_EXPECT_NE(test, 0, result); /* Case sensitive */ 1795 1796 /* But special chars should still be converted */ 1797 create_qstr(&name, "file/name"); 1798 result = hfsplus_compare_dentry(&test_dentry, 9, "file:name", &name); 1799 KUNIT_EXPECT_EQ(test, 0, result); 1800 1801 free_mock_sb(mock_sb); 1802 } 1803 1804 /* 1805 * Test that hfsplus_compare_dentry() treats two names as equal when they 1806 * differ only in the (non-canonical) typed order of the same combining 1807 * marks, since both refer to the same canonically-ordered catalog entry. 1808 */ 1809 static void hfsplus_compare_dentry_combining_reorder_test(struct kunit *test) 1810 { 1811 struct test_mock_sb *mock_sb; 1812 struct qstr name; 1813 int result; 1814 1815 mock_sb = setup_mock_sb(); 1816 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1817 1818 setup_mock_dentry(&mock_sb->sb); 1819 mock_sb->nls.char2uni = test_char2uni_utf8; 1820 1821 create_qstr(&name, "e\xcc\xa3\xcc\x81"); /* dot-below, then acute */ 1822 result = hfsplus_compare_dentry(&test_dentry, 5, "e\xcc\x81\xcc\xa3", 1823 &name); /* acute, then dot-below */ 1824 KUNIT_EXPECT_EQ(test, 0, result); 1825 1826 free_mock_sb(mock_sb); 1827 } 1828 1829 /* 1830 * Test that a precomposed character using one of fsck_hfs's known legacy 1831 * decompositions compares equal to the already-decomposed form of the 1832 * same character - i.e. that hfsplus_compare_dentry() applies the same 1833 * legacy-decomposition correction as hfsplus_asc2uni() does on storage, 1834 * so a lookup finds the entry regardless of which form was typed. 1835 */ 1836 static void hfsplus_compare_dentry_legacy_decomp_test(struct kunit *test) 1837 { 1838 struct test_mock_sb *mock_sb; 1839 struct qstr name; 1840 int result; 1841 1842 mock_sb = setup_mock_sb(); 1843 KUNIT_ASSERT_NOT_NULL(test, mock_sb); 1844 1845 setup_mock_dentry(&mock_sb->sb); 1846 mock_sb->nls.char2uni = test_char2uni_utf8; 1847 1848 /* "N" + COMBINING GRAVE ACCENT (U+0300), already decomposed */ 1849 create_qstr(&name, "N\xcc\x80"); 1850 /* U+01F8, precomposed */ 1851 result = hfsplus_compare_dentry(&test_dentry, 2, "\xc7\xb8", &name); 1852 KUNIT_EXPECT_EQ(test, 0, result); 1853 1854 free_mock_sb(mock_sb); 1855 } 1856 1857 static struct kunit_case hfsplus_unicode_test_cases[] = { 1858 KUNIT_CASE(hfsplus_strcasecmp_test), 1859 KUNIT_CASE(hfsplus_strcmp_test), 1860 KUNIT_CASE(hfsplus_unicode_edge_cases_test), 1861 KUNIT_CASE(hfsplus_unicode_boundary_test), 1862 KUNIT_CASE(hfsplus_uni2asc_basic_test), 1863 KUNIT_CASE(hfsplus_uni2asc_special_chars_test), 1864 KUNIT_CASE(hfsplus_uni2asc_buffer_test), 1865 KUNIT_CASE(hfsplus_uni2asc_corrupted_test), 1866 KUNIT_CASE(hfsplus_uni2asc_edge_cases_test), 1867 KUNIT_CASE(hfsplus_asc2uni_basic_test), 1868 KUNIT_CASE(hfsplus_asc2uni_special_chars_test), 1869 KUNIT_CASE(hfsplus_asc2uni_buffer_limits_test), 1870 KUNIT_CASE(hfsplus_asc2uni_edge_cases_test), 1871 KUNIT_CASE(hfsplus_asc2uni_decompose_test), 1872 KUNIT_CASE(hfsplus_asc2uni_combining_reorder_test), 1873 KUNIT_CASE(hfsplus_unicode_combining_reorder_roundtrip_test), 1874 KUNIT_CASE(hfsplus_asc2uni_legacy_decomp_test), 1875 KUNIT_CASE(hfsplus_asc2uni_legacy_seq_fixup_test), 1876 KUNIT_CASE(hfsplus_hash_dentry_basic_test), 1877 KUNIT_CASE(hfsplus_hash_dentry_casefold_test), 1878 KUNIT_CASE(hfsplus_hash_dentry_special_chars_test), 1879 KUNIT_CASE(hfsplus_hash_dentry_decompose_test), 1880 KUNIT_CASE(hfsplus_hash_dentry_consistency_test), 1881 KUNIT_CASE(hfsplus_hash_dentry_edge_cases_test), 1882 KUNIT_CASE(hfsplus_hash_dentry_combining_reorder_test), 1883 KUNIT_CASE(hfsplus_compare_dentry_basic_test), 1884 KUNIT_CASE(hfsplus_compare_dentry_casefold_test), 1885 KUNIT_CASE(hfsplus_compare_dentry_special_chars_test), 1886 KUNIT_CASE(hfsplus_compare_dentry_length_test), 1887 KUNIT_CASE(hfsplus_compare_dentry_decompose_test), 1888 KUNIT_CASE(hfsplus_compare_dentry_edge_cases_test), 1889 KUNIT_CASE(hfsplus_compare_dentry_combined_flags_test), 1890 KUNIT_CASE(hfsplus_compare_dentry_combining_reorder_test), 1891 KUNIT_CASE(hfsplus_compare_dentry_legacy_decomp_test), 1892 {} 1893 }; 1894 1895 static struct kunit_suite hfsplus_unicode_test_suite = { 1896 .name = "hfsplus_unicode", 1897 .test_cases = hfsplus_unicode_test_cases, 1898 }; 1899 1900 kunit_test_suite(hfsplus_unicode_test_suite); 1901 1902 MODULE_DESCRIPTION("KUnit tests for HFS+ Unicode string operations"); 1903 MODULE_LICENSE("GPL"); 1904 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 1905