diff options
author | Alexander Block <ablock84@googlemail.com> | 2012-06-19 09:42:26 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2012-06-21 07:19:34 -0400 |
commit | 69bca40d41c613927b150c5392505f1894fe3010 (patch) | |
tree | 924b5d5f914a254f4348fc6b20e0e1e2e131a53c /fs | |
parent | 1c8f52a5e9539600543347bcdefafa1854e07986 (diff) |
Btrfs: don't assume to be on the correct extent in add_all_parents
add_all_parents did assume that path is already at a correct extent data
item, which may not be true in case of data extents that were partly
rewritten and splitted.
We need to check if we're on a matching extent for every item and only
for the ones after the first. The loop is changed to do this now.
This patch also fixes a bug introduced with commit 3b127fd8 "Btrfs:
remove obsolete btrfs_next_leaf call from __resolve_indirect_ref".
The removal of next_leaf did sometimes result in slot==nritems when
the above described case happens, and thus resulting in invalid values
(e.g. wanted_obejctid) in add_all_parents (leading to missed backrefs
or even crashes).
Signed-off-by: Alexander Block <ablock84@googlemail.com>
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/backref.c | 94 |
1 files changed, 52 insertions, 42 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 8f7d1237b7a0..7301cdb4b2cb 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c | |||
@@ -179,61 +179,74 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id, | |||
179 | 179 | ||
180 | static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, | 180 | static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, |
181 | struct ulist *parents, int level, | 181 | struct ulist *parents, int level, |
182 | struct btrfs_key *key, u64 time_seq, | 182 | struct btrfs_key *key_for_search, u64 time_seq, |
183 | u64 wanted_disk_byte, | 183 | u64 wanted_disk_byte, |
184 | const u64 *extent_item_pos) | 184 | const u64 *extent_item_pos) |
185 | { | 185 | { |
186 | int ret; | 186 | int ret = 0; |
187 | int slot = path->slots[level]; | 187 | int slot; |
188 | struct extent_buffer *eb = path->nodes[level]; | 188 | struct extent_buffer *eb; |
189 | struct btrfs_key key; | ||
189 | struct btrfs_file_extent_item *fi; | 190 | struct btrfs_file_extent_item *fi; |
190 | struct extent_inode_elem *eie = NULL; | 191 | struct extent_inode_elem *eie = NULL; |
191 | u64 disk_byte; | 192 | u64 disk_byte; |
192 | u64 wanted_objectid = key->objectid; | ||
193 | 193 | ||
194 | add_parent: | 194 | if (level != 0) { |
195 | if (level == 0 && extent_item_pos) { | 195 | eb = path->nodes[level]; |
196 | fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); | 196 | ret = ulist_add(parents, eb->start, 0, GFP_NOFS); |
197 | ret = check_extent_in_eb(key, eb, fi, *extent_item_pos, &eie); | ||
198 | if (ret < 0) | 197 | if (ret < 0) |
199 | return ret; | 198 | return ret; |
200 | } | ||
201 | ret = ulist_add(parents, eb->start, (unsigned long)eie, GFP_NOFS); | ||
202 | if (ret < 0) | ||
203 | return ret; | ||
204 | |||
205 | if (level != 0) | ||
206 | return 0; | 199 | return 0; |
200 | } | ||
207 | 201 | ||
208 | /* | 202 | /* |
209 | * if the current leaf is full with EXTENT_DATA items, we must | 203 | * We normally enter this function with the path already pointing to |
210 | * check the next one if that holds a reference as well. | 204 | * the first item to check. But sometimes, we may enter it with |
211 | * ref->count cannot be used to skip this check. | 205 | * slot==nritems. In that case, go to the next leaf before we continue. |
212 | * repeat this until we don't find any additional EXTENT_DATA items. | ||
213 | */ | 206 | */ |
214 | while (1) { | 207 | if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) |
215 | eie = NULL; | ||
216 | ret = btrfs_next_old_leaf(root, path, time_seq); | 208 | ret = btrfs_next_old_leaf(root, path, time_seq); |
217 | if (ret < 0) | ||
218 | return ret; | ||
219 | if (ret) | ||
220 | return 0; | ||
221 | 209 | ||
210 | while (!ret) { | ||
222 | eb = path->nodes[0]; | 211 | eb = path->nodes[0]; |
223 | for (slot = 0; slot < btrfs_header_nritems(eb); ++slot) { | 212 | slot = path->slots[0]; |
224 | btrfs_item_key_to_cpu(eb, key, slot); | 213 | |
225 | if (key->objectid != wanted_objectid || | 214 | btrfs_item_key_to_cpu(eb, &key, slot); |
226 | key->type != BTRFS_EXTENT_DATA_KEY) | 215 | |
227 | return 0; | 216 | if (key.objectid != key_for_search->objectid || |
228 | fi = btrfs_item_ptr(eb, slot, | 217 | key.type != BTRFS_EXTENT_DATA_KEY) |
229 | struct btrfs_file_extent_item); | 218 | break; |
230 | disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); | 219 | |
231 | if (disk_byte == wanted_disk_byte) | 220 | fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); |
232 | goto add_parent; | 221 | disk_byte = btrfs_file_extent_disk_bytenr(eb, fi); |
222 | |||
223 | if (disk_byte == wanted_disk_byte) { | ||
224 | eie = NULL; | ||
225 | if (extent_item_pos) { | ||
226 | ret = check_extent_in_eb(&key, eb, fi, | ||
227 | *extent_item_pos, | ||
228 | &eie); | ||
229 | if (ret < 0) | ||
230 | break; | ||
231 | } | ||
232 | if (!ret) { | ||
233 | ret = ulist_add(parents, eb->start, | ||
234 | (unsigned long)eie, GFP_NOFS); | ||
235 | if (ret < 0) | ||
236 | break; | ||
237 | if (!extent_item_pos) { | ||
238 | ret = btrfs_next_old_leaf(root, path, | ||
239 | time_seq); | ||
240 | continue; | ||
241 | } | ||
242 | } | ||
233 | } | 243 | } |
244 | ret = btrfs_next_old_item(root, path, time_seq); | ||
234 | } | 245 | } |
235 | 246 | ||
236 | return 0; | 247 | if (ret > 0) |
248 | ret = 0; | ||
249 | return ret; | ||
237 | } | 250 | } |
238 | 251 | ||
239 | /* | 252 | /* |
@@ -250,7 +263,6 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, | |||
250 | struct btrfs_path *path; | 263 | struct btrfs_path *path; |
251 | struct btrfs_root *root; | 264 | struct btrfs_root *root; |
252 | struct btrfs_key root_key; | 265 | struct btrfs_key root_key; |
253 | struct btrfs_key key = {0}; | ||
254 | struct extent_buffer *eb; | 266 | struct extent_buffer *eb; |
255 | int ret = 0; | 267 | int ret = 0; |
256 | int root_level; | 268 | int root_level; |
@@ -295,11 +307,9 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, | |||
295 | goto out; | 307 | goto out; |
296 | } | 308 | } |
297 | 309 | ||
298 | if (level == 0) | 310 | ret = add_all_parents(root, path, parents, level, &ref->key_for_search, |
299 | btrfs_item_key_to_cpu(eb, &key, path->slots[0]); | 311 | time_seq, ref->wanted_disk_byte, |
300 | 312 | extent_item_pos); | |
301 | ret = add_all_parents(root, path, parents, level, &key, time_seq, | ||
302 | ref->wanted_disk_byte, extent_item_pos); | ||
303 | out: | 313 | out: |
304 | btrfs_free_path(path); | 314 | btrfs_free_path(path); |
305 | return ret; | 315 | return ret; |