Commit 9512c47e authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs

Pull btrfs fixes from Chris Mason:
 "Some fixes for btrfs send/recv and fsync from Filipe and Robbie Ko.

  Bonus points to Filipe for already having xfstests in place for many
  of these"

* 'for-linus-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs:
  Btrfs: remove unused function btrfs_add_delayed_qgroup_reserve()
  Btrfs: improve performance on fsync against new inode after rename/unlink
  Btrfs: be more precise on errors when getting an inode from disk
  Btrfs: send, don't bug on inconsistent snapshots
  Btrfs: send, avoid incorrect leaf accesses when sending utimes operations
  Btrfs: send, fix invalid leaf accesses due to incorrect utimes operations
  Btrfs: send, fix warning due to late freeing of orphan_dir_info structures
  Btrfs: incremental send, fix premature rmdir operations
  Btrfs: incremental send, fix invalid paths for rename operations
  Btrfs: send, add missing error check for calls to path_loop()
  Btrfs: send, fix failure to move directories with the same name around
  Btrfs: add missing check for writeback errors on fsync
parents 315581a2 10838816
...@@ -862,33 +862,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, ...@@ -862,33 +862,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
return 0; return 0;
} }
int btrfs_add_delayed_qgroup_reserve(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans,
u64 ref_root, u64 bytenr, u64 num_bytes)
{
struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_delayed_ref_head *ref_head;
int ret = 0;
if (!fs_info->quota_enabled || !is_fstree(ref_root))
return 0;
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
ref_head = find_ref_head(&delayed_refs->href_root, bytenr, 0);
if (!ref_head) {
ret = -ENOENT;
goto out;
}
WARN_ON(ref_head->qgroup_reserved || ref_head->qgroup_ref_root);
ref_head->qgroup_ref_root = ref_root;
ref_head->qgroup_reserved = num_bytes;
out:
spin_unlock(&delayed_refs->lock);
return ret;
}
int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans, struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 bytenr, u64 num_bytes,
......
...@@ -250,9 +250,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, ...@@ -250,9 +250,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
u64 parent, u64 ref_root, u64 parent, u64 ref_root,
u64 owner, u64 offset, u64 reserved, int action, u64 owner, u64 offset, u64 reserved, int action,
struct btrfs_delayed_extent_op *extent_op); struct btrfs_delayed_extent_op *extent_op);
int btrfs_add_delayed_qgroup_reserve(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans,
u64 ref_root, u64 bytenr, u64 num_bytes);
int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans, struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 bytenr, u64 num_bytes,
......
...@@ -2033,6 +2033,14 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -2033,6 +2033,14 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
*/ */
clear_bit(BTRFS_INODE_NEEDS_FULL_SYNC, clear_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
&BTRFS_I(inode)->runtime_flags); &BTRFS_I(inode)->runtime_flags);
/*
* An ordered extent might have started before and completed
* already with io errors, in which case the inode was not
* updated and we end up here. So check the inode's mapping
* flags for any errors that might have happened while doing
* writeback of file data.
*/
ret = btrfs_inode_check_errors(inode);
inode_unlock(inode); inode_unlock(inode);
goto out; goto out;
} }
......
...@@ -3435,10 +3435,10 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) ...@@ -3435,10 +3435,10 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
found_key.offset = 0; found_key.offset = 0;
inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL); inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL);
ret = PTR_ERR_OR_ZERO(inode); ret = PTR_ERR_OR_ZERO(inode);
if (ret && ret != -ESTALE) if (ret && ret != -ENOENT)
goto out; goto out;
if (ret == -ESTALE && root == root->fs_info->tree_root) { if (ret == -ENOENT && root == root->fs_info->tree_root) {
struct btrfs_root *dead_root; struct btrfs_root *dead_root;
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
int is_dead_root = 0; int is_dead_root = 0;
...@@ -3474,7 +3474,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) ...@@ -3474,7 +3474,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)
* Inode is already gone but the orphan item is still there, * Inode is already gone but the orphan item is still there,
* kill the orphan item. * kill the orphan item.
*/ */
if (ret == -ESTALE) { if (ret == -ENOENT) {
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
ret = PTR_ERR(trans); ret = PTR_ERR(trans);
...@@ -3633,7 +3633,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf, ...@@ -3633,7 +3633,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
/* /*
* read an inode from the btree into the in-memory inode * read an inode from the btree into the in-memory inode
*/ */
static void btrfs_read_locked_inode(struct inode *inode) static int btrfs_read_locked_inode(struct inode *inode)
{ {
struct btrfs_path *path; struct btrfs_path *path;
struct extent_buffer *leaf; struct extent_buffer *leaf;
...@@ -3652,14 +3652,19 @@ static void btrfs_read_locked_inode(struct inode *inode) ...@@ -3652,14 +3652,19 @@ static void btrfs_read_locked_inode(struct inode *inode)
filled = true; filled = true;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path) {
ret = -ENOMEM;
goto make_bad; goto make_bad;
}
memcpy(&location, &BTRFS_I(inode)->location, sizeof(location)); memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
ret = btrfs_lookup_inode(NULL, root, path, &location, 0); ret = btrfs_lookup_inode(NULL, root, path, &location, 0);
if (ret) if (ret) {
if (ret > 0)
ret = -ENOENT;
goto make_bad; goto make_bad;
}
leaf = path->nodes[0]; leaf = path->nodes[0];
...@@ -3812,11 +3817,12 @@ static void btrfs_read_locked_inode(struct inode *inode) ...@@ -3812,11 +3817,12 @@ static void btrfs_read_locked_inode(struct inode *inode)
} }
btrfs_update_iflags(inode); btrfs_update_iflags(inode);
return; return 0;
make_bad: make_bad:
btrfs_free_path(path); btrfs_free_path(path);
make_bad_inode(inode); make_bad_inode(inode);
return ret;
} }
/* /*
...@@ -4204,6 +4210,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -4204,6 +4210,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
int err = 0; int err = 0;
struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *root = BTRFS_I(dir)->root;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
u64 last_unlink_trans;
if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) if (inode->i_size > BTRFS_EMPTY_DIR_SIZE)
return -ENOTEMPTY; return -ENOTEMPTY;
...@@ -4226,11 +4233,27 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -4226,11 +4233,27 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
if (err) if (err)
goto out; goto out;
last_unlink_trans = BTRFS_I(inode)->last_unlink_trans;
/* now the directory is empty */ /* now the directory is empty */
err = btrfs_unlink_inode(trans, root, dir, d_inode(dentry), err = btrfs_unlink_inode(trans, root, dir, d_inode(dentry),
dentry->d_name.name, dentry->d_name.len); dentry->d_name.name, dentry->d_name.len);
if (!err) if (!err) {
btrfs_i_size_write(inode, 0); btrfs_i_size_write(inode, 0);
/*
* Propagate the last_unlink_trans value of the deleted dir to
* its parent directory. This is to prevent an unrecoverable
* log tree in the case we do something like this:
* 1) create dir foo
* 2) create snapshot under dir foo
* 3) delete the snapshot
* 4) rmdir foo
* 5) mkdir foo
* 6) fsync foo or some file inside foo
*/
if (last_unlink_trans >= trans->transid)
BTRFS_I(dir)->last_unlink_trans = last_unlink_trans;
}
out: out:
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root); btrfs_btree_balance_dirty(root);
...@@ -5606,7 +5629,9 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, ...@@ -5606,7 +5629,9 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (inode->i_state & I_NEW) { if (inode->i_state & I_NEW) {
btrfs_read_locked_inode(inode); int ret;
ret = btrfs_read_locked_inode(inode);
if (!is_bad_inode(inode)) { if (!is_bad_inode(inode)) {
inode_tree_add(inode); inode_tree_add(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
...@@ -5615,7 +5640,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, ...@@ -5615,7 +5640,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
} else { } else {
unlock_new_inode(inode); unlock_new_inode(inode);
iput(inode); iput(inode);
inode = ERR_PTR(-ESTALE); ASSERT(ret < 0);
inode = ERR_PTR(ret < 0 ? ret : -ESTALE);
} }
} }
......
...@@ -231,7 +231,6 @@ struct pending_dir_move { ...@@ -231,7 +231,6 @@ struct pending_dir_move {
u64 parent_ino; u64 parent_ino;
u64 ino; u64 ino;
u64 gen; u64 gen;
bool is_orphan;
struct list_head update_refs; struct list_head update_refs;
}; };
...@@ -274,6 +273,39 @@ struct name_cache_entry { ...@@ -274,6 +273,39 @@ struct name_cache_entry {
char name[]; char name[];
}; };
static void inconsistent_snapshot_error(struct send_ctx *sctx,
enum btrfs_compare_tree_result result,
const char *what)
{
const char *result_string;
switch (result) {
case BTRFS_COMPARE_TREE_NEW:
result_string = "new";
break;
case BTRFS_COMPARE_TREE_DELETED:
result_string = "deleted";
break;
case BTRFS_COMPARE_TREE_CHANGED:
result_string = "updated";
break;
case BTRFS_COMPARE_TREE_SAME:
ASSERT(0);
result_string = "unchanged";
break;
default:
ASSERT(0);
result_string = "unexpected";
}
btrfs_err(sctx->send_root->fs_info,
"Send: inconsistent snapshot, found %s %s for inode %llu without updated inode item, send root is %llu, parent root is %llu",
result_string, what, sctx->cmp_key->objectid,
sctx->send_root->root_key.objectid,
(sctx->parent_root ?
sctx->parent_root->root_key.objectid : 0));
}
static int is_waiting_for_move(struct send_ctx *sctx, u64 ino); static int is_waiting_for_move(struct send_ctx *sctx, u64 ino);
static struct waiting_dir_move * static struct waiting_dir_move *
...@@ -1861,7 +1893,8 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, ...@@ -1861,7 +1893,8 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
* was already unlinked/moved, so we can safely assume that we will not * was already unlinked/moved, so we can safely assume that we will not
* overwrite anything at this point in time. * overwrite anything at this point in time.
*/ */
if (other_inode > sctx->send_progress) { if (other_inode > sctx->send_progress ||
is_waiting_for_move(sctx, other_inode)) {
ret = get_inode_info(sctx->parent_root, other_inode, NULL, ret = get_inode_info(sctx->parent_root, other_inode, NULL,
who_gen, NULL, NULL, NULL, NULL); who_gen, NULL, NULL, NULL, NULL);
if (ret < 0) if (ret < 0)
...@@ -2502,6 +2535,8 @@ verbose_printk("btrfs: send_utimes %llu\n", ino); ...@@ -2502,6 +2535,8 @@ verbose_printk("btrfs: send_utimes %llu\n", ino);
key.type = BTRFS_INODE_ITEM_KEY; key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0; key.offset = 0;
ret = btrfs_search_slot(NULL, sctx->send_root, &key, path, 0, 0); ret = btrfs_search_slot(NULL, sctx->send_root, &key, path, 0, 0);
if (ret > 0)
ret = -ENOENT;
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -2947,6 +2982,10 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen, ...@@ -2947,6 +2982,10 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
} }
if (loc.objectid > send_progress) { if (loc.objectid > send_progress) {
struct orphan_dir_info *odi;
odi = get_orphan_dir_info(sctx, dir);
free_orphan_dir_info(sctx, odi);
ret = 0; ret = 0;
goto out; goto out;
} }
...@@ -3047,7 +3086,6 @@ static int add_pending_dir_move(struct send_ctx *sctx, ...@@ -3047,7 +3086,6 @@ static int add_pending_dir_move(struct send_ctx *sctx,
pm->parent_ino = parent_ino; pm->parent_ino = parent_ino;
pm->ino = ino; pm->ino = ino;
pm->gen = ino_gen; pm->gen = ino_gen;
pm->is_orphan = is_orphan;
INIT_LIST_HEAD(&pm->list); INIT_LIST_HEAD(&pm->list);
INIT_LIST_HEAD(&pm->update_refs); INIT_LIST_HEAD(&pm->update_refs);
RB_CLEAR_NODE(&pm->node); RB_CLEAR_NODE(&pm->node);
...@@ -3113,6 +3151,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx, ...@@ -3113,6 +3151,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx,
return NULL; return NULL;
} }
static int path_loop(struct send_ctx *sctx, struct fs_path *name,
u64 ino, u64 gen, u64 *ancestor_ino)
{
int ret = 0;
u64 parent_inode = 0;
u64 parent_gen = 0;
u64 start_ino = ino;
*ancestor_ino = 0;
while (ino != BTRFS_FIRST_FREE_OBJECTID) {
fs_path_reset(name);
if (is_waiting_for_rm(sctx, ino))
break;
if (is_waiting_for_move(sctx, ino)) {
if (*ancestor_ino == 0)
*ancestor_ino = ino;
ret = get_first_ref(sctx->parent_root, ino,
&parent_inode, &parent_gen, name);
} else {
ret = __get_cur_name_and_parent(sctx, ino, gen,
&parent_inode,
&parent_gen, name);
if (ret > 0) {
ret = 0;
break;
}
}
if (ret < 0)
break;
if (parent_inode == start_ino) {
ret = 1;
if (*ancestor_ino == 0)
*ancestor_ino = ino;
break;
}
ino = parent_inode;
gen = parent_gen;
}
return ret;
}
static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
{ {
struct fs_path *from_path = NULL; struct fs_path *from_path = NULL;
...@@ -3123,6 +3203,8 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) ...@@ -3123,6 +3203,8 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
u64 parent_ino, parent_gen; u64 parent_ino, parent_gen;
struct waiting_dir_move *dm = NULL; struct waiting_dir_move *dm = NULL;
u64 rmdir_ino = 0; u64 rmdir_ino = 0;
u64 ancestor;
bool is_orphan;
int ret; int ret;
name = fs_path_alloc(); name = fs_path_alloc();
...@@ -3135,9 +3217,10 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) ...@@ -3135,9 +3217,10 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
dm = get_waiting_dir_move(sctx, pm->ino); dm = get_waiting_dir_move(sctx, pm->ino);
ASSERT(dm); ASSERT(dm);
rmdir_ino = dm->rmdir_ino; rmdir_ino = dm->rmdir_ino;
is_orphan = dm->orphanized;
free_waiting_dir_move(sctx, dm); free_waiting_dir_move(sctx, dm);
if (pm->is_orphan) { if (is_orphan) {
ret = gen_unique_name(sctx, pm->ino, ret = gen_unique_name(sctx, pm->ino,
pm->gen, from_path); pm->gen, from_path);
} else { } else {
...@@ -3155,6 +3238,24 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) ...@@ -3155,6 +3238,24 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
goto out; goto out;
sctx->send_progress = sctx->cur_ino + 1; sctx->send_progress = sctx->cur_ino + 1;
ret = path_loop(sctx, name, pm->ino, pm->gen, &ancestor);
if (ret < 0)
goto out;
if (ret) {
LIST_HEAD(deleted_refs);
ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID);
ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor,
&pm->update_refs, &deleted_refs,
is_orphan);
if (ret < 0)
goto out;
if (rmdir_ino) {
dm = get_waiting_dir_move(sctx, pm->ino);
ASSERT(dm);
dm->rmdir_ino = rmdir_ino;
}
goto out;
}
fs_path_reset(name); fs_path_reset(name);
to_path = name; to_path = name;
name = NULL; name = NULL;
...@@ -3174,7 +3275,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) ...@@ -3174,7 +3275,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
/* already deleted */ /* already deleted */
goto finish; goto finish;
} }
ret = can_rmdir(sctx, rmdir_ino, odi->gen, sctx->cur_ino + 1); ret = can_rmdir(sctx, rmdir_ino, odi->gen, sctx->cur_ino);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (!ret) if (!ret)
...@@ -3204,8 +3305,18 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) ...@@ -3204,8 +3305,18 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
* and old parent(s). * and old parent(s).
*/ */
list_for_each_entry(cur, &pm->update_refs, list) { list_for_each_entry(cur, &pm->update_refs, list) {
if (cur->dir == rmdir_ino) /*
* The parent inode might have been deleted in the send snapshot
*/
ret = get_inode_info(sctx->send_root, cur->dir, NULL,
NULL, NULL, NULL, NULL, NULL);
if (ret == -ENOENT) {
ret = 0;
continue; continue;
}
if (ret < 0)
goto out;
ret = send_utimes(sctx, cur->dir, cur->dir_gen); ret = send_utimes(sctx, cur->dir, cur->dir_gen);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -3325,6 +3436,7 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, ...@@ -3325,6 +3436,7 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
u64 left_gen; u64 left_gen;
u64 right_gen; u64 right_gen;
int ret = 0; int ret = 0;
struct waiting_dir_move *wdm;
if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves)) if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves))
return 0; return 0;
...@@ -3383,7 +3495,8 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, ...@@ -3383,7 +3495,8 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
goto out; goto out;
} }
if (is_waiting_for_move(sctx, di_key.objectid)) { wdm = get_waiting_dir_move(sctx, di_key.objectid);
if (wdm && !wdm->orphanized) {
ret = add_pending_dir_move(sctx, ret = add_pending_dir_move(sctx,
sctx->cur_ino, sctx->cur_ino,
sctx->cur_inode_gen, sctx->cur_inode_gen,
...@@ -3470,6 +3583,7 @@ static int wait_for_parent_move(struct send_ctx *sctx, ...@@ -3470,6 +3583,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
ret = is_ancestor(sctx->parent_root, ret = is_ancestor(sctx->parent_root,
sctx->cur_ino, sctx->cur_inode_gen, sctx->cur_ino, sctx->cur_inode_gen,
ino, path_before); ino, path_before);
if (ret)
break; break;
} }
...@@ -3643,11 +3757,26 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); ...@@ -3643,11 +3757,26 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
goto out; goto out;
if (ret) { if (ret) {
struct name_cache_entry *nce; struct name_cache_entry *nce;
struct waiting_dir_move *wdm;
ret = orphanize_inode(sctx, ow_inode, ow_gen, ret = orphanize_inode(sctx, ow_inode, ow_gen,
cur->full_path); cur->full_path);
if (ret < 0) if (ret < 0)
goto out; goto out;
/*
* If ow_inode has its rename operation delayed
* make sure that its orphanized name is used in
* the source path when performing its rename
* operation.
*/
if (is_waiting_for_move(sctx, ow_inode)) {
wdm = get_waiting_dir_move(sctx,
ow_inode);
ASSERT(wdm);
wdm->orphanized = true;
}
/* /*
* Make sure we clear our orphanized inode's * Make sure we clear our orphanized inode's
* name from the name cache. This is because the * name from the name cache. This is because the
...@@ -3663,6 +3792,19 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); ...@@ -3663,6 +3792,19 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
name_cache_delete(sctx, nce); name_cache_delete(sctx, nce);
kfree(nce); kfree(nce);
} }
/*
* ow_inode might currently be an ancestor of
* cur_ino, therefore compute valid_path (the
* current path of cur_ino) again because it
* might contain the pre-orphanization name of
* ow_inode, which is no longer valid.
*/
fs_path_reset(valid_path);
ret = get_cur_path(sctx, sctx->cur_ino,
sctx->cur_inode_gen, valid_path);
if (ret < 0)
goto out;
} else { } else {
ret = send_unlink(sctx, cur->full_path); ret = send_unlink(sctx, cur->full_path);
if (ret < 0) if (ret < 0)
...@@ -5602,7 +5744,10 @@ static int changed_ref(struct send_ctx *sctx, ...@@ -5602,7 +5744,10 @@ static int changed_ref(struct send_ctx *sctx,
{ {
int ret = 0; int ret = 0;
BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid); if (sctx->cur_ino != sctx->cmp_key->objectid) {
inconsistent_snapshot_error(sctx, result, "reference");
return -EIO;
}
if (!sctx->cur_inode_new_gen && if (!sctx->cur_inode_new_gen &&
sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) { sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) {
...@@ -5627,7 +5772,10 @@ static int changed_xattr(struct send_ctx *sctx, ...@@ -5627,7 +5772,10 @@ static int changed_xattr(struct send_ctx *sctx,
{ {
int ret = 0; int ret = 0;
BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid); if (sctx->cur_ino != sctx->cmp_key->objectid) {
inconsistent_snapshot_error(sctx, result, "xattr");
return -EIO;
}
if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) { if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) {
if (result == BTRFS_COMPARE_TREE_NEW) if (result == BTRFS_COMPARE_TREE_NEW)
...@@ -5651,7 +5799,10 @@ static int changed_extent(struct send_ctx *sctx, ...@@ -5651,7 +5799,10 @@ static int changed_extent(struct send_ctx *sctx,
{ {
int ret = 0; int ret = 0;
BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid); if (sctx->cur_ino != sctx->cmp_key->objectid) {
inconsistent_snapshot_error(sctx, result, "extent");
return -EIO;
}
if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) { if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) {
if (result != BTRFS_COMPARE_TREE_DELETED) if (result != BTRFS_COMPARE_TREE_DELETED)
......
...@@ -4469,7 +4469,8 @@ static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans, ...@@ -4469,7 +4469,8 @@ static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans,
static int btrfs_check_ref_name_override(struct extent_buffer *eb, static int btrfs_check_ref_name_override(struct extent_buffer *eb,
const int slot, const int slot,
const struct btrfs_key *key, const struct btrfs_key *key,
struct inode *inode) struct inode *inode,
u64 *other_ino)
{ {
int ret; int ret;
struct btrfs_path *search_path; struct btrfs_path *search_path;
...@@ -4528,7 +4529,16 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb, ...@@ -4528,7 +4529,16 @@ static int btrfs_check_ref_name_override(struct extent_buffer *eb,
search_path, parent, search_path, parent,
name, this_name_len, 0); name, this_name_len, 0);
if (di && !IS_ERR(di)) { if (di && !IS_ERR(di)) {
struct btrfs_key di_key;
btrfs_dir_item_key_to_cpu(search_path->nodes[0],
di, &di_key);
if (di_key.type == BTRFS_INODE_ITEM_KEY) {
ret = 1; ret = 1;
*other_ino = di_key.objectid;
} else {
ret = -EAGAIN;
}
goto out; goto out;
} else if (IS_ERR(di)) { } else if (IS_ERR(di)) {
ret = PTR_ERR(di); ret = PTR_ERR(di);
...@@ -4722,17 +4732,72 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, ...@@ -4722,17 +4732,72 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
if ((min_key.type == BTRFS_INODE_REF_KEY || if ((min_key.type == BTRFS_INODE_REF_KEY ||
min_key.type == BTRFS_INODE_EXTREF_KEY) && min_key.type == BTRFS_INODE_EXTREF_KEY) &&
BTRFS_I(inode)->generation == trans->transid) { BTRFS_I(inode)->generation == trans->transid) {
u64 other_ino = 0;
ret = btrfs_check_ref_name_override(path->nodes[0], ret = btrfs_check_ref_name_override(path->nodes[0],
path->slots[0], path->slots[0],
&min_key, inode); &min_key, inode,
&other_ino);
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
goto out_unlock; goto out_unlock;
} else if (ret > 0) { } else if (ret > 0) {
err = 1; struct btrfs_key inode_key;
btrfs_set_log_full_commit(root->fs_info, trans); struct inode *other_inode;
if (ins_nr > 0) {
ins_nr++;
} else {
ins_nr = 1;
ins_start_slot = path->slots[0];
}
ret = copy_items(trans, inode, dst_path, path,
&last_extent, ins_start_slot,
ins_nr, inode_only,
logged_isize);
if (ret < 0) {
err = ret;
goto out_unlock; goto out_unlock;
} }
ins_nr = 0;
btrfs_release_path(path);
inode_key.objectid = other_ino;
inode_key.type = BTRFS_INODE_ITEM_KEY;
inode_key.offset = 0;
other_inode = btrfs_iget(root->fs_info->sb,
&inode_key, root,
NULL);
/*
* If the other inode that had a conflicting dir
* entry was deleted in the current transaction,
* we don't need to do more work nor fallback to
* a transaction commit.
*/
if (IS_ERR(other_inode) &&
PTR_ERR(other_inode) == -ENOENT) {
goto next_key;
} else if (IS_ERR(other_inode)) {
err = PTR_ERR(other_inode);
goto out_unlock;
}
/*
* We are safe logging the other inode without
* acquiring its i_mutex as long as we log with
* the LOG_INODE_EXISTS mode. We're safe against
* concurrent renames of the other inode as well
* because during a rename we pin the log and
* update the log with the new name before we
* unpin it.
*/
err = btrfs_log_inode(trans, root, other_inode,
LOG_INODE_EXISTS,
0, LLONG_MAX, ctx);
iput(other_inode);
if (err)
goto out_unlock;
else
goto next_key;
}
} }
/* Skip xattrs, we log them later with btrfs_log_all_xattrs() */ /* Skip xattrs, we log them later with btrfs_log_all_xattrs() */
...@@ -4799,7 +4864,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, ...@@ -4799,7 +4864,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
ins_nr = 0; ins_nr = 0;
} }
btrfs_release_path(path); btrfs_release_path(path);
next_key:
if (min_key.offset < (u64)-1) { if (min_key.offset < (u64)-1) {
min_key.offset++; min_key.offset++;
} else if (min_key.type < max_key.type) { } else if (min_key.type < max_key.type) {
...@@ -4993,8 +5058,12 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans, ...@@ -4993,8 +5058,12 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
if (!parent || d_really_is_negative(parent) || sb != parent->d_sb) if (!parent || d_really_is_negative(parent) || sb != parent->d_sb)
break; break;
if (IS_ROOT(parent)) if (IS_ROOT(parent)) {
inode = d_inode(parent);
if (btrfs_must_commit_transaction(trans, inode))
ret = 1;
break; break;
}
parent = dget_parent(parent); parent = dget_parent(parent);
dput(old_parent); dput(old_parent);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment