Btrfs: working file_write, reorganized key flags

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index ff8f333..8cc3c1d 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -4,7 +4,7 @@
 #include "transaction.h"
 
 #define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
-				 sizeof(struct btrfs_item)) / \
+				 sizeof(struct btrfs_item) * 2) / \
 				sizeof(struct btrfs_csum_item)) - 1))
 int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
 			       struct btrfs_root *root,
@@ -19,11 +19,6 @@
 	path = btrfs_alloc_path();
 	BUG_ON(!path);
 	btrfs_init_path(path);
-	/*
-	ret = btrfs_alloc_extent(trans, root, num_blocks, hint_block,
-				 (u64)-1, &ins);
-				 */
-	BUG_ON(ret);
 	file_key.objectid = objectid;
 	file_key.offset = pos;
 	file_key.flags = 0;
@@ -40,6 +35,7 @@
 	btrfs_set_file_extent_num_blocks(item, num_blocks);
 	btrfs_set_file_extent_generation(item, trans->transid);
 	btrfs_mark_buffer_dirty(path->nodes[0]);
+
 	btrfs_release_path(root, path);
 	btrfs_free_path(path);
 	return 0;
@@ -57,6 +53,7 @@
 	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;
@@ -79,9 +76,11 @@
 		}
 		csum_offset = (offset - found_key.offset) >>
 				root->fs_info->sb->s_blocksize_bits;
-		if (csum_offset >=
-		    btrfs_item_size(leaf->items + path->slots[0]) /
-		    sizeof(struct btrfs_csum_item)) {
+		csums_in_item = btrfs_item_size(leaf->items + path->slots[0]);
+		csums_in_item /= sizeof(struct btrfs_csum_item);
+
+		if (csum_offset >= csums_in_item) {
+			ret = -EFBIG;
 			goto fail;
 		}
 	}
@@ -128,16 +127,36 @@
 
 	path = btrfs_alloc_path();
 	BUG_ON(!path);
-	btrfs_init_path(path);
 
-	item = btrfs_lookup_csum(trans, root, path, objectid, offset, 0);
-	if (!IS_ERR(item))
-		goto found;
-	btrfs_release_path(root, path);
 	file_key.objectid = objectid;
 	file_key.offset = offset;
 	file_key.flags = 0;
 	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
+
+	item = btrfs_lookup_csum(trans, root, path, objectid, offset, 1);
+	if (!IS_ERR(item))
+		goto found;
+	ret = PTR_ERR(item);
+	if (ret == -EFBIG) {
+		u32 item_size;
+		/* we found one, but it isn't big enough yet */
+		leaf = btrfs_buffer_leaf(path->nodes[0]);
+		item_size = btrfs_item_size(leaf->items + path->slots[0]);
+		if ((item_size / sizeof(struct btrfs_csum_item)) >=
+		    MAX_CSUM_ITEMS(root)) {
+			/* already at max size, make a new one */
+			goto insert;
+		}
+	} else {
+		/* we didn't find a csum item, insert one */
+		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,
 				sizeof(struct btrfs_csum_item), 1);
 	if (ret < 0)
@@ -146,7 +165,6 @@
 		BUG();
 	}
 	if (path->slots[0] == 0) {
-		btrfs_release_path(root, path);
 		goto insert;
 	}
 	path->slots[0]--;
@@ -157,29 +175,36 @@
 	if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
 	    found_key.objectid != objectid ||
 	    csum_offset >= MAX_CSUM_ITEMS(root)) {
-		btrfs_release_path(root, path);
+		WARN_ON(1);
 		goto insert;
 	}
 	if (csum_offset >= btrfs_item_size(leaf->items + path->slots[0]) /
 	    sizeof(struct btrfs_csum_item)) {
-		ret = btrfs_extend_item(trans, root, path,
-					sizeof(struct btrfs_csum_item));
+		u32 diff = (csum_offset + 1) * sizeof(struct btrfs_csum_item);
+		diff = diff - btrfs_item_size(leaf->items + path->slots[0]);
+		WARN_ON(diff != sizeof(struct btrfs_csum_item));
+		ret = btrfs_extend_item(trans, root, path, diff);
 		BUG_ON(ret);
 		goto csum;
 	}
 
 insert:
+	btrfs_release_path(root, path);
 	csum_offset = 0;
 	ret = btrfs_insert_empty_item(trans, root, path, &file_key,
 				      sizeof(struct btrfs_csum_item));
-	if (ret != 0 && ret != -EEXIST)
+	if (ret != 0) {
+		printk("at insert for %Lu %u %Lu ret is %d\n", file_key.objectid, file_key.flags, file_key.offset, ret);
+		WARN_ON(1);
 		goto fail;
+	}
 csum:
 	item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
 			      struct btrfs_csum_item);
 	ret = 0;
 	item += csum_offset;
 found:
+	btrfs_check_bounds(item->csum, BTRFS_CSUM_SIZE, path->nodes[0]->b_data, root->fs_info->sb->s_blocksize);
 	ret = btrfs_csum_data(root, data, len, item->csum);
 	btrfs_mark_buffer_dirty(path->nodes[0]);
 fail:
@@ -210,6 +235,9 @@
 	item = btrfs_lookup_csum(NULL, root, path, objectid, offset, 0);
 	if (IS_ERR(item)) {
 		ret = PTR_ERR(item);
+		/* a csum that isn't present is a preallocated region. */
+		if (ret == -ENOENT || ret == -EFBIG)
+			ret = 1;
 		goto fail;
 	}