|
@@ -493,6 +493,7 @@ static struct btrfs_path *alloc_path_for_send(void)
|
|
|
return NULL;
|
|
|
path->search_commit_root = 1;
|
|
|
path->skip_locking = 1;
|
|
|
+ path->need_commit_sem = 1;
|
|
|
return path;
|
|
|
}
|
|
|
|
|
@@ -771,29 +772,22 @@ out:
|
|
|
/*
|
|
|
* Helper function to retrieve some fields from an inode item.
|
|
|
*/
|
|
|
-static int get_inode_info(struct btrfs_root *root,
|
|
|
- u64 ino, u64 *size, u64 *gen,
|
|
|
- u64 *mode, u64 *uid, u64 *gid,
|
|
|
- u64 *rdev)
|
|
|
+static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,
|
|
|
+ u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid,
|
|
|
+ u64 *gid, u64 *rdev)
|
|
|
{
|
|
|
int ret;
|
|
|
struct btrfs_inode_item *ii;
|
|
|
struct btrfs_key key;
|
|
|
- struct btrfs_path *path;
|
|
|
-
|
|
|
- path = alloc_path_for_send();
|
|
|
- if (!path)
|
|
|
- return -ENOMEM;
|
|
|
|
|
|
key.objectid = ino;
|
|
|
key.type = BTRFS_INODE_ITEM_KEY;
|
|
|
key.offset = 0;
|
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
if (ret) {
|
|
|
- ret = -ENOENT;
|
|
|
- goto out;
|
|
|
+ if (ret > 0)
|
|
|
+ ret = -ENOENT;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
|
@@ -811,7 +805,22 @@ static int get_inode_info(struct btrfs_root *root,
|
|
|
if (rdev)
|
|
|
*rdev = btrfs_inode_rdev(path->nodes[0], ii);
|
|
|
|
|
|
-out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int get_inode_info(struct btrfs_root *root,
|
|
|
+ u64 ino, u64 *size, u64 *gen,
|
|
|
+ u64 *mode, u64 *uid, u64 *gid,
|
|
|
+ u64 *rdev)
|
|
|
+{
|
|
|
+ struct btrfs_path *path;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ path = alloc_path_for_send();
|
|
|
+ if (!path)
|
|
|
+ return -ENOMEM;
|
|
|
+ ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid,
|
|
|
+ rdev);
|
|
|
btrfs_free_path(path);
|
|
|
return ret;
|
|
|
}
|
|
@@ -1085,6 +1094,7 @@ out:
|
|
|
struct backref_ctx {
|
|
|
struct send_ctx *sctx;
|
|
|
|
|
|
+ struct btrfs_path *path;
|
|
|
/* number of total found references */
|
|
|
u64 found;
|
|
|
|
|
@@ -1155,8 +1165,9 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
|
|
|
* There are inodes that have extents that lie behind its i_size. Don't
|
|
|
* accept clones from these extents.
|
|
|
*/
|
|
|
- ret = get_inode_info(found->root, ino, &i_size, NULL, NULL, NULL, NULL,
|
|
|
- NULL);
|
|
|
+ ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
|
|
|
+ NULL, NULL, NULL);
|
|
|
+ btrfs_release_path(bctx->path);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -1235,12 +1246,17 @@ static int find_extent_clone(struct send_ctx *sctx,
|
|
|
if (!tmp_path)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ /* We only use this path under the commit sem */
|
|
|
+ tmp_path->need_commit_sem = 0;
|
|
|
+
|
|
|
backref_ctx = kmalloc(sizeof(*backref_ctx), GFP_NOFS);
|
|
|
if (!backref_ctx) {
|
|
|
ret = -ENOMEM;
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ backref_ctx->path = tmp_path;
|
|
|
+
|
|
|
if (data_offset >= ino_size) {
|
|
|
/*
|
|
|
* There may be extents that lie behind the file's size.
|
|
@@ -1268,8 +1284,10 @@ static int find_extent_clone(struct send_ctx *sctx,
|
|
|
}
|
|
|
logical = disk_byte + btrfs_file_extent_offset(eb, fi);
|
|
|
|
|
|
+ down_read(&sctx->send_root->fs_info->commit_root_sem);
|
|
|
ret = extent_from_logical(sctx->send_root->fs_info, disk_byte, tmp_path,
|
|
|
&found_key, &flags);
|
|
|
+ up_read(&sctx->send_root->fs_info->commit_root_sem);
|
|
|
btrfs_release_path(tmp_path);
|
|
|
|
|
|
if (ret < 0)
|
|
@@ -4418,14 +4436,14 @@ static int send_hole(struct send_ctx *sctx, u64 end)
|
|
|
p = fs_path_alloc();
|
|
|
if (!p)
|
|
|
return -ENOMEM;
|
|
|
+ ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
|
|
|
+ if (ret < 0)
|
|
|
+ goto tlv_put_failure;
|
|
|
memset(sctx->read_buf, 0, BTRFS_SEND_READ_SIZE);
|
|
|
while (offset < end) {
|
|
|
len = min_t(u64, end - offset, BTRFS_SEND_READ_SIZE);
|
|
|
|
|
|
ret = begin_cmd(sctx, BTRFS_SEND_C_WRITE);
|
|
|
- if (ret < 0)
|
|
|
- break;
|
|
|
- ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
|
|
|
if (ret < 0)
|
|
|
break;
|
|
|
TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);
|
|
@@ -4968,7 +4986,9 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
|
|
|
|
|
|
if (S_ISREG(sctx->cur_inode_mode)) {
|
|
|
if (need_send_hole(sctx)) {
|
|
|
- if (sctx->cur_inode_last_extent == (u64)-1) {
|
|
|
+ if (sctx->cur_inode_last_extent == (u64)-1 ||
|
|
|
+ sctx->cur_inode_last_extent <
|
|
|
+ sctx->cur_inode_size) {
|
|
|
ret = get_last_extent(sctx, (u64)-1);
|
|
|
if (ret)
|
|
|
goto out;
|
|
@@ -5367,57 +5387,21 @@ out:
|
|
|
static int full_send_tree(struct send_ctx *sctx)
|
|
|
{
|
|
|
int ret;
|
|
|
- struct btrfs_trans_handle *trans = NULL;
|
|
|
struct btrfs_root *send_root = sctx->send_root;
|
|
|
struct btrfs_key key;
|
|
|
struct btrfs_key found_key;
|
|
|
struct btrfs_path *path;
|
|
|
struct extent_buffer *eb;
|
|
|
int slot;
|
|
|
- u64 start_ctransid;
|
|
|
- u64 ctransid;
|
|
|
|
|
|
path = alloc_path_for_send();
|
|
|
if (!path)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- spin_lock(&send_root->root_item_lock);
|
|
|
- start_ctransid = btrfs_root_ctransid(&send_root->root_item);
|
|
|
- spin_unlock(&send_root->root_item_lock);
|
|
|
-
|
|
|
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
|
|
|
key.type = BTRFS_INODE_ITEM_KEY;
|
|
|
key.offset = 0;
|
|
|
|
|
|
-join_trans:
|
|
|
- /*
|
|
|
- * We need to make sure the transaction does not get committed
|
|
|
- * while we do anything on commit roots. Join a transaction to prevent
|
|
|
- * this.
|
|
|
- */
|
|
|
- trans = btrfs_join_transaction(send_root);
|
|
|
- if (IS_ERR(trans)) {
|
|
|
- ret = PTR_ERR(trans);
|
|
|
- trans = NULL;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Make sure the tree has not changed after re-joining. We detect this
|
|
|
- * by comparing start_ctransid and ctransid. They should always match.
|
|
|
- */
|
|
|
- spin_lock(&send_root->root_item_lock);
|
|
|
- ctransid = btrfs_root_ctransid(&send_root->root_item);
|
|
|
- spin_unlock(&send_root->root_item_lock);
|
|
|
-
|
|
|
- if (ctransid != start_ctransid) {
|
|
|
- WARN(1, KERN_WARNING "BTRFS: the root that you're trying to "
|
|
|
- "send was modified in between. This is "
|
|
|
- "probably a bug.\n");
|
|
|
- ret = -EIO;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
ret = btrfs_search_slot_for_read(send_root, &key, path, 1, 0);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
@@ -5425,19 +5409,6 @@ join_trans:
|
|
|
goto out_finish;
|
|
|
|
|
|
while (1) {
|
|
|
- /*
|
|
|
- * When someone want to commit while we iterate, end the
|
|
|
- * joined transaction and rejoin.
|
|
|
- */
|
|
|
- if (btrfs_should_end_transaction(trans, send_root)) {
|
|
|
- ret = btrfs_end_transaction(trans, send_root);
|
|
|
- trans = NULL;
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
- btrfs_release_path(path);
|
|
|
- goto join_trans;
|
|
|
- }
|
|
|
-
|
|
|
eb = path->nodes[0];
|
|
|
slot = path->slots[0];
|
|
|
btrfs_item_key_to_cpu(eb, &found_key, slot);
|
|
@@ -5465,12 +5436,6 @@ out_finish:
|
|
|
|
|
|
out:
|
|
|
btrfs_free_path(path);
|
|
|
- if (trans) {
|
|
|
- if (!ret)
|
|
|
- ret = btrfs_end_transaction(trans, send_root);
|
|
|
- else
|
|
|
- btrfs_end_transaction(trans, send_root);
|
|
|
- }
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -5718,7 +5683,9 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
|
|
|
NULL);
|
|
|
sort_clone_roots = 1;
|
|
|
|
|
|
+ current->journal_info = (void *)BTRFS_SEND_TRANS_STUB;
|
|
|
ret = send_subvol(sctx);
|
|
|
+ current->journal_info = NULL;
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
|