Btrfs: introduce subvol uuids and times
[linux-2.6-block.git] / fs / btrfs / ioctl.c
index 0e92e5763005214b5d55d091d6fcfb94d73f4875..99fe2ce7f7216c7e56c651a5128a807f877218f8 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
+#include <linux/uuid.h>
 #include "compat.h"
 #include "ctree.h"
 #include "disk-io.h"
@@ -346,11 +347,13 @@ static noinline int create_subvol(struct btrfs_root *root,
        struct btrfs_root *new_root;
        struct dentry *parent = dentry->d_parent;
        struct inode *dir;
+       struct timespec cur_time = CURRENT_TIME;
        int ret;
        int err;
        u64 objectid;
        u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
        u64 index = 0;
+       uuid_le new_uuid;
 
        ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
        if (ret)
@@ -389,8 +392,9 @@ static noinline int create_subvol(struct btrfs_root *root,
                            BTRFS_UUID_SIZE);
        btrfs_mark_buffer_dirty(leaf);
 
+       memset(&root_item, 0, sizeof(root_item));
+
        inode_item = &root_item.inode;
-       memset(inode_item, 0, sizeof(*inode_item));
        inode_item->generation = cpu_to_le64(1);
        inode_item->size = cpu_to_le64(3);
        inode_item->nlink = cpu_to_le32(1);
@@ -408,8 +412,15 @@ static noinline int create_subvol(struct btrfs_root *root,
        btrfs_set_root_used(&root_item, leaf->len);
        btrfs_set_root_last_snapshot(&root_item, 0);
 
-       memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress));
-       root_item.drop_level = 0;
+       btrfs_set_root_generation_v2(&root_item,
+                       btrfs_root_generation(&root_item));
+       uuid_le_gen(&new_uuid);
+       memcpy(root_item.uuid, new_uuid.b, BTRFS_UUID_SIZE);
+       root_item.otime.sec = cpu_to_le64(cur_time.tv_sec);
+       root_item.otime.nsec = cpu_to_le64(cur_time.tv_nsec);
+       root_item.ctime = root_item.otime;
+       btrfs_set_root_ctransid(&root_item, trans->transid);
+       btrfs_set_root_otransid(&root_item, trans->transid);
 
        btrfs_tree_unlock(leaf);
        free_extent_buffer(leaf);
@@ -2340,6 +2351,10 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                goto out_drop_write;
        }
 
+       ret = -EXDEV;
+       if (src_file->f_path.mnt != file->f_path.mnt)
+               goto out_fput;
+
        src = src_file->f_dentry->d_inode;
 
        ret = -EINVAL;
@@ -2360,7 +2375,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                goto out_fput;
 
        ret = -EXDEV;
-       if (src->i_sb != inode->i_sb || BTRFS_I(src)->root != root)
+       if (src->i_sb != inode->i_sb)
                goto out_fput;
 
        ret = -ENOMEM;
@@ -2434,13 +2449,14 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
                 * note the key will change type as we walk through the
                 * tree.
                 */
-               ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+               ret = btrfs_search_slot(NULL, BTRFS_I(src)->root, &key, path,
+                               0, 0);
                if (ret < 0)
                        goto out;
 
                nritems = btrfs_header_nritems(path->nodes[0]);
                if (path->slots[0] >= nritems) {
-                       ret = btrfs_next_leaf(root, path);
+                       ret = btrfs_next_leaf(BTRFS_I(src)->root, path);
                        if (ret < 0)
                                goto out;
                        if (ret > 0)
@@ -3390,6 +3406,87 @@ out:
        return ret;
 }
 
+static long btrfs_ioctl_set_received_subvol(struct file *file,
+                                           void __user *arg)
+{
+       struct btrfs_ioctl_received_subvol_args *sa = NULL;
+       struct inode *inode = fdentry(file)->d_inode;
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_root_item *root_item = &root->root_item;
+       struct btrfs_trans_handle *trans;
+       struct timespec ct = CURRENT_TIME;
+       int ret = 0;
+
+       ret = mnt_want_write_file(file);
+       if (ret < 0)
+               return ret;
+
+       down_write(&root->fs_info->subvol_sem);
+
+       if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (btrfs_root_readonly(root)) {
+               ret = -EROFS;
+               goto out;
+       }
+
+       if (!inode_owner_or_capable(inode)) {
+               ret = -EACCES;
+               goto out;
+       }
+
+       sa = memdup_user(arg, sizeof(*sa));
+       if (IS_ERR(sa)) {
+               ret = PTR_ERR(sa);
+               sa = NULL;
+               goto out;
+       }
+
+       trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans)) {
+               ret = PTR_ERR(trans);
+               trans = NULL;
+               goto out;
+       }
+
+       sa->rtransid = trans->transid;
+       sa->rtime.sec = ct.tv_sec;
+       sa->rtime.nsec = ct.tv_nsec;
+
+       memcpy(root_item->received_uuid, sa->uuid, BTRFS_UUID_SIZE);
+       btrfs_set_root_stransid(root_item, sa->stransid);
+       btrfs_set_root_rtransid(root_item, sa->rtransid);
+       root_item->stime.sec = cpu_to_le64(sa->stime.sec);
+       root_item->stime.nsec = cpu_to_le32(sa->stime.nsec);
+       root_item->rtime.sec = cpu_to_le64(sa->rtime.sec);
+       root_item->rtime.nsec = cpu_to_le32(sa->rtime.nsec);
+
+       ret = btrfs_update_root(trans, root->fs_info->tree_root,
+                               &root->root_key, &root->root_item);
+       if (ret < 0) {
+               btrfs_end_transaction(trans, root);
+               trans = NULL;
+               goto out;
+       } else {
+               ret = btrfs_commit_transaction(trans, root);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = copy_to_user(arg, sa, sizeof(*sa));
+       if (ret)
+               ret = -EFAULT;
+
+out:
+       kfree(sa);
+       up_write(&root->fs_info->subvol_sem);
+       mnt_drop_write_file(file);
+       return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
 {
@@ -3472,6 +3569,8 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_balance_ctl(root, arg);
        case BTRFS_IOC_BALANCE_PROGRESS:
                return btrfs_ioctl_balance_progress(root, argp);
+       case BTRFS_IOC_SET_RECEIVED_SUBVOL:
+               return btrfs_ioctl_set_received_subvol(file, argp);
        case BTRFS_IOC_GET_DEV_STATS:
                return btrfs_ioctl_get_dev_stats(root, argp, 0);
        case BTRFS_IOC_GET_AND_RESET_DEV_STATS: