diff options
author | Jan Kara <jack@suse.cz> | 2008-01-28 23:58:27 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2008-01-28 23:58:27 -0500 |
commit | a72d7f834e1afa08421938d7eb06bd8e56b0e58c (patch) | |
tree | 21c6fbbf4187fa8ba56878ea2d5e576e123e1f95 /fs | |
parent | afc7cbca5bfd556c3e12d3acefbee5ab0cbd4670 (diff) |
ext4: Avoid rec_len overflow with 64KB block size
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/dir.c | 12 | ||||
-rw-r--r-- | fs/ext4/namei.c | 77 |
2 files changed, 43 insertions, 46 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index f612bef98315..145a9c0c972d 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -67,7 +67,7 @@ int ext4_check_dir_entry (const char * function, struct inode * dir, | |||
67 | unsigned long offset) | 67 | unsigned long offset) |
68 | { | 68 | { |
69 | const char * error_msg = NULL; | 69 | const char * error_msg = NULL; |
70 | const int rlen = le16_to_cpu(de->rec_len); | 70 | const int rlen = ext4_rec_len_from_disk(de->rec_len); |
71 | 71 | ||
72 | if (rlen < EXT4_DIR_REC_LEN(1)) | 72 | if (rlen < EXT4_DIR_REC_LEN(1)) |
73 | error_msg = "rec_len is smaller than minimal"; | 73 | error_msg = "rec_len is smaller than minimal"; |
@@ -172,10 +172,10 @@ revalidate: | |||
172 | * least that it is non-zero. A | 172 | * least that it is non-zero. A |
173 | * failure will be detected in the | 173 | * failure will be detected in the |
174 | * dirent test below. */ | 174 | * dirent test below. */ |
175 | if (le16_to_cpu(de->rec_len) < | 175 | if (ext4_rec_len_from_disk(de->rec_len) |
176 | EXT4_DIR_REC_LEN(1)) | 176 | < EXT4_DIR_REC_LEN(1)) |
177 | break; | 177 | break; |
178 | i += le16_to_cpu(de->rec_len); | 178 | i += ext4_rec_len_from_disk(de->rec_len); |
179 | } | 179 | } |
180 | offset = i; | 180 | offset = i; |
181 | filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | 181 | filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) |
@@ -197,7 +197,7 @@ revalidate: | |||
197 | ret = stored; | 197 | ret = stored; |
198 | goto out; | 198 | goto out; |
199 | } | 199 | } |
200 | offset += le16_to_cpu(de->rec_len); | 200 | offset += ext4_rec_len_from_disk(de->rec_len); |
201 | if (le32_to_cpu(de->inode)) { | 201 | if (le32_to_cpu(de->inode)) { |
202 | /* We might block in the next section | 202 | /* We might block in the next section |
203 | * if the data destination is | 203 | * if the data destination is |
@@ -219,7 +219,7 @@ revalidate: | |||
219 | goto revalidate; | 219 | goto revalidate; |
220 | stored ++; | 220 | stored ++; |
221 | } | 221 | } |
222 | filp->f_pos += le16_to_cpu(de->rec_len); | 222 | filp->f_pos += ext4_rec_len_from_disk(de->rec_len); |
223 | } | 223 | } |
224 | offset = 0; | 224 | offset = 0; |
225 | brelse (bh); | 225 | brelse (bh); |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 94ee6f315dc1..d9a3a2fc5b0d 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -280,7 +280,7 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_ent | |||
280 | space += EXT4_DIR_REC_LEN(de->name_len); | 280 | space += EXT4_DIR_REC_LEN(de->name_len); |
281 | names++; | 281 | names++; |
282 | } | 282 | } |
283 | de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | 283 | de = ext4_next_entry(de); |
284 | } | 284 | } |
285 | printk("(%i)\n", names); | 285 | printk("(%i)\n", names); |
286 | return (struct stats) { names, space, 1 }; | 286 | return (struct stats) { names, space, 1 }; |
@@ -551,7 +551,8 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash, | |||
551 | */ | 551 | */ |
552 | static inline struct ext4_dir_entry_2 *ext4_next_entry(struct ext4_dir_entry_2 *p) | 552 | static inline struct ext4_dir_entry_2 *ext4_next_entry(struct ext4_dir_entry_2 *p) |
553 | { | 553 | { |
554 | return (struct ext4_dir_entry_2 *)((char*)p + le16_to_cpu(p->rec_len)); | 554 | return (struct ext4_dir_entry_2 *)((char *)p + |
555 | ext4_rec_len_from_disk(p->rec_len)); | ||
555 | } | 556 | } |
556 | 557 | ||
557 | /* | 558 | /* |
@@ -720,7 +721,7 @@ static int dx_make_map (struct ext4_dir_entry_2 *de, int size, | |||
720 | cond_resched(); | 721 | cond_resched(); |
721 | } | 722 | } |
722 | /* XXX: do we need to check rec_len == 0 case? -Chris */ | 723 | /* XXX: do we need to check rec_len == 0 case? -Chris */ |
723 | de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); | 724 | de = ext4_next_entry(de); |
724 | } | 725 | } |
725 | return count; | 726 | return count; |
726 | } | 727 | } |
@@ -820,7 +821,7 @@ static inline int search_dirblock(struct buffer_head * bh, | |||
820 | return 1; | 821 | return 1; |
821 | } | 822 | } |
822 | /* prevent looping on a bad block */ | 823 | /* prevent looping on a bad block */ |
823 | de_len = le16_to_cpu(de->rec_len); | 824 | de_len = ext4_rec_len_from_disk(de->rec_len); |
824 | if (de_len <= 0) | 825 | if (de_len <= 0) |
825 | return -1; | 826 | return -1; |
826 | offset += de_len; | 827 | offset += de_len; |
@@ -1128,7 +1129,7 @@ dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count) | |||
1128 | rec_len = EXT4_DIR_REC_LEN(de->name_len); | 1129 | rec_len = EXT4_DIR_REC_LEN(de->name_len); |
1129 | memcpy (to, de, rec_len); | 1130 | memcpy (to, de, rec_len); |
1130 | ((struct ext4_dir_entry_2 *) to)->rec_len = | 1131 | ((struct ext4_dir_entry_2 *) to)->rec_len = |
1131 | cpu_to_le16(rec_len); | 1132 | ext4_rec_len_to_disk(rec_len); |
1132 | de->inode = 0; | 1133 | de->inode = 0; |
1133 | map++; | 1134 | map++; |
1134 | to += rec_len; | 1135 | to += rec_len; |
@@ -1147,13 +1148,12 @@ static struct ext4_dir_entry_2* dx_pack_dirents(char *base, int size) | |||
1147 | 1148 | ||
1148 | prev = to = de; | 1149 | prev = to = de; |
1149 | while ((char*)de < base + size) { | 1150 | while ((char*)de < base + size) { |
1150 | next = (struct ext4_dir_entry_2 *) ((char *) de + | 1151 | next = ext4_next_entry(de); |
1151 | le16_to_cpu(de->rec_len)); | ||
1152 | if (de->inode && de->name_len) { | 1152 | if (de->inode && de->name_len) { |
1153 | rec_len = EXT4_DIR_REC_LEN(de->name_len); | 1153 | rec_len = EXT4_DIR_REC_LEN(de->name_len); |
1154 | if (de > to) | 1154 | if (de > to) |
1155 | memmove(to, de, rec_len); | 1155 | memmove(to, de, rec_len); |
1156 | to->rec_len = cpu_to_le16(rec_len); | 1156 | to->rec_len = ext4_rec_len_to_disk(rec_len); |
1157 | prev = to; | 1157 | prev = to; |
1158 | to = (struct ext4_dir_entry_2 *) (((char *) to) + rec_len); | 1158 | to = (struct ext4_dir_entry_2 *) (((char *) to) + rec_len); |
1159 | } | 1159 | } |
@@ -1227,8 +1227,8 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, | |||
1227 | /* Fancy dance to stay within two buffers */ | 1227 | /* Fancy dance to stay within two buffers */ |
1228 | de2 = dx_move_dirents(data1, data2, map + split, count - split); | 1228 | de2 = dx_move_dirents(data1, data2, map + split, count - split); |
1229 | de = dx_pack_dirents(data1,blocksize); | 1229 | de = dx_pack_dirents(data1,blocksize); |
1230 | de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de); | 1230 | de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de); |
1231 | de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2); | 1231 | de2->rec_len = ext4_rec_len_to_disk(data2 + blocksize - (char *) de2); |
1232 | dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1)); | 1232 | dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1)); |
1233 | dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1)); | 1233 | dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1)); |
1234 | 1234 | ||
@@ -1297,7 +1297,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, | |||
1297 | return -EEXIST; | 1297 | return -EEXIST; |
1298 | } | 1298 | } |
1299 | nlen = EXT4_DIR_REC_LEN(de->name_len); | 1299 | nlen = EXT4_DIR_REC_LEN(de->name_len); |
1300 | rlen = le16_to_cpu(de->rec_len); | 1300 | rlen = ext4_rec_len_from_disk(de->rec_len); |
1301 | if ((de->inode? rlen - nlen: rlen) >= reclen) | 1301 | if ((de->inode? rlen - nlen: rlen) >= reclen) |
1302 | break; | 1302 | break; |
1303 | de = (struct ext4_dir_entry_2 *)((char *)de + rlen); | 1303 | de = (struct ext4_dir_entry_2 *)((char *)de + rlen); |
@@ -1316,11 +1316,11 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, | |||
1316 | 1316 | ||
1317 | /* By now the buffer is marked for journaling */ | 1317 | /* By now the buffer is marked for journaling */ |
1318 | nlen = EXT4_DIR_REC_LEN(de->name_len); | 1318 | nlen = EXT4_DIR_REC_LEN(de->name_len); |
1319 | rlen = le16_to_cpu(de->rec_len); | 1319 | rlen = ext4_rec_len_from_disk(de->rec_len); |
1320 | if (de->inode) { | 1320 | if (de->inode) { |
1321 | struct ext4_dir_entry_2 *de1 = (struct ext4_dir_entry_2 *)((char *)de + nlen); | 1321 | struct ext4_dir_entry_2 *de1 = (struct ext4_dir_entry_2 *)((char *)de + nlen); |
1322 | de1->rec_len = cpu_to_le16(rlen - nlen); | 1322 | de1->rec_len = ext4_rec_len_to_disk(rlen - nlen); |
1323 | de->rec_len = cpu_to_le16(nlen); | 1323 | de->rec_len = ext4_rec_len_to_disk(nlen); |
1324 | de = de1; | 1324 | de = de1; |
1325 | } | 1325 | } |
1326 | de->file_type = EXT4_FT_UNKNOWN; | 1326 | de->file_type = EXT4_FT_UNKNOWN; |
@@ -1397,17 +1397,18 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, | |||
1397 | 1397 | ||
1398 | /* The 0th block becomes the root, move the dirents out */ | 1398 | /* The 0th block becomes the root, move the dirents out */ |
1399 | fde = &root->dotdot; | 1399 | fde = &root->dotdot; |
1400 | de = (struct ext4_dir_entry_2 *)((char *)fde + le16_to_cpu(fde->rec_len)); | 1400 | de = (struct ext4_dir_entry_2 *)((char *)fde + |
1401 | ext4_rec_len_from_disk(fde->rec_len)); | ||
1401 | len = ((char *) root) + blocksize - (char *) de; | 1402 | len = ((char *) root) + blocksize - (char *) de; |
1402 | memcpy (data1, de, len); | 1403 | memcpy (data1, de, len); |
1403 | de = (struct ext4_dir_entry_2 *) data1; | 1404 | de = (struct ext4_dir_entry_2 *) data1; |
1404 | top = data1 + len; | 1405 | top = data1 + len; |
1405 | while ((char *)(de2=(void*)de+le16_to_cpu(de->rec_len)) < top) | 1406 | while ((char *)(de2 = ext4_next_entry(de)) < top) |
1406 | de = de2; | 1407 | de = de2; |
1407 | de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de); | 1408 | de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de); |
1408 | /* Initialize the root; the dot dirents already exist */ | 1409 | /* Initialize the root; the dot dirents already exist */ |
1409 | de = (struct ext4_dir_entry_2 *) (&root->dotdot); | 1410 | de = (struct ext4_dir_entry_2 *) (&root->dotdot); |
1410 | de->rec_len = cpu_to_le16(blocksize - EXT4_DIR_REC_LEN(2)); | 1411 | de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(2)); |
1411 | memset (&root->info, 0, sizeof(root->info)); | 1412 | memset (&root->info, 0, sizeof(root->info)); |
1412 | root->info.info_length = sizeof(root->info); | 1413 | root->info.info_length = sizeof(root->info); |
1413 | root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; | 1414 | root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; |
@@ -1487,7 +1488,7 @@ static int ext4_add_entry (handle_t *handle, struct dentry *dentry, | |||
1487 | return retval; | 1488 | return retval; |
1488 | de = (struct ext4_dir_entry_2 *) bh->b_data; | 1489 | de = (struct ext4_dir_entry_2 *) bh->b_data; |
1489 | de->inode = 0; | 1490 | de->inode = 0; |
1490 | de->rec_len = cpu_to_le16(blocksize); | 1491 | de->rec_len = ext4_rec_len_to_disk(blocksize); |
1491 | return add_dirent_to_buf(handle, dentry, inode, de, bh); | 1492 | return add_dirent_to_buf(handle, dentry, inode, de, bh); |
1492 | } | 1493 | } |
1493 | 1494 | ||
@@ -1550,7 +1551,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, | |||
1550 | goto cleanup; | 1551 | goto cleanup; |
1551 | node2 = (struct dx_node *)(bh2->b_data); | 1552 | node2 = (struct dx_node *)(bh2->b_data); |
1552 | entries2 = node2->entries; | 1553 | entries2 = node2->entries; |
1553 | node2->fake.rec_len = cpu_to_le16(sb->s_blocksize); | 1554 | node2->fake.rec_len = ext4_rec_len_to_disk(sb->s_blocksize); |
1554 | node2->fake.inode = 0; | 1555 | node2->fake.inode = 0; |
1555 | BUFFER_TRACE(frame->bh, "get_write_access"); | 1556 | BUFFER_TRACE(frame->bh, "get_write_access"); |
1556 | err = ext4_journal_get_write_access(handle, frame->bh); | 1557 | err = ext4_journal_get_write_access(handle, frame->bh); |
@@ -1648,9 +1649,9 @@ static int ext4_delete_entry (handle_t *handle, | |||
1648 | BUFFER_TRACE(bh, "get_write_access"); | 1649 | BUFFER_TRACE(bh, "get_write_access"); |
1649 | ext4_journal_get_write_access(handle, bh); | 1650 | ext4_journal_get_write_access(handle, bh); |
1650 | if (pde) | 1651 | if (pde) |
1651 | pde->rec_len = | 1652 | pde->rec_len = ext4_rec_len_to_disk( |
1652 | cpu_to_le16(le16_to_cpu(pde->rec_len) + | 1653 | ext4_rec_len_from_disk(pde->rec_len) + |
1653 | le16_to_cpu(de->rec_len)); | 1654 | ext4_rec_len_from_disk(de->rec_len)); |
1654 | else | 1655 | else |
1655 | de->inode = 0; | 1656 | de->inode = 0; |
1656 | dir->i_version++; | 1657 | dir->i_version++; |
@@ -1658,10 +1659,9 @@ static int ext4_delete_entry (handle_t *handle, | |||
1658 | ext4_journal_dirty_metadata(handle, bh); | 1659 | ext4_journal_dirty_metadata(handle, bh); |
1659 | return 0; | 1660 | return 0; |
1660 | } | 1661 | } |
1661 | i += le16_to_cpu(de->rec_len); | 1662 | i += ext4_rec_len_from_disk(de->rec_len); |
1662 | pde = de; | 1663 | pde = de; |
1663 | de = (struct ext4_dir_entry_2 *) | 1664 | de = ext4_next_entry(de); |
1664 | ((char *) de + le16_to_cpu(de->rec_len)); | ||
1665 | } | 1665 | } |
1666 | return -ENOENT; | 1666 | return -ENOENT; |
1667 | } | 1667 | } |
@@ -1824,13 +1824,13 @@ retry: | |||
1824 | de = (struct ext4_dir_entry_2 *) dir_block->b_data; | 1824 | de = (struct ext4_dir_entry_2 *) dir_block->b_data; |
1825 | de->inode = cpu_to_le32(inode->i_ino); | 1825 | de->inode = cpu_to_le32(inode->i_ino); |
1826 | de->name_len = 1; | 1826 | de->name_len = 1; |
1827 | de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de->name_len)); | 1827 | de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len)); |
1828 | strcpy (de->name, "."); | 1828 | strcpy (de->name, "."); |
1829 | ext4_set_de_type(dir->i_sb, de, S_IFDIR); | 1829 | ext4_set_de_type(dir->i_sb, de, S_IFDIR); |
1830 | de = (struct ext4_dir_entry_2 *) | 1830 | de = ext4_next_entry(de); |
1831 | ((char *) de + le16_to_cpu(de->rec_len)); | ||
1832 | de->inode = cpu_to_le32(dir->i_ino); | 1831 | de->inode = cpu_to_le32(dir->i_ino); |
1833 | de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT4_DIR_REC_LEN(1)); | 1832 | de->rec_len = ext4_rec_len_to_disk(inode->i_sb->s_blocksize - |
1833 | EXT4_DIR_REC_LEN(1)); | ||
1834 | de->name_len = 2; | 1834 | de->name_len = 2; |
1835 | strcpy (de->name, ".."); | 1835 | strcpy (de->name, ".."); |
1836 | ext4_set_de_type(dir->i_sb, de, S_IFDIR); | 1836 | ext4_set_de_type(dir->i_sb, de, S_IFDIR); |
@@ -1882,8 +1882,7 @@ static int empty_dir (struct inode * inode) | |||
1882 | return 1; | 1882 | return 1; |
1883 | } | 1883 | } |
1884 | de = (struct ext4_dir_entry_2 *) bh->b_data; | 1884 | de = (struct ext4_dir_entry_2 *) bh->b_data; |
1885 | de1 = (struct ext4_dir_entry_2 *) | 1885 | de1 = ext4_next_entry(de); |
1886 | ((char *) de + le16_to_cpu(de->rec_len)); | ||
1887 | if (le32_to_cpu(de->inode) != inode->i_ino || | 1886 | if (le32_to_cpu(de->inode) != inode->i_ino || |
1888 | !le32_to_cpu(de1->inode) || | 1887 | !le32_to_cpu(de1->inode) || |
1889 | strcmp (".", de->name) || | 1888 | strcmp (".", de->name) || |
@@ -1894,9 +1893,9 @@ static int empty_dir (struct inode * inode) | |||
1894 | brelse (bh); | 1893 | brelse (bh); |
1895 | return 1; | 1894 | return 1; |
1896 | } | 1895 | } |
1897 | offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); | 1896 | offset = ext4_rec_len_from_disk(de->rec_len) + |
1898 | de = (struct ext4_dir_entry_2 *) | 1897 | ext4_rec_len_from_disk(de1->rec_len); |
1899 | ((char *) de1 + le16_to_cpu(de1->rec_len)); | 1898 | de = ext4_next_entry(de1); |
1900 | while (offset < inode->i_size ) { | 1899 | while (offset < inode->i_size ) { |
1901 | if (!bh || | 1900 | if (!bh || |
1902 | (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) { | 1901 | (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) { |
@@ -1925,9 +1924,8 @@ static int empty_dir (struct inode * inode) | |||
1925 | brelse (bh); | 1924 | brelse (bh); |
1926 | return 0; | 1925 | return 0; |
1927 | } | 1926 | } |
1928 | offset += le16_to_cpu(de->rec_len); | 1927 | offset += ext4_rec_len_from_disk(de->rec_len); |
1929 | de = (struct ext4_dir_entry_2 *) | 1928 | de = ext4_next_entry(de); |
1930 | ((char *) de + le16_to_cpu(de->rec_len)); | ||
1931 | } | 1929 | } |
1932 | brelse (bh); | 1930 | brelse (bh); |
1933 | return 1; | 1931 | return 1; |
@@ -2282,8 +2280,7 @@ retry: | |||
2282 | } | 2280 | } |
2283 | 2281 | ||
2284 | #define PARENT_INO(buffer) \ | 2282 | #define PARENT_INO(buffer) \ |
2285 | ((struct ext4_dir_entry_2 *) ((char *) buffer + \ | 2283 | (ext4_next_entry((struct ext4_dir_entry_2 *)(buffer))->inode) |
2286 | le16_to_cpu(((struct ext4_dir_entry_2 *) buffer)->rec_len)))->inode | ||
2287 | 2284 | ||
2288 | /* | 2285 | /* |
2289 | * Anybody can rename anything with this: the permission checks are left to the | 2286 | * Anybody can rename anything with this: the permission checks are left to the |