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 *s, const char *path, u64 *root_id)
69 struct subvol_info *si;
71 si = subvol_uuid_search(&s->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 *s, u64 root_id)
83 struct subvol_info *si_tmp;
84 struct subvol_info *si;
86 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
87 subvol_search_by_root_id);
91 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
92 subvol_search_by_uuid);
98 static int find_good_parent(struct btrfs_send *s, 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(s, root_id);
114 for (i = 0; i < s->clone_sources_count; i++) {
115 if (s->clone_sources[i] == parent->root_id) {
116 best_parent = parent;
122 for (i = 0; i < s->clone_sources_count; i++) {
123 parent2 = get_parent(s, s->clone_sources[i]);
126 if (parent2->root_id != parent->root_id) {
135 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
136 0, NULL, subvol_search_by_root_id);
142 tmp = parent2->ctransid - parent->ctransid;
145 if (tmp < best_diff) {
147 free(best_parent->path);
150 best_parent = parent2;
166 *found = best_parent->root_id;
175 free(best_parent->path);
181 static int add_clone_source(struct btrfs_send *s, u64 root_id)
185 tmp = s->clone_sources;
186 s->clone_sources = realloc(s->clone_sources,
187 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
189 if (!s->clone_sources) {
193 s->clone_sources[s->clone_sources_count++] = root_id;
198 static int write_buf(int fd, const void *buf, int size)
204 ret = write(fd, (char*)buf + pos, size - pos);
207 error("failed to dump stream: %s", strerror(-ret));
212 error("failed to dump stream: %s", strerror(-ret));
223 static void *dump_thread(void *arg_)
226 struct btrfs_send *s = (struct btrfs_send*)arg_;
227 char buf[SEND_BUFFER_SIZE];
231 readed = read(s->send_fd, buf, sizeof(buf));
234 error("failed to read stream from kernel: %s\n",
242 ret = write_buf(s->dump_fd, buf, readed);
255 static int do_send(struct btrfs_send *send, u64 parent_root_id,
256 int is_first_subvol, int is_last_subvol, const char *subvol,
261 struct btrfs_ioctl_send_args io_send;
264 int pipefd[2] = {-1, -1};
266 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
269 error("cannot open %s: %s", subvol, strerror(-ret));
276 error("pipe failed: %s", strerror(-ret));
280 memset(&io_send, 0, sizeof(io_send));
281 io_send.send_fd = pipefd[1];
282 send->send_fd = pipefd[0];
285 ret = pthread_create(&t_read, NULL, dump_thread,
289 error("thread setup failed: %s", strerror(-ret));
293 io_send.flags = flags;
294 io_send.clone_sources = (__u64*)send->clone_sources;
295 io_send.clone_sources_count = send->clone_sources_count;
296 io_send.parent_root = parent_root_id;
297 if (!is_first_subvol)
298 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
300 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
301 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
304 error("send ioctl failed with %d: %s", ret, strerror(-ret));
305 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
307 "Try upgrading your kernel or don't use -e.\n");
311 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
314 fprintf(stderr, "joining genl thread\n");
319 ret = pthread_join(t_read, &t_err);
322 error("pthread_join failed: %s", strerror(-ret));
326 ret = (long int)t_err;
327 error("failed to process send stream, ret=%ld (%s)",
328 (long int)t_err, strerror(-ret));
344 static int init_root_path(struct btrfs_send *s, const char *subvol)
351 ret = find_mount_root(subvol, &s->root_path);
353 error("failed to determine mount point for %s: %s",
354 subvol, strerror(-ret));
359 error("%s doesn't belong to btrfs mount point", subvol);
364 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
367 error("cannot open '%s': %s", s->root_path, strerror(-ret));
371 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
373 error("failed to initialize subvol search: %s",
383 static int is_subvol_ro(struct btrfs_send *s, const char *subvol)
389 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
392 error("cannot open %s: %s", subvol, strerror(-ret));
396 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
399 error("failed to get flags for subvolume %s: %s",
400 subvol, strerror(-ret));
404 if (flags & BTRFS_SUBVOL_RDONLY)
416 int cmd_send(int argc, char **argv)
420 char outname[PATH_MAX];
421 struct btrfs_send send;
423 char *mount_root = NULL;
424 char *snapshot_parent = NULL;
426 u64 parent_root_id = 0;
428 int new_end_cmd_semantic = 0;
431 memset(&send, 0, sizeof(send));
432 send.dump_fd = fileno(stdout);
436 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
437 static const struct option long_options[] = {
438 { "verbose", no_argument, NULL, 'v' },
439 { "quiet", no_argument, NULL, 'q' },
440 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
442 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
455 new_end_cmd_semantic = 1;
458 subvol = realpath(optarg, NULL);
461 error("realpath %s failed: %s\n", optarg, strerror(-ret));
465 ret = init_root_path(&send, subvol);
469 ret = get_root_id(&send,
470 subvol_strip_mountpoint(send.root_path, subvol),
473 error("cannot resolve rootid for %s", subvol);
477 ret = is_subvol_ro(&send, subvol);
482 error("cloned subvolume %s is not read-only", subvol);
486 ret = add_clone_source(&send, root_id);
488 error("cannot add clone source: %s", strerror(-ret));
491 subvol_uuid_search_finit(&send.sus);
494 if (send.mnt_fd >= 0) {
498 free(send.root_path);
499 send.root_path = NULL;
503 if (arg_copy_path(outname, optarg, sizeof(outname))) {
504 error("output file path too long (%zu)", strlen(optarg));
510 if (snapshot_parent) {
511 error("you cannot have more than one parent (-p)");
515 snapshot_parent = realpath(optarg, NULL);
516 if (!snapshot_parent) {
518 error("realpath %s failed: %s", optarg, strerror(-ret));
522 ret = is_subvol_ro(&send, snapshot_parent);
527 error("parent subvolume %s is not read-only",
535 error("option -i was removed, use -c instead");
538 case GETOPT_VAL_SEND_NO_DATA:
539 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
543 error("send arguments invalid");
549 if (check_argc_min(argc - optind, 1))
550 usage(cmd_send_usage);
553 send.dump_fd = creat(outname, 0600);
554 if (send.dump_fd == -1) {
556 error("cannot create '%s': %s", outname, strerror(-ret));
561 if (isatty(send.dump_fd)) {
563 "not dumping send stream into a terminal, redirect it into a file");
568 /* use first send subvol to determine mount_root */
569 subvol = realpath(argv[optind], NULL);
572 error("unable to resolve %s", argv[optind]);
576 ret = init_root_path(&send, subvol);
580 if (snapshot_parent != NULL) {
581 ret = get_root_id(&send,
582 subvol_strip_mountpoint(send.root_path, snapshot_parent),
585 error("could not resolve rootid for %s", snapshot_parent);
589 ret = add_clone_source(&send, parent_root_id);
591 error("cannot add clone source: %s", strerror(-ret));
596 for (i = optind; i < argc; i++) {
598 subvol = realpath(argv[i], NULL);
601 error("unable to resolve %s", argv[i]);
605 ret = find_mount_root(subvol, &mount_root);
607 error("find_mount_root failed on %s: %s", subvol,
612 error("%s does not belong to btrfs mount point",
617 if (strcmp(send.root_path, mount_root) != 0) {
619 error("all subvolumes must be from the same filesystem");
624 ret = is_subvol_ro(&send, subvol);
629 error("subvolume %s is not read-only", subvol);
634 if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
636 fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
638 for (i = optind; i < argc; i++) {
646 fprintf(stderr, "At subvol %s\n", subvol);
648 subvol = realpath(subvol, NULL);
651 error("realpath %s failed: %s", argv[i], strerror(-ret));
655 if (!full_send && root_id) {
656 ret = find_good_parent(&send, root_id, &parent_root_id);
658 error("parent determination failed for %lld",
664 if (new_end_cmd_semantic) {
665 /* require new kernel */
666 is_first_subvol = (i == optind);
667 is_last_subvol = (i == argc - 1);
669 /* be compatible to old and new kernel */
673 ret = do_send(&send, parent_root_id, is_first_subvol,
674 is_last_subvol, subvol, send_flags);
678 if (!full_send && root_id) {
679 /* done with this subvol, so add it to the clone sources */
680 ret = add_clone_source(&send, root_id);
682 error("cannot add clone source: %s", strerror(-ret));
692 free(snapshot_parent);
693 free(send.clone_sources);
694 if (send.mnt_fd >= 0)
696 free(send.root_path);
697 subvol_uuid_search_finit(&send.sus);
701 const char * const cmd_send_usage[] = {
702 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
703 "Send the subvolume(s) to stdout.",
704 "Sends the subvolume(s) specified by <subvol> to stdout.",
705 "<subvol> should be read-only here.",
706 "By default, this will send the whole subvolume. To do an incremental",
707 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
708 "any additional local snapshots, use '-c <clone-src>' (multiple times",
709 "where applicable). You must not specify clone sources unless you",
710 "guarantee that these snapshots are exactly in the same state on both",
711 "sides, the sender and the receiver. It is allowed to omit the",
712 "'-p <parent>' option when '-c <clone-src>' options are given, in",
713 "which case 'btrfs send' will determine a suitable parent among the",
714 "clone sources itself.",
716 "-e If sending multiple subvols at once, use the new",
717 " format and omit the end-cmd between the subvols.",
718 "-p <parent> Send an incremental stream from <parent> to",
720 "-c <clone-src> Use this snapshot as a clone source for an ",
721 " incremental send (multiple allowed)",
722 "-f <outfile> Output is normally written to stdout. To write to",
723 " a file, use this option. An alternative would be to",
725 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
726 " does not contain any file data and thus cannot be used",
727 " to transfer changes. This mode is faster and useful to",
728 " show the differences in metadata.",
729 "-v|--verbose enable verbose output to stderr, each occurrence of",
730 " this option increases verbosity",
731 "-q|--quiet suppress all messages, except errors",