aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/scrub.c
diff options
context:
space:
mode:
authorArne Jansen <sensille@gmx.net>2011-06-03 04:09:26 -0400
committerArne Jansen <sensille@gmx.net>2011-06-10 06:14:13 -0400
commit8c51032f978bac5bec5dae0c5de4f85db97c1cc9 (patch)
treecb67a12fd149fa57a5024482f272b4ae22c48164 /fs/btrfs/scrub.c
parentad3e34bba4b64ab8e1f5ea1a17768e1a0d9648ea (diff)
btrfs: scrub: errors in tree enumeration
due to the semantics of btrfs_search_slot the path can point to an invalid slot when ret > 0. This condition went unnoticed, which in turn could have led to an incomplete scrubbing. Signed-off-by: Arne Jansen <sensille@gmx.net>
Diffstat (limited to 'fs/btrfs/scrub.c')
-rw-r--r--fs/btrfs/scrub.c57
1 files changed, 34 insertions, 23 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index df50fd1eca8f..d5a4108cedaf 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -804,18 +804,12 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev,
804 804
805 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 805 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
806 if (ret < 0) 806 if (ret < 0)
807 goto out; 807 goto out_noplug;
808
809 l = path->nodes[0];
810 slot = path->slots[0];
811 btrfs_item_key_to_cpu(l, &key, slot);
812 if (key.objectid != logical) {
813 ret = btrfs_previous_item(root, path, 0,
814 BTRFS_EXTENT_ITEM_KEY);
815 if (ret < 0)
816 goto out;
817 }
818 808
809 /*
810 * we might miss half an extent here, but that doesn't matter,
811 * as it's only the prefetch
812 */
819 while (1) { 813 while (1) {
820 l = path->nodes[0]; 814 l = path->nodes[0];
821 slot = path->slots[0]; 815 slot = path->slots[0];
@@ -824,7 +818,7 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev,
824 if (ret == 0) 818 if (ret == 0)
825 continue; 819 continue;
826 if (ret < 0) 820 if (ret < 0)
827 goto out; 821 goto out_noplug;
828 822
829 break; 823 break;
830 } 824 }
@@ -906,15 +900,20 @@ again:
906 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 900 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
907 if (ret < 0) 901 if (ret < 0)
908 goto out; 902 goto out;
909 903 if (ret > 0) {
910 l = path->nodes[0];
911 slot = path->slots[0];
912 btrfs_item_key_to_cpu(l, &key, slot);
913 if (key.objectid != logical) {
914 ret = btrfs_previous_item(root, path, 0, 904 ret = btrfs_previous_item(root, path, 0,
915 BTRFS_EXTENT_ITEM_KEY); 905 BTRFS_EXTENT_ITEM_KEY);
916 if (ret < 0) 906 if (ret < 0)
917 goto out; 907 goto out;
908 if (ret > 0) {
909 /* there's no smaller item, so stick with the
910 * larger one */
911 btrfs_release_path(path);
912 ret = btrfs_search_slot(NULL, root, &key,
913 path, 0, 0);
914 if (ret < 0)
915 goto out;
916 }
918 } 917 }
919 918
920 while (1) { 919 while (1) {
@@ -989,6 +988,7 @@ next:
989 988
990out: 989out:
991 blk_finish_plug(&plug); 990 blk_finish_plug(&plug);
991out_noplug:
992 btrfs_free_path(path); 992 btrfs_free_path(path);
993 return ret < 0 ? ret : 0; 993 return ret < 0 ? ret : 0;
994} 994}
@@ -1064,8 +1064,15 @@ int scrub_enumerate_chunks(struct scrub_dev *sdev, u64 start, u64 end)
1064 while (1) { 1064 while (1) {
1065 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 1065 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
1066 if (ret < 0) 1066 if (ret < 0)
1067 goto out; 1067 break;
1068 ret = 0; 1068 if (ret > 0) {
1069 if (path->slots[0] >=
1070 btrfs_header_nritems(path->nodes[0])) {
1071 ret = btrfs_next_leaf(root, path);
1072 if (ret)
1073 break;
1074 }
1075 }
1069 1076
1070 l = path->nodes[0]; 1077 l = path->nodes[0];
1071 slot = path->slots[0]; 1078 slot = path->slots[0];
@@ -1075,7 +1082,7 @@ int scrub_enumerate_chunks(struct scrub_dev *sdev, u64 start, u64 end)
1075 if (found_key.objectid != sdev->dev->devid) 1082 if (found_key.objectid != sdev->dev->devid)
1076 break; 1083 break;
1077 1084
1078 if (btrfs_key_type(&key) != BTRFS_DEV_EXTENT_KEY) 1085 if (btrfs_key_type(&found_key) != BTRFS_DEV_EXTENT_KEY)
1079 break; 1086 break;
1080 1087
1081 if (found_key.offset >= end) 1088 if (found_key.offset >= end)
@@ -1104,7 +1111,7 @@ int scrub_enumerate_chunks(struct scrub_dev *sdev, u64 start, u64 end)
1104 cache = btrfs_lookup_block_group(fs_info, chunk_offset); 1111 cache = btrfs_lookup_block_group(fs_info, chunk_offset);
1105 if (!cache) { 1112 if (!cache) {
1106 ret = -ENOENT; 1113 ret = -ENOENT;
1107 goto out; 1114 break;
1108 } 1115 }
1109 ret = scrub_chunk(sdev, chunk_tree, chunk_objectid, 1116 ret = scrub_chunk(sdev, chunk_tree, chunk_objectid,
1110 chunk_offset, length); 1117 chunk_offset, length);
@@ -1116,9 +1123,13 @@ int scrub_enumerate_chunks(struct scrub_dev *sdev, u64 start, u64 end)
1116 btrfs_release_path(path); 1123 btrfs_release_path(path);
1117 } 1124 }
1118 1125
1119out:
1120 btrfs_free_path(path); 1126 btrfs_free_path(path);
1121 return ret; 1127
1128 /*
1129 * ret can still be 1 from search_slot or next_leaf,
1130 * that's not an error
1131 */
1132 return ret < 0 ? ret : 0;
1122} 1133}
1123 1134
1124static noinline_for_stack int scrub_supers(struct scrub_dev *sdev) 1135static noinline_for_stack int scrub_supers(struct scrub_dev *sdev)