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>
44 #include "send-utils.h"
46 static int g_verbose = 0;
54 u64 clone_sources_count;
57 struct subvol_uuid_search sus;
60 int find_mount_root(const char *path, char **mount_root)
66 int longest_matchlen = 0;
67 char *longest_match = NULL;
69 fd = open(path, O_RDONLY | O_NOATIME);
74 mnttab = fopen("/proc/mounts", "r");
78 while ((ent = getmntent(mnttab))) {
79 len = strlen(ent->mnt_dir);
80 if (strncmp(ent->mnt_dir, path, len) == 0) {
82 if (longest_matchlen < len) {
84 longest_matchlen = len;
85 longest_match = strdup(ent->mnt_dir);
93 "ERROR: Failed to find mount root for path %s.\n",
98 *mount_root = realpath(longest_match, NULL);
104 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
106 struct subvol_info *si;
108 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
109 subvol_search_by_path);
112 *root_id = si->root_id;
118 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
120 struct subvol_info *si_tmp;
121 struct subvol_info *si;
123 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
124 subvol_search_by_root_id);
128 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
129 subvol_search_by_uuid);
135 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
138 struct subvol_info *parent = NULL;
139 struct subvol_info *parent2 = NULL;
140 struct subvol_info *best_parent = NULL;
142 u64 best_diff = (u64)-1;
145 parent = get_parent(s, root_id);
151 for (i = 0; i < s->clone_sources_count; i++) {
152 if (s->clone_sources[i] == parent->root_id) {
153 best_parent = parent;
159 for (i = 0; i < s->clone_sources_count; i++) {
160 parent2 = get_parent(s, s->clone_sources[i]);
163 if (parent2->root_id != parent->root_id) {
172 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
173 0, NULL, subvol_search_by_root_id);
176 tmp = parent2->ctransid - parent->ctransid;
179 if (tmp < best_diff) {
181 free(best_parent->path);
184 best_parent = parent2;
200 *found = best_parent->root_id;
209 free(best_parent->path);
215 static void add_clone_source(struct btrfs_send *s, u64 root_id)
217 s->clone_sources = realloc(s->clone_sources,
218 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
219 s->clone_sources[s->clone_sources_count++] = root_id;
222 static int write_buf(int fd, const void *buf, int size)
228 ret = write(fd, (char*)buf + pos, size - pos);
231 fprintf(stderr, "ERROR: failed to dump stream. %s",
237 fprintf(stderr, "ERROR: failed to dump stream. %s",
249 static void *dump_thread(void *arg_)
252 struct btrfs_send *s = (struct btrfs_send*)arg_;
257 readed = read(s->send_fd, buf, sizeof(buf));
260 fprintf(stderr, "ERROR: failed to read stream from "
261 "kernel. %s\n", strerror(-ret));
268 ret = write_buf(s->dump_fd, buf, readed);
281 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id,
282 int is_first_subvol, int is_last_subvol)
286 pthread_attr_t t_attr;
287 struct btrfs_ioctl_send_args io_send;
288 struct subvol_info *si;
291 int pipefd[2] = {-1, -1};
293 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
294 subvol_search_by_root_id);
297 fprintf(stderr, "ERROR: could not find subvol info for %llu",
302 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
305 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
310 ret = pthread_attr_init(&t_attr);
315 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
319 memset(&io_send, 0, sizeof(io_send));
320 io_send.send_fd = pipefd[1];
321 send->send_fd = pipefd[0];
324 ret = pthread_create(&t_read, &t_attr, dump_thread,
328 fprintf(stderr, "ERROR: thread setup failed: %s\n",
333 io_send.clone_sources = (__u64*)send->clone_sources;
334 io_send.clone_sources_count = send->clone_sources_count;
335 io_send.parent_root = parent_root_id;
336 if (!is_first_subvol)
337 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
339 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
340 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
343 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
345 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
347 "Try upgrading your kernel or don't use -e.\n");
351 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
354 fprintf(stderr, "joining genl thread\n");
359 ret = pthread_join(t_read, &t_err);
362 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
367 ret = (long int)t_err;
368 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
369 "(%s)\n", (long int)t_err, strerror(-ret));
373 pthread_attr_destroy(&t_attr);
391 char *get_subvol_name(char *mnt, char *full_path)
393 int len = strlen(mnt);
396 if (mnt[len - 1] != '/')
399 return full_path + len;
402 static int init_root_path(struct btrfs_send *s, const char *subvol)
409 ret = find_mount_root(subvol, &s->root_path);
412 fprintf(stderr, "ERROR: failed to determine mount point "
417 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
420 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
425 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
427 fprintf(stderr, "ERROR: failed to initialize subvol search. "
428 "%s\n", strerror(-ret));
437 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
443 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
446 fprintf(stderr, "ERROR: failed to open %s. %s\n",
447 subvol, strerror(-ret));
451 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
454 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
455 "%s\n", strerror(-ret));
459 if (flags & BTRFS_SUBVOL_RDONLY)
471 int cmd_send(int argc, char **argv)
476 char *outname = NULL;
477 struct btrfs_send send;
479 char *mount_root = NULL;
480 char *snapshot_parent = NULL;
482 u64 parent_root_id = 0;
484 int new_end_cmd_semantic = 0;
486 memset(&send, 0, sizeof(send));
487 send.dump_fd = fileno(stdout);
489 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
495 new_end_cmd_semantic = 1;
498 subvol = realpath(optarg, NULL);
501 fprintf(stderr, "ERROR: realpath %s failed. "
502 "%s\n", optarg, strerror(-ret));
506 ret = init_root_path(&send, subvol);
510 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
513 fprintf(stderr, "ERROR: could not resolve "
514 "root_id for %s\n", subvol);
517 add_clone_source(&send, root_id);
518 subvol_uuid_search_finit(&send.sus);
521 if (send.mnt_fd >= 0) {
525 free(send.root_path);
526 send.root_path = NULL;
533 if (snapshot_parent) {
534 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
538 snapshot_parent = realpath(optarg, NULL);
539 if (!snapshot_parent) {
541 fprintf(stderr, "ERROR: realpath %s failed. "
542 "%s\n", optarg, strerror(-ret));
549 "ERROR: -i was removed, use -c instead\n");
554 fprintf(stderr, "ERROR: send args invalid.\n");
560 if (optind == argc) {
561 fprintf(stderr, "ERROR: send needs path to snapshot\n");
566 if (outname != NULL) {
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 add_clone_source(&send, parent_root_id);
611 for (i = optind; i < argc; i++) {
613 subvol = realpath(argv[i], NULL);
616 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
620 ret = find_mount_root(subvol, &mount_root);
622 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
627 if (strcmp(send.root_path, mount_root) != 0) {
629 fprintf(stderr, "ERROR: all subvols must be from the "
635 ret = is_subvol_ro(&send, subvol);
640 fprintf(stderr, "ERROR: %s is not read-only.\n",
646 for (i = optind; i < argc; i++) {
653 fprintf(stderr, "At subvol %s\n", subvol);
655 subvol = realpath(subvol, NULL);
658 fprintf(stderr, "ERROR: realpath %s failed. "
659 "%s\n", argv[i], strerror(-ret));
663 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
666 fprintf(stderr, "ERROR: could not resolve root_id "
671 if (!full_send && !parent_root_id) {
672 ret = find_good_parent(&send, root_id, &parent_root_id);
674 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
680 ret = is_subvol_ro(&send, subvol);
685 fprintf(stderr, "ERROR: %s is not read-only.\n",
690 if (new_end_cmd_semantic) {
691 /* require new kernel */
692 is_first_subvol = (i == optind);
693 is_last_subvol = (i == argc - 1);
695 /* be compatible to old and new kernel */
699 ret = do_send(&send, root_id, parent_root_id,
700 is_first_subvol, is_last_subvol);
704 /* done with this subvol, so add it to the clone sources */
705 add_clone_source(&send, root_id);
715 free(snapshot_parent);
716 free(send.clone_sources);
717 if (send.mnt_fd >= 0)
719 free(send.root_path);
720 subvol_uuid_search_finit(&send.sus);
724 const char * const cmd_send_usage[] = {
725 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol>",
726 "Send the subvolume to stdout.",
727 "Sends the subvolume specified by <subvol> to stdout.",
728 "By default, this will send the whole subvolume. To do an incremental",
729 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
730 "any additional local snapshots, use '-c <clone-src>' (multiple times",
731 "where applicable). You must not specify clone sources unless you",
732 "guarantee that these snapshots are exactly in the same state on both",
733 "sides, the sender and the receiver. It is allowed to omit the",
734 "'-p <parent>' option when '-c <clone-src>' options are given, in",
735 "which case 'btrfs send' will determine a suitable parent among the",
736 "clone sources itself.",
738 "-v Enable verbose debug output. Each occurrence of",
739 " this option increases the verbose level more.",
740 "-e If sending multiple subvols at once, use the new",
741 " format and omit the end-cmd between the subvols.",
742 "-p <parent> Send an incremental stream from <parent> to",
744 "-c <clone-src> Use this snapshot as a clone source for an ",
745 " incremental send (multiple allowed)",
746 "-f <outfile> Output is normally written to stdout. To write to",
747 " a file, use this option. An alternative would be to",