linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Incremental btrfs receive in opposite direction fails
@ 2012-12-28 22:42 junlion
  2012-12-29 13:00 ` Alex Lyakas
  0 siblings, 1 reply; 6+ messages in thread
From: junlion @ 2012-12-28 22:42 UTC (permalink / raw)
  To: linux-btrfs

Hi everyone!

(I use kernel 3.7.1 and mason/btrfs-progs.git head + the receive
lchown patch [1]. Is there a more up to date btrfs-progs repo for
send/receive enthusiasts?)

What I'm trying is basically:

# Transfer a snapshot of my old system from a backup to the new system
btrfs send /mnt/bak/.snap | btrfs receive /                 # OK

# Merge the old system's files with the new system's files
btrfs subvolume snapshot /.snap /.snap-merge                # OK
mkdir /.snap-merge/.old                                     # OK
mv /.snap-merge/* /.snap-merge/.old                         # OK
cp -a --reflink /* /.snap-merge                             # OK
btrfs subvolume snapshot -r /.snap-merge /.snap-merge-done  # OK

# Incrementally transfer the merged files back to the backup drive
btrfs send -p /.snap /.snap-merge-done | btrfs receive -p /mnt/bak

The last step fails due to the changed direction of the transfer.
"ERROR: could not find parent subvolume". It seems like btrfs receive
cannot find the parent/clone source because it is searching for its UUID
only by looking through the /mnt/bak snapshots' received_uuid fields.

Is there a way for me to directly change the received_uuid of
/mnt/bak/.snap to make it identical to the UUID of /.snap? This looks
like the easiest way if I only need to do it once.

[1] https://patchwork.kernel.org/patch/1606091/

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Incremental btrfs receive in opposite direction fails
  2012-12-28 22:42 Incremental btrfs receive in opposite direction fails junlion
@ 2012-12-29 13:00 ` Alex Lyakas
  2012-12-30  6:40   ` junlion
  0 siblings, 1 reply; 6+ messages in thread
From: Alex Lyakas @ 2012-12-29 13:00 UTC (permalink / raw)
  To: junlion; +Cc: linux-btrfs

Hi,

On Sat, Dec 29, 2012 at 12:42 AM,  <junlion@tormail.org> wrote:
> Hi everyone!
>
> (I use kernel 3.7.1 and mason/btrfs-progs.git head + the receive
> lchown patch [1]. Is there a more up to date btrfs-progs repo for
> send/receive enthusiasts?)
There is no special repo, but you may want, in addition to the patch
you mentioned, apply this one as well:
https://patchwork.kernel.org/patch/1604391/
Whether you need it or not depends on how your subvolumes are
organized in a file tree.


>
> What I'm trying is basically:
>
> # Transfer a snapshot of my old system from a backup to the new system
> btrfs send /mnt/bak/.snap | btrfs receive /                 # OK
>
> # Merge the old system's files with the new system's files
> btrfs subvolume snapshot /.snap /.snap-merge                # OK
> mkdir /.snap-merge/.old                                     # OK
> mv /.snap-merge/* /.snap-merge/.old                         # OK
> cp -a --reflink /* /.snap-merge                             # OK
> btrfs subvolume snapshot -r /.snap-merge /.snap-merge-done  # OK
>
> # Incrementally transfer the merged files back to the backup drive
> btrfs send -p /.snap /.snap-merge-done | btrfs receive -p /mnt/bak
>
> The last step fails due to the changed direction of the transfer.
> "ERROR: could not find parent subvolume". It seems like btrfs receive
> cannot find the parent/clone source because it is searching for its UUID
> only by looking through the /mnt/bak snapshots' received_uuid fields.
>
> Is there a way for me to directly change the received_uuid of
> /mnt/bak/.snap to make it identical to the UUID of /.snap? This looks
> like the easiest way if I only need to do it once.
There is no implemented way, but since you debugged this far, you can
put up some code that sends BTRFS_IOC_SET_RECEIVED_SUBVOL, which is
the one setting the received_uuid (and some other small stuff, please
check the kernel code for it).

Thanks,
Alex.


>
> [1] https://patchwork.kernel.org/patch/1606091/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Incremental btrfs receive in opposite direction fails
  2012-12-29 13:00 ` Alex Lyakas
@ 2012-12-30  6:40   ` junlion
  2013-01-02 16:57     ` Jan Schmidt
  0 siblings, 1 reply; 6+ messages in thread
From: junlion @ 2012-12-30  6:40 UTC (permalink / raw)
  To: Alex Lyakas; +Cc: linux-btrfs

[-- Attachment #1: Type: text/plain, Size: 1235 bytes --]

On 2012-12-29 15:00 +0200, Alex Lyakas wrote:
> There is no special repo, but you may want, in addition to the patch
> you mentioned, apply this one as well:
> https://patchwork.kernel.org/patch/1604391/

Very useful! Somehow a few lines got wrapped though.

> > Is there a way for me to directly change the received_uuid of
> > /mnt/bak/.snap to make it identical to the UUID of /.snap? This looks
> > like the easiest way if I only need to do it once.
> There is no implemented way, but since you debugged this far, you can
> put up some code that sends BTRFS_IOC_SET_RECEIVED_SUBVOL, which is
> the one setting the received_uuid (and some other small stuff, please
> check the kernel code for it).

Ah, BTRFS_IOC_SET_RECEIVED_SUBVOL. Thanks for pointing me in the right
direction. After some hacking, it worked without eating my data... These
are the two commands to run before the failing one:

btrfs subvolume snapshot /mnt/bak/.snap /mnt/bak/.snap-mangled
uu / .snap /mnt/bak/.snap-mangled

... where uu is my crude little received_uuid and stransid display and
adjustment tool. I've attached its source.

Would be cool if someone modified btrfs send/receive to handle this use
case automatically, but it's too difficult for me.

[-- Attachment #2: unwrapped__btrfs-progs-Fix-the-receive-code-pathing.patch --]
[-- Type: text/x-diff, Size: 4603 bytes --]

diff --git a/cmds-receive.c b/cmds-receive.c
index a8be6fa..40d2e48 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -52,11 +52,13 @@ static int g_verbose = 0;
 struct btrfs_receive
 {
 	int mnt_fd;
+	int dest_dir_fd;
 
 	int write_fd;
 	char *write_path;
 
 	char *root_path;
+	char *dest_dir_path; /* relative to root_path */
 	char *full_subvol_path;
 
 	struct subvol_info *cur_subvol;
@@ -150,8 +152,11 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
 	r->cur_subvol = calloc(1, sizeof(*r->cur_subvol));
 	r->parent_subvol = NULL;
 
-	r->cur_subvol->path = strdup(path);
-	r->full_subvol_path = path_cat(r->root_path, path);
+	if (strlen(r->dest_dir_path) == 0)
+		r->cur_subvol->path = strdup(path);
+	else
+		r->cur_subvol->path = path_cat(r->dest_dir_path, path);
+	r->full_subvol_path = path_cat3(r->root_path, r->dest_dir_path, path);
 
 	fprintf(stderr, "At subvol %s\n", path);
 
@@ -167,7 +172,7 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
 
 	memset(&args_v1, 0, sizeof(args_v1));
 	strcpy(args_v1.name, path);
-	ret = ioctl(r->mnt_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
+	ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
 	if (ret < 0) {
 		ret = -errno;
 		fprintf(stderr, "ERROR: creating subvolume %s failed. "
@@ -195,8 +200,11 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
 	r->cur_subvol = calloc(1, sizeof(*r->cur_subvol));
 	r->parent_subvol = NULL;
 
-	r->cur_subvol->path = strdup(path);
-	r->full_subvol_path = path_cat(r->root_path, path);
+	if (strlen(r->dest_dir_path) == 0)
+		r->cur_subvol->path = strdup(path);
+	else
+		r->cur_subvol->path = path_cat(r->dest_dir_path, path);
+	r->full_subvol_path = path_cat3(r->root_path, r->dest_dir_path, path);
 
 	fprintf(stderr, "At snapshot %s\n", path);
 
@@ -243,7 +251,7 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
 		goto out;
 	}
 
-	ret = ioctl(r->mnt_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
+	ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
 	close(args_v2.fd);
 	if (ret < 0) {
 		ret = -errno;
@@ -790,17 +798,48 @@ struct btrfs_send_ops send_ops = {
 int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd)
 {
 	int ret;
+	char *dest_dir_full_path;
 	int end = 0;
 
-	r->root_path = strdup(tomnt);
-	r->mnt_fd = open(tomnt, O_RDONLY | O_NOATIME);
+	dest_dir_full_path = realpath(tomnt, NULL);
+	if (!dest_dir_full_path) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: realpath(%s) failed. %s\n", tomnt,
+				strerror(-ret));
+		goto out;
+	}
+	r->dest_dir_fd = open(dest_dir_full_path, O_RDONLY | O_NOATIME);
+	if (r->dest_dir_fd < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: failed to open destination directory %s. %s\n",
+			    dest_dir_full_path, strerror(-ret));
+		goto out;
+	}
+
+	ret = find_mount_root(dest_dir_full_path, &r->root_path);
+	if (ret < 0) {
+		ret = -EINVAL;
+		fprintf(stderr, "ERROR: failed to determine mount point "
+				"for %s\n", dest_dir_full_path);
+		goto out;
+	}
+	r->mnt_fd = open(r->root_path, O_RDONLY | O_NOATIME);
 	if (r->mnt_fd < 0) {
 		ret = -errno;
-		fprintf(stderr, "ERROR: failed to open %s. %s\n", tomnt,
+		fprintf(stderr, "ERROR: failed to open %s. %s\n", r->root_path,
 				strerror(-ret));
 		goto out;
 	}
 
+	/*
+	 * find_mount_root returns a root_path that is a subpath of
+	 * dest_dir_full_path. Now get the other part of root_path,
+	 * which is the destination dir relative to root_path.
+	 */
+	r->dest_dir_path = dest_dir_full_path + strlen(r->root_path);
+	if (r->dest_dir_path[0] == '/')
+		r->dest_dir_path++;
+
 	ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
 	if (ret < 0)
 		return ret;
diff --git a/cmds-send.c b/cmds-send.c
index 9b47e70..c408bc7 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -81,6 +81,14 @@ int find_mount_root(const char *path, char **mount_root)
 		}
 	}
 
+	if (!longest_match) {
+		fprintf(stderr, "ERROR: Failed to find mount root for path %s.\n",
+			    path);
+		fprintf(stderr, "Please make sure that you have a valid \
+			/etc/mtab file.\n");
+		return -ENOENT;
+	}
+
 	*mount_root = realpath(longest_match, NULL);
 	free(longest_match);
 
diff --git a/send-utils.h b/send-utils.h
index da407eb..a3e038b 100644
--- a/send-utils.h
+++ b/send-utils.h
@@ -65,5 +65,6 @@ void subvol_uuid_search_add(struct subvol_uuid_search *s,
 char *path_cat(const char *p1, const char *p2);
 char *path_cat3(const char *p1, const char *p2, const char *p3);
 
+int find_mount_root(const char *path, char **mount_root);
 
 #endif /* SEND_UTILS_H_ */

[-- Attachment #3: uu.c --]
[-- Type: text/x-c, Size: 2697 bytes --]

/*
git clone https://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-progs.git
cd btrfs-progs
make
gcc -O2 -luuid -o uu send-utils.o rbtree.o btrfs-list.o /path/to/this/file/uu.c
*/

#define _GNU_SOURCE

#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <uuid/uuid.h>
#include <sys/ioctl.h>

#include "ctree.h"
#include "ioctl.h"
#include "send-utils.h"

#define CLEAR(var) memset(&var, 0, sizeof(var))


int main(int argc, char **argv) {
	int ret, fd;
	struct subvol_uuid_search sus;
	struct btrfs_ioctl_received_subvol_args rs_args;
	struct subvol_info *si;
	char uuidbuf[37], parent_uuidbuf[37], received_uuidbuf[37];


	if (argc != 3 && argc != 4) {
		printf("usage: uu srcmnt srcsub_reltosrcmnt [dstsub_abs]\n");
		exit(1);
	}

	printf("opening srcmnt %s\n", argv[1]);
	fd = open(argv[1], O_RDONLY | O_NOATIME);
	if (fd < 0) {
		printf("failed to open srcmnt %s! %s\n", argv[1], strerror(errno));
		exit(2);
	}

	puts("initializing sub search");
	CLEAR(sus);
	ret = subvol_uuid_search_init(fd, &sus);
	if (ret < 0) {
		printf("failed to initialize sub search! %s\n", strerror(-ret));
		exit(3);
	}
	
	printf("searching srcsub %s\n", argv[2]);
	si = subvol_uuid_search(&sus, 0, NULL, 0, argv[2], subvol_search_by_path);
	if (!si) {
		puts("srcsub not found!");
		exit(4);
	}

	uuid_unparse(si->uuid,                   uuidbuf);
	uuid_unparse(si->parent_uuid,     parent_uuidbuf);
	uuid_unparse(si->received_uuid, received_uuidbuf);

	printf("\nsrcsub found:\n"
	       "         uuid=%s\n"
	       "  parent_uuid=%s\n"
	       "received_uuid=%s\n"
	       "ctransid=%Lu otransid=%Lu stransid=%Lu rtransid=%Lu\n\n",
	       uuidbuf, parent_uuidbuf, received_uuidbuf,
	       (unsigned long long)(si->ctransid),
	       (unsigned long long)(si->otransid),
	       (unsigned long long)(si->stransid),
	       (unsigned long long)(si->rtransid));

	if (argc == 3)
		goto done;

	printf("opening dst subvol %s\n", argv[3]);
	fd = open(argv[3], O_RDONLY | O_NOATIME);
	if (fd < 0) {
		printf("failed to open dst subvol %s. %s\n", argv[3], strerror(errno));
		exit(5);
	}

	printf("\nhere we go with BTRFS_IOC_SET_RECEIVED_SUBVOL:\n"
	       "dstsub.received_uuid = srcsub.uuid == %s\n"
	       "dstsub.stransid = srcsub.ctransid == %Lu\n\n",
	       uuidbuf, (unsigned long long)(si->ctransid));

	CLEAR(rs_args);
	memcpy(rs_args.uuid, si->uuid, BTRFS_UUID_SIZE);
	rs_args.stransid = si->ctransid;

	ret = ioctl(fd, BTRFS_IOC_SET_RECEIVED_SUBVOL, &rs_args);
	if (ret < 0) {
		printf("BTRFS_IOC_SET_RECEIVED_SUBVOL failed: %s", strerror(-ret));
		exit(6);
	}

done:
	printf("done.\n");
	exit(0);
}

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: Incremental btrfs receive in opposite direction fails
  2012-12-30  6:40   ` junlion
@ 2013-01-02 16:57     ` Jan Schmidt
  2013-01-02 20:56       ` Jun Lion
       [not found]       ` <20130102205351.GA2242@localhost>
  0 siblings, 2 replies; 6+ messages in thread
From: Jan Schmidt @ 2013-01-02 16:57 UTC (permalink / raw)
  To: junlion; +Cc: Alex Lyakas, linux-btrfs

Hi,

I admit I haven't completely understood what you're trying to achieve. You can
only receive an incremental stream if the internal (!) file system state on the
receiver's side is the same as on the sender's. Thus I don't see where setting a
uuid to trick btrfs receive could do you any good.

On Sun, December 30, 2012 at 07:40 (+0100), junlion wrote:
> On 2012-12-29 15:00 +0200, Alex Lyakas wrote:
>> There is no special repo, but you may want, in addition to the patch
>> you mentioned, apply this one as well:
>> https://patchwork.kernel.org/patch/1604391/
> 
> Very useful! Somehow a few lines got wrapped though.
> 
>>> Is there a way for me to directly change the received_uuid of
>>> /mnt/bak/.snap to make it identical to the UUID of /.snap? This looks
>>> like the easiest way if I only need to do it once.
>> There is no implemented way, but since you debugged this far, you can
>> put up some code that sends BTRFS_IOC_SET_RECEIVED_SUBVOL, which is
>> the one setting the received_uuid (and some other small stuff, please
>> check the kernel code for it).
> 
> Ah, BTRFS_IOC_SET_RECEIVED_SUBVOL. Thanks for pointing me in the right
> direction. After some hacking, it worked without eating my data... These
> are the two commands to run before the failing one:
> 
> btrfs subvolume snapshot /mnt/bak/.snap /mnt/bak/.snap-mangled
> uu / .snap /mnt/bak/.snap-mangled
> 
> ... where uu is my crude little received_uuid and stransid display and
> adjustment tool. I've attached its source.

Still in doubt, I'd like to recommend checking with Arne's fssum tool if both
file systems are really the same now. You can fetch and build from his repository at

	git://git.kernel.org/pub/scm/linux/kernel/git/arne/far-progs.git

As a general rule, please send patches inline for people to quote them when
commenting.

> Would be cool if someone modified btrfs send/receive to handle this use
> case automatically, but it's too difficult for me.

We could completely drop the check leading to the "could not find parent
subvolume" error message, if someone explained why removing it was sensible.

Looking forward to your explanation!
-Jan

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Incremental btrfs receive in opposite direction fails
  2013-01-02 16:57     ` Jan Schmidt
@ 2013-01-02 20:56       ` Jun Lion
       [not found]       ` <20130102205351.GA2242@localhost>
  1 sibling, 0 replies; 6+ messages in thread
From: Jun Lion @ 2013-01-02 20:56 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jan Schmidt, Alex Lyakas

Hi,

> I admit I haven't completely understood what you're trying to achieve. You can
> only receive an incremental stream if the internal (!) file system state on the
> receiver's side is the same as on the sender's.

Understood, and the internal states are identical, except for the
snapshot metadata.

This executable example that shows the problem:


    # Create two throwaway filesystems /tmp/img1 and /tmp/img2
    # and mount them at /tmp/mp1 and /tmp/mp2
    for i in 1 2; do
        truncate -s 1G /tmp/img$i
        loop=`losetup -f --show /tmp/img$i`
        mkfs.btrfs "$loop"
        losetup -d "$loop"
        mkdir /tmp/mp$i
        mount /tmp/img$i /tmp/mp$i
    done

    # Create a subvol on the first fs, populate it
    btrfs sub create /tmp/mp1/foo
    echo 1 >/tmp/mp1/foo/1

    # Make an ro snapshot of the subvol, transfer it to the 2nd fs
    btrfs sub snap -r /tmp/mp1/foo /tmp/mp1/foo.1
    btrfs send /tmp/mp1/foo.1 | btrfs receive -v /tmp/mp2

    # Make an rw snapshot of the transferred snapshot on the 2nd fs,
    # add something to this rw snapshot
    btrfs sub snap /tmp/mp2/foo.1 /tmp/mp2/foo
    echo 2 >/tmp/mp2/foo/2

    # Make an ro snapshot of the added-to snapshot
    btrfs sub snap -r /tmp/mp2/foo /tmp/mp2/foo.2

    # Now try to transfer it back in the other direction
    btrfs send -p /tmp/mp2/foo.1 /tmp/mp2/foo.2 |
    btrfs receive -v /tmp/mp1
    # "ERROR: could not find parent subvolume"
    # even though both fs have the same foo.1


As I understand it, if "clone" is a clone/parent source, btrfs send
transmits
    clone.uuid
    clone.ctransid
and btrfs receive searches for a subvol "sub" with
    sub.received_uuid == clone.uuid and sub.stransid == clone.ctransid.

But because the direction of the transfer has changed, it can't find
anything. It would work if btrfs send was modified to *additionally*
transmit
    clone.received_uuid
    clone.stransid
and btrfs receive to do a *fallback* search for a subvol with
    sub.uuid == clone.received_uuid and sub.ctransid == clone.stransid

However, since I'm really not familiar with btrfs code, it was easier
for me to just make a rw snapshot of mp1/foo.1 and modify its metadata:

    # can't modify the info about a ro snapshot
    btrfs sub snap /tmp/mp1/foo.1 /tmp/mp1/foo.1rw

    # mp1/foo.1rw.received_uuid := mp2/foo.1.uuid
    # mp1/foo.1rw.stransid      := mp2/foo.1.ctransid
    uu /tmp/mp2 foo.1 /tmp/mp1/foo.1rw

    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # !! Now we've made it look as if mp1/foo.1 was received from !!
    # !! mp2/foo.1 when actually it was the other way around.     !!
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    # Let's retry the original problematic command:
    btrfs send -p /tmp/mp2/foo.1 /tmp/mp2/foo.2 |
    btrfs receive -v /tmp/mp1

    # It works fine! Hurray!

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Incremental btrfs receive in opposite direction fails
       [not found]       ` <20130102205351.GA2242@localhost>
@ 2013-01-02 22:19         ` junlion
  0 siblings, 0 replies; 6+ messages in thread
From: junlion @ 2013-01-02 22:19 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jan Schmidt, Alex Lyakas

>     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
>     # !! Now we've made it look as if mp1/foo.1 was received from !!
>     # !! mp2/foo.1 when actually it was the other way around.     !!
>     # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Better just ignore what I wrote there, it's not completely correct.
(Because we do not modify mp1/foo.1 but snapshot it to mp1/foo.1rw
and modify that one.)

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2013-01-02 22:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-12-28 22:42 Incremental btrfs receive in opposite direction fails junlion
2012-12-29 13:00 ` Alex Lyakas
2012-12-30  6:40   ` junlion
2013-01-02 16:57     ` Jan Schmidt
2013-01-02 20:56       ` Jun Lion
     [not found]       ` <20130102205351.GA2242@localhost>
2013-01-02 22:19         ` junlion

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).