lkml.org 
[lkml]   [2007]   [Oct]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 2/2] ext4: Avoid rec_len overflow with 64KB block size
    From
    Date
    ext4: Avoid rec_len overflow with 64KB block size

    From: Jan Kara <jack@suse.cz>

    With 64KB blocksize, a directory entry can have size 64KB which does not fit
    into 16 bits we have for entry lenght. So we store 0xffff instead and convert
    value when read from / written to disk. The patch also converts some places
    to use ext4_next_entry() when we are changing them anyway.

    Signed-off-by: Jan Kara <jack@suse.cz>
    Signed-off-by: Mingming Cao <cmm@us.ibm.com>
    ---

    fs/ext4/dir.c | 12 ++++---
    fs/ext4/namei.c | 76 ++++++++++++++++++++++-------------------------
    include/linux/ext4_fs.h | 20 ++++++++++++
    3 files changed, 62 insertions(+), 46 deletions(-)


    diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
    index 3ab01c0..20b1e28 100644
    --- a/fs/ext4/dir.c
    +++ b/fs/ext4/dir.c
    @@ -69,7 +69,7 @@ int ext4_check_dir_entry (const char * function, struct inode * dir,
    unsigned long offset)
    {
    const char * error_msg = NULL;
    - const int rlen = le16_to_cpu(de->rec_len);
    + const int rlen = ext4_rec_len_from_disk(de->rec_len);

    if (rlen < EXT4_DIR_REC_LEN(1))
    error_msg = "rec_len is smaller than minimal";
    @@ -176,10 +176,10 @@ revalidate:
    * least that it is non-zero. A
    * failure will be detected in the
    * dirent test below. */
    - if (le16_to_cpu(de->rec_len) <
    - EXT4_DIR_REC_LEN(1))
    + if (ext4_rec_len_from_disk(de->rec_len)
    + < EXT4_DIR_REC_LEN(1))
    break;
    - i += le16_to_cpu(de->rec_len);
    + i += ext4_rec_len_from_disk(de->rec_len);
    }
    offset = i;
    filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
    @@ -201,7 +201,7 @@ revalidate:
    ret = stored;
    goto out;
    }
    - offset += le16_to_cpu(de->rec_len);
    + offset += ext4_rec_len_from_disk(de->rec_len);
    if (le32_to_cpu(de->inode)) {
    /* We might block in the next section
    * if the data destination is
    @@ -223,7 +223,7 @@ revalidate:
    goto revalidate;
    stored ++;
    }
    - filp->f_pos += le16_to_cpu(de->rec_len);
    + filp->f_pos += ext4_rec_len_from_disk(de->rec_len);
    }
    offset = 0;
    brelse (bh);
    diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
    index 5fdb862..96e8a85 100644
    --- a/fs/ext4/namei.c
    +++ b/fs/ext4/namei.c
    @@ -281,7 +281,7 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_ent
    space += EXT4_DIR_REC_LEN(de->name_len);
    names++;
    }
    - de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
    + de = ext4_next_entry(de);
    }
    printk("(%i)\n", names);
    return (struct stats) { names, space, 1 };
    @@ -552,7 +552,8 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
    */
    static inline struct ext4_dir_entry_2 *ext4_next_entry(struct ext4_dir_entry_2 *p)
    {
    - return (struct ext4_dir_entry_2 *)((char*)p + le16_to_cpu(p->rec_len));
    + return (struct ext4_dir_entry_2 *)((char*)p +
    + ext4_rec_len_from_disk(p->rec_len));
    }

    /*
    @@ -721,7 +722,7 @@ static int dx_make_map (struct ext4_dir_entry_2 *de, int size,
    cond_resched();
    }
    /* XXX: do we need to check rec_len == 0 case? -Chris */
    - de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
    + de = ext4_next_entry(de);
    }
    return count;
    }
    @@ -823,7 +824,7 @@ static inline int search_dirblock(struct buffer_head * bh,
    return 1;
    }
    /* prevent looping on a bad block */
    - de_len = le16_to_cpu(de->rec_len);
    + de_len = ext4_rec_len_from_disk(de->rec_len);
    if (de_len <= 0)
    return -1;
    offset += de_len;
    @@ -1136,7 +1137,7 @@ dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count)
    rec_len = EXT4_DIR_REC_LEN(de->name_len);
    memcpy (to, de, rec_len);
    ((struct ext4_dir_entry_2 *) to)->rec_len =
    - cpu_to_le16(rec_len);
    + ext4_rec_len_to_disk(rec_len);
    de->inode = 0;
    map++;
    to += rec_len;
    @@ -1155,13 +1156,12 @@ static struct ext4_dir_entry_2* dx_pack_dirents(char *base, int size)

    prev = to = de;
    while ((char*)de < base + size) {
    - next = (struct ext4_dir_entry_2 *) ((char *) de +
    - le16_to_cpu(de->rec_len));
    + next = ext4_next_entry(de);
    if (de->inode && de->name_len) {
    rec_len = EXT4_DIR_REC_LEN(de->name_len);
    if (de > to)
    memmove(to, de, rec_len);
    - to->rec_len = cpu_to_le16(rec_len);
    + to->rec_len = ext4_rec_len_to_disk(rec_len);
    prev = to;
    to = (struct ext4_dir_entry_2 *) (((char *) to) + rec_len);
    }
    @@ -1235,8 +1235,8 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
    /* Fancy dance to stay within two buffers */
    de2 = dx_move_dirents(data1, data2, map + split, count - split);
    de = dx_pack_dirents(data1,blocksize);
    - de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
    - de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2);
    + de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de);
    + de2->rec_len = ext4_rec_len_to_disk(data2 + blocksize - (char *) de2);
    dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1));
    dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1));

    @@ -1307,7 +1307,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
    return -EEXIST;
    }
    nlen = EXT4_DIR_REC_LEN(de->name_len);
    - rlen = le16_to_cpu(de->rec_len);
    + rlen = ext4_rec_len_from_disk(de->rec_len);
    if ((de->inode? rlen - nlen: rlen) >= reclen)
    break;
    de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
    @@ -1326,11 +1326,11 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,

    /* By now the buffer is marked for journaling */
    nlen = EXT4_DIR_REC_LEN(de->name_len);
    - rlen = le16_to_cpu(de->rec_len);
    + rlen = ext4_rec_len_from_disk(de->rec_len);
    if (de->inode) {
    struct ext4_dir_entry_2 *de1 = (struct ext4_dir_entry_2 *)((char *)de + nlen);
    - de1->rec_len = cpu_to_le16(rlen - nlen);
    - de->rec_len = cpu_to_le16(nlen);
    + de1->rec_len = ext4_rec_len_to_disk(rlen - nlen);
    + de->rec_len = ext4_rec_len_to_disk(nlen);
    de = de1;
    }
    de->file_type = EXT4_FT_UNKNOWN;
    @@ -1408,17 +1408,18 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,

    /* The 0th block becomes the root, move the dirents out */
    fde = &root->dotdot;
    - de = (struct ext4_dir_entry_2 *)((char *)fde + le16_to_cpu(fde->rec_len));
    + de = (struct ext4_dir_entry_2 *)((char *)fde +
    + ext4_rec_len_from_disk(fde->rec_len));
    len = ((char *) root) + blocksize - (char *) de;
    memcpy (data1, de, len);
    de = (struct ext4_dir_entry_2 *) data1;
    top = data1 + len;
    - while ((char *)(de2=(void*)de+le16_to_cpu(de->rec_len)) < top)
    + while ((char *)(de2 = ext4_next_entry(de)) < top)
    de = de2;
    - de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
    + de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de);
    /* Initialize the root; the dot dirents already exist */
    de = (struct ext4_dir_entry_2 *) (&root->dotdot);
    - de->rec_len = cpu_to_le16(blocksize - EXT4_DIR_REC_LEN(2));
    + de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(2));
    memset (&root->info, 0, sizeof(root->info));
    root->info.info_length = sizeof(root->info);
    root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;
    @@ -1505,7 +1506,7 @@ static int ext4_add_entry (handle_t *handle, struct dentry *dentry,
    return retval;
    de = (struct ext4_dir_entry_2 *) bh->b_data;
    de->inode = 0;
    - de->rec_len = cpu_to_le16(blocksize);
    + de->rec_len = ext4_rec_len_to_disk(blocksize);
    return add_dirent_to_buf(handle, dentry, inode, de, bh);
    }

    @@ -1569,7 +1570,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
    goto cleanup;
    node2 = (struct dx_node *)(bh2->b_data);
    entries2 = node2->entries;
    - node2->fake.rec_len = cpu_to_le16(sb->s_blocksize);
    + node2->fake.rec_len = ext4_rec_len_to_disk(sb->s_blocksize);
    node2->fake.inode = 0;
    BUFFER_TRACE(frame->bh, "get_write_access");
    err = ext4_journal_get_write_access(handle, frame->bh);
    @@ -1668,9 +1669,9 @@ static int ext4_delete_entry (handle_t *handle,
    BUFFER_TRACE(bh, "get_write_access");
    ext4_journal_get_write_access(handle, bh);
    if (pde)
    - pde->rec_len =
    - cpu_to_le16(le16_to_cpu(pde->rec_len) +
    - le16_to_cpu(de->rec_len));
    + pde->rec_len = ext4_rec_len_to_disk(
    + ext4_rec_len_from_disk(pde->rec_len) +
    + ext4_rec_len_from_disk(de->rec_len));
    else
    de->inode = 0;
    dir->i_version++;
    @@ -1678,10 +1679,9 @@ static int ext4_delete_entry (handle_t *handle,
    ext4_journal_dirty_metadata(handle, bh);
    return 0;
    }
    - i += le16_to_cpu(de->rec_len);
    + i += ext4_rec_len_from_disk(de->rec_len);
    pde = de;
    - de = (struct ext4_dir_entry_2 *)
    - ((char *) de + le16_to_cpu(de->rec_len));
    + de = ext4_next_entry(de);
    }
    return -ENOENT;
    }
    @@ -1844,13 +1844,12 @@ retry:
    de = (struct ext4_dir_entry_2 *) dir_block->b_data;
    de->inode = cpu_to_le32(inode->i_ino);
    de->name_len = 1;
    - de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de->name_len));
    + de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len));
    strcpy (de->name, ".");
    ext4_set_de_type(dir->i_sb, de, S_IFDIR);
    - de = (struct ext4_dir_entry_2 *)
    - ((char *) de + le16_to_cpu(de->rec_len));
    + de = ext4_next_entry(de);
    de->inode = cpu_to_le32(dir->i_ino);
    - de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT4_DIR_REC_LEN(1));
    + de->rec_len = ext4_rec_len_to_disk(inode->i_sb->s_blocksize-EXT4_DIR_REC_LEN(1));
    de->name_len = 2;
    strcpy (de->name, "..");
    ext4_set_de_type(dir->i_sb, de, S_IFDIR);
    @@ -1902,8 +1901,7 @@ static int empty_dir (struct inode * inode)
    return 1;
    }
    de = (struct ext4_dir_entry_2 *) bh->b_data;
    - de1 = (struct ext4_dir_entry_2 *)
    - ((char *) de + le16_to_cpu(de->rec_len));
    + de1 = ext4_next_entry(de);
    if (le32_to_cpu(de->inode) != inode->i_ino ||
    !le32_to_cpu(de1->inode) ||
    strcmp (".", de->name) ||
    @@ -1914,9 +1912,9 @@ static int empty_dir (struct inode * inode)
    brelse (bh);
    return 1;
    }
    - offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len);
    - de = (struct ext4_dir_entry_2 *)
    - ((char *) de1 + le16_to_cpu(de1->rec_len));
    + offset = ext4_rec_len_from_disk(de->rec_len) +
    + ext4_rec_len_from_disk(de1->rec_len);
    + de = ext4_next_entry(de1);
    while (offset < inode->i_size ) {
    if (!bh ||
    (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
    @@ -1945,9 +1943,8 @@ static int empty_dir (struct inode * inode)
    brelse (bh);
    return 0;
    }
    - offset += le16_to_cpu(de->rec_len);
    - de = (struct ext4_dir_entry_2 *)
    - ((char *) de + le16_to_cpu(de->rec_len));
    + offset += ext4_rec_len_from_disk(de->rec_len);
    + de = ext4_next_entry(de);
    }
    brelse (bh);
    return 1;
    @@ -2302,8 +2299,7 @@ retry:
    }

    #define PARENT_INO(buffer) \
    - ((struct ext4_dir_entry_2 *) ((char *) buffer + \
    - le16_to_cpu(((struct ext4_dir_entry_2 *) buffer)->rec_len)))->inode
    + (ext4_next_entry((struct ext4_dir_entry_2 *)(buffer))->inode)

    /*
    * Anybody can rename anything with this: the permission checks are left to the
    diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
    index d15a15e..e1caf0a 100644
    --- a/include/linux/ext4_fs.h
    +++ b/include/linux/ext4_fs.h
    @@ -771,6 +771,26 @@ struct ext4_dir_entry_2 {
    #define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)
    #define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \
    ~EXT4_DIR_ROUND)
    +#define EXT4_MAX_REC_LEN ((1<<16)-1)
    +
    +static inline unsigned ext4_rec_len_from_disk(__le16 dlen)
    +{
    + unsigned len = le16_to_cpu(dlen);
    +
    + if (len == EXT4_MAX_REC_LEN)
    + return 1 << 16;
    + return len;
    +}
    +
    +static inline __le16 ext4_rec_len_to_disk(unsigned len)
    +{
    + if (len == (1 << 16))
    + return cpu_to_le16(EXT4_MAX_REC_LEN);
    + else if (len > (1 << 16))
    + BUG();
    + return cpu_to_le16(len);
    +}
    +
    /*
    * Hash Tree Directory indexing
    * (c) Daniel Phillips, 2001

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-10-02 02:45    [W:4.272 / U:0.080 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site