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)
67 int longest_matchlen = 0;
68 char *longest_match = NULL;
70 fd = open(path, O_RDONLY | O_NOATIME);
75 mnttab = fopen("/proc/mounts", "r");
79 while ((ent = getmntent(mnttab))) {
80 len = strlen(ent->mnt_dir);
81 if (strncmp(ent->mnt_dir, path, len) == 0) {
83 if (longest_matchlen < len) {
85 longest_matchlen = len;
86 longest_match = strdup(ent->mnt_dir);
94 "ERROR: Failed to find mount root for path %s.\n",
100 *mount_root = realpath(longest_match, NULL);
108 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
110 struct subvol_info *si;
112 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
113 subvol_search_by_path);
116 *root_id = si->root_id;
122 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
124 struct subvol_info *si_tmp;
125 struct subvol_info *si;
127 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
128 subvol_search_by_root_id);
132 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
133 subvol_search_by_uuid);
139 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
142 struct subvol_info *parent = NULL;
143 struct subvol_info *parent2 = NULL;
144 struct subvol_info *best_parent = NULL;
146 u64 best_diff = (u64)-1;
149 parent = get_parent(s, root_id);
155 for (i = 0; i < s->clone_sources_count; i++) {
156 if (s->clone_sources[i] == parent->root_id) {
157 best_parent = parent;
163 for (i = 0; i < s->clone_sources_count; i++) {
164 parent2 = get_parent(s, s->clone_sources[i]);
167 if (parent2->root_id != parent->root_id) {
176 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
177 0, NULL, subvol_search_by_root_id);
180 tmp = parent2->ctransid - parent->ctransid;
183 if (tmp < best_diff) {
185 free(best_parent->path);
188 best_parent = parent2;
204 *found = best_parent->root_id;
213 free(best_parent->path);
219 static void add_clone_source(struct btrfs_send *s, u64 root_id)
221 s->clone_sources = realloc(s->clone_sources,
222 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
223 s->clone_sources[s->clone_sources_count++] = root_id;
226 static int write_buf(int fd, const void *buf, int size)
232 ret = write(fd, (char*)buf + pos, size - pos);
235 fprintf(stderr, "ERROR: failed to dump stream. %s",
241 fprintf(stderr, "ERROR: failed to dump stream. %s",
253 static void *dump_thread(void *arg_)
256 struct btrfs_send *s = (struct btrfs_send*)arg_;
261 readed = read(s->send_fd, buf, sizeof(buf));
264 fprintf(stderr, "ERROR: failed to read stream from "
265 "kernel. %s\n", strerror(-ret));
272 ret = write_buf(s->dump_fd, buf, readed);
285 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id,
286 int is_first_subvol, int is_last_subvol)
290 pthread_attr_t t_attr;
291 struct btrfs_ioctl_send_args io_send;
292 struct subvol_info *si;
295 int pipefd[2] = {-1, -1};
297 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
298 subvol_search_by_root_id);
301 fprintf(stderr, "ERROR: could not find subvol info for %llu",
306 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
309 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
314 ret = pthread_attr_init(&t_attr);
319 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
323 memset(&io_send, 0, sizeof(io_send));
324 io_send.send_fd = pipefd[1];
325 send->send_fd = pipefd[0];
328 ret = pthread_create(&t_read, &t_attr, dump_thread,
332 fprintf(stderr, "ERROR: thread setup failed: %s\n",
337 io_send.clone_sources = (__u64*)send->clone_sources;
338 io_send.clone_sources_count = send->clone_sources_count;
339 io_send.parent_root = parent_root_id;
340 if (!is_first_subvol)
341 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
343 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
344 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
347 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
349 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
351 "Try upgrading your kernel or don't use -e.\n");
355 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
358 fprintf(stderr, "joining genl thread\n");
363 ret = pthread_join(t_read, &t_err);
366 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
371 ret = (long int)t_err;
372 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
373 "(%s)\n", (long int)t_err, strerror(-ret));
377 pthread_attr_destroy(&t_attr);
395 char *get_subvol_name(char *mnt, char *full_path)
397 int len = strlen(mnt);
400 if (mnt[len - 1] != '/')
403 return full_path + len;
406 static int init_root_path(struct btrfs_send *s, const char *subvol)
413 ret = find_mount_root(subvol, &s->root_path);
416 fprintf(stderr, "ERROR: failed to determine mount point "
421 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
424 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
429 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
431 fprintf(stderr, "ERROR: failed to initialize subvol search. "
432 "%s\n", strerror(-ret));
441 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
447 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
450 fprintf(stderr, "ERROR: failed to open %s. %s\n",
451 subvol, strerror(-ret));
455 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
458 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
459 "%s\n", strerror(-ret));
463 if (flags & BTRFS_SUBVOL_RDONLY)
475 int cmd_send(int argc, char **argv)
480 char *outname = NULL;
481 struct btrfs_send send;
483 char *mount_root = NULL;
484 char *snapshot_parent = NULL;
486 u64 parent_root_id = 0;
488 int new_end_cmd_semantic = 0;
490 memset(&send, 0, sizeof(send));
491 send.dump_fd = fileno(stdout);
493 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
499 new_end_cmd_semantic = 1;
502 subvol = realpath(optarg, NULL);
505 fprintf(stderr, "ERROR: realpath %s failed. "
506 "%s\n", optarg, strerror(-ret));
510 ret = init_root_path(&send, subvol);
514 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
517 fprintf(stderr, "ERROR: could not resolve "
518 "root_id for %s\n", subvol);
521 add_clone_source(&send, root_id);
522 subvol_uuid_search_finit(&send.sus);
525 if (send.mnt_fd >= 0) {
529 free(send.root_path);
530 send.root_path = NULL;
537 if (snapshot_parent) {
538 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
542 snapshot_parent = realpath(optarg, NULL);
543 if (!snapshot_parent) {
545 fprintf(stderr, "ERROR: realpath %s failed. "
546 "%s\n", optarg, strerror(-ret));
553 "ERROR: -i was removed, use -c instead\n");
558 fprintf(stderr, "ERROR: send args invalid.\n");
564 if (optind == argc) {
565 fprintf(stderr, "ERROR: send needs path to snapshot\n");
570 if (outname != NULL) {
571 send.dump_fd = creat(outname, 0600);
572 if (send.dump_fd == -1) {
574 fprintf(stderr, "ERROR: can't create '%s': %s\n",
575 outname, strerror(-ret));
580 if (isatty(send.dump_fd)) {
582 "ERROR: not dumping send stream into a terminal, "
583 "redirect it into a file\n");
588 /* use first send subvol to determine mount_root */
589 subvol = argv[optind];
591 subvol = realpath(argv[optind], NULL);
594 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
598 ret = init_root_path(&send, subvol);
602 if (snapshot_parent != NULL) {
603 ret = get_root_id(&send,
604 get_subvol_name(send.root_path, snapshot_parent),
607 fprintf(stderr, "ERROR: could not resolve root_id "
608 "for %s\n", snapshot_parent);
612 add_clone_source(&send, parent_root_id);
615 for (i = optind; i < argc; i++) {
617 subvol = realpath(argv[i], NULL);
620 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
624 ret = find_mount_root(subvol, &mount_root);
626 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
631 if (strcmp(send.root_path, mount_root) != 0) {
633 fprintf(stderr, "ERROR: all subvols must be from the "
639 ret = is_subvol_ro(&send, subvol);
644 fprintf(stderr, "ERROR: %s is not read-only.\n",
650 for (i = optind; i < argc; i++) {
657 fprintf(stderr, "At subvol %s\n", subvol);
659 subvol = realpath(subvol, NULL);
662 fprintf(stderr, "ERROR: realpath %s failed. "
663 "%s\n", argv[i], strerror(-ret));
667 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
670 fprintf(stderr, "ERROR: could not resolve root_id "
675 if (!full_send && !parent_root_id) {
676 ret = find_good_parent(&send, root_id, &parent_root_id);
678 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
684 ret = is_subvol_ro(&send, subvol);
689 fprintf(stderr, "ERROR: %s is not read-only.\n",
694 if (new_end_cmd_semantic) {
695 /* require new kernel */
696 is_first_subvol = (i == optind);
697 is_last_subvol = (i == argc - 1);
699 /* be compatible to old and new kernel */
703 ret = do_send(&send, root_id, parent_root_id,
704 is_first_subvol, is_last_subvol);
708 /* done with this subvol, so add it to the clone sources */
709 add_clone_source(&send, root_id);
719 free(snapshot_parent);
720 free(send.clone_sources);
721 if (send.mnt_fd >= 0)
723 free(send.root_path);
724 subvol_uuid_search_finit(&send.sus);
728 const char * const cmd_send_usage[] = {
729 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol>",
730 "Send the subvolume to stdout.",
731 "Sends the subvolume specified by <subvol> to stdout.",
732 "By default, this will send the whole subvolume. To do an incremental",
733 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
734 "any additional local snapshots, use '-c <clone-src>' (multiple times",
735 "where applicable). You must not specify clone sources unless you",
736 "guarantee that these snapshots are exactly in the same state on both",
737 "sides, the sender and the receiver. It is allowed to omit the",
738 "'-p <parent>' option when '-c <clone-src>' options are given, in",
739 "which case 'btrfs send' will determine a suitable parent among the",
740 "clone sources itself.",
742 "-v Enable verbose debug output. Each occurrence of",
743 " this option increases the verbose level more.",
744 "-e If sending multiple subvols at once, use the new",
745 " format and omit the end-cmd between the subvols.",
746 "-p <parent> Send an incremental stream from <parent> to",
748 "-c <clone-src> Use this snapshot as a clone source for an ",
749 " incremental send (multiple allowed)",
750 "-f <outfile> Output is normally written to stdout. To write to",
751 " a file, use this option. An alternative would be to",