static int process_subvol(const char *path, const u8 *uuid, u64 ctransid, void *user) { int ret; struct btrfs_receive *r = user; struct btrfs_ioctl_vol_args args_v1; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; ret = finish_subvol(r); if (ret < 0) goto out; BUG_ON(r->cur_subvol.path); BUG_ON(r->cur_subvol_path[0]); if (*r->dest_dir_path == 0) { strncpy_null(r->cur_subvol_path, path); } else { ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path); if (ret < 0) { error("subvol: path invalid: %s\n", path); goto out; } } ret = path_cat3_out(r->full_subvol_path, r->root_path, r->dest_dir_path, path); if (ret < 0) { error("subvol: path invalid: %s", path); goto out; } fprintf(stderr, "At subvol %s\n", path); memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE); r->cur_subvol.stransid = ctransid; if (g_verbose) { uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str); fprintf(stderr, "receiving subvol %s uuid=%s, stransid=%llu\n", path, uuid_str, r->cur_subvol.stransid); } memset(&args_v1, 0, sizeof(args_v1)); strncpy_null(args_v1.name, path); ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1); if (ret < 0) { ret = -errno; error("creating subvolume %s failed: %s", path, strerror(-ret)); goto out; } out: return ret; }
static int process_subvol(const char *path, const u8 *uuid, u64 ctransid, void *user) { int ret; struct btrfs_receive *r = user; struct btrfs_ioctl_vol_args args_v1; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; ret = finish_subvol(r); if (ret < 0) goto out; r->cur_subvol = calloc(1, sizeof(*r->cur_subvol)); 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); free(r->full_subvol_path); r->full_subvol_path = path_cat3(r->root_path, r->dest_dir_path, path); fprintf(stderr, "At subvol %s\n", path); memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE); r->cur_subvol->stransid = ctransid; if (g_verbose) { uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str); fprintf(stderr, "receiving subvol %s uuid=%s, stransid=%llu\n", path, uuid_str, r->cur_subvol->stransid); } memset(&args_v1, 0, sizeof(args_v1)); strncpy_null(args_v1.name, path); ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1); if (ret < 0) { ret = -errno; fprintf(stderr, "ERROR: creating subvolume %s failed. " "%s\n", path, strerror(-ret)); goto out; } out: return ret; }
int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd) { int ret; int end = 0; r->root_path = strdup(tomnt); r->mnt_fd = open(tomnt, O_RDONLY | O_NOATIME); if (r->mnt_fd < 0) { ret = -errno; fprintf(stderr, "ERROR: failed to open %s. %s\n", tomnt, strerror(-ret)); goto out; } ret = subvol_uuid_search_init(r->mnt_fd, &r->sus); if (ret < 0) return ret; r->write_fd = -1; while (!end) { ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r); if (ret < 0) goto out; if (ret) end = 1; ret = close_inode_for_write(r); if (ret < 0) goto out; ret = finish_subvol(r); if (ret < 0) goto out; } ret = 0; out: return ret; }
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, const u8 *parent_uuid, u64 parent_ctransid, void *user) { int ret; struct btrfs_receive *r = user; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; struct btrfs_ioctl_vol_args_v2 args_v2; struct subvol_info *parent_subvol = NULL; ret = finish_subvol(r); if (ret < 0) goto out; BUG_ON(r->cur_subvol.path); BUG_ON(r->cur_subvol_path[0]); if (*r->dest_dir_path == 0) { strncpy_null(r->cur_subvol_path, path); } else { ret = path_cat_out(r->cur_subvol_path, r->dest_dir_path, path); if (ret < 0) { error("snapshot: path invalid: %s", path); goto out; } } ret = path_cat3_out(r->full_subvol_path, r->root_path, r->dest_dir_path, path); if (ret < 0) { error("snapshot: path invalid: %s", path); goto out; } fprintf(stdout, "At snapshot %s\n", path); memcpy(r->cur_subvol.received_uuid, uuid, BTRFS_UUID_SIZE); r->cur_subvol.stransid = ctransid; if (g_verbose) { uuid_unparse((u8*)r->cur_subvol.received_uuid, uuid_str); fprintf(stderr, "receiving snapshot %s uuid=%s, " "ctransid=%llu ", path, uuid_str, r->cur_subvol.stransid); uuid_unparse(parent_uuid, uuid_str); fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n", uuid_str, parent_ctransid); } memset(&args_v2, 0, sizeof(args_v2)); strncpy_null(args_v2.name, path); parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, parent_ctransid, NULL, subvol_search_by_received_uuid); if (!parent_subvol) { parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, parent_ctransid, NULL, subvol_search_by_uuid); } if (!parent_subvol) { ret = -ENOENT; error("cannot find parent subvolume"); 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) { error( "parent subvol is not reachable from inside the root subvol"); 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; fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name); goto out; } else { fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name); } }*/ if (*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) error("cannot open %s: %s", parent_subvol->path, strerror(-ret)); else fprintf(stderr, "It seems that you have changed your default " "subvolume or you specify other subvolume to\n" "mount btrfs, try to remount this btrfs filesystem " "with fs tree, and run btrfs receive again!\n"); goto out; } ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2); close(args_v2.fd); if (ret < 0) { ret = -errno; error("creating snapshot %s -> %s failed: %s", parent_subvol->path, path, strerror(-ret)); goto out; } out: if (parent_subvol) { free(parent_subvol->path); free(parent_subvol); } return ret; }
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[PATH_MAX]; int end = 0; dest_dir_full_path = realpath(tomnt, NULL); if (!dest_dir_full_path) { ret = -errno; error("realpath(%s) failed: %s", 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; error("cannot open destination directory %s: %s", dest_dir_full_path, strerror(-ret)); goto out; } if (realmnt[0]) { r->root_path = realmnt; } else { ret = find_mount_root(dest_dir_full_path, &r->root_path); if (ret < 0) { error("failed to determine mount point for %s: %s", dest_dir_full_path, strerror(-ret)); ret = -EINVAL; goto out; } if (ret > 0) { error("%s doesn't belong to btrfs mount point", dest_dir_full_path); ret = -EINVAL; goto out; } } r->mnt_fd = open(r->root_path, O_RDONLY | O_NOATIME); if (r->mnt_fd < 0) { ret = -errno; error("cannot open %s: %s", r->root_path, strerror(-ret)); 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) { error("cannot resolve our subvolid: %d", ret); goto out; } root_subvol_path[0] = 0; ret = btrfs_subvolid_resolve(r->mnt_fd, root_subvol_path, PATH_MAX, subvol_id); if (ret) { error("cannot resolve our subvol path"); goto out; } /* * Ok we're inside of a subvol off of the root subvol, we need to * actually set full_root_path. */ if (*root_subvol_path) r->full_root_path = root_subvol_path; if (r->dest_dir_chroot) { if (chroot(dest_dir_full_path)) { ret = -errno; error("failed to chroot to %s: %s", dest_dir_full_path, strerror(-ret)); goto out; } if (chdir("/")) { ret = -errno; error("failed to chdir to / after chroot: %s", strerror(-ret)); goto out; } fprintf(stderr, "Chroot to %s\n", dest_dir_full_path); r->root_path = strdup("/"); r->dest_dir_path = r->root_path; } else { /* * 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); while (r->dest_dir_path[0] == '/') r->dest_dir_path++; } ret = subvol_uuid_search_init(r->mnt_fd, &r->sus); if (ret < 0) goto out; while (!end) { if (r->cached_capabilities_len) { if (g_verbose >= 3) fprintf(stderr, "clear cached capabilities\n"); memset(r->cached_capabilities, 0, sizeof(r->cached_capabilities)); r->cached_capabilities_len = 0; } ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r, r->honor_end_cmd, max_errors); if (ret < 0) goto out; if (ret) end = 1; close_inode_for_write(r); ret = finish_subvol(r); if (ret < 0) goto out; } ret = 0; out: if (r->write_fd != -1) { close(r->write_fd); r->write_fd = -1; } if (r->root_path != realmnt) free(r->root_path); r->root_path = NULL; r->dest_dir_path = NULL; free(dest_dir_full_path); subvol_uuid_search_finit(&r->sus); if (r->mnt_fd != -1) { close(r->mnt_fd); r->mnt_fd = -1; } if (r->dest_dir_fd != -1) { close(r->dest_dir_fd); r->dest_dir_fd = -1; } return ret; }
static int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd, u64 max_errors) { int ret; char *dest_dir_full_path; int end = 0; 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) { fprintf(stderr, "ERROR: failed to determine mount point for %s: %s\n", dest_dir_full_path, strerror(-ret)); ret = -EINVAL; goto out; } if (ret > 0) { fprintf(stderr, "ERROR: %s doesn't belong to btrfs mount point\n", dest_dir_full_path); ret = -EINVAL; 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", 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); while (r->dest_dir_path[0] == '/') r->dest_dir_path++; ret = subvol_uuid_search_init(r->mnt_fd, &r->sus); if (ret < 0) goto out; while (!end) { ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r, r->honor_end_cmd, max_errors); if (ret < 0) goto out; if (ret) end = 1; close_inode_for_write(r); ret = finish_subvol(r); if (ret < 0) goto out; } ret = 0; out: if (r->write_fd != -1) { close(r->write_fd); r->write_fd = -1; } free(r->root_path); r->root_path = NULL; free(r->write_path); r->write_path = NULL; free(r->full_subvol_path); r->full_subvol_path = NULL; r->dest_dir_path = NULL; free(dest_dir_full_path); if (r->cur_subvol) { free(r->cur_subvol->path); free(r->cur_subvol); r->cur_subvol = NULL; } subvol_uuid_search_finit(&r->sus); if (r->mnt_fd != -1) { close(r->mnt_fd); r->mnt_fd = -1; } if (r->dest_dir_fd != -1) { close(r->dest_dir_fd); r->dest_dir_fd = -1; } return ret; }
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, const u8 *parent_uuid, u64 parent_ctransid, void *user) { int ret; struct btrfs_receive *r = user; char uuid_str[BTRFS_UUID_UNPARSED_SIZE]; struct btrfs_ioctl_vol_args_v2 args_v2; struct subvol_info *parent_subvol = NULL; ret = finish_subvol(r); if (ret < 0) goto out; r->cur_subvol = calloc(1, sizeof(*r->cur_subvol)); 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); free(r->full_subvol_path); r->full_subvol_path = path_cat3(r->root_path, r->dest_dir_path, path); fprintf(stdout, "At snapshot %s\n", path); memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE); r->cur_subvol->stransid = ctransid; if (g_verbose) { uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str); fprintf(stderr, "receiving snapshot %s uuid=%s, " "ctransid=%llu ", path, uuid_str, r->cur_subvol->stransid); uuid_unparse(parent_uuid, uuid_str); fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n", uuid_str, parent_ctransid); } memset(&args_v2, 0, sizeof(args_v2)); strncpy_null(args_v2.name, path); parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, parent_ctransid, NULL, subvol_search_by_received_uuid); if (!parent_subvol) { parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, parent_ctransid, NULL, subvol_search_by_uuid); } if (!parent_subvol) { ret = -ENOENT; fprintf(stderr, "ERROR: could not find parent subvolume\n"); goto out; } /*if (rs_args.ctransid > rs_args.rtransid) { if (!r->force) { ret = -EINVAL; fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name); goto out; } else { fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name); } }*/ args_v2.fd = openat(r->mnt_fd, parent_subvol->path, O_RDONLY | O_NOATIME); if (args_v2.fd < 0) { ret = -errno; if (errno != ENOENT) fprintf(stderr, "ERROR: open %s failed. %s\n", parent_subvol->path, strerror(-ret)); else fprintf(stderr, "It seems that you have changed your default " "subvolume or you specify other subvolume to\n" "mount btrfs, try to remount this btrfs filesystem " "with fs tree, and run btrfs receive again!\n"); goto out; } ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2); close(args_v2.fd); if (ret < 0) { ret = -errno; fprintf(stderr, "ERROR: creating snapshot %s -> %s " "failed. %s\n", parent_subvol->path, path, strerror(-ret)); goto out; } out: if (parent_subvol) { free(parent_subvol->path); free(parent_subvol); } return ret; }
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid, const u8 *parent_uuid, u64 parent_ctransid, void *user) { int ret; struct btrfs_receive *r = user; char uuid_str[128]; struct btrfs_ioctl_vol_args_v2 args_v2; ret = finish_subvol(r); if (ret < 0) goto out; 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); fprintf(stderr, "At snapshot %s\n", path); memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE); r->cur_subvol->stransid = ctransid; if (g_verbose) { uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str); fprintf(stderr, "receiving snapshot %s uuid=%s, " "ctransid=%llu ", path, uuid_str, r->cur_subvol->stransid); uuid_unparse(parent_uuid, uuid_str); fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n", uuid_str, parent_ctransid); } memset(&args_v2, 0, sizeof(args_v2)); strcpy(args_v2.name, path); r->parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid, parent_ctransid, NULL, subvol_search_by_received_uuid); if (!r->parent_subvol) { ret = -ENOENT; fprintf(stderr, "ERROR: could not find parent subvolume\n"); goto out; } /*if (rs_args.ctransid > rs_args.rtransid) { if (!r->force) { ret = -EINVAL; fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name); goto out; } else { fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name); } }*/ args_v2.fd = openat(r->mnt_fd, r->parent_subvol->path, O_RDONLY | O_NOATIME); if (args_v2.fd < 0) { ret = -errno; fprintf(stderr, "ERROR: open %s failed. %s\n", r->parent_subvol->path, strerror(-ret)); goto out; } ret = ioctl(r->mnt_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2); close(args_v2.fd); if (ret < 0) { ret = -errno; fprintf(stderr, "ERROR: creating snapshot %s -> %s " "failed. %s\n", r->parent_subvol->path, path, strerror(-ret)); goto out; } out: return ret; }