From 2b2c1d87d0bcbdb58fdc4e38844ae3bc924977be Mon Sep 17 00:00:00 2001
From: Martin Krizek <martin.krizek@gmail.com>
Date: Wed, 27 Mar 2013 14:52:18 -0400
Subject: [PATCH] Add deduplication tree

---
 convert.c    | 11 ++++++++---
 ctree.h      | 30 ++++++++++++++++++++++++++++--
 disk-io.c    | 38 ++++++++++++++++++++++++++++++++------
 disk-io.h    |  2 +-
 find-root.c  | 10 ++++++++--
 ioctl.h      |  1 +
 mkfs.c       |  9 +++++++--
 print-tree.c |  6 ++++++
 utils.c      | 33 ++++++++++++++++++++++++++++-----
 9 files changed, 119 insertions(+), 21 deletions(-)

diff --git a/convert.c b/convert.c
index f462597..7825100 100644
--- a/convert.c
+++ b/convert.c
@@ -1715,6 +1715,11 @@ static int init_btrfs(struct btrfs_root *root)
 	BUG_ON(ret);
 	free_extent_buffer(tmp);
 
+	ret = __btrfs_cow_block(trans, fs_info->dedup_root,
+				fs_info->dedup_root->node, NULL, 0, &tmp, 0, 0);
+	BUG_ON(ret);
+	free_extent_buffer(tmp);
+
 	ret = btrfs_commit_transaction(trans, root);
 	BUG_ON(ret);
 err:
@@ -2280,7 +2285,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 	int i, ret;
 	int fd = -1;
 	u32 blocksize;
-	u64 blocks[7];
+	u64 blocks[8];
 	u64 total_bytes;
 	u64 super_bytenr;
 	ext2_filsys ext2_fs;
@@ -2303,7 +2308,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 		fprintf(stderr, "filetype feature is missing\n");
 		goto fail;
 	}
-	for (i = 0; i < 7; i++) {
+	for (i = 0; i < 8; i++) {
 		ret = ext2_alloc_block(ext2_fs, 0, blocks + i);
 		if (ret) {
 			fprintf(stderr, "not enough free space\n");
@@ -2342,7 +2347,7 @@ int do_convert(const char *devname, int datacsum, int packing, int noxattr)
 	}
 	root->fs_info->extent_ops = &extent_ops;
 	/* recover block allocation bitmap */
-	for (i = 0; i < 7; i++) {
+	for (i = 0; i < 8; i++) {
 		blocks[i] /= blocksize;
 		ext2_free_block(ext2_fs, blocks[i]);
 	}
diff --git a/ctree.h b/ctree.h
index 9442e03..18a8b9c 100644
--- a/ctree.h
+++ b/ctree.h
@@ -69,6 +69,8 @@ struct btrfs_trans_handle;
 /* holds checksums of all the data extents */
 #define BTRFS_CSUM_TREE_OBJECTID 7ULL
 #define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+/* holds hashes of all the data extents for dedup */
+#define BTRFS_DEDUP_TREE_OBJECTID 9ULL
 
 
 /* for storing balance parameters in the root tree */
@@ -367,11 +369,15 @@ struct btrfs_root_backup {
 	__le64 csum_root;
 	__le64 csum_root_gen;
 
+	__le64 dedup_root;
+	__le64 dedup_root_gen;
+
 	__le64 total_bytes;
 	__le64 bytes_used;
 	__le64 num_devices;
+
 	/* future */
-	__le64 unsed_64[4];
+	__le64 unsed_64[2];
 
 	u8 tree_root_level;
 	u8 chunk_root_level;
@@ -379,8 +385,10 @@ struct btrfs_root_backup {
 	u8 fs_root_level;
 	u8 dev_root_level;
 	u8 csum_root_level;
+	u8 dedup_root_level;
+
 	/* future and to align */
-	u8 unused_8[10];
+	u8 unused_8[9];
 } __attribute__ ((__packed__));
 
 /*
@@ -411,6 +419,7 @@ struct btrfs_super_block {
 	__le32 nodesize;
 	__le32 leafsize;
 	__le32 stripesize;
+	__le32 dedupsize;
 	__le32 sys_chunk_array_size;
 	__le64 chunk_root_generation;
 	__le64 compat_flags;
@@ -895,6 +904,7 @@ struct btrfs_fs_info {
 	struct btrfs_root *chunk_root;
 	struct btrfs_root *dev_root;
 	struct btrfs_root *csum_root;
+	struct btrfs_root *dedup_root;
 
 	struct cache_tree fs_root_cache;
 
@@ -970,6 +980,8 @@ struct btrfs_root {
 	/* leaf allocations are done in leafsize units */
 	u32 stripesize;
 
+	u32 dedupsize;
+
 	int ref_cows;
 	int track_dirty;
 
@@ -1025,6 +1037,12 @@ struct btrfs_root {
 #define BTRFS_ROOT_ITEM_KEY	132
 
 /*
+ * extents hashes are stored in a separate tree and hold hashes of
+ * dedup-sized block for an entire extent on disk.
+ */
+#define BTRFS_DEDUP_ITEM_KEY	136
+
+/*
  * root backrefs tie subvols and snapshots to the directory entries that
  * reference them
  */
@@ -1837,6 +1855,12 @@ BTRFS_SETGET_STACK_FUNCS(backup_csum_root_gen, struct btrfs_root_backup,
 		   csum_root_gen, 64);
 BTRFS_SETGET_STACK_FUNCS(backup_csum_root_level, struct btrfs_root_backup,
 		   csum_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(backup_dedup_root, struct btrfs_root_backup,
+		   dedup_root, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_dedup_root_gen, struct btrfs_root_backup,
+		   dedup_root_gen, 64);
+BTRFS_SETGET_STACK_FUNCS(backup_dedup_root_level, struct btrfs_root_backup,
+		   dedup_root_level, 8);
 BTRFS_SETGET_STACK_FUNCS(backup_total_bytes, struct btrfs_root_backup,
 		   total_bytes, 64);
 BTRFS_SETGET_STACK_FUNCS(backup_bytes_used, struct btrfs_root_backup,
@@ -1879,6 +1903,8 @@ BTRFS_SETGET_STACK_FUNCS(super_leafsize, struct btrfs_super_block,
 			 leafsize, 32);
 BTRFS_SETGET_STACK_FUNCS(super_stripesize, struct btrfs_super_block,
 			 stripesize, 32);
+BTRFS_SETGET_STACK_FUNCS(super_dedupsize, struct btrfs_super_block,
+			 dedupsize, 32);
 BTRFS_SETGET_STACK_FUNCS(super_root_dir, struct btrfs_super_block,
 			 root_dir_objectid, 64);
 BTRFS_SETGET_STACK_FUNCS(super_num_devices, struct btrfs_super_block,
diff --git a/disk-io.c b/disk-io.c
index a9fd374..87b2655 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -464,7 +464,8 @@ int write_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 }
 
 int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
-			u32 stripesize, struct btrfs_root *root,
+			u32 stripesize, u32 dedupsize,
+			struct btrfs_root *root,
 			struct btrfs_fs_info *fs_info, u64 objectid)
 {
 	root->node = NULL;
@@ -473,6 +474,7 @@ int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
 	root->nodesize = nodesize;
 	root->leafsize = leafsize;
 	root->stripesize = stripesize;
+	root->dedupsize = dedupsize;
 	root->ref_cows = 0;
 	root->track_dirty = 0;
 
@@ -614,7 +616,7 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
 
 	__setup_root(tree_root->nodesize, tree_root->leafsize,
 		     tree_root->sectorsize, tree_root->stripesize,
-		     root, fs_info, objectid);
+		     tree_root->dedupsize, root, fs_info, objectid);
 	ret = btrfs_find_last_root(tree_root, objectid,
 				   &root->root_item, &root->root_key);
 	if (ret)
@@ -651,7 +653,8 @@ static int find_and_setup_log_root(struct btrfs_root *tree_root,
 
 	__setup_root(tree_root->nodesize, tree_root->leafsize,
 		     tree_root->sectorsize, tree_root->stripesize,
-		     log_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
+		     tree_root->dedupsize, log_root, fs_info,
+		     BTRFS_TREE_LOG_OBJECTID);
 
 	log_root->node = read_tree_block(tree_root, blocknr,
 				     blocksize,
@@ -723,7 +726,8 @@ struct btrfs_root *btrfs_read_fs_root_no_cache(struct btrfs_fs_info *fs_info,
 
 	__setup_root(tree_root->nodesize, tree_root->leafsize,
 		     tree_root->sectorsize, tree_root->stripesize,
-		     root, fs_info, location->objectid);
+		     tree_root->dedupsize, root, fs_info,
+		     location->objectid);
 
 	path = btrfs_alloc_path();
 	BUG_ON(!path);
@@ -773,6 +777,8 @@ struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
 		return fs_info->dev_root;
 	if (location->objectid == BTRFS_CSUM_TREE_OBJECTID)
 		return fs_info->csum_root;
+	if (location->objectid == BTRFS_DEDUP_TREE_OBJECTID)
+		return fs_info->dedup_root;
 
 	BUG_ON(location->objectid == BTRFS_TREE_RELOC_OBJECTID ||
 	       location->offset != (u64)-1);
@@ -804,6 +810,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	u32 leafsize;
 	u32 blocksize;
 	u32 stripesize;
+	u32 dedupsize;
 	u64 generation;
 	struct btrfs_key key;
 	struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
@@ -811,6 +818,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *dedup_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
 	int ret;
 	struct btrfs_super_block *disk_super;
@@ -846,6 +854,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	fs_info->chunk_root = chunk_root;
 	fs_info->dev_root = dev_root;
 	fs_info->csum_root = csum_root;
+	fs_info->dedup_root = dedup_root;
 
 	if (!writes)
 		fs_info->readonly = 1;
@@ -865,7 +874,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
 	INIT_LIST_HEAD(&fs_info->space_info);
 
-	__setup_root(4096, 4096, 4096, 4096, tree_root,
+	__setup_root(4096, 4096, 4096, 4096, 4096, tree_root,
 		     fs_info, BTRFS_ROOT_TREE_OBJECTID);
 
 	if (writes)
@@ -915,10 +924,12 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	leafsize = btrfs_super_leafsize(disk_super);
 	sectorsize = btrfs_super_sectorsize(disk_super);
 	stripesize = btrfs_super_stripesize(disk_super);
+	dedupsize = btrfs_super_dedupsize(disk_super);
 	tree_root->nodesize = nodesize;
 	tree_root->leafsize = leafsize;
 	tree_root->sectorsize = sectorsize;
 	tree_root->stripesize = stripesize;
+	tree_root->dedupsize = dedupsize;
 
 	ret = btrfs_read_sys_array(tree_root);
 	if (ret)
@@ -927,7 +938,7 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 				     btrfs_super_chunk_root_level(disk_super));
 	generation = btrfs_super_chunk_root_generation(disk_super);
 
-	__setup_root(nodesize, leafsize, sectorsize, stripesize,
+	__setup_root(nodesize, leafsize, sectorsize, stripesize, dedupsize,
 		     chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
 
 	chunk_root->node = read_tree_block(chunk_root,
@@ -986,6 +997,15 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, const char *path,
 	}
 	csum_root->track_dirty = 1;
 
+	ret = find_and_setup_root(tree_root, fs_info,
+				  BTRFS_DEDUP_TREE_OBJECTID, dedup_root);
+	if (ret) {
+		printk("Couldn't setup dedup tree\n");
+		if (!partial)
+			goto out_failed;
+	}
+	dedup_root->track_dirty = 1;
+
 	find_and_setup_log_root(tree_root, fs_info, disk_super);
 
 	fs_info->generation = generation;
@@ -1012,6 +1032,8 @@ out_failed:
 
 	if (fs_info->csum_root)
 		free_extent_buffer(fs_info->csum_root->node);
+	if (fs_info->dedup_root)
+		free_extent_buffer(fs_info->dedup_root->node);
 	if (fs_info->dev_root)
 		free_extent_buffer(fs_info->dev_root->node);
 	if (fs_info->extent_root)
@@ -1035,6 +1057,7 @@ out:
 	free(chunk_root);
 	free(dev_root);
 	free(csum_root);
+	free(dedup_root);
 	free(fs_info);
 	return NULL;
 }
@@ -1327,6 +1350,8 @@ int close_ctree(struct btrfs_root *root)
 		free_extent_buffer(fs_info->dev_root->node);
 	if (fs_info->csum_root->node)
 		free_extent_buffer(fs_info->csum_root->node);
+	if (fs_info->dedup_root->node)
+		free_extent_buffer(fs_info->dedup_root->node);
 
 	if (fs_info->log_root_tree) {
 		if (fs_info->log_root_tree->node)
@@ -1347,6 +1372,7 @@ int close_ctree(struct btrfs_root *root)
 	free(fs_info->chunk_root);
 	free(fs_info->dev_root);
 	free(fs_info->csum_root);
+	free(fs_info->dedup_root);
 	free(fs_info);
 
 	return 0;
diff --git a/disk-io.h b/disk-io.h
index ff87958..e169b75 100644
--- a/disk-io.h
+++ b/disk-io.h
@@ -43,7 +43,7 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
 						   u64 bytenr, u32 blocksize);
 
 int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
-                        u32 stripesize, struct btrfs_root *root,
+                        u32 stripesize, u32 dedupsize, struct btrfs_root *root,
                         struct btrfs_fs_info *fs_info, u64 objectid);
 int clean_tree_block(struct btrfs_trans_handle *trans,
 		     struct btrfs_root *root, struct extent_buffer *buf);
diff --git a/find-root.c b/find-root.c
index 810d835..6d771c1 100644
--- a/find-root.c
+++ b/find-root.c
@@ -88,12 +88,14 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 	u32 leafsize;
 	u32 blocksize;
 	u32 stripesize;
+	u32 dedupsize;
 	u64 generation;
 	struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *chunk_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *dev_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_root *csum_root = malloc(sizeof(struct btrfs_root));
+	struct btrfs_root *dedup_root = malloc(sizeof(struct btrfs_root));
 	struct btrfs_fs_info *fs_info = malloc(sizeof(*fs_info));
 	int ret;
 	struct btrfs_super_block *disk_super;
@@ -122,6 +124,7 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 	fs_info->chunk_root = chunk_root;
 	fs_info->dev_root = dev_root;
 	fs_info->csum_root = csum_root;
+	fs_info->dedup_root = dedup_root;
 
 	fs_info->readonly = 1;
 
@@ -140,7 +143,7 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 	INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots);
 	INIT_LIST_HEAD(&fs_info->space_info);
 
-	__setup_root(4096, 4096, 4096, 4096, tree_root,
+	__setup_root(4096, 4096, 4096, 4096, 4096, tree_root,
 		     fs_info, BTRFS_ROOT_TREE_OBJECTID);
 
 	ret = btrfs_open_devices(fs_devices, O_RDONLY);
@@ -177,10 +180,12 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 	leafsize = btrfs_super_leafsize(disk_super);
 	sectorsize = btrfs_super_sectorsize(disk_super);
 	stripesize = btrfs_super_stripesize(disk_super);
+	dedupsize = btrfs_super_dedupsize(disk_super);
 	tree_root->nodesize = nodesize;
 	tree_root->leafsize = leafsize;
 	tree_root->sectorsize = sectorsize;
 	tree_root->stripesize = stripesize;
+	tree_root->dedupsize = dedupsize;
 
 	ret = btrfs_read_sys_array(tree_root);
 	if (ret)
@@ -189,7 +194,7 @@ static struct btrfs_root *open_ctree_broken(int fd, const char *device)
 				     btrfs_super_chunk_root_level(disk_super));
 	generation = btrfs_super_chunk_root_generation(disk_super);
 
-	__setup_root(nodesize, leafsize, sectorsize, stripesize,
+	__setup_root(nodesize, leafsize, sectorsize, stripesize, dedupsize,
 		     chunk_root, fs_info, BTRFS_CHUNK_TREE_OBJECTID);
 
 	chunk_root->node = read_tree_block(chunk_root,
@@ -228,6 +233,7 @@ out:
 	free(chunk_root);
 	free(dev_root);
 	free(csum_root);
+	free(dedup_root);
 	free(fs_info);
 	return NULL;
 }
diff --git a/ioctl.h b/ioctl.h
index e841913..6951571 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -528,6 +528,7 @@ struct btrfs_ioctl_clone_range_args {
 				      struct btrfs_ioctl_get_dev_stats)
 #define BTRFS_IOC_DEV_REPLACE _IOWR(BTRFS_IOCTL_MAGIC, 53, \
 				    struct btrfs_ioctl_dev_replace_args)
+#define BTRFS_IOC_DEDUP_TEST _IO(BTRFS_IOCTL_MAGIC, 54)
 
 #ifdef __cplusplus
 }
diff --git a/mkfs.c b/mkfs.c
index c8cb395..fbf986b 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -183,6 +183,11 @@ static int recow_roots(struct btrfs_trans_handle *trans,
 	BUG_ON(ret);
 	free_extent_buffer(tmp);
 
+	ret = __btrfs_cow_block(trans, info->dedup_root, info->dedup_root->node,
+				NULL, 0, &tmp, 0, 0);
+	BUG_ON(ret);
+	free_extent_buffer(tmp);
+
 	return 0;
 }
 
@@ -1350,7 +1355,7 @@ int main(int ac, char **av)
 	char *first_file;
 	u64 block_count = 0;
 	u64 dev_block_count = 0;
-	u64 blocks[7];
+	u64 blocks[8];
 	u64 alloc_start = 0;
 	u64 metadata_profile = 0;
 	u64 data_profile = 0;
@@ -1537,7 +1542,7 @@ int main(int ac, char **av)
 	}
 
 	blocks[0] = BTRFS_SUPER_INFO_OFFSET;
-	for (i = 1; i < 7; i++) {
+	for (i = 1; i < 8; i++) {
 		blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
 			leafsize * i;
 	}
diff --git a/print-tree.c b/print-tree.c
index c9e891b..7082b0a 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -461,6 +461,9 @@ static void print_key_type(u64 objectid, u8 type)
 	case BTRFS_EXTENT_CSUM_KEY:
 		printf("EXTENT_CSUM");
 		break;
+	case BTRFS_DEDUP_ITEM_KEY:
+		printk("DEDUP_ITEM");
+		break;
 	case BTRFS_EXTENT_DATA_KEY:
 		printf("EXTENT_DATA");
 		break;
@@ -734,6 +737,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
 		case BTRFS_EXTENT_CSUM_KEY:
 			printf("\t\textent csum item\n");
 			break;
+		case BTRFS_DEDUP_ITEM_KEY:
+			printf("\t\tdedup item \n");
+			break;
 		case BTRFS_EXTENT_DATA_KEY:
 			fi = btrfs_item_ptr(l, i,
 					    struct btrfs_file_extent_item);
diff --git a/utils.c b/utils.c
index 7b028a7..5625784 100644
--- a/utils.c
+++ b/utils.c
@@ -76,10 +76,11 @@ static u64 reference_root_table[] = {
 	[4] =	BTRFS_DEV_TREE_OBJECTID,
 	[5] =	BTRFS_FS_TREE_OBJECTID,
 	[6] =	BTRFS_CSUM_TREE_OBJECTID,
+	[7] =	BTRFS_DEDUP_TREE_OBJECTID,
 };
 
 int make_btrfs(int fd, const char *device, const char *label,
-	       u64 blocks[7], u64 num_bytes, u32 nodesize,
+	       u64 blocks[8], u64 num_bytes, u32 nodesize,
 	       u32 leafsize, u32 sectorsize, u32 stripesize)
 {
 	struct btrfs_super_block super;
@@ -119,7 +120,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	btrfs_set_super_root(&super, blocks[1]);
 	btrfs_set_super_chunk_root(&super, blocks[3]);
 	btrfs_set_super_total_bytes(&super, num_bytes);
-	btrfs_set_super_bytes_used(&super, 6 * leafsize);
+	btrfs_set_super_bytes_used(&super, 7 * leafsize);
 	btrfs_set_super_sectorsize(&super, sectorsize);
 	btrfs_set_super_leafsize(&super, leafsize);
 	btrfs_set_super_nodesize(&super, nodesize);
@@ -136,7 +137,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	memset(buf->data, 0, leafsize);
 	buf->len = leafsize;
 	btrfs_set_header_bytenr(buf, blocks[1]);
-	btrfs_set_header_nritems(buf, 4);
+	btrfs_set_header_nritems(buf, 5);
 	btrfs_set_header_generation(buf, 1);
 	btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV);
 	btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID);
@@ -211,6 +212,18 @@ int make_btrfs(int fd, const char *device, const char *label,
 			    sizeof(root_item));
 	nritems++;
 
+	itemoff = itemoff - sizeof(root_item);
+	btrfs_set_root_bytenr(&root_item, blocks[7]);
+	btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEDUP_TREE_OBJECTID);
+	btrfs_set_item_key(buf, &disk_key, nritems);
+	btrfs_set_item_offset(buf, btrfs_item_nr(buf, nritems), itemoff);
+	btrfs_set_item_size(buf, btrfs_item_nr(buf, nritems),
+			    sizeof(root_item));
+	write_extent_buffer(buf, &root_item,
+			    btrfs_item_ptr_offset(buf, nritems),
+			    sizeof(root_item));
+	nritems++;
+
 
 	csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
 	ret = pwrite(fd, buf->data, leafsize, blocks[1]);
@@ -221,7 +234,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 		leafsize-sizeof(struct btrfs_header));
 	nritems = 0;
 	itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize);
-	for (i = 1; i < 7; i++) {
+	for (i = 1; i < 8; i++) {
 		BUG_ON(blocks[i] < first_free);
 		BUG_ON(blocks[i] < blocks[i - 1]);
 
@@ -391,7 +404,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	ret = pwrite(fd, buf->data, leafsize, blocks[5]);
 	BUG_ON(ret != leafsize);
 
-	/* finally create the csum root */
+	/* create the csum root */
 	memset(buf->data+sizeof(struct btrfs_header), 0,
 		leafsize-sizeof(struct btrfs_header));
 	btrfs_set_header_bytenr(buf, blocks[6]);
@@ -401,6 +414,16 @@ int make_btrfs(int fd, const char *device, const char *label,
 	ret = pwrite(fd, buf->data, leafsize, blocks[6]);
 	BUG_ON(ret != leafsize);
 
+	/* finally create the dedup root */
+	memset(buf->data+sizeof(struct btrfs_header), 0,
+		leafsize-sizeof(struct btrfs_header));
+	btrfs_set_header_bytenr(buf, blocks[7]);
+	btrfs_set_header_owner(buf, BTRFS_DEDUP_TREE_OBJECTID);
+	btrfs_set_header_nritems(buf, 0);
+	csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0);
+	ret = pwrite(fd, buf->data, leafsize, blocks[7]);
+	BUG_ON(ret != leafsize);
+
 	/* and write out the super block */
 	BUG_ON(sizeof(super) > sectorsize);
 	memset(buf->data, 0, sectorsize);
-- 
1.8.1.4

