aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2013-02-14 23:59:26 -0500
committerTheodore Ts'o <tytso@mit.edu>2013-02-14 23:59:26 -0500
commitdc6982ff4db1f47da73b1967ef5302d6721e5b95 (patch)
tree67c6f4187964e1430154a991e9f8c21e3cfd0b68 /fs/ext4
parent01a523eb51cb505a4bc1eaffeeccd2527d6ab619 (diff)
ext4: refactor code to read directory blocks into ext4_read_dirblock()
The code to read in directory blocks and verify their metadata checksums was replicated in ten different places across fs/ext4/namei.c, and the code was buggy in subtle ways in a number of those replicated sites. In some cases, ext4_error() was called with a training newline. In others, in particularly in empty_dir(), it was possible to call ext4_dirent_csum_verify() on an index block, which would trigger false warnings requesting the system adminsitrator to run e2fsck. By refactoring the code, we make the code more readable, as well as shrinking the compiled object file by over 700 bytes and 50 lines of code. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/namei.c291
1 files changed, 119 insertions, 172 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 3e1529c39808..0e28c749e273 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -83,6 +83,86 @@ static struct buffer_head *ext4_append(handle_t *handle,
83 return bh; 83 return bh;
84} 84}
85 85
86static int ext4_dx_csum_verify(struct inode *inode,
87 struct ext4_dir_entry *dirent);
88
89typedef enum {
90 EITHER, INDEX, DIRENT
91} dirblock_type_t;
92
93#define ext4_read_dirblock(inode, block, type) \
94 __ext4_read_dirblock((inode), (block), (type), __LINE__)
95
96static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
97 ext4_lblk_t block,
98 dirblock_type_t type,
99 unsigned int line)
100{
101 struct buffer_head *bh;
102 struct ext4_dir_entry *dirent;
103 int err = 0, is_dx_block = 0;
104
105 bh = ext4_bread(NULL, inode, block, 0, &err);
106 if (!bh) {
107 if (err == 0) {
108 ext4_error_inode(inode, __func__, line, block,
109 "Directory hole found");
110 return ERR_PTR(-EIO);
111 }
112 __ext4_warning(inode->i_sb, __func__, line,
113 "error reading directory block "
114 "(ino %lu, block %lu)", inode->i_ino,
115 (unsigned long) block);
116 return ERR_PTR(err);
117 }
118 dirent = (struct ext4_dir_entry *) bh->b_data;
119 /* Determine whether or not we have an index block */
120 if (is_dx(inode)) {
121 if (block == 0)
122 is_dx_block = 1;
123 else if (ext4_rec_len_from_disk(dirent->rec_len,
124 inode->i_sb->s_blocksize) ==
125 inode->i_sb->s_blocksize)
126 is_dx_block = 1;
127 }
128 if (!is_dx_block && type == INDEX) {
129 ext4_error_inode(inode, __func__, line, block,
130 "directory leaf block found instead of index block");
131 return ERR_PTR(-EIO);
132 }
133 if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
134 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) ||
135 buffer_verified(bh))
136 return bh;
137
138 /*
139 * An empty leaf block can get mistaken for a index block; for
140 * this reason, we can only check the index checksum when the
141 * caller is sure it should be an index block.
142 */
143 if (is_dx_block && type == INDEX) {
144 if (ext4_dx_csum_verify(inode, dirent))
145 set_buffer_verified(bh);
146 else {
147 ext4_error_inode(inode, __func__, line, block,
148 "Directory index failed checksum");
149 brelse(bh);
150 return ERR_PTR(-EIO);
151 }
152 }
153 if (!is_dx_block) {
154 if (ext4_dirent_csum_verify(inode, dirent))
155 set_buffer_verified(bh);
156 else {
157 ext4_error_inode(inode, __func__, line, block,
158 "Directory block failed checksum");
159 brelse(bh);
160 return ERR_PTR(-EIO);
161 }
162 }
163 return bh;
164}
165
86#ifndef assert 166#ifndef assert
87#define assert(test) J_ASSERT(test) 167#define assert(test) J_ASSERT(test)
88#endif 168#endif
@@ -604,9 +684,9 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
604 u32 hash; 684 u32 hash;
605 685
606 frame->bh = NULL; 686 frame->bh = NULL;
607 if (!(bh = ext4_bread(NULL, dir, 0, 0, err))) { 687 bh = ext4_read_dirblock(dir, 0, INDEX);
608 if (*err == 0) 688 if (IS_ERR(bh)) {
609 *err = ERR_BAD_DX_DIR; 689 *err = PTR_ERR(bh);
610 goto fail; 690 goto fail;
611 } 691 }
612 root = (struct dx_root *) bh->b_data; 692 root = (struct dx_root *) bh->b_data;
@@ -643,15 +723,6 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
643 goto fail; 723 goto fail;
644 } 724 }
645 725
646 if (!buffer_verified(bh) &&
647 !ext4_dx_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data)) {
648 ext4_warning(dir->i_sb, "Root failed checksum");
649 brelse(bh);
650 *err = ERR_BAD_DX_DIR;
651 goto fail;
652 }
653 set_buffer_verified(bh);
654
655 entries = (struct dx_entry *) (((char *)&root->info) + 726 entries = (struct dx_entry *) (((char *)&root->info) +
656 root->info.info_length); 727 root->info.info_length);
657 728
@@ -709,23 +780,13 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
709 frame->entries = entries; 780 frame->entries = entries;
710 frame->at = at; 781 frame->at = at;
711 if (!indirect--) return frame; 782 if (!indirect--) return frame;
712 if (!(bh = ext4_bread(NULL, dir, dx_get_block(at), 0, err))) { 783 bh = ext4_read_dirblock(dir, dx_get_block(at), INDEX);
713 if (!(*err)) 784 if (IS_ERR(bh)) {
714 *err = ERR_BAD_DX_DIR; 785 *err = PTR_ERR(bh);
715 goto fail2; 786 goto fail2;
716 } 787 }
717 entries = ((struct dx_node *) bh->b_data)->entries; 788 entries = ((struct dx_node *) bh->b_data)->entries;
718 789
719 if (!buffer_verified(bh) &&
720 !ext4_dx_csum_verify(dir,
721 (struct ext4_dir_entry *)bh->b_data)) {
722 ext4_warning(dir->i_sb, "Node failed checksum");
723 brelse(bh);
724 *err = ERR_BAD_DX_DIR;
725 goto fail2;
726 }
727 set_buffer_verified(bh);
728
729 if (dx_get_limit(entries) != dx_node_limit (dir)) { 790 if (dx_get_limit(entries) != dx_node_limit (dir)) {
730 ext4_warning(dir->i_sb, 791 ext4_warning(dir->i_sb,
731 "dx entry: limit != node limit"); 792 "dx entry: limit != node limit");
@@ -783,7 +844,7 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
783{ 844{
784 struct dx_frame *p; 845 struct dx_frame *p;
785 struct buffer_head *bh; 846 struct buffer_head *bh;
786 int err, num_frames = 0; 847 int num_frames = 0;
787 __u32 bhash; 848 __u32 bhash;
788 849
789 p = frame; 850 p = frame;
@@ -822,26 +883,9 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
822 * block so no check is necessary 883 * block so no check is necessary
823 */ 884 */
824 while (num_frames--) { 885 while (num_frames--) {
825 if (!(bh = ext4_bread(NULL, dir, dx_get_block(p->at), 886 bh = ext4_read_dirblock(dir, dx_get_block(p->at), INDEX);
826 0, &err))) { 887 if (IS_ERR(bh))
827 if (!err) { 888 return PTR_ERR(bh);
828 ext4_error(dir->i_sb,
829 "Directory hole detected on inode %lu\n",
830 dir->i_ino);
831 return -EIO;
832 }
833 return err; /* Failure */
834 }
835
836 if (!buffer_verified(bh) &&
837 !ext4_dx_csum_verify(dir,
838 (struct ext4_dir_entry *)bh->b_data)) {
839 ext4_warning(dir->i_sb, "Node failed checksum");
840 brelse(bh);
841 return -EIO;
842 }
843 set_buffer_verified(bh);
844
845 p++; 889 p++;
846 brelse(p->bh); 890 brelse(p->bh);
847 p->bh = bh; 891 p->bh = bh;
@@ -867,23 +911,9 @@ static int htree_dirblock_to_tree(struct file *dir_file,
867 911
868 dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n", 912 dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
869 (unsigned long)block)); 913 (unsigned long)block));
870 if (!(bh = ext4_bread(NULL, dir, block, 0, &err))) { 914 bh = ext4_read_dirblock(dir, block, DIRENT);
871 if (!err) { 915 if (IS_ERR(bh))
872 err = -EIO; 916 return PTR_ERR(bh);
873 ext4_error(dir->i_sb,
874 "Directory hole detected on inode %lu\n",
875 dir->i_ino);
876 }
877 return err;
878 }
879
880 if (!buffer_verified(bh) &&
881 !ext4_dirent_csum_verify(dir,
882 (struct ext4_dir_entry *)bh->b_data)) {
883 brelse(bh);
884 return -EIO;
885 }
886 set_buffer_verified(bh);
887 917
888 de = (struct ext4_dir_entry_2 *) bh->b_data; 918 de = (struct ext4_dir_entry_2 *) bh->b_data;
889 top = (struct ext4_dir_entry_2 *) ((char *) de + 919 top = (struct ext4_dir_entry_2 *) ((char *) de +
@@ -1337,26 +1367,11 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
1337 return NULL; 1367 return NULL;
1338 do { 1368 do {
1339 block = dx_get_block(frame->at); 1369 block = dx_get_block(frame->at);
1340 if (!(bh = ext4_bread(NULL, dir, block, 0, err))) { 1370 bh = ext4_read_dirblock(dir, block, DIRENT);
1341 if (!(*err)) { 1371 if (IS_ERR(bh)) {
1342 *err = -EIO; 1372 *err = PTR_ERR(bh);
1343 ext4_error(dir->i_sb,
1344 "Directory hole detected on inode %lu\n",
1345 dir->i_ino);
1346 }
1347 goto errout; 1373 goto errout;
1348 } 1374 }
1349
1350 if (!buffer_verified(bh) &&
1351 !ext4_dirent_csum_verify(dir,
1352 (struct ext4_dir_entry *)bh->b_data)) {
1353 EXT4_ERROR_INODE(dir, "checksumming directory "
1354 "block %lu", (unsigned long)block);
1355 brelse(bh);
1356 *err = -EIO;
1357 goto errout;
1358 }
1359 set_buffer_verified(bh);
1360 retval = search_dirblock(bh, dir, d_name, 1375 retval = search_dirblock(bh, dir, d_name,
1361 block << EXT4_BLOCK_SIZE_BITS(sb), 1376 block << EXT4_BLOCK_SIZE_BITS(sb),
1362 res_dir); 1377 res_dir);
@@ -1920,22 +1935,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
1920 } 1935 }
1921 blocks = dir->i_size >> sb->s_blocksize_bits; 1936 blocks = dir->i_size >> sb->s_blocksize_bits;
1922 for (block = 0; block < blocks; block++) { 1937 for (block = 0; block < blocks; block++) {
1923 if (!(bh = ext4_bread(handle, dir, block, 0, &retval))) { 1938 bh = ext4_read_dirblock(dir, block, DIRENT);
1924 if (!retval) { 1939 if (IS_ERR(bh))
1925 retval = -EIO; 1940 return PTR_ERR(bh);
1926 ext4_error(inode->i_sb, 1941
1927 "Directory hole detected on inode %lu\n",
1928 inode->i_ino);
1929 }
1930 return retval;
1931 }
1932 if (!buffer_verified(bh) &&
1933 !ext4_dirent_csum_verify(dir,
1934 (struct ext4_dir_entry *)bh->b_data)) {
1935 brelse(bh);
1936 return -EIO;
1937 }
1938 set_buffer_verified(bh);
1939 retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); 1942 retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
1940 if (retval != -ENOSPC) { 1943 if (retval != -ENOSPC) {
1941 brelse(bh); 1944 brelse(bh);
@@ -1986,22 +1989,13 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
1986 return err; 1989 return err;
1987 entries = frame->entries; 1990 entries = frame->entries;
1988 at = frame->at; 1991 at = frame->at;
1989 1992 bh = ext4_read_dirblock(dir, dx_get_block(frame->at), DIRENT);
1990 if (!(bh = ext4_bread(handle, dir, dx_get_block(frame->at), 0, &err))) { 1993 if (IS_ERR(bh)) {
1991 if (!err) { 1994 err = PTR_ERR(bh);
1992 err = -EIO; 1995 bh = NULL;
1993 ext4_error(dir->i_sb,
1994 "Directory hole detected on inode %lu\n",
1995 dir->i_ino);
1996 }
1997 goto cleanup; 1996 goto cleanup;
1998 } 1997 }
1999 1998
2000 if (!buffer_verified(bh) &&
2001 !ext4_dirent_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data))
2002 goto journal_error;
2003 set_buffer_verified(bh);
2004
2005 BUFFER_TRACE(bh, "get_write_access"); 1999 BUFFER_TRACE(bh, "get_write_access");
2006 err = ext4_journal_get_write_access(handle, bh); 2000 err = ext4_journal_get_write_access(handle, bh);
2007 if (err) 2001 if (err)
@@ -2352,6 +2346,7 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir,
2352 struct buffer_head *dir_block = NULL; 2346 struct buffer_head *dir_block = NULL;
2353 struct ext4_dir_entry_2 *de; 2347 struct ext4_dir_entry_2 *de;
2354 struct ext4_dir_entry_tail *t; 2348 struct ext4_dir_entry_tail *t;
2349 ext4_lblk_t block = 0;
2355 unsigned int blocksize = dir->i_sb->s_blocksize; 2350 unsigned int blocksize = dir->i_sb->s_blocksize;
2356 int csum_size = 0; 2351 int csum_size = 0;
2357 int err; 2352 int err;
@@ -2368,16 +2363,9 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir,
2368 goto out; 2363 goto out;
2369 } 2364 }
2370 2365
2371 inode->i_size = EXT4_I(inode)->i_disksize = blocksize; 2366 inode->i_size = 0;
2372 if (!(dir_block = ext4_bread(handle, inode, 0, 1, &err))) { 2367 if (!(dir_block = ext4_append(handle, inode, &block, &err)))
2373 if (!err) {
2374 err = -EIO;
2375 ext4_error(inode->i_sb,
2376 "Directory hole detected on inode %lu\n",
2377 inode->i_ino);
2378 }
2379 goto out; 2368 goto out;
2380 }
2381 BUFFER_TRACE(dir_block, "get_write_access"); 2369 BUFFER_TRACE(dir_block, "get_write_access");
2382 err = ext4_journal_get_write_access(handle, dir_block); 2370 err = ext4_journal_get_write_access(handle, dir_block);
2383 if (err) 2371 if (err)
@@ -2477,26 +2465,14 @@ static int empty_dir(struct inode *inode)
2477 } 2465 }
2478 2466
2479 sb = inode->i_sb; 2467 sb = inode->i_sb;
2480 if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) || 2468 if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2)) {
2481 !(bh = ext4_bread(NULL, inode, 0, 0, &err))) { 2469 EXT4_ERROR_INODE(inode, "invalid size");
2482 if (err)
2483 EXT4_ERROR_INODE(inode,
2484 "error %d reading directory lblock 0", err);
2485 else
2486 ext4_warning(inode->i_sb,
2487 "bad directory (dir #%lu) - no data block",
2488 inode->i_ino);
2489 return 1; 2470 return 1;
2490 } 2471 }
2491 if (!buffer_verified(bh) && 2472 bh = ext4_read_dirblock(inode, 0, EITHER);
2492 !ext4_dirent_csum_verify(inode, 2473 if (IS_ERR(bh))
2493 (struct ext4_dir_entry *)bh->b_data)) { 2474 return 1;
2494 EXT4_ERROR_INODE(inode, "checksum error reading directory " 2475
2495 "lblock 0");
2496 brelse(bh);
2497 return -EIO;
2498 }
2499 set_buffer_verified(bh);
2500 de = (struct ext4_dir_entry_2 *) bh->b_data; 2476 de = (struct ext4_dir_entry_2 *) bh->b_data;
2501 de1 = ext4_next_entry(de, sb->s_blocksize); 2477 de1 = ext4_next_entry(de, sb->s_blocksize);
2502 if (le32_to_cpu(de->inode) != inode->i_ino || 2478 if (le32_to_cpu(de->inode) != inode->i_ino ||
@@ -2519,29 +2495,9 @@ static int empty_dir(struct inode *inode)
2519 err = 0; 2495 err = 0;
2520 brelse(bh); 2496 brelse(bh);
2521 lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb); 2497 lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb);
2522 bh = ext4_bread(NULL, inode, lblock, 0, &err); 2498 bh = ext4_read_dirblock(inode, lblock, EITHER);
2523 if (!bh) { 2499 if (IS_ERR(bh))
2524 if (err) 2500 return 1;
2525 EXT4_ERROR_INODE(inode,
2526 "error %d reading directory "
2527 "lblock %u", err, lblock);
2528 else
2529 ext4_warning(inode->i_sb,
2530 "bad directory (dir #%lu) - no data block",
2531 inode->i_ino);
2532
2533 offset += sb->s_blocksize;
2534 continue;
2535 }
2536 if (!buffer_verified(bh) &&
2537 !ext4_dirent_csum_verify(inode,
2538 (struct ext4_dir_entry *)bh->b_data)) {
2539 EXT4_ERROR_INODE(inode, "checksum error "
2540 "reading directory lblock 0");
2541 brelse(bh);
2542 return -EIO;
2543 }
2544 set_buffer_verified(bh);
2545 de = (struct ext4_dir_entry_2 *) bh->b_data; 2501 de = (struct ext4_dir_entry_2 *) bh->b_data;
2546 } 2502 }
2547 if (ext4_check_dir_entry(inode, NULL, de, bh, 2503 if (ext4_check_dir_entry(inode, NULL, de, bh,
@@ -3004,13 +2960,9 @@ static struct buffer_head *ext4_get_first_dir_block(handle_t *handle,
3004 struct buffer_head *bh; 2960 struct buffer_head *bh;
3005 2961
3006 if (!ext4_has_inline_data(inode)) { 2962 if (!ext4_has_inline_data(inode)) {
3007 if (!(bh = ext4_bread(handle, inode, 0, 0, retval))) { 2963 bh = ext4_read_dirblock(inode, 0, EITHER);
3008 if (!*retval) { 2964 if (IS_ERR(bh)) {
3009 *retval = -EIO; 2965 *retval = PTR_ERR(bh);
3010 ext4_error(inode->i_sb,
3011 "Directory hole detected on inode %lu\n",
3012 inode->i_ino);
3013 }
3014 return NULL; 2966 return NULL;
3015 } 2967 }
3016 *parent_de = ext4_next_entry( 2968 *parent_de = ext4_next_entry(
@@ -3089,11 +3041,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
3089 &inlined); 3041 &inlined);
3090 if (!dir_bh) 3042 if (!dir_bh)
3091 goto end_rename; 3043 goto end_rename;
3092 if (!inlined && !buffer_verified(dir_bh) &&
3093 !ext4_dirent_csum_verify(old_inode,
3094 (struct ext4_dir_entry *)dir_bh->b_data))
3095 goto end_rename;
3096 set_buffer_verified(dir_bh);
3097 if (le32_to_cpu(parent_de->inode) != old_dir->i_ino) 3044 if (le32_to_cpu(parent_de->inode) != old_dir->i_ino)
3098 goto end_rename; 3045 goto end_rename;
3099 retval = -EMLINK; 3046 retval = -EMLINK;