diff options
author | Tao Ma <boyu.mt@taobao.com> | 2013-04-19 17:53:09 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2013-04-19 17:53:09 -0400 |
commit | 8af0f08227977079f8f227e74d27c59db2ab84f6 (patch) | |
tree | 8db354c62e7d529bf4b5cdb0f2800a065cc1cd20 /fs/ext4/dir.c | |
parent | 28daf4fae8693d4a285123494899fe01950cba50 (diff) |
ext4: fix readdir error in the case of inline_data+dir_index
Zach reported a problem that if inline data is enabled, we don't
tell the difference between the offset of '.' and '..'. And a
getdents will fail if the user only want to get '.' and what's worse,
if there is a conversion happens when the user calls getdents
many times, he/she may get the same entry twice.
In theory, a dir block would also fail if it is converted to a
hashed-index based dir since f_pos will become a hash value, not the
real one, but it doesn't happen. And a deep investigation shows that
we uses a hash based solution even for a normal dir if the dir_index
feature is enabled.
So this patch just adds a new htree_inlinedir_to_tree for inline dir,
and if we find that the hash index is supported, we will do like what
we do for a dir block.
Reported-by: Zach Brown <zab@redhat.com>
Signed-off-by: Tao Ma <boyu.mt@taobao.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/dir.c')
-rw-r--r-- | fs/ext4/dir.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index d8cd1f0f4661..f8d56e4254e0 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -46,7 +46,8 @@ static int is_dx_dir(struct inode *inode) | |||
46 | if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb, | 46 | if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb, |
47 | EXT4_FEATURE_COMPAT_DIR_INDEX) && | 47 | EXT4_FEATURE_COMPAT_DIR_INDEX) && |
48 | ((ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) || | 48 | ((ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) || |
49 | ((inode->i_size >> sb->s_blocksize_bits) == 1))) | 49 | ((inode->i_size >> sb->s_blocksize_bits) == 1) || |
50 | ext4_has_inline_data(inode))) | ||
50 | return 1; | 51 | return 1; |
51 | 52 | ||
52 | return 0; | 53 | return 0; |
@@ -115,14 +116,6 @@ static int ext4_readdir(struct file *filp, | |||
115 | int ret = 0; | 116 | int ret = 0; |
116 | int dir_has_error = 0; | 117 | int dir_has_error = 0; |
117 | 118 | ||
118 | if (ext4_has_inline_data(inode)) { | ||
119 | int has_inline_data = 1; | ||
120 | ret = ext4_read_inline_dir(filp, dirent, filldir, | ||
121 | &has_inline_data); | ||
122 | if (has_inline_data) | ||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | if (is_dx_dir(inode)) { | 119 | if (is_dx_dir(inode)) { |
127 | err = ext4_dx_readdir(filp, dirent, filldir); | 120 | err = ext4_dx_readdir(filp, dirent, filldir); |
128 | if (err != ERR_BAD_DX_DIR) { | 121 | if (err != ERR_BAD_DX_DIR) { |
@@ -136,6 +129,15 @@ static int ext4_readdir(struct file *filp, | |||
136 | ext4_clear_inode_flag(file_inode(filp), | 129 | ext4_clear_inode_flag(file_inode(filp), |
137 | EXT4_INODE_INDEX); | 130 | EXT4_INODE_INDEX); |
138 | } | 131 | } |
132 | |||
133 | if (ext4_has_inline_data(inode)) { | ||
134 | int has_inline_data = 1; | ||
135 | ret = ext4_read_inline_dir(filp, dirent, filldir, | ||
136 | &has_inline_data); | ||
137 | if (has_inline_data) | ||
138 | return ret; | ||
139 | } | ||
140 | |||
139 | stored = 0; | 141 | stored = 0; |
140 | offset = filp->f_pos & (sb->s_blocksize - 1); | 142 | offset = filp->f_pos & (sb->s_blocksize - 1); |
141 | 143 | ||