btrfs-progs: make receive work inside of subvolumes

Kind of a big feature of btrfs is being able to have a default subvol.  However
the receive code generates the paths to the subvols from the root of the fs,
even in the case of a default subvol.  So instead figure out if we're inside of
a subvol, either because we have a different default or we've chroot'ed and are
using -m.  Then strip this extra path off of the subvol we find so we can look
up our parent properly.  Thanks

Reported-by: Neil Horman <nhorman@redhat.com>
Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
master
Josef Bacik 2015-06-10 15:05:51 -04:00 committed by David Sterba
parent 6d7999d5b7
commit b47fdc73c3
1 changed files with 88 additions and 2 deletions

View File

@ -61,6 +61,7 @@ struct btrfs_receive
char *root_path;
char *dest_dir_path; /* relative to root_path */
char *full_subvol_path;
char *full_root_path;
int dest_dir_chroot;
struct subvol_info *cur_subvol;
@ -248,6 +249,46 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
goto out;
}
/*
* The path is resolved from the root subvol, but we could be in some
* subvolume under the root subvolume, so try and adjust the path to be
* relative to our root path.
*/
if (r->full_root_path) {
size_t root_len;
size_t sub_len;
root_len = strlen(r->full_root_path);
sub_len = strlen(parent_subvol->path);
/* First make sure the parent subvol is actually in our path */
if (sub_len < root_len ||
strstr(parent_subvol->path, r->full_root_path) == NULL) {
fprintf(stderr, "ERROR: parent subvol is not reachable"
" from inside the root subvol.\n");
ret = -ENOENT;
goto out;
}
if (sub_len == root_len) {
parent_subvol->path[0] = '/';
parent_subvol->path[1] = '\0';
} else {
/*
* root path is foo/bar
* subvol path is foo/bar/baz
*
* we need to have baz be the path, so we need to move
* the bit after foo/bar/, so path + root_len + 1, and
* move the part we care about, so sub_len - root_len -
* 1.
*/
memmove(parent_subvol->path,
parent_subvol->path + root_len + 1,
sub_len - root_len - 1);
parent_subvol->path[sub_len - root_len - 1] = '\0';
}
}
/*if (rs_args.ctransid > rs_args.rtransid) {
if (!r->force) {
ret = -EINVAL;
@ -258,8 +299,11 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
}
}*/
args_v2.fd = openat(r->mnt_fd, parent_subvol->path,
O_RDONLY | O_NOATIME);
if (strlen(parent_subvol->path) == 0)
args_v2.fd = dup(r->mnt_fd);
else
args_v2.fd = openat(r->mnt_fd, parent_subvol->path,
O_RDONLY | O_NOATIME);
if (args_v2.fd < 0) {
ret = -errno;
if (errno != ENOENT)
@ -859,8 +903,10 @@ static struct btrfs_send_ops send_ops = {
static int do_receive(struct btrfs_receive *r, const char *tomnt,
char *realmnt, int r_fd, u64 max_errors)
{
u64 subvol_id;
int ret;
char *dest_dir_full_path;
char *root_subvol_path;
int end = 0;
dest_dir_full_path = realpath(tomnt, NULL);
@ -906,6 +952,42 @@ static int do_receive(struct btrfs_receive *r, const char *tomnt,
goto out;
}
/*
* If we use -m or a default subvol we want to resolve the path to the
* subvolume we're sitting in so that we can adjust the paths of any
* subvols we want to receive in.
*/
ret = btrfs_list_get_path_rootid(r->mnt_fd, &subvol_id);
if (ret) {
fprintf(stderr, "ERROR: couldn't resolve our subvolid %d\n",
ret);
goto out;
}
root_subvol_path = malloc(BTRFS_PATH_NAME_MAX);
if (!root_subvol_path) {
ret = -ENOMEM;
fprintf(stderr, "ERROR: couldn't allocate buffer for the root "
"subvol path\n");
goto out;
}
ret = btrfs_subvolid_resolve(r->mnt_fd, root_subvol_path,
BTRFS_PATH_NAME_MAX, subvol_id);
if (ret) {
fprintf(stderr, "ERROR: couldn't resolve our subvol path\n");
goto out;
}
/*
* Ok we're inside of a subvol off of the root subvol, we need to
* actually set full_root_path.
*/
if (strlen(root_subvol_path))
r->full_root_path = root_subvol_path;
else
free(root_subvol_path);
if (r->dest_dir_chroot) {
if (chroot(dest_dir_full_path)) {
ret = -errno;
@ -991,6 +1073,10 @@ out:
close(r->dest_dir_fd);
r->dest_dir_fd = -1;
}
if (r->full_root_path) {
free(r->full_root_path);
r->full_root_path = NULL;
}
return ret;
}