Commit e7ddc6ac authored by Hirofumi Ogawa's avatar Hirofumi Ogawa Committed by Arnaldo Carvalho de Melo

[PATCH] Fix VFAT_IOCTL_READDIR_BOTH/_SHORT ioctl (2/5)

This fixes the return value of ioctl() for enables using the same way as
readdir().

put/get_user() return code check patch from John R R Leavitt
<jrrl@steampunk.com>
parent 99df9271
...@@ -366,7 +366,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, ...@@ -366,7 +366,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
lock_kernel(); lock_kernel();
cpos = filp->f_pos; cpos = filp->f_pos;
/* Fake . and .. for the root directory. */ /* Fake . and .. for the root directory. */
if (inode->i_ino == MSDOS_ROOT_INO) { if (inode->i_ino == MSDOS_ROOT_INO) {
while (cpos < 2) { while (cpos < 2) {
if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0) if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0)
...@@ -592,23 +592,23 @@ int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -592,23 +592,23 @@ 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);
} }
static int vfat_ioctl_fill( struct fat_ioctl_filldir_callback {
void * buf, struct dirent __user *dirent;
const char * name, int result;
int name_len, };
loff_t offset,
ino_t ino, static int fat_ioctl_filldir(void *__buf, const char * name, int name_len,
unsigned int d_type) loff_t offset, ino_t ino, unsigned int d_type)
{ {
struct dirent *d1 = (struct dirent *)buf; struct fat_ioctl_filldir_callback *buf = __buf;
struct dirent *d2 = d1 + 1; struct dirent __user *d1 = buf->dirent;
struct dirent __user *d2 = d1 + 1;
int len, slen; int len, slen;
int dotdir; int dotdir;
get_user(len, &d1->d_reclen); if (buf->result)
if (len != 0) { return -EINVAL;
return -1; buf->result++;
}
if ((name_len == 1 && name[0] == '.') || if ((name_len == 1 && name[0] == '.') ||
(name_len == 2 && name[0] == '.' && name[1] == '.')) { (name_len == 2 && name[0] == '.' && name[1] == '.')) {
...@@ -619,60 +619,66 @@ static int vfat_ioctl_fill( ...@@ -619,60 +619,66 @@ static int vfat_ioctl_fill(
len = strlen(name); len = strlen(name);
} }
if (len != name_len) { if (len != name_len) {
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);
slen = name_len - len; slen = name_len - len;
copy_to_user(d1->d_name, name+len+1, slen); if (copy_to_user(d2->d_name, name, len) ||
put_user(0, d1->d_name+slen); put_user(0, d2->d_name + len) ||
put_user(slen, &d1->d_reclen); 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 { } else {
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, len) ||
put_user(0, d1->d_name+len); put_user(0, d1->d_name+len) ||
put_user(len, &d1->d_reclen); put_user(len, &d1->d_reclen))
goto efault;
} }
return 0; return 0;
efault:
buf->result = -EFAULT;
return -EFAULT;
} }
int fat_dir_ioctl(struct inode * inode, struct file * filp, int fat_dir_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int err; struct fat_ioctl_filldir_callback buf;
struct dirent __user *d1 = (struct dirent *)arg;
int ret, shortname, both;
if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2])))
return -EFAULT;
/* /*
* We want to provide an interface for Samba to be able * Yes, we don't need this put_user() absolutely. However old
* to get the short filename for a given long filename. * code didn't return the right value. So, app use this value,
* Samba should use this ioctl instead of readdir() to * in order to check whether it is EOF.
* get the information it needs.
*/ */
if (put_user(0, &d1->d_reclen))
return -EFAULT;
buf.dirent = d1;
buf.result = 0;
switch (cmd) { switch (cmd) {
case VFAT_IOCTL_READDIR_BOTH: { case VFAT_IOCTL_READDIR_SHORT:
struct dirent *d1 = (struct dirent *)arg; shortname = 1;
err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2])); both = 1;
if (err) break;
return err; case VFAT_IOCTL_READDIR_BOTH:
put_user(0, &d1->d_reclen); shortname = 0;
return fat_readdirx(inode,filp,(void *)arg, both = 1;
vfat_ioctl_fill, 0, 1); break;
}
case VFAT_IOCTL_READDIR_SHORT: {
struct dirent *d1 = (struct dirent *)arg;
put_user(0, &d1->d_reclen);
err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
if (err)
return err;
return fat_readdirx(inode,filp,(void *)arg,
vfat_ioctl_fill, 1, 1);
}
default: default:
return -EINVAL; return -EINVAL;
} }
ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir,
return 0; shortname, both);
if (ret >= 0)
ret = buf.result;
return ret;
} }
/***** See if directory is empty */ /***** See if directory is empty */
......
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