aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@ftp.linux.org.uk>2006-03-15 16:41:59 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-15 19:31:51 -0500
commit2d7f2ea9c989853310c7f6e8be52cc090cc8e66b (patch)
treea4fab82e393dc525c1f5d7549c0cbd6758e7523e
parentf13b83580acef03a36c785dccc534ccdd7e43084 (diff)
[PATCH] Fix ext2 readdir f_pos re-validation logic
This fixes not one, but _two_, silly (but admittedly hard to hit) bugs in the ext2 filesystem "readdir()" function. It also cleans up the code to avoid the unnecessary goto mess. The bugs were related to re-valiating the f_pos value after somebody had either done an "lseek()" on the directory to an invalid offset, or when the offset had become invalid due to a file being unlinked in the directory. The code would not only set the f_version too eagerly, it would also not update f_pos appropriately for when the offset fixup took place. When that happened, we'd occasionally subsequently fail the readdir() even when we shouldn't (no real harm done, but an ugly printk, and obviously you would end up not necessarily seeing all entries). Thanks to Masoud Sharbiani <masouds@google.com> who noticed the problem and had a test-case for it, and also fixed up a thinko in the first version of this patch. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Acked-by: Masoud Sharbiani <masouds@google.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/ext2/dir.c28
1 files changed, 12 insertions, 16 deletions
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 7442bdd1267a..b3dbd716cd3a 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -256,11 +256,10 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
256 unsigned long npages = dir_pages(inode); 256 unsigned long npages = dir_pages(inode);
257 unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); 257 unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
258 unsigned char *types = NULL; 258 unsigned char *types = NULL;
259 int need_revalidate = (filp->f_version != inode->i_version); 259 int need_revalidate = filp->f_version != inode->i_version;
260 int ret;
261 260
262 if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) 261 if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
263 goto success; 262 return 0;
264 263
265 if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) 264 if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
266 types = ext2_filetype_table; 265 types = ext2_filetype_table;
@@ -275,12 +274,15 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
275 "bad page in #%lu", 274 "bad page in #%lu",
276 inode->i_ino); 275 inode->i_ino);
277 filp->f_pos += PAGE_CACHE_SIZE - offset; 276 filp->f_pos += PAGE_CACHE_SIZE - offset;
278 ret = -EIO; 277 return -EIO;
279 goto done;
280 } 278 }
281 kaddr = page_address(page); 279 kaddr = page_address(page);
282 if (need_revalidate) { 280 if (unlikely(need_revalidate)) {
283 offset = ext2_validate_entry(kaddr, offset, chunk_mask); 281 if (offset) {
282 offset = ext2_validate_entry(kaddr, offset, chunk_mask);
283 filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;
284 }
285 filp->f_version = inode->i_version;
284 need_revalidate = 0; 286 need_revalidate = 0;
285 } 287 }
286 de = (ext2_dirent *)(kaddr+offset); 288 de = (ext2_dirent *)(kaddr+offset);
@@ -289,9 +291,8 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
289 if (de->rec_len == 0) { 291 if (de->rec_len == 0) {
290 ext2_error(sb, __FUNCTION__, 292 ext2_error(sb, __FUNCTION__,
291 "zero-length directory entry"); 293 "zero-length directory entry");
292 ret = -EIO;
293 ext2_put_page(page); 294 ext2_put_page(page);
294 goto done; 295 return -EIO;
295 } 296 }
296 if (de->inode) { 297 if (de->inode) {
297 int over; 298 int over;
@@ -306,19 +307,14 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
306 le32_to_cpu(de->inode), d_type); 307 le32_to_cpu(de->inode), d_type);
307 if (over) { 308 if (over) {
308 ext2_put_page(page); 309 ext2_put_page(page);
309 goto success; 310 return 0;
310 } 311 }
311 } 312 }
312 filp->f_pos += le16_to_cpu(de->rec_len); 313 filp->f_pos += le16_to_cpu(de->rec_len);
313 } 314 }
314 ext2_put_page(page); 315 ext2_put_page(page);
315 } 316 }
316 317 return 0;
317success:
318 ret = 0;
319done:
320 filp->f_version = inode->i_version;
321 return ret;
322} 318}
323 319
324/* 320/*