From 07a4f1b81df6cd9b9e09773f680fc098faed310c Mon Sep 17 00:00:00 2001 From: Yan Date: Wed, 5 Dec 2007 10:41:38 -0500 Subject: Update btrfs-progs to better match the kernel --- ctree.c | 478 ++++++++++++++++++++++++++++++++++++++++++++++++---------- ctree.h | 64 ++++++-- dir-item.c | 114 +++++++++++--- disk-io.c | 14 +- extent-tree.c | 26 +++- file-item.c | 263 ++++++++++++++++++++++++++++++++ kerncompat.h | 6 +- transaction.h | 5 +- 8 files changed, 836 insertions(+), 134 deletions(-) diff --git a/ctree.c b/ctree.c index 1da711ab..88261838 100644 --- a/ctree.c +++ b/ctree.c @@ -19,15 +19,15 @@ #include #include #include "kerncompat.h" -#include "radix-tree.h" #include "ctree.h" #include "disk-io.h" +#include "transaction.h" #include "print-tree.h" - static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int level); static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size); + *root, struct btrfs_key *ins_key, + struct btrfs_path *path, int data_size, int extend); static int push_node_left(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_buffer *dst, struct btrfs_buffer *src); @@ -52,8 +52,7 @@ void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p) } memset(p, 0, sizeof(*p)); } - -static int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root +int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_buffer *buf, struct btrfs_buffer *parent, int parent_slot, struct btrfs_buffer **cow_ret) @@ -67,6 +66,7 @@ static int btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root cow = btrfs_alloc_free_block(trans, root, buf->size); memcpy(&cow->node, &buf->node, buf->size); btrfs_set_header_bytenr(&cow->node.header, cow->bytenr); + btrfs_set_header_generation(&cow->node.header, trans->transid); btrfs_set_header_owner(&cow->node.header, root->root_key.objectid); *cow_ret = cow; btrfs_inc_ref(trans, root, buf); @@ -476,6 +476,128 @@ static int balance_level(struct btrfs_trans_handle *trans, struct btrfs_root btrfs_block_release(root, left_buf); return ret; } +static int push_nodes_for_insert(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, int level) +{ + struct btrfs_node *right; + struct btrfs_node *mid; + struct btrfs_node *left; + struct btrfs_node *parent; + struct btrfs_buffer *right_buf; + struct btrfs_buffer *mid_buf; + struct btrfs_buffer *left_buf; + struct btrfs_buffer *parent_buf = NULL; + int ret = 0; + int wret; + int pslot; + int orig_slot = path->slots[level]; + u64 orig_ptr; + + if (level == 0) + return 1; + + mid_buf = path->nodes[level]; + mid = &mid_buf->node; + orig_ptr = btrfs_node_blockptr(mid, orig_slot); + + if (level < BTRFS_MAX_LEVEL - 1) + parent_buf = path->nodes[level + 1]; + pslot = path->slots[level + 1]; + + if (!parent_buf) + return 1; + parent = &parent_buf->node; + + left_buf = read_node_slot(root, parent_buf, pslot - 1); + left = &left_buf->node; + + /* first, try to make some room in the middle buffer */ + if (left_buf) { + u32 left_nr; + left_nr = btrfs_header_nritems(&left->header); + if (left_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { + wret = 1; + } else { + ret = btrfs_cow_block(trans, root, left_buf, + parent_buf, pslot - 1, + &left_buf); + left = &left_buf->node; + if (ret) + wret = 1; + else { + wret = push_node_left(trans, root, + left_buf, mid_buf); + } + } + if (wret < 0) + ret = wret; + if (wret == 0) { + orig_slot += left_nr; + memcpy(&parent->ptrs[pslot].key, &mid->ptrs[0].key, + sizeof(struct btrfs_disk_key)); + BUG_ON(list_empty(&parent_buf->dirty)); + if (btrfs_header_nritems(&left->header) > orig_slot) { + path->nodes[level] = left_buf; + path->slots[level + 1] -= 1; + path->slots[level] = orig_slot; + btrfs_block_release(root, mid_buf); + } else { + orig_slot -= + btrfs_header_nritems(&left->header); + path->slots[level] = orig_slot; + btrfs_block_release(root, left_buf); + } + return 0; + } + btrfs_block_release(root, left_buf); + } + + right_buf = read_node_slot(root, parent_buf, pslot + 1); + right = &right_buf->node; + + /* + * then try to empty the right most buffer into the middle + */ + if (right_buf) { + u32 right_nr; + right_nr = btrfs_header_nritems(&right->header); + if (right_nr >= BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { + wret = 1; + } else { + ret = btrfs_cow_block(trans, root, right_buf, + parent_buf, pslot + 1, + &right_buf); + right = &right_buf->node; + if (ret) + wret = 1; + else { + wret = balance_node_right(trans, root, + right_buf, mid_buf); + } + } + if (wret < 0) + ret = wret; + if (wret == 0) { + memcpy(&parent->ptrs[pslot + 1].key, + &right->ptrs[0].key, + sizeof(struct btrfs_disk_key)); + BUG_ON(list_empty(&parent_buf->dirty)); + if (btrfs_header_nritems(&mid->header) <= orig_slot) { + path->nodes[level] = right_buf; + path->slots[level + 1] += 1; + path->slots[level] = orig_slot - + btrfs_header_nritems(&mid->header); + btrfs_block_release(root, mid_buf); + } else { + btrfs_block_release(root, right_buf); + } + return 0; + } + btrfs_block_release(root, right_buf); + } + return 1; +} /* * look for key in the tree. path is filled in with nodes along the way @@ -495,7 +617,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ins_len, int cow) { struct btrfs_buffer *b; - struct btrfs_buffer *cow_buf; struct btrfs_node *c; int slot; int ret; @@ -508,10 +629,14 @@ again: level = btrfs_header_level(&b->node.header); if (cow) { int wret; - wret = btrfs_cow_block(trans, root, b, p->nodes[level + - 1], p->slots[level + 1], - &cow_buf); - b = cow_buf; + wret = btrfs_cow_block(trans, root, b, + p->nodes[level + 1], + p->slots[level + 1], + &b); + if (wret) { + btrfs_block_release(root, b); + return wret; + } } BUG_ON(!cow && ins_len); c = &b->node; @@ -524,8 +649,8 @@ again: if (ret && slot > 0) slot -= 1; p->slots[level] = slot; - if (ins_len > 0 && btrfs_header_nritems(&c->header) == - BTRFS_NODEPTRS_PER_BLOCK(root)) { + if (ins_len > 0 && btrfs_header_nritems(&c->header) >= + BTRFS_NODEPTRS_PER_BLOCK(root) - 1) { int sret = split_node(trans, root, p, level); BUG_ON(sret > 0); if (sret) @@ -539,8 +664,10 @@ again: if (sret) return sret; b = p->nodes[level]; - if (!b) + if (!b) { + btrfs_release_path(NULL, p); goto again; + } c = &b->node; slot = p->slots[level]; BUG_ON(btrfs_header_nritems(&c->header) == 1); @@ -553,7 +680,8 @@ again: p->slots[level] = slot; if (ins_len > 0 && btrfs_leaf_free_space(root, l) < sizeof(struct btrfs_item) + ins_len) { - int sret = split_leaf(trans, root, p, ins_len); + int sret = split_leaf(trans, root, key, + p, ins_len, ret == 0); BUG_ON(sret > 0); if (sret) return sret; @@ -665,10 +793,9 @@ static int balance_node_right(struct btrfs_trans_handle *trans, struct if (push_items <= 0) { return 1; } - max_push = src_nritems / 2 + 1; /* don't try to empty the node */ - if (max_push > src_nritems) + if (max_push >= src_nritems) return 1; if (max_push < push_items) push_items = max_push; @@ -703,13 +830,13 @@ static int insert_new_root(struct btrfs_trans_handle *trans, struct btrfs_root BUG_ON(path->nodes[level]); BUG_ON(path->nodes[level-1] != root->node); - t = btrfs_alloc_free_block(trans, root, root->nodesize); c = &t->node; - memset(c, 0, root->nodesize); + memset(&c->header, 0, sizeof(c->header)); btrfs_set_header_nritems(&c->header, 1); btrfs_set_header_level(&c->header, level); btrfs_set_header_bytenr(&c->header, t->bytenr); + btrfs_set_header_generation(&c->header, trans->transid); btrfs_set_header_owner(&c->header, root->root_key.objectid); memcpy(c->header.fsid, root->fs_info->disk_super->fsid, sizeof(c->header.fsid)); @@ -719,9 +846,9 @@ static int insert_new_root(struct btrfs_trans_handle *trans, struct btrfs_root lower_key = &((struct btrfs_leaf *)lower)->items[0].key; else lower_key = &lower->ptrs[0].key; - memcpy(&c->ptrs[0].key, lower_key, sizeof(struct btrfs_disk_key)); btrfs_set_node_blockptr(c, 0, path->nodes[level - 1]->bytenr); + BUG_ON(list_empty(&t->dirty)); /* the super has an extra ref to root->node */ btrfs_block_release(root, root->node); root->node = t; @@ -793,6 +920,15 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root ret = insert_new_root(trans, root, path, level + 1); if (ret) return ret; + } else { + ret = push_nodes_for_insert(trans, root, path, level); + t = path->nodes[level]; + c = &t->node; + if (!ret && btrfs_header_nritems(&c->header) < + BTRFS_NODEPTRS_PER_BLOCK(root) - 1) + return 0; + if (ret < 0) + return ret; } c_nritems = btrfs_header_nritems(&c->header); split_buffer = btrfs_alloc_free_block(trans, root, root->nodesize); @@ -800,6 +936,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root btrfs_set_header_flags(&split->header, btrfs_header_flags(&c->header)); btrfs_set_header_level(&split->header, btrfs_header_level(&c->header)); btrfs_set_header_bytenr(&split->header, split_buffer->bytenr); + btrfs_set_header_generation(&split->header, trans->transid); btrfs_set_header_owner(&split->header, root->root_key.objectid); memcpy(split->header.fsid, root->fs_info->disk_super->fsid, sizeof(split->header.fsid)); @@ -836,7 +973,8 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root * room, 0 if everything worked out and < 0 if there were major errors. */ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size) + *root, struct btrfs_path *path, int data_size, + int empty) { struct btrfs_buffer *left_buf = path->nodes[0]; struct btrfs_leaf *left = &left_buf->leaf; @@ -844,14 +982,14 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root struct btrfs_buffer *right_buf; struct btrfs_buffer *upper; int slot; - int i; + u32 i; int free_space; int push_space = 0; int push_items = 0; struct btrfs_item *item; u32 left_nritems; + u32 nr; u32 right_nritems; - slot = path->slots[1]; if (!path->nodes[1]) { return 1; @@ -877,9 +1015,19 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root btrfs_block_release(root, right_buf); return 1; } - left_nritems = btrfs_header_nritems(&left->header); - for (i = left_nritems - 1; i >= 0; i--) { + if (left_nritems == 0) { + btrfs_block_release(root, right_buf); + return 1; + } + + if (empty) + nr = 0; + else + nr = 1; + + i = left_nritems - 1; + while (i >= nr) { item = left->items + i; if (path->slots[0] == i) push_space += data_size + sizeof(*item); @@ -888,6 +1036,9 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root break; push_items++; push_space += btrfs_item_size(item) + sizeof(*item); + if (i == 0) + break; + i--; } if (push_items == 0) { btrfs_block_release(root, right_buf); @@ -944,7 +1095,8 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root * least data_size bytes. returns zero if the push worked, nonzero otherwise */ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size) + *root, struct btrfs_path *path, int data_size, + int empty) { struct btrfs_buffer *right_buf = path->nodes[0]; struct btrfs_leaf *right = &right_buf->leaf; @@ -957,9 +1109,10 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root int push_items = 0; struct btrfs_item *item; u32 old_left_nritems; + u32 right_nritems; + u32 nr; int ret = 0; int wret; - slot = path->slots[1]; if (slot == 0) { return 1; @@ -967,6 +1120,11 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root if (!path->nodes[1]) { return 1; } + right_nritems = btrfs_header_nritems(&right->header); + if (right_nritems == 0) { + return 1; + } + t = read_tree_block(root, btrfs_node_blockptr(&path->nodes[1]->node, slot - 1), root->leafsize); @@ -985,8 +1143,12 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root btrfs_block_release(root, t); return 1; } + if (empty) + nr = right_nritems; + else + nr = right_nritems - 1; - for (i = 0; i < btrfs_header_nritems(&right->header); i++) { + for (i = 0; i < nr; i++) { item = right->items + i; if (path->slots[0] == i) push_space += data_size + sizeof(*item); @@ -1020,22 +1182,21 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root old_left_nritems - 1))); } btrfs_set_header_nritems(&left->header, old_left_nritems + push_items); - /* fixup right node */ - push_space = btrfs_item_offset(right->items + push_items - 1) - - leaf_data_end(root, right); - memmove(btrfs_leaf_data(right) + BTRFS_LEAF_DATA_SIZE(root) - - push_space, btrfs_leaf_data(right) + - leaf_data_end(root, right), push_space); - memmove(right->items, right->items + push_items, - (btrfs_header_nritems(&right->header) - push_items) * - sizeof(struct btrfs_item)); - btrfs_set_header_nritems(&right->header, - btrfs_header_nritems(&right->header) - - push_items); + if (push_items < right_nritems) { + push_space = btrfs_item_offset(right->items + push_items - 1) - + leaf_data_end(root, right); + memmove(btrfs_leaf_data(right) + BTRFS_LEAF_DATA_SIZE(root) - + push_space, btrfs_leaf_data(right) + + leaf_data_end(root, right), push_space); + memmove(right->items, right->items + push_items, + (right_nritems - push_items) * + sizeof(struct btrfs_item)); + } + right_nritems -= push_items; + btrfs_set_header_nritems(&right->header, right_nritems); push_space = BTRFS_LEAF_DATA_SIZE(root); - - for (i = 0; i < btrfs_header_nritems(&right->header); i++) { + for (i = 0; i < right_nritems; i++) { btrfs_set_item_offset(right->items + i, push_space - btrfs_item_size(right->items + i)); push_space = btrfs_item_offset(right->items + i); @@ -1069,7 +1230,8 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root * returns 0 if all went well and < 0 on failure. */ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size) + *root, struct btrfs_key *ins_key, + struct btrfs_path *path, int data_size, int extend) { struct btrfs_buffer *l_buf; struct btrfs_leaf *l; @@ -1082,67 +1244,125 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root int data_copy_size; int rt_data_off; int i; - int ret; + int ret = 0; int wret; + int double_split; + int num_doubles = 0; + struct btrfs_disk_key disk_key; + if (extend) + space_needed = data_size; /* first try to make some room by pushing left and right */ - wret = push_leaf_left(trans, root, path, data_size); - if (wret < 0) - return wret; - if (wret) { - wret = push_leaf_right(trans, root, path, data_size); - if (wret < 0) + if (ins_key->type != BTRFS_DIR_ITEM_KEY) { + wret = push_leaf_right(trans, root, path, data_size, 0); + if (wret < 0) { return wret; - } - l_buf = path->nodes[0]; - l = &l_buf->leaf; - - /* did the pushes work? */ - if (btrfs_leaf_free_space(root, l) >= - sizeof(struct btrfs_item) + data_size) - return 0; + } + if (wret) { + wret = push_leaf_left(trans, root, path, data_size, 0); + if (wret < 0) + return wret; + } + l_buf = path->nodes[0]; + l = &l_buf->leaf; + /* did the pushes work? */ + if (btrfs_leaf_free_space(root, l) >= space_needed) + return 0; + } if (!path->nodes[1]) { ret = insert_new_root(trans, root, path, 1); if (ret) return ret; } +again: + double_split = 0; + l_buf = path->nodes[0]; + l = &l_buf->leaf; slot = path->slots[0]; nritems = btrfs_header_nritems(&l->header); mid = (nritems + 1)/ 2; + right_buffer = btrfs_alloc_free_block(trans, root, root->leafsize); - BUG_ON(!right_buffer); - BUG_ON(mid == nritems); right = &right_buffer->leaf; memset(&right->header, 0, sizeof(right->header)); - if (mid <= slot) { - /* FIXME, just alloc a new leaf here */ - if (leaf_space_used(l, mid, nritems - mid) + space_needed > - BTRFS_LEAF_DATA_SIZE(root)) - BUG(); - } else { - /* FIXME, just alloc a new leaf here */ - if (leaf_space_used(l, 0, mid + 1) + space_needed > - BTRFS_LEAF_DATA_SIZE(root)) - BUG(); - } - btrfs_set_header_nritems(&right->header, nritems - mid); btrfs_set_header_bytenr(&right->header, right_buffer->bytenr); + btrfs_set_header_generation(&right->header, trans->transid); btrfs_set_header_level(&right->header, 0); btrfs_set_header_owner(&right->header, root->root_key.objectid); memcpy(right->header.fsid, root->fs_info->disk_super->fsid, sizeof(right->header.fsid)); - data_copy_size = btrfs_item_end(l->items + mid) - + if (mid <= slot) { + if (nritems == 1 || + leaf_space_used(l, mid, nritems - mid) + space_needed > + BTRFS_LEAF_DATA_SIZE(root)) { + if (slot >= nritems) { + btrfs_cpu_key_to_disk(&disk_key, ins_key); + btrfs_set_header_nritems(&right->header, 0); + wret = insert_ptr(trans, root, path, + &disk_key, right_buffer->bytenr, + path->slots[1] + 1, 1); + if (wret) + ret = wret; + btrfs_block_release(root, path->nodes[0]); + path->nodes[0] = right_buffer; + path->slots[0] = 0; + path->slots[1] += 1; + return ret; + } + mid = slot; + if (mid != nritems && + leaf_space_used(l, mid, nritems - mid) + + space_needed > BTRFS_LEAF_DATA_SIZE(root)) { + double_split = 1; + } + } + } else { + if (leaf_space_used(l, 0, mid) + space_needed > + BTRFS_LEAF_DATA_SIZE(root)) { + if (!extend && slot == 0) { + btrfs_cpu_key_to_disk(&disk_key, ins_key); + btrfs_set_header_nritems(&right->header, 0); + wret = insert_ptr(trans, root, path, + &disk_key, + right_buffer->bytenr, + path->slots[1], 1); + if (wret) + ret = wret; + btrfs_block_release(root, path->nodes[0]); + path->nodes[0] = right_buffer; + path->slots[0] = 0; + if (path->slots[1] == 0) { + wret = fixup_low_keys(trans, root, + path, &disk_key, 1); + if (wret) + ret = wret; + } + return ret; + } else if (extend && slot == 0) { + mid = 1; + } else { + mid = slot; + if (mid != nritems && + leaf_space_used(l, mid, nritems - mid) + + space_needed > BTRFS_LEAF_DATA_SIZE(root)) { + double_split = 1; + } + } + } + } + nritems = nritems - mid; + btrfs_set_header_nritems(&right->header, nritems); + data_copy_size = btrfs_item_end(l->items + mid) - leaf_data_end(root, l); memcpy(right->items, l->items + mid, - (nritems - mid) * sizeof(struct btrfs_item)); + nritems * sizeof(struct btrfs_item)); memcpy(btrfs_leaf_data(right) + BTRFS_LEAF_DATA_SIZE(root) - data_copy_size, btrfs_leaf_data(l) + leaf_data_end(root, l), data_copy_size); rt_data_off = BTRFS_LEAF_DATA_SIZE(root) - btrfs_item_end(l->items + mid); - - for (i = 0; i < btrfs_header_nritems(&right->header); i++) { + for (i = 0; i < nritems; i++) { u32 ioff = btrfs_item_offset(right->items + i); btrfs_set_item_offset(right->items + i, ioff + rt_data_off); } @@ -1153,6 +1373,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root right_buffer->bytenr, path->slots[1] + 1, 1); if (wret) ret = wret; + BUG_ON(list_empty(&right_buffer->dirty)); BUG_ON(list_empty(&l_buf->dirty)); BUG_ON(path->slots[0] != slot); @@ -1163,10 +1384,15 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root path->slots[1] += 1; } else btrfs_block_release(root, right_buffer); + BUG_ON(path->slots[0] < 0); + if (double_split) { + BUG_ON(num_doubles != 0); + num_doubles++; + goto again; + } return ret; } - /* * Given a key and some data, insert an item into the tree. * This does all the path init required, making room in the tree if needed. @@ -1385,12 +1611,12 @@ int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, */ slot = path->slots[1]; leaf_buf->count++; - wret = push_leaf_left(trans, root, path, 1); + wret = push_leaf_right(trans, root, path, 1, 1); if (wret < 0) ret = wret; if (path->nodes[0] == leaf_buf && btrfs_header_nritems(&leaf->header)) { - wret = push_leaf_right(trans, root, path, 1); + wret = push_leaf_left(trans, root, path, 1, 1); if (wret < 0) ret = wret; } @@ -1412,6 +1638,101 @@ int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, } return ret; } +int btrfs_truncate_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u32 new_size, int from_end) +{ + int ret = 0; + int slot; + int slot_orig; + struct btrfs_leaf *leaf; + struct btrfs_item *item; + u32 nritems; + unsigned int data_end; + unsigned int old_data_start; + unsigned int old_size; + unsigned int size_diff; + int i; + + slot_orig = path->slots[0]; + leaf = &path->nodes[0]->leaf; + slot = path->slots[0]; + + old_size = btrfs_item_size(leaf->items + slot); + if (old_size == new_size) + return 0; + + nritems = btrfs_header_nritems(&leaf->header); + data_end = leaf_data_end(root, leaf); + + old_data_start = btrfs_item_offset(leaf->items + slot); + + size_diff = old_size - new_size; + + BUG_ON(slot < 0); + BUG_ON(slot >= nritems); + + /* + * item0..itemN ... dataN.offset..dataN.size .. data0.size + */ + /* first correct the data pointers */ + for (i = slot; i < nritems; i++) { + u32 ioff; + item = leaf->items + i; + ioff = btrfs_item_offset(item); + btrfs_set_item_offset(item, ioff + size_diff); + } + + /* shift the data */ + if (from_end) { + memmove(btrfs_leaf_data(leaf) + data_end + size_diff, + btrfs_leaf_data(leaf) + data_end, + old_data_start + new_size - data_end); + } else { + struct btrfs_disk_key *disk_key; + u64 offset; + + disk_key = &leaf->items[slot].key; + if (btrfs_disk_key_type(disk_key) == BTRFS_EXTENT_DATA_KEY) { + char *ptr; + struct btrfs_file_extent_item *fi; + + fi = btrfs_item_ptr(leaf, slot, + struct btrfs_file_extent_item); + fi = (struct btrfs_file_extent_item *)( + (unsigned long)fi - size_diff); + + if (btrfs_file_extent_type(fi) == + BTRFS_FILE_EXTENT_INLINE) { + ptr = btrfs_item_ptr(leaf, slot, char); + memmove(ptr, (char *)fi, + offsetof(struct btrfs_file_extent_item, + disk_bytenr)); + } + } + + memmove(btrfs_leaf_data(leaf) + data_end + size_diff, + btrfs_leaf_data(leaf) + data_end, + old_data_start - data_end); + + offset = btrfs_disk_key_offset(disk_key); + btrfs_set_disk_key_offset(disk_key, offset + size_diff); + if (slot == 0) + fixup_low_keys(trans, root, path, disk_key, 1); + } + + item = leaf->items + slot; + btrfs_set_item_size(item, new_size); + BUG_ON(list_empty(&path->nodes[0]->dirty)); + + ret = 0; + if (btrfs_leaf_free_space(root, leaf) < 0) { + btrfs_print_leaf(root, leaf); + BUG(); + } + return ret; +} int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, u32 data_size) @@ -1507,5 +1828,6 @@ int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path) btrfs_node_blockptr(&next->node, 0), btrfs_level_size(root, level - 1)); } + check_leaf(root, path, 0); return 0; } diff --git a/ctree.h b/ctree.h index fb972c1d..57d04ae8 100644 --- a/ctree.h +++ b/ctree.h @@ -23,9 +23,8 @@ #include "kerncompat.h" #include "radix-tree.h" #include "extent-cache.h" - +struct btrfs_root; struct btrfs_trans_handle; - #define BTRFS_MAGIC "_B2RfS_M" #define BTRFS_ROOT_TREE_OBJECTID 1ULL @@ -102,7 +101,9 @@ struct btrfs_header { (sizeof(struct btrfs_disk_key) + sizeof(u64))) #define __BTRFS_LEAF_DATA_SIZE(bs) ((bs) - sizeof(struct btrfs_header)) #define BTRFS_LEAF_DATA_SIZE(r) (__BTRFS_LEAF_DATA_SIZE(r->leafsize)) - +#define BTRFS_MAX_INLINE_DATA_SIZE(r) (BTRFS_LEAF_DATA_SIZE(r) - \ + sizeof(struct btrfs_item) - \ + sizeof(struct btrfs_file_extent_item)) struct btrfs_buffer; /* * the super block basically lists the main trees of the FS @@ -759,6 +760,17 @@ static inline void btrfs_set_root_bytenr(struct btrfs_root_item *item, u64 val) item->bytenr = cpu_to_le64(val); } +static inline u64 btrfs_root_byte_limit(struct btrfs_root_item *item) +{ + return le64_to_cpu(item->byte_limit); +} + +static inline void btrfs_set_root_byte_limit(struct btrfs_root_item *item, + u64 val) +{ + item->byte_limit = cpu_to_le64(val); +} + static inline u8 btrfs_root_level(struct btrfs_root_item *item) { return item->level; @@ -1028,6 +1040,9 @@ static inline void btrfs_set_file_extent_num_bytes(struct #define btrfs_item_ptr(leaf, slot, type) \ ((type *)(btrfs_leaf_data(leaf) + \ btrfs_item_offset((leaf)->items + (slot)))) +#define btrfs_item_ptr_offset(leaf, slot) \ + ((unsigned long)(btrfs_leaf_data(leaf) + \ + btrfs_item_offset_nr(leaf, slot))) static inline u32 btrfs_level_size(struct btrfs_root *root, int level) { @@ -1036,15 +1051,25 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) return root->nodesize; } int btrfs_comp_keys(struct btrfs_disk_key *disk, struct btrfs_key *k2); -int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, u32 data_size); struct btrfs_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, u32 blocksize); int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_buffer *buf); +int btrfs_inc_root_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root); int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, int pin); +int btrfs_cow_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_buffer *buf, + struct btrfs_buffer *parent, int parent_slot, + struct btrfs_buffer **cow_ret); +int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, u32 data_size); +int btrfs_truncate_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u32 new_size, int from_end); int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_path *p, int ins_len, int cow); @@ -1076,11 +1101,16 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, char *name, int name_len, u64 dir, struct btrfs_key *location, u8 type); -int btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, u64 dir, char *name, - int name_len, int mod); -int btrfs_match_dir_item_name(struct btrfs_root *root, struct btrfs_path *path, - char *name, int name_len); +struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + u64 dir, char *name, int name_len, int mod); +struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root, + struct btrfs_path *path, + const char *name, int name_len); +int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_dir_item *di); int btrfs_find_free_objectid(struct btrfs_trans_handle *trans, struct btrfs_root *fs_root, u64 dirid, u64 *objectid); @@ -1097,4 +1127,18 @@ int btrfs_insert_block_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_key *key, struct btrfs_block_group_item *bi); +/* file-item.c */ +int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 objectid, u64 pos, u64 offset, + u64 disk_num_bytes, u64 num_bytes); +int btrfs_insert_inline_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + u64 offset, char *buffer, size_t size); +int btrfs_lookup_csum(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_path *path, u64 objectid, + u64 offset, int cow, struct btrfs_csum_item **item_ret); +int btrfs_csum_file_block(struct btrfs_trans_handle *trans, struct btrfs_root + *root, struct btrfs_inode_item *inode, + u64 objectid, u64 offset, char *data, size_t len); #endif diff --git a/dir-item.c b/dir-item.c index 3c49c569..a3cb2787 100644 --- a/dir-item.c +++ b/dir-item.c @@ -30,21 +30,25 @@ static struct btrfs_dir_item *insert_with_overflow(struct struct btrfs_root *root, struct btrfs_path *path, struct btrfs_key *cpu_key, - u32 data_size) + u32 data_size, + const char *name, + int name_len) { int ret; char *ptr; struct btrfs_item *item; struct btrfs_leaf *leaf; - ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size); if (ret == -EEXIST) { - ret = btrfs_extend_item(trans, root, path, data_size); - BUG_ON(ret > 0); - if (ret) + struct btrfs_dir_item *di; + di = btrfs_match_dir_item_name(root, path, name, name_len); + if (di) return NULL; + ret = btrfs_extend_item(trans, root, path, data_size); } BUG_ON(ret > 0); + if (ret) + return NULL; leaf = &path->nodes[0]->leaf; item = leaf->items + path->slots[0]; ptr = btrfs_item_ptr(leaf, path->slots[0], char); @@ -75,7 +79,8 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root BUG_ON(ret); btrfs_init_path(&path); data_size = sizeof(*dir_item) + name_len; - dir_item = insert_with_overflow(trans, root, &path, &key, data_size); + dir_item = insert_with_overflow(trans, root, &path, &key, data_size, + name, name_len); if (!dir_item) { ret = -1; goto out; @@ -94,7 +99,8 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root btrfs_release_path(root, &path); btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY); key.offset = location->objectid; - dir_item = insert_with_overflow(trans, root, &path, &key, data_size); + dir_item = insert_with_overflow(trans, root, &path, &key, data_size, + name, name_len); if (!dir_item) { ret = -1; goto out; @@ -109,37 +115,95 @@ out: btrfs_release_path(root, &path); return ret; } - -int btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, u64 dir, char *name, - int name_len, int mod) +struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, u64 dir, + char *name, int name_len, int mod) { int ret; struct btrfs_key key; int ins_len = mod < 0 ? -1 : 0; int cow = mod != 0; - + struct btrfs_key found_key; + struct btrfs_leaf *leaf; key.objectid = dir; btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); ret = btrfs_name_hash(name, name_len, &key.offset); BUG_ON(ret); ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); - return ret; + if (ret < 0) + return NULL; + if (ret > 0) { + if (path->slots[0] == 0) + return NULL; + path->slots[0]--; + } + + leaf = &path->nodes[0]->leaf; + btrfs_disk_key_to_cpu(&found_key, &leaf->items[path->slots[0]].key); + + if (found_key.objectid != dir || + btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY || + found_key.offset != key.offset) + return NULL; + + return btrfs_match_dir_item_name(root, path, name, name_len); } -int btrfs_match_dir_item_name(struct btrfs_root *root, - struct btrfs_path *path, char - *name, int name_len) +struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root, + struct btrfs_path *path, + const char *name, int name_len) { - struct btrfs_dir_item *dir_item; + u32 cur = 0; + u32 this_len; + u32 total_len; char *name_ptr; + struct btrfs_leaf *leaf; + struct btrfs_dir_item *dir_item; - dir_item = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0], - struct btrfs_dir_item); - if (btrfs_dir_name_len(dir_item) != name_len) - return 0; - name_ptr = (char *)(dir_item + 1); - if (memcmp(name_ptr, name, name_len)) - return 0; - return 1; + leaf = &path->nodes[0]->leaf; + dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item); + total_len = btrfs_item_size(leaf->items + path->slots[0]); + while(cur < total_len) { + this_len = sizeof(*dir_item) + btrfs_dir_name_len(dir_item) + + btrfs_dir_data_len(dir_item); + name_ptr = (char *)(dir_item + 1); + + if (btrfs_dir_name_len(dir_item) == name_len && + memcmp(name, name_ptr, name_len) == 0) + return dir_item; + + cur += this_len; + dir_item = (struct btrfs_dir_item *)((char *)dir_item + + this_len); + } + return NULL; +} + +int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct btrfs_dir_item *di) +{ + + struct btrfs_leaf *leaf; + u32 sub_item_len; + u32 item_len; + int ret = 0; + + leaf = &path->nodes[0]->leaf; + sub_item_len = sizeof(*di) + btrfs_dir_name_len(di) + + btrfs_dir_data_len(di); + item_len = btrfs_item_size(leaf->items + path->slots[0]); + if (sub_item_len == item_len) { + ret = btrfs_del_item(trans, root, path); + } else { + char *ptr = (char *)di; + char *start = btrfs_item_ptr(leaf, path->slots[0], char); + memmove(ptr, ptr + sub_item_len, + item_len - (ptr + sub_item_len - start)); + ret = btrfs_truncate_item(trans, root, path, + item_len - sub_item_len, 1); + } + return 0; } diff --git a/disk-io.c b/disk-io.c index 026cdfde..da654918 100644 --- a/disk-io.c +++ b/disk-io.c @@ -302,11 +302,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct root->node->count++; ret = btrfs_drop_snapshot(trans, root, snap); BUG_ON(ret); - ret = btrfs_del_root(trans, root->fs_info->tree_root, &snap_key); BUG_ON(ret); - root->fs_info->generation = root->root_key.offset + 1; - + btrfs_free_transaction(root, trans); return ret; } @@ -420,7 +418,7 @@ struct btrfs_root *open_ctree_fd(int fp, struct btrfs_super_block *super) root->commit_root = root->node; root->node->count++; root->ref_cows = 1; - root->fs_info->generation = root->root_key.offset + 1; + root->fs_info->generation = btrfs_super_generation(super) + 1; btrfs_read_block_groups(root); return root; } @@ -429,8 +427,8 @@ int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_super_block *s) { int ret; - btrfs_set_super_root(s, root->fs_info->tree_root->node->bytenr); + btrfs_set_super_generation(s, trans->transid); btrfs_set_super_root_level(s, btrfs_header_level(&root->fs_info->tree_root->node->node.header)); btrfs_csum_super(root, s); @@ -460,17 +458,17 @@ int close_ctree(struct btrfs_root *root, struct btrfs_super_block *s) { int ret; struct btrfs_trans_handle *trans; - - trans = root->fs_info->running_transaction; + trans = btrfs_start_transaction(root, 1); btrfs_commit_transaction(trans, root, s); + trans = btrfs_start_transaction(root, 1); ret = commit_tree_roots(trans, root->fs_info); BUG_ON(ret); ret = __commit_transaction(trans, root); BUG_ON(ret); write_ctree_super(trans, root, s); + btrfs_free_transaction(root, trans); drop_cache(root); BUG_ON(!list_empty(&root->fs_info->trans)); - btrfs_free_block_groups(root->fs_info); close(root->fs_info->fp); if (root->node) diff --git a/extent-tree.c b/extent-tree.c index 8615a432..621f3f6c 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -88,21 +88,34 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_buffer *buf) { u64 bytenr; + u32 blocksize; int i; + int level; if (!root->ref_cows) return 0; + level = btrfs_header_level(&buf->node.header) - 1; + blocksize = btrfs_level_size(root, level); + if (btrfs_is_leaf(&buf->node)) return 0; for (i = 0; i < btrfs_header_nritems(&buf->node.header); i++) { bytenr = btrfs_node_blockptr(&buf->node, i); - inc_block_ref(trans, root, bytenr, root->nodesize); + inc_block_ref(trans, root, bytenr, blocksize); } + return 0; } +int btrfs_inc_root_ref(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + return inc_block_ref(trans, root, root->node->bytenr, + root->node->size); +} + static int write_one_cache_group(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, @@ -525,9 +538,8 @@ static int alloc_extent(struct btrfs_trans_handle *trans, ret = insert_cache_extent(&root->fs_info->pending_tree, ins->objectid, ins->offset); BUG_ON(ret); - return 0; + goto update_block; } - ret = btrfs_insert_item(trans, extent_root, ins, &extent_item, sizeof(extent_item)); @@ -537,9 +549,11 @@ static int alloc_extent(struct btrfs_trans_handle *trans, return ret; if (pending_ret) return pending_ret; +update_block: + ret = update_block_group(trans, root, ins->objectid, ins->offset, 1); + BUG_ON(ret); return 0; } - /* * helper function to allocate a block for a given tree * returns the tree buffer or NULL. @@ -551,14 +565,12 @@ struct btrfs_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, struct btrfs_key ins; int ret; struct btrfs_buffer *buf; - ret = alloc_extent(trans, root, root->root_key.objectid, - blocksize, 0, (unsigned long)-1, &ins); + blocksize, 0, (u64)-1, &ins); if (ret) { BUG(); return NULL; } - ret = update_block_group(trans, root, ins.objectid, ins.offset, 1); buf = find_tree_block(root, ins.objectid, blocksize); btrfs_set_header_generation(&buf->node.header, root->root_key.offset + 1); diff --git a/file-item.c b/file-item.c index 9d0a3544..9d119cf2 100644 --- a/file-item.c +++ b/file-item.c @@ -23,9 +23,272 @@ #include "ctree.h" #include "disk-io.h" #include "transaction.h" +#include "crc32c.h" +#define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \ + sizeof(struct btrfs_item) * 2) / \ + BTRFS_CRC32_SIZE) - 1)) int btrfs_create_file(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 dirid, u64 *objectid) { return 0; } + +int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + u64 objectid, u64 pos, + u64 offset, u64 disk_num_bytes, + u64 num_bytes) +{ + int ret = 0; + struct btrfs_file_extent_item *item; + struct btrfs_key file_key; + struct btrfs_path path; + struct btrfs_leaf *leaf; + + + btrfs_init_path(&path); + file_key.objectid = objectid; + file_key.offset = pos; + btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); + + ret = btrfs_insert_empty_item(trans, root, &path, &file_key, + sizeof(*item)); + if (ret < 0) + goto out; + BUG_ON(ret); + leaf = &path.nodes[0]->leaf; + item = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + btrfs_set_file_extent_disk_bytenr(item, offset); + btrfs_set_file_extent_disk_num_bytes(item, disk_num_bytes); + btrfs_set_file_extent_offset(item, 0); + btrfs_set_file_extent_num_bytes(item, num_bytes); + btrfs_set_file_extent_generation(item, trans->transid); + btrfs_set_file_extent_type(item, BTRFS_FILE_EXTENT_REG); +out: + btrfs_release_path(root, &path); + return ret; +} + +int btrfs_insert_inline_file_extent(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 objectid, + u64 offset, char *buffer, size_t size) +{ + int ret; + char *ptr; + u32 datasize; + struct btrfs_key key; + struct btrfs_path path; + struct btrfs_leaf *leaf; + struct btrfs_file_extent_item *ei; + + btrfs_init_path(&path); + key.objectid = objectid; + key.offset = offset; + btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); + + datasize = btrfs_file_extent_calc_inline_size(size); + ret = btrfs_insert_empty_item(trans, root, &path, &key, + datasize); + BUG_ON(ret); + leaf = &path.nodes[0]->leaf; + ei = btrfs_item_ptr(leaf, path.slots[0], + struct btrfs_file_extent_item); + btrfs_set_file_extent_generation(ei, trans->transid); + btrfs_set_file_extent_type(ei, BTRFS_FILE_EXTENT_INLINE); + ptr = btrfs_file_extent_inline_start(ei); + memcpy(ptr, buffer, size); + btrfs_release_path(root, &path); + return 0; +} + +int btrfs_lookup_csum(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + u64 objectid, u64 offset, int cow, + struct btrfs_csum_item **item_ret) +{ + int ret; + int slot; + struct btrfs_key file_key; + struct btrfs_key found_key; + struct btrfs_csum_item *item; + struct btrfs_leaf *leaf; + u64 csum_offset = 0; + int csums_in_item; + + file_key.objectid = objectid; + file_key.offset = offset; + btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY); + ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); + if (ret < 0) + goto fail; + leaf = &path->nodes[0]->leaf; + if (ret > 0) { + if (path->slots[0] == 0) + goto fail; + path->slots[0]--; + + slot = path->slots[0]; + btrfs_disk_key_to_cpu(&found_key, &leaf->items[slot].key); + if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY || + found_key.objectid != objectid) { + goto fail; + } + csum_offset = (offset - found_key.offset) / root->sectorsize; + csums_in_item = btrfs_item_size(&leaf->items[slot]); + csums_in_item /= BTRFS_CRC32_SIZE; + + if (csum_offset >= csums_in_item) { + ret = -EFBIG; + goto fail; + } + ret = 0; + } + item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); + item = (struct btrfs_csum_item *)((unsigned char *)item + + csum_offset * BTRFS_CRC32_SIZE); + *item_ret = item; +fail: + if (ret > 0) + ret = -ENOENT; + return ret; +} + +int btrfs_csum_file_block(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_inode_item *inode, + u64 objectid, u64 offset, + char *data, size_t len) +{ + int ret; + int slot; + struct btrfs_key file_key; + struct btrfs_key found_key; + u64 next_offset = (u64)-1; + int found_next = 0; + struct btrfs_path path; + struct btrfs_csum_item *item; + struct btrfs_leaf *leaf = NULL; + u64 csum_offset; + u32 csum_result = ~(u32)0; + u32 nritems; + u32 ins_size; + + btrfs_init_path(&path); + + file_key.objectid = objectid; + file_key.offset = offset; + btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY); + + ret = btrfs_lookup_csum(trans, root, &path, objectid, + offset, 1, &item); + if (!ret) { + leaf = &path.nodes[0]->leaf; + goto found; + } + if (ret != -EFBIG && ret != -ENOENT) + goto fail; + leaf = &path.nodes[0]->leaf; + if (ret == -EFBIG) { + u32 item_size; + slot = path.slots[0]; + /* we found one, but it isn't big enough yet */ + item_size = btrfs_item_size(&leaf->items[slot]); + if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) { + /* already at max size, make a new one */ + goto insert; + } + } else { + slot = path.slots[0] + 1; + /* we didn't find a csum item, insert one */ + nritems = btrfs_header_nritems(&leaf->header); + if (path.slots[0] >= nritems - 1) { + ret = btrfs_next_leaf(root, &path); + if (ret == 1) + found_next = 1; + if (ret != 0) + goto insert; + slot = 0; + } + btrfs_disk_key_to_cpu(&found_key, &leaf->items[slot].key); + if (found_key.objectid != objectid || + found_key.type != BTRFS_CSUM_ITEM_KEY) { + found_next = 1; + goto insert; + } + next_offset = found_key.offset; + found_next = 1; + goto insert; + } + + /* + * at this point, we know the tree has an item, but it isn't big + * enough yet to put our csum in. Grow it + */ + btrfs_release_path(root, &path); + ret = btrfs_search_slot(trans, root, &file_key, &path, + BTRFS_CRC32_SIZE, 1); + if (ret < 0) + goto fail; + BUG_ON(ret == 0); + if (path.slots[0] == 0) { + goto insert; + } + path.slots[0]--; + slot = path.slots[0]; + leaf = &path.nodes[0]->leaf; + btrfs_disk_key_to_cpu(&found_key, &leaf->items[slot].key); + csum_offset = (offset - found_key.offset) / root->sectorsize; + if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY || + found_key.objectid != objectid || + csum_offset >= MAX_CSUM_ITEMS(root)) { + goto insert; + } + if (csum_offset >= btrfs_item_size(&leaf->items[slot]) / + BTRFS_CRC32_SIZE) { + u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE; + diff = diff - btrfs_item_size(&leaf->items[slot]); + if (diff != BTRFS_CRC32_SIZE) + goto insert; + ret = btrfs_extend_item(trans, root, &path, diff); + BUG_ON(ret); + goto csum; + } + +insert: + btrfs_release_path(root, &path); + csum_offset = 0; + if (found_next) { + u64 tmp; + if (next_offset > btrfs_inode_size(inode)) + next_offset = btrfs_inode_size(inode); + tmp = next_offset - offset + root->sectorsize - 1; + tmp /= root->sectorsize; + if (tmp > MAX_CSUM_ITEMS(root)) + tmp = MAX_CSUM_ITEMS(root); + ins_size = BTRFS_CRC32_SIZE * tmp; + } else { + ins_size = BTRFS_CRC32_SIZE; + } + ret = btrfs_insert_empty_item(trans, root, &path, &file_key, + ins_size); + if (ret < 0) + goto fail; + BUG_ON(ret != 0); +csum: + slot = path.slots[0]; + leaf = &path.nodes[0]->leaf; + item = btrfs_item_ptr(leaf, slot, struct btrfs_csum_item); + item = (struct btrfs_csum_item *)((unsigned char *)item + + csum_offset * BTRFS_CRC32_SIZE); +found: + csum_result = crc32c(csum_result, data, len); + csum_result = ~cpu_to_le32(csum_result); + memcpy(item, &csum_result, BTRFS_CRC32_SIZE); + ret = 0; +fail: + btrfs_release_path(root, &path); + return ret; +} diff --git a/kerncompat.h b/kerncompat.h index 86860127..3681dd88 100644 --- a/kerncompat.h +++ b/kerncompat.h @@ -18,9 +18,9 @@ #ifndef __KERNCOMPAT #define __KERNCOMPAT - #include #include +#include #include #include #include @@ -124,10 +124,6 @@ static inline int test_bit(int nr, const volatile unsigned long *addr) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) - -#define ENOMEM 5 -#define EEXIST 6 - #ifdef __CHECKER__ #define __CHECK_ENDIAN__ #define __bitwise __bitwise__ diff --git a/transaction.h b/transaction.h index bffd5b2b..ac026aad 100644 --- a/transaction.h +++ b/transaction.h @@ -28,8 +28,11 @@ struct btrfs_trans_handle { static inline struct btrfs_trans_handle * btrfs_start_transaction(struct btrfs_root *root, int num_blocks) { + struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_trans_handle *h = malloc(sizeof(*h)); - h->transid = root->root_key.offset; + fs_info->running_transaction = h; + fs_info->generation++; + h->transid = fs_info->generation; h->blocks_reserved = num_blocks; h->blocks_used = 0; return h; -- cgit v1.2.3