]>
Commit | Line | Data |
---|---|---|
ac6dc5f6 CW |
1 | From nobody Mon Sep 17 00:00:00 2001 |
2 | From: Al Viro <viro@ftp.linux.org.uk> | |
3 | Date: Wed Mar 15 21:41:59 2006 +0000 | |
4 | Subject: [PATCH] Fix ext2 readdir f_pos re-validation logic | |
5 | ||
6 | This fixes not one, but _two_, silly (but admittedly hard to hit) bugs | |
7 | in the ext2 filesystem "readdir()" function. It also cleans up the code | |
8 | to avoid the unnecessary goto mess. | |
9 | ||
10 | The bugs were related to re-valiating the f_pos value after somebody had | |
11 | either done an "lseek()" on the directory to an invalid offset, or when | |
12 | the offset had become invalid due to a file being unlinked in the | |
13 | directory. The code would not only set the f_version too eagerly, it | |
14 | would also not update f_pos appropriately for when the offset fixup took | |
15 | place. | |
16 | ||
17 | When that happened, we'd occasionally subsequently fail the readdir() | |
18 | even when we shouldn't (no real harm done, but an ugly printk, and | |
19 | obviously you would end up not necessarily seeing all entries). | |
20 | ||
21 | Thanks to Masoud Sharbiani <masouds@google.com> who noticed the problem | |
22 | and had a test-case for it, and also fixed up a thinko in the first | |
23 | version of this patch. | |
24 | ||
25 | Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> | |
26 | Acked-by: Masoud Sharbiani <masouds@google.com> | |
27 | Signed-off-by: Linus Torvalds <torvalds@osdl.org> | |
28 | Signed-off-by: Chris Wright <chrisw@sous-sol.org> | |
5390119a | 29 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
ac6dc5f6 CW |
30 | --- |
31 | ||
32 | fs/ext2/dir.c | 28 ++++++++++++---------------- | |
5390119a | 33 | 1 file changed, 12 insertions(+), 16 deletions(-) |
ac6dc5f6 CW |
34 | |
35 | 2d7f2ea9c989853310c7f6e8be52cc090cc8e66b | |
ac6dc5f6 CW |
36 | --- linux-2.6.15.6.orig/fs/ext2/dir.c |
37 | +++ linux-2.6.15.6/fs/ext2/dir.c | |
38 | @@ -256,11 +256,10 @@ ext2_readdir (struct file * filp, void * | |
39 | unsigned long npages = dir_pages(inode); | |
40 | unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); | |
41 | unsigned char *types = NULL; | |
42 | - int need_revalidate = (filp->f_version != inode->i_version); | |
43 | - int ret; | |
44 | + int need_revalidate = filp->f_version != inode->i_version; | |
45 | ||
46 | if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) | |
47 | - goto success; | |
48 | + return 0; | |
49 | ||
50 | if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) | |
51 | types = ext2_filetype_table; | |
52 | @@ -275,12 +274,15 @@ ext2_readdir (struct file * filp, void * | |
53 | "bad page in #%lu", | |
54 | inode->i_ino); | |
55 | filp->f_pos += PAGE_CACHE_SIZE - offset; | |
56 | - ret = -EIO; | |
57 | - goto done; | |
58 | + return -EIO; | |
59 | } | |
60 | kaddr = page_address(page); | |
61 | - if (need_revalidate) { | |
62 | - offset = ext2_validate_entry(kaddr, offset, chunk_mask); | |
63 | + if (unlikely(need_revalidate)) { | |
64 | + if (offset) { | |
65 | + offset = ext2_validate_entry(kaddr, offset, chunk_mask); | |
66 | + filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset; | |
67 | + } | |
68 | + filp->f_version = inode->i_version; | |
69 | need_revalidate = 0; | |
70 | } | |
71 | de = (ext2_dirent *)(kaddr+offset); | |
72 | @@ -289,9 +291,8 @@ ext2_readdir (struct file * filp, void * | |
73 | if (de->rec_len == 0) { | |
74 | ext2_error(sb, __FUNCTION__, | |
75 | "zero-length directory entry"); | |
76 | - ret = -EIO; | |
77 | ext2_put_page(page); | |
78 | - goto done; | |
79 | + return -EIO; | |
80 | } | |
81 | if (de->inode) { | |
82 | int over; | |
83 | @@ -306,19 +307,14 @@ ext2_readdir (struct file * filp, void * | |
84 | le32_to_cpu(de->inode), d_type); | |
85 | if (over) { | |
86 | ext2_put_page(page); | |
87 | - goto success; | |
88 | + return 0; | |
89 | } | |
90 | } | |
91 | filp->f_pos += le16_to_cpu(de->rec_len); | |
92 | } | |
93 | ext2_put_page(page); | |
94 | } | |
95 | - | |
96 | -success: | |
97 | - ret = 0; | |
98 | -done: | |
99 | - filp->f_version = inode->i_version; | |
100 | - return ret; | |
101 | + return 0; | |
102 | } | |
103 | ||
104 | /* |