#include "transaction.h"
#include "print-tree.h"
#include "repair.h"
+#include "internal.h"
static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_path *path, int level);
void btrfs_free_path(struct btrfs_path *p)
{
+ if (!p)
+ return;
btrfs_release_path(p);
kfree(p);
}
struct extent_buffer *buf)
{
/*
- * Tree blocks not in refernece counted trees and tree roots
+ * Tree blocks not in reference counted trees and tree roots
* are never shared. If a block was allocated after the last
* snapshot and the block was not allocated by tree relocation,
* we know the block is not shared.
return ret;
}
-/*
- * compare two keys in a memcmp fashion
- */
-static int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2)
+int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2)
{
- struct btrfs_key k1;
-
- btrfs_disk_key_to_cpu(&k1, disk);
-
- if (k1.objectid > k2->objectid)
+ if (k1->objectid > k2->objectid)
return 1;
- if (k1.objectid < k2->objectid)
+ if (k1->objectid < k2->objectid)
return -1;
- if (k1.type > k2->type)
+ if (k1->type > k2->type)
return 1;
- if (k1.type < k2->type)
+ if (k1->type < k2->type)
return -1;
- if (k1.offset > k2->offset)
+ if (k1->offset > k2->offset)
return 1;
- if (k1.offset < k2->offset)
+ if (k1->offset < k2->offset)
return -1;
return 0;
}
/*
+ * compare two keys in a memcmp fashion
+ */
+static int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2)
+{
+ struct btrfs_key k1;
+
+ btrfs_disk_key_to_cpu(&k1, disk);
+ return btrfs_comp_cpu_keys(&k1, k2);
+}
+
+/*
* The leaf data grows from end-to-front in the node.
* this returns the address of the start of the last item,
* which is the stop of the leaf data stack
return btrfs_item_offset_nr(leaf, nr - 1);
}
-int btrfs_check_node(struct btrfs_root *root,
- struct btrfs_disk_key *parent_key,
- struct extent_buffer *buf)
+enum btrfs_tree_block_status
+btrfs_check_node(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
+ struct extent_buffer *buf)
{
int i;
struct btrfs_key cpukey;
struct btrfs_disk_key key;
u32 nritems = btrfs_header_nritems(buf);
+ enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
if (nritems == 0 || nritems > BTRFS_NODEPTRS_PER_BLOCK(root))
goto fail;
+ ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
if (parent_key && parent_key->type) {
btrfs_node_key(buf, &key, 0);
if (memcmp(parent_key, &key, sizeof(key)))
goto fail;
}
+ ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
for (i = 0; nritems > 1 && i < nritems - 2; i++) {
btrfs_node_key(buf, &key, i);
btrfs_node_key_to_cpu(buf, &cpukey, i + 1);
if (btrfs_comp_keys(&key, &cpukey) >= 0)
goto fail;
}
- return 0;
+ return BTRFS_TREE_BLOCK_CLEAN;
fail:
if (btrfs_header_owner(buf) == BTRFS_EXTENT_TREE_OBJECTID) {
if (parent_key)
buf->start, buf->len,
btrfs_header_level(buf));
}
- return -EIO;
+ return ret;
}
-int btrfs_check_leaf(struct btrfs_root *root,
- struct btrfs_disk_key *parent_key,
- struct extent_buffer *buf)
+enum btrfs_tree_block_status
+btrfs_check_leaf(struct btrfs_root *root, struct btrfs_disk_key *parent_key,
+ struct extent_buffer *buf)
{
int i;
struct btrfs_key cpukey;
struct btrfs_disk_key key;
u32 nritems = btrfs_header_nritems(buf);
+ enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
if (nritems * sizeof(struct btrfs_item) > buf->len) {
fprintf(stderr, "invalid number of items %llu\n",
}
if (btrfs_header_level(buf) != 0) {
+ ret = BTRFS_TREE_BLOCK_INVALID_LEVEL;
fprintf(stderr, "leaf is not a leaf %llu\n",
(unsigned long long)btrfs_header_bytenr(buf));
goto fail;
}
if (btrfs_leaf_free_space(root, buf) < 0) {
+ ret = BTRFS_TREE_BLOCK_INVALID_FREE_SPACE;
fprintf(stderr, "leaf free space incorrect %llu %d\n",
(unsigned long long)btrfs_header_bytenr(buf),
btrfs_leaf_free_space(root, buf));
}
if (nritems == 0)
- return 0;
+ return BTRFS_TREE_BLOCK_CLEAN;
btrfs_item_key(buf, &key, 0);
if (parent_key && parent_key->type &&
memcmp(parent_key, &key, sizeof(key))) {
+ ret = BTRFS_TREE_BLOCK_INVALID_PARENT_KEY;
fprintf(stderr, "leaf parent key incorrect %llu\n",
(unsigned long long)btrfs_header_bytenr(buf));
goto fail;
}
- for (i = 0; nritems > 1 && i < nritems - 2; i++) {
+ for (i = 0; nritems > 1 && i < nritems - 1; i++) {
btrfs_item_key(buf, &key, i);
btrfs_item_key_to_cpu(buf, &cpukey, i + 1);
if (btrfs_comp_keys(&key, &cpukey) >= 0) {
+ ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
fprintf(stderr, "bad key ordering %d %d\n", i, i+1);
goto fail;
}
if (btrfs_item_offset_nr(buf, i) !=
btrfs_item_end_nr(buf, i + 1)) {
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
fprintf(stderr, "incorrect offsets %u %u\n",
btrfs_item_offset_nr(buf, i),
btrfs_item_end_nr(buf, i + 1));
}
if (i == 0 && btrfs_item_end_nr(buf, i) !=
BTRFS_LEAF_DATA_SIZE(root)) {
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
fprintf(stderr, "bad item end %u wanted %u\n",
btrfs_item_end_nr(buf, i),
(unsigned)BTRFS_LEAF_DATA_SIZE(root));
goto fail;
}
}
- return 0;
+
+ for (i = 0; i < nritems; i++) {
+ if (btrfs_item_end_nr(buf, i) > BTRFS_LEAF_DATA_SIZE(root)) {
+ btrfs_item_key(buf, &key, 0);
+ btrfs_print_key(&key);
+ fflush(stdout);
+ ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
+ fprintf(stderr, "slot end outside of leaf %llu > %llu\n",
+ (unsigned long long)btrfs_item_end_nr(buf, i),
+ (unsigned long long)BTRFS_LEAF_DATA_SIZE(root));
+ goto fail;
+ }
+ }
+
+ return BTRFS_TREE_BLOCK_CLEAN;
fail:
if (btrfs_header_owner(buf) == BTRFS_EXTENT_TREE_OBJECTID) {
if (parent_key)
btrfs_add_corrupt_extent_record(root->fs_info, &cpukey,
buf->start, buf->len, 0);
}
- return -EIO;
+ return ret;
}
static int noinline check_block(struct btrfs_root *root,
struct btrfs_disk_key key;
struct btrfs_disk_key *key_ptr = NULL;
struct extent_buffer *parent;
+ enum btrfs_tree_block_status ret;
+ if (path->skip_check_block)
+ return 0;
if (path->nodes[level + 1]) {
parent = path->nodes[level + 1];
btrfs_node_key(parent, &key, path->slots[level + 1]);
key_ptr = &key;
}
if (level == 0)
- return btrfs_check_leaf(root, key_ptr, path->nodes[0]);
- return btrfs_check_node(root, key_ptr, path->nodes[level]);
+ ret = btrfs_check_leaf(root, key_ptr, path->nodes[0]);
+ else
+ ret = btrfs_check_node(root, key_ptr, path->nodes[level]);
+ if (ret == BTRFS_TREE_BLOCK_CLEAN)
+ return 0;
+ return -EIO;
}
/*
return NULL;
return read_tree_block(root, btrfs_node_blockptr(parent, slot),
- btrfs_level_size(root, level - 1),
+ root->nodesize,
btrfs_node_ptr_generation(parent, slot));
}
/* promote the child to a root */
child = read_node_slot(root, mid, 0);
- BUG_ON(!child);
+ BUG_ON(!extent_buffer_uptodate(child));
ret = btrfs_cow_block(trans, root, child, mid, 0, &child);
BUG_ON(ret);
return 0;
left = read_node_slot(root, parent, pslot - 1);
- if (left) {
+ if (extent_buffer_uptodate(left)) {
wret = btrfs_cow_block(trans, root, left,
parent, pslot - 1, &left);
if (wret) {
}
}
right = read_node_slot(root, parent, pslot + 1);
- if (right) {
+ if (extent_buffer_uptodate(right)) {
wret = btrfs_cow_block(trans, root, right,
parent, pslot + 1, &right);
if (wret) {
left = read_node_slot(root, parent, pslot - 1);
/* first, try to make some room in the middle buffer */
- if (left) {
+ if (extent_buffer_uptodate(left)) {
u32 left_nr;
left_nr = btrfs_header_nritems(left);
if (left_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) {
/*
* then try to empty the right most buffer into the middle
*/
- if (right) {
+ if (extent_buffer_uptodate(right)) {
u32 right_nr;
right_nr = btrfs_header_nritems(right);
if (right_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) {
node = path->nodes[level];
search = btrfs_node_blockptr(node, slot);
- blocksize = btrfs_level_size(root, level - 1);
+ blocksize = root->nodesize;
eb = btrfs_find_tree_block(root, search, blocksize);
if (eb) {
free_extent_buffer(eb);
}
}
+int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
+ u64 iobjectid, u64 ioff, u8 key_type,
+ struct btrfs_key *found_key)
+{
+ int ret;
+ struct btrfs_key key;
+ struct extent_buffer *eb;
+ struct btrfs_path *path;
+
+ key.type = key_type;
+ key.objectid = iobjectid;
+ key.offset = ioff;
+
+ if (found_path == NULL) {
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+ } else
+ path = found_path;
+
+ ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
+ if ((ret < 0) || (found_key == NULL))
+ goto out;
+
+ eb = path->nodes[0];
+ if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
+ ret = btrfs_next_leaf(fs_root, path);
+ if (ret)
+ goto out;
+ eb = path->nodes[0];
+ }
+
+ btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
+ if (found_key->type != key.type ||
+ found_key->objectid != key.objectid) {
+ ret = 1;
+ goto out;
+ }
+
+out:
+ if (path != found_path)
+ btrfs_free_path(path);
+ return ret;
+}
+
/*
* look for key in the tree. path is filled in with nodes along the way
* if key is found, we return zero and you can find the item in the leaf
* This is used after shifting pointers to the left, so it stops
* fixing up pointers when a given leaf/node is not in slot 0 of the
* higher levels
- *
- * If this fails to write a tree block, it returns -1, but continues
- * fixing up the blocks in ram so the tree is consistent.
*/
-static int fixup_low_keys(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
+void btrfs_fixup_low_keys(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_disk_key *key, int level)
{
int i;
- int ret = 0;
struct extent_buffer *t;
for (i = level; i < BTRFS_MAX_LEVEL; i++) {
if (tslot != 0)
break;
}
- return ret;
}
/*
* This function isn't completely safe. It's the caller's responsibility
* that the new key won't break the order
*/
-int btrfs_set_item_key_safe(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, struct btrfs_path *path,
+int btrfs_set_item_key_safe(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_key *new_key)
{
struct btrfs_disk_key disk_key;
btrfs_set_item_key(eb, &disk_key, slot);
btrfs_mark_buffer_dirty(eb);
if (slot == 0)
- fixup_low_keys(trans, root, path, &disk_key, 1);
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
return 0;
}
/*
+ * update an item key without the safety checks. This is meant to be called by
+ * fsck only.
+ */
+void btrfs_set_item_key_unsafe(struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct btrfs_key *new_key)
+{
+ struct btrfs_disk_key disk_key;
+ struct extent_buffer *eb;
+ int slot;
+
+ eb = path->nodes[0];
+ slot = path->slots[0];
+
+ btrfs_cpu_key_to_disk(&disk_key, new_key);
+ btrfs_set_item_key(eb, &disk_key, slot);
+ btrfs_mark_buffer_dirty(eb);
+ if (slot == 0)
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
+}
+
+/*
* try to push data from one node into the next node left in the
* tree.
*
*/
int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf)
{
+ u32 nodesize = (root ? BTRFS_LEAF_DATA_SIZE(root) : leaf->len);
int nritems = btrfs_header_nritems(leaf);
int ret;
- ret = BTRFS_LEAF_DATA_SIZE(root) - leaf_space_used(leaf, 0, nritems);
+ ret = nodesize - leaf_space_used(leaf, 0, nritems);
if (ret < 0) {
- printk("leaf free space ret %d, leaf data size %lu, used %d nritems %d\n",
- ret, (unsigned long) BTRFS_LEAF_DATA_SIZE(root),
- leaf_space_used(leaf, 0, nritems), nritems);
+ printk("leaf free space ret %d, leaf data size %u, used %d nritems %d\n",
+ ret, nodesize, leaf_space_used(leaf, 0, nritems),
+ nritems);
}
return ret;
}
return 1;
right = read_node_slot(root, upper, slot + 1);
+ if (!extent_buffer_uptodate(right)) {
+ if (IS_ERR(right))
+ return PTR_ERR(right);
+ return -EIO;
+ }
free_space = btrfs_leaf_free_space(root, right);
if (free_space < data_size) {
free_extent_buffer(right);
u32 right_nritems;
u32 nr;
int ret = 0;
- int wret;
u32 this_item_size;
u32 old_left_item_size;
btrfs_mark_buffer_dirty(right);
btrfs_item_key(right, &disk_key, 0);
- wret = fixup_low_keys(trans, root, path, &disk_key, 1);
- if (wret)
- ret = wret;
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
/* then fixup the leaf pointer in the path */
if (path->slots[0] < push_items) {
int split;
int num_doubles = 0;
+ l = path->nodes[0];
+ slot = path->slots[0];
+ if (extend && data_size + btrfs_item_size_nr(l, slot) +
+ sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(root))
+ return -EOVERFLOW;
+
/* first try to make some room by pushing left and right */
if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) {
wret = push_leaf_right(trans, root, path, data_size, 0);
else
btrfs_item_key(l, &disk_key, mid);
- right = btrfs_alloc_free_block(trans, root, root->leafsize,
+ right = btrfs_alloc_free_block(trans, root, root->nodesize,
root->root_key.objectid,
&disk_key, 0, l->start, 0);
if (IS_ERR(right)) {
path->nodes[0] = right;
path->slots[0] = 0;
if (path->slots[1] == 0) {
- wret = fixup_low_keys(trans, root,
- path, &disk_key, 1);
- if (wret)
- ret = wret;
+ btrfs_fixup_low_keys(root, path,
+ &disk_key, 1);
}
}
btrfs_mark_buffer_dirty(right);
buf = kmalloc(item_size, GFP_NOFS);
+ BUG_ON(!buf);
read_extent_buffer(leaf, buf, btrfs_item_ptr_offset(leaf,
path->slots[0]), item_size);
slot = path->slots[0] + 1;
btrfs_set_disk_key_offset(&disk_key, offset + size_diff);
btrfs_set_item_key(leaf, &disk_key, slot);
if (slot == 0)
- fixup_low_keys(trans, root, path, &disk_key, 1);
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
}
item = btrfs_item_nr(slot);
ret = 0;
if (slot == 0) {
btrfs_cpu_key_to_disk(&disk_key, cpu_key);
- ret = fixup_low_keys(trans, root, path, &disk_key, 1);
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
}
if (btrfs_leaf_free_space(root, leaf) < 0) {
unsigned long ptr;
path = btrfs_alloc_path();
- BUG_ON(!path);
+ if (!path)
+ return -ENOMEM;
+
ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
if (!ret) {
leaf = path->nodes[0];
struct extent_buffer *parent = path->nodes[level];
u32 nritems;
int ret = 0;
- int wret;
nritems = btrfs_header_nritems(parent);
if (slot != nritems -1) {
struct btrfs_disk_key disk_key;
btrfs_node_key(parent, &disk_key, 0);
- wret = fixup_low_keys(trans, root, path, &disk_key, level + 1);
- if (wret)
- ret = wret;
+ btrfs_fixup_low_keys(root, path, &disk_key, level + 1);
}
btrfs_mark_buffer_dirty(parent);
return ret;
struct btrfs_disk_key disk_key;
btrfs_item_key(leaf, &disk_key, 0);
- wret = fixup_low_keys(trans, root, path,
- &disk_key, 1);
- if (wret)
- ret = wret;
+ btrfs_fixup_low_keys(root, path, &disk_key, 1);
}
/* delete the leaf if it is mostly empty */
slot--;
next = read_node_slot(root, c, slot);
+ if (!extent_buffer_uptodate(next)) {
+ if (IS_ERR(next))
+ return PTR_ERR(next);
+ return -EIO;
+ }
break;
}
path->slots[level] = slot;
if (!level)
break;
next = read_node_slot(root, next, slot);
+ if (!extent_buffer_uptodate(next)) {
+ if (IS_ERR(next))
+ return PTR_ERR(next);
+ return -EIO;
+ }
}
return 0;
}
reada_for_search(root, path, level, slot, 0);
next = read_node_slot(root, c, slot);
- if (!next)
+ if (!extent_buffer_uptodate(next))
return -EIO;
break;
}
if (path->reada)
reada_for_search(root, path, level, 0, 0);
next = read_node_slot(root, next, 0);
- if (!next)
+ if (!extent_buffer_uptodate(next))
return -EIO;
}
return 0;
{
struct btrfs_key found_key;
struct extent_buffer *leaf;
+ u32 nritems;
int ret;
while(1) {
path->slots[0]--;
}
leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (nritems == 0)
+ return 1;
+ if (path->slots[0] == nritems)
+ path->slots[0]--;
+
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid < min_objectid)
+ break;
if (found_key.type == type)
return 0;
+ if (found_key.objectid == min_objectid &&
+ found_key.type < type)
+ break;
}
return 1;
}
+/*
+ * search in extent tree to find a previous Metadata/Data extent item with
+ * min objecitd.
+ *
+ * returns 0 if something is found, 1 if nothing was found and < 0 on error
+ */
+int btrfs_previous_extent_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 min_objectid)
+{
+ struct btrfs_key found_key;
+ struct extent_buffer *leaf;
+ u32 nritems;
+ int ret;
+
+ while (1) {
+ if (path->slots[0] == 0) {
+ ret = btrfs_prev_leaf(root, path);
+ if (ret != 0)
+ return ret;
+ } else {
+ path->slots[0]--;
+ }
+ leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (nritems == 0)
+ return 1;
+ if (path->slots[0] == nritems)
+ path->slots[0]--;
+
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid < min_objectid)
+ break;
+ if (found_key.type == BTRFS_EXTENT_ITEM_KEY ||
+ found_key.type == BTRFS_METADATA_ITEM_KEY)
+ return 0;
+ if (found_key.objectid == min_objectid &&
+ found_key.type < BTRFS_EXTENT_ITEM_KEY)
+ break;
+ }
+ return 1;
+}
+
+/*
+ * Search in extent tree to found next meta/data extent
+ * Caller needs to check for no-hole or skinny metadata features.
+ */
+int btrfs_next_extent_item(struct btrfs_root *root,
+ struct btrfs_path *path, u64 max_objectid)
+{
+ struct btrfs_key found_key;
+ int ret;
+
+ while (1) {
+ ret = btrfs_next_item(root, path);
+ if (ret)
+ return ret;
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0]);
+ if (found_key.objectid > max_objectid)
+ return 1;
+ if (found_key.type == BTRFS_EXTENT_ITEM_KEY ||
+ found_key.type == BTRFS_METADATA_ITEM_KEY)
+ return 0;
+ }
+}