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");
75 while ((ent = getmntent(mnttab))) {
76 len = strlen(ent->mnt_dir);
77 if (strncmp(ent->mnt_dir, path, len) == 0) {
79 if (longest_matchlen < len) {
81 longest_matchlen = len;
82 longest_match = strdup(ent->mnt_dir);
90 "ERROR: Failed to find mount root for path %s.\n",
95 *mount_root = realpath(longest_match, NULL);
101 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
103 struct subvol_info *si;
105 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
106 subvol_search_by_path);
109 *root_id = si->root_id;
115 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
117 struct subvol_info *si_tmp;
118 struct subvol_info *si;
120 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
121 subvol_search_by_root_id);
125 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
126 subvol_search_by_uuid);
132 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
135 struct subvol_info *parent = NULL;
136 struct subvol_info *parent2 = NULL;
137 struct subvol_info *best_parent = NULL;
139 u64 best_diff = (u64)-1;
142 parent = get_parent(s, root_id);
148 for (i = 0; i < s->clone_sources_count; i++) {
149 if (s->clone_sources[i] == parent->root_id) {
150 best_parent = parent;
156 for (i = 0; i < s->clone_sources_count; i++) {
157 parent2 = get_parent(s, s->clone_sources[i]);
160 if (parent2->root_id != parent->root_id) {
169 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
170 0, NULL, subvol_search_by_root_id);
173 tmp = parent2->ctransid - parent->ctransid;
176 if (tmp < best_diff) {
178 free(best_parent->path);
181 best_parent = parent2;
197 *found = best_parent->root_id;
206 free(best_parent->path);
212 static void add_clone_source(struct btrfs_send *s, u64 root_id)
214 s->clone_sources = realloc(s->clone_sources,
215 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
216 s->clone_sources[s->clone_sources_count++] = root_id;
219 static int write_buf(int fd, const void *buf, int size)
225 ret = write(fd, (char*)buf + pos, size - pos);
228 fprintf(stderr, "ERROR: failed to dump stream. %s",
234 fprintf(stderr, "ERROR: failed to dump stream. %s",
246 static void *dump_thread(void *arg_)
249 struct btrfs_send *s = (struct btrfs_send*)arg_;
254 readed = read(s->send_fd, buf, sizeof(buf));
257 fprintf(stderr, "ERROR: failed to read stream from "
258 "kernel. %s\n", strerror(-ret));
265 ret = write_buf(s->dump_fd, buf, readed);
278 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id,
279 int is_first_subvol, int is_last_subvol)
283 pthread_attr_t t_attr;
284 struct btrfs_ioctl_send_args io_send;
285 struct subvol_info *si;
288 int pipefd[2] = {-1, -1};
290 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
291 subvol_search_by_root_id);
294 fprintf(stderr, "ERROR: could not find subvol info for %llu",
299 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
302 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
307 ret = pthread_attr_init(&t_attr);
312 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
316 memset(&io_send, 0, sizeof(io_send));
317 io_send.send_fd = pipefd[1];
318 send->send_fd = pipefd[0];
321 ret = pthread_create(&t_read, &t_attr, dump_thread,
325 fprintf(stderr, "ERROR: thread setup failed: %s\n",
330 io_send.clone_sources = (__u64*)send->clone_sources;
331 io_send.clone_sources_count = send->clone_sources_count;
332 io_send.parent_root = parent_root_id;
333 if (!is_first_subvol)
334 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
336 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
337 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
340 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
342 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
344 "Try upgrading your kernel or don't use -e.\n");
348 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
351 fprintf(stderr, "joining genl thread\n");
356 ret = pthread_join(t_read, &t_err);
359 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
364 ret = (long int)t_err;
365 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
366 "(%s)\n", (long int)t_err, strerror(-ret));
370 pthread_attr_destroy(&t_attr);
388 char *get_subvol_name(char *mnt, char *full_path)
390 int len = strlen(mnt);
393 if (mnt[len - 1] != '/')
396 return full_path + len;
399 static int init_root_path(struct btrfs_send *s, const char *subvol)
406 ret = find_mount_root(subvol, &s->root_path);
409 fprintf(stderr, "ERROR: failed to determine mount point "
414 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
417 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
422 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
424 fprintf(stderr, "ERROR: failed to initialize subvol search. "
425 "%s\n", strerror(-ret));
434 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
440 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
443 fprintf(stderr, "ERROR: failed to open %s. %s\n",
444 subvol, strerror(-ret));
448 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
451 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
452 "%s\n", strerror(-ret));
456 if (flags & BTRFS_SUBVOL_RDONLY)
468 int cmd_send(int argc, char **argv)
473 char *outname = NULL;
474 struct btrfs_send send;
476 char *mount_root = NULL;
477 char *snapshot_parent = NULL;
479 u64 parent_root_id = 0;
481 int new_end_cmd_semantic = 0;
483 memset(&send, 0, sizeof(send));
484 send.dump_fd = fileno(stdout);
486 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
492 new_end_cmd_semantic = 1;
495 subvol = realpath(optarg, NULL);
498 fprintf(stderr, "ERROR: realpath %s failed. "
499 "%s\n", optarg, strerror(-ret));
503 ret = init_root_path(&send, subvol);
507 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
510 fprintf(stderr, "ERROR: could not resolve "
511 "root_id for %s\n", subvol);
514 add_clone_source(&send, root_id);
515 subvol_uuid_search_finit(&send.sus);
518 if (send.mnt_fd >= 0) {
522 free(send.root_path);
523 send.root_path = NULL;
530 if (snapshot_parent) {
531 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
535 snapshot_parent = realpath(optarg, NULL);
536 if (!snapshot_parent) {
538 fprintf(stderr, "ERROR: realpath %s failed. "
539 "%s\n", optarg, strerror(-ret));
546 "ERROR: -i was removed, use -c instead\n");
551 fprintf(stderr, "ERROR: send args invalid.\n");
557 if (optind == argc) {
558 fprintf(stderr, "ERROR: send needs path to snapshot\n");
563 if (outname != NULL) {
564 send.dump_fd = creat(outname, 0600);
565 if (send.dump_fd == -1) {
567 fprintf(stderr, "ERROR: can't create '%s': %s\n",
568 outname, strerror(-ret));
573 if (isatty(send.dump_fd)) {
575 "ERROR: not dumping send stream into a terminal, "
576 "redirect it into a file\n");
581 /* use first send subvol to determine mount_root */
582 subvol = argv[optind];
584 subvol = realpath(argv[optind], NULL);
587 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
591 ret = init_root_path(&send, subvol);
595 if (snapshot_parent != NULL) {
596 ret = get_root_id(&send,
597 get_subvol_name(send.root_path, snapshot_parent),
600 fprintf(stderr, "ERROR: could not resolve root_id "
601 "for %s\n", snapshot_parent);
605 add_clone_source(&send, parent_root_id);
608 for (i = optind; i < argc; i++) {
610 subvol = realpath(argv[i], NULL);
613 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
617 ret = find_mount_root(subvol, &mount_root);
619 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
624 if (strcmp(send.root_path, mount_root) != 0) {
626 fprintf(stderr, "ERROR: all subvols must be from the "
632 ret = is_subvol_ro(&send, subvol);
637 fprintf(stderr, "ERROR: %s is not read-only.\n",
643 for (i = optind; i < argc; i++) {
650 fprintf(stderr, "At subvol %s\n", subvol);
652 subvol = realpath(subvol, NULL);
655 fprintf(stderr, "ERROR: realpath %s failed. "
656 "%s\n", argv[i], strerror(-ret));
660 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
663 fprintf(stderr, "ERROR: could not resolve root_id "
668 if (!full_send && !parent_root_id) {
669 ret = find_good_parent(&send, root_id, &parent_root_id);
671 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
677 ret = is_subvol_ro(&send, subvol);
682 fprintf(stderr, "ERROR: %s is not read-only.\n",
687 if (new_end_cmd_semantic) {
688 /* require new kernel */
689 is_first_subvol = (i == optind);
690 is_last_subvol = (i == argc - 1);
692 /* be compatible to old and new kernel */
696 ret = do_send(&send, root_id, parent_root_id,
697 is_first_subvol, is_last_subvol);
701 /* done with this subvol, so add it to the clone sources */
702 add_clone_source(&send, root_id);
712 free(snapshot_parent);
713 free(send.clone_sources);
714 if (send.mnt_fd >= 0)
716 free(send.root_path);
717 subvol_uuid_search_finit(&send.sus);
721 const char * const cmd_send_usage[] = {
722 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol>",
723 "Send the subvolume to stdout.",
724 "Sends the subvolume specified by <subvol> to stdout.",
725 "By default, this will send the whole subvolume. To do an incremental",
726 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
727 "any additional local snapshots, use '-c <clone-src>' (multiple times",
728 "where applicable). You must not specify clone sources unless you",
729 "guarantee that these snapshots are exactly in the same state on both",
730 "sides, the sender and the receiver. It is allowed to omit the",
731 "'-p <parent>' option when '-c <clone-src>' options are given, in",
732 "which case 'btrfs send' will determine a suitable parent among the",
733 "clone sources itself.",
735 "-v Enable verbose debug output. Each occurrence of",
736 " this option increases the verbose level more.",
737 "-e If sending multiple subvols at once, use the new",
738 " format and omit the end-cmd between the subvols.",
739 "-p <parent> Send an incremental stream from <parent> to",
741 "-c <clone-src> Use this snapshot as a clone source for an ",
742 " incremental send (multiple allowed)",
743 "-f <outfile> Output is normally written to stdout. To write to",
744 " a file, use this option. An alternative would be to",