diff options
-rw-r--r-- | fs/ext4/dir.c | 40 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 3 | ||||
-rw-r--r-- | fs/ext4/namei.c | 14 |
3 files changed, 32 insertions, 25 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index ece76fb6a40c..bd5d74d06399 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -60,7 +60,11 @@ static unsigned char get_dtype(struct super_block *sb, int filetype) | |||
60 | return (ext4_filetype_table[filetype]); | 60 | return (ext4_filetype_table[filetype]); |
61 | } | 61 | } |
62 | 62 | ||
63 | 63 | /* | |
64 | * Return 0 if the directory entry is OK, and 1 if there is a problem | ||
65 | * | ||
66 | * Note: this is the opposite of what ext2 and ext3 historically returned... | ||
67 | */ | ||
64 | int __ext4_check_dir_entry(const char *function, unsigned int line, | 68 | int __ext4_check_dir_entry(const char *function, unsigned int line, |
65 | struct inode *dir, | 69 | struct inode *dir, |
66 | struct ext4_dir_entry_2 *de, | 70 | struct ext4_dir_entry_2 *de, |
@@ -71,26 +75,28 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, | |||
71 | const int rlen = ext4_rec_len_from_disk(de->rec_len, | 75 | const int rlen = ext4_rec_len_from_disk(de->rec_len, |
72 | dir->i_sb->s_blocksize); | 76 | dir->i_sb->s_blocksize); |
73 | 77 | ||
74 | if (rlen < EXT4_DIR_REC_LEN(1)) | 78 | if (unlikely(rlen < EXT4_DIR_REC_LEN(1))) |
75 | error_msg = "rec_len is smaller than minimal"; | 79 | error_msg = "rec_len is smaller than minimal"; |
76 | else if (rlen % 4 != 0) | 80 | else if (unlikely(rlen % 4 != 0)) |
77 | error_msg = "rec_len % 4 != 0"; | 81 | error_msg = "rec_len % 4 != 0"; |
78 | else if (rlen < EXT4_DIR_REC_LEN(de->name_len)) | 82 | else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len))) |
79 | error_msg = "rec_len is too small for name_len"; | 83 | error_msg = "rec_len is too small for name_len"; |
80 | else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) | 84 | else if (unlikely(((char *) de - bh->b_data) + rlen > |
85 | dir->i_sb->s_blocksize)) | ||
81 | error_msg = "directory entry across blocks"; | 86 | error_msg = "directory entry across blocks"; |
82 | else if (le32_to_cpu(de->inode) > | 87 | else if (unlikely(le32_to_cpu(de->inode) > |
83 | le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)) | 88 | le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count))) |
84 | error_msg = "inode out of bounds"; | 89 | error_msg = "inode out of bounds"; |
90 | else | ||
91 | return 0; | ||
85 | 92 | ||
86 | if (error_msg != NULL) | 93 | ext4_error_inode(dir, function, line, bh->b_blocknr, |
87 | ext4_error_inode(dir, function, line, bh->b_blocknr, | 94 | "bad entry in directory: %s - " |
88 | "bad entry in directory: %s - " | 95 | "offset=%u(%u), inode=%u, rec_len=%d, name_len=%d", |
89 | "offset=%u(%u), inode=%u, rec_len=%d, name_len=%d", | 96 | error_msg, (unsigned) (offset%bh->b_size), offset, |
90 | error_msg, (unsigned) (offset%bh->b_size), offset, | 97 | le32_to_cpu(de->inode), |
91 | le32_to_cpu(de->inode), | 98 | rlen, de->name_len); |
92 | rlen, de->name_len); | 99 | return 1; |
93 | return error_msg == NULL ? 1 : 0; | ||
94 | } | 100 | } |
95 | 101 | ||
96 | static int ext4_readdir(struct file *filp, | 102 | static int ext4_readdir(struct file *filp, |
@@ -194,8 +200,8 @@ revalidate: | |||
194 | while (!error && filp->f_pos < inode->i_size | 200 | while (!error && filp->f_pos < inode->i_size |
195 | && offset < sb->s_blocksize) { | 201 | && offset < sb->s_blocksize) { |
196 | de = (struct ext4_dir_entry_2 *) (bh->b_data + offset); | 202 | de = (struct ext4_dir_entry_2 *) (bh->b_data + offset); |
197 | if (!ext4_check_dir_entry(inode, de, | 203 | if (ext4_check_dir_entry(inode, de, |
198 | bh, offset)) { | 204 | bh, offset)) { |
199 | /* | 205 | /* |
200 | * On error, skip the f_pos to the next block | 206 | * On error, skip the f_pos to the next block |
201 | */ | 207 | */ |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 17baecbf8cda..49f1ceaac57d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1639,7 +1639,8 @@ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, | |||
1639 | struct ext4_dir_entry_2 *, | 1639 | struct ext4_dir_entry_2 *, |
1640 | struct buffer_head *, unsigned int); | 1640 | struct buffer_head *, unsigned int); |
1641 | #define ext4_check_dir_entry(dir, de, bh, offset) \ | 1641 | #define ext4_check_dir_entry(dir, de, bh, offset) \ |
1642 | __ext4_check_dir_entry(__func__, __LINE__, (dir), (de), (bh), (offset)) | 1642 | unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (de), \ |
1643 | (bh), (offset))) | ||
1643 | extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, | 1644 | extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, |
1644 | __u32 minor_hash, | 1645 | __u32 minor_hash, |
1645 | struct ext4_dir_entry_2 *dirent); | 1646 | struct ext4_dir_entry_2 *dirent); |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 203086498caa..e275464f7754 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -581,9 +581,9 @@ static int htree_dirblock_to_tree(struct file *dir_file, | |||
581 | dir->i_sb->s_blocksize - | 581 | dir->i_sb->s_blocksize - |
582 | EXT4_DIR_REC_LEN(0)); | 582 | EXT4_DIR_REC_LEN(0)); |
583 | for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) { | 583 | for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) { |
584 | if (!ext4_check_dir_entry(dir, de, bh, | 584 | if (ext4_check_dir_entry(dir, de, bh, |
585 | (block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb)) | 585 | (block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb)) |
586 | +((char *)de - bh->b_data))) { | 586 | + ((char *)de - bh->b_data))) { |
587 | /* On error, skip the f_pos to the next block. */ | 587 | /* On error, skip the f_pos to the next block. */ |
588 | dir_file->f_pos = (dir_file->f_pos | | 588 | dir_file->f_pos = (dir_file->f_pos | |
589 | (dir->i_sb->s_blocksize - 1)) + 1; | 589 | (dir->i_sb->s_blocksize - 1)) + 1; |
@@ -820,7 +820,7 @@ static inline int search_dirblock(struct buffer_head *bh, | |||
820 | if ((char *) de + namelen <= dlimit && | 820 | if ((char *) de + namelen <= dlimit && |
821 | ext4_match (namelen, name, de)) { | 821 | ext4_match (namelen, name, de)) { |
822 | /* found a match - just to be sure, do a full check */ | 822 | /* found a match - just to be sure, do a full check */ |
823 | if (!ext4_check_dir_entry(dir, de, bh, offset)) | 823 | if (ext4_check_dir_entry(dir, de, bh, offset)) |
824 | return -1; | 824 | return -1; |
825 | *res_dir = de; | 825 | *res_dir = de; |
826 | return 1; | 826 | return 1; |
@@ -1269,7 +1269,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, | |||
1269 | de = (struct ext4_dir_entry_2 *)bh->b_data; | 1269 | de = (struct ext4_dir_entry_2 *)bh->b_data; |
1270 | top = bh->b_data + blocksize - reclen; | 1270 | top = bh->b_data + blocksize - reclen; |
1271 | while ((char *) de <= top) { | 1271 | while ((char *) de <= top) { |
1272 | if (!ext4_check_dir_entry(dir, de, bh, offset)) | 1272 | if (ext4_check_dir_entry(dir, de, bh, offset)) |
1273 | return -EIO; | 1273 | return -EIO; |
1274 | if (ext4_match(namelen, name, de)) | 1274 | if (ext4_match(namelen, name, de)) |
1275 | return -EEXIST; | 1275 | return -EEXIST; |
@@ -1636,7 +1636,7 @@ static int ext4_delete_entry(handle_t *handle, | |||
1636 | pde = NULL; | 1636 | pde = NULL; |
1637 | de = (struct ext4_dir_entry_2 *) bh->b_data; | 1637 | de = (struct ext4_dir_entry_2 *) bh->b_data; |
1638 | while (i < bh->b_size) { | 1638 | while (i < bh->b_size) { |
1639 | if (!ext4_check_dir_entry(dir, de, bh, i)) | 1639 | if (ext4_check_dir_entry(dir, de, bh, i)) |
1640 | return -EIO; | 1640 | return -EIO; |
1641 | if (de == de_del) { | 1641 | if (de == de_del) { |
1642 | BUFFER_TRACE(bh, "get_write_access"); | 1642 | BUFFER_TRACE(bh, "get_write_access"); |
@@ -1919,7 +1919,7 @@ static int empty_dir(struct inode *inode) | |||
1919 | } | 1919 | } |
1920 | de = (struct ext4_dir_entry_2 *) bh->b_data; | 1920 | de = (struct ext4_dir_entry_2 *) bh->b_data; |
1921 | } | 1921 | } |
1922 | if (!ext4_check_dir_entry(inode, de, bh, offset)) { | 1922 | if (ext4_check_dir_entry(inode, de, bh, offset)) { |
1923 | de = (struct ext4_dir_entry_2 *)(bh->b_data + | 1923 | de = (struct ext4_dir_entry_2 *)(bh->b_data + |
1924 | sb->s_blocksize); | 1924 | sb->s_blocksize); |
1925 | offset = (offset | (sb->s_blocksize - 1)) + 1; | 1925 | offset = (offset | (sb->s_blocksize - 1)) + 1; |