diff mbox series

[T,SRU,1/1] Btrfs: send, don't send rmdir for same target multiple times

Message ID 20190103110727.23558-2-po-hsu.lin@canonical.com
State New
Headers show
Series [T,SRU,1/1] Btrfs: send, don't send rmdir for same target multiple times | expand

Commit Message

Po-Hsu Lin Jan. 3, 2019, 11:07 a.m. UTC
From: Filipe Manana <fdmanana@gmail.com>

BugLink: https://bugs.launchpad.net/bugs/1809868

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>
(backported from commit 29d6d30f5c8aa58b04f40a58442df3bcaae5a1d5)
Signed-off-by: Po-Hsu Lin <po-hsu.lin@canonical.com>
---
 fs/btrfs/send.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Comments

Kleber Sacilotto de Souza Jan. 8, 2019, 1:24 p.m. UTC | #1
On 1/3/19 12:07 PM, Po-Hsu Lin wrote:
> From: Filipe Manana <fdmanana@gmail.com>
>
> BugLink: https://bugs.launchpad.net/bugs/1809868
>
> 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>
> (backported from commit 29d6d30f5c8aa58b04f40a58442df3bcaae5a1d5)

Simple backport, regression potential seems to be low.

Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>

> Signed-off-by: Po-Hsu Lin <po-hsu.lin@canonical.com>
> ---
>  fs/btrfs/send.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index d553752..9cc2ef0 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -2686,6 +2686,7 @@ static int process_recorded_refs(struct send_ctx *sctx)
>  	u64 ow_gen;
>  	int did_overwrite = 0;
>  	int is_orphan = 0;
> +	u64 last_dir_ino_rm = 0;
>  
>  verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>  
> @@ -2941,7 +2942,8 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>  			ret = send_utimes(sctx, cur->dir, cur->dir_gen);
>  			if (ret < 0)
>  				goto out;
> -		} else if (ret == inode_state_did_delete) {
> +		} else if (ret == inode_state_did_delete &&
> +			   cur->dir != last_dir_ino_rm) {
>  			ret = can_rmdir(sctx, cur->dir, sctx->cur_ino);
>  			if (ret < 0)
>  				goto out;
> @@ -2953,6 +2955,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>  				ret = send_rmdir(sctx, valid_path);
>  				if (ret < 0)
>  					goto out;
> +				last_dir_ino_rm = cur->dir;
>  			}
>  		}
>  	}
Stefan Bader Jan. 9, 2019, 10:49 a.m. UTC | #2
On 03.01.19 12:07, Po-Hsu Lin wrote:
> From: Filipe Manana <fdmanana@gmail.com>
> 
> BugLink: https://bugs.launchpad.net/bugs/1809868
> 
> 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>
> (backported from commit 29d6d30f5c8aa58b04f40a58442df3bcaae5a1d5)
> Signed-off-by: Po-Hsu Lin <po-hsu.lin@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
> ---
>  fs/btrfs/send.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index d553752..9cc2ef0 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -2686,6 +2686,7 @@ static int process_recorded_refs(struct send_ctx *sctx)
>  	u64 ow_gen;
>  	int did_overwrite = 0;
>  	int is_orphan = 0;
> +	u64 last_dir_ino_rm = 0;
>  
>  verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>  
> @@ -2941,7 +2942,8 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>  			ret = send_utimes(sctx, cur->dir, cur->dir_gen);
>  			if (ret < 0)
>  				goto out;
> -		} else if (ret == inode_state_did_delete) {
> +		} else if (ret == inode_state_did_delete &&
> +			   cur->dir != last_dir_ino_rm) {
>  			ret = can_rmdir(sctx, cur->dir, sctx->cur_ino);
>  			if (ret < 0)
>  				goto out;
> @@ -2953,6 +2955,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
>  				ret = send_rmdir(sctx, valid_path);
>  				if (ret < 0)
>  					goto out;
> +				last_dir_ino_rm = cur->dir;
>  			}
>  		}
>  	}
>
diff mbox series

Patch

diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index d553752..9cc2ef0 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -2686,6 +2686,7 @@  static int process_recorded_refs(struct send_ctx *sctx)
 	u64 ow_gen;
 	int did_overwrite = 0;
 	int is_orphan = 0;
+	u64 last_dir_ino_rm = 0;
 
 verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
 
@@ -2941,7 +2942,8 @@  verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
 			ret = send_utimes(sctx, cur->dir, cur->dir_gen);
 			if (ret < 0)
 				goto out;
-		} else if (ret == inode_state_did_delete) {
+		} else if (ret == inode_state_did_delete &&
+			   cur->dir != last_dir_ino_rm) {
 			ret = can_rmdir(sctx, cur->dir, sctx->cur_ino);
 			if (ret < 0)
 				goto out;
@@ -2953,6 +2955,7 @@  verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
 				ret = send_rmdir(sctx, valid_path);
 				if (ret < 0)
 					goto out;
+				last_dir_ino_rm = cur->dir;
 			}
 		}
 	}