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);
133 tmp = parent2->ctransid - parent->ctransid;
136 if (tmp < best_diff) {
138 free(best_parent->path);
141 best_parent = parent2;
157 *found = best_parent->root_id;
166 free(best_parent->path);
172 static void add_clone_source(struct btrfs_send *s, u64 root_id)
174 s->clone_sources = realloc(s->clone_sources,
175 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
176 s->clone_sources[s->clone_sources_count++] = root_id;
179 static int write_buf(int fd, const void *buf, int size)
185 ret = write(fd, (char*)buf + pos, size - pos);
188 fprintf(stderr, "ERROR: failed to dump stream. %s",
194 fprintf(stderr, "ERROR: failed to dump stream. %s",
206 static void *dump_thread(void *arg_)
209 struct btrfs_send *s = (struct btrfs_send*)arg_;
214 readed = read(s->send_fd, buf, sizeof(buf));
217 fprintf(stderr, "ERROR: failed to read stream from "
218 "kernel. %s\n", strerror(-ret));
225 ret = write_buf(s->dump_fd, buf, readed);
238 static int do_send(struct btrfs_send *send, u64 parent_root_id,
239 int is_first_subvol, int is_last_subvol, char *subvol)
243 struct btrfs_ioctl_send_args io_send;
246 int pipefd[2] = {-1, -1};
248 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
251 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
259 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
263 memset(&io_send, 0, sizeof(io_send));
264 io_send.send_fd = pipefd[1];
265 send->send_fd = pipefd[0];
268 ret = pthread_create(&t_read, NULL, dump_thread,
272 fprintf(stderr, "ERROR: thread setup failed: %s\n",
277 io_send.clone_sources = (__u64*)send->clone_sources;
278 io_send.clone_sources_count = send->clone_sources_count;
279 io_send.parent_root = parent_root_id;
280 if (!is_first_subvol)
281 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
283 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
284 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
287 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
289 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
291 "Try upgrading your kernel or don't use -e.\n");
295 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
298 fprintf(stderr, "joining genl thread\n");
303 ret = pthread_join(t_read, &t_err);
306 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
311 ret = (long int)t_err;
312 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
313 "(%s)\n", (long int)t_err, strerror(-ret));
329 char *get_subvol_name(char *mnt, char *full_path)
331 int len = strlen(mnt);
334 if (mnt[len - 1] != '/')
337 return full_path + len;
340 static int init_root_path(struct btrfs_send *s, const char *subvol)
347 ret = find_mount_root(subvol, &s->root_path);
350 fprintf(stderr, "ERROR: failed to determine mount point "
355 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
358 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
363 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
365 fprintf(stderr, "ERROR: failed to initialize subvol search. "
366 "%s\n", strerror(-ret));
375 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
381 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
384 fprintf(stderr, "ERROR: failed to open %s. %s\n",
385 subvol, strerror(-ret));
389 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
392 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
393 "%s\n", strerror(-ret));
397 if (flags & BTRFS_SUBVOL_RDONLY)
409 int cmd_send(int argc, char **argv)
414 char *outname = NULL;
415 struct btrfs_send send;
417 char *mount_root = NULL;
418 char *snapshot_parent = NULL;
420 u64 parent_root_id = 0;
422 int new_end_cmd_semantic = 0;
424 memset(&send, 0, sizeof(send));
425 send.dump_fd = fileno(stdout);
427 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
433 new_end_cmd_semantic = 1;
436 subvol = realpath(optarg, NULL);
439 fprintf(stderr, "ERROR: realpath %s failed. "
440 "%s\n", optarg, strerror(-ret));
444 ret = init_root_path(&send, subvol);
448 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
451 fprintf(stderr, "ERROR: could not resolve "
452 "root_id for %s\n", subvol);
456 ret = is_subvol_ro(&send, subvol);
462 "ERROR: cloned subvol %s is not read-only.\n",
467 add_clone_source(&send, root_id);
468 subvol_uuid_search_finit(&send.sus);
471 if (send.mnt_fd >= 0) {
475 free(send.root_path);
476 send.root_path = NULL;
483 if (snapshot_parent) {
484 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
488 snapshot_parent = realpath(optarg, NULL);
489 if (!snapshot_parent) {
491 fprintf(stderr, "ERROR: realpath %s failed. "
492 "%s\n", optarg, strerror(-ret));
496 ret = is_subvol_ro(&send, snapshot_parent);
502 "ERROR: parent %s is not read-only.\n",
511 "ERROR: -i was removed, use -c instead\n");
516 fprintf(stderr, "ERROR: send args invalid.\n");
523 usage(cmd_send_usage);
525 if (outname != NULL) {
526 send.dump_fd = creat(outname, 0600);
527 if (send.dump_fd == -1) {
529 fprintf(stderr, "ERROR: can't create '%s': %s\n",
530 outname, strerror(-ret));
535 if (isatty(send.dump_fd)) {
537 "ERROR: not dumping send stream into a terminal, "
538 "redirect it into a file\n");
543 /* use first send subvol to determine mount_root */
544 subvol = argv[optind];
546 subvol = realpath(argv[optind], NULL);
549 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
553 ret = init_root_path(&send, subvol);
557 if (snapshot_parent != NULL) {
558 ret = get_root_id(&send,
559 get_subvol_name(send.root_path, snapshot_parent),
562 fprintf(stderr, "ERROR: could not resolve root_id "
563 "for %s\n", snapshot_parent);
567 add_clone_source(&send, parent_root_id);
570 for (i = optind; i < argc; i++) {
572 subvol = realpath(argv[i], NULL);
575 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
579 ret = find_mount_root(subvol, &mount_root);
581 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
586 if (strcmp(send.root_path, mount_root) != 0) {
588 fprintf(stderr, "ERROR: all subvols must be from the "
594 ret = is_subvol_ro(&send, subvol);
599 fprintf(stderr, "ERROR: %s is not read-only.\n",
605 for (i = optind; i < argc; i++) {
612 fprintf(stderr, "At subvol %s\n", subvol);
614 subvol = realpath(subvol, NULL);
617 fprintf(stderr, "ERROR: realpath %s failed. "
618 "%s\n", argv[i], strerror(-ret));
622 if (!full_send && !parent_root_id) {
623 ret = find_good_parent(&send, root_id, &parent_root_id);
625 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
631 ret = is_subvol_ro(&send, subvol);
636 fprintf(stderr, "ERROR: %s is not read-only.\n",
641 if (new_end_cmd_semantic) {
642 /* require new kernel */
643 is_first_subvol = (i == optind);
644 is_last_subvol = (i == argc - 1);
646 /* be compatible to old and new kernel */
650 ret = do_send(&send, parent_root_id, is_first_subvol,
651 is_last_subvol, subvol);
655 /* done with this subvol, so add it to the clone sources */
656 add_clone_source(&send, root_id);
666 free(snapshot_parent);
667 free(send.clone_sources);
668 if (send.mnt_fd >= 0)
670 free(send.root_path);
671 subvol_uuid_search_finit(&send.sus);
675 const char * const cmd_send_usage[] = {
676 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
677 "Send the subvolume(s) to stdout.",
678 "Sends the subvolume(s) specified by <subvol> to stdout.",
679 "By default, this will send the whole subvolume. To do an incremental",
680 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
681 "any additional local snapshots, use '-c <clone-src>' (multiple times",
682 "where applicable). You must not specify clone sources unless you",
683 "guarantee that these snapshots are exactly in the same state on both",
684 "sides, the sender and the receiver. It is allowed to omit the",
685 "'-p <parent>' option when '-c <clone-src>' options are given, in",
686 "which case 'btrfs send' will determine a suitable parent among the",
687 "clone sources itself.",
689 "-v Enable verbose debug output. Each occurrence of",
690 " this option increases the verbose level more.",
691 "-e If sending multiple subvols at once, use the new",
692 " format and omit the end-cmd between the subvols.",
693 "-p <parent> Send an incremental stream from <parent> to",
695 "-c <clone-src> Use this snapshot as a clone source for an ",
696 " incremental send (multiple allowed)",
697 "-f <outfile> Output is normally written to stdout. To write to",
698 " a file, use this option. An alternative would be to",