Loading fs/btrfs/delayed-ref.c +0 −27 Original line number Diff line number Diff line Loading @@ -862,33 +862,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, 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, struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, Loading fs/btrfs/delayed-ref.h +0 −3 Original line number Diff line number Diff line Loading @@ -250,9 +250,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, u64 parent, u64 ref_root, u64 owner, u64 offset, u64 reserved, int action, 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, struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, Loading fs/btrfs/file.c +8 −0 Original line number Diff line number Diff line Loading @@ -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, &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); goto out; } Loading fs/btrfs/inode.c +36 −10 Original line number Diff line number Diff line Loading @@ -3436,10 +3436,10 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) found_key.offset = 0; inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL); ret = PTR_ERR_OR_ZERO(inode); if (ret && ret != -ESTALE) if (ret && ret != -ENOENT) 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_fs_info *fs_info = root->fs_info; int is_dead_root = 0; Loading Loading @@ -3475,7 +3475,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) * Inode is already gone but the orphan item is still there, * kill the orphan item. */ if (ret == -ESTALE) { if (ret == -ENOENT) { trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); Loading Loading @@ -3634,7 +3634,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf, /* * 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 extent_buffer *leaf; Loading @@ -3653,14 +3653,19 @@ static void btrfs_read_locked_inode(struct inode *inode) filled = true; path = btrfs_alloc_path(); if (!path) if (!path) { ret = -ENOMEM; goto make_bad; } memcpy(&location, &BTRFS_I(inode)->location, sizeof(location)); ret = btrfs_lookup_inode(NULL, root, path, &location, 0); if (ret) if (ret) { if (ret > 0) ret = -ENOENT; goto make_bad; } leaf = path->nodes[0]; Loading Loading @@ -3813,11 +3818,12 @@ static void btrfs_read_locked_inode(struct inode *inode) } btrfs_update_iflags(inode); return; return 0; make_bad: btrfs_free_path(path); make_bad_inode(inode); return ret; } /* Loading Loading @@ -4205,6 +4211,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) int err = 0; struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_trans_handle *trans; u64 last_unlink_trans; if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) return -ENOTEMPTY; Loading @@ -4227,11 +4234,27 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) if (err) goto out; last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; /* now the directory is empty */ err = btrfs_unlink_inode(trans, root, dir, d_inode(dentry), dentry->d_name.name, dentry->d_name.len); if (!err) if (!err) { 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: btrfs_end_transaction(trans, root); btrfs_btree_balance_dirty(root); Loading Loading @@ -5607,7 +5630,9 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, return ERR_PTR(-ENOMEM); if (inode->i_state & I_NEW) { btrfs_read_locked_inode(inode); int ret; ret = btrfs_read_locked_inode(inode); if (!is_bad_inode(inode)) { inode_tree_add(inode); unlock_new_inode(inode); Loading @@ -5616,7 +5641,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, } else { unlock_new_inode(inode); iput(inode); inode = ERR_PTR(-ESTALE); ASSERT(ret < 0); inode = ERR_PTR(ret < 0 ? ret : -ESTALE); } } Loading fs/btrfs/send.c +162 −11 Original line number Diff line number Diff line Loading @@ -231,7 +231,6 @@ struct pending_dir_move { u64 parent_ino; u64 ino; u64 gen; bool is_orphan; struct list_head update_refs; }; Loading Loading @@ -274,6 +273,39 @@ struct name_cache_entry { 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 struct waiting_dir_move * Loading Loading @@ -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 * 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, who_gen, NULL, NULL, NULL, NULL); if (ret < 0) Loading Loading @@ -2502,6 +2535,8 @@ verbose_printk("btrfs: send_utimes %llu\n", ino); key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(NULL, sctx->send_root, &key, path, 0, 0); if (ret > 0) ret = -ENOENT; if (ret < 0) goto out; Loading Loading @@ -2947,6 +2982,10 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen, } 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; goto out; } Loading Loading @@ -3047,7 +3086,6 @@ static int add_pending_dir_move(struct send_ctx *sctx, pm->parent_ino = parent_ino; pm->ino = ino; pm->gen = ino_gen; pm->is_orphan = is_orphan; INIT_LIST_HEAD(&pm->list); INIT_LIST_HEAD(&pm->update_refs); RB_CLEAR_NODE(&pm->node); Loading Loading @@ -3113,6 +3151,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx, 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) { struct fs_path *from_path = NULL; Loading @@ -3123,6 +3203,8 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) u64 parent_ino, parent_gen; struct waiting_dir_move *dm = NULL; u64 rmdir_ino = 0; u64 ancestor; bool is_orphan; int ret; name = fs_path_alloc(); Loading @@ -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); ASSERT(dm); rmdir_ino = dm->rmdir_ino; is_orphan = dm->orphanized; free_waiting_dir_move(sctx, dm); if (pm->is_orphan) { if (is_orphan) { ret = gen_unique_name(sctx, pm->ino, pm->gen, from_path); } else { Loading @@ -3155,6 +3238,24 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) goto out; 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); to_path = name; name = NULL; Loading @@ -3174,7 +3275,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) /* already deleted */ 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) goto out; if (!ret) Loading Loading @@ -3204,8 +3305,18 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) * and old parent(s). */ 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; } if (ret < 0) goto out; ret = send_utimes(sctx, cur->dir, cur->dir_gen); if (ret < 0) goto out; Loading Loading @@ -3325,6 +3436,7 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, u64 left_gen; u64 right_gen; int ret = 0; struct waiting_dir_move *wdm; if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves)) return 0; Loading Loading @@ -3383,7 +3495,8 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, 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, sctx->cur_ino, sctx->cur_inode_gen, Loading Loading @@ -3470,6 +3583,7 @@ static int wait_for_parent_move(struct send_ctx *sctx, ret = is_ancestor(sctx->parent_root, sctx->cur_ino, sctx->cur_inode_gen, ino, path_before); if (ret) break; } Loading Loading @@ -3643,11 +3757,26 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); goto out; if (ret) { struct name_cache_entry *nce; struct waiting_dir_move *wdm; ret = orphanize_inode(sctx, ow_inode, ow_gen, cur->full_path); if (ret < 0) 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 * name from the name cache. This is because the Loading @@ -3663,6 +3792,19 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); name_cache_delete(sctx, 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 { ret = send_unlink(sctx, cur->full_path); if (ret < 0) Loading Loading @@ -5602,7 +5744,10 @@ static int changed_ref(struct send_ctx *sctx, { 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 && sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) { Loading @@ -5627,7 +5772,10 @@ static int changed_xattr(struct send_ctx *sctx, { 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 (result == BTRFS_COMPARE_TREE_NEW) Loading @@ -5651,7 +5799,10 @@ static int changed_extent(struct send_ctx *sctx, { 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 (result != BTRFS_COMPARE_TREE_DELETED) Loading Loading
fs/btrfs/delayed-ref.c +0 −27 Original line number Diff line number Diff line Loading @@ -862,33 +862,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, 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, struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, Loading
fs/btrfs/delayed-ref.h +0 −3 Original line number Diff line number Diff line Loading @@ -250,9 +250,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, u64 parent, u64 ref_root, u64 owner, u64 offset, u64 reserved, int action, 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, struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, Loading
fs/btrfs/file.c +8 −0 Original line number Diff line number Diff line Loading @@ -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, &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); goto out; } Loading
fs/btrfs/inode.c +36 −10 Original line number Diff line number Diff line Loading @@ -3436,10 +3436,10 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) found_key.offset = 0; inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL); ret = PTR_ERR_OR_ZERO(inode); if (ret && ret != -ESTALE) if (ret && ret != -ENOENT) 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_fs_info *fs_info = root->fs_info; int is_dead_root = 0; Loading Loading @@ -3475,7 +3475,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) * Inode is already gone but the orphan item is still there, * kill the orphan item. */ if (ret == -ESTALE) { if (ret == -ENOENT) { trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); Loading Loading @@ -3634,7 +3634,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf, /* * 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 extent_buffer *leaf; Loading @@ -3653,14 +3653,19 @@ static void btrfs_read_locked_inode(struct inode *inode) filled = true; path = btrfs_alloc_path(); if (!path) if (!path) { ret = -ENOMEM; goto make_bad; } memcpy(&location, &BTRFS_I(inode)->location, sizeof(location)); ret = btrfs_lookup_inode(NULL, root, path, &location, 0); if (ret) if (ret) { if (ret > 0) ret = -ENOENT; goto make_bad; } leaf = path->nodes[0]; Loading Loading @@ -3813,11 +3818,12 @@ static void btrfs_read_locked_inode(struct inode *inode) } btrfs_update_iflags(inode); return; return 0; make_bad: btrfs_free_path(path); make_bad_inode(inode); return ret; } /* Loading Loading @@ -4205,6 +4211,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) int err = 0; struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_trans_handle *trans; u64 last_unlink_trans; if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) return -ENOTEMPTY; Loading @@ -4227,11 +4234,27 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) if (err) goto out; last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; /* now the directory is empty */ err = btrfs_unlink_inode(trans, root, dir, d_inode(dentry), dentry->d_name.name, dentry->d_name.len); if (!err) if (!err) { 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: btrfs_end_transaction(trans, root); btrfs_btree_balance_dirty(root); Loading Loading @@ -5607,7 +5630,9 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, return ERR_PTR(-ENOMEM); if (inode->i_state & I_NEW) { btrfs_read_locked_inode(inode); int ret; ret = btrfs_read_locked_inode(inode); if (!is_bad_inode(inode)) { inode_tree_add(inode); unlock_new_inode(inode); Loading @@ -5616,7 +5641,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, } else { unlock_new_inode(inode); iput(inode); inode = ERR_PTR(-ESTALE); ASSERT(ret < 0); inode = ERR_PTR(ret < 0 ? ret : -ESTALE); } } Loading
fs/btrfs/send.c +162 −11 Original line number Diff line number Diff line Loading @@ -231,7 +231,6 @@ struct pending_dir_move { u64 parent_ino; u64 ino; u64 gen; bool is_orphan; struct list_head update_refs; }; Loading Loading @@ -274,6 +273,39 @@ struct name_cache_entry { 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 struct waiting_dir_move * Loading Loading @@ -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 * 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, who_gen, NULL, NULL, NULL, NULL); if (ret < 0) Loading Loading @@ -2502,6 +2535,8 @@ verbose_printk("btrfs: send_utimes %llu\n", ino); key.type = BTRFS_INODE_ITEM_KEY; key.offset = 0; ret = btrfs_search_slot(NULL, sctx->send_root, &key, path, 0, 0); if (ret > 0) ret = -ENOENT; if (ret < 0) goto out; Loading Loading @@ -2947,6 +2982,10 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen, } 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; goto out; } Loading Loading @@ -3047,7 +3086,6 @@ static int add_pending_dir_move(struct send_ctx *sctx, pm->parent_ino = parent_ino; pm->ino = ino; pm->gen = ino_gen; pm->is_orphan = is_orphan; INIT_LIST_HEAD(&pm->list); INIT_LIST_HEAD(&pm->update_refs); RB_CLEAR_NODE(&pm->node); Loading Loading @@ -3113,6 +3151,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx, 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) { struct fs_path *from_path = NULL; Loading @@ -3123,6 +3203,8 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) u64 parent_ino, parent_gen; struct waiting_dir_move *dm = NULL; u64 rmdir_ino = 0; u64 ancestor; bool is_orphan; int ret; name = fs_path_alloc(); Loading @@ -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); ASSERT(dm); rmdir_ino = dm->rmdir_ino; is_orphan = dm->orphanized; free_waiting_dir_move(sctx, dm); if (pm->is_orphan) { if (is_orphan) { ret = gen_unique_name(sctx, pm->ino, pm->gen, from_path); } else { Loading @@ -3155,6 +3238,24 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) goto out; 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); to_path = name; name = NULL; Loading @@ -3174,7 +3275,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) /* already deleted */ 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) goto out; if (!ret) Loading Loading @@ -3204,8 +3305,18 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) * and old parent(s). */ 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; } if (ret < 0) goto out; ret = send_utimes(sctx, cur->dir, cur->dir_gen); if (ret < 0) goto out; Loading Loading @@ -3325,6 +3436,7 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, u64 left_gen; u64 right_gen; int ret = 0; struct waiting_dir_move *wdm; if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves)) return 0; Loading Loading @@ -3383,7 +3495,8 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, 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, sctx->cur_ino, sctx->cur_inode_gen, Loading Loading @@ -3470,6 +3583,7 @@ static int wait_for_parent_move(struct send_ctx *sctx, ret = is_ancestor(sctx->parent_root, sctx->cur_ino, sctx->cur_inode_gen, ino, path_before); if (ret) break; } Loading Loading @@ -3643,11 +3757,26 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); goto out; if (ret) { struct name_cache_entry *nce; struct waiting_dir_move *wdm; ret = orphanize_inode(sctx, ow_inode, ow_gen, cur->full_path); if (ret < 0) 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 * name from the name cache. This is because the Loading @@ -3663,6 +3792,19 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); name_cache_delete(sctx, 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 { ret = send_unlink(sctx, cur->full_path); if (ret < 0) Loading Loading @@ -5602,7 +5744,10 @@ static int changed_ref(struct send_ctx *sctx, { 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 && sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) { Loading @@ -5627,7 +5772,10 @@ static int changed_xattr(struct send_ctx *sctx, { 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 (result == BTRFS_COMPARE_TREE_NEW) Loading @@ -5651,7 +5799,10 @@ static int changed_extent(struct send_ctx *sctx, { 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 (result != BTRFS_COMPARE_TREE_DELETED) Loading