static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id) { struct subvol_info *si; si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL, subvol_search_by_root_id); if (!si) return NULL; si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL, subvol_search_by_uuid); if (!si) return NULL; return si; }
static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id) { struct subvol_info *si_tmp; struct subvol_info *si; si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL, subvol_search_by_root_id); if (IS_ERR_OR_NULL(si_tmp)) return si_tmp; si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL, subvol_search_by_uuid); free(si_tmp->path); free(si_tmp); return si; }
static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id) { struct subvol_info *si; si = subvol_uuid_search(&s->sus, 0, NULL, 0, path, subvol_search_by_path); if (!si) return -ENOENT; *root_id = si->root_id; return 0; }
static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found) { int ret; struct subvol_info *parent; struct subvol_info *parent2; struct subvol_info *best_parent = NULL; __s64 tmp; u64 best_diff = (u64)-1; int i; parent = get_parent(s, root_id); if (!parent) { ret = -ENOENT; goto out; } for (i = 0; i < s->clone_sources_count; i++) { if (s->clone_sources[i] == parent->root_id) { best_parent = parent; goto out_found; } } for (i = 0; i < s->clone_sources_count; i++) { parent2 = get_parent(s, s->clone_sources[i]); if (parent2 != parent) continue; parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL, 0, NULL, subvol_search_by_root_id); tmp = parent2->ctransid - parent->ctransid; if (tmp < 0) tmp *= -1; if (tmp < best_diff) { best_parent = parent2; best_diff = tmp; } } if (!best_parent) { ret = -ENOENT; goto out; } out_found: *found = best_parent->root_id; ret = 0; out: return ret; }
static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id) { struct subvol_info *si; si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path, subvol_search_by_path); if (IS_ERR_OR_NULL(si)) { if (!si) return -ENOENT; else return PTR_ERR(si); } *root_id = si->root_id; free(si->path); free(si); return 0; }
static int process_clone(const char *path, u64 offset, u64 len, const u8 *clone_uuid, u64 clone_ctransid, const char *clone_path, u64 clone_offset, void *user) { int ret; struct btrfs_receive *r = user; struct btrfs_ioctl_clone_range_args clone_args; struct subvol_info *si = NULL; char full_path[PATH_MAX]; char *subvol_path = NULL; char full_clone_path[PATH_MAX]; int clone_fd = -1; ret = path_cat_out(full_path, r->full_subvol_path, path); if (ret < 0) { error("clone: source path invalid: %s", path); goto out; } ret = open_inode_for_write(r, full_path); if (ret < 0) goto out; si = subvol_uuid_search(&r->sus, 0, clone_uuid, clone_ctransid, NULL, subvol_search_by_received_uuid); if (!si) { if (memcmp(clone_uuid, r->cur_subvol.received_uuid, BTRFS_UUID_SIZE) == 0) { /* TODO check generation of extent */ subvol_path = strdup(r->cur_subvol_path); } else { ret = -ENOENT; error("clone: did not find source subvol"); goto out; } } else { /*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); } }*/ subvol_path = strdup(si->path); } ret = path_cat_out(full_clone_path, subvol_path, clone_path); if (ret < 0) { error("clone: target path invalid: %s", clone_path); goto out; } clone_fd = openat(r->mnt_fd, full_clone_path, O_RDONLY | O_NOATIME); if (clone_fd < 0) { ret = -errno; error("cannot open %s: %s", full_clone_path, strerror(-ret)); goto out; } clone_args.src_fd = clone_fd; clone_args.src_offset = clone_offset; clone_args.src_length = len; clone_args.dest_offset = offset; ret = ioctl(r->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args); if (ret < 0) { ret = -errno; error("failed to clone extents to %s\n%s\n", path, strerror(-ret)); goto out; } out: if (si) { free(si->path); free(si); } free(subvol_path); if (clone_fd != -1) close(clone_fd); 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 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 find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found) { int ret; struct subvol_info *parent = NULL; struct subvol_info *parent2 = NULL; struct subvol_info *best_parent = NULL; u64 best_diff = (u64)-1; int i; parent = get_parent(sctx, root_id); if (IS_ERR_OR_NULL(parent)) { if (!parent) ret = -ENOENT; else ret = PTR_ERR(parent); goto out; } for (i = 0; i < sctx->clone_sources_count; i++) { if (sctx->clone_sources[i] == parent->root_id) { best_parent = parent; parent = NULL; goto out_found; } } for (i = 0; i < sctx->clone_sources_count; i++) { s64 tmp; parent2 = get_parent(sctx, sctx->clone_sources[i]); if (IS_ERR_OR_NULL(parent2)) continue; if (parent2->root_id != parent->root_id) { free(parent2->path); free(parent2); parent2 = NULL; continue; } free(parent2->path); free(parent2); parent2 = subvol_uuid_search(&sctx->sus, sctx->clone_sources[i], NULL, 0, NULL, subvol_search_by_root_id); if (IS_ERR_OR_NULL(parent2)) { if (!parent2) ret = -ENOENT; else ret = PTR_ERR(parent2); goto out; } tmp = parent2->ctransid - parent->ctransid; if (tmp < 0) tmp = -tmp; if (tmp < best_diff) { if (best_parent) { free(best_parent->path); free(best_parent); } best_parent = parent2; parent2 = NULL; best_diff = tmp; } else { free(parent2->path); free(parent2); parent2 = NULL; } } if (!best_parent) { ret = -ENOENT; goto out; } out_found: *found = best_parent->root_id; ret = 0; out: if (parent) { free(parent->path); free(parent); } if (best_parent) { free(best_parent->path); free(best_parent); } return ret; }
static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id) { int ret; pthread_t t_read; pthread_attr_t t_attr; struct btrfs_ioctl_send_args io_send; struct subvol_info *si; void *t_err = NULL; int subvol_fd = -1; int pipefd[2] = {-1, -1}; si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL, subvol_search_by_root_id); if (!si) { ret = -ENOENT; fprintf(stderr, "ERROR: could not find subvol info for %llu", root_id); goto out; } subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME); if (subvol_fd < 0) { ret = -errno; fprintf(stderr, "ERROR: open %s failed. %s\n", si->path, strerror(-ret)); goto out; } ret = pthread_attr_init(&t_attr); ret = pipe(pipefd); if (ret < 0) { ret = -errno; fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret)); goto out; } memset(&io_send, 0, sizeof(io_send)); io_send.send_fd = pipefd[1]; send->send_fd = pipefd[0]; if (!ret) ret = pthread_create(&t_read, &t_attr, dump_thread, send); if (ret) { ret = -ret; fprintf(stderr, "ERROR: thread setup failed: %s\n", strerror(-ret)); goto out; } io_send.clone_sources = (__u64*)send->clone_sources; io_send.clone_sources_count = send->clone_sources_count; io_send.parent_root = parent_root_id; ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send); if (ret) { ret = -errno; fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret, strerror(-ret)); goto out; } if (g_verbose > 0) fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret); if (g_verbose > 0) fprintf(stderr, "joining genl thread\n"); close(pipefd[1]); pipefd[1] = 0; ret = pthread_join(t_read, &t_err); if (ret) { ret = -ret; fprintf(stderr, "ERROR: pthread_join failed: %s\n", strerror(-ret)); goto out; } if (t_err) { ret = (long int)t_err; fprintf(stderr, "ERROR: failed to process send stream, ret=%ld " "(%s)\n", (long int)t_err, strerror(-ret)); goto out; } pthread_attr_destroy(&t_attr); ret = 0; out: if (subvol_fd != -1) close(subvol_fd); if (pipefd[0] != -1) close(pipefd[0]); if (pipefd[1] != -1) close(pipefd[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[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; }