aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2008-01-28 23:58:27 -0500
committerTheodore Ts'o <tytso@mit.edu>2008-01-28 23:58:27 -0500
commita72d7f834e1afa08421938d7eb06bd8e56b0e58c (patch)
tree21c6fbbf4187fa8ba56878ea2d5e576e123e1f95
parentafc7cbca5bfd556c3e12d3acefbee5ab0cbd4670 (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>
-rw-r--r--fs/ext4/dir.c12
-rw-r--r--fs/ext4/namei.c77
-rw-r--r--include/linux/ext4_fs.h20
3 files changed, 63 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 */
552static inline struct ext4_dir_entry_2 *ext4_next_entry(struct ext4_dir_entry_2 *p) 552static 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
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index dfe4487fc739..fb31c1aba989 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -767,6 +767,26 @@ struct ext4_dir_entry_2 {
767#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1) 767#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)
768#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \ 768#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \
769 ~EXT4_DIR_ROUND) 769 ~EXT4_DIR_ROUND)
770#define EXT4_MAX_REC_LEN ((1<<16)-1)
771
772static inline unsigned ext4_rec_len_from_disk(__le16 dlen)
773{
774 unsigned len = le16_to_cpu(dlen);
775
776 if (len == EXT4_MAX_REC_LEN)
777 return 1 << 16;
778 return len;
779}
780
781static inline __le16 ext4_rec_len_to_disk(unsigned len)
782{
783 if (len == (1 << 16))
784 return cpu_to_le16(EXT4_MAX_REC_LEN);
785 else if (len > (1 << 16))
786 BUG();
787 return cpu_to_le16(len);
788}
789
770/* 790/*
771 * Hash Tree Directory indexing 791 * Hash Tree Directory indexing
772 * (c) Daniel Phillips, 2001 792 * (c) Daniel Phillips, 2001