--- /dev/null
+From nobody Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@ftp.linux.org.uk>
+Date: Wed Mar 15 21:41:59 2006 +0000
+Subject: [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>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ fs/ext2/dir.c | 28 ++++++++++++----------------
+ 1 files changed, 12 insertions(+), 16 deletions(-)
+
+2d7f2ea9c989853310c7f6e8be52cc090cc8e66b
+diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
+index 7442bdd..b3dbd71 100644
+--- linux-2.6.15.6.orig/fs/ext2/dir.c
++++ linux-2.6.15.6/fs/ext2/dir.c
+@@ -256,11 +256,10 @@ ext2_readdir (struct file * filp, void *
+ unsigned long npages = dir_pages(inode);
+ unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
+ unsigned char *types = NULL;
+- int need_revalidate = (filp->f_version != inode->i_version);
+- int ret;
++ int need_revalidate = filp->f_version != inode->i_version;
+
+ if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
+- goto success;
++ return 0;
+
+ if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
+ types = ext2_filetype_table;
+@@ -275,12 +274,15 @@ ext2_readdir (struct file * filp, void *
+ "bad page in #%lu",
+ inode->i_ino);
+ filp->f_pos += PAGE_CACHE_SIZE - offset;
+- ret = -EIO;
+- goto done;
++ return -EIO;
+ }
+ kaddr = page_address(page);
+- if (need_revalidate) {
+- offset = ext2_validate_entry(kaddr, offset, chunk_mask);
++ if (unlikely(need_revalidate)) {
++ if (offset) {
++ offset = ext2_validate_entry(kaddr, offset, chunk_mask);
++ filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;
++ }
++ filp->f_version = inode->i_version;
+ need_revalidate = 0;
+ }
+ de = (ext2_dirent *)(kaddr+offset);
+@@ -289,9 +291,8 @@ ext2_readdir (struct file * filp, void *
+ if (de->rec_len == 0) {
+ ext2_error(sb, __FUNCTION__,
+ "zero-length directory entry");
+- ret = -EIO;
+ ext2_put_page(page);
+- goto done;
++ return -EIO;
+ }
+ if (de->inode) {
+ int over;
+@@ -306,19 +307,14 @@ ext2_readdir (struct file * filp, void *
+ le32_to_cpu(de->inode), d_type);
+ if (over) {
+ ext2_put_page(page);
+- goto success;
++ return 0;
+ }
+ }
+ filp->f_pos += le16_to_cpu(de->rec_len);
+ }
+ ext2_put_page(page);
+ }
+-
+-success:
+- ret = 0;
+-done:
+- filp->f_version = inode->i_version;
+- return ret;
++ return 0;
+ }
+
+ /*