Btrfs: introduce subvol uuids and times
This patch introduces uuids for subvolumes. Each subvolume has it's own uuid. In case it was snapshotted, it also contains parent_uuid. In case it was received, it also contains received_uuid. It also introduces subvolume ctime/otime/stime/rtime. The first two are comparable to the times found in inodes. otime is the origin/creation time and ctime is the change time. stime/rtime are only valid on received subvolumes. stime is the time of the subvolume when it was sent. rtime is the time of the subvolume when it was received. Additionally to the times, we have a transid for each time. They are updated at the same place as the times. btrfs receive uses stransid and rtransid to find out if a received subvolume changed in the meantime. If an older kernel mounts a filesystem with the extented fields, all fields become invalid. The next mount with a new kernel will detect this and reset the fields. Signed-off-by: Alexander Block <ablock84@googlemail.com> Reviewed-by: David Sterba <dave@jikos.cz> Reviewed-by: Arne Jansen <sensille@gmx.net> Reviewed-by: Jan Schmidt <list.btrfs@jan-o-sch.net> Reviewed-by: Alex Lyakas <alex.bolshoy.btrfs@gmail.com>
This commit is contained in:
100
fs/btrfs/ioctl.c
100
fs/btrfs/ioctl.c
@@ -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);
|
||||
@@ -3395,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)
|
||||
{
|
||||
@@ -3477,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:
|
||||
|
Reference in New Issue
Block a user