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 = parent;
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)
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 io_send.send_fd = pipefd[1];
277 send->send_fd = pipefd[0];
280 ret = pthread_create(&t_read, &t_attr, dump_thread,
284 fprintf(stderr, "ERROR: thread setup failed: %s\n",
289 io_send.clone_sources = (__u64*)send->clone_sources;
290 io_send.clone_sources_count = send->clone_sources_count;
291 io_send.parent_root = parent_root;
292 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
295 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
300 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
303 fprintf(stderr, "joining genl thread\n");
308 ret = pthread_join(t_read, &t_err);
311 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
316 ret = (long int)t_err;
317 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
318 "(%s)\n", (long int)t_err, strerror(-ret));
322 pthread_attr_destroy(&t_attr);
336 static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
338 int len = strlen(s->root_path);
341 if (s->root_path[len - 1] != '/')
344 return full_path + len;
347 static int init_root_path(struct btrfs_send *s, const char *subvol)
354 ret = find_mount_root(subvol, &s->root_path);
357 fprintf(stderr, "ERROR: failed to determine mount point "
362 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
365 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
370 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
372 fprintf(stderr, "ERROR: failed to initialize subvol search. "
373 "%s\n", strerror(-ret));
382 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
388 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
391 fprintf(stderr, "ERROR: failed to open %s. %s\n",
392 subvol, strerror(-ret));
396 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
399 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
400 "%s\n", strerror(-ret));
404 if (flags & BTRFS_SUBVOL_RDONLY)
416 int cmd_send_start(int argc, char **argv)
421 char *outname = NULL;
422 struct btrfs_send send;
424 char *mount_root = NULL;
425 char *snapshot_parent = NULL;
427 u64 parent_root_id = 0;
429 memset(&send, 0, sizeof(send));
430 send.dump_fd = fileno(stdout);
432 if (isatty(send.dump_fd)) {
433 fprintf(stderr, "ERROR: not dumping send stream into a terminal, redirect it into a file\n");
437 while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
443 subvol = realpath(optarg, NULL);
446 fprintf(stderr, "ERROR: realpath %s failed. "
447 "%s\n", optarg, strerror(-ret));
451 ret = init_root_path(&send, subvol);
455 ret = get_root_id(&send, get_subvol_name(&send, subvol),
458 fprintf(stderr, "ERROR: could not resolve "
459 "root_id for %s\n", subvol);
462 add_clone_source(&send, root_id);
470 snapshot_parent = realpath(optarg, NULL);
471 if (!snapshot_parent) {
473 fprintf(stderr, "ERROR: realpath %s failed. "
474 "%s\n", optarg, strerror(-ret));
480 fprintf(stderr, "ERROR: send args invalid.\n");
485 if (optind == argc) {
486 fprintf(stderr, "ERROR: send needs path to snapshot\n");
490 if (outname != NULL) {
491 send.dump_fd = creat(outname, 0600);
492 if (send.dump_fd == -1) {
494 fprintf(stderr, "ERROR: can't create '%s': %s\n",
495 outname, strerror(-ret));
500 /* use first send subvol to determine mount_root */
501 subvol = argv[optind];
503 subvol = realpath(argv[optind], NULL);
506 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
510 ret = init_root_path(&send, subvol);
514 if (snapshot_parent != NULL) {
515 ret = get_root_id(&send,
516 get_subvol_name(&send, snapshot_parent),
519 fprintf(stderr, "ERROR: could not resolve root_id "
520 "for %s\n", snapshot_parent);
524 add_clone_source(&send, parent_root_id);
527 for (i = optind; i < argc; i++) {
528 subvol = realpath(argv[i], NULL);
531 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
535 ret = find_mount_root(subvol, &mount_root);
537 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
542 if (strcmp(send.root_path, mount_root) != 0) {
544 fprintf(stderr, "ERROR: all subvols must be from the "
550 ret = is_subvol_ro(&send, subvol);
555 fprintf(stderr, "ERROR: %s is not read-only.\n",
562 for (i = optind; i < argc; i++) {
565 fprintf(stderr, "At subvol %s\n", subvol);
567 subvol = realpath(subvol, NULL);
570 fprintf(stderr, "ERROR: realpath %s failed. "
571 "%s\n", argv[i], strerror(-ret));
575 ret = get_root_id(&send, get_subvol_name(&send, subvol),
578 fprintf(stderr, "ERROR: could not resolve root_id "
583 if (!parent_root_id) {
584 ret = find_good_parent(&send, root_id, &parent_root_id);
589 ret = is_subvol_ro(&send, subvol);
594 fprintf(stderr, "ERROR: %s is not read-only.\n",
599 ret = do_send(&send, root_id, parent_root_id);
603 /* done with this subvol, so add it to the clone sources */
604 add_clone_source(&send, root_id);
613 if (send.mnt_fd >= 0)
618 static const char * const send_cmd_group_usage[] = {
619 "btrfs send <command> <args>",
623 static const char * const cmd_send_usage[] = {
624 "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
625 "Send the subvolume to stdout.",
626 "Sends the subvolume specified by <subvol> to stdout.",
627 "By default, this will send the whole subvolume. To do",
628 "an incremental send, one or multiple '-i <clone_source>'",
629 "arguments have to be specified. A 'clone source' is",
630 "a subvolume that is known to exist on the receiving",
631 "side in exactly the same state as on the sending side.\n",
632 "Normally, a good snapshot parent is searched automatically",
633 "in the list of 'clone sources'. To override this, use",
634 "'-p <parent>' to manually specify a snapshot parent.",
635 "A manually specified snapshot parent is also regarded",
636 "as 'clone source'.\n",
637 "-v Enable verbose debug output. Each",
638 " occurrency of this option increases the",
639 " verbose level more.",
640 "-i <subvol> Informs btrfs send that this subvolume,",
641 " can be taken as 'clone source'. This can",
642 " be used for incremental sends.",
643 "-p <subvol> Disable automatic snaphot parent",
644 " determination and use <subvol> as parent.",
645 " This subvolume is also added to the list",
646 " of 'clone sources' (see -i).",
647 "-f <outfile> Output is normally written to stdout.",
648 " To write to a file, use this option.",
649 " An alternative would be to use pipes.",
653 const struct cmd_group send_cmd_group = {
654 send_cmd_group_usage, NULL, {
655 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
660 int cmd_send(int argc, char **argv)
662 return cmd_send_start(argc, argv);