aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2016-07-31 20:50:37 -0400
committerFilipe Manana <fdmanana@suse.com>2016-08-01 02:31:41 -0400
commit951555856b88aa47bc238de6b4c6e97bfd9d36df (patch)
tree2c593dc8a659d69f5b92d6bfc3c7ba5b0fe2d046
parent15b253eace1f98dabbc6e03f6793fcf8603b1655 (diff)
Btrfs: send, don't bug on inconsistent snapshots
When doing an incremental send, if we find a new/modified/deleted extent, reference or xattr without having previously processed the corresponding inode item we end up exexuting a BUG_ON(). This is because whenever an extent, xattr or reference is added, modified or deleted, we always expect to have the corresponding inode item updated. However there are situations where this will not happen due to transient -ENOMEM or -ENOSPC errors when doing delayed inode updates. For example, when punching holes we can succeed in deleting and modifying (shrinking) extents but later fail to do the delayed inode update. So after such failure we close our transaction handle and right after a snapshot of the fs/subvol tree can be made and used later for a send operation. The same thing can happen during truncate, link, unlink, and xattr related operations. So instead of executing a BUG_ON, make send return an -EIO error and print an informative error message do dmesg/syslog. Signed-off-by: Filipe Manana <fdmanana@suse.com>
-rw-r--r--fs/btrfs/send.c48
1 files changed, 45 insertions, 3 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 2db8dc89ca41..efe129fe2678 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -273,6 +273,39 @@ struct name_cache_entry {
273 char name[]; 273 char name[];
274}; 274};
275 275
276static void inconsistent_snapshot_error(struct send_ctx *sctx,
277 enum btrfs_compare_tree_result result,
278 const char *what)
279{
280 const char *result_string;
281
282 switch (result) {
283 case BTRFS_COMPARE_TREE_NEW:
284 result_string = "new";
285 break;
286 case BTRFS_COMPARE_TREE_DELETED:
287 result_string = "deleted";
288 break;
289 case BTRFS_COMPARE_TREE_CHANGED:
290 result_string = "updated";
291 break;
292 case BTRFS_COMPARE_TREE_SAME:
293 ASSERT(0);
294 result_string = "unchanged";
295 break;
296 default:
297 ASSERT(0);
298 result_string = "unexpected";
299 }
300
301 btrfs_err(sctx->send_root->fs_info,
302 "Send: inconsistent snapshot, found %s %s for inode %llu without updated inode item, send root is %llu, parent root is %llu",
303 result_string, what, sctx->cmp_key->objectid,
304 sctx->send_root->root_key.objectid,
305 (sctx->parent_root ?
306 sctx->parent_root->root_key.objectid : 0));
307}
308
276static int is_waiting_for_move(struct send_ctx *sctx, u64 ino); 309static int is_waiting_for_move(struct send_ctx *sctx, u64 ino);
277 310
278static struct waiting_dir_move * 311static struct waiting_dir_move *
@@ -5711,7 +5744,10 @@ static int changed_ref(struct send_ctx *sctx,
5711{ 5744{
5712 int ret = 0; 5745 int ret = 0;
5713 5746
5714 BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid); 5747 if (sctx->cur_ino != sctx->cmp_key->objectid) {
5748 inconsistent_snapshot_error(sctx, result, "reference");
5749 return -EIO;
5750 }
5715 5751
5716 if (!sctx->cur_inode_new_gen && 5752 if (!sctx->cur_inode_new_gen &&
5717 sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) { 5753 sctx->cur_ino != BTRFS_FIRST_FREE_OBJECTID) {
@@ -5736,7 +5772,10 @@ static int changed_xattr(struct send_ctx *sctx,
5736{ 5772{
5737 int ret = 0; 5773 int ret = 0;
5738 5774
5739 BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid); 5775 if (sctx->cur_ino != sctx->cmp_key->objectid) {
5776 inconsistent_snapshot_error(sctx, result, "xattr");
5777 return -EIO;
5778 }
5740 5779
5741 if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) { 5780 if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) {
5742 if (result == BTRFS_COMPARE_TREE_NEW) 5781 if (result == BTRFS_COMPARE_TREE_NEW)
@@ -5760,7 +5799,10 @@ static int changed_extent(struct send_ctx *sctx,
5760{ 5799{
5761 int ret = 0; 5800 int ret = 0;
5762 5801
5763 BUG_ON(sctx->cur_ino != sctx->cmp_key->objectid); 5802 if (sctx->cur_ino != sctx->cmp_key->objectid) {
5803 inconsistent_snapshot_error(sctx, result, "extent");
5804 return -EIO;
5805 }
5764 5806
5765 if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) { 5807 if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) {
5766 if (result != BTRFS_COMPARE_TREE_DELETED) 5808 if (result != BTRFS_COMPARE_TREE_DELETED)