Btrfs: Better block record keeping, real mkfs

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 636c63e..0720169 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -2,7 +2,7 @@
 CFLAGS = -g -Wall -Werror
 headers = radix-tree.h ctree.h disk-io.h kerncompat.h print-tree.h list.h \
 	  transaction.h
-objects = ctree.o disk-io.o radix-tree.o mkfs.o extent-tree.o print-tree.o \
+objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
 	  root-tree.o dir-item.o hash.o file-item.o inode-item.o \
 	  inode-map.o \
 
@@ -16,7 +16,10 @@
 	$(check) $<
 	$(CC) $(CFLAGS) -c $<
 
-all: tester debug-tree quick-test dir-test tags
+all: tester debug-tree quick-test dir-test tags mkfs.btrfs
+
+mkfs.btrfs: $(objects) mkfs.o
+	gcc $(CFLAGS) -o mkfs.btrfs $(objects) mkfs.o
 
 debug-tree: $(objects) debug-tree.o
 	gcc $(CFLAGS) -o debug-tree $(objects) debug-tree.o
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index b30b2ce..1a4d1d6 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -223,6 +223,7 @@
 	int cache_size;
 	int fp;
 	struct btrfs_trans_handle *running_transaction;
+	struct btrfs_super_block *disk_super;
 };
 
 /*
diff --git a/fs/btrfs/debug-tree.c b/fs/btrfs/debug-tree.c
index d9f36ef..fd07969 100644
--- a/fs/btrfs/debug-tree.c
+++ b/fs/btrfs/debug-tree.c
@@ -10,8 +10,17 @@
 int main(int ac, char **av) {
 	struct btrfs_super_block super;
 	struct btrfs_root *root;
+
+	if (ac != 2) {
+		fprintf(stderr, "usage: %s device\n", av[0]);
+		exit(1);
+	}
 	radix_tree_init();
-	root = open_ctree("dbfile", &super);
+	root = open_ctree(av[1], &super);
+	if (!root) {
+		fprintf(stderr, "unable to open %s\n", av[1]);
+		exit(1);
+	}
 	printf("fs tree\n");
 	btrfs_print_tree(root, root->node);
 	printf("map tree\n");
@@ -23,5 +32,7 @@
 	printf("root tree\n");
 	btrfs_print_tree(root->fs_info->tree_root,
 			 root->fs_info->tree_root->node);
+	printf("total blocks %Lu\n", btrfs_super_total_blocks(&super));
+	printf("blocks used %Lu\n", btrfs_super_blocks_used(&super));
 	return 0;
 }
diff --git a/fs/btrfs/dir-test.c b/fs/btrfs/dir-test.c
index 8fc77c8..b673982a 100644
--- a/fs/btrfs/dir-test.c
+++ b/fs/btrfs/dir-test.c
@@ -425,8 +425,6 @@
 	struct btrfs_trans_handle *trans;
 	radix_tree_init();
 
-	printf("removing old tree\n");
-	unlink("dbfile");
 	root = open_ctree("dbfile", &super);
 	trans = btrfs_start_transaction(root, 1);
 
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index bacaa38..0322c55 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -293,20 +293,15 @@
 	fs_info->inode_root = inode_root;
 	fs_info->last_inode_alloc = 0;
 	fs_info->last_inode_alloc_dirid = 0;
+	fs_info->disk_super = super;
 	memset(&fs_info->current_insert, 0, sizeof(fs_info->current_insert));
 	memset(&fs_info->last_insert, 0, sizeof(fs_info->last_insert));
 
 	ret = pread(fp, super, sizeof(struct btrfs_super_block),
 		     BTRFS_SUPER_INFO_OFFSET);
 	if (ret == 0 || btrfs_super_root(super) == 0) {
-		printf("making new FS!\n");
-		ret = mkfs(fp, 0, 1024);
-		if (ret)
-			return NULL;
-		ret = pread(fp, super, sizeof(struct btrfs_super_block),
-			     BTRFS_SUPER_INFO_OFFSET);
-		if (ret != sizeof(struct btrfs_super_block))
-			return NULL;
+		BUG();
+		return NULL;
 	}
 	BUG_ON(ret < 0);
 
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 09eeeb4..9bc4ad3 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -134,6 +134,8 @@
 	struct btrfs_extent_item extent_item;
 	int i;
 	int ret;
+	u64 super_blocks_used;
+	struct btrfs_fs_info *info = extent_root->fs_info;
 
 	btrfs_set_extent_refs(&extent_item, 1);
 	btrfs_set_extent_owner(&extent_item,
@@ -145,6 +147,9 @@
 	for (i = 0; i < extent_root->fs_info->current_insert.flags; i++) {
 		ins.objectid = extent_root->fs_info->current_insert.objectid +
 				i;
+		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
+		btrfs_set_super_blocks_used(info->disk_super,
+					    super_blocks_used + 1);
 		ret = btrfs_insert_item(trans, extent_root, &ins, &extent_item,
 					sizeof(extent_item));
 		BUG_ON(ret);
@@ -161,7 +166,8 @@
 {
 	struct btrfs_path path;
 	struct btrfs_key key;
-	struct btrfs_root *extent_root = root->fs_info->extent_root;
+	struct btrfs_fs_info *info = root->fs_info;
+	struct btrfs_root *extent_root = info->extent_root;
 	int ret;
 	struct btrfs_extent_item *ei;
 	struct btrfs_key ins;
@@ -188,15 +194,18 @@
 	refs = btrfs_extent_refs(ei) - 1;
 	btrfs_set_extent_refs(ei, refs);
 	if (refs == 0) {
+		u64 super_blocks_used;
 		if (pin) {
 			int err;
 			radix_tree_preload(GFP_KERNEL);
-			err = radix_tree_insert(
-					&extent_root->fs_info->pinned_radix,
-					blocknr, (void *)blocknr);
+			err = radix_tree_insert(&info->pinned_radix,
+						blocknr, (void *)blocknr);
 			BUG_ON(err);
 			radix_tree_preload_end();
 		}
+		super_blocks_used = btrfs_super_blocks_used(info->disk_super);
+		btrfs_set_super_blocks_used(info->disk_super,
+					    super_blocks_used - num_blocks);
 		ret = btrfs_del_item(trans, extent_root, &path);
 		if (!pin && extent_root->fs_info->last_insert.objectid >
 		    blocknr)
@@ -392,7 +401,9 @@
 {
 	int ret;
 	int pending_ret;
-	struct btrfs_root *extent_root = root->fs_info->extent_root;
+	u64 super_blocks_used;
+	struct btrfs_fs_info *info = root->fs_info;
+	struct btrfs_root *extent_root = info->extent_root;
 	struct btrfs_extent_item extent_item;
 
 	btrfs_set_extent_refs(&extent_item, 1);
@@ -413,6 +424,9 @@
 	if (ret)
 		return ret;
 
+	super_blocks_used = btrfs_super_blocks_used(info->disk_super);
+	btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
+				    num_blocks);
 	ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
 				sizeof(extent_item));
 
diff --git a/fs/btrfs/mkfs.c b/fs/btrfs/mkfs.c
index 1cac5ab..f7efc8a 100644
--- a/fs/btrfs/mkfs.c
+++ b/fs/btrfs/mkfs.c
@@ -1,4 +1,8 @@
 #define _XOPEN_SOURCE 500
+#ifndef __CHECKER__
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -10,6 +14,17 @@
 #include "ctree.h"
 #include "disk-io.h"
 
+#ifdef __CHECKER__
+#define BLKGETSIZE64 0
+static inline int ioctl(int fd, int define, u64 *size) { return 0; }
+#endif
+
+#if 0
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#   define BLKGETSIZE64 _IOR(0x12, 114, __u64)
+#endif
+#endif
+
 int mkfs(int fd, u64 num_blocks, u32 blocksize)
 {
 	struct btrfs_super_block super;
@@ -27,7 +42,7 @@
 	strcpy((char *)(&super.magic), BTRFS_MAGIC);
 	btrfs_set_super_blocksize(&super, blocksize);
 	btrfs_set_super_total_blocks(&super, num_blocks);
-	btrfs_set_super_blocks_used(&super, 0);
+	btrfs_set_super_blocks_used(&super, start_block + 5);
 
 	block = malloc(blocksize);
 	memset(block, 0, blocksize);
@@ -160,3 +175,81 @@
 		return -1;
 	return 0;
 }
+
+u64 device_size(int fd, struct stat *st)
+{
+	u64 size;
+	if (S_ISREG(st->st_mode)) {
+		return st->st_size;
+	}
+	if (!S_ISBLK(st->st_mode)) {
+		return 0;
+	}
+	if (ioctl(fd, BLKGETSIZE64, &size) >= 0) {
+		return size;
+	}
+	return 0;
+}
+
+int main(int ac, char **av)
+{
+	char *file;
+	u64 block_count = 0;
+	int fd;
+	struct stat st;
+	int ret;
+	int i;
+	char *buf = malloc(4096);
+	if (ac >= 2) {
+		file = av[1];
+		if (ac == 3) {
+			block_count = atoi(av[2]);
+			if (!block_count) {
+				fprintf(stderr, "error finding block count\n");
+				exit(1);
+			}
+		}
+	} else {
+		fprintf(stderr, "usage: mkfs.btrfs file [block count]\n");
+		exit(1);
+	}
+	fd = open(file, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "unable to open %s\n", file);
+		exit(1);
+	}
+	ret = fstat(fd, &st);
+	if (ret < 0) {
+		fprintf(stderr, "unable to stat %s\n", file);
+		exit(1);
+	}
+	if (block_count == 0) {
+		block_count = device_size(fd, &st);
+		if (block_count == 0) {
+			fprintf(stderr, "unable to find %s size\n", file);
+			exit(1);
+		}
+	}
+	block_count /= 4096;
+	if (block_count < 256) {
+		fprintf(stderr, "device %s is too small\n", file);
+		exit(1);
+	}
+	memset(buf, 0, 4096);
+	for(i = 0; i < 6; i++) {
+		ret = write(fd, buf, 4096);
+		if (ret != 4096) {
+			fprintf(stderr, "unable to zero fill device\n");
+			exit(1);
+		}
+	}
+	ret = mkfs(fd, block_count, 4096);
+	if (ret) {
+		fprintf(stderr, "error during mkfs %d\n", ret);
+		exit(1);
+	}
+	printf("fs created on %s blocksize %d blocks %Lu\n",
+	       file, 4096, block_count);
+	return 0;
+}
+