ext4: refactor __ext4_check_dir_entry() to accept start and size
authorTao Ma <boyu.mt@taobao.com>
Mon, 10 Dec 2012 19:05:58 +0000 (14:05 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 10 Dec 2012 19:05:58 +0000 (14:05 -0500)
The __ext4_check_dir_entry() function() is used to check whether the
de is over the block boundary.  Now with inline data, it could be
within the block boundary while exceeds the inode size.  So check this
function to check the overflow more precisely.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/dir.c
fs/ext4/ext4.h
fs/ext4/namei.c

index 8e07d2a5a13952836c820c32419f340b148a8ce2..7c9d08b0f2feff052dd8e2abcb7941f7ffb015b0 100644 (file)
@@ -72,7 +72,7 @@ static int is_dx_dir(struct inode *inode)
 int __ext4_check_dir_entry(const char *function, unsigned int line,
                           struct inode *dir, struct file *filp,
                           struct ext4_dir_entry_2 *de,
-                          struct buffer_head *bh,
+                          struct buffer_head *bh, char *buf, int size,
                           unsigned int offset)
 {
        const char *error_msg = NULL;
@@ -85,9 +85,8 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
                error_msg = "rec_len % 4 != 0";
        else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
                error_msg = "rec_len is too small for name_len";
-       else if (unlikely(((char *) de - bh->b_data) + rlen >
-                         dir->i_sb->s_blocksize))
-               error_msg = "directory entry across blocks";
+       else if (unlikely(((char *) de - buf) + rlen > size))
+               error_msg = "directory entry across range";
        else if (unlikely(le32_to_cpu(de->inode) >
                        le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
                error_msg = "inode out of bounds";
@@ -98,14 +97,14 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
                ext4_error_file(filp, function, line, bh->b_blocknr,
                                "bad entry in directory: %s - offset=%u(%u), "
                                "inode=%u, rec_len=%d, name_len=%d",
-                               error_msg, (unsigned) (offset % bh->b_size),
+                               error_msg, (unsigned) (offset % size),
                                offset, le32_to_cpu(de->inode),
                                rlen, de->name_len);
        else
                ext4_error_inode(dir, function, line, bh->b_blocknr,
                                "bad entry in directory: %s - offset=%u(%u), "
                                "inode=%u, rec_len=%d, name_len=%d",
-                               error_msg, (unsigned) (offset % bh->b_size),
+                               error_msg, (unsigned) (offset % size),
                                offset, le32_to_cpu(de->inode),
                                rlen, de->name_len);
 
@@ -221,8 +220,9 @@ revalidate:
                while (!error && filp->f_pos < inode->i_size
                       && offset < sb->s_blocksize) {
                        de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
-                       if (ext4_check_dir_entry(inode, filp, de,
-                                                bh, offset)) {
+                       if (ext4_check_dir_entry(inode, filp, de, bh,
+                                                bh->b_data, bh->b_size,
+                                                offset)) {
                                /*
                                 * On error, skip the f_pos to the next block
                                 */
index cf840146ce814749537d611fc9c272b83667053f..59cbf498fd5fec650ee02891e4bb78a950373652 100644 (file)
@@ -1960,10 +1960,11 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
 extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
                                  struct file *,
                                  struct ext4_dir_entry_2 *,
-                                 struct buffer_head *, unsigned int);
-#define ext4_check_dir_entry(dir, filp, de, bh, offset)                        \
+                                 struct buffer_head *, char *, int,
+                                 unsigned int);
+#define ext4_check_dir_entry(dir, filp, de, bh, buf, size, offset)     \
        unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \
-                                       (de), (bh), (offset)))
+                                       (de), (bh), (buf), (size), (offset)))
 extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
                                    __u32 minor_hash,
                                    struct ext4_dir_entry_2 *dirent);
index edb9f10c14559cbc667c42ef3b1460d5991a3888..10da2d50a5d8be30f7a4d94702974df4a26ed9b3 100644 (file)
@@ -892,6 +892,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
                                           EXT4_DIR_REC_LEN(0));
        for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
                if (ext4_check_dir_entry(dir, NULL, de, bh,
+                               bh->b_data, bh->b_size,
                                (block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
                                         + ((char *)de - bh->b_data))) {
                        /* On error, skip the f_pos to the next block. */
@@ -1130,7 +1131,8 @@ static inline int search_dirblock(struct buffer_head *bh,
                if ((char *) de + namelen <= dlimit &&
                    ext4_match (namelen, name, de)) {
                        /* found a match - just to be sure, do a full check */
-                       if (ext4_check_dir_entry(dir, NULL, de, bh, offset))
+                       if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+                                                bh->b_size, offset))
                                return -1;
                        *res_dir = de;
                        return 1;
@@ -1643,7 +1645,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
                de = (struct ext4_dir_entry_2 *)bh->b_data;
                top = bh->b_data + (blocksize - csum_size) - reclen;
                while ((char *) de <= top) {
-                       if (ext4_check_dir_entry(dir, NULL, de, bh, offset))
+                       if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+                                                bh->b_size, offset))
                                return -EIO;
                        if (ext4_match(namelen, name, de))
                                return -EEXIST;
@@ -2076,7 +2079,8 @@ static int ext4_delete_entry(handle_t *handle,
        pde = NULL;
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        while (i < bh->b_size - csum_size) {
-               if (ext4_check_dir_entry(dir, NULL, de, bh, i))
+               if (ext4_check_dir_entry(dir, NULL, de, bh,
+                                        bh->b_data, bh->b_size, i))
                        return -EIO;
                if (de == de_del)  {
                        BUFFER_TRACE(bh, "get_write_access");
@@ -2439,7 +2443,8 @@ static int empty_dir(struct inode *inode)
                        set_buffer_verified(bh);
                        de = (struct ext4_dir_entry_2 *) bh->b_data;
                }
-               if (ext4_check_dir_entry(inode, NULL, de, bh, offset)) {
+               if (ext4_check_dir_entry(inode, NULL, de, bh,
+                                        bh->b_data, bh->b_size, offset)) {
                        de = (struct ext4_dir_entry_2 *)(bh->b_data +
                                                         sb->s_blocksize);
                        offset = (offset | (sb->s_blocksize - 1)) + 1;