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);
468 if (snapshot_parent) {
469 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
472 snapshot_parent = realpath(optarg, NULL);
473 if (!snapshot_parent) {
475 fprintf(stderr, "ERROR: realpath %s failed. "
476 "%s\n", optarg, strerror(-ret));
483 "ERROR: -i was removed, use -c instead\n");
487 fprintf(stderr, "ERROR: send args invalid.\n");
492 if (optind == argc) {
493 fprintf(stderr, "ERROR: send needs path to snapshot\n");
497 if (outname != NULL) {
498 send.dump_fd = creat(outname, 0600);
499 if (send.dump_fd == -1) {
501 fprintf(stderr, "ERROR: can't create '%s': %s\n",
502 outname, strerror(-ret));
507 if (isatty(send.dump_fd)) {
509 "ERROR: not dumping send stream into a terminal, "
510 "redirect it into a file\n");
514 /* use first send subvol to determine mount_root */
515 subvol = argv[optind];
517 subvol = realpath(argv[optind], NULL);
520 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
524 ret = init_root_path(&send, subvol);
528 if (snapshot_parent != NULL) {
529 ret = get_root_id(&send,
530 get_subvol_name(send.root_path, snapshot_parent),
533 fprintf(stderr, "ERROR: could not resolve root_id "
534 "for %s\n", snapshot_parent);
538 add_clone_source(&send, parent_root_id);
541 for (i = optind; i < argc; i++) {
542 subvol = realpath(argv[i], NULL);
545 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
549 ret = find_mount_root(subvol, &mount_root);
551 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
556 if (strcmp(send.root_path, mount_root) != 0) {
558 fprintf(stderr, "ERROR: all subvols must be from the "
564 ret = is_subvol_ro(&send, subvol);
569 fprintf(stderr, "ERROR: %s is not read-only.\n",
576 for (i = optind; i < argc; i++) {
579 fprintf(stderr, "At subvol %s\n", subvol);
581 subvol = realpath(subvol, NULL);
584 fprintf(stderr, "ERROR: realpath %s failed. "
585 "%s\n", argv[i], strerror(-ret));
589 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
592 fprintf(stderr, "ERROR: could not resolve root_id "
597 if (!full_send && !parent_root_id) {
598 ret = find_good_parent(&send, root_id, &parent_root_id);
600 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
606 ret = is_subvol_ro(&send, subvol);
611 fprintf(stderr, "ERROR: %s is not read-only.\n",
616 ret = do_send(&send, root_id, parent_root_id);
620 /* done with this subvol, so add it to the clone sources */
621 add_clone_source(&send, root_id);
631 if (send.mnt_fd >= 0)
636 static const char * const send_cmd_group_usage[] = {
637 "btrfs send <command> <args>",
641 const char * const cmd_send_usage[] = {
642 "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
643 "Send the subvolume to stdout.",
644 "Sends the subvolume specified by <subvol> to stdout.",
645 "By default, this will send the whole subvolume. To do an incremental",
646 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
647 "any additional local snapshots, use -c <clone-src> (multiple times",
648 "where applicable). You must not specify clone sources unless you",
649 "guarantee that these snapshots are exactly in the same state on both",
650 "sides, the sender and the receiver. It is allowed to omit the",
651 "'-p <parent>' option when '-c <clone-src>' options are given, in",
652 "which case 'btrfs send' will determine a suitable parent among the",
653 "clone sources itself.",
655 "-v Enable verbose debug output. Each occurrence of",
656 " this option increases the verbose level more.",
657 "-p <parent> Send an incremental stream from <parent> to",
659 "-c <clone-src> Use this snapshot as a clone source for an ",
660 " incremental send (multiple allowed)",
661 "-f <outfile> Output is normally written to stdout. To write to",
662 " a file, use this option. An alternative would be to",
667 int cmd_send(int argc, char **argv)
669 return cmd_send_start(argc, argv);