lkml.org 
[lkml]   [2008]   [Feb]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] vfat: bug fix for vfat cannot handle filename with 255 asian characters when mounted with utf8
This patch fix the problem that the buffer allocated for convert of
unicode to utf8 in fat/dir.c is too small.
And cannot handle filename with 255 asian characters when mounted with
utf8 options.

Also it fix the filename length limitation checking in vfat/namei.c that
the filename length should be checked against the number of converted
unicode characters.
Not the length before NLS/UTF8 converted.

Any comments ?


Signed-off-by: Keith Mok <ek9852@gmail.com>

---
Keith Mok




--- linux-source-2.6.22/fs/vfat/namei.c.orig 2007-07-09 07:32:17.000000000 +0800
+++ linux-source-2.6.22/fs/vfat/namei.c 2008-02-13 22:56:14.000000000 +0800
@@ -176,15 +176,8 @@ static inline int vfat_is_used_badchars(
for (i = 0; i < len; i++)
if (vfat_bad_char(s[i]))
return -EINVAL;
- return 0;
-}
-
-static int vfat_valid_longname(const unsigned char *name, unsigned int len)
-{
- if (name[len - 1] == ' ')
+ if(s[i-1] == 0x0020) /* last character cannot be space */
return -EINVAL;
- if (len >= 256)
- return -ENAMETOOLONG;
return 0;
}

@@ -489,7 +482,7 @@ xlate_to_uni(const unsigned char *name,
} else {
if (nls) {
for (i = 0, ip = name, op = outname, *outlen = 0;
- i < len && *outlen <= 260;
+ i < len && *outlen <= 255;
*outlen += 1)
{
if (escape && (*ip == ':')) {
@@ -527,7 +520,7 @@ xlate_to_uni(const unsigned char *name,
}
} else {
for (i = 0, ip = name, op = outname, *outlen = 0;
- i < len && *outlen <= 260;
+ i < len && *outlen <= 255;
i++, *outlen += 1)
{
*op++ = *ip++;
@@ -535,7 +528,7 @@ xlate_to_uni(const unsigned char *name,
}
}
}
- if (*outlen > 260)
+ if (*outlen > 255)
return -ENAMETOOLONG;

*longlen = *outlen;
@@ -574,9 +567,6 @@ static int vfat_build_slots(struct inode
loff_t offset;

*nr_slots = 0;
- err = vfat_valid_longname(name, len);
- if (err)
- return err;

page = __get_free_page(GFP_KERNEL);
if (!page)
--- linux-source-2.6.22/fs/fat/dir.c.orig 2007-07-09 07:32:17.000000000 +0800
+++ linux-source-2.6.22/fs/fat/dir.c 2008-02-14 00:42:52.000000000 +0800
@@ -124,7 +124,7 @@ static inline int fat_get_entry(struct i
* but ignore that right now.
* Ahem... Stack smashing in ring 0 isn't fun. Fixed.
*/
-static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
+static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len, int uni_xlate,
struct nls_table *nls)
{
wchar_t *ip, ec;
@@ -135,10 +135,13 @@ static int uni16_to_x8(unsigned char *as
ip = uni;
op = ascii;

- while (*ip) {
+ BUG_ON(len <= NLS_MAX_CHARSET_SIZE);
+
+ while (*ip && (len-NLS_MAX_CHARSET_SIZE)>0) {
ec = *ip++;
if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {
op += charlen;
+ len -= charlen;
} else {
if (uni_xlate == 1) {
*op = ':';
@@ -149,15 +152,18 @@ static int uni16_to_x8(unsigned char *as
ec >>= 4;
}
op += 5;
+ len -= 5;
} else {
*op++ = '?';
+ len--;
}
}
- /* We have some slack there, so it's OK */
- if (op>ascii+256) {
- op = ascii + 256;
- break;
- }
+ }
+
+ if(unlikely(*ip)) {
+ printk(KERN_WARNING
+ "FAT: truncated while convert unicode characters in %s\n",
+ __FUNCTION__);
}
*op = 0;
return (op - ascii);
@@ -311,9 +317,11 @@ int fat_search_long(struct inode *inode,
struct nls_table *nls_io = sbi->nls_io;
struct nls_table *nls_disk = sbi->nls_disk;
wchar_t bufuname[14];
- unsigned char xlate_len, nr_slots;
+ int xlate_len;
+ unsigned char nr_slots;
wchar_t *unicode = NULL;
- unsigned char work[8], bufname[260]; /* 256 + 4 */
+ unsigned char work[8];
+ unsigned char *bufname = NULL;
int uni_xlate = sbi->options.unicode_xlate;
int utf8 = sbi->options.utf8;
int anycase = (sbi->options.name_check != 's');
@@ -321,6 +329,10 @@ int fat_search_long(struct inode *inode,
loff_t cpos = 0;
int chl, i, j, last_u, err;

+ bufname = (unsigned char*)__get_free_page(GFP_KERNEL);
+ if (!bufname) {
+ return -ENOMEM;
+ }
err = -ENOENT;
while(1) {
if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
@@ -383,8 +395,8 @@ parse_record:

bufuname[last_u] = 0x0000;
xlate_len = utf8
- ?utf8_wcstombs(bufname, bufuname, sizeof(bufname))
- :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
+ ?utf8_wcstombs(bufname, bufuname, PAGE_SIZE)
+ :uni16_to_x8(bufname, bufuname, PAGE_SIZE, uni_xlate, nls_io);
if (xlate_len == name_len)
if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
(anycase && !nls_strnicmp(nls_io, name, bufname,
@@ -393,8 +405,8 @@ parse_record:

if (nr_slots) {
xlate_len = utf8
- ?utf8_wcstombs(bufname, unicode, sizeof(bufname))
- :uni16_to_x8(bufname, unicode, uni_xlate, nls_io);
+ ?utf8_wcstombs(bufname, unicode, PAGE_SIZE)
+ :uni16_to_x8(bufname, unicode, PAGE_SIZE, uni_xlate, nls_io);
if (xlate_len != name_len)
continue;
if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
@@ -413,6 +425,8 @@ Found:
sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
err = 0;
EODir:
+ if (bufname)
+ free_page((unsigned long)bufname);
if (unicode)
free_page((unsigned long)unicode);

@@ -593,7 +607,7 @@ parse_record:
if (isvfat) {
bufuname[j] = 0x0000;
i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname))
- : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
+ : uni16_to_x8(bufname, bufuname, sizeof(bufname), uni_xlate, nls_io);
}

fill_name = bufname;
@@ -605,7 +619,7 @@ parse_record:
int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0]));
int long_len = utf8
? utf8_wcstombs(longname, unicode, buf_size)
- : uni16_to_x8(longname, unicode, uni_xlate, nls_io);
+ : uni16_to_x8(longname, unicode, buf_size, uni_xlate, nls_io);

if (!both) {
fill_name = longname;



\
 
 \ /
  Last update: 2008-02-13 17:57    [W:0.042 / U:0.644 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site