X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Ffat%2Fdir.c;h=486725ee99ae68a977bc9ca2d8e8cd47990ed579;hb=1df5a8d004f64b1aa3fb93e0556886ba00ebc979;hp=c16af246d245ddd45daf90a59ef0612fe5904dac;hpb=cece901481bafbf14de8cbd3a89ae869ea881055;p=linux-2.6 diff --git a/fs/fat/dir.c b/fs/fat/dir.c index c16af246d2..486725ee99 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -124,8 +124,8 @@ static inline int fat_get_entry(struct inode *dir, loff_t *pos, * 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, - struct nls_table *nls) +static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int len, + int uni_xlate, struct nls_table *nls) { wchar_t *ip, ec; unsigned char *op, nc; @@ -135,10 +135,11 @@ static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, ip = uni; op = ascii; - while (*ip) { + 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,16 +150,19 @@ static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, 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: filename was truncated while " + "converting."); + } + *op = 0; return (op - ascii); } @@ -243,7 +247,7 @@ static int fat_parse_long(struct inode *dir, loff_t *pos, unsigned char id, slot, slots, alias_checksum; if (!*unicode) { - *unicode = (wchar_t *)__get_free_page(GFP_KERNEL); + *unicode = __getname(); if (!*unicode) { brelse(*bh); return -ENOMEM; @@ -311,9 +315,11 @@ int fat_search_long(struct inode *inode, const unsigned char *name, 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; + unsigned char nr_slots; + int xlate_len; wchar_t *unicode = NULL; - unsigned char work[8], bufname[260]; /* 256 + 4 */ + unsigned char work[MSDOS_NAME]; + 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 +327,10 @@ int fat_search_long(struct inode *inode, const unsigned char *name, loff_t cpos = 0; int chl, i, j, last_u, err; + bufname = __getname(); + if (!bufname) + return -ENOMEM; + err = -ENOENT; while(1) { if (fat_get_entry(inode, &cpos, &bh, &de) == -1) @@ -351,7 +361,8 @@ parse_record: if (work[0] == 0x05) work[0] = 0xE5; for (i = 0, j = 0, last_u = 0; i < 8;) { - if (!work[i]) break; + if (!work[i]) + break; chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_BASE); @@ -365,13 +376,15 @@ parse_record: } j = last_u; fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); - for (i = 0; i < 3;) { - if (!de->ext[i]) break; - chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i, + for (i = 8; i < MSDOS_NAME;) { + if (!work[i]) + break; + chl = fat_shortname2uni(nls_disk, &work[i], + MSDOS_NAME - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_EXT); if (chl <= 1) { - if (de->ext[i] != ' ') + if (work[i] != ' ') last_u = j; } else { last_u = j; @@ -383,8 +396,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, PATH_MAX) + :uni16_to_x8(bufname, bufuname, PATH_MAX, 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 +406,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, PATH_MAX) + :uni16_to_x8(bufname, unicode, PATH_MAX, uni_xlate, nls_io); if (xlate_len != name_len) continue; if ((!anycase && !memcmp(name, bufname, xlate_len)) || @@ -413,8 +426,10 @@ Found: sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); err = 0; EODir: + if (bufname) + __putname(bufname); if (unicode) - free_page((unsigned long)unicode); + __putname(unicode); return err; } @@ -422,7 +437,7 @@ EODir: EXPORT_SYMBOL_GPL(fat_search_long); struct fat_ioctl_filldir_callback { - struct dirent __user *dirent; + void __user *dirent; int result; /* for dir ioctl */ const char *longname; @@ -445,7 +460,7 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, int fill_len; wchar_t bufuname[14]; wchar_t *unicode = NULL; - unsigned char c, work[8], bufname[56], *ptname = bufname; + unsigned char c, work[MSDOS_NAME], bufname[56], *ptname = bufname; unsigned long lpos, dummy, *furrfu = &lpos; int uni_xlate = sbi->options.unicode_xlate; int isvfat = sbi->options.isvfat; @@ -527,7 +542,8 @@ parse_record: if (work[0] == 0x05) work[0] = 0xE5; for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) { - if (!(c = work[i])) break; + if (!(c = work[i])) + break; chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_BASE); @@ -549,9 +565,10 @@ parse_record: j = last_u; fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); ptname[i++] = '.'; - for (i2 = 0; i2 < 3;) { - if (!(c = de->ext[i2])) break; - chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2, + for (i2 = 8; i2 < MSDOS_NAME;) { + if (!(c = work[i2])) + break; + chl = fat_shortname2uni(nls_disk, &work[i2], MSDOS_NAME - i2, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_EXT); if (chl <= 1) { @@ -563,8 +580,8 @@ parse_record: } } else { last_u = j; - for (chi = 0; chi < chl && i2 < 3; chi++) { - ptname[i++] = de->ext[i2++]; + for (chi = 0; chi < chl && i2 < MSDOS_NAME; chi++) { + ptname[i++] = work[i2++]; last = i; } } @@ -593,7 +610,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; @@ -602,10 +619,10 @@ parse_record: /* convert the unicode long name. 261 is maximum size * of unicode buffer. (13 * slots + nul) */ void *longname = unicode + 261; - int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0])); + int buf_size = PATH_MAX - (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; @@ -635,7 +652,7 @@ EODir: FillFailed: brelse(bh); if (unicode) - free_page((unsigned long)unicode); + __putname(unicode); out: unlock_kernel(); return ret; @@ -647,62 +664,85 @@ static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) return __fat_readdir(inode, filp, dirent, filldir, 0, 0); } -static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, - loff_t offset, u64 ino, unsigned int d_type) +#define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type) \ +static int func(void *__buf, const char *name, int name_len, \ + loff_t offset, u64 ino, unsigned int d_type) \ +{ \ + struct fat_ioctl_filldir_callback *buf = __buf; \ + struct dirent_type __user *d1 = buf->dirent; \ + struct dirent_type __user *d2 = d1 + 1; \ + \ + if (buf->result) \ + return -EINVAL; \ + buf->result++; \ + \ + if (name != NULL) { \ + /* dirent has only short name */ \ + if (name_len >= sizeof(d1->d_name)) \ + name_len = sizeof(d1->d_name) - 1; \ + \ + if (put_user(0, d2->d_name) || \ + put_user(0, &d2->d_reclen) || \ + copy_to_user(d1->d_name, name, name_len) || \ + put_user(0, d1->d_name + name_len) || \ + put_user(name_len, &d1->d_reclen)) \ + goto efault; \ + } else { \ + /* dirent has short and long name */ \ + const char *longname = buf->longname; \ + int long_len = buf->long_len; \ + const char *shortname = buf->shortname; \ + int short_len = buf->short_len; \ + \ + if (long_len >= sizeof(d1->d_name)) \ + long_len = sizeof(d1->d_name) - 1; \ + if (short_len >= sizeof(d1->d_name)) \ + short_len = sizeof(d1->d_name) - 1; \ + \ + if (copy_to_user(d2->d_name, longname, long_len) || \ + put_user(0, d2->d_name + long_len) || \ + put_user(long_len, &d2->d_reclen) || \ + put_user(ino, &d2->d_ino) || \ + put_user(offset, &d2->d_off) || \ + copy_to_user(d1->d_name, shortname, short_len) || \ + put_user(0, d1->d_name + short_len) || \ + put_user(short_len, &d1->d_reclen)) \ + goto efault; \ + } \ + return 0; \ +efault: \ + buf->result = -EFAULT; \ + return -EFAULT; \ +} + +FAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, dirent) + +static int fat_ioctl_readdir(struct inode *inode, struct file *filp, + void __user *dirent, filldir_t filldir, + int short_only, int both) { - struct fat_ioctl_filldir_callback *buf = __buf; - struct dirent __user *d1 = buf->dirent; - struct dirent __user *d2 = d1 + 1; - - if (buf->result) - return -EINVAL; - buf->result++; - - if (name != NULL) { - /* dirent has only short name */ - if (name_len >= sizeof(d1->d_name)) - name_len = sizeof(d1->d_name) - 1; - - if (put_user(0, d2->d_name) || - put_user(0, &d2->d_reclen) || - copy_to_user(d1->d_name, name, name_len) || - put_user(0, d1->d_name + name_len) || - put_user(name_len, &d1->d_reclen)) - goto efault; - } else { - /* dirent has short and long name */ - const char *longname = buf->longname; - int long_len = buf->long_len; - const char *shortname = buf->shortname; - int short_len = buf->short_len; - - if (long_len >= sizeof(d1->d_name)) - long_len = sizeof(d1->d_name) - 1; - if (short_len >= sizeof(d1->d_name)) - short_len = sizeof(d1->d_name) - 1; - - if (copy_to_user(d2->d_name, longname, long_len) || - put_user(0, d2->d_name + long_len) || - put_user(long_len, &d2->d_reclen) || - put_user(ino, &d2->d_ino) || - put_user(offset, &d2->d_off) || - copy_to_user(d1->d_name, shortname, short_len) || - put_user(0, d1->d_name + short_len) || - put_user(short_len, &d1->d_reclen)) - goto efault; + struct fat_ioctl_filldir_callback buf; + int ret; + + buf.dirent = dirent; + buf.result = 0; + mutex_lock(&inode->i_mutex); + ret = -ENOENT; + if (!IS_DEADDIR(inode)) { + ret = __fat_readdir(inode, filp, &buf, filldir, + short_only, both); } - return 0; -efault: - buf->result = -EFAULT; - return -EFAULT; + mutex_unlock(&inode->i_mutex); + if (ret >= 0) + ret = buf.result; + return ret; } -static int fat_dir_ioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long arg) +static int fat_dir_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) { - struct fat_ioctl_filldir_callback buf; - struct dirent __user *d1; - int ret, short_only, both; + struct dirent __user *d1 = (struct dirent __user *)arg; + int short_only, both; switch (cmd) { case VFAT_IOCTL_READDIR_SHORT: @@ -717,7 +757,6 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, return fat_generic_ioctl(inode, filp, cmd, arg); } - d1 = (struct dirent __user *)arg; if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2]))) return -EFAULT; /* @@ -728,69 +767,48 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, if (put_user(0, &d1->d_reclen)) return -EFAULT; - buf.dirent = d1; - buf.result = 0; - mutex_lock(&inode->i_mutex); - ret = -ENOENT; - if (!IS_DEADDIR(inode)) { - ret = __fat_readdir(inode, filp, &buf, fat_ioctl_filldir, - short_only, both); - } - mutex_unlock(&inode->i_mutex); - if (ret >= 0) - ret = buf.result; - return ret; + return fat_ioctl_readdir(inode, filp, d1, fat_ioctl_filldir, + short_only, both); } #ifdef CONFIG_COMPAT #define VFAT_IOCTL_READDIR_BOTH32 _IOR('r', 1, struct compat_dirent[2]) #define VFAT_IOCTL_READDIR_SHORT32 _IOR('r', 2, struct compat_dirent[2]) -static long fat_compat_put_dirent32(struct dirent *d, - struct compat_dirent __user *d32) -{ - if (!access_ok(VERIFY_WRITE, d32, sizeof(struct compat_dirent))) - return -EFAULT; - - __put_user(d->d_ino, &d32->d_ino); - __put_user(d->d_off, &d32->d_off); - __put_user(d->d_reclen, &d32->d_reclen); - if (__copy_to_user(d32->d_name, d->d_name, d->d_reclen)) - return -EFAULT; +FAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent) - return 0; -} - -static long fat_compat_dir_ioctl(struct file *file, unsigned cmd, +static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, unsigned long arg) { - struct compat_dirent __user *p = compat_ptr(arg); - int ret; - mm_segment_t oldfs = get_fs(); - struct dirent d[2]; + struct inode *inode = filp->f_path.dentry->d_inode; + struct compat_dirent __user *d1 = compat_ptr(arg); + int short_only, both; switch (cmd) { - case VFAT_IOCTL_READDIR_BOTH32: - cmd = VFAT_IOCTL_READDIR_BOTH; - break; case VFAT_IOCTL_READDIR_SHORT32: - cmd = VFAT_IOCTL_READDIR_SHORT; + short_only = 1; + both = 0; + break; + case VFAT_IOCTL_READDIR_BOTH32: + short_only = 0; + both = 1; break; default: return -ENOIOCTLCMD; } - set_fs(KERNEL_DS); - lock_kernel(); - ret = fat_dir_ioctl(file->f_path.dentry->d_inode, file, - cmd, (unsigned long) &d); - unlock_kernel(); - set_fs(oldfs); - if (ret >= 0) { - ret |= fat_compat_put_dirent32(&d[0], p); - ret |= fat_compat_put_dirent32(&d[1], p + 1); - } - return ret; + if (!access_ok(VERIFY_WRITE, d1, sizeof(struct compat_dirent[2]))) + return -EFAULT; + /* + * Yes, we don't need this put_user() absolutely. However old + * code didn't return the right value. So, app use this value, + * in order to check whether it is EOF. + */ + if (put_user(0, &d1->d_reclen)) + return -EFAULT; + + return fat_ioctl_readdir(inode, filp, d1, fat_compat_ioctl_filldir, + short_only, both); } #endif /* CONFIG_COMPAT */