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.
20 #include "kerncompat.h"
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
35 #include <uuid/uuid.h>
44 #include "send-utils.h"
46 static int g_verbose = 0;
54 u64 clone_sources_count;
57 struct subvol_uuid_search sus;
60 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
62 struct subvol_info *si;
64 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
65 subvol_search_by_path);
68 *root_id = si->root_id;
74 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
76 struct subvol_info *si_tmp;
77 struct subvol_info *si;
79 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
80 subvol_search_by_root_id);
84 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
85 subvol_search_by_uuid);
91 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
94 struct subvol_info *parent = NULL;
95 struct subvol_info *parent2 = NULL;
96 struct subvol_info *best_parent = NULL;
98 u64 best_diff = (u64)-1;
101 parent = get_parent(s, root_id);
107 for (i = 0; i < s->clone_sources_count; i++) {
108 if (s->clone_sources[i] == parent->root_id) {
109 best_parent = parent;
115 for (i = 0; i < s->clone_sources_count; i++) {
116 parent2 = get_parent(s, s->clone_sources[i]);
119 if (parent2->root_id != parent->root_id) {
128 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
129 0, NULL, subvol_search_by_root_id);
135 tmp = parent2->ctransid - parent->ctransid;
138 if (tmp < best_diff) {
140 free(best_parent->path);
143 best_parent = parent2;
159 *found = best_parent->root_id;
168 free(best_parent->path);
174 static int add_clone_source(struct btrfs_send *s, u64 root_id)
176 s->clone_sources = realloc(s->clone_sources,
177 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
179 if (!s->clone_sources)
181 s->clone_sources[s->clone_sources_count++] = root_id;
186 static int write_buf(int fd, const void *buf, int size)
192 ret = write(fd, (char*)buf + pos, size - pos);
195 fprintf(stderr, "ERROR: failed to dump stream. %s",
201 fprintf(stderr, "ERROR: failed to dump stream. %s",
213 static void *dump_thread(void *arg_)
216 struct btrfs_send *s = (struct btrfs_send*)arg_;
221 readed = read(s->send_fd, buf, sizeof(buf));
224 fprintf(stderr, "ERROR: failed to read stream from "
225 "kernel. %s\n", strerror(-ret));
232 ret = write_buf(s->dump_fd, buf, readed);
245 static int do_send(struct btrfs_send *send, u64 parent_root_id,
246 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.flags = flags;
286 io_send.clone_sources = (__u64*)send->clone_sources;
287 io_send.clone_sources_count = send->clone_sources_count;
288 io_send.parent_root = parent_root_id;
289 if (!is_first_subvol)
290 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
292 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
293 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
296 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
298 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
300 "Try upgrading your kernel or don't use -e.\n");
304 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
307 fprintf(stderr, "joining genl thread\n");
312 ret = pthread_join(t_read, &t_err);
315 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
320 ret = (long int)t_err;
321 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
322 "(%s)\n", (long int)t_err, strerror(-ret));
338 char *get_subvol_name(char *mnt, char *full_path)
340 int len = strlen(mnt);
343 if (mnt[len - 1] != '/')
346 return full_path + len;
349 static int init_root_path(struct btrfs_send *s, const char *subvol)
356 ret = find_mount_root(subvol, &s->root_path);
359 "ERROR: failed to determine mount point for %s: %s\n",
360 subvol, strerror(-ret));
366 "ERROR: %s doesn't belong to btrfs mount point\n",
372 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
375 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
380 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
382 fprintf(stderr, "ERROR: failed to initialize subvol search. "
383 "%s\n", strerror(-ret));
392 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
398 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
401 fprintf(stderr, "ERROR: failed to open %s. %s\n",
402 subvol, strerror(-ret));
406 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
409 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
410 "%s\n", strerror(-ret));
414 if (flags & BTRFS_SUBVOL_RDONLY)
426 int cmd_send(int argc, char **argv)
430 char outname[PATH_MAX];
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;
441 memset(&send, 0, sizeof(send));
442 send.dump_fd = fileno(stdout);
446 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
447 static const struct option long_options[] = {
448 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
450 int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
460 new_end_cmd_semantic = 1;
463 subvol = realpath(optarg, NULL);
466 fprintf(stderr, "ERROR: realpath %s failed. "
467 "%s\n", optarg, strerror(-ret));
471 ret = init_root_path(&send, subvol);
475 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
478 fprintf(stderr, "ERROR: could not resolve "
479 "root_id for %s\n", subvol);
483 ret = is_subvol_ro(&send, subvol);
489 "ERROR: cloned subvol %s is not read-only.\n",
494 ret = add_clone_source(&send, root_id);
496 fprintf(stderr, "ERROR: not enough memory\n");
499 subvol_uuid_search_finit(&send.sus);
502 if (send.mnt_fd >= 0) {
506 free(send.root_path);
507 send.root_path = NULL;
511 if (arg_copy_path(outname, optarg, sizeof(outname))) {
513 "ERROR: output file path too long (%zu)\n",
520 if (snapshot_parent) {
521 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
525 snapshot_parent = realpath(optarg, NULL);
526 if (!snapshot_parent) {
528 fprintf(stderr, "ERROR: realpath %s failed. "
529 "%s\n", optarg, strerror(-ret));
533 ret = is_subvol_ro(&send, snapshot_parent);
539 "ERROR: parent %s is not read-only.\n",
548 "ERROR: -i was removed, use -c instead\n");
551 case GETOPT_VAL_SEND_NO_DATA:
552 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
556 fprintf(stderr, "ERROR: send args invalid.\n");
562 if (check_argc_min(argc - optind, 1))
563 usage(cmd_send_usage);
566 send.dump_fd = creat(outname, 0600);
567 if (send.dump_fd == -1) {
569 fprintf(stderr, "ERROR: can't create '%s': %s\n",
570 outname, strerror(-ret));
575 if (isatty(send.dump_fd)) {
577 "ERROR: not dumping send stream into a terminal, "
578 "redirect it into a file\n");
583 /* use first send subvol to determine mount_root */
584 subvol = argv[optind];
586 subvol = realpath(argv[optind], NULL);
589 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
593 ret = init_root_path(&send, subvol);
597 if (snapshot_parent != NULL) {
598 ret = get_root_id(&send,
599 get_subvol_name(send.root_path, snapshot_parent),
602 fprintf(stderr, "ERROR: could not resolve root_id "
603 "for %s\n", snapshot_parent);
607 ret = add_clone_source(&send, parent_root_id);
609 fprintf(stderr, "ERROR: not enough memory\n");
614 for (i = optind; i < argc; i++) {
616 subvol = realpath(argv[i], NULL);
619 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
623 ret = find_mount_root(subvol, &mount_root);
625 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
632 "ERROR: %s doesn't belong to btrfs mount point\n",
637 if (strcmp(send.root_path, mount_root) != 0) {
639 fprintf(stderr, "ERROR: all subvols must be from the "
645 ret = is_subvol_ro(&send, subvol);
650 fprintf(stderr, "ERROR: %s is not read-only.\n",
656 if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
657 printf("Mode NO_FILE_DATA enabled\n");
659 for (i = optind; i < argc; i++) {
666 fprintf(stderr, "At subvol %s\n", subvol);
668 subvol = realpath(subvol, NULL);
671 fprintf(stderr, "ERROR: realpath %s failed. "
672 "%s\n", argv[i], strerror(-ret));
676 if (!full_send && !parent_root_id) {
677 ret = find_good_parent(&send, root_id, &parent_root_id);
679 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
685 ret = is_subvol_ro(&send, subvol);
690 fprintf(stderr, "ERROR: %s is not read-only.\n",
695 if (new_end_cmd_semantic) {
696 /* require new kernel */
697 is_first_subvol = (i == optind);
698 is_last_subvol = (i == argc - 1);
700 /* be compatible to old and new kernel */
704 ret = do_send(&send, parent_root_id, is_first_subvol,
705 is_last_subvol, subvol, send_flags);
709 /* done with this subvol, so add it to the clone sources */
710 ret = add_clone_source(&send, root_id);
712 fprintf(stderr, "ERROR: not enough memory\n");
724 free(snapshot_parent);
725 free(send.clone_sources);
726 if (send.mnt_fd >= 0)
728 free(send.root_path);
729 subvol_uuid_search_finit(&send.sus);
733 const char * const cmd_send_usage[] = {
734 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
735 "Send the subvolume(s) to stdout.",
736 "Sends the subvolume(s) specified by <subvol> to stdout.",
737 "By default, this will send the whole subvolume. To do an incremental",
738 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
739 "any additional local snapshots, use '-c <clone-src>' (multiple times",
740 "where applicable). You must not specify clone sources unless you",
741 "guarantee that these snapshots are exactly in the same state on both",
742 "sides, the sender and the receiver. It is allowed to omit the",
743 "'-p <parent>' option when '-c <clone-src>' options are given, in",
744 "which case 'btrfs send' will determine a suitable parent among the",
745 "clone sources itself.",
747 "-v Enable verbose debug output. Each occurrence of",
748 " this option increases the verbose level more.",
749 "-e If sending multiple subvols at once, use the new",
750 " format and omit the end-cmd between the subvols.",
751 "-p <parent> Send an incremental stream from <parent> to",
753 "-c <clone-src> Use this snapshot as a clone source for an ",
754 " incremental send (multiple allowed)",
755 "-f <outfile> Output is normally written to stdout. To write to",
756 " a file, use this option. An alternative would be to",
758 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
759 " does not contain any file data and thus cannot be used",
760 " to transfer changes. This mode is faster and useful to",
761 " show the differences in metadata.",