Commit 2a0e760a authored by Hirofumi Ogawa's avatar Hirofumi Ogawa Committed by Linus Torvalds

[PATCH] FAT: fix VFAT_IOCTL_READDIR_BOTH ioctl

If "utf8" option was specified and app uses VFAT_IOCTL_READDIR_BOTH
ioctl, utf8_wcstombs() is used. utf8_wcstombs() doesn't add the nul
terminate. Then fat_ioctl_filldir() uses a wrong length of name by
strlen().

This patch passes the correct length to fat_ioctl_filldir(), and
doesn't use nul terminate anymore.

Many helps by Alex Villacís Lasso. Thanks.
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2680a9bd
...@@ -325,16 +325,28 @@ int fat_search_long(struct inode *inode, const unsigned char *name, ...@@ -325,16 +325,28 @@ int fat_search_long(struct inode *inode, const unsigned char *name,
return res; return res;
} }
struct fat_ioctl_filldir_callback {
struct dirent __user *dirent;
int result;
/* for dir ioctl */
const char *longname;
int long_len;
const char *shortname;
int short_len;
};
static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
filldir_t filldir, int shortnames, int both) filldir_t filldir, int short_only, int both)
{ {
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct buffer_head *bh; struct buffer_head *bh;
struct msdos_dir_entry *de; struct msdos_dir_entry *de;
struct nls_table *nls_io = MSDOS_SB(sb)->nls_io; struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk; struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
wchar_t bufuname[14];
unsigned char long_slots; unsigned char long_slots;
const char *fill_name;
int fill_len;
wchar_t bufuname[14];
wchar_t *unicode = NULL; wchar_t *unicode = NULL;
unsigned char c, work[8], bufname[56], *ptname = bufname; unsigned char c, work[8], bufname[56], *ptname = bufname;
unsigned long lpos, dummy, *furrfu = &lpos; unsigned long lpos, dummy, *furrfu = &lpos;
...@@ -397,8 +409,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, ...@@ -397,8 +409,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
unsigned char alias_checksum; unsigned char alias_checksum;
if (!unicode) { if (!unicode) {
unicode = (wchar_t *) unicode = (wchar_t *)__get_free_page(GFP_KERNEL);
__get_free_page(GFP_KERNEL);
if (!unicode) { if (!unicode) {
filp->f_pos = cpos; filp->f_pos = cpos;
brelse(bh); brelse(bh);
...@@ -533,26 +544,35 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, ...@@ -533,26 +544,35 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
: uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
} }
if (!long_slots||shortnames) { fill_name = bufname;
if (both) fill_len = i;
bufname[i] = '\0'; if (!short_only && long_slots) {
if (filldir(dirent, bufname, i, *furrfu, inum, /* convert the unicode long name. 261 is maximum size
(de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) * of unicode buffer. (13 * slots + nul) */
goto FillFailed; void *longname = unicode + 261;
} else { int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0]));
unsigned char longname[275];
int long_len = utf8 int long_len = utf8
? utf8_wcstombs(longname, unicode, sizeof(longname)) ? utf8_wcstombs(longname, unicode, buf_size)
: uni16_to_x8(longname, unicode, uni_xlate, : uni16_to_x8(longname, unicode, uni_xlate, nls_io);
nls_io);
if (both) { if (!both) {
memcpy(&longname[long_len+1], bufname, i); fill_name = longname;
long_len += i; fill_len = long_len;
} else {
/* hack for fat_ioctl_filldir() */
struct fat_ioctl_filldir_callback *p = dirent;
p->longname = longname;
p->long_len = long_len;
p->shortname = bufname;
p->short_len = i;
fill_name = NULL;
fill_len = 0;
} }
if (filldir(dirent, longname, long_len, *furrfu, inum,
(de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
goto FillFailed;
} }
if (filldir(dirent, fill_name, fill_len, *furrfu, inum,
(de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
goto FillFailed;
RecEnd: RecEnd:
furrfu = &lpos; furrfu = &lpos;
...@@ -563,9 +583,8 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, ...@@ -563,9 +583,8 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
FillFailed: FillFailed:
if (bh) if (bh)
brelse(bh); brelse(bh);
if (unicode) { if (unicode)
free_page((unsigned long) unicode); free_page((unsigned long)unicode);
}
out: out:
unlock_kernel(); unlock_kernel();
return ret; return ret;
...@@ -577,49 +596,48 @@ static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -577,49 +596,48 @@ static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
return fat_readdirx(inode, filp, dirent, filldir, 0, 0); return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
} }
struct fat_ioctl_filldir_callback { static int fat_ioctl_filldir(void *__buf, const char *name, int name_len,
struct dirent __user *dirent;
int result;
};
static int fat_ioctl_filldir(void *__buf, const char * name, int name_len,
loff_t offset, ino_t ino, unsigned int d_type) loff_t offset, ino_t ino, unsigned int d_type)
{ {
struct fat_ioctl_filldir_callback *buf = __buf; struct fat_ioctl_filldir_callback *buf = __buf;
struct dirent __user *d1 = buf->dirent; struct dirent __user *d1 = buf->dirent;
struct dirent __user *d2 = d1 + 1; struct dirent __user *d2 = d1 + 1;
int len, slen;
int dotdir;
if (buf->result) if (buf->result)
return -EINVAL; return -EINVAL;
buf->result++; buf->result++;
if ((name_len == 1 && name[0] == '.') || if (name != NULL) {
(name_len == 2 && name[0] == '.' && name[1] == '.')) { /* dirent has only short name */
dotdir = 1; if (name_len >= sizeof(d1->d_name))
len = name_len; name_len = sizeof(d1->d_name) - 1;
} else {
dotdir = 0;
len = strlen(name);
}
if (len != name_len) {
slen = name_len - len;
if (copy_to_user(d2->d_name, name, len) ||
put_user(0, d2->d_name + len) ||
put_user(len, &d2->d_reclen) ||
put_user(ino, &d2->d_ino) ||
put_user(offset, &d2->d_off) ||
copy_to_user(d1->d_name, name+len+1, slen) ||
put_user(0, d1->d_name+slen) ||
put_user(slen, &d1->d_reclen))
goto efault;
} else {
if (put_user(0, d2->d_name) || if (put_user(0, d2->d_name) ||
put_user(0, &d2->d_reclen) || put_user(0, &d2->d_reclen) ||
copy_to_user(d1->d_name, name, len) || copy_to_user(d1->d_name, name, name_len) ||
put_user(0, d1->d_name+len) || put_user(0, d1->d_name + name_len) ||
put_user(len, &d1->d_reclen)) 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; goto efault;
} }
return 0; return 0;
...@@ -633,15 +651,15 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, ...@@ -633,15 +651,15 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp,
{ {
struct fat_ioctl_filldir_callback buf; struct fat_ioctl_filldir_callback buf;
struct dirent __user *d1; struct dirent __user *d1;
int ret, shortname, both; int ret, short_only, both;
switch (cmd) { switch (cmd) {
case VFAT_IOCTL_READDIR_SHORT: case VFAT_IOCTL_READDIR_SHORT:
shortname = 1; short_only = 1;
both = 1; both = 0;
break; break;
case VFAT_IOCTL_READDIR_BOTH: case VFAT_IOCTL_READDIR_BOTH:
shortname = 0; short_only = 0;
both = 1; both = 1;
break; break;
default: default:
...@@ -665,7 +683,7 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp, ...@@ -665,7 +683,7 @@ static int fat_dir_ioctl(struct inode * inode, struct file * filp,
ret = -ENOENT; ret = -ENOENT;
if (!IS_DEADDIR(inode)) { if (!IS_DEADDIR(inode)) {
ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir, ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir,
shortname, both); short_only, both);
} }
up(&inode->i_sem); up(&inode->i_sem);
if (ret >= 0) if (ret >= 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment