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);
87 *mount_root = realpath(longest_match, NULL);
93 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
95 struct subvol_info *si;
97 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
98 subvol_search_by_path);
101 *root_id = si->root_id;
105 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
107 struct subvol_info *si;
109 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
110 subvol_search_by_root_id);
114 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
115 subvol_search_by_uuid);
121 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
124 struct subvol_info *parent;
125 struct subvol_info *parent2;
126 struct subvol_info *best_parent = NULL;
128 u64 best_diff = (u64)-1;
131 parent = get_parent(s, root_id);
137 for (i = 0; i < s->clone_sources_count; i++) {
138 if (s->clone_sources[i] == parent->root_id) {
139 best_parent = parent;
144 for (i = 0; i < s->clone_sources_count; i++) {
145 parent2 = get_parent(s, s->clone_sources[i]);
146 if (parent2 != parent)
149 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
150 0, NULL, subvol_search_by_root_id);
152 tmp = parent2->ctransid - parent->ctransid;
155 if (tmp < best_diff) {
156 best_parent = parent2;
167 *found = best_parent->root_id;
174 static void add_clone_source(struct btrfs_send *s, u64 root_id)
176 s->clone_sources = realloc(s->clone_sources,
177 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
178 s->clone_sources[s->clone_sources_count++] = root_id;
181 static int write_buf(int fd, const void *buf, int size)
187 ret = write(fd, (char*)buf + pos, size - pos);
190 fprintf(stderr, "ERROR: failed to dump stream. %s",
196 fprintf(stderr, "ERROR: failed to dump stream. %s",
208 static void *dump_thread(void *arg_)
211 struct btrfs_send *s = (struct btrfs_send*)arg_;
216 readed = read(s->send_fd, buf, sizeof(buf));
219 fprintf(stderr, "ERROR: failed to read stream from "
220 "kernel. %s\n", strerror(-ret));
227 ret = write_buf(s->dump_fd, buf, readed);
240 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
244 pthread_attr_t t_attr;
245 struct btrfs_ioctl_send_args io_send;
246 struct subvol_info *si;
249 int pipefd[2] = {-1, -1};
251 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
252 subvol_search_by_root_id);
255 fprintf(stderr, "ERROR: could not find subvol info for %llu",
260 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
263 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
268 ret = pthread_attr_init(&t_attr);
273 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
277 memset(&io_send, 0, sizeof(io_send));
278 io_send.send_fd = pipefd[1];
279 send->send_fd = pipefd[0];
282 ret = pthread_create(&t_read, &t_attr, dump_thread,
286 fprintf(stderr, "ERROR: thread setup failed: %s\n",
291 io_send.clone_sources = (__u64*)send->clone_sources;
292 io_send.clone_sources_count = send->clone_sources_count;
293 io_send.parent_root = parent_root_id;
294 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
297 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
302 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
305 fprintf(stderr, "joining genl thread\n");
310 ret = pthread_join(t_read, &t_err);
313 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
318 ret = (long int)t_err;
319 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
320 "(%s)\n", (long int)t_err, strerror(-ret));
324 pthread_attr_destroy(&t_attr);
338 char *get_subvol_name(char *mnt, char *full_path)
340 int len = strlen(mnt);
343 if (mnt[len - 1] != '/')
346 return full_path + len;
349 static int init_root_path(struct btrfs_send *s, const char *subvol)
356 ret = find_mount_root(subvol, &s->root_path);
359 fprintf(stderr, "ERROR: failed to determine mount point "
364 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
367 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
372 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
374 fprintf(stderr, "ERROR: failed to initialize subvol search. "
375 "%s\n", strerror(-ret));
384 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
390 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
393 fprintf(stderr, "ERROR: failed to open %s. %s\n",
394 subvol, strerror(-ret));
398 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
401 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
402 "%s\n", strerror(-ret));
406 if (flags & BTRFS_SUBVOL_RDONLY)
418 int cmd_send_start(int argc, char **argv)
423 char *outname = NULL;
424 struct btrfs_send send;
426 char *mount_root = NULL;
427 char *snapshot_parent = NULL;
429 u64 parent_root_id = 0;
432 memset(&send, 0, sizeof(send));
433 send.dump_fd = fileno(stdout);
435 while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
441 subvol = realpath(optarg, NULL);
444 fprintf(stderr, "ERROR: realpath %s failed. "
445 "%s\n", optarg, strerror(-ret));
449 ret = init_root_path(&send, subvol);
453 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
456 fprintf(stderr, "ERROR: could not resolve "
457 "root_id for %s\n", subvol);
460 add_clone_source(&send, root_id);
461 subvol_uuid_search_finit(&send.sus);
469 if (snapshot_parent) {
470 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
473 snapshot_parent = realpath(optarg, NULL);
474 if (!snapshot_parent) {
476 fprintf(stderr, "ERROR: realpath %s failed. "
477 "%s\n", optarg, strerror(-ret));
484 "ERROR: -i was removed, use -c instead\n");
488 fprintf(stderr, "ERROR: send args invalid.\n");
493 if (optind == argc) {
494 fprintf(stderr, "ERROR: send needs path to snapshot\n");
498 if (outname != NULL) {
499 send.dump_fd = creat(outname, 0600);
500 if (send.dump_fd == -1) {
502 fprintf(stderr, "ERROR: can't create '%s': %s\n",
503 outname, strerror(-ret));
508 if (isatty(send.dump_fd)) {
510 "ERROR: not dumping send stream into a terminal, "
511 "redirect it into a file\n");
515 /* use first send subvol to determine mount_root */
516 subvol = argv[optind];
518 subvol = realpath(argv[optind], NULL);
521 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
525 ret = init_root_path(&send, subvol);
529 if (snapshot_parent != NULL) {
530 ret = get_root_id(&send,
531 get_subvol_name(send.root_path, snapshot_parent),
534 fprintf(stderr, "ERROR: could not resolve root_id "
535 "for %s\n", snapshot_parent);
539 add_clone_source(&send, parent_root_id);
542 for (i = optind; i < argc; i++) {
543 subvol = realpath(argv[i], NULL);
546 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
550 ret = find_mount_root(subvol, &mount_root);
552 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
557 if (strcmp(send.root_path, mount_root) != 0) {
559 fprintf(stderr, "ERROR: all subvols must be from the "
565 ret = is_subvol_ro(&send, subvol);
570 fprintf(stderr, "ERROR: %s is not read-only.\n",
577 for (i = optind; i < argc; i++) {
580 fprintf(stderr, "At subvol %s\n", subvol);
582 subvol = realpath(subvol, NULL);
585 fprintf(stderr, "ERROR: realpath %s failed. "
586 "%s\n", argv[i], strerror(-ret));
590 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
593 fprintf(stderr, "ERROR: could not resolve root_id "
598 if (!full_send && !parent_root_id) {
599 ret = find_good_parent(&send, root_id, &parent_root_id);
601 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
607 ret = is_subvol_ro(&send, subvol);
612 fprintf(stderr, "ERROR: %s is not read-only.\n",
617 ret = do_send(&send, root_id, parent_root_id);
621 /* done with this subvol, so add it to the clone sources */
622 add_clone_source(&send, root_id);
632 if (send.mnt_fd >= 0)
634 subvol_uuid_search_finit(&send.sus);
638 static const char * const send_cmd_group_usage[] = {
639 "btrfs send <command> <args>",
643 const char * const cmd_send_usage[] = {
644 "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
645 "Send the subvolume to stdout.",
646 "Sends the subvolume specified by <subvol> to stdout.",
647 "By default, this will send the whole subvolume. To do an incremental",
648 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
649 "any additional local snapshots, use -c <clone-src> (multiple times",
650 "where applicable). You must not specify clone sources unless you",
651 "guarantee that these snapshots are exactly in the same state on both",
652 "sides, the sender and the receiver. It is allowed to omit the",
653 "'-p <parent>' option when '-c <clone-src>' options are given, in",
654 "which case 'btrfs send' will determine a suitable parent among the",
655 "clone sources itself.",
657 "-v Enable verbose debug output. Each occurrence of",
658 " this option increases the verbose level more.",
659 "-p <parent> Send an incremental stream from <parent> to",
661 "-c <clone-src> Use this snapshot as a clone source for an ",
662 " incremental send (multiple allowed)",
663 "-f <outfile> Output is normally written to stdout. To write to",
664 " a file, use this option. An alternative would be to",
669 int cmd_send(int argc, char **argv)
671 return cmd_send_start(argc, argv);