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)
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,
252 struct btrfs_ioctl_send_args io_send;
255 int pipefd[2] = {-1, -1};
257 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
260 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
268 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
272 memset(&io_send, 0, sizeof(io_send));
273 io_send.send_fd = pipefd[1];
274 send->send_fd = pipefd[0];
277 ret = pthread_create(&t_read, NULL, dump_thread,
281 fprintf(stderr, "ERROR: thread setup failed: %s\n",
286 io_send.flags = flags;
287 io_send.clone_sources = (__u64*)send->clone_sources;
288 io_send.clone_sources_count = send->clone_sources_count;
289 io_send.parent_root = parent_root_id;
290 if (!is_first_subvol)
291 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
293 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
294 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
297 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
299 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
301 "Try upgrading your kernel or don't use -e.\n");
305 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
308 fprintf(stderr, "joining genl thread\n");
313 ret = pthread_join(t_read, &t_err);
316 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
321 ret = (long int)t_err;
322 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
323 "(%s)\n", (long int)t_err, strerror(-ret));
339 char *get_subvol_name(char *mnt, char *full_path)
341 int len = strlen(mnt);
344 if (mnt[len - 1] != '/')
347 return full_path + len;
350 static int init_root_path(struct btrfs_send *s, const char *subvol)
357 ret = find_mount_root(subvol, &s->root_path);
360 "ERROR: failed to determine mount point for %s: %s\n",
361 subvol, strerror(-ret));
367 "ERROR: %s doesn't belong to btrfs mount point\n",
373 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
376 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
381 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
383 fprintf(stderr, "ERROR: failed to initialize subvol search. "
384 "%s\n", strerror(-ret));
393 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
399 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
402 fprintf(stderr, "ERROR: failed to open %s. %s\n",
403 subvol, strerror(-ret));
407 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
410 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
411 "%s\n", strerror(-ret));
415 if (flags & BTRFS_SUBVOL_RDONLY)
427 int cmd_send(int argc, char **argv)
431 char outname[PATH_MAX];
432 struct btrfs_send send;
434 char *mount_root = NULL;
435 char *snapshot_parent = NULL;
437 u64 parent_root_id = 0;
439 int new_end_cmd_semantic = 0;
442 memset(&send, 0, sizeof(send));
443 send.dump_fd = fileno(stdout);
447 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
448 static const struct option long_options[] = {
449 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
451 int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
461 new_end_cmd_semantic = 1;
464 subvol = realpath(optarg, NULL);
467 fprintf(stderr, "ERROR: realpath %s failed. "
468 "%s\n", optarg, strerror(-ret));
472 ret = init_root_path(&send, subvol);
476 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
479 fprintf(stderr, "ERROR: could not resolve "
480 "root_id for %s\n", subvol);
484 ret = is_subvol_ro(&send, subvol);
490 "ERROR: cloned subvol %s is not read-only.\n",
495 ret = add_clone_source(&send, root_id);
497 fprintf(stderr, "ERROR: not enough memory\n");
500 subvol_uuid_search_finit(&send.sus);
503 if (send.mnt_fd >= 0) {
507 free(send.root_path);
508 send.root_path = NULL;
512 if (arg_copy_path(outname, optarg, sizeof(outname))) {
514 "ERROR: output file path too long (%zu)\n",
521 if (snapshot_parent) {
522 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
526 snapshot_parent = realpath(optarg, NULL);
527 if (!snapshot_parent) {
529 fprintf(stderr, "ERROR: realpath %s failed. "
530 "%s\n", optarg, strerror(-ret));
534 ret = is_subvol_ro(&send, snapshot_parent);
540 "ERROR: parent %s is not read-only.\n",
549 "ERROR: -i was removed, use -c instead\n");
552 case GETOPT_VAL_SEND_NO_DATA:
553 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
557 fprintf(stderr, "ERROR: send args invalid.\n");
563 if (check_argc_min(argc - optind, 1))
564 usage(cmd_send_usage);
567 send.dump_fd = creat(outname, 0600);
568 if (send.dump_fd == -1) {
570 fprintf(stderr, "ERROR: can't create '%s': %s\n",
571 outname, strerror(-ret));
576 if (isatty(send.dump_fd)) {
578 "ERROR: not dumping send stream into a terminal, "
579 "redirect it into a file\n");
584 /* use first send subvol to determine mount_root */
585 subvol = argv[optind];
587 subvol = realpath(argv[optind], NULL);
590 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
594 ret = init_root_path(&send, subvol);
598 if (snapshot_parent != NULL) {
599 ret = get_root_id(&send,
600 get_subvol_name(send.root_path, snapshot_parent),
603 fprintf(stderr, "ERROR: could not resolve root_id "
604 "for %s\n", snapshot_parent);
608 ret = add_clone_source(&send, parent_root_id);
610 fprintf(stderr, "ERROR: not enough memory\n");
615 for (i = optind; i < argc; i++) {
617 subvol = realpath(argv[i], NULL);
620 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
624 ret = find_mount_root(subvol, &mount_root);
626 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
633 "ERROR: %s doesn't belong to btrfs mount point\n",
638 if (strcmp(send.root_path, mount_root) != 0) {
640 fprintf(stderr, "ERROR: all subvols must be from the "
646 ret = is_subvol_ro(&send, subvol);
651 fprintf(stderr, "ERROR: %s is not read-only.\n",
657 if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
658 printf("Mode NO_FILE_DATA enabled\n");
660 for (i = optind; i < argc; i++) {
667 fprintf(stderr, "At subvol %s\n", subvol);
669 subvol = realpath(subvol, NULL);
672 fprintf(stderr, "ERROR: realpath %s failed. "
673 "%s\n", argv[i], strerror(-ret));
677 if (!full_send && !parent_root_id) {
678 ret = find_good_parent(&send, root_id, &parent_root_id);
680 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
686 ret = is_subvol_ro(&send, subvol);
691 fprintf(stderr, "ERROR: %s is not read-only.\n",
696 if (new_end_cmd_semantic) {
697 /* require new kernel */
698 is_first_subvol = (i == optind);
699 is_last_subvol = (i == argc - 1);
701 /* be compatible to old and new kernel */
705 ret = do_send(&send, parent_root_id, is_first_subvol,
706 is_last_subvol, subvol, send_flags);
710 /* done with this subvol, so add it to the clone sources */
711 ret = add_clone_source(&send, root_id);
713 fprintf(stderr, "ERROR: not enough memory\n");
725 free(snapshot_parent);
726 free(send.clone_sources);
727 if (send.mnt_fd >= 0)
729 free(send.root_path);
730 subvol_uuid_search_finit(&send.sus);
734 const char * const cmd_send_usage[] = {
735 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
736 "Send the subvolume(s) to stdout.",
737 "Sends the subvolume(s) specified by <subvol> to stdout.",
738 "By default, this will send the whole subvolume. To do an incremental",
739 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
740 "any additional local snapshots, use '-c <clone-src>' (multiple times",
741 "where applicable). You must not specify clone sources unless you",
742 "guarantee that these snapshots are exactly in the same state on both",
743 "sides, the sender and the receiver. It is allowed to omit the",
744 "'-p <parent>' option when '-c <clone-src>' options are given, in",
745 "which case 'btrfs send' will determine a suitable parent among the",
746 "clone sources itself.",
748 "-v Enable verbose debug output. Each occurrence of",
749 " this option increases the verbose level more.",
750 "-e If sending multiple subvols at once, use the new",
751 " format and omit the end-cmd between the subvols.",
752 "-p <parent> Send an incremental stream from <parent> to",
754 "-c <clone-src> Use this snapshot as a clone source for an ",
755 " incremental send (multiple allowed)",
756 "-f <outfile> Output is normally written to stdout. To write to",
757 " a file, use this option. An alternative would be to",
759 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
760 " does not contain any file data and thus cannot be used",
761 " to transfer changes. This mode is faster and useful to",
762 " show the differences in metadata.",