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"
48 * Default is 1 for historical reasons, changing may break scripts that expect
49 * the 'At subvol' message.
51 static int g_verbose = 1;
59 u64 clone_sources_count;
62 struct subvol_uuid_search sus;
65 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
67 struct subvol_info *si;
69 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
70 subvol_search_by_path);
73 *root_id = si->root_id;
79 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
81 struct subvol_info *si_tmp;
82 struct subvol_info *si;
84 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
85 subvol_search_by_root_id);
89 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
90 subvol_search_by_uuid);
96 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
99 struct subvol_info *parent = NULL;
100 struct subvol_info *parent2 = NULL;
101 struct subvol_info *best_parent = NULL;
103 u64 best_diff = (u64)-1;
106 parent = get_parent(s, root_id);
112 for (i = 0; i < s->clone_sources_count; i++) {
113 if (s->clone_sources[i] == parent->root_id) {
114 best_parent = parent;
120 for (i = 0; i < s->clone_sources_count; i++) {
121 parent2 = get_parent(s, s->clone_sources[i]);
124 if (parent2->root_id != parent->root_id) {
133 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
134 0, NULL, subvol_search_by_root_id);
140 tmp = parent2->ctransid - parent->ctransid;
143 if (tmp < best_diff) {
145 free(best_parent->path);
148 best_parent = parent2;
164 *found = best_parent->root_id;
173 free(best_parent->path);
179 static int add_clone_source(struct btrfs_send *s, u64 root_id)
183 tmp = s->clone_sources;
184 s->clone_sources = realloc(s->clone_sources,
185 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
187 if (!s->clone_sources) {
191 s->clone_sources[s->clone_sources_count++] = root_id;
196 static int write_buf(int fd, const void *buf, int size)
202 ret = write(fd, (char*)buf + pos, size - pos);
205 error("failed to dump stream: %s", strerror(-ret));
210 error("failed to dump stream: %s", strerror(-ret));
221 static void *dump_thread(void *arg_)
224 struct btrfs_send *s = (struct btrfs_send*)arg_;
229 readed = read(s->send_fd, buf, sizeof(buf));
232 error("failed to read stream from kernel: %s\n",
240 ret = write_buf(s->dump_fd, buf, readed);
253 static int do_send(struct btrfs_send *send, u64 parent_root_id,
254 int is_first_subvol, int is_last_subvol, char *subvol,
259 struct btrfs_ioctl_send_args io_send;
262 int pipefd[2] = {-1, -1};
264 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
267 error("cannot open %s: %s", subvol, strerror(-ret));
274 error("pipe failed: %s", strerror(-ret));
278 memset(&io_send, 0, sizeof(io_send));
279 io_send.send_fd = pipefd[1];
280 send->send_fd = pipefd[0];
283 ret = pthread_create(&t_read, NULL, dump_thread,
287 error("thread setup failed: %s", strerror(-ret));
291 io_send.flags = flags;
292 io_send.clone_sources = (__u64*)send->clone_sources;
293 io_send.clone_sources_count = send->clone_sources_count;
294 io_send.parent_root = parent_root_id;
295 if (!is_first_subvol)
296 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
298 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
299 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
302 error("send ioctl failed with %d: %s", ret, strerror(-ret));
303 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
305 "Try upgrading your kernel or don't use -e.\n");
309 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
312 fprintf(stderr, "joining genl thread\n");
317 ret = pthread_join(t_read, &t_err);
320 error("pthread_join failed: %s", strerror(-ret));
324 ret = (long int)t_err;
325 error("failed to process send stream, ret=%ld (%s)",
326 (long int)t_err, strerror(-ret));
342 static int init_root_path(struct btrfs_send *s, const char *subvol)
349 ret = find_mount_root(subvol, &s->root_path);
351 error("failed to determine mount point for %s: %s",
352 subvol, strerror(-ret));
357 error("%s doesn't belong to btrfs mount point", subvol);
362 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
365 error("cannot open '%s': %s", s->root_path, strerror(-ret));
369 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
371 error("failed to initialize subvol search: %s",
381 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
387 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
390 error("cannot open %s: %s", subvol, strerror(-ret));
394 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
397 error("failed to get flags for subvolume %s: %s",
398 subvol, strerror(-ret));
402 if (flags & BTRFS_SUBVOL_RDONLY)
414 int cmd_send(int argc, char **argv)
418 char outname[PATH_MAX];
419 struct btrfs_send send;
421 char *mount_root = NULL;
422 char *snapshot_parent = NULL;
424 u64 parent_root_id = 0;
426 int new_end_cmd_semantic = 0;
429 memset(&send, 0, sizeof(send));
430 send.dump_fd = fileno(stdout);
434 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
435 static const struct option long_options[] = {
436 { "verbose", no_argument, NULL, 'v' },
437 { "quiet", no_argument, NULL, 'q' },
438 { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
440 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
453 new_end_cmd_semantic = 1;
456 subvol = realpath(optarg, NULL);
459 error("realpath %s failed: %s\n", optarg, strerror(-ret));
463 ret = init_root_path(&send, subvol);
467 ret = get_root_id(&send,
468 subvol_strip_mountpoint(send.root_path, subvol),
471 error("cannot resolve rootid for %s", subvol);
475 ret = is_subvol_ro(&send, subvol);
480 error("cloned subvolume %s is not read-only", subvol);
484 ret = add_clone_source(&send, root_id);
486 error("cannot add clone source: %s", strerror(-ret));
489 subvol_uuid_search_finit(&send.sus);
492 if (send.mnt_fd >= 0) {
496 free(send.root_path);
497 send.root_path = NULL;
501 if (arg_copy_path(outname, optarg, sizeof(outname))) {
502 error("output file path too long (%zu)", strlen(optarg));
508 if (snapshot_parent) {
509 error("you cannot have more than one parent (-p)");
513 snapshot_parent = realpath(optarg, NULL);
514 if (!snapshot_parent) {
516 error("realpath %s failed: %s", optarg, strerror(-ret));
520 ret = is_subvol_ro(&send, snapshot_parent);
525 error("parent subvolume %s is not read-only",
533 error("option -i was removed, use -c instead");
536 case GETOPT_VAL_SEND_NO_DATA:
537 send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
541 error("send arguments invalid");
547 if (check_argc_min(argc - optind, 1))
548 usage(cmd_send_usage);
551 send.dump_fd = creat(outname, 0600);
552 if (send.dump_fd == -1) {
554 error("cannot create '%s': %s", outname, strerror(-ret));
559 if (isatty(send.dump_fd)) {
561 "not dumping send stream into a terminal, redirect it into a file");
566 /* use first send subvol to determine mount_root */
567 subvol = argv[optind];
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 && !parent_root_id) {
656 ret = find_good_parent(&send, root_id, &parent_root_id);
658 error("parent determination failed for %lld",
664 ret = is_subvol_ro(&send, subvol);
669 error("subvolume %s is not read-only", subvol);
673 if (new_end_cmd_semantic) {
674 /* require new kernel */
675 is_first_subvol = (i == optind);
676 is_last_subvol = (i == argc - 1);
678 /* be compatible to old and new kernel */
682 ret = do_send(&send, parent_root_id, is_first_subvol,
683 is_last_subvol, subvol, send_flags);
688 /* done with this subvol, so add it to the clone sources */
689 ret = add_clone_source(&send, root_id);
691 error("cannot add clone source: %s", strerror(-ret));
703 free(snapshot_parent);
704 free(send.clone_sources);
705 if (send.mnt_fd >= 0)
707 free(send.root_path);
708 subvol_uuid_search_finit(&send.sus);
712 const char * const cmd_send_usage[] = {
713 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
714 "Send the subvolume(s) to stdout.",
715 "Sends the subvolume(s) specified by <subvol> to stdout.",
716 "<subvol> should be read-only here.",
717 "By default, this will send the whole subvolume. To do an incremental",
718 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
719 "any additional local snapshots, use '-c <clone-src>' (multiple times",
720 "where applicable). You must not specify clone sources unless you",
721 "guarantee that these snapshots are exactly in the same state on both",
722 "sides, the sender and the receiver. It is allowed to omit the",
723 "'-p <parent>' option when '-c <clone-src>' options are given, in",
724 "which case 'btrfs send' will determine a suitable parent among the",
725 "clone sources itself.",
727 "-e If sending multiple subvols at once, use the new",
728 " format and omit the end-cmd between the subvols.",
729 "-p <parent> Send an incremental stream from <parent> to",
731 "-c <clone-src> Use this snapshot as a clone source for an ",
732 " incremental send (multiple allowed)",
733 "-f <outfile> Output is normally written to stdout. To write to",
734 " a file, use this option. An alternative would be to",
736 "--no-data send in NO_FILE_DATA mode, Note: the output stream",
737 " does not contain any file data and thus cannot be used",
738 " to transfer changes. This mode is faster and useful to",
739 " show the differences in metadata.",
740 "-v|--verbose enable verbose output to stderr, each occurrence of",
741 " this option increases verbosity",
742 "-q|--quiet suppress all messages, except errors",