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>
35 #include <uuid/uuid.h>
43 #include "send-utils.h"
45 static int g_verbose = 0;
53 u64 clone_sources_count;
56 struct subvol_uuid_search sus;
59 int find_mount_root(const char *path, char **mount_root)
65 int longest_matchlen = 0;
66 char *longest_match = NULL;
68 fd = open(path, O_RDONLY | O_NOATIME);
73 mnttab = fopen("/proc/mounts", "r");
74 while ((ent = getmntent(mnttab))) {
75 len = strlen(ent->mnt_dir);
76 if (strncmp(ent->mnt_dir, path, len) == 0) {
78 if (longest_matchlen < len) {
80 longest_matchlen = len;
81 longest_match = strdup(ent->mnt_dir);
89 "ERROR: Failed to find mount root for path %s.\n",
94 *mount_root = realpath(longest_match, NULL);
100 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
102 struct subvol_info *si;
104 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
105 subvol_search_by_path);
108 *root_id = si->root_id;
112 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
114 struct subvol_info *si;
116 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
117 subvol_search_by_root_id);
121 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
122 subvol_search_by_uuid);
128 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
131 struct subvol_info *parent;
132 struct subvol_info *parent2;
133 struct subvol_info *best_parent = NULL;
135 u64 best_diff = (u64)-1;
138 parent = get_parent(s, root_id);
144 for (i = 0; i < s->clone_sources_count; i++) {
145 if (s->clone_sources[i] == parent->root_id) {
146 best_parent = parent;
151 for (i = 0; i < s->clone_sources_count; i++) {
152 parent2 = get_parent(s, s->clone_sources[i]);
153 if (parent2 != parent)
156 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
157 0, NULL, subvol_search_by_root_id);
159 tmp = parent2->ctransid - parent->ctransid;
162 if (tmp < best_diff) {
163 best_parent = parent2;
174 *found = best_parent->root_id;
181 static void add_clone_source(struct btrfs_send *s, u64 root_id)
183 s->clone_sources = realloc(s->clone_sources,
184 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
185 s->clone_sources[s->clone_sources_count++] = root_id;
188 static int write_buf(int fd, const void *buf, int size)
194 ret = write(fd, (char*)buf + pos, size - pos);
197 fprintf(stderr, "ERROR: failed to dump stream. %s",
203 fprintf(stderr, "ERROR: failed to dump stream. %s",
215 static void *dump_thread(void *arg_)
218 struct btrfs_send *s = (struct btrfs_send*)arg_;
223 readed = read(s->send_fd, buf, sizeof(buf));
226 fprintf(stderr, "ERROR: failed to read stream from "
227 "kernel. %s\n", strerror(-ret));
234 ret = write_buf(s->dump_fd, buf, readed);
247 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id,
248 int is_first_subvol, int is_last_subvol)
252 pthread_attr_t t_attr;
253 struct btrfs_ioctl_send_args io_send;
254 struct subvol_info *si;
257 int pipefd[2] = {-1, -1};
259 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
260 subvol_search_by_root_id);
263 fprintf(stderr, "ERROR: could not find subvol info for %llu",
268 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
271 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
276 ret = pthread_attr_init(&t_attr);
281 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
285 memset(&io_send, 0, sizeof(io_send));
286 io_send.send_fd = pipefd[1];
287 send->send_fd = pipefd[0];
290 ret = pthread_create(&t_read, &t_attr, dump_thread,
294 fprintf(stderr, "ERROR: thread setup failed: %s\n",
299 io_send.clone_sources = (__u64*)send->clone_sources;
300 io_send.clone_sources_count = send->clone_sources_count;
301 io_send.parent_root = parent_root_id;
302 if (!is_first_subvol)
303 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
305 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
306 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
309 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
311 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
313 "Try upgrading your kernel or don't use -e.\n");
317 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
320 fprintf(stderr, "joining genl thread\n");
325 ret = pthread_join(t_read, &t_err);
328 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
333 ret = (long int)t_err;
334 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
335 "(%s)\n", (long int)t_err, strerror(-ret));
339 pthread_attr_destroy(&t_attr);
353 char *get_subvol_name(char *mnt, char *full_path)
355 int len = strlen(mnt);
358 if (mnt[len - 1] != '/')
361 return full_path + len;
364 static int init_root_path(struct btrfs_send *s, const char *subvol)
371 ret = find_mount_root(subvol, &s->root_path);
374 fprintf(stderr, "ERROR: failed to determine mount point "
379 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
382 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
387 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
389 fprintf(stderr, "ERROR: failed to initialize subvol search. "
390 "%s\n", strerror(-ret));
399 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
405 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
408 fprintf(stderr, "ERROR: failed to open %s. %s\n",
409 subvol, strerror(-ret));
413 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
416 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
417 "%s\n", strerror(-ret));
421 if (flags & BTRFS_SUBVOL_RDONLY)
433 int cmd_send_start(int argc, char **argv)
438 char *outname = NULL;
439 struct btrfs_send send;
441 char *mount_root = NULL;
442 char *snapshot_parent = NULL;
444 u64 parent_root_id = 0;
446 int new_end_cmd_semantic = 0;
448 memset(&send, 0, sizeof(send));
449 send.dump_fd = fileno(stdout);
451 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
457 new_end_cmd_semantic = 1;
460 subvol = realpath(optarg, NULL);
463 fprintf(stderr, "ERROR: realpath %s failed. "
464 "%s\n", optarg, strerror(-ret));
468 ret = init_root_path(&send, subvol);
472 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
475 fprintf(stderr, "ERROR: could not resolve "
476 "root_id for %s\n", subvol);
479 add_clone_source(&send, root_id);
480 subvol_uuid_search_finit(&send.sus);
483 if (send.mnt_fd >= 0) {
487 free(send.root_path);
488 send.root_path = NULL;
495 if (snapshot_parent) {
496 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
500 snapshot_parent = realpath(optarg, NULL);
501 if (!snapshot_parent) {
503 fprintf(stderr, "ERROR: realpath %s failed. "
504 "%s\n", optarg, strerror(-ret));
511 "ERROR: -i was removed, use -c instead\n");
516 fprintf(stderr, "ERROR: send args invalid.\n");
522 if (optind == argc) {
523 fprintf(stderr, "ERROR: send needs path to snapshot\n");
528 if (outname != NULL) {
529 send.dump_fd = creat(outname, 0600);
530 if (send.dump_fd == -1) {
532 fprintf(stderr, "ERROR: can't create '%s': %s\n",
533 outname, strerror(-ret));
538 if (isatty(send.dump_fd)) {
540 "ERROR: not dumping send stream into a terminal, "
541 "redirect it into a file\n");
546 /* use first send subvol to determine mount_root */
547 subvol = argv[optind];
549 subvol = realpath(argv[optind], NULL);
552 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
556 ret = init_root_path(&send, subvol);
560 if (snapshot_parent != NULL) {
561 ret = get_root_id(&send,
562 get_subvol_name(send.root_path, snapshot_parent),
565 fprintf(stderr, "ERROR: could not resolve root_id "
566 "for %s\n", snapshot_parent);
570 add_clone_source(&send, parent_root_id);
573 for (i = optind; i < argc; i++) {
575 subvol = realpath(argv[i], NULL);
578 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
582 ret = find_mount_root(subvol, &mount_root);
584 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
589 if (strcmp(send.root_path, mount_root) != 0) {
591 fprintf(stderr, "ERROR: all subvols must be from the "
597 ret = is_subvol_ro(&send, subvol);
602 fprintf(stderr, "ERROR: %s is not read-only.\n",
608 for (i = optind; i < argc; i++) {
615 fprintf(stderr, "At subvol %s\n", subvol);
617 subvol = realpath(subvol, NULL);
620 fprintf(stderr, "ERROR: realpath %s failed. "
621 "%s\n", argv[i], strerror(-ret));
625 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
628 fprintf(stderr, "ERROR: could not resolve root_id "
633 if (!full_send && !parent_root_id) {
634 ret = find_good_parent(&send, root_id, &parent_root_id);
636 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
642 ret = is_subvol_ro(&send, subvol);
647 fprintf(stderr, "ERROR: %s is not read-only.\n",
652 if (new_end_cmd_semantic) {
653 /* require new kernel */
654 is_first_subvol = (i == optind);
655 is_last_subvol = (i == argc - 1);
657 /* be compatible to old and new kernel */
661 ret = do_send(&send, root_id, parent_root_id,
662 is_first_subvol, is_last_subvol);
666 /* done with this subvol, so add it to the clone sources */
667 add_clone_source(&send, root_id);
677 free(snapshot_parent);
678 free(send.clone_sources);
679 if (send.mnt_fd >= 0)
681 free(send.root_path);
682 subvol_uuid_search_finit(&send.sus);
686 static const char * const send_cmd_group_usage[] = {
687 "btrfs send <command> <args>",
691 const char * const cmd_send_usage[] = {
692 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] <subvol>",
693 "Send the subvolume to stdout.",
694 "Sends the subvolume specified by <subvol> to stdout.",
695 "By default, this will send the whole subvolume. To do an incremental",
696 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
697 "any additional local snapshots, use -c <clone-src> (multiple times",
698 "where applicable). You must not specify clone sources unless you",
699 "guarantee that these snapshots are exactly in the same state on both",
700 "sides, the sender and the receiver. It is allowed to omit the",
701 "'-p <parent>' option when '-c <clone-src>' options are given, in",
702 "which case 'btrfs send' will determine a suitable parent among the",
703 "clone sources itself.",
705 "-v Enable verbose debug output. Each occurrence of",
706 " this option increases the verbose level more.",
707 "-e If sending multiple subvols at once, use the new",
708 " format and omit the end-cmd between the subvols.",
709 "-p <parent> Send an incremental stream from <parent> to",
711 "-c <clone-src> Use this snapshot as a clone source for an ",
712 " incremental send (multiple allowed)",
713 "-f <outfile> Output is normally written to stdout. To write to",
714 " a file, use this option. An alternative would be to",
719 int cmd_send(int argc, char **argv)
721 return cmd_send_start(argc, argv);