return NULL;
}
+/*
+ * In HFS+, a filename can contain / because : is the separator.
+ * The slash is a valid filename character on macOS.
+ * But on Linux, / is the path separator and
+ * it cannot appear in a filename component.
+ * There's a parallel mapping for the NUL character (0 -> U+2400).
+ * NUL terminates strings in C/POSIX but is valid in HFS+ filenames.
+ */
+static inline
+void hfsplus_mac2linux_compatibility_check(u16 symbol, u16 *conversion,
+ int name_type)
+{
+ *conversion = symbol;
+
+ switch (name_type) {
+ case HFS_XATTR_NAME:
+ /* ignore conversion */
+ return;
+
+ default:
+ /* continue logic */
+ break;
+ }
+
+ switch (symbol) {
+ case 0:
+ *conversion = 0x2400;
+ break;
+ case '/':
+ *conversion = ':';
+ break;
+ }
+}
+
static int hfsplus_uni2asc(struct super_block *sb,
const struct hfsplus_unistr *ustr,
- int max_len, char *astr, int *len_p)
+ int max_len, char *astr, int *len_p,
+ int name_type)
{
const hfsplus_unichr *ip;
struct nls_table *nls = HFSPLUS_SB(sb)->nls;
hfsplus_compose_table, c1);
if (ce1)
break;
- switch (c0) {
- case 0:
- c0 = 0x2400;
- break;
- case '/':
- c0 = ':';
- break;
- }
+ hfsplus_mac2linux_compatibility_check(c0, &c0,
+ name_type);
res = nls->uni2char(c0, op, len);
if (res < 0) {
if (res == -ENAMETOOLONG)
}
}
same:
- switch (c0) {
- case 0:
- cc = 0x2400;
- break;
- case '/':
- cc = ':';
- break;
- default:
- cc = c0;
- }
+ hfsplus_mac2linux_compatibility_check(c0, &cc,
+ name_type);
done:
res = nls->uni2char(cc, op, len);
if (res < 0) {
const struct hfsplus_unistr *ustr, char *astr,
int *len_p)
{
- return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p);
+ return hfsplus_uni2asc(sb,
+ ustr, HFSPLUS_MAX_STRLEN,
+ astr, len_p,
+ HFS_REGULAR_NAME);
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_str);
char *astr, int *len_p)
{
return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr,
- HFSPLUS_ATTR_MAX_STRLEN, astr, len_p);
+ HFSPLUS_ATTR_MAX_STRLEN, astr, len_p,
+ HFS_XATTR_NAME);
}
EXPORT_SYMBOL_IF_KUNIT(hfsplus_uni2asc_xattr_str);
/*
- * Convert one or more ASCII characters into a single unicode character.
- * Returns the number of ASCII characters corresponding to the unicode char.
+ * In HFS+, a filename can contain / because : is the separator.
+ * The slash is a valid filename character on macOS.
+ * But on Linux, / is the path separator and
+ * it cannot appear in a filename component.
+ * There's a parallel mapping for the NUL character (0 -> U+2400).
+ * NUL terminates strings in C/POSIX but is valid in HFS+ filenames.
*/
-static inline int asc2unichar(struct super_block *sb, const char *astr, int len,
- wchar_t *uc)
+static inline
+void hfsplus_linux2mac_compatibility_check(wchar_t *uc, int name_type)
{
- int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc);
- if (size <= 0) {
- *uc = '?';
- size = 1;
+ switch (name_type) {
+ case HFS_XATTR_NAME:
+ /* ignore conversion */
+ return;
+
+ default:
+ /* continue logic */
+ break;
}
+
switch (*uc) {
case 0x2400:
*uc = 0;
*uc = '/';
break;
}
+}
+
+/*
+ * Convert one or more ASCII characters into a single unicode character.
+ * Returns the number of ASCII characters corresponding to the unicode char.
+ */
+static inline int asc2unichar(struct super_block *sb, const char *astr, int len,
+ wchar_t *uc, int name_type)
+{
+ int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc);
+
+ if (size <= 0) {
+ *uc = '?';
+ size = 1;
+ }
+
+ hfsplus_linux2mac_compatibility_check(uc, name_type);
return size;
}
int hfsplus_asc2uni(struct super_block *sb,
struct hfsplus_unistr *ustr, int max_unistr_len,
- const char *astr, int len)
+ const char *astr, int len, int name_type)
{
int size, dsize, decompose;
u16 *dstr, outlen = 0;
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
while (outlen < max_unistr_len && len > 0) {
- size = asc2unichar(sb, astr, len, &c);
+ size = asc2unichar(sb, astr, len, &c, name_type);
if (decompose)
dstr = decompose_unichar(c, &dsize, dhangul);
len = str->len;
while (len > 0) {
int dsize;
- size = asc2unichar(sb, astr, len, &c);
+ size = asc2unichar(sb, astr, len, &c, HFS_REGULAR_NAME);
astr += size;
len -= size;
while (len1 > 0 && len2 > 0) {
if (!dsize1) {
- size = asc2unichar(sb, astr1, len1, &c);
+ size = asc2unichar(sb, astr1, len1, &c,
+ HFS_REGULAR_NAME);
astr1 += size;
len1 -= size;
}
if (!dsize2) {
- size = asc2unichar(sb, astr2, len2, &c);
+ size = asc2unichar(sb, astr2, len2, &c,
+ HFS_REGULAR_NAME);
astr2 += size;
len2 -= size;
/* Test simple ASCII string conversion */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, "hello", 5);
+ HFSPLUS_MAX_STRLEN, "hello", 5,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &mock_env->str1, "hello");
/* Test empty string */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, "", 0);
+ HFSPLUS_MAX_STRLEN, "", 0,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
/* Test single character */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, "A", 1);
+ HFSPLUS_MAX_STRLEN, "A", 1,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &mock_env->str1, "A");
/* Test null-terminated string with explicit length */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, "test\0extra", 4);
+ HFSPLUS_MAX_STRLEN, "test\0extra", 4,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &mock_env->str1, "test");
/* Test colon conversion (should become forward slash) */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, ":", 1);
+ HFSPLUS_MAX_STRLEN, ":", 1,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, 1, be16_to_cpu(mock_env->str1.length));
/* Test string with mixed special characters */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, "a:b", 3);
+ HFSPLUS_MAX_STRLEN, "a:b", 3,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
/* Test multiple special characters */
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, ":::", 3);
+ HFSPLUS_MAX_STRLEN, ":::", 3,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(mock_env->str1.length));
memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN);
result = hfsplus_asc2uni(&mock_sb->sb,
&mock_env->str1, HFSPLUS_MAX_STRLEN,
- mock_env->buf, HFSPLUS_MAX_STRLEN);
+ mock_env->buf, HFSPLUS_MAX_STRLEN,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
memset(mock_env->buf, 'a', HFSPLUS_MAX_STRLEN + 5);
result = hfsplus_asc2uni(&mock_sb->sb,
&mock_env->str1, HFSPLUS_MAX_STRLEN,
- mock_env->buf, HFSPLUS_MAX_STRLEN + 5);
+ mock_env->buf, HFSPLUS_MAX_STRLEN + 5,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
KUNIT_EXPECT_EQ(test, HFSPLUS_MAX_STRLEN,
/* Test with smaller max_unistr_len */
result = hfsplus_asc2uni(&mock_sb->sb,
- &mock_env->str1, 5, "toolongstring", 13);
+ &mock_env->str1, 5, "toolongstring", 13,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
KUNIT_EXPECT_EQ(test, 5, be16_to_cpu(mock_env->str1.length));
/* Test zero max length */
- result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4);
+ result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1, 0, "test", 4,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, -ENAMETOOLONG, result);
KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(mock_env->str1.length));
/* Test zero length input */
result = hfsplus_asc2uni(&mock_sb->sb,
- &ustr, HFSPLUS_MAX_STRLEN, "test", 0);
+ &ustr, HFSPLUS_MAX_STRLEN, "test", 0,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, 0, be16_to_cpu(ustr.length));
/* Test input with length mismatch */
result = hfsplus_asc2uni(&mock_sb->sb,
- &ustr, HFSPLUS_MAX_STRLEN, "hello", 3);
+ &ustr, HFSPLUS_MAX_STRLEN, "hello", 3,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &ustr, "hel");
/* Test with various printable ASCII characters */
result = hfsplus_asc2uni(&mock_sb->sb,
- &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9);
+ &ustr, HFSPLUS_MAX_STRLEN, "ABC123!@#", 9,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &ustr, "ABC123!@#");
/* Test null character in the middle */
result = hfsplus_asc2uni(&mock_sb->sb,
- &ustr, HFSPLUS_MAX_STRLEN, test_str, 3);
+ &ustr, HFSPLUS_MAX_STRLEN, test_str, 3,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
KUNIT_EXPECT_EQ(test, 3, be16_to_cpu(ustr.length));
/* Test with decomposition disabled (default) */
clear_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str1,
- HFSPLUS_MAX_STRLEN, "test", 4);
+ HFSPLUS_MAX_STRLEN, "test", 4,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &mock_env->str1, "test");
/* Test with decomposition enabled */
set_bit(HFSPLUS_SB_NODECOMPOSE, &mock_sb->sb_info.flags);
result = hfsplus_asc2uni(&mock_sb->sb, &mock_env->str2,
- HFSPLUS_MAX_STRLEN, "test", 4);
+ HFSPLUS_MAX_STRLEN, "test", 4,
+ HFS_REGULAR_NAME);
KUNIT_EXPECT_EQ(test, 0, result);
check_unistr_content(test, &mock_env->str2, "test");