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 = setmntent("/proc/self/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 parent_root_id,
286 int is_first_subvol, int is_last_subvol, char *subvol)
290 pthread_attr_t t_attr;
291 struct btrfs_ioctl_send_args io_send;
294 int pipefd[2] = {-1, -1};
296 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
299 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
304 ret = pthread_attr_init(&t_attr);
309 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
313 memset(&io_send, 0, sizeof(io_send));
314 io_send.send_fd = pipefd[1];
315 send->send_fd = pipefd[0];
318 ret = pthread_create(&t_read, &t_attr, dump_thread,
322 fprintf(stderr, "ERROR: thread setup failed: %s\n",
327 io_send.clone_sources = (__u64*)send->clone_sources;
328 io_send.clone_sources_count = send->clone_sources_count;
329 io_send.parent_root = parent_root_id;
330 if (!is_first_subvol)
331 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
333 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
334 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
337 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
339 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
341 "Try upgrading your kernel or don't use -e.\n");
345 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
348 fprintf(stderr, "joining genl thread\n");
353 ret = pthread_join(t_read, &t_err);
356 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
361 ret = (long int)t_err;
362 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
363 "(%s)\n", (long int)t_err, strerror(-ret));
367 pthread_attr_destroy(&t_attr);
381 char *get_subvol_name(char *mnt, char *full_path)
383 int len = strlen(mnt);
386 if (mnt[len - 1] != '/')
389 return full_path + len;
392 static int init_root_path(struct btrfs_send *s, const char *subvol)
399 ret = find_mount_root(subvol, &s->root_path);
402 fprintf(stderr, "ERROR: failed to determine mount point "
407 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
410 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
415 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
417 fprintf(stderr, "ERROR: failed to initialize subvol search. "
418 "%s\n", strerror(-ret));
427 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
433 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
436 fprintf(stderr, "ERROR: failed to open %s. %s\n",
437 subvol, strerror(-ret));
441 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
444 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
445 "%s\n", strerror(-ret));
449 if (flags & BTRFS_SUBVOL_RDONLY)
461 int cmd_send(int argc, char **argv)
466 char *outname = NULL;
467 struct btrfs_send send;
469 char *mount_root = NULL;
470 char *snapshot_parent = NULL;
472 u64 parent_root_id = 0;
474 int new_end_cmd_semantic = 0;
476 memset(&send, 0, sizeof(send));
477 send.dump_fd = fileno(stdout);
479 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
485 new_end_cmd_semantic = 1;
488 subvol = realpath(optarg, NULL);
491 fprintf(stderr, "ERROR: realpath %s failed. "
492 "%s\n", optarg, strerror(-ret));
496 ret = init_root_path(&send, subvol);
500 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
503 fprintf(stderr, "ERROR: could not resolve "
504 "root_id for %s\n", subvol);
508 ret = is_subvol_ro(&send, subvol);
514 "ERROR: cloned subvol %s is not read-only.\n",
519 add_clone_source(&send, root_id);
520 subvol_uuid_search_finit(&send.sus);
523 if (send.mnt_fd >= 0) {
527 free(send.root_path);
528 send.root_path = NULL;
535 if (snapshot_parent) {
536 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
540 snapshot_parent = realpath(optarg, NULL);
541 if (!snapshot_parent) {
543 fprintf(stderr, "ERROR: realpath %s failed. "
544 "%s\n", optarg, strerror(-ret));
548 ret = is_subvol_ro(&send, snapshot_parent);
554 "ERROR: parent %s is not read-only.\n",
563 "ERROR: -i was removed, use -c instead\n");
568 fprintf(stderr, "ERROR: send args invalid.\n");
574 if (optind == argc) {
575 fprintf(stderr, "ERROR: send needs path to snapshot\n");
580 if (outname != NULL) {
581 send.dump_fd = creat(outname, 0600);
582 if (send.dump_fd == -1) {
584 fprintf(stderr, "ERROR: can't create '%s': %s\n",
585 outname, strerror(-ret));
590 if (isatty(send.dump_fd)) {
592 "ERROR: not dumping send stream into a terminal, "
593 "redirect it into a file\n");
598 /* use first send subvol to determine mount_root */
599 subvol = argv[optind];
601 subvol = realpath(argv[optind], NULL);
604 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
608 ret = init_root_path(&send, subvol);
612 if (snapshot_parent != NULL) {
613 ret = get_root_id(&send,
614 get_subvol_name(send.root_path, snapshot_parent),
617 fprintf(stderr, "ERROR: could not resolve root_id "
618 "for %s\n", snapshot_parent);
622 add_clone_source(&send, parent_root_id);
625 for (i = optind; i < argc; i++) {
627 subvol = realpath(argv[i], NULL);
630 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
634 ret = find_mount_root(subvol, &mount_root);
636 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
641 if (strcmp(send.root_path, mount_root) != 0) {
643 fprintf(stderr, "ERROR: all subvols must be from the "
649 ret = is_subvol_ro(&send, subvol);
654 fprintf(stderr, "ERROR: %s is not read-only.\n",
660 for (i = optind; i < argc; i++) {
667 fprintf(stderr, "At subvol %s\n", subvol);
669 subvol = realpath(subvol, NULL);
672 fprintf(stderr, "ERROR: realpath %s failed. "
673 "%s\n", argv[i], strerror(-ret));
677 if (!full_send && !parent_root_id) {
678 ret = find_good_parent(&send, root_id, &parent_root_id);
680 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
686 ret = is_subvol_ro(&send, subvol);
691 fprintf(stderr, "ERROR: %s is not read-only.\n",
696 if (new_end_cmd_semantic) {
697 /* require new kernel */
698 is_first_subvol = (i == optind);
699 is_last_subvol = (i == argc - 1);
701 /* be compatible to old and new kernel */
705 ret = do_send(&send, parent_root_id, is_first_subvol,
706 is_last_subvol, subvol);
710 /* done with this subvol, so add it to the clone sources */
711 add_clone_source(&send, root_id);
721 free(snapshot_parent);
722 free(send.clone_sources);
723 if (send.mnt_fd >= 0)
725 free(send.root_path);
726 subvol_uuid_search_finit(&send.sus);
730 const char * const cmd_send_usage[] = {
731 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
732 "Send the subvolume(s) to stdout.",
733 "Sends the subvolume(s) specified by <subvol> to stdout.",
734 "By default, this will send the whole subvolume. To do an incremental",
735 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
736 "any additional local snapshots, use '-c <clone-src>' (multiple times",
737 "where applicable). You must not specify clone sources unless you",
738 "guarantee that these snapshots are exactly in the same state on both",
739 "sides, the sender and the receiver. It is allowed to omit the",
740 "'-p <parent>' option when '-c <clone-src>' options are given, in",
741 "which case 'btrfs send' will determine a suitable parent among the",
742 "clone sources itself.",
744 "-v Enable verbose debug output. Each occurrence of",
745 " this option increases the verbose level more.",
746 "-e If sending multiple subvols at once, use the new",
747 " format and omit the end-cmd between the subvols.",
748 "-p <parent> Send an incremental stream from <parent> to",
750 "-c <clone-src> Use this snapshot as a clone source for an ",
751 " incremental send (multiple allowed)",
752 "-f <outfile> Output is normally written to stdout. To write to",
753 " a file, use this option. An alternative would be to",