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>
32 #include <uuid/uuid.h>
40 #include "send-utils.h"
42 static int g_verbose = 0;
50 u64 clone_sources_count;
53 struct subvol_uuid_search sus;
56 int find_mount_root(const char *path, char **mount_root)
60 char fsid[BTRFS_FSID_SIZE];
66 struct btrfs_ioctl_fs_info_args args;
68 fd = open(path, O_RDONLY | O_NOATIME);
79 if (!S_ISDIR(st.st_mode)) {
84 ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
89 memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
101 fd = open(tmp, O_RDONLY | O_NOATIME);
107 ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
112 if (memcmp(fsid, args.fsid, BTRFS_FSID_SIZE) != 0)
119 if (strcmp(cur, "/") == 0)
121 if (strcmp(cur, ".") == 0)
126 *mount_root = realpath(cur, NULL);
136 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
138 struct subvol_info *si;
140 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
141 subvol_search_by_path);
144 *root_id = si->root_id;
148 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
150 struct subvol_info *si;
152 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
153 subvol_search_by_root_id);
157 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
158 subvol_search_by_uuid);
164 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
167 struct subvol_info *parent;
168 struct subvol_info *parent2;
169 struct subvol_info *best_parent = NULL;
171 u64 best_diff = (u64)-1;
174 parent = get_parent(s, root_id);
180 for (i = 0; i < s->clone_sources_count; i++) {
181 if (s->clone_sources[i] == parent->root_id) {
182 best_parent = parent;
187 for (i = 0; i < s->clone_sources_count; i++) {
188 parent2 = get_parent(s, s->clone_sources[i]);
189 if (parent2 != parent)
192 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
193 0, NULL, subvol_search_by_root_id);
195 tmp = parent2->ctransid - parent->ctransid;
198 if (tmp < best_diff) {
199 best_parent = parent;
210 *found = best_parent->root_id;
217 static void add_clone_source(struct btrfs_send *s, u64 root_id)
219 s->clone_sources = realloc(s->clone_sources,
220 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
221 s->clone_sources[s->clone_sources_count++] = root_id;
224 static int write_buf(int fd, const void *buf, int size)
230 ret = write(fd, (char*)buf + pos, size - pos);
233 fprintf(stderr, "ERROR: failed to dump stream. %s",
239 fprintf(stderr, "ERROR: failed to dump stream. %s",
251 static void *dump_thread(void *arg_)
254 struct btrfs_send *s = (struct btrfs_send*)arg_;
259 readed = read(s->send_fd, buf, sizeof(buf));
262 fprintf(stderr, "ERROR: failed to read stream from "
263 "kernel. %s\n", strerror(-ret));
270 ret = write_buf(s->dump_fd, buf, readed);
283 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
287 pthread_attr_t t_attr;
288 struct btrfs_ioctl_send_args io_send;
289 struct subvol_info *si;
294 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
295 subvol_search_by_root_id);
298 fprintf(stderr, "ERROR: could not find subvol info for %llu",
303 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
306 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
311 ret = pthread_attr_init(&t_attr);
316 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
320 io_send.send_fd = pipefd[1];
321 send->send_fd = pipefd[0];
324 ret = pthread_create(&t_read, &t_attr, dump_thread,
328 fprintf(stderr, "ERROR: thread setup failed: %s\n",
333 io_send.clone_sources = (__u64*)send->clone_sources;
334 io_send.clone_sources_count = send->clone_sources_count;
335 io_send.parent_root = parent_root;
336 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
339 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
344 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
347 fprintf(stderr, "joining genl thread\n");
352 ret = pthread_join(t_read, &t_err);
355 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
360 ret = (long int)t_err;
361 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
362 "(%s)\n", (long int)t_err, strerror(-ret));
366 pthread_attr_destroy(&t_attr);
380 static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
382 int len = strlen(s->root_path);
385 if (s->root_path[len - 1] != '/')
388 return full_path + len;
391 static int init_root_path(struct btrfs_send *s, const char *subvol)
398 ret = find_mount_root(subvol, &s->root_path);
401 fprintf(stderr, "ERROR: failed to determine mount point "
406 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
409 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
414 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
416 fprintf(stderr, "ERROR: failed to initialize subvol search. "
417 "%s\n", strerror(-ret));
426 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
432 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
435 fprintf(stderr, "ERROR: failed to open %s. %s\n",
436 subvol, strerror(-ret));
440 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
443 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
444 "%s\n", strerror(-ret));
448 if (flags & BTRFS_SUBVOL_RDONLY)
460 int cmd_send_start(int argc, char **argv)
465 char *outname = NULL;
466 struct btrfs_send send;
468 char *mount_root = NULL;
469 char *snapshot_parent = NULL;
471 u64 parent_root_id = 0;
473 memset(&send, 0, sizeof(send));
474 send.dump_fd = fileno(stdout);
476 while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
482 subvol = realpath(optarg, NULL);
485 fprintf(stderr, "ERROR: realpath %s failed. "
486 "%s\n", optarg, strerror(-ret));
490 ret = init_root_path(&send, subvol);
494 ret = get_root_id(&send, get_subvol_name(&send, subvol),
497 fprintf(stderr, "ERROR: could not resolve "
498 "root_id for %s\n", subvol);
501 add_clone_source(&send, root_id);
509 snapshot_parent = realpath(optarg, NULL);
510 if (!snapshot_parent) {
512 fprintf(stderr, "ERROR: realpath %s failed. "
513 "%s\n", optarg, strerror(-ret));
519 fprintf(stderr, "ERROR: send args invalid.\n");
524 if (optind == argc) {
525 fprintf(stderr, "ERROR: send needs path to snapshot\n");
529 if (outname != NULL) {
530 send.dump_fd = creat(outname, 0600);
531 if (send.dump_fd == -1) {
533 fprintf(stderr, "ERROR: can't create '%s': %s\n",
534 outname, strerror(-ret));
539 /* use first send subvol to determine mount_root */
540 subvol = argv[optind];
542 subvol = realpath(argv[optind], NULL);
544 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
548 ret = init_root_path(&send, subvol);
552 if (snapshot_parent != NULL) {
553 ret = get_root_id(&send,
554 get_subvol_name(&send, snapshot_parent),
557 fprintf(stderr, "ERROR: could not resolve root_id "
558 "for %s\n", snapshot_parent);
562 add_clone_source(&send, parent_root_id);
565 for (i = optind; i < argc; i++) {
566 subvol = realpath(argv[i], NULL);
568 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
572 ret = find_mount_root(subvol, &mount_root);
574 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
579 if (strcmp(send.root_path, mount_root) != 0) {
581 fprintf(stderr, "ERROR: all subvols must be from the "
587 ret = is_subvol_ro(&send, subvol);
592 fprintf(stderr, "ERROR: %s is not read-only.\n",
599 for (i = optind; i < argc; i++) {
602 fprintf(stderr, "At subvol %s\n", subvol);
604 subvol = realpath(subvol, NULL);
607 fprintf(stderr, "ERROR: realpath %s failed. "
608 "%s\n", argv[i], strerror(-ret));
612 ret = get_root_id(&send, get_subvol_name(&send, subvol),
615 fprintf(stderr, "ERROR: could not resolve root_id "
620 if (!parent_root_id) {
621 ret = find_good_parent(&send, root_id, &parent_root_id);
626 ret = is_subvol_ro(&send, subvol);
631 fprintf(stderr, "ERROR: %s is not read-only.\n",
636 ret = do_send(&send, root_id, parent_root_id);
640 /* done with this subvol, so add it to the clone sources */
641 add_clone_source(&send, root_id);
650 if (send.mnt_fd >= 0)
655 static const char * const send_cmd_group_usage[] = {
656 "btrfs send <command> <args>",
660 static const char * const cmd_send_usage[] = {
661 "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
662 "Send the subvolume to stdout.",
663 "Sends the subvolume specified by <subvol> to stdout.",
664 "By default, this will send the whole subvolume. To do",
665 "an incremental send, one or multiple '-i <clone_source>'",
666 "arguments have to be specified. A 'clone source' is",
667 "a subvolume that is known to exist on the receiving",
668 "side in exactly the same state as on the sending side.\n",
669 "Normally, a good snapshot parent is searched automatically",
670 "in the list of 'clone sources'. To override this, use",
671 "'-p <parent>' to manually specify a snapshot parent.",
672 "A manually specified snapshot parent is also regarded",
673 "as 'clone source'.\n",
674 "-v Enable verbose debug output. Each",
675 " occurrency of this option increases the",
676 " verbose level more.",
677 "-i <subvol> Informs btrfs send that this subvolume,",
678 " can be taken as 'clone source'. This can",
679 " be used for incremental sends.",
680 "-p <subvol> Disable automatic snaphot parent",
681 " determination and use <subvol> as parent.",
682 " This subvolume is also added to the list",
683 " of 'clone sources' (see -i).",
684 "-f <outfile> Output is normally written to stdout.",
685 " To write to a file, use this option.",
686 " An alternative would be to use pipes.",
690 const struct cmd_group send_cmd_group = {
691 send_cmd_group_usage, NULL, {
692 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
697 int cmd_send(int argc, char **argv)
699 return cmd_send_start(argc, argv);