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/namei.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/namei.c')
-rw-r--r-- | fs/ext4/namei.c | 29 |
1 files changed, 11 insertions, 18 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 955c907fc980..6653fc35ecb7 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -972,6 +972,17 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, | |||
972 | hinfo.hash_version += | 972 | hinfo.hash_version += |
973 | EXT4_SB(dir->i_sb)->s_hash_unsigned; | 973 | EXT4_SB(dir->i_sb)->s_hash_unsigned; |
974 | hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; | 974 | hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; |
975 | if (ext4_has_inline_data(dir)) { | ||
976 | int has_inline_data = 1; | ||
977 | count = htree_inlinedir_to_tree(dir_file, dir, 0, | ||
978 | &hinfo, start_hash, | ||
979 | start_minor_hash, | ||
980 | &has_inline_data); | ||
981 | if (has_inline_data) { | ||
982 | *next_hash = ~0; | ||
983 | return count; | ||
984 | } | ||
985 | } | ||
975 | count = htree_dirblock_to_tree(dir_file, dir, 0, &hinfo, | 986 | count = htree_dirblock_to_tree(dir_file, dir, 0, &hinfo, |
976 | start_hash, start_minor_hash); | 987 | start_hash, start_minor_hash); |
977 | *next_hash = ~0; | 988 | *next_hash = ~0; |
@@ -1456,24 +1467,6 @@ struct dentry *ext4_get_parent(struct dentry *child) | |||
1456 | return d_obtain_alias(ext4_iget(child->d_inode->i_sb, ino)); | 1467 | return d_obtain_alias(ext4_iget(child->d_inode->i_sb, ino)); |
1457 | } | 1468 | } |
1458 | 1469 | ||
1459 | #define S_SHIFT 12 | ||
1460 | static unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = { | ||
1461 | [S_IFREG >> S_SHIFT] = EXT4_FT_REG_FILE, | ||
1462 | [S_IFDIR >> S_SHIFT] = EXT4_FT_DIR, | ||
1463 | [S_IFCHR >> S_SHIFT] = EXT4_FT_CHRDEV, | ||
1464 | [S_IFBLK >> S_SHIFT] = EXT4_FT_BLKDEV, | ||
1465 | [S_IFIFO >> S_SHIFT] = EXT4_FT_FIFO, | ||
1466 | [S_IFSOCK >> S_SHIFT] = EXT4_FT_SOCK, | ||
1467 | [S_IFLNK >> S_SHIFT] = EXT4_FT_SYMLINK, | ||
1468 | }; | ||
1469 | |||
1470 | static inline void ext4_set_de_type(struct super_block *sb, | ||
1471 | struct ext4_dir_entry_2 *de, | ||
1472 | umode_t mode) { | ||
1473 | if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE)) | ||
1474 | de->file_type = ext4_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; | ||
1475 | } | ||
1476 | |||
1477 | /* | 1470 | /* |
1478 | * Move count entries from end of map between two memory locations. | 1471 | * Move count entries from end of map between two memory locations. |
1479 | * Returns pointer to last entry moved. | 1472 | * Returns pointer to last entry moved. |