]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
ext2_readdir fix
authorChris Wright <chrisw@sous-sol.org>
Fri, 24 Mar 2006 18:05:58 +0000 (10:05 -0800)
committerChris Wright <chrisw@sous-sol.org>
Fri, 24 Mar 2006 18:05:58 +0000 (10:05 -0800)
queue/fix-ext2-readdir-f_pos-re-validation-logic.patch [new file with mode: 0644]
queue/series

diff --git a/queue/fix-ext2-readdir-f_pos-re-validation-logic.patch b/queue/fix-ext2-readdir-f_pos-re-validation-logic.patch
new file mode 100644 (file)
index 0000000..00856c7
--- /dev/null
@@ -0,0 +1,105 @@
+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;
+ }
+ /*
index ff952e30f6cd1314f447d66832cdc0311fc7e301..b3c1dd868c626433420fcf71dd1623adbf2b9661 100644 (file)
@@ -5,3 +5,4 @@ cramfs-mounts-provide-corrupted-content-since-2.6.15.patch
 kconfig-video_decoder-must-select-fw_loader.patch
 tcp-do-not-use-inet-id-of-global-tcp_socket-when-sending-rst.patch
 net-ensure-device-name-passed-to-so_bindtodevice-is-null-terminated.patch
+fix-ext2-readdir-f_pos-re-validation-logic.patch