2 * Copyright (C) 2012 Alexander Block. All rights reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
21 #include "kerncompat.h"
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
36 #include <uuid/uuid.h>
45 #include "send-utils.h"
47 static int g_verbose = 0;
55 u64 clone_sources_count;
58 struct subvol_uuid_search sus;
61 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
63 struct subvol_info *si;
65 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
66 subvol_search_by_path);
69 *root_id = si->root_id;
75 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
77 struct subvol_info *si_tmp;
78 struct subvol_info *si;
80 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
81 subvol_search_by_root_id);
85 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
86 subvol_search_by_uuid);
92 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
95 struct subvol_info *parent = NULL;
96 struct subvol_info *parent2 = NULL;
97 struct subvol_info *best_parent = NULL;
99 u64 best_diff = (u64)-1;
102 parent = get_parent(s, root_id);
108 for (i = 0; i < s->clone_sources_count; i++) {
109 if (s->clone_sources[i] == parent->root_id) {
110 best_parent = parent;
116 for (i = 0; i < s->clone_sources_count; i++) {
117 parent2 = get_parent(s, s->clone_sources[i]);
120 if (parent2->root_id != parent->root_id) {
129 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
130 0, NULL, subvol_search_by_root_id);
136 tmp = parent2->ctransid - parent->ctransid;
139 if (tmp < best_diff) {
141 free(best_parent->path);
144 best_parent = parent2;
160 *found = best_parent->root_id;
169 free(best_parent->path);
175 static int add_clone_source(struct btrfs_send *s, u64 root_id)
177 s->clone_sources = realloc(s->clone_sources,
178 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
180 if (!s->clone_sources)
182 s->clone_sources[s->clone_sources_count++] = root_id;
187 static int write_buf(int fd, const void *buf, int size)
193 ret = write(fd, (char*)buf + pos, size - pos);
196 fprintf(stderr, "ERROR: failed to dump stream. %s",
202 fprintf(stderr, "ERROR: failed to dump stream. %s",
214 static void *dump_thread(void *arg_)
217 struct btrfs_send *s = (struct btrfs_send*)arg_;
222 readed = read(s->send_fd, buf, sizeof(buf));
225 fprintf(stderr, "ERROR: failed to read stream from "
226 "kernel. %s\n", strerror(-ret));
233 ret = write_buf(s->dump_fd, buf, readed);
246 static int do_send(struct btrfs_send *send, u64 parent_root_id,
247 int is_first_subvol, int is_last_subvol, char *subvol)
251 struct btrfs_ioctl_send_args io_send;
254 int pipefd[2] = {-1, -1};
256 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
259 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
267 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
271 memset(&io_send, 0, sizeof(io_send));
272 io_send.send_fd = pipefd[1];
273 send->send_fd = pipefd[0];
276 ret = pthread_create(&t_read, NULL, dump_thread,
280 fprintf(stderr, "ERROR: thread setup failed: %s\n",
285 io_send.clone_sources = (__u64*)send->clone_sources;
286 io_send.clone_sources_count = send->clone_sources_count;
287 io_send.parent_root = parent_root_id;
288 if (!is_first_subvol)
289 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
291 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
292 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
295 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
297 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
299 "Try upgrading your kernel or don't use -e.\n");
303 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
306 fprintf(stderr, "joining genl thread\n");
311 ret = pthread_join(t_read, &t_err);
314 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
319 ret = (long int)t_err;
320 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
321 "(%s)\n", (long int)t_err, strerror(-ret));
337 char *get_subvol_name(char *mnt, char *full_path)
339 int len = strlen(mnt);
342 if (mnt[len - 1] != '/')
345 return full_path + len;
348 static int init_root_path(struct btrfs_send *s, const char *subvol)
355 ret = find_mount_root(subvol, &s->root_path);
358 "ERROR: failed to determine mount point for %s: %s\n",
359 subvol, strerror(-ret));
365 "ERROR: %s doesn't belong to btrfs mount point\n",
371 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
374 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
379 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
381 fprintf(stderr, "ERROR: failed to initialize subvol search. "
382 "%s\n", strerror(-ret));
391 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
397 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
400 fprintf(stderr, "ERROR: failed to open %s. %s\n",
401 subvol, strerror(-ret));
405 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
408 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
409 "%s\n", strerror(-ret));
413 if (flags & BTRFS_SUBVOL_RDONLY)
425 int cmd_send(int argc, char **argv)
430 char *outname = NULL;
431 struct btrfs_send send;
433 char *mount_root = NULL;
434 char *snapshot_parent = NULL;
436 u64 parent_root_id = 0;
438 int new_end_cmd_semantic = 0;
440 memset(&send, 0, sizeof(send));
441 send.dump_fd = fileno(stdout);
443 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
449 new_end_cmd_semantic = 1;
452 subvol = realpath(optarg, NULL);
455 fprintf(stderr, "ERROR: realpath %s failed. "
456 "%s\n", optarg, strerror(-ret));
460 ret = init_root_path(&send, subvol);
464 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
467 fprintf(stderr, "ERROR: could not resolve "
468 "root_id for %s\n", subvol);
472 ret = is_subvol_ro(&send, subvol);
478 "ERROR: cloned subvol %s is not read-only.\n",
483 ret = add_clone_source(&send, root_id);
485 fprintf(stderr, "ERROR: not enough memory\n");
488 subvol_uuid_search_finit(&send.sus);
491 if (send.mnt_fd >= 0) {
495 free(send.root_path);
496 send.root_path = NULL;
503 if (snapshot_parent) {
504 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
508 snapshot_parent = realpath(optarg, NULL);
509 if (!snapshot_parent) {
511 fprintf(stderr, "ERROR: realpath %s failed. "
512 "%s\n", optarg, strerror(-ret));
516 ret = is_subvol_ro(&send, snapshot_parent);
522 "ERROR: parent %s is not read-only.\n",
531 "ERROR: -i was removed, use -c instead\n");
536 fprintf(stderr, "ERROR: send args invalid.\n");
542 if (check_argc_min(argc - optind, 1))
543 usage(cmd_send_usage);
545 if (outname != NULL) {
546 send.dump_fd = creat(outname, 0600);
547 if (send.dump_fd == -1) {
549 fprintf(stderr, "ERROR: can't create '%s': %s\n",
550 outname, strerror(-ret));
555 if (isatty(send.dump_fd)) {
557 "ERROR: not dumping send stream into a terminal, "
558 "redirect it into a file\n");
563 /* use first send subvol to determine mount_root */
564 subvol = argv[optind];
566 subvol = realpath(argv[optind], NULL);
569 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
573 ret = init_root_path(&send, subvol);
577 if (snapshot_parent != NULL) {
578 ret = get_root_id(&send,
579 get_subvol_name(send.root_path, snapshot_parent),
582 fprintf(stderr, "ERROR: could not resolve root_id "
583 "for %s\n", snapshot_parent);
587 ret = add_clone_source(&send, parent_root_id);
589 fprintf(stderr, "ERROR: not enough memory\n");
594 for (i = optind; i < argc; i++) {
596 subvol = realpath(argv[i], NULL);
599 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
603 ret = find_mount_root(subvol, &mount_root);
605 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
612 "ERROR: %s doesn't belong to btrfs mount point\n",
617 if (strcmp(send.root_path, mount_root) != 0) {
619 fprintf(stderr, "ERROR: all subvols must be from the "
625 ret = is_subvol_ro(&send, subvol);
630 fprintf(stderr, "ERROR: %s is not read-only.\n",
636 for (i = optind; i < argc; i++) {
643 fprintf(stderr, "At subvol %s\n", subvol);
645 subvol = realpath(subvol, NULL);
648 fprintf(stderr, "ERROR: realpath %s failed. "
649 "%s\n", argv[i], strerror(-ret));
653 if (!full_send && !parent_root_id) {
654 ret = find_good_parent(&send, root_id, &parent_root_id);
656 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
662 ret = is_subvol_ro(&send, subvol);
667 fprintf(stderr, "ERROR: %s is not read-only.\n",
672 if (new_end_cmd_semantic) {
673 /* require new kernel */
674 is_first_subvol = (i == optind);
675 is_last_subvol = (i == argc - 1);
677 /* be compatible to old and new kernel */
681 ret = do_send(&send, parent_root_id, is_first_subvol,
682 is_last_subvol, subvol);
686 /* done with this subvol, so add it to the clone sources */
687 ret = add_clone_source(&send, root_id);
689 fprintf(stderr, "ERROR: not enough memory\n");
701 free(snapshot_parent);
702 free(send.clone_sources);
703 if (send.mnt_fd >= 0)
705 free(send.root_path);
706 subvol_uuid_search_finit(&send.sus);
710 const char * const cmd_send_usage[] = {
711 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
712 "Send the subvolume(s) to stdout.",
713 "Sends the subvolume(s) specified by <subvol> to stdout.",
714 "By default, this will send the whole subvolume. To do an incremental",
715 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
716 "any additional local snapshots, use '-c <clone-src>' (multiple times",
717 "where applicable). You must not specify clone sources unless you",
718 "guarantee that these snapshots are exactly in the same state on both",
719 "sides, the sender and the receiver. It is allowed to omit the",
720 "'-p <parent>' option when '-c <clone-src>' options are given, in",
721 "which case 'btrfs send' will determine a suitable parent among the",
722 "clone sources itself.",
724 "-v Enable verbose debug output. Each occurrence of",
725 " this option increases the verbose level more.",
726 "-e If sending multiple subvols at once, use the new",
727 " format and omit the end-cmd between the subvols.",
728 "-p <parent> Send an incremental stream from <parent> to",
730 "-c <clone-src> Use this snapshot as a clone source for an ",
731 " incremental send (multiple allowed)",
732 "-f <outfile> Output is normally written to stdout. To write to",
733 " a file, use this option. An alternative would be to",