Commit 63edbce1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull ubifs fixes from Al Viro:
 "A couple of ubifs readdir/lseek race fixes.  Stable fodder, really
  nasty..."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  UBIFS: fix a horrid bug
  UBIFS: prepare to fix a horrid bug
parents a61aef7f 605c912b
...@@ -349,31 +349,50 @@ static unsigned int vfs_dent_type(uint8_t type) ...@@ -349,31 +349,50 @@ static unsigned int vfs_dent_type(uint8_t type)
static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
{ {
int err, over = 0; int err, over = 0;
loff_t pos = file->f_pos;
struct qstr nm; struct qstr nm;
union ubifs_key key; union ubifs_key key;
struct ubifs_dent_node *dent; struct ubifs_dent_node *dent;
struct inode *dir = file_inode(file); struct inode *dir = file_inode(file);
struct ubifs_info *c = dir->i_sb->s_fs_info; struct ubifs_info *c = dir->i_sb->s_fs_info;
dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos); dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, pos);
if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2) if (pos > UBIFS_S_KEY_HASH_MASK || pos == 2)
/* /*
* The directory was seek'ed to a senseless position or there * The directory was seek'ed to a senseless position or there
* are no more entries. * are no more entries.
*/ */
return 0; return 0;
if (file->f_version == 0) {
/*
* The file was seek'ed, which means that @file->private_data
* is now invalid. This may also be just the first
* 'ubifs_readdir()' invocation, in which case
* @file->private_data is NULL, and the below code is
* basically a no-op.
*/
kfree(file->private_data);
file->private_data = NULL;
}
/*
* 'generic_file_llseek()' unconditionally sets @file->f_version to
* zero, and we use this for detecting whether the file was seek'ed.
*/
file->f_version = 1;
/* File positions 0 and 1 correspond to "." and ".." */ /* File positions 0 and 1 correspond to "." and ".." */
if (file->f_pos == 0) { if (pos == 0) {
ubifs_assert(!file->private_data); ubifs_assert(!file->private_data);
over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR); over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR);
if (over) if (over)
return 0; return 0;
file->f_pos = 1; file->f_pos = pos = 1;
} }
if (file->f_pos == 1) { if (pos == 1) {
ubifs_assert(!file->private_data); ubifs_assert(!file->private_data);
over = filldir(dirent, "..", 2, 1, over = filldir(dirent, "..", 2, 1,
parent_ino(file->f_path.dentry), DT_DIR); parent_ino(file->f_path.dentry), DT_DIR);
...@@ -389,7 +408,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) ...@@ -389,7 +408,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
goto out; goto out;
} }
file->f_pos = key_hash_flash(c, &dent->key); file->f_pos = pos = key_hash_flash(c, &dent->key);
file->private_data = dent; file->private_data = dent;
} }
...@@ -397,17 +416,16 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) ...@@ -397,17 +416,16 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
if (!dent) { if (!dent) {
/* /*
* The directory was seek'ed to and is now readdir'ed. * The directory was seek'ed to and is now readdir'ed.
* Find the entry corresponding to @file->f_pos or the * Find the entry corresponding to @pos or the closest one.
* closest one.
*/ */
dent_key_init_hash(c, &key, dir->i_ino, file->f_pos); dent_key_init_hash(c, &key, dir->i_ino, pos);
nm.name = NULL; nm.name = NULL;
dent = ubifs_tnc_next_ent(c, &key, &nm); dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) { if (IS_ERR(dent)) {
err = PTR_ERR(dent); err = PTR_ERR(dent);
goto out; goto out;
} }
file->f_pos = key_hash_flash(c, &dent->key); file->f_pos = pos = key_hash_flash(c, &dent->key);
file->private_data = dent; file->private_data = dent;
} }
...@@ -419,7 +437,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) ...@@ -419,7 +437,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
ubifs_inode(dir)->creat_sqnum); ubifs_inode(dir)->creat_sqnum);
nm.len = le16_to_cpu(dent->nlen); nm.len = le16_to_cpu(dent->nlen);
over = filldir(dirent, dent->name, nm.len, file->f_pos, over = filldir(dirent, dent->name, nm.len, pos,
le64_to_cpu(dent->inum), le64_to_cpu(dent->inum),
vfs_dent_type(dent->type)); vfs_dent_type(dent->type));
if (over) if (over)
...@@ -435,9 +453,17 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) ...@@ -435,9 +453,17 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
} }
kfree(file->private_data); kfree(file->private_data);
file->f_pos = key_hash_flash(c, &dent->key); file->f_pos = pos = key_hash_flash(c, &dent->key);
file->private_data = dent; file->private_data = dent;
cond_resched(); cond_resched();
if (file->f_version == 0)
/*
* The file was seek'ed meanwhile, lets return and start
* reading direntries from the new position on the next
* invocation.
*/
return 0;
} }
out: out:
...@@ -448,15 +474,13 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) ...@@ -448,15 +474,13 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
kfree(file->private_data); kfree(file->private_data);
file->private_data = NULL; file->private_data = NULL;
/* 2 is a special value indicating that there are no more direntries */
file->f_pos = 2; file->f_pos = 2;
return 0; return 0;
} }
/* If a directory is seeked, we have to free saved readdir() state */
static loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int whence) static loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int whence)
{ {
kfree(file->private_data);
file->private_data = NULL;
return generic_file_llseek(file, offset, whence); return generic_file_llseek(file, offset, whence);
} }
......
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