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("/etc/mtab", "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);
86 *mount_root = realpath(longest_match, NULL);
92 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
94 struct subvol_info *si;
96 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
97 subvol_search_by_path);
100 *root_id = si->root_id;
104 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
106 struct subvol_info *si;
108 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
109 subvol_search_by_root_id);
113 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
114 subvol_search_by_uuid);
120 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
123 struct subvol_info *parent;
124 struct subvol_info *parent2;
125 struct subvol_info *best_parent = NULL;
127 u64 best_diff = (u64)-1;
130 parent = get_parent(s, root_id);
136 for (i = 0; i < s->clone_sources_count; i++) {
137 if (s->clone_sources[i] == parent->root_id) {
138 best_parent = parent;
143 for (i = 0; i < s->clone_sources_count; i++) {
144 parent2 = get_parent(s, s->clone_sources[i]);
145 if (parent2 != parent)
148 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
149 0, NULL, subvol_search_by_root_id);
151 tmp = parent2->ctransid - parent->ctransid;
154 if (tmp < best_diff) {
155 best_parent = parent2;
166 *found = best_parent->root_id;
173 static void add_clone_source(struct btrfs_send *s, u64 root_id)
175 s->clone_sources = realloc(s->clone_sources,
176 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
177 s->clone_sources[s->clone_sources_count++] = root_id;
180 static int write_buf(int fd, const void *buf, int size)
186 ret = write(fd, (char*)buf + pos, size - pos);
189 fprintf(stderr, "ERROR: failed to dump stream. %s",
195 fprintf(stderr, "ERROR: failed to dump stream. %s",
207 static void *dump_thread(void *arg_)
210 struct btrfs_send *s = (struct btrfs_send*)arg_;
215 readed = read(s->send_fd, buf, sizeof(buf));
218 fprintf(stderr, "ERROR: failed to read stream from "
219 "kernel. %s\n", strerror(-ret));
226 ret = write_buf(s->dump_fd, buf, readed);
239 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
243 pthread_attr_t t_attr;
244 struct btrfs_ioctl_send_args io_send;
245 struct subvol_info *si;
250 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
251 subvol_search_by_root_id);
254 fprintf(stderr, "ERROR: could not find subvol info for %llu",
259 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
262 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
267 ret = pthread_attr_init(&t_attr);
272 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
276 memset(&io_send, 0, sizeof(io_send));
277 io_send.send_fd = pipefd[1];
278 send->send_fd = pipefd[0];
281 ret = pthread_create(&t_read, &t_attr, dump_thread,
285 fprintf(stderr, "ERROR: thread setup failed: %s\n",
290 io_send.clone_sources = (__u64*)send->clone_sources;
291 io_send.clone_sources_count = send->clone_sources_count;
292 io_send.parent_root = parent_root_id;
293 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
296 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
301 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
304 fprintf(stderr, "joining genl thread\n");
309 ret = pthread_join(t_read, &t_err);
312 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
317 ret = (long int)t_err;
318 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
319 "(%s)\n", (long int)t_err, strerror(-ret));
323 pthread_attr_destroy(&t_attr);
337 static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
339 int len = strlen(s->root_path);
342 if (s->root_path[len - 1] != '/')
345 return full_path + len;
348 static int init_root_path(struct btrfs_send *s, const char *subvol)
355 ret = find_mount_root(subvol, &s->root_path);
358 fprintf(stderr, "ERROR: failed to determine mount point "
363 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
366 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
371 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
373 fprintf(stderr, "ERROR: failed to initialize subvol search. "
374 "%s\n", strerror(-ret));
383 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
389 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
392 fprintf(stderr, "ERROR: failed to open %s. %s\n",
393 subvol, strerror(-ret));
397 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
400 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
401 "%s\n", strerror(-ret));
405 if (flags & BTRFS_SUBVOL_RDONLY)
417 int cmd_send_start(int argc, char **argv)
422 char *outname = NULL;
423 struct btrfs_send send;
425 char *mount_root = NULL;
426 char *snapshot_parent = NULL;
428 u64 parent_root_id = 0;
431 memset(&send, 0, sizeof(send));
432 send.dump_fd = fileno(stdout);
434 if (isatty(send.dump_fd)) {
435 fprintf(stderr, "ERROR: not dumping send stream into a terminal, redirect it into a file\n");
439 while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
445 subvol = realpath(optarg, NULL);
448 fprintf(stderr, "ERROR: realpath %s failed. "
449 "%s\n", optarg, strerror(-ret));
453 ret = init_root_path(&send, subvol);
457 ret = get_root_id(&send, get_subvol_name(&send, subvol),
460 fprintf(stderr, "ERROR: could not resolve "
461 "root_id for %s\n", subvol);
464 add_clone_source(&send, root_id);
472 if (snapshot_parent) {
473 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
476 snapshot_parent = realpath(optarg, NULL);
477 if (!snapshot_parent) {
479 fprintf(stderr, "ERROR: realpath %s failed. "
480 "%s\n", optarg, strerror(-ret));
487 "ERROR: -i was removed, use -c instead\n");
491 fprintf(stderr, "ERROR: send args invalid.\n");
496 if (optind == argc) {
497 fprintf(stderr, "ERROR: send needs path to snapshot\n");
501 if (outname != NULL) {
502 send.dump_fd = creat(outname, 0600);
503 if (send.dump_fd == -1) {
505 fprintf(stderr, "ERROR: can't create '%s': %s\n",
506 outname, strerror(-ret));
511 /* use first send subvol to determine mount_root */
512 subvol = argv[optind];
514 subvol = realpath(argv[optind], NULL);
517 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
521 ret = init_root_path(&send, subvol);
525 if (snapshot_parent != NULL) {
526 ret = get_root_id(&send,
527 get_subvol_name(&send, snapshot_parent),
530 fprintf(stderr, "ERROR: could not resolve root_id "
531 "for %s\n", snapshot_parent);
535 add_clone_source(&send, parent_root_id);
538 for (i = optind; i < argc; i++) {
539 subvol = realpath(argv[i], NULL);
542 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
546 ret = find_mount_root(subvol, &mount_root);
548 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
553 if (strcmp(send.root_path, mount_root) != 0) {
555 fprintf(stderr, "ERROR: all subvols must be from the "
561 ret = is_subvol_ro(&send, subvol);
566 fprintf(stderr, "ERROR: %s is not read-only.\n",
573 for (i = optind; i < argc; i++) {
576 fprintf(stderr, "At subvol %s\n", subvol);
578 subvol = realpath(subvol, NULL);
581 fprintf(stderr, "ERROR: realpath %s failed. "
582 "%s\n", argv[i], strerror(-ret));
586 ret = get_root_id(&send, get_subvol_name(&send, subvol),
589 fprintf(stderr, "ERROR: could not resolve root_id "
594 if (!full_send && !parent_root_id) {
595 ret = find_good_parent(&send, root_id, &parent_root_id);
597 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
603 ret = is_subvol_ro(&send, subvol);
608 fprintf(stderr, "ERROR: %s is not read-only.\n",
613 ret = do_send(&send, root_id, parent_root_id);
617 /* done with this subvol, so add it to the clone sources */
618 add_clone_source(&send, root_id);
628 if (send.mnt_fd >= 0)
633 static const char * const send_cmd_group_usage[] = {
634 "btrfs send <command> <args>",
638 static const char * const cmd_send_usage[] = {
639 "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
640 "Send the subvolume to stdout.",
641 "Sends the subvolume specified by <subvol> to stdout.",
642 "By default, this will send the whole subvolume. To do an incremental",
643 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
644 "any additional local snapshots, use -c <clone-src> (multiple times",
645 "where applicable). You must not specify clone sources unless you",
646 "guarantee that these snapshots are exactly in the same state on both",
647 "sides, the sender and the receiver. It is allowed to omit the",
648 "'-p <parent>' option when '-c <clone-src>' options are given, in",
649 "which case 'btrfs send' will determine a suitable parent among the",
650 "clone sources itself.",
652 "-v Enable verbose debug output. Each occurrency of",
653 " this option increases the verbose level more.",
654 "-p <parent> Send an incremental stream from <parent> to",
656 "-c <clone-src> Use this snapshot as a clone source for an ",
657 " incremental send (multiple allowed)",
658 "-f <outfile> Output is normally written to stdout. To write to",
659 " a file, use this option. An alternative would be to",
664 const struct cmd_group send_cmd_group = {
665 send_cmd_group_usage, NULL, {
666 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
671 int cmd_send(int argc, char **argv)
673 return cmd_send_start(argc, argv);