1595 lines · 46.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * KUnit tests for HFS+ Unicode string operations * * Copyright (C) 2025 Viacheslav Dubeyko <slava@dubeyko.com> */ #include <kunit/test.h>#include <linux/nls.h>#include <linux/dcache.h>#include <linux/stringhash.h>#include "hfsplus_fs.h" struct test_mock_string_env {	struct hfsplus_unistr str1;	struct hfsplus_unistr str2;	char *buf;	u32 buf_size;}; static struct test_mock_string_env *setup_mock_str_env(u32 buf_size){	struct test_mock_string_env *env; 	env = kzalloc_obj(struct test_mock_string_env);	if (!env)		return NULL; 	env->buf = kzalloc(buf_size, GFP_KERNEL);	if (!env->buf) {		kfree(env);		return NULL;	} 	env->buf_size = buf_size; 	return env;} static void free_mock_str_env(struct test_mock_string_env *env){	kfree(env->buf);	kfree(env);} /* Helper function to create hfsplus_unistr */static void create_unistr(struct hfsplus_unistr *ustr, const char *ascii_str){	int len = strlen(ascii_str);	int i; 	memset(ustr->unicode, 0, sizeof(ustr->unicode)); 	ustr->length = cpu_to_be16(len);	for (i = 0; i < len && i < HFSPLUS_MAX_STRLEN; i++)		ustr->unicode[i] = cpu_to_be16((u16)ascii_str[i]);} static void corrupt_unistr(struct hfsplus_unistr *ustr){	ustr->length = cpu_to_be16(U16_MAX);} /* Test hfsplus_strcasecmp function */static void hfsplus_strcasecmp_test(struct kunit *test){	struct test_mock_string_env *mock_env; 	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);	KUNIT_ASSERT_NOT_NULL(test, mock_env); 	/* Test identical strings */	create_unistr(&mock_env->str1, "hello");	create_unistr(&mock_env->str2, "hello");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	/* Test case insensitive comparison */	create_unistr(&mock_env->str1, "Hello");	create_unistr(&mock_env->str2, "hello");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	create_unistr(&mock_env->str1, "HELLO");	create_unistr(&mock_env->str2, "hello");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	/* Test different strings */	create_unistr(&mock_env->str1, "apple");	create_unistr(&mock_env->str2, "banana");	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "zebra");	create_unistr(&mock_env->str2, "apple");	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	/* Test different lengths */	create_unistr(&mock_env->str1, "test");	create_unistr(&mock_env->str2, "testing");	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "testing");	create_unistr(&mock_env->str2, "test");	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	/* Test empty strings */	create_unistr(&mock_env->str1, "");	create_unistr(&mock_env->str2, "");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	create_unistr(&mock_env->str1, "");	create_unistr(&mock_env->str2, "test");	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	/* Test single characters */	create_unistr(&mock_env->str1, "A");	create_unistr(&mock_env->str2, "a");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	create_unistr(&mock_env->str1, "A");	create_unistr(&mock_env->str2, "B");	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	/* Test maximum length strings */	memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);	mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';	create_unistr(&mock_env->str1, mock_env->buf);	create_unistr(&mock_env->str2, mock_env->buf);	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	/* Change one character in the middle */	mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';	create_unistr(&mock_env->str2, mock_env->buf);	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	/* Test corrupted strings */	create_unistr(&mock_env->str1, "");	corrupt_unistr(&mock_env->str1);	create_unistr(&mock_env->str2, "");	KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	create_unistr(&mock_env->str1, "");	create_unistr(&mock_env->str2, "");	corrupt_unistr(&mock_env->str2);	KUNIT_EXPECT_NE(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	create_unistr(&mock_env->str1, "test");	corrupt_unistr(&mock_env->str1);	create_unistr(&mock_env->str2, "testing");	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "test");	create_unistr(&mock_env->str2, "testing");	corrupt_unistr(&mock_env->str2);	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "testing");	corrupt_unistr(&mock_env->str1);	create_unistr(&mock_env->str2, "test");	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "testing");	create_unistr(&mock_env->str2, "test");	corrupt_unistr(&mock_env->str2);	KUNIT_EXPECT_LT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	free_mock_str_env(mock_env);} /* Test hfsplus_strcmp function (case-sensitive) */static void hfsplus_strcmp_test(struct kunit *test){	struct test_mock_string_env *mock_env; 	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);	KUNIT_ASSERT_NOT_NULL(test, mock_env); 	/* Test identical strings */	create_unistr(&mock_env->str1, "hello");	create_unistr(&mock_env->str2, "hello");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	/* Test case sensitive comparison - should NOT be equal */	create_unistr(&mock_env->str1, "Hello");	create_unistr(&mock_env->str2, "hello");	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2));	 /* 'H' < 'h' in Unicode */	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	/* Test lexicographic ordering */	create_unistr(&mock_env->str1, "apple");	create_unistr(&mock_env->str2, "banana");	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "zebra");	create_unistr(&mock_env->str2, "apple");	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	/* Test different lengths with common prefix */	create_unistr(&mock_env->str1, "test");	create_unistr(&mock_env->str2, "testing");	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "testing");	create_unistr(&mock_env->str2, "test");	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	/* Test empty strings */	create_unistr(&mock_env->str1, "");	create_unistr(&mock_env->str2, "");	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	/* Test maximum length strings */	memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);	mock_env->buf[HFSPLUS_MAX_STRLEN] = '\0';	create_unistr(&mock_env->str1, mock_env->buf);	create_unistr(&mock_env->str2, mock_env->buf);	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	/* Change one character in the middle */	mock_env->buf[HFSPLUS_MAX_STRLEN / 2] = 'b';	create_unistr(&mock_env->str2, mock_env->buf);	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	/* Test corrupted strings */	create_unistr(&mock_env->str1, "");	corrupt_unistr(&mock_env->str1);	create_unistr(&mock_env->str2, "");	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	create_unistr(&mock_env->str1, "");	create_unistr(&mock_env->str2, "");	corrupt_unistr(&mock_env->str2);	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	create_unistr(&mock_env->str1, "test");	corrupt_unistr(&mock_env->str1);	create_unistr(&mock_env->str2, "testing");	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "test");	create_unistr(&mock_env->str2, "testing");	corrupt_unistr(&mock_env->str2);	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "testing");	corrupt_unistr(&mock_env->str1);	create_unistr(&mock_env->str2, "test");	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	create_unistr(&mock_env->str1, "testing");	create_unistr(&mock_env->str2, "test");	corrupt_unistr(&mock_env->str2);	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	free_mock_str_env(mock_env);} /* Test Unicode edge cases */static void hfsplus_unicode_edge_cases_test(struct kunit *test){	struct test_mock_string_env *mock_env; 	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);	KUNIT_ASSERT_NOT_NULL(test, mock_env); 	/* Test with special characters */	mock_env->str1.length = cpu_to_be16(3);	mock_env->str1.unicode[0] = cpu_to_be16(0x00E9); /* é */	mock_env->str1.unicode[1] = cpu_to_be16(0x00F1); /* ñ */	mock_env->str1.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 	mock_env->str2.length = cpu_to_be16(3);	mock_env->str2.unicode[0] = cpu_to_be16(0x00E9); /* é */	mock_env->str2.unicode[1] = cpu_to_be16(0x00F1); /* ñ */	mock_env->str2.unicode[2] = cpu_to_be16(0x00FC); /* ü */ 	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2));	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	/* Test with different special characters */	mock_env->str2.unicode[1] = cpu_to_be16(0x00F2); /* ò */	KUNIT_EXPECT_NE(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	/* Test null characters within string (should be handled correctly) */	mock_env->str1.length = cpu_to_be16(3);	mock_env->str1.unicode[0] = cpu_to_be16('a');	mock_env->str1.unicode[1] = cpu_to_be16(0x0000); /* null */	mock_env->str1.unicode[2] = cpu_to_be16('b'); 	mock_env->str2.length = cpu_to_be16(3);	mock_env->str2.unicode[0] = cpu_to_be16('a');	mock_env->str2.unicode[1] = cpu_to_be16(0x0000); /* null */	mock_env->str2.unicode[2] = cpu_to_be16('b'); 	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	free_mock_str_env(mock_env);} /* Test boundary conditions */static void hfsplus_unicode_boundary_test(struct kunit *test){	struct test_mock_string_env *mock_env;	int i; 	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);	KUNIT_ASSERT_NOT_NULL(test, mock_env); 	/* Test maximum length boundary */	mock_env->str1.length = cpu_to_be16(HFSPLUS_MAX_STRLEN);	mock_env->str2.length = cpu_to_be16(HFSPLUS_MAX_STRLEN); 	for (i = 0; i < HFSPLUS_MAX_STRLEN; i++) {		mock_env->str1.unicode[i] = cpu_to_be16('A');		mock_env->str2.unicode[i] = cpu_to_be16('A');	} 	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2)); 	/* Change last character */	mock_env->str2.unicode[HFSPLUS_MAX_STRLEN - 1] = cpu_to_be16('B');	KUNIT_EXPECT_LT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0); 	/* Test zero length strings */	mock_env->str1.length = cpu_to_be16(0);	mock_env->str2.length = cpu_to_be16(0);	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcmp(&mock_env->str1,						&mock_env->str2));	KUNIT_EXPECT_EQ(test, 0, hfsplus_strcasecmp(&mock_env->str1,						    &mock_env->str2)); 	/* Test one character vs empty */	mock_env->str1.length = cpu_to_be16(1);	mock_env->str1.unicode[0] = cpu_to_be16('A');	mock_env->str2.length = cpu_to_be16(0);	KUNIT_EXPECT_GT(test, hfsplus_strcmp(&mock_env->str1,					     &mock_env->str2), 0);	KUNIT_EXPECT_GT(test, hfsplus_strcasecmp(&mock_env->str1,						 &mock_env->str2), 0); 	free_mock_str_env(mock_env);} /* Mock superblock and NLS table for testing hfsplus_uni2asc */struct test_mock_sb {	struct nls_table nls;	struct hfsplus_sb_info sb_info;	struct super_block sb;}; static struct test_mock_sb *setup_mock_sb(void){	struct test_mock_sb *ptr; 	ptr = kzalloc_obj(struct test_mock_sb);	if (!ptr)		return NULL; 	ptr->nls.charset = "utf8";	ptr->nls.uni2char = NULL; /* Will use default behavior */	ptr->sb_info.nls = &ptr->nls;	ptr->sb.s_fs_info = &ptr->sb_info; 	/* Set default flags - no decomposition, no case folding */	clear_bit(HFSPLUS_SB_NODECOMPOSE, &ptr->sb_info.flags);	clear_bit(HFSPLUS_SB_CASEFOLD, &ptr->sb_info.flags); 	return ptr;} static void free_mock_sb(struct test_mock_sb *ptr){	kfree(ptr);} /* Simple uni2char implementation for testing */static int test_uni2char(wchar_t uni, unsigned char *out, int boundlen){	if (boundlen <= 0)		return -ENAMETOOLONG; 	if (uni < 0x80) {		*out = (unsigned char)uni;		return 1;	} 	/* For non-ASCII, just use '?' as fallback */	*out = '?';	return 1;} /* Test hfsplus_uni2asc basic functionality */static void hfsplus_uni2asc_basic_test(struct kunit *test){	struct test_mock_sb *mock_sb;	struct test_mock_string_env *mock_env;	int len, result; 	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);	KUNIT_ASSERT_NOT_NULL(test, mock_env); 	mock_sb = setup_mock_sb();	KUNIT_ASSERT_NOT_NULL(test, mock_sb); 	mock_sb->nls.uni2char = test_uni2char; 	/* Test simple ASCII string conversion */	create_unistr(&mock_env->str1, "hello");	len = mock_env->buf_size;	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,				     mock_env->buf, &len); 	KUNIT_EXPECT_EQ(test, 0, result);	KUNIT_EXPECT_EQ(test, 5, len);	KUNIT_EXPECT_STREQ(test, "hello", mock_env->buf); 	/* Test empty string */	create_unistr(&mock_env->str1, "");	len = mock_env->buf_size;	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,				     mock_env->buf, &len); 	KUNIT_EXPECT_EQ(test, 0, result);	KUNIT_EXPECT_EQ(test, 0, len); 	/* Test single character */	create_unistr(&mock_env->str1, "A");	len = mock_env->buf_size;	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,				     mock_env->buf, &len); 	KUNIT_EXPECT_EQ(test, 0, result);	KUNIT_EXPECT_EQ(test, 1, len);	KUNIT_EXPECT_EQ(test, 'A', mock_env->buf[0]); 	free_mock_str_env(mock_env);	free_mock_sb(mock_sb);} /* Test special character handling */static void hfsplus_uni2asc_special_chars_test(struct kunit *test){	struct test_mock_sb *mock_sb;	struct test_mock_string_env *mock_env;	int len, result; 	mock_env = setup_mock_str_env(HFSPLUS_MAX_STRLEN + 1);	KUNIT_ASSERT_NOT_NULL(test, mock_env); 	mock_sb = setup_mock_sb();	KUNIT_ASSERT_NOT_NULL(test, mock_sb); 	mock_sb->nls.uni2char = test_uni2char; 	/* Test null character conversion (should become 0x2400) */	mock_env->str1.length = cpu_to_be16(1);	mock_env->str1.unicode[0] = cpu_to_be16(0x0000);	len = mock_env->buf_size;	result = hfsplus_uni2asc_str(&mock_sb->sb, &mock_env->str1,				     mock_env->buf, &len);

Lines 1–500 of 1595.