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 pthread_attr_t t_attr;
244 struct btrfs_ioctl_send_args io_send;
247 int pipefd[2] = {-1, -1};
249 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
252 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
257 ret = pthread_attr_init(&t_attr);
262 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
266 memset(&io_send, 0, sizeof(io_send));
267 io_send.send_fd = pipefd[1];
268 send->send_fd = pipefd[0];
271 ret = pthread_create(&t_read, &t_attr, dump_thread,
275 fprintf(stderr, "ERROR: thread setup failed: %s\n",
280 io_send.clone_sources = (__u64*)send->clone_sources;
281 io_send.clone_sources_count = send->clone_sources_count;
282 io_send.parent_root = parent_root_id;
283 if (!is_first_subvol)
284 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
286 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
287 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
290 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
292 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
294 "Try upgrading your kernel or don't use -e.\n");
298 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
301 fprintf(stderr, "joining genl thread\n");
306 ret = pthread_join(t_read, &t_err);
309 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
314 ret = (long int)t_err;
315 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
316 "(%s)\n", (long int)t_err, strerror(-ret));
320 pthread_attr_destroy(&t_attr);
334 char *get_subvol_name(char *mnt, char *full_path)
336 int len = strlen(mnt);
339 if (mnt[len - 1] != '/')
342 return full_path + len;
345 static int init_root_path(struct btrfs_send *s, const char *subvol)
352 ret = find_mount_root(subvol, &s->root_path);
355 fprintf(stderr, "ERROR: failed to determine mount point "
360 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
363 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
368 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
370 fprintf(stderr, "ERROR: failed to initialize subvol search. "
371 "%s\n", strerror(-ret));
380 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
386 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
389 fprintf(stderr, "ERROR: failed to open %s. %s\n",
390 subvol, strerror(-ret));
394 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
397 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
398 "%s\n", strerror(-ret));
402 if (flags & BTRFS_SUBVOL_RDONLY)
414 int cmd_send(int argc, char **argv)
419 char *outname = NULL;
420 struct btrfs_send send;
422 char *mount_root = NULL;
423 char *snapshot_parent = NULL;
425 u64 parent_root_id = 0;
427 int new_end_cmd_semantic = 0;
429 memset(&send, 0, sizeof(send));
430 send.dump_fd = fileno(stdout);
432 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
438 new_end_cmd_semantic = 1;
441 subvol = realpath(optarg, NULL);
444 fprintf(stderr, "ERROR: realpath %s failed. "
445 "%s\n", optarg, strerror(-ret));
449 ret = init_root_path(&send, subvol);
453 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
456 fprintf(stderr, "ERROR: could not resolve "
457 "root_id for %s\n", subvol);
461 ret = is_subvol_ro(&send, subvol);
467 "ERROR: cloned subvol %s is not read-only.\n",
472 add_clone_source(&send, root_id);
473 subvol_uuid_search_finit(&send.sus);
476 if (send.mnt_fd >= 0) {
480 free(send.root_path);
481 send.root_path = NULL;
488 if (snapshot_parent) {
489 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
493 snapshot_parent = realpath(optarg, NULL);
494 if (!snapshot_parent) {
496 fprintf(stderr, "ERROR: realpath %s failed. "
497 "%s\n", optarg, strerror(-ret));
501 ret = is_subvol_ro(&send, snapshot_parent);
507 "ERROR: parent %s is not read-only.\n",
516 "ERROR: -i was removed, use -c instead\n");
521 fprintf(stderr, "ERROR: send args invalid.\n");
527 if (optind == argc) {
528 fprintf(stderr, "ERROR: send needs path to snapshot\n");
533 if (outname != NULL) {
534 send.dump_fd = creat(outname, 0600);
535 if (send.dump_fd == -1) {
537 fprintf(stderr, "ERROR: can't create '%s': %s\n",
538 outname, strerror(-ret));
543 if (isatty(send.dump_fd)) {
545 "ERROR: not dumping send stream into a terminal, "
546 "redirect it into a file\n");
551 /* use first send subvol to determine mount_root */
552 subvol = argv[optind];
554 subvol = realpath(argv[optind], NULL);
557 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
561 ret = init_root_path(&send, subvol);
565 if (snapshot_parent != NULL) {
566 ret = get_root_id(&send,
567 get_subvol_name(send.root_path, snapshot_parent),
570 fprintf(stderr, "ERROR: could not resolve root_id "
571 "for %s\n", snapshot_parent);
575 add_clone_source(&send, parent_root_id);
578 for (i = optind; i < argc; i++) {
580 subvol = realpath(argv[i], NULL);
583 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
587 ret = find_mount_root(subvol, &mount_root);
589 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
594 if (strcmp(send.root_path, mount_root) != 0) {
596 fprintf(stderr, "ERROR: all subvols must be from the "
602 ret = is_subvol_ro(&send, subvol);
607 fprintf(stderr, "ERROR: %s is not read-only.\n",
613 for (i = optind; i < argc; i++) {
620 fprintf(stderr, "At subvol %s\n", subvol);
622 subvol = realpath(subvol, NULL);
625 fprintf(stderr, "ERROR: realpath %s failed. "
626 "%s\n", argv[i], strerror(-ret));
630 if (!full_send && !parent_root_id) {
631 ret = find_good_parent(&send, root_id, &parent_root_id);
633 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
639 ret = is_subvol_ro(&send, subvol);
644 fprintf(stderr, "ERROR: %s is not read-only.\n",
649 if (new_end_cmd_semantic) {
650 /* require new kernel */
651 is_first_subvol = (i == optind);
652 is_last_subvol = (i == argc - 1);
654 /* be compatible to old and new kernel */
658 ret = do_send(&send, parent_root_id, is_first_subvol,
659 is_last_subvol, subvol);
663 /* done with this subvol, so add it to the clone sources */
664 add_clone_source(&send, root_id);
674 free(snapshot_parent);
675 free(send.clone_sources);
676 if (send.mnt_fd >= 0)
678 free(send.root_path);
679 subvol_uuid_search_finit(&send.sus);
683 const char * const cmd_send_usage[] = {
684 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
685 "Send the subvolume(s) to stdout.",
686 "Sends the subvolume(s) specified by <subvol> to stdout.",
687 "By default, this will send the whole subvolume. To do an incremental",
688 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
689 "any additional local snapshots, use '-c <clone-src>' (multiple times",
690 "where applicable). You must not specify clone sources unless you",
691 "guarantee that these snapshots are exactly in the same state on both",
692 "sides, the sender and the receiver. It is allowed to omit the",
693 "'-p <parent>' option when '-c <clone-src>' options are given, in",
694 "which case 'btrfs send' will determine a suitable parent among the",
695 "clone sources itself.",
697 "-v Enable verbose debug output. Each occurrence of",
698 " this option increases the verbose level more.",
699 "-e If sending multiple subvols at once, use the new",
700 " format and omit the end-cmd between the subvols.",
701 "-p <parent> Send an incremental stream from <parent> to",
703 "-c <clone-src> Use this snapshot as a clone source for an ",
704 " incremental send (multiple allowed)",
705 "-f <outfile> Output is normally written to stdout. To write to",
706 " a file, use this option. An alternative would be to",