diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2014-07-28 13:06:26 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2014-07-28 13:06:26 -0400 |
commit | 40b163f1c45f52752677e66d2fd273dbfd273a22 (patch) | |
tree | 9047b84d0ab4086388c351d2f49e3ed1fba576d0 /fs | |
parent | 6e2631463f3a2ce430a295c68aead3ff228ca3cf (diff) |
ext4: check inline directory before converting
Before converting an inline directory to a regular directory, check
the directory entries to make sure they're not obviously broken.
This helps us to avoid a BUG_ON if one of the dirents is trashed.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/dir.c | 25 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/inline.c | 12 |
3 files changed, 39 insertions, 0 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index ef1bed66c14f..0bb3f9ea0832 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -571,6 +571,31 @@ static int ext4_release_dir(struct inode *inode, struct file *filp) | |||
571 | return 0; | 571 | return 0; |
572 | } | 572 | } |
573 | 573 | ||
574 | int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, void *buf, | ||
575 | int buf_size) | ||
576 | { | ||
577 | struct ext4_dir_entry_2 *de; | ||
578 | int nlen, rlen; | ||
579 | unsigned int offset = 0; | ||
580 | char *top; | ||
581 | |||
582 | de = (struct ext4_dir_entry_2 *)buf; | ||
583 | top = buf + buf_size; | ||
584 | while ((char *) de < top) { | ||
585 | if (ext4_check_dir_entry(dir, NULL, de, bh, | ||
586 | buf, buf_size, offset)) | ||
587 | return -EIO; | ||
588 | nlen = EXT4_DIR_REC_LEN(de->name_len); | ||
589 | rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); | ||
590 | de = (struct ext4_dir_entry_2 *)((char *)de + rlen); | ||
591 | offset += rlen; | ||
592 | } | ||
593 | if ((char *) de > top) | ||
594 | return -EIO; | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
574 | const struct file_operations ext4_dir_operations = { | 599 | const struct file_operations ext4_dir_operations = { |
575 | .llseek = ext4_dir_llseek, | 600 | .llseek = ext4_dir_llseek, |
576 | .read = generic_read_dir, | 601 | .read = generic_read_dir, |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 321760d6b353..5b19760b1de5 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2028,6 +2028,8 @@ static inline unsigned char get_dtype(struct super_block *sb, int filetype) | |||
2028 | 2028 | ||
2029 | return ext4_filetype_table[filetype]; | 2029 | return ext4_filetype_table[filetype]; |
2030 | } | 2030 | } |
2031 | extern int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, | ||
2032 | void *buf, int buf_size); | ||
2031 | 2033 | ||
2032 | /* fsync.c */ | 2034 | /* fsync.c */ |
2033 | extern int ext4_sync_file(struct file *, loff_t, loff_t, int); | 2035 | extern int ext4_sync_file(struct file *, loff_t, loff_t, int); |
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 141b6acbc51c..bea662bd0ca6 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c | |||
@@ -1172,6 +1172,18 @@ static int ext4_convert_inline_data_nolock(handle_t *handle, | |||
1172 | if (error < 0) | 1172 | if (error < 0) |
1173 | goto out; | 1173 | goto out; |
1174 | 1174 | ||
1175 | /* | ||
1176 | * Make sure the inline directory entries pass checks before we try to | ||
1177 | * convert them, so that we avoid touching stuff that needs fsck. | ||
1178 | */ | ||
1179 | if (S_ISDIR(inode->i_mode)) { | ||
1180 | error = ext4_check_all_de(inode, iloc->bh, | ||
1181 | buf + EXT4_INLINE_DOTDOT_SIZE, | ||
1182 | inline_size - EXT4_INLINE_DOTDOT_SIZE); | ||
1183 | if (error) | ||
1184 | goto out; | ||
1185 | } | ||
1186 | |||
1175 | error = ext4_destroy_inline_data_nolock(handle, inode); | 1187 | error = ext4_destroy_inline_data_nolock(handle, inode); |
1176 | if (error) | 1188 | if (error) |
1177 | goto out; | 1189 | goto out; |