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 while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
438 subvol = realpath(optarg, NULL);
441 fprintf(stderr, "ERROR: realpath %s failed. "
442 "%s\n", optarg, strerror(-ret));
446 ret = init_root_path(&send, subvol);
450 ret = get_root_id(&send, get_subvol_name(&send, subvol),
453 fprintf(stderr, "ERROR: could not resolve "
454 "root_id for %s\n", subvol);
457 add_clone_source(&send, root_id);
465 snapshot_parent = realpath(optarg, NULL);
466 if (!snapshot_parent) {
468 fprintf(stderr, "ERROR: realpath %s failed. "
469 "%s\n", optarg, strerror(-ret));
475 fprintf(stderr, "ERROR: send args invalid.\n");
480 if (optind == argc) {
481 fprintf(stderr, "ERROR: send needs path to snapshot\n");
485 if (outname != NULL) {
486 send.dump_fd = creat(outname, 0600);
487 if (send.dump_fd == -1) {
489 fprintf(stderr, "ERROR: can't create '%s': %s\n",
490 outname, strerror(-ret));
495 /* use first send subvol to determine mount_root */
496 subvol = argv[optind];
498 subvol = realpath(argv[optind], NULL);
501 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
505 ret = init_root_path(&send, subvol);
509 if (snapshot_parent != NULL) {
510 ret = get_root_id(&send,
511 get_subvol_name(&send, snapshot_parent),
514 fprintf(stderr, "ERROR: could not resolve root_id "
515 "for %s\n", snapshot_parent);
519 add_clone_source(&send, parent_root_id);
522 for (i = optind; i < argc; i++) {
523 subvol = realpath(argv[i], NULL);
526 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
530 ret = find_mount_root(subvol, &mount_root);
532 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
537 if (strcmp(send.root_path, mount_root) != 0) {
539 fprintf(stderr, "ERROR: all subvols must be from the "
545 ret = is_subvol_ro(&send, subvol);
550 fprintf(stderr, "ERROR: %s is not read-only.\n",
557 for (i = optind; i < argc; i++) {
560 fprintf(stderr, "At subvol %s\n", subvol);
562 subvol = realpath(subvol, NULL);
565 fprintf(stderr, "ERROR: realpath %s failed. "
566 "%s\n", argv[i], strerror(-ret));
570 ret = get_root_id(&send, get_subvol_name(&send, subvol),
573 fprintf(stderr, "ERROR: could not resolve root_id "
578 if (!parent_root_id) {
579 ret = find_good_parent(&send, root_id, &parent_root_id);
584 ret = is_subvol_ro(&send, subvol);
589 fprintf(stderr, "ERROR: %s is not read-only.\n",
594 ret = do_send(&send, root_id, parent_root_id);
598 /* done with this subvol, so add it to the clone sources */
599 add_clone_source(&send, root_id);
608 if (send.mnt_fd >= 0)
613 static const char * const send_cmd_group_usage[] = {
614 "btrfs send <command> <args>",
618 static const char * const cmd_send_usage[] = {
619 "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
620 "Send the subvolume to stdout.",
621 "Sends the subvolume specified by <subvol> to stdout.",
622 "By default, this will send the whole subvolume. To do",
623 "an incremental send, one or multiple '-i <clone_source>'",
624 "arguments have to be specified. A 'clone source' is",
625 "a subvolume that is known to exist on the receiving",
626 "side in exactly the same state as on the sending side.\n",
627 "Normally, a good snapshot parent is searched automatically",
628 "in the list of 'clone sources'. To override this, use",
629 "'-p <parent>' to manually specify a snapshot parent.",
630 "A manually specified snapshot parent is also regarded",
631 "as 'clone source'.\n",
632 "-v Enable verbose debug output. Each",
633 " occurrency of this option increases the",
634 " verbose level more.",
635 "-i <subvol> Informs btrfs send that this subvolume,",
636 " can be taken as 'clone source'. This can",
637 " be used for incremental sends.",
638 "-p <subvol> Disable automatic snaphot parent",
639 " determination and use <subvol> as parent.",
640 " This subvolume is also added to the list",
641 " of 'clone sources' (see -i).",
642 "-f <outfile> Output is normally written to stdout.",
643 " To write to a file, use this option.",
644 " An alternative would be to use pipes.",
648 const struct cmd_group send_cmd_group = {
649 send_cmd_group_usage, NULL, {
650 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
655 int cmd_send(int argc, char **argv)
657 return cmd_send_start(argc, argv);