ntfs: fix lookup of POSIX names with Windows-invalid characters

POSIX FILE_NAME entries may contain characters such as '?' that are
invalid in the Windows namespace. Directory index lookup currently
passes an invalid-character error value to ntfs_collate_names(),
causing lookup and removal of such names to fail with -EINVAL.

Allow callers to disable invalid-character errors when collation is
used for index traversal, and suppress those errors when comparing
POSIX FILE_NAME attributes.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Tested-by: Baolin Liu <liubaolin@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Hyunchul Lee 2026-09-13 09:02:36 +08:00 committed by Namjae Jeon
parent 418452b43d
commit 351bdddcc0
5 changed files with 33 additions and 22 deletions

View File

@ -909,7 +909,7 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
rc = ntfs_collate_names(name, name_len,
(__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)),
a->name_length, 1, IGNORE_CASE,
a->name_length, true, IGNORE_CASE,
upcase, upcase_len);
/*
* If @name collates before a->name, there is no
@ -922,7 +922,7 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
continue;
rc = ntfs_collate_names(name, name_len,
(__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)),
a->name_length, 1, CASE_SENSITIVE,
a->name_length, true, CASE_SENSITIVE,
upcase, upcase_len);
if (rc == -1)
return -ENOENT;
@ -1313,7 +1313,7 @@ static int ntfs_external_attr_find(const __le32 type,
register int rc;
rc = ntfs_collate_names(name, name_len, al_name,
al_name_len, 1, IGNORE_CASE,
al_name_len, true, IGNORE_CASE,
vol->upcase, vol->upcase_len);
/*
* If @name collates before al_name, there is no
@ -1326,7 +1326,7 @@ static int ntfs_external_attr_find(const __le32 type,
continue;
rc = ntfs_collate_names(name, name_len, al_name,
al_name_len, 1, CASE_SENSITIVE,
al_name_len, true, CASE_SENSITIVE,
vol->upcase, vol->upcase_len);
if (rc == -1)
goto not_found;

View File

@ -100,11 +100,11 @@ static int ntfs_collate_file_name(struct ntfs_volume *vol,
{
int rc;
rc = ntfs_file_compare_values(data1, data2, -EINVAL,
IGNORE_CASE, vol->upcase, vol->upcase_len);
rc = ntfs_file_compare_values(data1, data2,
true, IGNORE_CASE, vol->upcase, vol->upcase_len);
if (!rc)
rc = ntfs_file_compare_values(data1, data2,
-EINVAL, CASE_SENSITIVE, vol->upcase, vol->upcase_len);
true, CASE_SENSITIVE, vol->upcase, vol->upcase_len);
return rc;
}

View File

@ -238,7 +238,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
*/
rc = ntfs_collate_names(uname, uname_len,
(__le16 *)&ie->key.file_name.file_name,
ie->key.file_name.file_name_length, 1,
ie->key.file_name.file_name_length, false,
IGNORE_CASE, vol->upcase, vol->upcase_len);
/*
* If uname collates before the name of the current entry, there
@ -257,7 +257,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
*/
rc = ntfs_collate_names(uname, uname_len,
(__le16 *)&ie->key.file_name.file_name,
ie->key.file_name.file_name_length, 1,
ie->key.file_name.file_name_length, false,
CASE_SENSITIVE, vol->upcase, vol->upcase_len);
if (rc == -1)
break;
@ -474,7 +474,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
*/
rc = ntfs_collate_names(uname, uname_len,
(__le16 *)&ie->key.file_name.file_name,
ie->key.file_name.file_name_length, 1,
ie->key.file_name.file_name_length, false,
IGNORE_CASE, vol->upcase, vol->upcase_len);
/*
* If uname collates before the name of the current entry, there
@ -493,7 +493,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
*/
rc = ntfs_collate_names(uname, uname_len,
(__le16 *)&ie->key.file_name.file_name,
ie->key.file_name.file_name_length, 1,
ie->key.file_name.file_name_length, false,
CASE_SENSITIVE, vol->upcase, vol->upcase_len);
if (rc == -1)
break;

View File

@ -234,14 +234,14 @@ bool ntfs_are_names_equal(const __le16 *s1, size_t s1_len,
const __le16 *upcase, const u32 upcase_size);
int ntfs_collate_names(const __le16 *name1, const u32 name1_len,
const __le16 *name2, const u32 name2_len,
const int err_val, const u32 ic,
const bool check_invalid, const u32 ic,
const __le16 *upcase, const u32 upcase_len);
int ntfs_ucsncmp(const __le16 *s1, const __le16 *s2, size_t n);
int ntfs_ucsncasecmp(const __le16 *s1, const __le16 *s2, size_t n,
const __le16 *upcase, const u32 upcase_size);
int ntfs_file_compare_values(const struct file_name_attr *file_name_attr1,
const struct file_name_attr *file_name_attr2,
const int err_val, const u32 ic,
const bool check_invalid, const u32 ic,
const __le16 *upcase, const u32 upcase_len);
int ntfs_nlstoucs(const struct ntfs_volume *vol, const char *ins,
const int ins_len, __le16 **outs, int max_name_len);

View File

@ -64,7 +64,8 @@ bool ntfs_are_names_equal(const __le16 *s1, size_t s1_len,
* @name1_len: first Unicode name length
* @name2: second Unicode name to compare
* @name2_len: second Unicode name length
* @err_val: if @name1 contains an invalid character return this value
* @check_invalid: if true and @name1 contains an invalid character,
* return -EINVAL
* @ic: either CASE_SENSITIVE or IGNORE_CASE
* @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
* @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
@ -74,13 +75,14 @@ bool ntfs_are_names_equal(const __le16 *s1, size_t s1_len,
* -1 if the first name collates before the second one,
* 0 if the names match,
* 1 if the second name collates before the first one, or
* @err_val if an invalid character is found in @name1 during the comparison.
* -EINVAL if @check_invalid is true and an invalid character is found in
* @name1 during the comparison.
*
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
*/
int ntfs_collate_names(const __le16 *name1, const u32 name1_len,
const __le16 *name2, const u32 name2_len,
const int err_val, const u32 ic,
const bool check_invalid, const u32 ic,
const __le16 *upcase, const u32 upcase_len)
{
u32 cnt, min_len;
@ -98,8 +100,8 @@ int ntfs_collate_names(const __le16 *name1, const u32 name1_len,
if (c2 < upcase_len)
c2 = le16_to_cpu(upcase[c2]);
}
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
if (check_invalid && c1 < 64 && legal_ansi_char_array[c1] & 8)
return -EINVAL;
if (c1 < c2)
return -1;
if (c1 > c2)
@ -111,8 +113,8 @@ int ntfs_collate_names(const __le16 *name1, const u32 name1_len,
return 0;
/* name1_len > name2_len */
c1 = le16_to_cpu(*name1);
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
return err_val;
if (check_invalid && c1 < 64 && legal_ansi_char_array[c1] & 8)
return -EINVAL;
return 1;
}
@ -191,14 +193,23 @@ int ntfs_ucsncasecmp(const __le16 *s1, const __le16 *s2, size_t n,
int ntfs_file_compare_values(const struct file_name_attr *file_name_attr1,
const struct file_name_attr *file_name_attr2,
const int err_val, const u32 ic,
const bool check_invalid, const u32 ic,
const __le16 *upcase, const u32 upcase_len)
{
bool compare_check = check_invalid;
/*
* POSIX file names may contain characters that are invalid in the
* Windows namespace, so compare them without treating them as errors.
*/
if (file_name_attr1->file_name_type == FILE_NAME_POSIX)
compare_check = false;
return ntfs_collate_names((__le16 *)&file_name_attr1->file_name,
file_name_attr1->file_name_length,
(__le16 *)&file_name_attr2->file_name,
file_name_attr2->file_name_length,
err_val, ic, upcase, upcase_len);
compare_check, ic, upcase, upcase_len);
}
/*