diff options
author | Jan Kara <jack@suse.cz> | 2007-10-21 19:41:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-22 11:13:18 -0400 |
commit | 89910cccb8fec0c1140d33a743e72a712efd4f05 (patch) | |
tree | 8165b7cf7039d02b2276fbb3e2aba0e1df7a2ca8 | |
parent | 321bcf92163038e2b96fd3bf9bc29f755c81d9ef (diff) |
ext2: 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 length. So we store 0xffff instead and
convert the value when read from / written to disk.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: <linux-ext4@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/ext2/dir.c | 44 | ||||
-rw-r--r-- | include/linux/ext2_fs.h | 1 |
2 files changed, 33 insertions, 12 deletions
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 05d9342bb64e..d868e26c15eb 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c | |||
@@ -28,6 +28,24 @@ | |||
28 | 28 | ||
29 | typedef struct ext2_dir_entry_2 ext2_dirent; | 29 | typedef struct ext2_dir_entry_2 ext2_dirent; |
30 | 30 | ||
31 | static inline unsigned ext2_rec_len_from_disk(__le16 dlen) | ||
32 | { | ||
33 | unsigned len = le16_to_cpu(dlen); | ||
34 | |||
35 | if (len == EXT2_MAX_REC_LEN) | ||
36 | return 1 << 16; | ||
37 | return len; | ||
38 | } | ||
39 | |||
40 | static inline __le16 ext2_rec_len_to_disk(unsigned len) | ||
41 | { | ||
42 | if (len == (1 << 16)) | ||
43 | return cpu_to_le16(EXT2_MAX_REC_LEN); | ||
44 | else if (len > (1 << 16)) | ||
45 | BUG(); | ||
46 | return cpu_to_le16(len); | ||
47 | } | ||
48 | |||
31 | /* | 49 | /* |
32 | * ext2 uses block-sized chunks. Arguably, sector-sized ones would be | 50 | * ext2 uses block-sized chunks. Arguably, sector-sized ones would be |
33 | * more robust, but we have what we have | 51 | * more robust, but we have what we have |
@@ -106,7 +124,7 @@ static void ext2_check_page(struct page *page) | |||
106 | } | 124 | } |
107 | for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { | 125 | for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { |
108 | p = (ext2_dirent *)(kaddr + offs); | 126 | p = (ext2_dirent *)(kaddr + offs); |
109 | rec_len = le16_to_cpu(p->rec_len); | 127 | rec_len = ext2_rec_len_from_disk(p->rec_len); |
110 | 128 | ||
111 | if (rec_len < EXT2_DIR_REC_LEN(1)) | 129 | if (rec_len < EXT2_DIR_REC_LEN(1)) |
112 | goto Eshort; | 130 | goto Eshort; |
@@ -204,7 +222,8 @@ static inline int ext2_match (int len, const char * const name, | |||
204 | */ | 222 | */ |
205 | static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) | 223 | static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) |
206 | { | 224 | { |
207 | return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len)); | 225 | return (ext2_dirent *)((char *)p + |
226 | ext2_rec_len_from_disk(p->rec_len)); | ||
208 | } | 227 | } |
209 | 228 | ||
210 | static inline unsigned | 229 | static inline unsigned |
@@ -316,7 +335,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) | |||
316 | return 0; | 335 | return 0; |
317 | } | 336 | } |
318 | } | 337 | } |
319 | filp->f_pos += le16_to_cpu(de->rec_len); | 338 | filp->f_pos += ext2_rec_len_from_disk(de->rec_len); |
320 | } | 339 | } |
321 | ext2_put_page(page); | 340 | ext2_put_page(page); |
322 | } | 341 | } |
@@ -425,7 +444,7 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, | |||
425 | { | 444 | { |
426 | loff_t pos = page_offset(page) + | 445 | loff_t pos = page_offset(page) + |
427 | (char *) de - (char *) page_address(page); | 446 | (char *) de - (char *) page_address(page); |
428 | unsigned len = le16_to_cpu(de->rec_len); | 447 | unsigned len = ext2_rec_len_from_disk(de->rec_len); |
429 | int err; | 448 | int err; |
430 | 449 | ||
431 | lock_page(page); | 450 | lock_page(page); |
@@ -482,7 +501,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) | |||
482 | /* We hit i_size */ | 501 | /* We hit i_size */ |
483 | name_len = 0; | 502 | name_len = 0; |
484 | rec_len = chunk_size; | 503 | rec_len = chunk_size; |
485 | de->rec_len = cpu_to_le16(chunk_size); | 504 | de->rec_len = ext2_rec_len_to_disk(chunk_size); |
486 | de->inode = 0; | 505 | de->inode = 0; |
487 | goto got_it; | 506 | goto got_it; |
488 | } | 507 | } |
@@ -496,7 +515,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) | |||
496 | if (ext2_match (namelen, name, de)) | 515 | if (ext2_match (namelen, name, de)) |
497 | goto out_unlock; | 516 | goto out_unlock; |
498 | name_len = EXT2_DIR_REC_LEN(de->name_len); | 517 | name_len = EXT2_DIR_REC_LEN(de->name_len); |
499 | rec_len = le16_to_cpu(de->rec_len); | 518 | rec_len = ext2_rec_len_from_disk(de->rec_len); |
500 | if (!de->inode && rec_len >= reclen) | 519 | if (!de->inode && rec_len >= reclen) |
501 | goto got_it; | 520 | goto got_it; |
502 | if (rec_len >= name_len + reclen) | 521 | if (rec_len >= name_len + reclen) |
@@ -518,8 +537,8 @@ got_it: | |||
518 | goto out_unlock; | 537 | goto out_unlock; |
519 | if (de->inode) { | 538 | if (de->inode) { |
520 | ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); | 539 | ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); |
521 | de1->rec_len = cpu_to_le16(rec_len - name_len); | 540 | de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len); |
522 | de->rec_len = cpu_to_le16(name_len); | 541 | de->rec_len = ext2_rec_len_to_disk(name_len); |
523 | de = de1; | 542 | de = de1; |
524 | } | 543 | } |
525 | de->name_len = namelen; | 544 | de->name_len = namelen; |
@@ -550,7 +569,8 @@ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) | |||
550 | struct inode *inode = mapping->host; | 569 | struct inode *inode = mapping->host; |
551 | char *kaddr = page_address(page); | 570 | char *kaddr = page_address(page); |
552 | unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); | 571 | unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); |
553 | unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len); | 572 | unsigned to = ((char *)dir - kaddr) + |
573 | ext2_rec_len_from_disk(dir->rec_len); | ||
554 | loff_t pos; | 574 | loff_t pos; |
555 | ext2_dirent * pde = NULL; | 575 | ext2_dirent * pde = NULL; |
556 | ext2_dirent * de = (ext2_dirent *) (kaddr + from); | 576 | ext2_dirent * de = (ext2_dirent *) (kaddr + from); |
@@ -574,7 +594,7 @@ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) | |||
574 | &page, NULL); | 594 | &page, NULL); |
575 | BUG_ON(err); | 595 | BUG_ON(err); |
576 | if (pde) | 596 | if (pde) |
577 | pde->rec_len = cpu_to_le16(to - from); | 597 | pde->rec_len = ext2_rec_len_to_disk(to - from); |
578 | dir->inode = 0; | 598 | dir->inode = 0; |
579 | err = ext2_commit_chunk(page, pos, to - from); | 599 | err = ext2_commit_chunk(page, pos, to - from); |
580 | inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; | 600 | inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; |
@@ -610,14 +630,14 @@ int ext2_make_empty(struct inode *inode, struct inode *parent) | |||
610 | memset(kaddr, 0, chunk_size); | 630 | memset(kaddr, 0, chunk_size); |
611 | de = (struct ext2_dir_entry_2 *)kaddr; | 631 | de = (struct ext2_dir_entry_2 *)kaddr; |
612 | de->name_len = 1; | 632 | de->name_len = 1; |
613 | de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1)); | 633 | de->rec_len = ext2_rec_len_to_disk(EXT2_DIR_REC_LEN(1)); |
614 | memcpy (de->name, ".\0\0", 4); | 634 | memcpy (de->name, ".\0\0", 4); |
615 | de->inode = cpu_to_le32(inode->i_ino); | 635 | de->inode = cpu_to_le32(inode->i_ino); |
616 | ext2_set_de_type (de, inode); | 636 | ext2_set_de_type (de, inode); |
617 | 637 | ||
618 | de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1)); | 638 | de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1)); |
619 | de->name_len = 2; | 639 | de->name_len = 2; |
620 | de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1)); | 640 | de->rec_len = ext2_rec_len_to_disk(chunk_size - EXT2_DIR_REC_LEN(1)); |
621 | de->inode = cpu_to_le32(parent->i_ino); | 641 | de->inode = cpu_to_le32(parent->i_ino); |
622 | memcpy (de->name, "..\0", 4); | 642 | memcpy (de->name, "..\0", 4); |
623 | ext2_set_de_type (de, inode); | 643 | ext2_set_de_type (de, inode); |
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index c77c3bbfe4bb..0f6c86c634fd 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h | |||
@@ -561,6 +561,7 @@ enum { | |||
561 | #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) | 561 | #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) |
562 | #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ | 562 | #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ |
563 | ~EXT2_DIR_ROUND) | 563 | ~EXT2_DIR_ROUND) |
564 | #define EXT2_MAX_REC_LEN ((1<<16)-1) | ||
564 | 565 | ||
565 | static inline ext2_fsblk_t | 566 | static inline ext2_fsblk_t |
566 | ext2_group_first_block_no(struct super_block *sb, unsigned long group_no) | 567 | ext2_group_first_block_no(struct super_block *sb, unsigned long group_no) |