aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@us.ibm.com>2012-04-29 18:39:10 -0400
committerTheodore Ts'o <tytso@mit.edu>2012-04-29 18:39:10 -0400
commitdbe89444042ab6540bc304343cfdcbc8b95d003d (patch)
tree436c6e039ffef3387fcfac0bb699f0b14b4c9ba6 /fs
parent7ac5990d5a3e2e465162880819cc46c6427d3b6f (diff)
ext4: Calculate and verify checksums for htree nodes
Calculate and verify the checksum for directory index tree (htree) node blocks. The checksum is stored in the last 4 bytes of the htree block and requires the dx_entry array to stop 1 dx_entry short of the end of the block. Signed-off-by: Darrick J. Wong <djwong@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/namei.c160
1 files changed, 156 insertions, 4 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 625125172d05..f8efedde7593 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -188,6 +188,121 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
188static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, 188static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
189 struct inode *inode); 189 struct inode *inode);
190 190
191/* checksumming functions */
192static struct dx_countlimit *get_dx_countlimit(struct inode *inode,
193 struct ext4_dir_entry *dirent,
194 int *offset)
195{
196 struct ext4_dir_entry *dp;
197 struct dx_root_info *root;
198 int count_offset;
199
200 if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb))
201 count_offset = 8;
202 else if (le16_to_cpu(dirent->rec_len) == 12) {
203 dp = (struct ext4_dir_entry *)(((void *)dirent) + 12);
204 if (le16_to_cpu(dp->rec_len) !=
205 EXT4_BLOCK_SIZE(inode->i_sb) - 12)
206 return NULL;
207 root = (struct dx_root_info *)(((void *)dp + 12));
208 if (root->reserved_zero ||
209 root->info_length != sizeof(struct dx_root_info))
210 return NULL;
211 count_offset = 32;
212 } else
213 return NULL;
214
215 if (offset)
216 *offset = count_offset;
217 return (struct dx_countlimit *)(((void *)dirent) + count_offset);
218}
219
220static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent,
221 int count_offset, int count, struct dx_tail *t)
222{
223 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
224 struct ext4_inode_info *ei = EXT4_I(inode);
225 __u32 csum, old_csum;
226 int size;
227
228 size = count_offset + (count * sizeof(struct dx_entry));
229 old_csum = t->dt_checksum;
230 t->dt_checksum = 0;
231 csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size);
232 csum = ext4_chksum(sbi, csum, (__u8 *)t, sizeof(struct dx_tail));
233 t->dt_checksum = old_csum;
234
235 return cpu_to_le32(csum);
236}
237
238static int ext4_dx_csum_verify(struct inode *inode,
239 struct ext4_dir_entry *dirent)
240{
241 struct dx_countlimit *c;
242 struct dx_tail *t;
243 int count_offset, limit, count;
244
245 if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
246 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
247 return 1;
248
249 c = get_dx_countlimit(inode, dirent, &count_offset);
250 if (!c) {
251 EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D.");
252 return 1;
253 }
254 limit = le16_to_cpu(c->limit);
255 count = le16_to_cpu(c->count);
256 if (count_offset + (limit * sizeof(struct dx_entry)) >
257 EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
258 EXT4_ERROR_INODE(inode, "metadata_csum set but no space for "
259 "tree checksum found. Run e2fsck -D.");
260 return 1;
261 }
262 t = (struct dx_tail *)(((struct dx_entry *)c) + limit);
263
264 if (t->dt_checksum != ext4_dx_csum(inode, dirent, count_offset,
265 count, t))
266 return 0;
267 return 1;
268}
269
270static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent)
271{
272 struct dx_countlimit *c;
273 struct dx_tail *t;
274 int count_offset, limit, count;
275
276 if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
277 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
278 return;
279
280 c = get_dx_countlimit(inode, dirent, &count_offset);
281 if (!c) {
282 EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D.");
283 return;
284 }
285 limit = le16_to_cpu(c->limit);
286 count = le16_to_cpu(c->count);
287 if (count_offset + (limit * sizeof(struct dx_entry)) >
288 EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) {
289 EXT4_ERROR_INODE(inode, "metadata_csum set but no space for "
290 "tree checksum. Run e2fsck -D.");
291 return;
292 }
293 t = (struct dx_tail *)(((struct dx_entry *)c) + limit);
294
295 t->dt_checksum = ext4_dx_csum(inode, dirent, count_offset, count, t);
296}
297
298static inline int ext4_handle_dirty_dx_node(handle_t *handle,
299 struct inode *inode,
300 struct buffer_head *bh)
301{
302 ext4_dx_csum_set(inode, (struct ext4_dir_entry *)bh->b_data);
303 return ext4_handle_dirty_metadata(handle, inode, bh);
304}
305
191/* 306/*
192 * p is at least 6 bytes before the end of page 307 * p is at least 6 bytes before the end of page
193 */ 308 */
@@ -247,12 +362,20 @@ static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
247{ 362{
248 unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) - 363 unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) -
249 EXT4_DIR_REC_LEN(2) - infosize; 364 EXT4_DIR_REC_LEN(2) - infosize;
365
366 if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
367 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
368 entry_space -= sizeof(struct dx_tail);
250 return entry_space / sizeof(struct dx_entry); 369 return entry_space / sizeof(struct dx_entry);
251} 370}
252 371
253static inline unsigned dx_node_limit(struct inode *dir) 372static inline unsigned dx_node_limit(struct inode *dir)
254{ 373{
255 unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0); 374 unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0);
375
376 if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb,
377 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
378 entry_space -= sizeof(struct dx_tail);
256 return entry_space / sizeof(struct dx_entry); 379 return entry_space / sizeof(struct dx_entry);
257} 380}
258 381
@@ -398,6 +521,15 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
398 goto fail; 521 goto fail;
399 } 522 }
400 523
524 if (!buffer_verified(bh) &&
525 !ext4_dx_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data)) {
526 ext4_warning(dir->i_sb, "Root failed checksum");
527 brelse(bh);
528 *err = ERR_BAD_DX_DIR;
529 goto fail;
530 }
531 set_buffer_verified(bh);
532
401 entries = (struct dx_entry *) (((char *)&root->info) + 533 entries = (struct dx_entry *) (((char *)&root->info) +
402 root->info.info_length); 534 root->info.info_length);
403 535
@@ -458,6 +590,17 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
458 if (!(bh = ext4_bread (NULL,dir, dx_get_block(at), 0, err))) 590 if (!(bh = ext4_bread (NULL,dir, dx_get_block(at), 0, err)))
459 goto fail2; 591 goto fail2;
460 at = entries = ((struct dx_node *) bh->b_data)->entries; 592 at = entries = ((struct dx_node *) bh->b_data)->entries;
593
594 if (!buffer_verified(bh) &&
595 !ext4_dx_csum_verify(dir,
596 (struct ext4_dir_entry *)bh->b_data)) {
597 ext4_warning(dir->i_sb, "Node failed checksum");
598 brelse(bh);
599 *err = ERR_BAD_DX_DIR;
600 goto fail;
601 }
602 set_buffer_verified(bh);
603
461 if (dx_get_limit(entries) != dx_node_limit (dir)) { 604 if (dx_get_limit(entries) != dx_node_limit (dir)) {
462 ext4_warning(dir->i_sb, 605 ext4_warning(dir->i_sb,
463 "dx entry: limit != node limit"); 606 "dx entry: limit != node limit");
@@ -557,6 +700,15 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
557 if (!(bh = ext4_bread(NULL, dir, dx_get_block(p->at), 700 if (!(bh = ext4_bread(NULL, dir, dx_get_block(p->at),
558 0, &err))) 701 0, &err)))
559 return err; /* Failure */ 702 return err; /* Failure */
703
704 if (!buffer_verified(bh) &&
705 !ext4_dx_csum_verify(dir,
706 (struct ext4_dir_entry *)bh->b_data)) {
707 ext4_warning(dir->i_sb, "Node failed checksum");
708 return -EIO;
709 }
710 set_buffer_verified(bh);
711
560 p++; 712 p++;
561 brelse(p->bh); 713 brelse(p->bh);
562 p->bh = bh; 714 p->bh = bh;
@@ -1232,7 +1384,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
1232 err = ext4_handle_dirty_metadata(handle, dir, bh2); 1384 err = ext4_handle_dirty_metadata(handle, dir, bh2);
1233 if (err) 1385 if (err)
1234 goto journal_error; 1386 goto journal_error;
1235 err = ext4_handle_dirty_metadata(handle, dir, frame->bh); 1387 err = ext4_handle_dirty_dx_node(handle, dir, frame->bh);
1236 if (err) 1388 if (err)
1237 goto journal_error; 1389 goto journal_error;
1238 brelse(bh2); 1390 brelse(bh2);
@@ -1419,7 +1571,7 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
1419 frame->bh = bh; 1571 frame->bh = bh;
1420 bh = bh2; 1572 bh = bh2;
1421 1573
1422 ext4_handle_dirty_metadata(handle, dir, frame->bh); 1574 ext4_handle_dirty_dx_node(handle, dir, frame->bh);
1423 ext4_handle_dirty_metadata(handle, dir, bh); 1575 ext4_handle_dirty_metadata(handle, dir, bh);
1424 1576
1425 de = do_split(handle,dir, &bh, frame, &hinfo, &retval); 1577 de = do_split(handle,dir, &bh, frame, &hinfo, &retval);
@@ -1594,7 +1746,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
1594 dxtrace(dx_show_index("node", frames[1].entries)); 1746 dxtrace(dx_show_index("node", frames[1].entries));
1595 dxtrace(dx_show_index("node", 1747 dxtrace(dx_show_index("node",
1596 ((struct dx_node *) bh2->b_data)->entries)); 1748 ((struct dx_node *) bh2->b_data)->entries));
1597 err = ext4_handle_dirty_metadata(handle, dir, bh2); 1749 err = ext4_handle_dirty_dx_node(handle, dir, bh2);
1598 if (err) 1750 if (err)
1599 goto journal_error; 1751 goto journal_error;
1600 brelse (bh2); 1752 brelse (bh2);
@@ -1620,7 +1772,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
1620 if (err) 1772 if (err)
1621 goto journal_error; 1773 goto journal_error;
1622 } 1774 }
1623 err = ext4_handle_dirty_metadata(handle, dir, frames[0].bh); 1775 err = ext4_handle_dirty_dx_node(handle, dir, frames[0].bh);
1624 if (err) { 1776 if (err) {
1625 ext4_std_error(inode->i_sb, err); 1777 ext4_std_error(inode->i_sb, err);
1626 goto cleanup; 1778 goto cleanup;