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>
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)
179 tmp = s->clone_sources;
180 s->clone_sources = realloc(s->clone_sources,
181 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
183 if (!s->clone_sources) {
187 s->clone_sources[s->clone_sources_count++] = root_id;
192 static int write_buf(int fd, const void *buf, int size)
198 ret = write(fd, (char*)buf + pos, size - pos);
201 fprintf(stderr, "ERROR: failed to dump stream. %s\n",
207 fprintf(stderr, "ERROR: failed to dump stream. %s\n",
219 static void *dump_thread(void *arg_)
222 struct btrfs_send *s = (struct btrfs_send*)arg_;
227 readed = read(s->send_fd, buf, sizeof(buf));
230 fprintf(stderr, "ERROR: failed to read stream from "
231 "kernel. %s\n", strerror(-ret));
238 ret = write_buf(s->dump_fd, buf, readed);
251 static int do_send(struct btrfs_send *send, u64 parent_root_id,
252 int is_first_subvol, int is_last_subvol, char *subvol,
257 struct btrfs_ioctl_send_args io_send;
260 int pipefd[2] = {-1, -1};
262 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
265 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
273 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
277 memset(&io_send, 0, sizeof(io_send));
278 io_send.send_fd = pipefd[1];
279 send->send_fd = pipefd[0];
282 ret = pthread_create(&t_read, NULL, dump_thread,
286 fprintf(stderr, "ERROR: thread setup failed: %s\n",
291 io_send.flags = flags;
292 io_send.clone_sources = (__u64*)send->clone_sources;
293 io_send.clone_sources_count = send->clone_sources_count;
294 io_send.parent_root = parent_root_id;
295 if (!is_first_subvol)
296 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
298 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
299 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
302 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
304 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
306 "Try upgrading your kernel or don't use -e.\n");
310 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
313 fprintf(stderr, "joining genl thread\n");
318 ret = pthread_join(t_read, &t_err);
321 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
326 ret = (long int)t_err;
327 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
328 "(%s)\n", (long int)t_err, strerror(-ret));
344 char *get_subvol_name(char *mnt, char *full_path)
346 int len = strlen(mnt);
349 if (mnt[len - 1] != '/')
352 return full_path + len;
355 static int init_root_path(struct btrfs_send *s, const char *subvol)
362 ret = find_mount_root(subvol, &s->root_path);
365 "ERROR: failed to determine mount point for %s: %s\n",
366 subvol, strerror(-ret));
372 "ERROR: %s doesn't belong to btrfs mount point\n",
378 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
381 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
386 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
388 fprintf(stderr, "ERROR: failed to initialize subvol search. "
389 "%s\n", strerror(-ret));
398 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
404 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
407 fprintf(stderr, "ERROR: failed to open %s. %s\n",
408 subvol, strerror(-ret));
412 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
415 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
416 "%s\n", strerror(-ret));
420 if (flags & BTRFS_SUBVOL_RDONLY)
432 int cmd_send(int argc, char **argv)
436 char outname[PATH_MAX];
437 struct btrfs_send send;
439 char *mount_root = NULL;
440 char *snapshot_parent = NULL;
442 u64 parent_root_id = 0;
444 int new_end_cmd_semantic = 0;
447 memset(&send, 0, sizeof(send));
448 send.dump_fd = fileno(stdout);
452 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
453 static const struct option long_options[] = {
454 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
456 int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
466 new_end_cmd_semantic = 1;
469 subvol = realpath(optarg, NULL);
472 fprintf(stderr, "ERROR: realpath %s failed. "
473 "%s\n", optarg, strerror(-ret));
477 ret = init_root_path(&send, subvol);
481 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
484 fprintf(stderr, "ERROR: could not resolve "
485 "root_id for %s\n", subvol);
489 ret = is_subvol_ro(&send, subvol);
495 "ERROR: cloned subvol %s is not read-only.\n",
500 ret = add_clone_source(&send, root_id);
502 fprintf(stderr, "ERROR: not enough memory\n");
505 subvol_uuid_search_finit(&send.sus);
508 if (send.mnt_fd >= 0) {
512 free(send.root_path);
513 send.root_path = NULL;
517 if (arg_copy_path(outname, optarg, sizeof(outname))) {
519 "ERROR: output file path too long (%zu)\n",
526 if (snapshot_parent) {
527 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
531 snapshot_parent = realpath(optarg, NULL);
532 if (!snapshot_parent) {
534 fprintf(stderr, "ERROR: realpath %s failed. "
535 "%s\n", optarg, strerror(-ret));
539 ret = is_subvol_ro(&send, snapshot_parent);
545 "ERROR: parent %s is not read-only.\n",
554 "ERROR: -i was removed, use -c instead\n");
557 case GETOPT_VAL_SEND_NO_DATA:
558 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
562 fprintf(stderr, "ERROR: send args invalid.\n");
568 if (check_argc_min(argc - optind, 1))
569 usage(cmd_send_usage);
572 send.dump_fd = creat(outname, 0600);
573 if (send.dump_fd == -1) {
575 fprintf(stderr, "ERROR: can't create '%s': %s\n",
576 outname, strerror(-ret));
581 if (isatty(send.dump_fd)) {
583 "ERROR: not dumping send stream into a terminal, "
584 "redirect it into a file\n");
589 /* use first send subvol to determine mount_root */
590 subvol = argv[optind];
592 subvol = realpath(argv[optind], NULL);
595 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
599 ret = init_root_path(&send, subvol);
603 if (snapshot_parent != NULL) {
604 ret = get_root_id(&send,
605 get_subvol_name(send.root_path, snapshot_parent),
608 fprintf(stderr, "ERROR: could not resolve root_id "
609 "for %s\n", snapshot_parent);
613 ret = add_clone_source(&send, parent_root_id);
615 fprintf(stderr, "ERROR: not enough memory\n");
620 for (i = optind; i < argc; i++) {
622 subvol = realpath(argv[i], NULL);
625 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
629 ret = find_mount_root(subvol, &mount_root);
631 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
638 "ERROR: %s doesn't belong to btrfs mount point\n",
643 if (strcmp(send.root_path, mount_root) != 0) {
645 fprintf(stderr, "ERROR: all subvols must be from the "
651 ret = is_subvol_ro(&send, subvol);
656 fprintf(stderr, "ERROR: %s is not read-only.\n",
662 if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
663 printf("Mode NO_FILE_DATA enabled\n");
665 for (i = optind; i < argc; i++) {
672 fprintf(stderr, "At subvol %s\n", subvol);
674 subvol = realpath(subvol, NULL);
677 fprintf(stderr, "ERROR: realpath %s failed. "
678 "%s\n", argv[i], strerror(-ret));
682 if (!full_send && !parent_root_id) {
683 ret = find_good_parent(&send, root_id, &parent_root_id);
685 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
691 ret = is_subvol_ro(&send, subvol);
696 fprintf(stderr, "ERROR: %s is not read-only.\n",
701 if (new_end_cmd_semantic) {
702 /* require new kernel */
703 is_first_subvol = (i == optind);
704 is_last_subvol = (i == argc - 1);
706 /* be compatible to old and new kernel */
710 ret = do_send(&send, parent_root_id, is_first_subvol,
711 is_last_subvol, subvol, send_flags);
715 /* done with this subvol, so add it to the clone sources */
716 ret = add_clone_source(&send, root_id);
718 fprintf(stderr, "ERROR: not enough memory\n");
730 free(snapshot_parent);
731 free(send.clone_sources);
732 if (send.mnt_fd >= 0)
734 free(send.root_path);
735 subvol_uuid_search_finit(&send.sus);
739 const char * const cmd_send_usage[] = {
740 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
741 "Send the subvolume(s) to stdout.",
742 "Sends the subvolume(s) specified by <subvol> to stdout.",
743 "By default, this will send the whole subvolume. To do an incremental",
744 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
745 "any additional local snapshots, use '-c <clone-src>' (multiple times",
746 "where applicable). You must not specify clone sources unless you",
747 "guarantee that these snapshots are exactly in the same state on both",
748 "sides, the sender and the receiver. It is allowed to omit the",
749 "'-p <parent>' option when '-c <clone-src>' options are given, in",
750 "which case 'btrfs send' will determine a suitable parent among the",
751 "clone sources itself.",
753 "-v Enable verbose debug output. Each occurrence of",
754 " this option increases the verbose level more.",
755 "-e If sending multiple subvols at once, use the new",
756 " format and omit the end-cmd between the subvols.",
757 "-p <parent> Send an incremental stream from <parent> to",
759 "-c <clone-src> Use this snapshot as a clone source for an ",
760 " incremental send (multiple allowed)",
761 "-f <outfile> Output is normally written to stdout. To write to",
762 " a file, use this option. An alternative would be to",
764 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
765 " does not contain any file data and thus cannot be used",
766 " to transfer changes. This mode is faster and useful to",
767 " show the differences in metadata.",