diff options
author | Arne Jansen <sensille@gmx.net> | 2011-06-03 04:09:26 -0400 |
---|---|---|
committer | Arne Jansen <sensille@gmx.net> | 2011-06-10 06:14:13 -0400 |
commit | 8c51032f978bac5bec5dae0c5de4f85db97c1cc9 (patch) | |
tree | cb67a12fd149fa57a5024482f272b4ae22c48164 /fs | |
parent | ad3e34bba4b64ab8e1f5ea1a17768e1a0d9648ea (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')
-rw-r--r-- | fs/btrfs/scrub.c | 57 |
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 | ||
990 | out: | 989 | out: |
991 | blk_finish_plug(&plug); | 990 | blk_finish_plug(&plug); |
991 | out_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 | ||
1119 | out: | ||
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 | ||
1124 | static noinline_for_stack int scrub_supers(struct scrub_dev *sdev) | 1135 | static noinline_for_stack int scrub_supers(struct scrub_dev *sdev) |