diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/namei.c | 291 |
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 | ||
86 | static int ext4_dx_csum_verify(struct inode *inode, | ||
87 | struct ext4_dir_entry *dirent); | ||
88 | |||
89 | typedef 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 | |||
96 | static 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; |