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;
105 u64 best_diff = (u64)-1;
108 parent = get_parent(sctx, root_id);
114 for (i = 0; i < sctx->clone_sources_count; i++) {
115 if (sctx->clone_sources[i] == parent->root_id) {
116 best_parent = parent;
122 for (i = 0; i < sctx->clone_sources_count; i++) {
123 parent2 = get_parent(sctx, sctx->clone_sources[i]);
126 if (parent2->root_id != parent->root_id) {
135 parent2 = subvol_uuid_search(&sctx->sus,
136 sctx->clone_sources[i], NULL, 0, NULL,
137 subvol_search_by_root_id);
143 tmp = parent2->ctransid - parent->ctransid;
146 if (tmp < best_diff) {
148 free(best_parent->path);
151 best_parent = parent2;
167 *found = best_parent->root_id;
176 free(best_parent->path);
182 static int add_clone_source(struct btrfs_send *sctx, u64 root_id)
186 tmp = sctx->clone_sources;
187 sctx->clone_sources = realloc(sctx->clone_sources,
188 sizeof(*sctx->clone_sources) * (sctx->clone_sources_count + 1));
190 if (!sctx->clone_sources) {
194 sctx->clone_sources[sctx->clone_sources_count++] = root_id;
199 static int write_buf(int fd, const char *buf, size_t size)
207 wbytes = write(fd, buf + pos, size - pos);
210 error("failed to dump stream: %s", strerror(-ret));
215 error("failed to dump stream: %s", strerror(-ret));
226 static void *dump_thread(void *arg)
229 struct btrfs_send *sctx = (struct btrfs_send*)arg;
230 char buf[SEND_BUFFER_SIZE];
235 rbytes = read(sctx->send_fd, buf, sizeof(buf));
238 error("failed to read stream from kernel: %s\n",
246 ret = write_buf(sctx->dump_fd, buf, rbytes);
258 static int do_send(struct btrfs_send *send, u64 parent_root_id,
259 int is_first_subvol, int is_last_subvol, const char *subvol,
264 struct btrfs_ioctl_send_args io_send;
267 int pipefd[2] = {-1, -1};
269 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
272 error("cannot open %s: %s", subvol, strerror(-ret));
279 error("pipe failed: %s", strerror(-ret));
283 memset(&io_send, 0, sizeof(io_send));
284 io_send.send_fd = pipefd[1];
285 send->send_fd = pipefd[0];
288 ret = pthread_create(&t_read, NULL, dump_thread,
292 error("thread setup failed: %s", strerror(-ret));
296 io_send.flags = flags;
297 io_send.clone_sources = (__u64*)send->clone_sources;
298 io_send.clone_sources_count = send->clone_sources_count;
299 io_send.parent_root = parent_root_id;
300 if (!is_first_subvol)
301 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
303 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
304 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
307 error("send ioctl failed with %d: %s", ret, strerror(-ret));
308 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
310 "Try upgrading your kernel or don't use -e.\n");
314 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
317 fprintf(stderr, "joining genl thread\n");
322 ret = pthread_join(t_read, &t_err);
325 error("pthread_join failed: %s", strerror(-ret));
329 ret = (long int)t_err;
330 error("failed to process send stream, ret=%ld (%s)",
331 (long int)t_err, strerror(-ret));
347 static int init_root_path(struct btrfs_send *sctx, const char *subvol)
354 ret = find_mount_root(subvol, &sctx->root_path);
356 error("failed to determine mount point for %s: %s",
357 subvol, strerror(-ret));
362 error("%s doesn't belong to btrfs mount point", subvol);
367 sctx->mnt_fd = open(sctx->root_path, O_RDONLY | O_NOATIME);
368 if (sctx->mnt_fd < 0) {
370 error("cannot open '%s': %s", sctx->root_path, strerror(-ret));
374 ret = subvol_uuid_search_init(sctx->mnt_fd, &sctx->sus);
376 error("failed to initialize subvol search: %s",
386 static int is_subvol_ro(struct btrfs_send *sctx, const char *subvol)
392 fd = openat(sctx->mnt_fd, subvol, O_RDONLY | O_NOATIME);
395 error("cannot open %s: %s", subvol, strerror(-ret));
399 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
402 error("failed to get flags for subvolume %s: %s",
403 subvol, strerror(-ret));
407 if (flags & BTRFS_SUBVOL_RDONLY)
419 int cmd_send(int argc, char **argv)
423 char outname[PATH_MAX];
424 struct btrfs_send send;
426 char *mount_root = NULL;
427 char *snapshot_parent = NULL;
429 u64 parent_root_id = 0;
431 int new_end_cmd_semantic = 0;
434 memset(&send, 0, sizeof(send));
435 send.dump_fd = fileno(stdout);
439 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
440 static const struct option long_options[] = {
441 { "verbose", no_argument, NULL, 'v' },
442 { "quiet", no_argument, NULL, 'q' },
443 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
445 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
458 new_end_cmd_semantic = 1;
461 subvol = realpath(optarg, NULL);
464 error("realpath %s failed: %s\n", optarg, strerror(-ret));
468 ret = init_root_path(&send, subvol);
472 ret = get_root_id(&send,
473 subvol_strip_mountpoint(send.root_path, subvol),
476 error("cannot resolve rootid for %s", subvol);
480 ret = is_subvol_ro(&send, subvol);
485 error("cloned subvolume %s is not read-only", subvol);
489 ret = add_clone_source(&send, root_id);
491 error("cannot add clone source: %s", strerror(-ret));
494 subvol_uuid_search_finit(&send.sus);
497 if (send.mnt_fd >= 0) {
501 free(send.root_path);
502 send.root_path = NULL;
506 if (arg_copy_path(outname, optarg, sizeof(outname))) {
507 error("output file path too long (%zu)", strlen(optarg));
513 if (snapshot_parent) {
514 error("you cannot have more than one parent (-p)");
518 snapshot_parent = realpath(optarg, NULL);
519 if (!snapshot_parent) {
521 error("realpath %s failed: %s", optarg, strerror(-ret));
525 ret = is_subvol_ro(&send, snapshot_parent);
530 error("parent subvolume %s is not read-only",
538 error("option -i was removed, use -c instead");
541 case GETOPT_VAL_SEND_NO_DATA:
542 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
546 error("send arguments invalid");
552 if (check_argc_min(argc - optind, 1))
553 usage(cmd_send_usage);
556 send.dump_fd = creat(outname, 0600);
557 if (send.dump_fd == -1) {
559 error("cannot create '%s': %s", outname, strerror(-ret));
564 if (isatty(send.dump_fd)) {
566 "not dumping send stream into a terminal, redirect it into a file");
571 /* use first send subvol to determine mount_root */
572 subvol = realpath(argv[optind], NULL);
575 error("unable to resolve %s", argv[optind]);
579 ret = init_root_path(&send, subvol);
583 if (snapshot_parent != NULL) {
584 ret = get_root_id(&send,
585 subvol_strip_mountpoint(send.root_path, snapshot_parent),
588 error("could not resolve rootid for %s", snapshot_parent);
592 ret = add_clone_source(&send, parent_root_id);
594 error("cannot add clone source: %s", strerror(-ret));
599 for (i = optind; i < argc; i++) {
601 subvol = realpath(argv[i], NULL);
604 error("unable to resolve %s", argv[i]);
608 ret = find_mount_root(subvol, &mount_root);
610 error("find_mount_root failed on %s: %s", subvol,
615 error("%s does not belong to btrfs mount point",
620 if (strcmp(send.root_path, mount_root) != 0) {
622 error("all subvolumes must be from the same filesystem");
627 ret = is_subvol_ro(&send, subvol);
632 error("subvolume %s is not read-only", subvol);
637 if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
639 fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
641 for (i = optind; i < argc; i++) {
649 fprintf(stderr, "At subvol %s\n", subvol);
651 subvol = realpath(subvol, NULL);
654 error("realpath %s failed: %s", argv[i], strerror(-ret));
658 if (!full_send && root_id) {
659 ret = find_good_parent(&send, root_id, &parent_root_id);
661 error("parent determination failed for %lld",
667 if (new_end_cmd_semantic) {
668 /* require new kernel */
669 is_first_subvol = (i == optind);
670 is_last_subvol = (i == argc - 1);
672 /* be compatible to old and new kernel */
676 ret = do_send(&send, parent_root_id, is_first_subvol,
677 is_last_subvol, subvol, send_flags);
681 if (!full_send && root_id) {
682 /* done with this subvol, so add it to the clone sources */
683 ret = add_clone_source(&send, root_id);
685 error("cannot add clone source: %s", strerror(-ret));
695 free(snapshot_parent);
696 free(send.clone_sources);
697 if (send.mnt_fd >= 0)
699 free(send.root_path);
700 subvol_uuid_search_finit(&send.sus);
704 const char * const cmd_send_usage[] = {
705 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
706 "Send the subvolume(s) to stdout.",
707 "Sends the subvolume(s) specified by <subvol> to stdout.",
708 "<subvol> should be read-only here.",
709 "By default, this will send the whole subvolume. To do an incremental",
710 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
711 "any additional local snapshots, use '-c <clone-src>' (multiple times",
712 "where applicable). You must not specify clone sources unless you",
713 "guarantee that these snapshots are exactly in the same state on both",
714 "sides, the sender and the receiver. It is allowed to omit the",
715 "'-p <parent>' option when '-c <clone-src>' options are given, in",
716 "which case 'btrfs send' will determine a suitable parent among the",
717 "clone sources itself.",
719 "-e If sending multiple subvols at once, use the new",
720 " format and omit the end-cmd between the subvols.",
721 "-p <parent> Send an incremental stream from <parent> to",
723 "-c <clone-src> Use this snapshot as a clone source for an ",
724 " incremental send (multiple allowed)",
725 "-f <outfile> Output is normally written to stdout. To write to",
726 " a file, use this option. An alternative would be to",
728 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
729 " does not contain any file data and thus cannot be used",
730 " to transfer changes. This mode is faster and useful to",
731 " show the differences in metadata.",
732 "-v|--verbose enable verbose output to stderr, each occurrence of",
733 " this option increases verbosity",
734 "-q|--quiet suppress all messages, except errors",