diff options
Diffstat (limited to 'fs/btrfs/root-tree.c')
-rw-r--r-- | fs/btrfs/root-tree.c | 201 |
1 files changed, 80 insertions, 121 deletions
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 5bf1ed57f178..ffb1036ef10d 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c | |||
@@ -64,52 +64,59 @@ void btrfs_read_root_item(struct extent_buffer *eb, int slot, | |||
64 | } | 64 | } |
65 | 65 | ||
66 | /* | 66 | /* |
67 | * lookup the root with the highest offset for a given objectid. The key we do | 67 | * btrfs_find_root - lookup the root by the key. |
68 | * find is copied into 'key'. If we find something return 0, otherwise 1, < 0 | 68 | * root: the root of the root tree |
69 | * on error. | 69 | * search_key: the key to search |
70 | * path: the path we search | ||
71 | * root_item: the root item of the tree we look for | ||
72 | * root_key: the reak key of the tree we look for | ||
73 | * | ||
74 | * If ->offset of 'seach_key' is -1ULL, it means we are not sure the offset | ||
75 | * of the search key, just lookup the root with the highest offset for a | ||
76 | * given objectid. | ||
77 | * | ||
78 | * If we find something return 0, otherwise > 0, < 0 on error. | ||
70 | */ | 79 | */ |
71 | int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, | 80 | int btrfs_find_root(struct btrfs_root *root, struct btrfs_key *search_key, |
72 | struct btrfs_root_item *item, struct btrfs_key *key) | 81 | struct btrfs_path *path, struct btrfs_root_item *root_item, |
82 | struct btrfs_key *root_key) | ||
73 | { | 83 | { |
74 | struct btrfs_path *path; | ||
75 | struct btrfs_key search_key; | ||
76 | struct btrfs_key found_key; | 84 | struct btrfs_key found_key; |
77 | struct extent_buffer *l; | 85 | struct extent_buffer *l; |
78 | int ret; | 86 | int ret; |
79 | int slot; | 87 | int slot; |
80 | 88 | ||
81 | search_key.objectid = objectid; | 89 | ret = btrfs_search_slot(NULL, root, search_key, path, 0, 0); |
82 | search_key.type = BTRFS_ROOT_ITEM_KEY; | ||
83 | search_key.offset = (u64)-1; | ||
84 | |||
85 | path = btrfs_alloc_path(); | ||
86 | if (!path) | ||
87 | return -ENOMEM; | ||
88 | ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0); | ||
89 | if (ret < 0) | 90 | if (ret < 0) |
90 | goto out; | 91 | return ret; |
91 | 92 | ||
92 | BUG_ON(ret == 0); | 93 | if (search_key->offset != -1ULL) { /* the search key is exact */ |
93 | if (path->slots[0] == 0) { | 94 | if (ret > 0) |
94 | ret = 1; | 95 | goto out; |
95 | goto out; | 96 | } else { |
97 | BUG_ON(ret == 0); /* Logical error */ | ||
98 | if (path->slots[0] == 0) | ||
99 | goto out; | ||
100 | path->slots[0]--; | ||
101 | ret = 0; | ||
96 | } | 102 | } |
103 | |||
97 | l = path->nodes[0]; | 104 | l = path->nodes[0]; |
98 | slot = path->slots[0] - 1; | 105 | slot = path->slots[0]; |
106 | |||
99 | btrfs_item_key_to_cpu(l, &found_key, slot); | 107 | btrfs_item_key_to_cpu(l, &found_key, slot); |
100 | if (found_key.objectid != objectid || | 108 | if (found_key.objectid != search_key->objectid || |
101 | found_key.type != BTRFS_ROOT_ITEM_KEY) { | 109 | found_key.type != BTRFS_ROOT_ITEM_KEY) { |
102 | ret = 1; | 110 | ret = 1; |
103 | goto out; | 111 | goto out; |
104 | } | 112 | } |
105 | if (item) | ||
106 | btrfs_read_root_item(l, slot, item); | ||
107 | if (key) | ||
108 | memcpy(key, &found_key, sizeof(found_key)); | ||
109 | 113 | ||
110 | ret = 0; | 114 | if (root_item) |
115 | btrfs_read_root_item(l, slot, root_item); | ||
116 | if (root_key) | ||
117 | memcpy(root_key, &found_key, sizeof(found_key)); | ||
111 | out: | 118 | out: |
112 | btrfs_free_path(path); | 119 | btrfs_release_path(path); |
113 | return ret; | 120 | return ret; |
114 | } | 121 | } |
115 | 122 | ||
@@ -212,86 +219,6 @@ int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
212 | return btrfs_insert_item(trans, root, key, item, sizeof(*item)); | 219 | return btrfs_insert_item(trans, root, key, item, sizeof(*item)); |
213 | } | 220 | } |
214 | 221 | ||
215 | /* | ||
216 | * at mount time we want to find all the old transaction snapshots that were in | ||
217 | * the process of being deleted if we crashed. This is any root item with an | ||
218 | * offset lower than the latest root. They need to be queued for deletion to | ||
219 | * finish what was happening when we crashed. | ||
220 | */ | ||
221 | int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid) | ||
222 | { | ||
223 | struct btrfs_root *dead_root; | ||
224 | struct btrfs_root_item *ri; | ||
225 | struct btrfs_key key; | ||
226 | struct btrfs_key found_key; | ||
227 | struct btrfs_path *path; | ||
228 | int ret; | ||
229 | u32 nritems; | ||
230 | struct extent_buffer *leaf; | ||
231 | int slot; | ||
232 | |||
233 | key.objectid = objectid; | ||
234 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); | ||
235 | key.offset = 0; | ||
236 | path = btrfs_alloc_path(); | ||
237 | if (!path) | ||
238 | return -ENOMEM; | ||
239 | |||
240 | again: | ||
241 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | ||
242 | if (ret < 0) | ||
243 | goto err; | ||
244 | while (1) { | ||
245 | leaf = path->nodes[0]; | ||
246 | nritems = btrfs_header_nritems(leaf); | ||
247 | slot = path->slots[0]; | ||
248 | if (slot >= nritems) { | ||
249 | ret = btrfs_next_leaf(root, path); | ||
250 | if (ret) | ||
251 | break; | ||
252 | leaf = path->nodes[0]; | ||
253 | nritems = btrfs_header_nritems(leaf); | ||
254 | slot = path->slots[0]; | ||
255 | } | ||
256 | btrfs_item_key_to_cpu(leaf, &key, slot); | ||
257 | if (btrfs_key_type(&key) != BTRFS_ROOT_ITEM_KEY) | ||
258 | goto next; | ||
259 | |||
260 | if (key.objectid < objectid) | ||
261 | goto next; | ||
262 | |||
263 | if (key.objectid > objectid) | ||
264 | break; | ||
265 | |||
266 | ri = btrfs_item_ptr(leaf, slot, struct btrfs_root_item); | ||
267 | if (btrfs_disk_root_refs(leaf, ri) != 0) | ||
268 | goto next; | ||
269 | |||
270 | memcpy(&found_key, &key, sizeof(key)); | ||
271 | key.offset++; | ||
272 | btrfs_release_path(path); | ||
273 | dead_root = | ||
274 | btrfs_read_fs_root_no_radix(root->fs_info->tree_root, | ||
275 | &found_key); | ||
276 | if (IS_ERR(dead_root)) { | ||
277 | ret = PTR_ERR(dead_root); | ||
278 | goto err; | ||
279 | } | ||
280 | |||
281 | ret = btrfs_add_dead_root(dead_root); | ||
282 | if (ret) | ||
283 | goto err; | ||
284 | goto again; | ||
285 | next: | ||
286 | slot++; | ||
287 | path->slots[0]++; | ||
288 | } | ||
289 | ret = 0; | ||
290 | err: | ||
291 | btrfs_free_path(path); | ||
292 | return ret; | ||
293 | } | ||
294 | |||
295 | int btrfs_find_orphan_roots(struct btrfs_root *tree_root) | 222 | int btrfs_find_orphan_roots(struct btrfs_root *tree_root) |
296 | { | 223 | { |
297 | struct extent_buffer *leaf; | 224 | struct extent_buffer *leaf; |
@@ -301,6 +228,10 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) | |||
301 | struct btrfs_root *root; | 228 | struct btrfs_root *root; |
302 | int err = 0; | 229 | int err = 0; |
303 | int ret; | 230 | int ret; |
231 | bool can_recover = true; | ||
232 | |||
233 | if (tree_root->fs_info->sb->s_flags & MS_RDONLY) | ||
234 | can_recover = false; | ||
304 | 235 | ||
305 | path = btrfs_alloc_path(); | 236 | path = btrfs_alloc_path(); |
306 | if (!path) | 237 | if (!path) |
@@ -340,20 +271,52 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) | |||
340 | root_key.objectid = key.offset; | 271 | root_key.objectid = key.offset; |
341 | key.offset++; | 272 | key.offset++; |
342 | 273 | ||
343 | root = btrfs_read_fs_root_no_name(tree_root->fs_info, | 274 | root = btrfs_read_fs_root(tree_root, &root_key); |
344 | &root_key); | 275 | err = PTR_RET(root); |
345 | if (!IS_ERR(root)) | 276 | if (err && err != -ENOENT) { |
277 | break; | ||
278 | } else if (err == -ENOENT) { | ||
279 | struct btrfs_trans_handle *trans; | ||
280 | |||
281 | btrfs_release_path(path); | ||
282 | |||
283 | trans = btrfs_join_transaction(tree_root); | ||
284 | if (IS_ERR(trans)) { | ||
285 | err = PTR_ERR(trans); | ||
286 | btrfs_error(tree_root->fs_info, err, | ||
287 | "Failed to start trans to delete " | ||
288 | "orphan item"); | ||
289 | break; | ||
290 | } | ||
291 | err = btrfs_del_orphan_item(trans, tree_root, | ||
292 | root_key.objectid); | ||
293 | btrfs_end_transaction(trans, tree_root); | ||
294 | if (err) { | ||
295 | btrfs_error(tree_root->fs_info, err, | ||
296 | "Failed to delete root orphan " | ||
297 | "item"); | ||
298 | break; | ||
299 | } | ||
346 | continue; | 300 | continue; |
301 | } | ||
347 | 302 | ||
348 | ret = PTR_ERR(root); | 303 | if (btrfs_root_refs(&root->root_item) == 0) { |
349 | if (ret != -ENOENT) { | 304 | btrfs_add_dead_root(root); |
350 | err = ret; | 305 | continue; |
306 | } | ||
307 | |||
308 | err = btrfs_init_fs_root(root); | ||
309 | if (err) { | ||
310 | btrfs_free_fs_root(root); | ||
351 | break; | 311 | break; |
352 | } | 312 | } |
353 | 313 | ||
354 | ret = btrfs_find_dead_roots(tree_root, root_key.objectid); | 314 | root->orphan_item_inserted = 1; |
355 | if (ret) { | 315 | |
356 | err = ret; | 316 | err = btrfs_insert_fs_root(root->fs_info, root); |
317 | if (err) { | ||
318 | BUG_ON(err == -EEXIST); | ||
319 | btrfs_free_fs_root(root); | ||
357 | break; | 320 | break; |
358 | } | 321 | } |
359 | } | 322 | } |
@@ -368,8 +331,6 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
368 | { | 331 | { |
369 | struct btrfs_path *path; | 332 | struct btrfs_path *path; |
370 | int ret; | 333 | int ret; |
371 | struct btrfs_root_item *ri; | ||
372 | struct extent_buffer *leaf; | ||
373 | 334 | ||
374 | path = btrfs_alloc_path(); | 335 | path = btrfs_alloc_path(); |
375 | if (!path) | 336 | if (!path) |
@@ -379,8 +340,6 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
379 | goto out; | 340 | goto out; |
380 | 341 | ||
381 | BUG_ON(ret != 0); | 342 | BUG_ON(ret != 0); |
382 | leaf = path->nodes[0]; | ||
383 | ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item); | ||
384 | 343 | ||
385 | ret = btrfs_del_item(trans, root, path); | 344 | ret = btrfs_del_item(trans, root, path); |
386 | out: | 345 | out: |