Btrfs: snapshot progress
authorChris Mason <chris.mason@oracle.com>
Tue, 10 Apr 2007 13:27:04 +0000 (09:27 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Tue, 10 Apr 2007 13:27:04 +0000 (09:27 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.h
fs/btrfs/dir-item.c
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/ioctl.h [new file with mode: 0644]
fs/btrfs/root-tree.c
fs/btrfs/super.c

index 5460030c9e6afa743cfd00eff19b6ea41cf725cf..2cbcaaeac9e55f8a9869315714f857374fedf27e 100644 (file)
@@ -227,7 +227,7 @@ struct btrfs_csum_item {
 } __attribute__ ((__packed__));
 
 struct btrfs_inode_map_item {
-       struct btrfs_disk_key key;
+       u32 refs;
 } __attribute__ ((__packed__));
 
 struct crypto_hash;
@@ -883,6 +883,17 @@ static inline void btrfs_set_file_extent_num_blocks(struct
        e->num_blocks = cpu_to_le64(val);
 }
 
+static inline u32 btrfs_inode_map_refs(struct btrfs_inode_map_item *m)
+{
+       return le32_to_cpu(m->refs);
+}
+
+static inline void btrfs_set_inode_map_refs(struct btrfs_inode_map_item *m,
+                                           u32 val)
+{
+       m->refs = cpu_to_le32(val);
+}
+
 static inline struct btrfs_root *btrfs_sb(struct super_block *sb)
 {
        return sb->s_fs_info;
@@ -925,6 +936,8 @@ static inline void btrfs_mark_buffer_dirty(struct buffer_head *bh)
        btrfs_item_offset((leaf)->items + (slot))))
 
 /* extent-item.c */
+int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root);
 struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
                                            struct btrfs_root *root);
 int btrfs_alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
index 7aed9f015b557c6691f0f29155a22fe41c86c0e5..0ba46bc0da9a41111aaee9f5f98b3e8ffa412b05 100644 (file)
@@ -55,12 +55,14 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
        btrfs_set_dir_flags(dir_item, 0);
        btrfs_set_dir_name_len(dir_item, name_len);
        name_ptr = (char *)(dir_item + 1);
+
+       btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
+       btrfs_mark_buffer_dirty(path->nodes[0]);
+
        /* FIXME, use some real flag for selecting the extra index */
        if (root == root->fs_info->tree_root)
                goto out;
 
-       btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
-       btrfs_mark_buffer_dirty(path->nodes[0]);
        btrfs_release_path(root, path);
 
        btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
index b557bdd1e26a44eff04cec1034e0eeb384413e85..6b097ede80b1329a5f9987620f3ecc8f771e8fef 100644 (file)
@@ -16,10 +16,6 @@ static int check_tree_block(struct btrfs_root *root, struct buffer_head *buf)
        if (buf->b_blocknr != btrfs_header_blocknr(&node->header)) {
                BUG();
        }
-       if (root->node && btrfs_header_parentid(&node->header) !=
-           btrfs_header_parentid(btrfs_buffer_header(root->node))) {
-               BUG();
-       }
        return 0;
 }
 
index 7c21f63f1b93321744679dec4436edc42c43dbf2..efc604eea0b00883e015511ac9e7b737ec2d92e8 100644 (file)
@@ -77,6 +77,12 @@ static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
        return 0;
 }
 
+int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
+                      struct btrfs_root *root)
+{
+       return inc_block_ref(trans, root, root->node->b_blocknr, 1);
+}
+
 int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
                  struct buffer_head *buf)
 {
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
new file mode 100644 (file)
index 0000000..201fb32
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __IOCTL_
+#define __IOCTL_
+#include <linux/ioctl.h>
+
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_VOL_NAME_MAX 255
+struct btrfs_ioctl_vol_args {
+       char name[BTRFS_VOL_NAME_MAX + 1];
+};
+
+#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
+                                  struct btrfs_ioctl_vol_args)
+#endif
index ddc1c13a53521c6fc51db692a6c8b1ac9c8dd656..72be9836932f3948e621857c6fda1538d010b2a2 100644 (file)
@@ -83,6 +83,8 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 {
        struct btrfs_path *path;
        int ret;
+       u32 refs;
+       struct btrfs_root_item *ri;
 
        path = btrfs_alloc_path();
        BUG_ON(!path);
@@ -91,7 +93,19 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
        if (ret < 0)
                goto out;
        BUG_ON(ret != 0);
-       ret = btrfs_del_item(trans, root, path);
+       ri = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]),
+                           path->slots[0], struct btrfs_root_item);
+
+       refs = btrfs_root_refs(ri);
+       BUG_ON(refs == 0);
+       if (refs == 1) {
+               ret = btrfs_del_item(trans, root, path);
+printk("deleting root %Lu %Lu %u\n", key->objectid, key->offset, key->flags);
+       } else {
+               btrfs_set_root_refs(ri, refs - 1);
+printk("ref now %u root %Lu %Lu %u\n", refs -1, key->objectid, key->offset, key->flags);
+               mark_buffer_dirty(path->nodes[0]);
+       }
 out:
        btrfs_release_path(root, path);
        btrfs_free_path(path);
index 3c9236ca889c0aca801dc0213696b78fd5aba2bf..bbe5cabcb42e875394d1ea8d6eacf6e9c1e8eff8 100644 (file)
@@ -15,6 +15,7 @@
 #include "disk-io.h"
 #include "transaction.h"
 #include "btrfs_inode.h"
+#include "ioctl.h"
 
 void btrfs_fsinfo_release(struct kobject *obj)
 {
@@ -27,6 +28,11 @@ struct kobj_type btrfs_fsinfo_ktype = {
        .release = btrfs_fsinfo_release,
 };
 
+struct btrfs_iget_args {
+       u64 ino;
+       struct btrfs_root *root;
+};
+
 decl_subsys(btrfs, &btrfs_fsinfo_ktype, NULL);
 
 #define BTRFS_SUPER_MAGIC 0x9123682E
@@ -461,6 +467,34 @@ int fixup_tree_root_location(struct btrfs_root *root,
        return 0;
 }
 
+int btrfs_init_locked_inode(struct inode *inode, void *p)
+{
+       struct btrfs_iget_args *args = p;
+       inode->i_ino = args->ino;
+       BTRFS_I(inode)->root = args->root;
+       return 0;
+}
+
+int btrfs_find_actor(struct inode *inode, void *opaque)
+{
+       struct btrfs_iget_args *args = opaque;
+       return (args->ino == inode->i_ino &&
+               args->root == BTRFS_I(inode)->root);
+}
+
+struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
+                               struct btrfs_root *root)
+{
+       struct inode *inode;
+       struct btrfs_iget_args args;
+       args.ino = objectid;
+       args.root = root;
+
+       inode = iget5_locked(s, objectid, btrfs_find_actor,
+                            btrfs_init_locked_inode,
+                            (void *)&args);
+       return inode;
+}
 
 static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
                                   struct nameidata *nd)
@@ -486,7 +520,8 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
                        return ERR_PTR(ret);
                if (ret > 0)
                        return ERR_PTR(-ENOENT);
-               inode = iget_locked(dir->i_sb, location.objectid);
+               inode = btrfs_iget_locked(dir->i_sb, location.objectid,
+                                         sub_root);
                if (!inode)
                        return ERR_PTR(-EACCES);
                if (inode->i_state & I_NEW) {
@@ -495,7 +530,7 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
                                                &root->fs_info->fs_roots_radix,
                                                (unsigned long)sub_root,
                                                sub_root);
-printk("adding new root for inode %lu\n", inode->i_ino);
+printk("adding new root for inode %lu root %p (found %p)\n", inode->i_ino, sub_root, BTRFS_I(inode)->root);
                                igrab(inode);
                                sub_root->inode = inode;
                        }
@@ -630,7 +665,8 @@ static int btrfs_fill_super(struct super_block * sb, void * data, int silent)
               btrfs_super_total_blocks(disk_super),
               btrfs_super_root_dir(disk_super));
 
-       inode = iget_locked(sb, btrfs_super_root_dir(disk_super));
+       inode = btrfs_iget_locked(sb, btrfs_super_root_dir(disk_super),
+                                 tree_root);
        bi = BTRFS_I(inode);
        bi->location.objectid = inode->i_ino;
        bi->location.offset = 0;
@@ -750,7 +786,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        inode->i_mode = mode;
        inode->i_ino = objectid;
        inode->i_blocks = 0;
-       inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
+       inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        fill_inode_item(&inode_item, inode);
 
        key->objectid = objectid;
@@ -1650,6 +1686,95 @@ static ssize_t btrfs_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
        return retval;
 }
 
+static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_key key;
+       struct btrfs_root_item new_root_item;
+       int ret;
+       u64 objectid;
+
+       mutex_lock(&root->fs_info->fs_mutex);
+       trans = btrfs_start_transaction(root, 1);
+       BUG_ON(!trans);
+
+       ret = btrfs_update_inode(trans, root, root->inode);
+       BUG_ON(ret);
+
+       ret = btrfs_find_free_objectid(trans, root, 0, &objectid);
+       BUG_ON(ret);
+
+       memset(&new_root_item, 0, sizeof(new_root_item));
+       memcpy(&new_root_item, &root->root_item,
+              sizeof(new_root_item));
+
+       key.objectid = objectid;
+       key.flags = 0;
+       key.offset = 0;
+       btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
+       ret = btrfs_insert_inode_map(trans, root, objectid, &key);
+       BUG_ON(ret);
+
+       key.objectid = objectid;
+       key.offset = 1;
+       key.flags = 0;
+       btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
+       btrfs_set_root_blocknr(&new_root_item, root->node->b_blocknr);
+
+       ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
+                               &new_root_item);
+       BUG_ON(ret);
+
+printk("adding snapshot name %.*s root %Lu %Lu %u\n", namelen, name, key.objectid, key.offset, key.flags);
+
+       /*
+        * insert the directory item
+        */
+       key.offset = (u64)-1;
+       ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root,
+                                   name, namelen,
+                                   root->fs_info->sb->s_root->d_inode->i_ino,
+                                   &key, 0);
+
+       BUG_ON(ret);
+
+       ret = btrfs_inc_root_ref(trans, root);
+       BUG_ON(ret);
+
+       ret = btrfs_commit_transaction(trans, root);
+       BUG_ON(ret);
+       mutex_unlock(&root->fs_info->fs_mutex);
+       return 0;
+}
+
+static int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
+                      cmd, unsigned long arg)
+{
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_ioctl_vol_args vol_args;
+       int ret;
+       int namelen;
+
+       if (!root->ref_cows)
+               return -EINVAL;
+       switch (cmd) {
+       case BTRFS_IOC_SNAP_CREATE:
+               if (copy_from_user(&vol_args,
+                                  (struct btrfs_ioctl_vol_args __user *)arg,
+                                  sizeof(vol_args)))
+                       return -EFAULT;
+               namelen = strlen(vol_args.name);
+               if (namelen > BTRFS_VOL_NAME_MAX)
+                       return -EINVAL;
+               ret = create_snapshot(root, vol_args.name, namelen);
+               WARN_ON(ret);
+               break;
+       default:
+               return -ENOTTY;
+       }
+       return 0;
+}
+
 static struct kmem_cache *btrfs_inode_cachep;
 struct kmem_cache *btrfs_trans_handle_cachep;
 struct kmem_cache *btrfs_transaction_cachep;
@@ -1781,6 +1906,7 @@ static struct file_operations btrfs_dir_file_operations = {
        .llseek         = generic_file_llseek,
        .read           = generic_read_dir,
        .readdir        = btrfs_readdir,
+       .ioctl          = btrfs_ioctl,
 };
 
 static struct address_space_operations btrfs_aops = {
@@ -1803,6 +1929,7 @@ static struct file_operations btrfs_file_operations = {
        .write          = btrfs_file_write,
        .mmap           = generic_file_mmap,
        .open           = generic_file_open,
+       .ioctl          = btrfs_ioctl,
 };
 
 static int __init init_btrfs_fs(void)