aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2014-07-28 13:06:26 -0400
committerTheodore Ts'o <tytso@mit.edu>2014-07-28 13:06:26 -0400
commit40b163f1c45f52752677e66d2fd273dbfd273a22 (patch)
tree9047b84d0ab4086388c351d2f49e3ed1fba576d0
parent6e2631463f3a2ce430a295c68aead3ff228ca3cf (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>
-rw-r--r--fs/ext4/dir.c25
-rw-r--r--fs/ext4/ext4.h2
-rw-r--r--fs/ext4/inline.c12
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
574int 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
574const struct file_operations ext4_dir_operations = { 599const 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}
2031extern 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 */
2033extern int ext4_sync_file(struct file *, loff_t, loff_t, int); 2035extern 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;