Mountable btrfs, with readdir

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 4ae7604..ccc056a 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1,4 +1,5 @@
 #include <linux/module.h>
+#include <linux/buffer_head.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
 #include <linux/highmem.h>
@@ -8,13 +9,18 @@
 #include <linux/smp_lock.h>
 #include <linux/backing-dev.h>
 #include "ctree.h"
+#include "disk-io.h"
 
 #define BTRFS_SUPER_MAGIC 0x9123682E
+
+static struct inode_operations btrfs_dir_inode_operations;
+static struct super_operations btrfs_super_ops;
+static struct file_operations btrfs_dir_file_operations;
+
 #if 0
 /* some random number */
 
 static struct super_operations ramfs_ops;
-static struct inode_operations ramfs_dir_inode_operations;
 
 static struct backing_dev_info ramfs_backing_dev_info = {
 	.ra_pages	= 0,	/* No readahead */
@@ -129,46 +135,243 @@
 };
 #endif
 
-struct inode *btrfs_get_inode(struct super_block *sb, int mode, dev_t dev)
+static void btrfs_read_locked_inode(struct inode *inode)
 {
-	struct inode * inode = new_inode(sb);
-
-	if (inode) {
-		inode->i_mode = mode;
-		inode->i_uid = current->fsuid;
-		inode->i_gid = current->fsgid;
-		inode->i_blocks = 0;
-		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	struct btrfs_path path;
+	struct btrfs_inode_item *inode_item;
+	struct btrfs_root *root = btrfs_sb(inode->i_sb);
+	int ret;
+printk("read locked inode %lu\n", inode->i_ino);
+	btrfs_init_path(&path);
+	ret = btrfs_lookup_inode(NULL, root, &path, inode->i_ino, 0);
+	if (ret) {
+		make_bad_inode(inode);
+		return;
 	}
-	return inode;
+	inode_item = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]),
+				  path.slots[0],
+				  struct btrfs_inode_item);
+
+printk("found locked inode %lu\n", inode->i_ino);
+	inode->i_mode = btrfs_inode_mode(inode_item);
+	inode->i_nlink = btrfs_inode_nlink(inode_item);
+	inode->i_uid = btrfs_inode_uid(inode_item);
+	inode->i_gid = btrfs_inode_gid(inode_item);
+	inode->i_size = btrfs_inode_size(inode_item);
+	inode->i_atime.tv_sec = btrfs_timespec_sec(&inode_item->atime);
+	inode->i_atime.tv_nsec = btrfs_timespec_nsec(&inode_item->atime);
+	inode->i_mtime.tv_sec = btrfs_timespec_sec(&inode_item->mtime);
+	inode->i_mtime.tv_nsec = btrfs_timespec_nsec(&inode_item->mtime);
+	inode->i_ctime.tv_sec = btrfs_timespec_sec(&inode_item->ctime);
+	inode->i_ctime.tv_nsec = btrfs_timespec_nsec(&inode_item->ctime);
+	inode->i_blocks = btrfs_inode_nblocks(inode_item);
+	inode->i_generation = btrfs_inode_generation(inode_item);
+printk("about to release\n");
+	btrfs_release_path(root, &path);
+	switch (inode->i_mode & S_IFMT) {
+#if 0
+	default:
+		init_special_inode(inode, inode->i_mode,
+				   btrfs_inode_rdev(inode_item));
+		break;
+#endif
+	case S_IFREG:
+printk("inode %lu now a file\n", inode->i_ino);
+		break;
+	case S_IFDIR:
+printk("inode %lu now a directory\n", inode->i_ino);
+		inode->i_op = &btrfs_dir_inode_operations;
+		inode->i_fop = &btrfs_dir_file_operations;
+		break;
+	case S_IFLNK:
+printk("inode %lu now a link\n", inode->i_ino);
+		// inode->i_op = &page_symlink_inode_operations;
+		break;
+	}
+printk("returning!\n");
+	return;
 }
 
-static struct super_operations btrfs_ops = {
-	.statfs		= simple_statfs,
-	.drop_inode	= generic_delete_inode,
-};
+static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
+			      ino_t *ino)
+{
+	const char *name = dentry->d_name.name;
+	int namelen = dentry->d_name.len;
+	struct btrfs_dir_item *di;
+	struct btrfs_path path;
+	struct btrfs_root *root = btrfs_sb(dir->i_sb);
+	int ret;
+
+	btrfs_init_path(&path);
+	ret = btrfs_lookup_dir_item(NULL, root, &path, dir->i_ino, name,
+				    namelen, 0);
+	if (ret) {
+		*ino = 0;
+		goto out;
+	}
+	di = btrfs_item_ptr(btrfs_buffer_leaf(path.nodes[0]), path.slots[0],
+			    struct btrfs_dir_item);
+	*ino = btrfs_dir_objectid(di);
+out:
+	btrfs_release_path(root, &path);
+	return ret;
+}
+
+static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
+				   struct nameidata *nd)
+{
+	struct inode * inode;
+	ino_t ino;
+	int ret;
+
+	if (dentry->d_name.len > BTRFS_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	ret = btrfs_inode_by_name(dir, dentry, &ino);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	inode = NULL;
+	if (ino) {
+printk("lookup on %.*s returns %lu\n", dentry->d_name.len, dentry->d_name.name, ino);
+		inode = iget(dir->i_sb, ino);
+		if (!inode)
+			return ERR_PTR(-EACCES);
+	}
+	return d_splice_alias(inode, dentry);
+}
+
+static int btrfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_path.dentry->d_inode;
+	struct btrfs_root *root = btrfs_sb(inode->i_sb);
+	struct btrfs_item *item;
+	struct btrfs_dir_item *di;
+	struct btrfs_key key;
+	struct btrfs_path path;
+	int ret;
+	u32 nritems;
+	struct btrfs_leaf *leaf;
+	int slot;
+	int advance;
+	unsigned char d_type = DT_UNKNOWN;
+	int over;
+
+	key.objectid = inode->i_ino;
+printk("readdir on dir %Lu pos %Lu\n", key.objectid, filp->f_pos);
+	key.flags = 0;
+	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
+	key.offset = filp->f_pos;
+	btrfs_init_path(&path);
+	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+	if (ret < 0) {
+		goto err;
+	}
+printk("first ret %d\n", ret);
+	advance = filp->f_pos > 0 && ret != 0;
+	while(1) {
+		leaf = btrfs_buffer_leaf(path.nodes[0]);
+		nritems = btrfs_header_nritems(&leaf->header);
+		slot = path.slots[0];
+printk("leaf %Lu nritems %lu slot %d\n", path.nodes[0]->b_blocknr, nritems, slot);
+		if (advance) {
+printk("advancing!\n");
+			if (slot == nritems -1) {
+				ret = btrfs_next_leaf(root, &path);
+				if (ret)
+					break;
+				leaf = btrfs_buffer_leaf(path.nodes[0]);
+				nritems = btrfs_header_nritems(&leaf->header);
+				slot = path.slots[0];
+printk("2leaf %Lu nritems %lu slot %d\n", path.nodes[0]->b_blocknr, nritems, slot);
+			} else {
+				slot++;
+				path.slots[0]++;
+			}
+		}
+		advance = 1;
+		item = leaf->items + slot;
+printk("item key %Lu %u %Lu\n", btrfs_disk_key_objectid(&item->key),
+       btrfs_disk_key_flags(&item->key), btrfs_disk_key_offset(&item->key));
+		if (btrfs_disk_key_objectid(&item->key) != key.objectid)
+			break;
+		if (btrfs_disk_key_type(&item->key) != BTRFS_DIR_ITEM_KEY)
+			continue;
+		di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item);
+printk("filldir name %.*s, objectid %Lu\n", btrfs_dir_name_len(di),
+       (const char *)(di + 1), btrfs_dir_objectid(di));
+		over = filldir(dirent, (const char *)(di + 1),
+			       btrfs_dir_name_len(di),
+			       btrfs_disk_key_offset(&item->key),
+			       btrfs_dir_objectid(di), d_type);
+		if (over)
+			break;
+		filp->f_pos = btrfs_disk_key_offset(&item->key) + 1;
+	}
+printk("filldir all done\n");
+	ret = 0;
+err:
+	btrfs_release_path(root, &path);
+	return ret;
+}
+
+static void btrfs_put_super (struct super_block * sb)
+{
+	struct btrfs_root *root = btrfs_sb(sb);
+	int ret;
+
+	ret = close_ctree(root);
+	if (ret) {
+		printk("close ctree returns %d\n", ret);
+	}
+	sb->s_fs_info = NULL;
+}
 
 static int btrfs_fill_super(struct super_block * sb, void * data, int silent)
 {
 	struct inode * inode;
-	struct dentry * root;
+	struct dentry * root_dentry;
+	struct btrfs_super_block *disk_super;
+	struct buffer_head *bh;
+	struct btrfs_root *root;
 
 	sb->s_maxbytes = MAX_LFS_FILESIZE;
 	sb->s_blocksize = PAGE_CACHE_SIZE;
 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
 	sb->s_magic = BTRFS_SUPER_MAGIC;
-	sb->s_op = &btrfs_ops;
+	sb->s_op = &btrfs_super_ops;
 	sb->s_time_gran = 1;
-	inode = btrfs_get_inode(sb, S_IFDIR | 0755, 0);
+
+	bh = sb_bread(sb, BTRFS_SUPER_INFO_OFFSET / sb->s_blocksize);
+	if (!bh) {
+		printk("btrfs: unable to read on disk super\n");
+		return -EIO;
+	}
+	disk_super = (struct btrfs_super_block *)bh->b_data;
+	root = open_ctree(sb, bh, disk_super);
+	sb->s_fs_info = root;
+	if (!root) {
+		printk("btrfs: open_ctree failed\n");
+		return -EIO;
+	}
+	printk("read in super total blocks %Lu root %Lu\n",
+	       btrfs_super_total_blocks(disk_super),
+	       btrfs_super_root_dir(disk_super));
+
+	inode = iget_locked(sb, btrfs_super_root_dir(disk_super));
 	if (!inode)
 		return -ENOMEM;
+	if (inode->i_state & I_NEW) {
+		btrfs_read_locked_inode(inode);
+		unlock_new_inode(inode);
+	}
 
-	root = d_alloc_root(inode);
-	if (!root) {
+	root_dentry = d_alloc_root(inode);
+	if (!root_dentry) {
 		iput(inode);
 		return -ENOMEM;
 	}
-	sb->s_root = root;
+	sb->s_root = root_dentry;
+
 	return 0;
 }
 
@@ -187,6 +390,24 @@
 	.fs_flags	= FS_REQUIRES_DEV,
 };
 
+static struct super_operations btrfs_super_ops = {
+	.statfs		= simple_statfs,
+	.drop_inode	= generic_delete_inode,
+	.put_super	= btrfs_put_super,
+	.read_inode	= btrfs_read_locked_inode,
+};
+
+static struct inode_operations btrfs_dir_inode_operations = {
+	.lookup		= btrfs_lookup,
+};
+
+static struct file_operations btrfs_dir_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_read_dir,
+	.readdir	= btrfs_readdir,
+};
+
+
 static int __init init_btrfs_fs(void)
 {
 	printk("btrfs loaded!\n");