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.
20 #include "kerncompat.h"
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
35 #include <uuid/uuid.h>
45 #include "send-utils.h"
47 #define SEND_BUFFER_SIZE (64 * 1024)
50 * Default is 1 for historical reasons, changing may break scripts that expect
51 * the 'At subvol' message.
53 static int g_verbose = 1;
61 u64 clone_sources_count;
64 struct subvol_uuid_search sus;
67 static int get_root_id(struct btrfs_send *sctx, const char *path, u64 *root_id)
69 struct subvol_info *si;
71 si = subvol_uuid_search(&sctx->sus, 0, NULL, 0, path,
72 subvol_search_by_path);
75 *root_id = si->root_id;
81 static struct subvol_info *get_parent(struct btrfs_send *sctx, u64 root_id)
83 struct subvol_info *si_tmp;
84 struct subvol_info *si;
86 si_tmp = subvol_uuid_search(&sctx->sus, root_id, NULL, 0, NULL,
87 subvol_search_by_root_id);
91 si = subvol_uuid_search(&sctx->sus, 0, si_tmp->parent_uuid, 0, NULL,
92 subvol_search_by_uuid);
98 static int find_good_parent(struct btrfs_send *sctx, u64 root_id, u64 *found)
101 struct subvol_info *parent = NULL;
102 struct subvol_info *parent2 = NULL;
103 struct subvol_info *best_parent = NULL;
104 u64 best_diff = (u64)-1;
107 parent = get_parent(sctx, root_id);
113 for (i = 0; i < sctx->clone_sources_count; i++) {
114 if (sctx->clone_sources[i] == parent->root_id) {
115 best_parent = parent;
121 for (i = 0; i < sctx->clone_sources_count; i++) {
124 parent2 = get_parent(sctx, sctx->clone_sources[i]);
127 if (parent2->root_id != parent->root_id) {
136 parent2 = subvol_uuid_search(&sctx->sus,
137 sctx->clone_sources[i], NULL, 0, NULL,
138 subvol_search_by_root_id);
144 tmp = parent2->ctransid - parent->ctransid;
147 if (tmp < best_diff) {
149 free(best_parent->path);
152 best_parent = parent2;
168 *found = best_parent->root_id;
177 free(best_parent->path);
183 static int add_clone_source(struct btrfs_send *sctx, u64 root_id)
187 tmp = sctx->clone_sources;
188 sctx->clone_sources = realloc(sctx->clone_sources,
189 sizeof(*sctx->clone_sources) * (sctx->clone_sources_count + 1));
191 if (!sctx->clone_sources) {
195 sctx->clone_sources[sctx->clone_sources_count++] = root_id;
200 static int write_buf(int fd, const char *buf, size_t size)
208 wbytes = write(fd, buf + pos, size - pos);
211 error("failed to dump stream: %s", strerror(-ret));
216 error("failed to dump stream: %s", strerror(-ret));
227 static void *dump_thread(void *arg)
230 struct btrfs_send *sctx = (struct btrfs_send*)arg;
231 char buf[SEND_BUFFER_SIZE];
236 rbytes = read(sctx->send_fd, buf, sizeof(buf));
239 error("failed to read stream from kernel: %s\n",
247 ret = write_buf(sctx->dump_fd, buf, rbytes);
259 static int do_send(struct btrfs_send *send, u64 parent_root_id,
260 int is_first_subvol, int is_last_subvol, const char *subvol,
265 struct btrfs_ioctl_send_args io_send;
268 int pipefd[2] = {-1, -1};
270 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
273 error("cannot open %s: %s", subvol, strerror(-ret));
280 error("pipe failed: %s", strerror(-ret));
284 memset(&io_send, 0, sizeof(io_send));
285 io_send.send_fd = pipefd[1];
286 send->send_fd = pipefd[0];
289 ret = pthread_create(&t_read, NULL, dump_thread,
293 error("thread setup failed: %s", strerror(-ret));
297 io_send.flags = flags;
298 io_send.clone_sources = (__u64*)send->clone_sources;
299 io_send.clone_sources_count = send->clone_sources_count;
300 io_send.parent_root = parent_root_id;
301 if (!is_first_subvol)
302 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
304 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
305 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
308 error("send ioctl failed with %d: %s", ret, strerror(-ret));
309 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
311 "Try upgrading your kernel or don't use -e.\n");
315 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
318 fprintf(stderr, "joining genl thread\n");
323 ret = pthread_join(t_read, &t_err);
326 error("pthread_join failed: %s", strerror(-ret));
330 ret = (long int)t_err;
331 error("failed to process send stream, ret=%ld (%s)",
332 (long int)t_err, strerror(-ret));
348 static int init_root_path(struct btrfs_send *sctx, const char *subvol)
355 ret = find_mount_root(subvol, &sctx->root_path);
357 error("failed to determine mount point for %s: %s",
358 subvol, strerror(-ret));
363 error("%s doesn't belong to btrfs mount point", subvol);
368 sctx->mnt_fd = open(sctx->root_path, O_RDONLY | O_NOATIME);
369 if (sctx->mnt_fd < 0) {
371 error("cannot open '%s': %s", sctx->root_path, strerror(-ret));
375 ret = subvol_uuid_search_init(sctx->mnt_fd, &sctx->sus);
377 error("failed to initialize subvol search: %s",
387 static int is_subvol_ro(struct btrfs_send *sctx, const char *subvol)
393 fd = openat(sctx->mnt_fd, subvol, O_RDONLY | O_NOATIME);
396 error("cannot open %s: %s", subvol, strerror(-ret));
400 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
403 error("failed to get flags for subvolume %s: %s",
404 subvol, strerror(-ret));
408 if (flags & BTRFS_SUBVOL_RDONLY)
420 static int set_root_info(struct btrfs_send *sctx, const char *subvol,
425 ret = init_root_path(sctx, subvol);
429 ret = get_root_id(sctx, subvol_strip_mountpoint(sctx->root_path, subvol),
432 error("cannot resolve rootid for %s", subvol);
440 static void free_send_info(struct btrfs_send *sctx)
442 if (sctx->mnt_fd >= 0) {
446 free(sctx->root_path);
447 sctx->root_path = NULL;
448 subvol_uuid_search_finit(&sctx->sus);
451 int cmd_send(int argc, char **argv)
455 char outname[PATH_MAX];
456 struct btrfs_send send;
458 char *mount_root = NULL;
459 char *snapshot_parent = NULL;
461 u64 parent_root_id = 0;
463 int new_end_cmd_semantic = 0;
466 memset(&send, 0, sizeof(send));
467 send.dump_fd = fileno(stdout);
471 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
472 static const struct option long_options[] = {
473 { "verbose", no_argument, NULL, 'v' },
474 { "quiet", no_argument, NULL, 'q' },
475 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
477 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
490 new_end_cmd_semantic = 1;
493 subvol = realpath(optarg, NULL);
496 error("realpath %s failed: %s\n", optarg, strerror(-ret));
500 ret = set_root_info(&send, subvol, &root_id);
504 ret = is_subvol_ro(&send, subvol);
509 error("cloned subvolume %s is not read-only", subvol);
513 ret = add_clone_source(&send, root_id);
515 error("cannot add clone source: %s", strerror(-ret));
520 free_send_info(&send);
524 if (arg_copy_path(outname, optarg, sizeof(outname))) {
525 error("output file path too long (%zu)", strlen(optarg));
531 if (snapshot_parent) {
532 error("you cannot have more than one parent (-p)");
536 snapshot_parent = realpath(optarg, NULL);
537 if (!snapshot_parent) {
539 error("realpath %s failed: %s", optarg, strerror(-ret));
543 ret = is_subvol_ro(&send, snapshot_parent);
548 error("parent subvolume %s is not read-only",
556 error("option -i was removed, use -c instead");
559 case GETOPT_VAL_SEND_NO_DATA:
560 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
564 error("send arguments invalid");
570 if (check_argc_min(argc - optind, 1))
571 usage(cmd_send_usage);
577 * Try to use an existing file first. Even if send runs as
578 * root, it might not have permissions to create file (eg. on a
579 * NFS) but it should still be able to use a pre-created file.
581 tmpfd = open(outname, O_WRONLY | O_TRUNC);
584 tmpfd = open(outname,
585 O_CREAT | O_WRONLY | O_TRUNC, 0600);
587 send.dump_fd = tmpfd;
588 if (send.dump_fd == -1) {
590 error("cannot create '%s': %s", outname, strerror(-ret));
595 if (isatty(send.dump_fd)) {
597 "not dumping send stream into a terminal, redirect it into a file");
602 /* use first send subvol to determine mount_root */
603 subvol = realpath(argv[optind], NULL);
606 error("unable to resolve %s", argv[optind]);
610 ret = init_root_path(&send, subvol);
614 if (snapshot_parent != NULL) {
615 ret = get_root_id(&send,
616 subvol_strip_mountpoint(send.root_path, snapshot_parent),
619 error("could not resolve rootid for %s", snapshot_parent);
623 ret = add_clone_source(&send, parent_root_id);
625 error("cannot add clone source: %s", strerror(-ret));
630 for (i = optind; i < argc; i++) {
632 subvol = realpath(argv[i], NULL);
635 error("unable to resolve %s", argv[i]);
639 ret = find_mount_root(subvol, &mount_root);
641 error("find_mount_root failed on %s: %s", subvol,
646 error("%s does not belong to btrfs mount point",
651 if (strcmp(send.root_path, mount_root) != 0) {
653 error("all subvolumes must be from the same filesystem");
658 ret = is_subvol_ro(&send, subvol);
663 error("subvolume %s is not read-only", subvol);
668 if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
670 fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
672 for (i = optind; i < argc; i++) {
680 fprintf(stderr, "At subvol %s\n", subvol);
682 subvol = realpath(subvol, NULL);
685 error("realpath %s failed: %s", argv[i], strerror(-ret));
689 if (!full_send && root_id) {
690 ret = set_root_info(&send, subvol, &root_id);
694 ret = find_good_parent(&send, root_id, &parent_root_id);
696 error("parent determination failed for %lld",
702 if (new_end_cmd_semantic) {
703 /* require new kernel */
704 is_first_subvol = (i == optind);
705 is_last_subvol = (i == argc - 1);
707 /* be compatible to old and new kernel */
711 ret = do_send(&send, parent_root_id, is_first_subvol,
712 is_last_subvol, subvol, send_flags);
716 if (!full_send && root_id) {
717 /* done with this subvol, so add it to the clone sources */
718 ret = add_clone_source(&send, root_id);
720 error("cannot add clone source: %s", strerror(-ret));
723 free_send_info(&send);
731 free(snapshot_parent);
732 free(send.clone_sources);
733 free_send_info(&send);
737 const char * const cmd_send_usage[] = {
738 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
739 "Send the subvolume(s) to stdout.",
740 "Sends the subvolume(s) specified by <subvol> to stdout.",
741 "<subvol> should be read-only here.",
742 "By default, this will send the whole subvolume. To do an incremental",
743 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
744 "any additional local snapshots, use '-c <clone-src>' (multiple times",
745 "where applicable). You must not specify clone sources unless you",
746 "guarantee that these snapshots are exactly in the same state on both",
747 "sides, the sender and the receiver. It is allowed to omit the",
748 "'-p <parent>' option when '-c <clone-src>' options are given, in",
749 "which case 'btrfs send' will determine a suitable parent among the",
750 "clone sources itself.",
752 "-e If sending multiple subvols at once, use the new",
753 " format and omit the end-cmd between the subvols.",
754 "-p <parent> Send an incremental stream from <parent> to",
756 "-c <clone-src> Use this snapshot as a clone source for an ",
757 " incremental send (multiple allowed)",
758 "-f <outfile> Output is normally written to stdout. To write to",
759 " a file, use this option. An alternative would be to",
761 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
762 " does not contain any file data and thus cannot be used",
763 " to transfer changes. This mode is faster and useful to",
764 " show the differences in metadata.",
765 "-v|--verbose enable verbose output to stderr, each occurrence of",
766 " this option increases verbosity",
767 "-q|--quiet suppress all messages, except errors",