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);
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 void 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));
179 s->clone_sources[s->clone_sources_count++] = root_id;
182 static int write_buf(int fd, const void *buf, int size)
188 ret = write(fd, (char*)buf + pos, size - pos);
191 fprintf(stderr, "ERROR: failed to dump stream. %s",
197 fprintf(stderr, "ERROR: failed to dump stream. %s",
209 static void *dump_thread(void *arg_)
212 struct btrfs_send *s = (struct btrfs_send*)arg_;
217 readed = read(s->send_fd, buf, sizeof(buf));
220 fprintf(stderr, "ERROR: failed to read stream from "
221 "kernel. %s\n", strerror(-ret));
228 ret = write_buf(s->dump_fd, buf, readed);
241 static int do_send(struct btrfs_send *send, u64 parent_root_id,
242 int is_first_subvol, int is_last_subvol, char *subvol)
246 struct btrfs_ioctl_send_args io_send;
249 int pipefd[2] = {-1, -1};
251 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
254 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
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, NULL, 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));
332 char *get_subvol_name(char *mnt, char *full_path)
334 int len = strlen(mnt);
337 if (mnt[len - 1] != '/')
340 return full_path + len;
343 static int init_root_path(struct btrfs_send *s, const char *subvol)
350 ret = find_mount_root(subvol, &s->root_path);
353 "ERROR: failed to determine mount point for %s: %s\n",
354 subvol, strerror(-ret));
360 "ERROR: %s doesn't belong to btrfs mount point\n",
366 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
369 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
374 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
376 fprintf(stderr, "ERROR: failed to initialize subvol search. "
377 "%s\n", strerror(-ret));
386 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
392 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
395 fprintf(stderr, "ERROR: failed to open %s. %s\n",
396 subvol, strerror(-ret));
400 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
403 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
404 "%s\n", strerror(-ret));
408 if (flags & BTRFS_SUBVOL_RDONLY)
420 int cmd_send(int argc, char **argv)
425 char *outname = NULL;
426 struct btrfs_send send;
428 char *mount_root = NULL;
429 char *snapshot_parent = NULL;
431 u64 parent_root_id = 0;
433 int new_end_cmd_semantic = 0;
435 memset(&send, 0, sizeof(send));
436 send.dump_fd = fileno(stdout);
438 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
444 new_end_cmd_semantic = 1;
447 subvol = realpath(optarg, NULL);
450 fprintf(stderr, "ERROR: realpath %s failed. "
451 "%s\n", optarg, strerror(-ret));
455 ret = init_root_path(&send, subvol);
459 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
462 fprintf(stderr, "ERROR: could not resolve "
463 "root_id for %s\n", subvol);
467 ret = is_subvol_ro(&send, subvol);
473 "ERROR: cloned subvol %s is not read-only.\n",
478 add_clone_source(&send, root_id);
479 subvol_uuid_search_finit(&send.sus);
482 if (send.mnt_fd >= 0) {
486 free(send.root_path);
487 send.root_path = NULL;
494 if (snapshot_parent) {
495 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
499 snapshot_parent = realpath(optarg, NULL);
500 if (!snapshot_parent) {
502 fprintf(stderr, "ERROR: realpath %s failed. "
503 "%s\n", optarg, strerror(-ret));
507 ret = is_subvol_ro(&send, snapshot_parent);
513 "ERROR: parent %s is not read-only.\n",
522 "ERROR: -i was removed, use -c instead\n");
527 fprintf(stderr, "ERROR: send args invalid.\n");
533 if (check_argc_min(argc - optind, 1))
534 usage(cmd_send_usage);
536 if (outname != NULL) {
537 send.dump_fd = creat(outname, 0600);
538 if (send.dump_fd == -1) {
540 fprintf(stderr, "ERROR: can't create '%s': %s\n",
541 outname, strerror(-ret));
546 if (isatty(send.dump_fd)) {
548 "ERROR: not dumping send stream into a terminal, "
549 "redirect it into a file\n");
554 /* use first send subvol to determine mount_root */
555 subvol = argv[optind];
557 subvol = realpath(argv[optind], NULL);
560 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
564 ret = init_root_path(&send, subvol);
568 if (snapshot_parent != NULL) {
569 ret = get_root_id(&send,
570 get_subvol_name(send.root_path, snapshot_parent),
573 fprintf(stderr, "ERROR: could not resolve root_id "
574 "for %s\n", snapshot_parent);
578 add_clone_source(&send, parent_root_id);
581 for (i = optind; i < argc; i++) {
583 subvol = realpath(argv[i], NULL);
586 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
590 ret = find_mount_root(subvol, &mount_root);
592 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
599 "ERROR: %s doesn't belong to btrfs mount point\n",
604 if (strcmp(send.root_path, mount_root) != 0) {
606 fprintf(stderr, "ERROR: all subvols must be from the "
612 ret = is_subvol_ro(&send, subvol);
617 fprintf(stderr, "ERROR: %s is not read-only.\n",
623 for (i = optind; i < argc; i++) {
630 fprintf(stderr, "At subvol %s\n", subvol);
632 subvol = realpath(subvol, NULL);
635 fprintf(stderr, "ERROR: realpath %s failed. "
636 "%s\n", argv[i], strerror(-ret));
640 if (!full_send && !parent_root_id) {
641 ret = find_good_parent(&send, root_id, &parent_root_id);
643 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
649 ret = is_subvol_ro(&send, subvol);
654 fprintf(stderr, "ERROR: %s is not read-only.\n",
659 if (new_end_cmd_semantic) {
660 /* require new kernel */
661 is_first_subvol = (i == optind);
662 is_last_subvol = (i == argc - 1);
664 /* be compatible to old and new kernel */
668 ret = do_send(&send, parent_root_id, is_first_subvol,
669 is_last_subvol, subvol);
673 /* done with this subvol, so add it to the clone sources */
674 add_clone_source(&send, root_id);
684 free(snapshot_parent);
685 free(send.clone_sources);
686 if (send.mnt_fd >= 0)
688 free(send.root_path);
689 subvol_uuid_search_finit(&send.sus);
693 const char * const cmd_send_usage[] = {
694 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
695 "Send the subvolume(s) to stdout.",
696 "Sends the subvolume(s) specified by <subvol> to stdout.",
697 "By default, this will send the whole subvolume. To do an incremental",
698 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
699 "any additional local snapshots, use '-c <clone-src>' (multiple times",
700 "where applicable). You must not specify clone sources unless you",
701 "guarantee that these snapshots are exactly in the same state on both",
702 "sides, the sender and the receiver. It is allowed to omit the",
703 "'-p <parent>' option when '-c <clone-src>' options are given, in",
704 "which case 'btrfs send' will determine a suitable parent among the",
705 "clone sources itself.",
707 "-v Enable verbose debug output. Each occurrence of",
708 " this option increases the verbose level more.",
709 "-e If sending multiple subvols at once, use the new",
710 " format and omit the end-cmd between the subvols.",
711 "-p <parent> Send an incremental stream from <parent> to",
713 "-c <clone-src> Use this snapshot as a clone source for an ",
714 " incremental send (multiple allowed)",
715 "-f <outfile> Output is normally written to stdout. To write to",
716 " a file, use this option. An alternative would be to",