Merge branch 'send-v2' of git://github.com/ablock84/linux-btrfs into for-linus
This is the kernel portion of btrfs send/receive Conflicts: fs/btrfs/Makefile fs/btrfs/backref.h fs/btrfs/ctree.c fs/btrfs/ioctl.c fs/btrfs/ioctl.h Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
commit
113c1cb530
14 changed files with 5483 additions and 40 deletions
|
@ -8,7 +8,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
|
|||
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
|
||||
export.o tree-log.o free-space-cache.o zlib.o lzo.o \
|
||||
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
|
||||
reada.o backref.o ulist.o qgroup.o
|
||||
reada.o backref.o ulist.o qgroup.o send.o
|
||||
|
||||
btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
|
||||
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
|
||||
|
|
|
@ -1122,10 +1122,10 @@ static int inode_ref_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
|
|||
* required for the path to fit into the buffer. in that case, the returned
|
||||
* value will be smaller than dest. callers must check this!
|
||||
*/
|
||||
static char *iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
struct btrfs_inode_ref *iref,
|
||||
struct extent_buffer *eb_in, u64 parent,
|
||||
char *dest, u32 size)
|
||||
char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
struct btrfs_inode_ref *iref,
|
||||
struct extent_buffer *eb_in, u64 parent,
|
||||
char *dest, u32 size)
|
||||
{
|
||||
u32 len;
|
||||
int slot;
|
||||
|
@ -1531,7 +1531,7 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
|
|||
ipath->fspath->bytes_left - s_ptr : 0;
|
||||
|
||||
fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
|
||||
fspath = iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb,
|
||||
fspath = btrfs_iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb,
|
||||
inum, fspath_min, bytes_left);
|
||||
if (IS_ERR(fspath))
|
||||
return PTR_ERR(fspath);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "ioctl.h"
|
||||
#include "ulist.h"
|
||||
#include "extent_io.h"
|
||||
|
||||
#define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0)
|
||||
|
||||
|
@ -59,6 +60,9 @@ int paths_from_inode(u64 inum, struct inode_fs_paths *ipath);
|
|||
int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_fs_info *fs_info, u64 bytenr,
|
||||
u64 time_seq, struct ulist **roots);
|
||||
char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
|
||||
struct btrfs_inode_ref *iref, struct extent_buffer *eb,
|
||||
u64 parent, char *dest, u32 size);
|
||||
|
||||
struct btrfs_data_container *init_data_container(u32 total_bytes);
|
||||
struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root,
|
||||
|
|
|
@ -1032,6 +1032,7 @@ static int btrfsic_process_metablock(
|
|||
struct btrfs_disk_key *disk_key;
|
||||
u8 type;
|
||||
u32 item_offset;
|
||||
u32 item_size;
|
||||
|
||||
if (disk_item_offset + sizeof(struct btrfs_item) >
|
||||
sf->block_ctx->len) {
|
||||
|
@ -1047,6 +1048,7 @@ static int btrfsic_process_metablock(
|
|||
disk_item_offset,
|
||||
sizeof(struct btrfs_item));
|
||||
item_offset = le32_to_cpu(disk_item.offset);
|
||||
item_size = le32_to_cpu(disk_item.size);
|
||||
disk_key = &disk_item.key;
|
||||
type = disk_key->type;
|
||||
|
||||
|
@ -1057,14 +1059,13 @@ static int btrfsic_process_metablock(
|
|||
|
||||
root_item_offset = item_offset +
|
||||
offsetof(struct btrfs_leaf, items);
|
||||
if (root_item_offset +
|
||||
sizeof(struct btrfs_root_item) >
|
||||
if (root_item_offset + item_size >
|
||||
sf->block_ctx->len)
|
||||
goto leaf_item_out_of_bounce_error;
|
||||
btrfsic_read_from_block_data(
|
||||
sf->block_ctx, &root_item,
|
||||
root_item_offset,
|
||||
sizeof(struct btrfs_root_item));
|
||||
item_size);
|
||||
next_bytenr = le64_to_cpu(root_item.bytenr);
|
||||
|
||||
sf->error =
|
||||
|
|
459
fs/btrfs/ctree.c
459
fs/btrfs/ctree.c
|
@ -2837,23 +2837,25 @@ int btrfs_search_slot_for_read(struct btrfs_root *root,
|
|||
goto again;
|
||||
}
|
||||
} else {
|
||||
if (p->slots[0] >= btrfs_header_nritems(leaf)) {
|
||||
/* we're sitting on an invalid slot */
|
||||
if (p->slots[0] == 0) {
|
||||
ret = btrfs_prev_leaf(root, p);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
if (!return_any)
|
||||
return 1;
|
||||
/*
|
||||
* no lower item found, return the next
|
||||
* higher instead
|
||||
*/
|
||||
return_any = 0;
|
||||
find_higher = 1;
|
||||
btrfs_release_path(p);
|
||||
goto again;
|
||||
if (p->slots[0] == 0) {
|
||||
ret = btrfs_prev_leaf(root, p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ret) {
|
||||
p->slots[0] = btrfs_header_nritems(leaf) - 1;
|
||||
return 0;
|
||||
}
|
||||
if (!return_any)
|
||||
return 1;
|
||||
/*
|
||||
* no lower item found, return the next
|
||||
* higher instead
|
||||
*/
|
||||
return_any = 0;
|
||||
find_higher = 1;
|
||||
btrfs_release_path(p);
|
||||
goto again;
|
||||
} else {
|
||||
--p->slots[0];
|
||||
}
|
||||
}
|
||||
|
@ -5070,6 +5072,431 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void tree_move_down(struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
int *level, int root_level)
|
||||
{
|
||||
path->nodes[*level - 1] = read_node_slot(root, path->nodes[*level],
|
||||
path->slots[*level]);
|
||||
path->slots[*level - 1] = 0;
|
||||
(*level)--;
|
||||
}
|
||||
|
||||
static int tree_move_next_or_upnext(struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
int *level, int root_level)
|
||||
{
|
||||
int ret = 0;
|
||||
int nritems;
|
||||
nritems = btrfs_header_nritems(path->nodes[*level]);
|
||||
|
||||
path->slots[*level]++;
|
||||
|
||||
while (path->slots[*level] == nritems) {
|
||||
if (*level == root_level)
|
||||
return -1;
|
||||
|
||||
/* move upnext */
|
||||
path->slots[*level] = 0;
|
||||
free_extent_buffer(path->nodes[*level]);
|
||||
path->nodes[*level] = NULL;
|
||||
(*level)++;
|
||||
path->slots[*level]++;
|
||||
|
||||
nritems = btrfs_header_nritems(path->nodes[*level]);
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if it had to move up and next. 0 is returned if it moved only next
|
||||
* or down.
|
||||
*/
|
||||
static int tree_advance(struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
int *level, int root_level,
|
||||
int allow_down,
|
||||
struct btrfs_key *key)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (*level == 0 || !allow_down) {
|
||||
ret = tree_move_next_or_upnext(root, path, level, root_level);
|
||||
} else {
|
||||
tree_move_down(root, path, level, root_level);
|
||||
ret = 0;
|
||||
}
|
||||
if (ret >= 0) {
|
||||
if (*level == 0)
|
||||
btrfs_item_key_to_cpu(path->nodes[*level], key,
|
||||
path->slots[*level]);
|
||||
else
|
||||
btrfs_node_key_to_cpu(path->nodes[*level], key,
|
||||
path->slots[*level]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tree_compare_item(struct btrfs_root *left_root,
|
||||
struct btrfs_path *left_path,
|
||||
struct btrfs_path *right_path,
|
||||
char *tmp_buf)
|
||||
{
|
||||
int cmp;
|
||||
int len1, len2;
|
||||
unsigned long off1, off2;
|
||||
|
||||
len1 = btrfs_item_size_nr(left_path->nodes[0], left_path->slots[0]);
|
||||
len2 = btrfs_item_size_nr(right_path->nodes[0], right_path->slots[0]);
|
||||
if (len1 != len2)
|
||||
return 1;
|
||||
|
||||
off1 = btrfs_item_ptr_offset(left_path->nodes[0], left_path->slots[0]);
|
||||
off2 = btrfs_item_ptr_offset(right_path->nodes[0],
|
||||
right_path->slots[0]);
|
||||
|
||||
read_extent_buffer(left_path->nodes[0], tmp_buf, off1, len1);
|
||||
|
||||
cmp = memcmp_extent_buffer(right_path->nodes[0], tmp_buf, off2, len1);
|
||||
if (cmp)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADVANCE 1
|
||||
#define ADVANCE_ONLY_NEXT -1
|
||||
|
||||
/*
|
||||
* This function compares two trees and calls the provided callback for
|
||||
* every changed/new/deleted item it finds.
|
||||
* If shared tree blocks are encountered, whole subtrees are skipped, making
|
||||
* the compare pretty fast on snapshotted subvolumes.
|
||||
*
|
||||
* This currently works on commit roots only. As commit roots are read only,
|
||||
* we don't do any locking. The commit roots are protected with transactions.
|
||||
* Transactions are ended and rejoined when a commit is tried in between.
|
||||
*
|
||||
* This function checks for modifications done to the trees while comparing.
|
||||
* If it detects a change, it aborts immediately.
|
||||
*/
|
||||
int btrfs_compare_trees(struct btrfs_root *left_root,
|
||||
struct btrfs_root *right_root,
|
||||
btrfs_changed_cb_t changed_cb, void *ctx)
|
||||
{
|
||||
int ret;
|
||||
int cmp;
|
||||
struct btrfs_trans_handle *trans = NULL;
|
||||
struct btrfs_path *left_path = NULL;
|
||||
struct btrfs_path *right_path = NULL;
|
||||
struct btrfs_key left_key;
|
||||
struct btrfs_key right_key;
|
||||
char *tmp_buf = NULL;
|
||||
int left_root_level;
|
||||
int right_root_level;
|
||||
int left_level;
|
||||
int right_level;
|
||||
int left_end_reached;
|
||||
int right_end_reached;
|
||||
int advance_left;
|
||||
int advance_right;
|
||||
u64 left_blockptr;
|
||||
u64 right_blockptr;
|
||||
u64 left_start_ctransid;
|
||||
u64 right_start_ctransid;
|
||||
u64 ctransid;
|
||||
|
||||
left_path = btrfs_alloc_path();
|
||||
if (!left_path) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
right_path = btrfs_alloc_path();
|
||||
if (!right_path) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp_buf = kmalloc(left_root->leafsize, GFP_NOFS);
|
||||
if (!tmp_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
left_path->search_commit_root = 1;
|
||||
left_path->skip_locking = 1;
|
||||
right_path->search_commit_root = 1;
|
||||
right_path->skip_locking = 1;
|
||||
|
||||
spin_lock(&left_root->root_times_lock);
|
||||
left_start_ctransid = btrfs_root_ctransid(&left_root->root_item);
|
||||
spin_unlock(&left_root->root_times_lock);
|
||||
|
||||
spin_lock(&right_root->root_times_lock);
|
||||
right_start_ctransid = btrfs_root_ctransid(&right_root->root_item);
|
||||
spin_unlock(&right_root->root_times_lock);
|
||||
|
||||
trans = btrfs_join_transaction(left_root);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
trans = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strategy: Go to the first items of both trees. Then do
|
||||
*
|
||||
* If both trees are at level 0
|
||||
* Compare keys of current items
|
||||
* If left < right treat left item as new, advance left tree
|
||||
* and repeat
|
||||
* If left > right treat right item as deleted, advance right tree
|
||||
* and repeat
|
||||
* If left == right do deep compare of items, treat as changed if
|
||||
* needed, advance both trees and repeat
|
||||
* If both trees are at the same level but not at level 0
|
||||
* Compare keys of current nodes/leafs
|
||||
* If left < right advance left tree and repeat
|
||||
* If left > right advance right tree and repeat
|
||||
* If left == right compare blockptrs of the next nodes/leafs
|
||||
* If they match advance both trees but stay at the same level
|
||||
* and repeat
|
||||
* If they don't match advance both trees while allowing to go
|
||||
* deeper and repeat
|
||||
* If tree levels are different
|
||||
* Advance the tree that needs it and repeat
|
||||
*
|
||||
* Advancing a tree means:
|
||||
* If we are at level 0, try to go to the next slot. If that's not
|
||||
* possible, go one level up and repeat. Stop when we found a level
|
||||
* where we could go to the next slot. We may at this point be on a
|
||||
* node or a leaf.
|
||||
*
|
||||
* If we are not at level 0 and not on shared tree blocks, go one
|
||||
* level deeper.
|
||||
*
|
||||
* If we are not at level 0 and on shared tree blocks, go one slot to
|
||||
* the right if possible or go up and right.
|
||||
*/
|
||||
|
||||
left_level = btrfs_header_level(left_root->commit_root);
|
||||
left_root_level = left_level;
|
||||
left_path->nodes[left_level] = left_root->commit_root;
|
||||
extent_buffer_get(left_path->nodes[left_level]);
|
||||
|
||||
right_level = btrfs_header_level(right_root->commit_root);
|
||||
right_root_level = right_level;
|
||||
right_path->nodes[right_level] = right_root->commit_root;
|
||||
extent_buffer_get(right_path->nodes[right_level]);
|
||||
|
||||
if (left_level == 0)
|
||||
btrfs_item_key_to_cpu(left_path->nodes[left_level],
|
||||
&left_key, left_path->slots[left_level]);
|
||||
else
|
||||
btrfs_node_key_to_cpu(left_path->nodes[left_level],
|
||||
&left_key, left_path->slots[left_level]);
|
||||
if (right_level == 0)
|
||||
btrfs_item_key_to_cpu(right_path->nodes[right_level],
|
||||
&right_key, right_path->slots[right_level]);
|
||||
else
|
||||
btrfs_node_key_to_cpu(right_path->nodes[right_level],
|
||||
&right_key, right_path->slots[right_level]);
|
||||
|
||||
left_end_reached = right_end_reached = 0;
|
||||
advance_left = advance_right = 0;
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* We need to make sure the transaction does not get committed
|
||||
* while we do anything on commit roots. This means, we need to
|
||||
* join and leave transactions for every item that we process.
|
||||
*/
|
||||
if (trans && btrfs_should_end_transaction(trans, left_root)) {
|
||||
btrfs_release_path(left_path);
|
||||
btrfs_release_path(right_path);
|
||||
|
||||
ret = btrfs_end_transaction(trans, left_root);
|
||||
trans = NULL;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
/* now rejoin the transaction */
|
||||
if (!trans) {
|
||||
trans = btrfs_join_transaction(left_root);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
trans = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&left_root->root_times_lock);
|
||||
ctransid = btrfs_root_ctransid(&left_root->root_item);
|
||||
spin_unlock(&left_root->root_times_lock);
|
||||
if (ctransid != left_start_ctransid)
|
||||
left_start_ctransid = 0;
|
||||
|
||||
spin_lock(&right_root->root_times_lock);
|
||||
ctransid = btrfs_root_ctransid(&right_root->root_item);
|
||||
spin_unlock(&right_root->root_times_lock);
|
||||
if (ctransid != right_start_ctransid)
|
||||
right_start_ctransid = 0;
|
||||
|
||||
if (!left_start_ctransid || !right_start_ctransid) {
|
||||
WARN(1, KERN_WARNING
|
||||
"btrfs: btrfs_compare_tree detected "
|
||||
"a change in one of the trees while "
|
||||
"iterating. This is probably a "
|
||||
"bug.\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* the commit root may have changed, so start again
|
||||
* where we stopped
|
||||
*/
|
||||
left_path->lowest_level = left_level;
|
||||
right_path->lowest_level = right_level;
|
||||
ret = btrfs_search_slot(NULL, left_root,
|
||||
&left_key, left_path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = btrfs_search_slot(NULL, right_root,
|
||||
&right_key, right_path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (advance_left && !left_end_reached) {
|
||||
ret = tree_advance(left_root, left_path, &left_level,
|
||||
left_root_level,
|
||||
advance_left != ADVANCE_ONLY_NEXT,
|
||||
&left_key);
|
||||
if (ret < 0)
|
||||
left_end_reached = ADVANCE;
|
||||
advance_left = 0;
|
||||
}
|
||||
if (advance_right && !right_end_reached) {
|
||||
ret = tree_advance(right_root, right_path, &right_level,
|
||||
right_root_level,
|
||||
advance_right != ADVANCE_ONLY_NEXT,
|
||||
&right_key);
|
||||
if (ret < 0)
|
||||
right_end_reached = ADVANCE;
|
||||
advance_right = 0;
|
||||
}
|
||||
|
||||
if (left_end_reached && right_end_reached) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else if (left_end_reached) {
|
||||
if (right_level == 0) {
|
||||
ret = changed_cb(left_root, right_root,
|
||||
left_path, right_path,
|
||||
&right_key,
|
||||
BTRFS_COMPARE_TREE_DELETED,
|
||||
ctx);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
advance_right = ADVANCE;
|
||||
continue;
|
||||
} else if (right_end_reached) {
|
||||
if (left_level == 0) {
|
||||
ret = changed_cb(left_root, right_root,
|
||||
left_path, right_path,
|
||||
&left_key,
|
||||
BTRFS_COMPARE_TREE_NEW,
|
||||
ctx);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
advance_left = ADVANCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (left_level == 0 && right_level == 0) {
|
||||
cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
|
||||
if (cmp < 0) {
|
||||
ret = changed_cb(left_root, right_root,
|
||||
left_path, right_path,
|
||||
&left_key,
|
||||
BTRFS_COMPARE_TREE_NEW,
|
||||
ctx);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
advance_left = ADVANCE;
|
||||
} else if (cmp > 0) {
|
||||
ret = changed_cb(left_root, right_root,
|
||||
left_path, right_path,
|
||||
&right_key,
|
||||
BTRFS_COMPARE_TREE_DELETED,
|
||||
ctx);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
advance_right = ADVANCE;
|
||||
} else {
|
||||
ret = tree_compare_item(left_root, left_path,
|
||||
right_path, tmp_buf);
|
||||
if (ret) {
|
||||
ret = changed_cb(left_root, right_root,
|
||||
left_path, right_path,
|
||||
&left_key,
|
||||
BTRFS_COMPARE_TREE_CHANGED,
|
||||
ctx);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
advance_left = ADVANCE;
|
||||
advance_right = ADVANCE;
|
||||
}
|
||||
} else if (left_level == right_level) {
|
||||
cmp = btrfs_comp_cpu_keys(&left_key, &right_key);
|
||||
if (cmp < 0) {
|
||||
advance_left = ADVANCE;
|
||||
} else if (cmp > 0) {
|
||||
advance_right = ADVANCE;
|
||||
} else {
|
||||
left_blockptr = btrfs_node_blockptr(
|
||||
left_path->nodes[left_level],
|
||||
left_path->slots[left_level]);
|
||||
right_blockptr = btrfs_node_blockptr(
|
||||
right_path->nodes[right_level],
|
||||
right_path->slots[right_level]);
|
||||
if (left_blockptr == right_blockptr) {
|
||||
/*
|
||||
* As we're on a shared block, don't
|
||||
* allow to go deeper.
|
||||
*/
|
||||
advance_left = ADVANCE_ONLY_NEXT;
|
||||
advance_right = ADVANCE_ONLY_NEXT;
|
||||
} else {
|
||||
advance_left = ADVANCE;
|
||||
advance_right = ADVANCE;
|
||||
}
|
||||
}
|
||||
} else if (left_level < right_level) {
|
||||
advance_right = ADVANCE;
|
||||
} else {
|
||||
advance_left = ADVANCE;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
btrfs_free_path(left_path);
|
||||
btrfs_free_path(right_path);
|
||||
kfree(tmp_buf);
|
||||
|
||||
if (trans) {
|
||||
if (!ret)
|
||||
ret = btrfs_end_transaction(trans, left_root);
|
||||
else
|
||||
btrfs_end_transaction(trans, left_root);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* this is similar to btrfs_next_leaf, but does not try to preserve
|
||||
* and fixup the path. It looks for and returns the next key in the
|
||||
|
|
|
@ -712,6 +712,36 @@ struct btrfs_root_item {
|
|||
struct btrfs_disk_key drop_progress;
|
||||
u8 drop_level;
|
||||
u8 level;
|
||||
|
||||
/*
|
||||
* The following fields appear after subvol_uuids+subvol_times
|
||||
* were introduced.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This generation number is used to test if the new fields are valid
|
||||
* and up to date while reading the root item. Everytime the root item
|
||||
* is written out, the "generation" field is copied into this field. If
|
||||
* anyone ever mounted the fs with an older kernel, we will have
|
||||
* mismatching generation values here and thus must invalidate the
|
||||
* new fields. See btrfs_update_root and btrfs_find_last_root for
|
||||
* details.
|
||||
* the offset of generation_v2 is also used as the start for the memset
|
||||
* when invalidating the fields.
|
||||
*/
|
||||
__le64 generation_v2;
|
||||
u8 uuid[BTRFS_UUID_SIZE];
|
||||
u8 parent_uuid[BTRFS_UUID_SIZE];
|
||||
u8 received_uuid[BTRFS_UUID_SIZE];
|
||||
__le64 ctransid; /* updated when an inode changes */
|
||||
__le64 otransid; /* trans when created */
|
||||
__le64 stransid; /* trans when sent. non-zero for received subvol */
|
||||
__le64 rtransid; /* trans when received. non-zero for received subvol */
|
||||
struct btrfs_timespec ctime;
|
||||
struct btrfs_timespec otime;
|
||||
struct btrfs_timespec stime;
|
||||
struct btrfs_timespec rtime;
|
||||
__le64 reserved[8]; /* for future */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/*
|
||||
|
@ -1520,6 +1550,8 @@ struct btrfs_root {
|
|||
dev_t anon_dev;
|
||||
|
||||
int force_cow;
|
||||
|
||||
spinlock_t root_times_lock;
|
||||
};
|
||||
|
||||
struct btrfs_ioctl_defrag_range_args {
|
||||
|
@ -2358,6 +2390,16 @@ BTRFS_SETGET_STACK_FUNCS(root_used, struct btrfs_root_item, bytes_used, 64);
|
|||
BTRFS_SETGET_STACK_FUNCS(root_limit, struct btrfs_root_item, byte_limit, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(root_last_snapshot, struct btrfs_root_item,
|
||||
last_snapshot, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(root_generation_v2, struct btrfs_root_item,
|
||||
generation_v2, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(root_ctransid, struct btrfs_root_item,
|
||||
ctransid, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(root_otransid, struct btrfs_root_item,
|
||||
otransid, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(root_stransid, struct btrfs_root_item,
|
||||
stransid, 64);
|
||||
BTRFS_SETGET_STACK_FUNCS(root_rtransid, struct btrfs_root_item,
|
||||
rtransid, 64);
|
||||
|
||||
static inline bool btrfs_root_readonly(struct btrfs_root *root)
|
||||
{
|
||||
|
@ -2893,6 +2935,21 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
|
|||
struct btrfs_key *max_key,
|
||||
struct btrfs_path *path, int cache_only,
|
||||
u64 min_trans);
|
||||
enum btrfs_compare_tree_result {
|
||||
BTRFS_COMPARE_TREE_NEW,
|
||||
BTRFS_COMPARE_TREE_DELETED,
|
||||
BTRFS_COMPARE_TREE_CHANGED,
|
||||
};
|
||||
typedef int (*btrfs_changed_cb_t)(struct btrfs_root *left_root,
|
||||
struct btrfs_root *right_root,
|
||||
struct btrfs_path *left_path,
|
||||
struct btrfs_path *right_path,
|
||||
struct btrfs_key *key,
|
||||
enum btrfs_compare_tree_result result,
|
||||
void *ctx);
|
||||
int btrfs_compare_trees(struct btrfs_root *left_root,
|
||||
struct btrfs_root *right_root,
|
||||
btrfs_changed_cb_t cb, void *ctx);
|
||||
int btrfs_cow_block(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *buf,
|
||||
struct extent_buffer *parent, int parent_slot,
|
||||
|
@ -3046,6 +3103,9 @@ int __must_check btrfs_update_root(struct btrfs_trans_handle *trans,
|
|||
struct btrfs_root *root,
|
||||
struct btrfs_key *key,
|
||||
struct btrfs_root_item *item);
|
||||
void btrfs_read_root_item(struct btrfs_root *root,
|
||||
struct extent_buffer *eb, int slot,
|
||||
struct btrfs_root_item *item);
|
||||
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
|
||||
btrfs_root_item *item, struct btrfs_key *key);
|
||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
|
||||
|
@ -3053,6 +3113,8 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
|
|||
void btrfs_set_root_node(struct btrfs_root_item *item,
|
||||
struct extent_buffer *node);
|
||||
void btrfs_check_and_init_root_item(struct btrfs_root_item *item);
|
||||
void btrfs_update_root_times(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
|
||||
/* dir-item.c */
|
||||
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans,
|
||||
|
|
|
@ -1182,6 +1182,8 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
|
|||
root->defrag_running = 0;
|
||||
root->root_key.objectid = objectid;
|
||||
root->anon_dev = 0;
|
||||
|
||||
spin_lock_init(&root->root_times_lock);
|
||||
}
|
||||
|
||||
static int __must_check find_and_setup_root(struct btrfs_root *tree_root,
|
||||
|
@ -1402,6 +1404,7 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
|
|||
u64 generation;
|
||||
u32 blocksize;
|
||||
int ret = 0;
|
||||
int slot;
|
||||
|
||||
root = btrfs_alloc_root(fs_info);
|
||||
if (!root)
|
||||
|
@ -1428,9 +1431,8 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
|
|||
ret = btrfs_search_slot(NULL, tree_root, location, path, 0, 0);
|
||||
if (ret == 0) {
|
||||
l = path->nodes[0];
|
||||
read_extent_buffer(l, &root->root_item,
|
||||
btrfs_item_ptr_offset(l, path->slots[0]),
|
||||
sizeof(root->root_item));
|
||||
slot = path->slots[0];
|
||||
btrfs_read_root_item(tree_root, l, slot, &root->root_item);
|
||||
memcpy(&root->root_key, location, sizeof(*location));
|
||||
}
|
||||
btrfs_free_path(path);
|
||||
|
|
|
@ -2734,6 +2734,8 @@ noinline int btrfs_update_inode(struct btrfs_trans_handle *trans,
|
|||
*/
|
||||
if (!btrfs_is_free_space_inode(inode)
|
||||
&& root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
||||
btrfs_update_root_times(trans, root);
|
||||
|
||||
ret = btrfs_delayed_update_inode(trans, root, inode);
|
||||
if (!ret)
|
||||
btrfs_set_inode_last_trans(trans, inode);
|
||||
|
@ -4728,6 +4730,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
trace_btrfs_inode_new(inode);
|
||||
btrfs_set_inode_last_trans(trans, inode);
|
||||
|
||||
btrfs_update_root_times(trans, root);
|
||||
|
||||
return inode;
|
||||
fail:
|
||||
if (dir)
|
||||
|
|
114
fs/btrfs/ioctl.c
114
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"
|
||||
|
@ -53,6 +54,7 @@
|
|||
#include "inode-map.h"
|
||||
#include "backref.h"
|
||||
#include "rcu-string.h"
|
||||
#include "send.h"
|
||||
|
||||
/* Mask out flags that are inappropriate for the given type of inode. */
|
||||
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
|
||||
|
@ -347,11 +349,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)
|
||||
|
@ -395,8 +399,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);
|
||||
|
@ -414,8 +419,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);
|
||||
|
@ -2370,6 +2382,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;
|
||||
|
@ -2390,7 +2406,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;
|
||||
|
@ -2464,13 +2480,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)
|
||||
|
@ -3589,6 +3606,87 @@ static long btrfs_ioctl_qgroup_limit(struct btrfs_root *root, void __user *arg)
|
|||
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)
|
||||
{
|
||||
|
@ -3673,6 +3771,10 @@ 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_SEND:
|
||||
return btrfs_ioctl_send(file, argp);
|
||||
case BTRFS_IOC_GET_DEV_STATS:
|
||||
return btrfs_ioctl_get_dev_stats(root, argp);
|
||||
case BTRFS_IOC_QUOTA_CTL:
|
||||
|
|
|
@ -348,6 +348,29 @@ struct btrfs_ioctl_qgroup_create_args {
|
|||
__u64 create;
|
||||
__u64 qgroupid;
|
||||
};
|
||||
struct btrfs_ioctl_timespec {
|
||||
__u64 sec;
|
||||
__u32 nsec;
|
||||
};
|
||||
|
||||
struct btrfs_ioctl_received_subvol_args {
|
||||
char uuid[BTRFS_UUID_SIZE]; /* in */
|
||||
__u64 stransid; /* in */
|
||||
__u64 rtransid; /* out */
|
||||
struct btrfs_ioctl_timespec stime; /* in */
|
||||
struct btrfs_ioctl_timespec rtime; /* out */
|
||||
__u64 flags; /* in */
|
||||
__u64 reserved[16]; /* in */
|
||||
};
|
||||
|
||||
struct btrfs_ioctl_send_args {
|
||||
__s64 send_fd; /* in */
|
||||
__u64 clone_sources_count; /* in */
|
||||
__u64 __user *clone_sources; /* in */
|
||||
__u64 parent_root; /* in */
|
||||
__u64 flags; /* in */
|
||||
__u64 reserved[4]; /* in */
|
||||
};
|
||||
|
||||
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
|
||||
struct btrfs_ioctl_vol_args)
|
||||
|
@ -415,6 +438,9 @@ struct btrfs_ioctl_qgroup_create_args {
|
|||
struct btrfs_ioctl_ino_path_args)
|
||||
#define BTRFS_IOC_LOGICAL_INO _IOWR(BTRFS_IOCTL_MAGIC, 36, \
|
||||
struct btrfs_ioctl_ino_path_args)
|
||||
#define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
|
||||
struct btrfs_ioctl_received_subvol_args)
|
||||
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
|
||||
#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
|
||||
struct btrfs_ioctl_vol_args)
|
||||
#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
|
||||
|
|
|
@ -16,11 +16,54 @@
|
|||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/uuid.h>
|
||||
#include "ctree.h"
|
||||
#include "transaction.h"
|
||||
#include "disk-io.h"
|
||||
#include "print-tree.h"
|
||||
|
||||
/*
|
||||
* Read a root item from the tree. In case we detect a root item smaller then
|
||||
* sizeof(root_item), we know it's an old version of the root structure and
|
||||
* initialize all new fields to zero. The same happens if we detect mismatching
|
||||
* generation numbers as then we know the root was once mounted with an older
|
||||
* kernel that was not aware of the root item structure change.
|
||||
*/
|
||||
void btrfs_read_root_item(struct btrfs_root *root,
|
||||
struct extent_buffer *eb, int slot,
|
||||
struct btrfs_root_item *item)
|
||||
{
|
||||
uuid_le uuid;
|
||||
int len;
|
||||
int need_reset = 0;
|
||||
|
||||
len = btrfs_item_size_nr(eb, slot);
|
||||
read_extent_buffer(eb, item, btrfs_item_ptr_offset(eb, slot),
|
||||
min_t(int, len, (int)sizeof(*item)));
|
||||
if (len < sizeof(*item))
|
||||
need_reset = 1;
|
||||
if (!need_reset && btrfs_root_generation(item)
|
||||
!= btrfs_root_generation_v2(item)) {
|
||||
if (btrfs_root_generation_v2(item) != 0) {
|
||||
printk(KERN_WARNING "btrfs: mismatching "
|
||||
"generation and generation_v2 "
|
||||
"found in root item. This root "
|
||||
"was probably mounted with an "
|
||||
"older kernel. Resetting all "
|
||||
"new fields.\n");
|
||||
}
|
||||
need_reset = 1;
|
||||
}
|
||||
if (need_reset) {
|
||||
memset(&item->generation_v2, 0,
|
||||
sizeof(*item) - offsetof(struct btrfs_root_item,
|
||||
generation_v2));
|
||||
|
||||
uuid_le_gen(&uuid);
|
||||
memcpy(item->uuid, uuid.b, BTRFS_UUID_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup the root with the highest offset for a given objectid. The key we do
|
||||
* find is copied into 'key'. If we find something return 0, otherwise 1, < 0
|
||||
|
@ -61,10 +104,10 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
|
|||
goto out;
|
||||
}
|
||||
if (item)
|
||||
read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
|
||||
sizeof(*item));
|
||||
btrfs_read_root_item(root, l, slot, item);
|
||||
if (key)
|
||||
memcpy(key, &found_key, sizeof(found_key));
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
|
@ -91,16 +134,15 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
|||
int ret;
|
||||
int slot;
|
||||
unsigned long ptr;
|
||||
int old_len;
|
||||
|
||||
path = btrfs_alloc_path();
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
|
||||
if (ret < 0) {
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto out_abort;
|
||||
|
||||
if (ret != 0) {
|
||||
btrfs_print_leaf(root, path->nodes[0]);
|
||||
|
@ -113,16 +155,56 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
|
|||
l = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
ptr = btrfs_item_ptr_offset(l, slot);
|
||||
old_len = btrfs_item_size_nr(l, slot);
|
||||
|
||||
/*
|
||||
* If this is the first time we update the root item which originated
|
||||
* from an older kernel, we need to enlarge the item size to make room
|
||||
* for the added fields.
|
||||
*/
|
||||
if (old_len < sizeof(*item)) {
|
||||
btrfs_release_path(path);
|
||||
ret = btrfs_search_slot(trans, root, key, path,
|
||||
-1, 1);
|
||||
if (ret < 0)
|
||||
goto out_abort;
|
||||
ret = btrfs_del_item(trans, root, path);
|
||||
if (ret < 0)
|
||||
goto out_abort;
|
||||
btrfs_release_path(path);
|
||||
ret = btrfs_insert_empty_item(trans, root, path,
|
||||
key, sizeof(*item));
|
||||
if (ret < 0)
|
||||
goto out_abort;
|
||||
l = path->nodes[0];
|
||||
slot = path->slots[0];
|
||||
ptr = btrfs_item_ptr_offset(l, slot);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update generation_v2 so at the next mount we know the new root
|
||||
* fields are valid.
|
||||
*/
|
||||
btrfs_set_root_generation_v2(item, btrfs_root_generation(item));
|
||||
|
||||
write_extent_buffer(l, item, ptr, sizeof(*item));
|
||||
btrfs_mark_buffer_dirty(path->nodes[0]);
|
||||
out:
|
||||
btrfs_free_path(path);
|
||||
return ret;
|
||||
|
||||
out_abort:
|
||||
btrfs_abort_transaction(trans, root, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int btrfs_insert_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
||||
struct btrfs_key *key, struct btrfs_root_item *item)
|
||||
{
|
||||
/*
|
||||
* Make sure generation v1 and v2 match. See update_root for details.
|
||||
*/
|
||||
btrfs_set_root_generation_v2(item, btrfs_root_generation(item));
|
||||
return btrfs_insert_item(trans, root, key, item, sizeof(*item));
|
||||
}
|
||||
|
||||
|
@ -454,3 +536,16 @@ void btrfs_check_and_init_root_item(struct btrfs_root_item *root_item)
|
|||
root_item->byte_limit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void btrfs_update_root_times(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_root_item *item = &root->root_item;
|
||||
struct timespec ct = CURRENT_TIME;
|
||||
|
||||
spin_lock(&root->root_times_lock);
|
||||
item->ctransid = trans->transid;
|
||||
item->ctime.sec = cpu_to_le64(ct.tv_sec);
|
||||
item->ctime.nsec = cpu_to_le64(ct.tv_nsec);
|
||||
spin_unlock(&root->root_times_lock);
|
||||
}
|
||||
|
|
4570
fs/btrfs/send.c
Normal file
4570
fs/btrfs/send.c
Normal file
File diff suppressed because it is too large
Load diff
133
fs/btrfs/send.h
Normal file
133
fs/btrfs/send.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Alexander Block. All rights reserved.
|
||||
* Copyright (C) 2012 STRATO. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 021110-1307, USA.
|
||||
*/
|
||||
|
||||
#include "ctree.h"
|
||||
|
||||
#define BTRFS_SEND_STREAM_MAGIC "btrfs-stream"
|
||||
#define BTRFS_SEND_STREAM_VERSION 1
|
||||
|
||||
#define BTRFS_SEND_BUF_SIZE (1024 * 64)
|
||||
#define BTRFS_SEND_READ_SIZE (1024 * 48)
|
||||
|
||||
enum btrfs_tlv_type {
|
||||
BTRFS_TLV_U8,
|
||||
BTRFS_TLV_U16,
|
||||
BTRFS_TLV_U32,
|
||||
BTRFS_TLV_U64,
|
||||
BTRFS_TLV_BINARY,
|
||||
BTRFS_TLV_STRING,
|
||||
BTRFS_TLV_UUID,
|
||||
BTRFS_TLV_TIMESPEC,
|
||||
};
|
||||
|
||||
struct btrfs_stream_header {
|
||||
char magic[sizeof(BTRFS_SEND_STREAM_MAGIC)];
|
||||
__le32 version;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_cmd_header {
|
||||
/* len excluding the header */
|
||||
__le32 len;
|
||||
__le16 cmd;
|
||||
/* crc including the header with zero crc field */
|
||||
__le32 crc;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct btrfs_tlv_header {
|
||||
__le16 tlv_type;
|
||||
/* len excluding the header */
|
||||
__le16 tlv_len;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* commands */
|
||||
enum btrfs_send_cmd {
|
||||
BTRFS_SEND_C_UNSPEC,
|
||||
|
||||
BTRFS_SEND_C_SUBVOL,
|
||||
BTRFS_SEND_C_SNAPSHOT,
|
||||
|
||||
BTRFS_SEND_C_MKFILE,
|
||||
BTRFS_SEND_C_MKDIR,
|
||||
BTRFS_SEND_C_MKNOD,
|
||||
BTRFS_SEND_C_MKFIFO,
|
||||
BTRFS_SEND_C_MKSOCK,
|
||||
BTRFS_SEND_C_SYMLINK,
|
||||
|
||||
BTRFS_SEND_C_RENAME,
|
||||
BTRFS_SEND_C_LINK,
|
||||
BTRFS_SEND_C_UNLINK,
|
||||
BTRFS_SEND_C_RMDIR,
|
||||
|
||||
BTRFS_SEND_C_SET_XATTR,
|
||||
BTRFS_SEND_C_REMOVE_XATTR,
|
||||
|
||||
BTRFS_SEND_C_WRITE,
|
||||
BTRFS_SEND_C_CLONE,
|
||||
|
||||
BTRFS_SEND_C_TRUNCATE,
|
||||
BTRFS_SEND_C_CHMOD,
|
||||
BTRFS_SEND_C_CHOWN,
|
||||
BTRFS_SEND_C_UTIMES,
|
||||
|
||||
BTRFS_SEND_C_END,
|
||||
__BTRFS_SEND_C_MAX,
|
||||
};
|
||||
#define BTRFS_SEND_C_MAX (__BTRFS_SEND_C_MAX - 1)
|
||||
|
||||
/* attributes in send stream */
|
||||
enum {
|
||||
BTRFS_SEND_A_UNSPEC,
|
||||
|
||||
BTRFS_SEND_A_UUID,
|
||||
BTRFS_SEND_A_CTRANSID,
|
||||
|
||||
BTRFS_SEND_A_INO,
|
||||
BTRFS_SEND_A_SIZE,
|
||||
BTRFS_SEND_A_MODE,
|
||||
BTRFS_SEND_A_UID,
|
||||
BTRFS_SEND_A_GID,
|
||||
BTRFS_SEND_A_RDEV,
|
||||
BTRFS_SEND_A_CTIME,
|
||||
BTRFS_SEND_A_MTIME,
|
||||
BTRFS_SEND_A_ATIME,
|
||||
BTRFS_SEND_A_OTIME,
|
||||
|
||||
BTRFS_SEND_A_XATTR_NAME,
|
||||
BTRFS_SEND_A_XATTR_DATA,
|
||||
|
||||
BTRFS_SEND_A_PATH,
|
||||
BTRFS_SEND_A_PATH_TO,
|
||||
BTRFS_SEND_A_PATH_LINK,
|
||||
|
||||
BTRFS_SEND_A_FILE_OFFSET,
|
||||
BTRFS_SEND_A_DATA,
|
||||
|
||||
BTRFS_SEND_A_CLONE_UUID,
|
||||
BTRFS_SEND_A_CLONE_CTRANSID,
|
||||
BTRFS_SEND_A_CLONE_PATH,
|
||||
BTRFS_SEND_A_CLONE_OFFSET,
|
||||
BTRFS_SEND_A_CLONE_LEN,
|
||||
|
||||
__BTRFS_SEND_A_MAX,
|
||||
};
|
||||
#define BTRFS_SEND_A_MAX (__BTRFS_SEND_A_MAX - 1)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
long btrfs_ioctl_send(struct file *mnt_file, void __user *arg);
|
||||
#endif
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/writeback.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/uuid.h>
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
#include "transaction.h"
|
||||
|
@ -953,11 +954,13 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
|||
struct dentry *dentry;
|
||||
struct extent_buffer *tmp;
|
||||
struct extent_buffer *old;
|
||||
struct timespec cur_time = CURRENT_TIME;
|
||||
int ret;
|
||||
u64 to_reserve = 0;
|
||||
u64 index = 0;
|
||||
u64 objectid;
|
||||
u64 root_flags;
|
||||
uuid_le new_uuid;
|
||||
|
||||
rsv = trans->block_rsv;
|
||||
|
||||
|
@ -1051,6 +1054,20 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
|||
root_flags &= ~BTRFS_ROOT_SUBVOL_RDONLY;
|
||||
btrfs_set_root_flags(new_root_item, root_flags);
|
||||
|
||||
btrfs_set_root_generation_v2(new_root_item,
|
||||
trans->transid);
|
||||
uuid_le_gen(&new_uuid);
|
||||
memcpy(new_root_item->uuid, new_uuid.b, BTRFS_UUID_SIZE);
|
||||
memcpy(new_root_item->parent_uuid, root->root_item.uuid,
|
||||
BTRFS_UUID_SIZE);
|
||||
new_root_item->otime.sec = cpu_to_le64(cur_time.tv_sec);
|
||||
new_root_item->otime.nsec = cpu_to_le64(cur_time.tv_nsec);
|
||||
btrfs_set_root_otransid(new_root_item, trans->transid);
|
||||
memset(&new_root_item->stime, 0, sizeof(new_root_item->stime));
|
||||
memset(&new_root_item->rtime, 0, sizeof(new_root_item->rtime));
|
||||
btrfs_set_root_stransid(new_root_item, 0);
|
||||
btrfs_set_root_rtransid(new_root_item, 0);
|
||||
|
||||
old = btrfs_lock_root_node(root);
|
||||
ret = btrfs_cow_block(trans, root, old, NULL, 0, &old);
|
||||
if (ret) {
|
||||
|
|
Loading…
Reference in a new issue