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 ece76fb6a40..bd5d74d0639 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 17baecbf8cd..49f1ceaac57 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 203086498ca..e275464f775 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; |
