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.
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
33 #include <uuid/uuid.h>
41 #include "send-utils.h"
43 static int g_verbose = 0;
51 u64 clone_sources_count;
54 struct subvol_uuid_search sus;
57 int find_mount_root(const char *path, char **mount_root)
63 int longest_matchlen = 0;
64 char *longest_match = NULL;
66 fd = open(path, O_RDONLY | O_NOATIME);
71 mnttab = fopen("/etc/mtab", "r");
72 while ((ent = getmntent(mnttab))) {
73 len = strlen(ent->mnt_dir);
74 if (strncmp(ent->mnt_dir, path, len) == 0) {
76 if (longest_matchlen < len) {
78 longest_matchlen = len;
79 longest_match = strdup(ent->mnt_dir);
84 *mount_root = realpath(longest_match, NULL);
90 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
92 struct subvol_info *si;
94 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
95 subvol_search_by_path);
98 *root_id = si->root_id;
102 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
104 struct subvol_info *si;
106 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
107 subvol_search_by_root_id);
111 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
112 subvol_search_by_uuid);
118 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
121 struct subvol_info *parent;
122 struct subvol_info *parent2;
123 struct subvol_info *best_parent = NULL;
125 u64 best_diff = (u64)-1;
128 parent = get_parent(s, root_id);
134 for (i = 0; i < s->clone_sources_count; i++) {
135 if (s->clone_sources[i] == parent->root_id) {
136 best_parent = parent;
141 for (i = 0; i < s->clone_sources_count; i++) {
142 parent2 = get_parent(s, s->clone_sources[i]);
143 if (parent2 != parent)
146 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
147 0, NULL, subvol_search_by_root_id);
149 tmp = parent2->ctransid - parent->ctransid;
152 if (tmp < best_diff) {
153 best_parent = parent;
164 *found = best_parent->root_id;
171 static void add_clone_source(struct btrfs_send *s, u64 root_id)
173 s->clone_sources = realloc(s->clone_sources,
174 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
175 s->clone_sources[s->clone_sources_count++] = root_id;
178 static int write_buf(int fd, const void *buf, int size)
184 ret = write(fd, (char*)buf + pos, size - pos);
187 fprintf(stderr, "ERROR: failed to dump stream. %s",
193 fprintf(stderr, "ERROR: failed to dump stream. %s",
205 static void *dump_thread(void *arg_)
208 struct btrfs_send *s = (struct btrfs_send*)arg_;
213 readed = read(s->send_fd, buf, sizeof(buf));
216 fprintf(stderr, "ERROR: failed to read stream from "
217 "kernel. %s\n", strerror(-ret));
224 ret = write_buf(s->dump_fd, buf, readed);
237 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
241 pthread_attr_t t_attr;
242 struct btrfs_ioctl_send_args io_send;
243 struct subvol_info *si;
248 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
249 subvol_search_by_root_id);
252 fprintf(stderr, "ERROR: could not find subvol info for %llu",
257 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
260 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
265 ret = pthread_attr_init(&t_attr);
270 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
274 io_send.send_fd = pipefd[1];
275 send->send_fd = pipefd[0];
278 ret = pthread_create(&t_read, &t_attr, dump_thread,
282 fprintf(stderr, "ERROR: thread setup failed: %s\n",
287 io_send.clone_sources = (__u64*)send->clone_sources;
288 io_send.clone_sources_count = send->clone_sources_count;
289 io_send.parent_root = parent_root;
290 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
293 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
298 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
301 fprintf(stderr, "joining genl thread\n");
306 ret = pthread_join(t_read, &t_err);
309 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
314 ret = (long int)t_err;
315 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
316 "(%s)\n", (long int)t_err, strerror(-ret));
320 pthread_attr_destroy(&t_attr);
334 static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
336 int len = strlen(s->root_path);
339 if (s->root_path[len - 1] != '/')
342 return full_path + len;
345 static int init_root_path(struct btrfs_send *s, const char *subvol)
352 ret = find_mount_root(subvol, &s->root_path);
355 fprintf(stderr, "ERROR: failed to determine mount point "
360 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
363 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
368 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
370 fprintf(stderr, "ERROR: failed to initialize subvol search. "
371 "%s\n", strerror(-ret));
380 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
386 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
389 fprintf(stderr, "ERROR: failed to open %s. %s\n",
390 subvol, strerror(-ret));
394 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
397 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
398 "%s\n", strerror(-ret));
402 if (flags & BTRFS_SUBVOL_RDONLY)
414 int cmd_send_start(int argc, char **argv)
419 char *outname = NULL;
420 struct btrfs_send send;
422 char *mount_root = NULL;
423 char *snapshot_parent = NULL;
425 u64 parent_root_id = 0;
427 memset(&send, 0, sizeof(send));
428 send.dump_fd = fileno(stdout);
430 while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
436 subvol = realpath(optarg, NULL);
439 fprintf(stderr, "ERROR: realpath %s failed. "
440 "%s\n", optarg, strerror(-ret));
444 ret = init_root_path(&send, subvol);
448 ret = get_root_id(&send, get_subvol_name(&send, subvol),
451 fprintf(stderr, "ERROR: could not resolve "
452 "root_id for %s\n", subvol);
455 add_clone_source(&send, root_id);
463 snapshot_parent = realpath(optarg, NULL);
464 if (!snapshot_parent) {
466 fprintf(stderr, "ERROR: realpath %s failed. "
467 "%s\n", optarg, strerror(-ret));
473 fprintf(stderr, "ERROR: send args invalid.\n");
478 if (optind == argc) {
479 fprintf(stderr, "ERROR: send needs path to snapshot\n");
483 if (outname != NULL) {
484 send.dump_fd = creat(outname, 0600);
485 if (send.dump_fd == -1) {
487 fprintf(stderr, "ERROR: can't create '%s': %s\n",
488 outname, strerror(-ret));
493 /* use first send subvol to determine mount_root */
494 subvol = argv[optind];
496 subvol = realpath(argv[optind], NULL);
499 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
503 ret = init_root_path(&send, subvol);
507 if (snapshot_parent != NULL) {
508 ret = get_root_id(&send,
509 get_subvol_name(&send, snapshot_parent),
512 fprintf(stderr, "ERROR: could not resolve root_id "
513 "for %s\n", snapshot_parent);
517 add_clone_source(&send, parent_root_id);
520 for (i = optind; i < argc; i++) {
521 subvol = realpath(argv[i], NULL);
524 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
528 ret = find_mount_root(subvol, &mount_root);
530 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
535 if (strcmp(send.root_path, mount_root) != 0) {
537 fprintf(stderr, "ERROR: all subvols must be from the "
543 ret = is_subvol_ro(&send, subvol);
548 fprintf(stderr, "ERROR: %s is not read-only.\n",
555 for (i = optind; i < argc; i++) {
558 fprintf(stderr, "At subvol %s\n", subvol);
560 subvol = realpath(subvol, NULL);
563 fprintf(stderr, "ERROR: realpath %s failed. "
564 "%s\n", argv[i], strerror(-ret));
568 ret = get_root_id(&send, get_subvol_name(&send, subvol),
571 fprintf(stderr, "ERROR: could not resolve root_id "
576 if (!parent_root_id) {
577 ret = find_good_parent(&send, root_id, &parent_root_id);
582 ret = is_subvol_ro(&send, subvol);
587 fprintf(stderr, "ERROR: %s is not read-only.\n",
592 ret = do_send(&send, root_id, parent_root_id);
596 /* done with this subvol, so add it to the clone sources */
597 add_clone_source(&send, root_id);
606 if (send.mnt_fd >= 0)
611 static const char * const send_cmd_group_usage[] = {
612 "btrfs send <command> <args>",
616 static const char * const cmd_send_usage[] = {
617 "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
618 "Send the subvolume to stdout.",
619 "Sends the subvolume specified by <subvol> to stdout.",
620 "By default, this will send the whole subvolume. To do",
621 "an incremental send, one or multiple '-i <clone_source>'",
622 "arguments have to be specified. A 'clone source' is",
623 "a subvolume that is known to exist on the receiving",
624 "side in exactly the same state as on the sending side.\n",
625 "Normally, a good snapshot parent is searched automatically",
626 "in the list of 'clone sources'. To override this, use",
627 "'-p <parent>' to manually specify a snapshot parent.",
628 "A manually specified snapshot parent is also regarded",
629 "as 'clone source'.\n",
630 "-v Enable verbose debug output. Each",
631 " occurrency of this option increases the",
632 " verbose level more.",
633 "-i <subvol> Informs btrfs send that this subvolume,",
634 " can be taken as 'clone source'. This can",
635 " be used for incremental sends.",
636 "-p <subvol> Disable automatic snaphot parent",
637 " determination and use <subvol> as parent.",
638 " This subvolume is also added to the list",
639 " of 'clone sources' (see -i).",
640 "-f <outfile> Output is normally written to stdout.",
641 " To write to a file, use this option.",
642 " An alternative would be to use pipes.",
646 const struct cmd_group send_cmd_group = {
647 send_cmd_group_usage, NULL, {
648 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
653 int cmd_send(int argc, char **argv)
655 return cmd_send_start(argc, argv);