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>
31 #include <uuid/uuid.h>
39 #include "send-utils.h"
41 static int g_verbose = 0;
49 u64 clone_sources_count;
52 struct subvol_uuid_search sus;
55 int find_mount_root(const char *path, char **mount_root)
58 char cur[BTRFS_PATH_NAME_MAX];
59 char fsid[BTRFS_FSID_SIZE];
65 struct btrfs_ioctl_fs_info_args args;
67 fd = open(path, O_RDONLY | O_NOATIME);
78 if (!S_ISDIR(st.st_mode)) {
83 ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
88 memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
94 tmp = strrchr(cur, '/');
102 fd = open(cur, O_RDONLY | O_NOATIME);
108 ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
115 if (memcmp(fsid, args.fsid, BTRFS_FSID_SIZE) != 0) {
122 *mount_root = realpath(cur, NULL);
130 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
132 struct subvol_info *si;
134 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
135 subvol_search_by_path);
138 *root_id = si->root_id;
142 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
144 struct subvol_info *si;
146 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
147 subvol_search_by_root_id);
151 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
152 subvol_search_by_uuid);
158 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
161 struct subvol_info *parent;
162 struct subvol_info *parent2;
163 struct subvol_info *best_parent = NULL;
165 u64 best_diff = (u64)-1;
168 parent = get_parent(s, root_id);
174 for (i = 0; i < s->clone_sources_count; i++) {
175 if (s->clone_sources[i] == parent->root_id) {
176 best_parent = parent;
181 for (i = 0; i < s->clone_sources_count; i++) {
182 parent2 = get_parent(s, s->clone_sources[i]);
183 if (parent2 != parent)
186 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
187 0, NULL, subvol_search_by_root_id);
189 tmp = parent2->ctransid - parent->ctransid;
192 if (tmp < best_diff) {
193 best_parent = parent;
204 *found = best_parent->root_id;
211 static void add_clone_source(struct btrfs_send *s, u64 root_id)
213 s->clone_sources = realloc(s->clone_sources,
214 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
215 s->clone_sources[s->clone_sources_count++] = root_id;
218 static int write_buf(int fd, const void *buf, int size)
224 ret = write(fd, (char*)buf + pos, size - pos);
227 fprintf(stderr, "ERROR: failed to dump stream. %s",
233 fprintf(stderr, "ERROR: failed to dump stream. %s",
245 static void *dump_thread(void *arg_)
248 struct btrfs_send *s = (struct btrfs_send*)arg_;
253 readed = read(s->send_fd, buf, sizeof(buf));
256 fprintf(stderr, "ERROR: failed to read stream from "
257 "kernel. %s\n", strerror(-ret));
264 ret = write_buf(s->dump_fd, buf, readed);
277 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
281 pthread_attr_t t_attr;
282 struct btrfs_ioctl_send_args io_send;
283 struct subvol_info *si;
288 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
289 subvol_search_by_root_id);
292 fprintf(stderr, "ERROR: could not find subvol info for %llu",
297 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
300 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
305 ret = pthread_attr_init(&t_attr);
310 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
314 io_send.send_fd = pipefd[1];
315 send->send_fd = pipefd[0];
318 ret = pthread_create(&t_read, &t_attr, dump_thread,
322 fprintf(stderr, "ERROR: thread setup failed: %s\n",
327 io_send.clone_sources = (__u64*)send->clone_sources;
328 io_send.clone_sources_count = send->clone_sources_count;
329 io_send.parent_root = parent_root;
330 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
333 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
338 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
341 fprintf(stderr, "joining genl thread\n");
346 ret = pthread_join(t_read, &t_err);
349 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
354 ret = (long int)t_err;
355 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
356 "(%s)\n", (long int)t_err, strerror(-ret));
360 pthread_attr_destroy(&t_attr);
374 static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
376 return full_path + strlen(s->root_path) + 1;
379 static int init_root_path(struct btrfs_send *s, const char *subvol)
386 ret = find_mount_root(subvol, &s->root_path);
389 fprintf(stderr, "ERROR: failed to determine mount point "
394 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
397 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
402 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
404 fprintf(stderr, "ERROR: failed to initialize subvol search. "
405 "%s\n", strerror(-ret));
414 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
420 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
423 fprintf(stderr, "ERROR: failed to open %s. %s\n",
424 subvol, strerror(-ret));
428 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
431 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
432 "%s\n", strerror(-ret));
436 if (flags & BTRFS_SUBVOL_RDONLY)
448 int cmd_send_start(int argc, char **argv)
453 char *outname = NULL;
454 struct btrfs_send send;
456 char *mount_root = NULL;
457 char *snapshot_parent = NULL;
459 u64 parent_root_id = 0;
461 memset(&send, 0, sizeof(send));
462 send.dump_fd = fileno(stdout);
464 while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
470 subvol = realpath(optarg, NULL);
473 fprintf(stderr, "ERROR: realpath %s failed. "
474 "%s\n", optarg, strerror(-ret));
478 ret = init_root_path(&send, subvol);
482 ret = get_root_id(&send, get_subvol_name(&send, subvol),
485 fprintf(stderr, "ERROR: could not resolve "
486 "root_id for %s\n", subvol);
489 add_clone_source(&send, root_id);
497 snapshot_parent = realpath(optarg, NULL);
498 if (!snapshot_parent) {
500 fprintf(stderr, "ERROR: realpath %s failed. "
501 "%s\n", optarg, strerror(-ret));
507 fprintf(stderr, "ERROR: send args invalid.\n");
512 if (optind == argc) {
513 fprintf(stderr, "ERROR: send needs path to snapshot\n");
517 if (outname != NULL) {
518 send.dump_fd = creat(outname, 0600);
519 if (send.dump_fd == -1) {
521 fprintf(stderr, "ERROR: can't create '%s': %s\n",
522 outname, strerror(-ret));
527 /* use first send subvol to determine mount_root */
528 subvol = argv[optind];
530 ret = init_root_path(&send, subvol);
534 if (snapshot_parent != NULL) {
535 ret = get_root_id(&send,
536 get_subvol_name(&send, snapshot_parent),
539 fprintf(stderr, "ERROR: could not resolve root_id "
540 "for %s\n", snapshot_parent);
544 add_clone_source(&send, parent_root_id);
547 for (i = optind; i < argc; 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",
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, subvol),
592 fprintf(stderr, "ERROR: could not resolve root_id "
597 if (!parent_root_id) {
598 ret = find_good_parent(&send, root_id, &parent_root_id);
603 ret = is_subvol_ro(&send, subvol);
608 fprintf(stderr, "ERROR: %s is not read-only.\n",
613 ret = do_send(&send, root_id, parent_root_id);
617 /* done with this subvol, so add it to the clone sources */
618 add_clone_source(&send, root_id);
627 if (send.mnt_fd >= 0)
632 static const char * const send_cmd_group_usage[] = {
633 "btrfs send <command> <args>",
637 static const char * const cmd_send_usage[] = {
638 "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
639 "Send the subvolume to stdout.",
640 "Sends the subvolume specified by <subvol> to stdout.",
641 "By default, this will send the whole subvolume. To do",
642 "an incremental send, one or multiple '-i <clone_source>'",
643 "arguments have to be specified. A 'clone source' is",
644 "a subvolume that is known to exist on the receiving",
645 "side in exactly the same state as on the sending side.\n",
646 "Normally, a good snapshot parent is searched automatically",
647 "in the list of 'clone sources'. To override this, use",
648 "'-p <parent>' to manually specify a snapshot parent.",
649 "A manually specified snapshot parent is also regarded",
650 "as 'clone source'.\n",
651 "-v Enable verbose debug output. Each",
652 " occurrency of this option increases the",
653 " verbose level more.",
654 "-i <subvol> Informs btrfs send that this subvolume,",
655 " can be taken as 'clone source'. This can",
656 " be used for incremental sends.",
657 "-p <subvol> Disable automatic snaphot parent",
658 " determination and use <subvol> as parent.",
659 " This subvolume is also added to the list",
660 " of 'clone sources' (see -i).",
661 "-f <outfile> Output is normally written to stdout.",
662 " To write to a file, use this option.",
663 " An alternative would be to use pipes.",
667 const struct cmd_group send_cmd_group = {
668 send_cmd_group_usage, NULL, {
669 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
674 int cmd_send(int argc, char **argv)
676 return cmd_send_start(argc, argv);