Commit 928a4771 authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Linus Torvalds

fat: fix fake_offset handling on error path

For the root directory, .  and ..  are faked (using dir_emit_dots()) and
ctx->pos is reset from 2 to 0.

A corrupted root directory could cause fat_get_entry() to fail, but
->iterate() (fat_readdir()) reports progress to the VFS (with ctx->pos
rewound to 0), so any following calls to ->iterate() continue to return
the same entries again and again.

The result is that userspace will never see the end of the directory,
causing e.g.  'ls' to hang in a getdents() loop.

[hirofumi@mail.parknet.co.jp: cleanup and make sure to correct fake_offset]
Reported-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Tested-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: default avatarRichard Weinberger <richard.weinberger@gmail.com>
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1817889e
...@@ -610,9 +610,9 @@ static int __fat_readdir(struct inode *inode, struct file *file, ...@@ -610,9 +610,9 @@ static int __fat_readdir(struct inode *inode, struct file *file,
int status = fat_parse_long(inode, &cpos, &bh, &de, int status = fat_parse_long(inode, &cpos, &bh, &de,
&unicode, &nr_slots); &unicode, &nr_slots);
if (status < 0) { if (status < 0) {
ctx->pos = cpos; bh = NULL;
ret = status; ret = status;
goto out; goto end_of_dir;
} else if (status == PARSE_INVALID) } else if (status == PARSE_INVALID)
goto record_end; goto record_end;
else if (status == PARSE_NOT_LONGNAME) else if (status == PARSE_NOT_LONGNAME)
...@@ -654,8 +654,9 @@ static int __fat_readdir(struct inode *inode, struct file *file, ...@@ -654,8 +654,9 @@ static int __fat_readdir(struct inode *inode, struct file *file,
fill_len = short_len; fill_len = short_len;
start_filldir: start_filldir:
if (!fake_offset)
ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry);
if (fake_offset && ctx->pos < 2)
ctx->pos = 2;
if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) { if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) {
if (!dir_emit_dot(file, ctx)) if (!dir_emit_dot(file, ctx))
...@@ -681,7 +682,11 @@ static int __fat_readdir(struct inode *inode, struct file *file, ...@@ -681,7 +682,11 @@ static int __fat_readdir(struct inode *inode, struct file *file,
fake_offset = 0; fake_offset = 0;
ctx->pos = cpos; ctx->pos = cpos;
goto get_new; goto get_new;
end_of_dir: end_of_dir:
if (fake_offset && cpos < 2)
ctx->pos = 2;
else
ctx->pos = cpos; ctx->pos = cpos;
fill_failed: fill_failed:
brelse(bh); brelse(bh);
...@@ -689,6 +694,7 @@ static int __fat_readdir(struct inode *inode, struct file *file, ...@@ -689,6 +694,7 @@ static int __fat_readdir(struct inode *inode, struct file *file,
__putname(unicode); __putname(unicode);
out: out:
mutex_unlock(&sbi->s_lock); mutex_unlock(&sbi->s_lock);
return ret; return ret;
} }
......
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