aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/send.c
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@gmail.com>2014-02-16 16:01:39 -0500
committerJosef Bacik <jbacik@fb.com>2014-03-10 15:16:48 -0400
commit29d6d30f5c8aa58b04f40a58442df3bcaae5a1d5 (patch)
treec5a6585d7c7ba0d92d6dec417a109445aef9c82c /fs/btrfs/send.c
parent2b863a135f22f242ba4fc669f3a6b2f6c826832c (diff)
Btrfs: send, don't send rmdir for same target multiple times
When doing an incremental send, if we delete a directory that has N > 1 hardlinks for the same file and that file has the highest inode number inside the directory contents, an incremental send would send N times an rmdir operation against the directory. This made the btrfs receive command fail on the second rmdir instruction, as the target directory didn't exist anymore. Steps to reproduce the issue: $ mkfs.btrfs -f /dev/sdb3 $ mount /dev/sdb3 /mnt/btrfs $ mkdir -p /mnt/btrfs/a/b/c $ echo 'ola mundo' > /mnt/btrfs/a/b/c/foo.txt $ ln /mnt/btrfs/a/b/c/foo.txt /mnt/btrfs/a/b/c/bar.txt $ btrfs subvolume snapshot -r /mnt/btrfs /mnt/btrfs/snap1 $ btrfs send /mnt/btrfs/snap1 -f /tmp/base.send $ rm -f /mnt/btrfs/a/b/c/foo.txt $ rm -f /mnt/btrfs/a/b/c/bar.txt $ rmdir /mnt/btrfs/a/b/c $ btrfs subvolume snapshot -r /mnt/btrfs /mnt/btrfs/snap2 $ btrfs send -p /mnt/btrfs/snap1 /mnt/btrfs/snap2 -f /tmp/incremental.send $ umount /mnt/btrfs $ mkfs.btrfs -f /dev/sdb3 $ mount /dev/sdb3 /mnt/btrfs $ btrfs receive /mnt/btrfs -f /tmp/base.send $ btrfs receive /mnt/btrfs -f /tmp/incremental.send The second btrfs receive command failed with: ERROR: rmdir o259-6-0 failed. No such file or directory A test case for xfstests follows. Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com> Signed-off-by: Josef Bacik <jbacik@fb.com>
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r--fs/btrfs/send.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index cb9502adccdc..cdfd4357a784 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -3085,6 +3085,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
3085 u64 ow_gen; 3085 u64 ow_gen;
3086 int did_overwrite = 0; 3086 int did_overwrite = 0;
3087 int is_orphan = 0; 3087 int is_orphan = 0;
3088 u64 last_dir_ino_rm = 0;
3088 3089
3089verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); 3090verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
3090 3091
@@ -3349,7 +3350,8 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
3349 ret = send_utimes(sctx, cur->dir, cur->dir_gen); 3350 ret = send_utimes(sctx, cur->dir, cur->dir_gen);
3350 if (ret < 0) 3351 if (ret < 0)
3351 goto out; 3352 goto out;
3352 } else if (ret == inode_state_did_delete) { 3353 } else if (ret == inode_state_did_delete &&
3354 cur->dir != last_dir_ino_rm) {
3353 ret = can_rmdir(sctx, cur->dir, sctx->cur_ino); 3355 ret = can_rmdir(sctx, cur->dir, sctx->cur_ino);
3354 if (ret < 0) 3356 if (ret < 0)
3355 goto out; 3357 goto out;
@@ -3361,6 +3363,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
3361 ret = send_rmdir(sctx, valid_path); 3363 ret = send_rmdir(sctx, valid_path);
3362 if (ret < 0) 3364 if (ret < 0)
3363 goto out; 3365 goto out;
3366 last_dir_ino_rm = cur->dir;
3364 } 3367 }
3365 } 3368 }
3366 } 3369 }