Commit 113c1cb5 authored by Chris Mason's avatar Chris Mason
Browse files

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: default avatarChris Mason <chris.mason@fusionio.com>
parents cd1cfc49 31db9f7c
......@@ -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 @@ continue_with_current_leaf_stack_frame:
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 @@ leaf_item_out_of_bounce_error:
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 @@ leaf_item_out_of_bounce_error:
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 =
......
......@@ -2837,23 +2837,25 @@ again:
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 @@ out:
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,