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)
250 struct btrfs_ioctl_send_args io_send;
253 int pipefd[2] = {-1, -1};
255 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
258 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
266 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
270 memset(&io_send, 0, sizeof(io_send));
271 io_send.send_fd = pipefd[1];
272 send->send_fd = pipefd[0];
275 ret = pthread_create(&t_read, NULL, dump_thread,
279 fprintf(stderr, "ERROR: thread setup failed: %s\n",
284 io_send.clone_sources = (__u64*)send->clone_sources;
285 io_send.clone_sources_count = send->clone_sources_count;
286 io_send.parent_root = parent_root_id;
287 if (!is_first_subvol)
288 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
290 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
291 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
294 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
296 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
298 "Try upgrading your kernel or don't use -e.\n");
302 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
305 fprintf(stderr, "joining genl thread\n");
310 ret = pthread_join(t_read, &t_err);
313 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
318 ret = (long int)t_err;
319 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
320 "(%s)\n", (long int)t_err, strerror(-ret));
336 char *get_subvol_name(char *mnt, char *full_path)
338 int len = strlen(mnt);
341 if (mnt[len - 1] != '/')
344 return full_path + len;
347 static int init_root_path(struct btrfs_send *s, const char *subvol)
354 ret = find_mount_root(subvol, &s->root_path);
357 "ERROR: failed to determine mount point for %s: %s\n",
358 subvol, strerror(-ret));
364 "ERROR: %s doesn't belong to btrfs mount point\n",
370 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
373 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
378 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
380 fprintf(stderr, "ERROR: failed to initialize subvol search. "
381 "%s\n", strerror(-ret));
390 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
396 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
399 fprintf(stderr, "ERROR: failed to open %s. %s\n",
400 subvol, strerror(-ret));
404 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
407 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
408 "%s\n", strerror(-ret));
412 if (flags & BTRFS_SUBVOL_RDONLY)
424 int cmd_send(int argc, char **argv)
429 char *outname = NULL;
430 struct btrfs_send send;
432 char *mount_root = NULL;
433 char *snapshot_parent = NULL;
435 u64 parent_root_id = 0;
437 int new_end_cmd_semantic = 0;
439 memset(&send, 0, sizeof(send));
440 send.dump_fd = fileno(stdout);
442 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
448 new_end_cmd_semantic = 1;
451 subvol = realpath(optarg, NULL);
454 fprintf(stderr, "ERROR: realpath %s failed. "
455 "%s\n", optarg, strerror(-ret));
459 ret = init_root_path(&send, subvol);
463 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
466 fprintf(stderr, "ERROR: could not resolve "
467 "root_id for %s\n", subvol);
471 ret = is_subvol_ro(&send, subvol);
477 "ERROR: cloned subvol %s is not read-only.\n",
482 ret = add_clone_source(&send, root_id);
484 fprintf(stderr, "ERROR: not enough memory\n");
487 subvol_uuid_search_finit(&send.sus);
490 if (send.mnt_fd >= 0) {
494 free(send.root_path);
495 send.root_path = NULL;
502 if (snapshot_parent) {
503 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
507 snapshot_parent = realpath(optarg, NULL);
508 if (!snapshot_parent) {
510 fprintf(stderr, "ERROR: realpath %s failed. "
511 "%s\n", optarg, strerror(-ret));
515 ret = is_subvol_ro(&send, snapshot_parent);
521 "ERROR: parent %s is not read-only.\n",
530 "ERROR: -i was removed, use -c instead\n");
535 fprintf(stderr, "ERROR: send args invalid.\n");
541 if (check_argc_min(argc - optind, 1))
542 usage(cmd_send_usage);
544 if (outname != NULL) {
545 send.dump_fd = creat(outname, 0600);
546 if (send.dump_fd == -1) {
548 fprintf(stderr, "ERROR: can't create '%s': %s\n",
549 outname, strerror(-ret));
554 if (isatty(send.dump_fd)) {
556 "ERROR: not dumping send stream into a terminal, "
557 "redirect it into a file\n");
562 /* use first send subvol to determine mount_root */
563 subvol = argv[optind];
565 subvol = realpath(argv[optind], NULL);
568 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
572 ret = init_root_path(&send, subvol);
576 if (snapshot_parent != NULL) {
577 ret = get_root_id(&send,
578 get_subvol_name(send.root_path, snapshot_parent),
581 fprintf(stderr, "ERROR: could not resolve root_id "
582 "for %s\n", snapshot_parent);
586 ret = add_clone_source(&send, parent_root_id);
588 fprintf(stderr, "ERROR: not enough memory\n");
593 for (i = optind; i < argc; i++) {
595 subvol = realpath(argv[i], NULL);
598 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
602 ret = find_mount_root(subvol, &mount_root);
604 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
611 "ERROR: %s doesn't belong to btrfs mount point\n",
616 if (strcmp(send.root_path, mount_root) != 0) {
618 fprintf(stderr, "ERROR: all subvols must be from the "
624 ret = is_subvol_ro(&send, subvol);
629 fprintf(stderr, "ERROR: %s is not read-only.\n",
635 for (i = optind; i < argc; i++) {
642 fprintf(stderr, "At subvol %s\n", subvol);
644 subvol = realpath(subvol, NULL);
647 fprintf(stderr, "ERROR: realpath %s failed. "
648 "%s\n", argv[i], strerror(-ret));
652 if (!full_send && !parent_root_id) {
653 ret = find_good_parent(&send, root_id, &parent_root_id);
655 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
661 ret = is_subvol_ro(&send, subvol);
666 fprintf(stderr, "ERROR: %s is not read-only.\n",
671 if (new_end_cmd_semantic) {
672 /* require new kernel */
673 is_first_subvol = (i == optind);
674 is_last_subvol = (i == argc - 1);
676 /* be compatible to old and new kernel */
680 ret = do_send(&send, parent_root_id, is_first_subvol,
681 is_last_subvol, subvol);
685 /* done with this subvol, so add it to the clone sources */
686 ret = add_clone_source(&send, root_id);
688 fprintf(stderr, "ERROR: not enough memory\n");
700 free(snapshot_parent);
701 free(send.clone_sources);
702 if (send.mnt_fd >= 0)
704 free(send.root_path);
705 subvol_uuid_search_finit(&send.sus);
709 const char * const cmd_send_usage[] = {
710 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
711 "Send the subvolume(s) to stdout.",
712 "Sends the subvolume(s) specified by <subvol> to stdout.",
713 "By default, this will send the whole subvolume. To do an incremental",
714 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
715 "any additional local snapshots, use '-c <clone-src>' (multiple times",
716 "where applicable). You must not specify clone sources unless you",
717 "guarantee that these snapshots are exactly in the same state on both",
718 "sides, the sender and the receiver. It is allowed to omit the",
719 "'-p <parent>' option when '-c <clone-src>' options are given, in",
720 "which case 'btrfs send' will determine a suitable parent among the",
721 "clone sources itself.",
723 "-v Enable verbose debug output. Each occurrence of",
724 " this option increases the verbose level more.",
725 "-e If sending multiple subvols at once, use the new",
726 " format and omit the end-cmd between the subvols.",
727 "-p <parent> Send an incremental stream from <parent> to",
729 "-c <clone-src> Use this snapshot as a clone source for an ",
730 " incremental send (multiple allowed)",
731 "-f <outfile> Output is normally written to stdout. To write to",
732 " a file, use this option. An alternative would be to",