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)
251 pthread_attr_t t_attr;
252 struct btrfs_ioctl_send_args io_send;
253 struct subvol_info *si;
256 int pipefd[2] = {-1, -1};
258 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
259 subvol_search_by_root_id);
262 fprintf(stderr, "ERROR: could not find subvol info for %llu",
267 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
270 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
275 ret = pthread_attr_init(&t_attr);
280 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
284 memset(&io_send, 0, sizeof(io_send));
285 io_send.send_fd = pipefd[1];
286 send->send_fd = pipefd[0];
289 ret = pthread_create(&t_read, &t_attr, dump_thread,
293 fprintf(stderr, "ERROR: thread setup failed: %s\n",
298 io_send.clone_sources = (__u64*)send->clone_sources;
299 io_send.clone_sources_count = send->clone_sources_count;
300 io_send.parent_root = parent_root_id;
301 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
304 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
309 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
312 fprintf(stderr, "joining genl thread\n");
317 ret = pthread_join(t_read, &t_err);
320 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
325 ret = (long int)t_err;
326 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
327 "(%s)\n", (long int)t_err, strerror(-ret));
331 pthread_attr_destroy(&t_attr);
345 char *get_subvol_name(char *mnt, char *full_path)
347 int len = strlen(mnt);
350 if (mnt[len - 1] != '/')
353 return full_path + len;
356 static int init_root_path(struct btrfs_send *s, const char *subvol)
363 ret = find_mount_root(subvol, &s->root_path);
366 fprintf(stderr, "ERROR: failed to determine mount point "
371 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
374 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
379 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
381 fprintf(stderr, "ERROR: failed to initialize subvol search. "
382 "%s\n", strerror(-ret));
391 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
397 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
400 fprintf(stderr, "ERROR: failed to open %s. %s\n",
401 subvol, strerror(-ret));
405 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
408 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
409 "%s\n", strerror(-ret));
413 if (flags & BTRFS_SUBVOL_RDONLY)
425 int cmd_send_start(int argc, char **argv)
430 char *outname = NULL;
431 struct btrfs_send send;
433 char *mount_root = NULL;
434 char *snapshot_parent = NULL;
436 u64 parent_root_id = 0;
439 memset(&send, 0, sizeof(send));
440 send.dump_fd = fileno(stdout);
442 while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
448 subvol = realpath(optarg, NULL);
451 fprintf(stderr, "ERROR: realpath %s failed. "
452 "%s\n", optarg, strerror(-ret));
456 ret = init_root_path(&send, subvol);
460 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
463 fprintf(stderr, "ERROR: could not resolve "
464 "root_id for %s\n", subvol);
467 add_clone_source(&send, root_id);
468 subvol_uuid_search_finit(&send.sus);
471 if (send.mnt_fd >= 0) {
475 free(send.root_path);
476 send.root_path = NULL;
483 if (snapshot_parent) {
484 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
488 snapshot_parent = realpath(optarg, NULL);
489 if (!snapshot_parent) {
491 fprintf(stderr, "ERROR: realpath %s failed. "
492 "%s\n", optarg, strerror(-ret));
499 "ERROR: -i was removed, use -c instead\n");
504 fprintf(stderr, "ERROR: send args invalid.\n");
510 if (optind == argc) {
511 fprintf(stderr, "ERROR: send needs path to snapshot\n");
516 if (outname != NULL) {
517 send.dump_fd = creat(outname, 0600);
518 if (send.dump_fd == -1) {
520 fprintf(stderr, "ERROR: can't create '%s': %s\n",
521 outname, strerror(-ret));
526 if (isatty(send.dump_fd)) {
528 "ERROR: not dumping send stream into a terminal, "
529 "redirect it into a file\n");
534 /* use first send subvol to determine mount_root */
535 subvol = argv[optind];
537 subvol = realpath(argv[optind], NULL);
540 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
544 ret = init_root_path(&send, subvol);
548 if (snapshot_parent != NULL) {
549 ret = get_root_id(&send,
550 get_subvol_name(send.root_path, snapshot_parent),
553 fprintf(stderr, "ERROR: could not resolve root_id "
554 "for %s\n", snapshot_parent);
558 add_clone_source(&send, parent_root_id);
561 for (i = optind; i < argc; i++) {
563 subvol = realpath(argv[i], NULL);
566 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
570 ret = find_mount_root(subvol, &mount_root);
572 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
577 if (strcmp(send.root_path, mount_root) != 0) {
579 fprintf(stderr, "ERROR: all subvols must be from the "
585 ret = is_subvol_ro(&send, subvol);
590 fprintf(stderr, "ERROR: %s is not read-only.\n",
596 for (i = optind; i < argc; i++) {
600 fprintf(stderr, "At subvol %s\n", subvol);
602 subvol = realpath(subvol, NULL);
605 fprintf(stderr, "ERROR: realpath %s failed. "
606 "%s\n", argv[i], strerror(-ret));
610 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
613 fprintf(stderr, "ERROR: could not resolve root_id "
618 if (!full_send && !parent_root_id) {
619 ret = find_good_parent(&send, root_id, &parent_root_id);
621 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
627 ret = is_subvol_ro(&send, subvol);
632 fprintf(stderr, "ERROR: %s is not read-only.\n",
637 ret = do_send(&send, root_id, parent_root_id);
641 /* done with this subvol, so add it to the clone sources */
642 add_clone_source(&send, root_id);
652 free(snapshot_parent);
653 free(send.clone_sources);
654 if (send.mnt_fd >= 0)
656 free(send.root_path);
657 subvol_uuid_search_finit(&send.sus);
661 static const char * const send_cmd_group_usage[] = {
662 "btrfs send <command> <args>",
666 const char * const cmd_send_usage[] = {
667 "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
668 "Send the subvolume to stdout.",
669 "Sends the subvolume specified by <subvol> to stdout.",
670 "By default, this will send the whole subvolume. To do an incremental",
671 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
672 "any additional local snapshots, use -c <clone-src> (multiple times",
673 "where applicable). You must not specify clone sources unless you",
674 "guarantee that these snapshots are exactly in the same state on both",
675 "sides, the sender and the receiver. It is allowed to omit the",
676 "'-p <parent>' option when '-c <clone-src>' options are given, in",
677 "which case 'btrfs send' will determine a suitable parent among the",
678 "clone sources itself.",
680 "-v Enable verbose debug output. Each occurrence of",
681 " this option increases the verbose level more.",
682 "-p <parent> Send an incremental stream from <parent> to",
684 "-c <clone-src> Use this snapshot as a clone source for an ",
685 " incremental send (multiple allowed)",
686 "-f <outfile> Output is normally written to stdout. To write to",
687 " a file, use this option. An alternative would be to",
692 int cmd_send(int argc, char **argv)
694 return cmd_send_start(argc, argv);