aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/dir.c
diff options
context:
space:
mode:
authorTao Ma <boyu.mt@taobao.com>2013-04-19 17:53:09 -0400
committerTheodore Ts'o <tytso@mit.edu>2013-04-19 17:53:09 -0400
commit8af0f08227977079f8f227e74d27c59db2ab84f6 (patch)
tree8db354c62e7d529bf4b5cdb0f2800a065cc1cd20 /fs/ext4/dir.c
parent28daf4fae8693d4a285123494899fe01950cba50 (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.c20
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