btrfs-progs: make pretty Documentation/ build match the rest
[btrfs-progs-unstable/devel.git] / cmds-send.c
blob2df16be81766c68fd3729f06c88b78e1352fcc03
1 /*
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.
19 #define _GNU_SOURCE
21 #include "kerncompat.h"
23 #include <unistd.h>
24 #include <stdint.h>
25 #include <dirent.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28 #include <math.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <libgen.h>
33 #include <mntent.h>
34 #include <assert.h>
36 #include <uuid/uuid.h>
38 #include "ctree.h"
39 #include "ioctl.h"
40 #include "commands.h"
41 #include "list.h"
42 #include "utils.h"
44 #include "send.h"
45 #include "send-utils.h"
47 static int g_verbose = 0;
49 struct btrfs_send {
50 int send_fd;
51 int dump_fd;
52 int mnt_fd;
54 u64 *clone_sources;
55 u64 clone_sources_count;
57 char *root_path;
58 struct subvol_uuid_search sus;
61 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
63 struct subvol_info *si;
65 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
66 subvol_search_by_path);
67 if (!si)
68 return -ENOENT;
69 *root_id = si->root_id;
70 free(si->path);
71 free(si);
72 return 0;
75 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
77 struct subvol_info *si_tmp;
78 struct subvol_info *si;
80 si_tmp = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
81 subvol_search_by_root_id);
82 if (!si_tmp)
83 return NULL;
85 si = subvol_uuid_search(&s->sus, 0, si_tmp->parent_uuid, 0, NULL,
86 subvol_search_by_uuid);
87 free(si_tmp->path);
88 free(si_tmp);
89 return si;
92 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
94 int ret;
95 struct subvol_info *parent = NULL;
96 struct subvol_info *parent2 = NULL;
97 struct subvol_info *best_parent = NULL;
98 __s64 tmp;
99 u64 best_diff = (u64)-1;
100 int i;
102 parent = get_parent(s, root_id);
103 if (!parent) {
104 ret = -ENOENT;
105 goto out;
108 for (i = 0; i < s->clone_sources_count; i++) {
109 if (s->clone_sources[i] == parent->root_id) {
110 best_parent = parent;
111 parent = NULL;
112 goto out_found;
116 for (i = 0; i < s->clone_sources_count; i++) {
117 parent2 = get_parent(s, s->clone_sources[i]);
118 if (!parent2)
119 continue;
120 if (parent2->root_id != parent->root_id) {
121 free(parent2->path);
122 free(parent2);
123 parent2 = NULL;
124 continue;
127 free(parent2->path);
128 free(parent2);
129 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
130 0, NULL, subvol_search_by_root_id);
132 if (!parent2) {
133 ret = -ENOENT;
134 goto out;
136 tmp = parent2->ctransid - parent->ctransid;
137 if (tmp < 0)
138 tmp *= -1;
139 if (tmp < best_diff) {
140 if (best_parent) {
141 free(best_parent->path);
142 free(best_parent);
144 best_parent = parent2;
145 parent2 = NULL;
146 best_diff = tmp;
147 } else {
148 free(parent2->path);
149 free(parent2);
150 parent2 = NULL;
154 if (!best_parent) {
155 ret = -ENOENT;
156 goto out;
159 out_found:
160 *found = best_parent->root_id;
161 ret = 0;
163 out:
164 if (parent) {
165 free(parent->path);
166 free(parent);
168 if (best_parent) {
169 free(best_parent->path);
170 free(best_parent);
172 return ret;
175 static void add_clone_source(struct btrfs_send *s, u64 root_id)
177 s->clone_sources = realloc(s->clone_sources,
178 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
179 s->clone_sources[s->clone_sources_count++] = root_id;
182 static int write_buf(int fd, const void *buf, int size)
184 int ret;
185 int pos = 0;
187 while (pos < size) {
188 ret = write(fd, (char*)buf + pos, size - pos);
189 if (ret < 0) {
190 ret = -errno;
191 fprintf(stderr, "ERROR: failed to dump stream. %s",
192 strerror(-ret));
193 goto out;
195 if (!ret) {
196 ret = -EIO;
197 fprintf(stderr, "ERROR: failed to dump stream. %s",
198 strerror(-ret));
199 goto out;
201 pos += ret;
203 ret = 0;
205 out:
206 return ret;
209 static void *dump_thread(void *arg_)
211 int ret;
212 struct btrfs_send *s = (struct btrfs_send*)arg_;
213 char buf[4096];
214 int readed;
216 while (1) {
217 readed = read(s->send_fd, buf, sizeof(buf));
218 if (readed < 0) {
219 ret = -errno;
220 fprintf(stderr, "ERROR: failed to read stream from "
221 "kernel. %s\n", strerror(-ret));
222 goto out;
224 if (!readed) {
225 ret = 0;
226 goto out;
228 ret = write_buf(s->dump_fd, buf, readed);
229 if (ret < 0)
230 goto out;
233 out:
234 if (ret < 0) {
235 exit(-ret);
238 return ERR_PTR(ret);
241 static int do_send(struct btrfs_send *send, u64 parent_root_id,
242 int is_first_subvol, int is_last_subvol, char *subvol)
244 int ret;
245 pthread_t t_read;
246 struct btrfs_ioctl_send_args io_send;
247 void *t_err = NULL;
248 int subvol_fd = -1;
249 int pipefd[2] = {-1, -1};
251 subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
252 if (subvol_fd < 0) {
253 ret = -errno;
254 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
255 strerror(-ret));
256 goto out;
259 ret = pipe(pipefd);
260 if (ret < 0) {
261 ret = -errno;
262 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
263 goto out;
266 memset(&io_send, 0, sizeof(io_send));
267 io_send.send_fd = pipefd[1];
268 send->send_fd = pipefd[0];
270 if (!ret)
271 ret = pthread_create(&t_read, NULL, dump_thread,
272 send);
273 if (ret) {
274 ret = -ret;
275 fprintf(stderr, "ERROR: thread setup failed: %s\n",
276 strerror(-ret));
277 goto out;
280 io_send.clone_sources = (__u64*)send->clone_sources;
281 io_send.clone_sources_count = send->clone_sources_count;
282 io_send.parent_root = parent_root_id;
283 if (!is_first_subvol)
284 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
285 if (!is_last_subvol)
286 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
287 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
288 if (ret) {
289 ret = -errno;
290 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
291 strerror(-ret));
292 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
293 fprintf(stderr,
294 "Try upgrading your kernel or don't use -e.\n");
295 goto out;
297 if (g_verbose > 0)
298 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
300 if (g_verbose > 0)
301 fprintf(stderr, "joining genl thread\n");
303 close(pipefd[1]);
304 pipefd[1] = -1;
306 ret = pthread_join(t_read, &t_err);
307 if (ret) {
308 ret = -ret;
309 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
310 strerror(-ret));
311 goto out;
313 if (t_err) {
314 ret = (long int)t_err;
315 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
316 "(%s)\n", (long int)t_err, strerror(-ret));
317 goto out;
320 ret = 0;
322 out:
323 if (subvol_fd != -1)
324 close(subvol_fd);
325 if (pipefd[0] != -1)
326 close(pipefd[0]);
327 if (pipefd[1] != -1)
328 close(pipefd[1]);
329 return ret;
332 char *get_subvol_name(char *mnt, char *full_path)
334 int len = strlen(mnt);
335 if (!len)
336 return full_path;
337 if (mnt[len - 1] != '/')
338 len += 1;
340 return full_path + len;
343 static int init_root_path(struct btrfs_send *s, const char *subvol)
345 int ret = 0;
347 if (s->root_path)
348 goto out;
350 ret = find_mount_root(subvol, &s->root_path);
351 if (ret < 0) {
352 ret = -EINVAL;
353 fprintf(stderr, "ERROR: failed to determine mount point "
354 "for %s\n", subvol);
355 goto out;
358 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
359 if (s->mnt_fd < 0) {
360 ret = -errno;
361 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
362 strerror(-ret));
363 goto out;
366 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
367 if (ret < 0) {
368 fprintf(stderr, "ERROR: failed to initialize subvol search. "
369 "%s\n", strerror(-ret));
370 goto out;
373 out:
374 return ret;
378 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
380 int ret;
381 u64 flags;
382 int fd = -1;
384 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
385 if (fd < 0) {
386 ret = -errno;
387 fprintf(stderr, "ERROR: failed to open %s. %s\n",
388 subvol, strerror(-ret));
389 goto out;
392 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
393 if (ret < 0) {
394 ret = -errno;
395 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
396 "%s\n", strerror(-ret));
397 goto out;
400 if (flags & BTRFS_SUBVOL_RDONLY)
401 ret = 1;
402 else
403 ret = 0;
405 out:
406 if (fd != -1)
407 close(fd);
409 return ret;
412 int cmd_send(int argc, char **argv)
414 char *subvol = NULL;
415 int c;
416 int ret;
417 char *outname = NULL;
418 struct btrfs_send send;
419 u32 i;
420 char *mount_root = NULL;
421 char *snapshot_parent = NULL;
422 u64 root_id;
423 u64 parent_root_id = 0;
424 int full_send = 1;
425 int new_end_cmd_semantic = 0;
427 memset(&send, 0, sizeof(send));
428 send.dump_fd = fileno(stdout);
430 while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
431 switch (c) {
432 case 'v':
433 g_verbose++;
434 break;
435 case 'e':
436 new_end_cmd_semantic = 1;
437 break;
438 case 'c':
439 subvol = realpath(optarg, NULL);
440 if (!subvol) {
441 ret = -errno;
442 fprintf(stderr, "ERROR: realpath %s failed. "
443 "%s\n", optarg, strerror(-ret));
444 goto out;
447 ret = init_root_path(&send, subvol);
448 if (ret < 0)
449 goto out;
451 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
452 &root_id);
453 if (ret < 0) {
454 fprintf(stderr, "ERROR: could not resolve "
455 "root_id for %s\n", subvol);
456 goto out;
459 ret = is_subvol_ro(&send, subvol);
460 if (ret < 0)
461 goto out;
462 if (!ret) {
463 ret = -EINVAL;
464 fprintf(stderr,
465 "ERROR: cloned subvol %s is not read-only.\n",
466 subvol);
467 goto out;
470 add_clone_source(&send, root_id);
471 subvol_uuid_search_finit(&send.sus);
472 free(subvol);
473 subvol = NULL;
474 if (send.mnt_fd >= 0) {
475 close(send.mnt_fd);
476 send.mnt_fd = -1;
478 free(send.root_path);
479 send.root_path = NULL;
480 full_send = 0;
481 break;
482 case 'f':
483 outname = optarg;
484 break;
485 case 'p':
486 if (snapshot_parent) {
487 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
488 ret = 1;
489 goto out;
491 snapshot_parent = realpath(optarg, NULL);
492 if (!snapshot_parent) {
493 ret = -errno;
494 fprintf(stderr, "ERROR: realpath %s failed. "
495 "%s\n", optarg, strerror(-ret));
496 goto out;
499 ret = is_subvol_ro(&send, snapshot_parent);
500 if (ret < 0)
501 goto out;
502 if (!ret) {
503 ret = -EINVAL;
504 fprintf(stderr,
505 "ERROR: parent %s is not read-only.\n",
506 snapshot_parent);
507 goto out;
510 full_send = 0;
511 break;
512 case 'i':
513 fprintf(stderr,
514 "ERROR: -i was removed, use -c instead\n");
515 ret = 1;
516 goto out;
517 case '?':
518 default:
519 fprintf(stderr, "ERROR: send args invalid.\n");
520 ret = 1;
521 goto out;
525 if (optind == argc)
526 usage(cmd_send_usage);
528 if (outname != NULL) {
529 send.dump_fd = creat(outname, 0600);
530 if (send.dump_fd == -1) {
531 ret = -errno;
532 fprintf(stderr, "ERROR: can't create '%s': %s\n",
533 outname, strerror(-ret));
534 goto out;
538 if (isatty(send.dump_fd)) {
539 fprintf(stderr,
540 "ERROR: not dumping send stream into a terminal, "
541 "redirect it into a file\n");
542 ret = 1;
543 goto out;
546 /* use first send subvol to determine mount_root */
547 subvol = argv[optind];
549 subvol = realpath(argv[optind], NULL);
550 if (!subvol) {
551 ret = -errno;
552 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
553 goto out;
556 ret = init_root_path(&send, subvol);
557 if (ret < 0)
558 goto out;
560 if (snapshot_parent != NULL) {
561 ret = get_root_id(&send,
562 get_subvol_name(send.root_path, snapshot_parent),
563 &parent_root_id);
564 if (ret < 0) {
565 fprintf(stderr, "ERROR: could not resolve root_id "
566 "for %s\n", snapshot_parent);
567 goto out;
570 add_clone_source(&send, parent_root_id);
573 for (i = optind; i < argc; i++) {
574 free(subvol);
575 subvol = realpath(argv[i], NULL);
576 if (!subvol) {
577 ret = -errno;
578 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
579 goto out;
582 ret = find_mount_root(subvol, &mount_root);
583 if (ret < 0) {
584 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
585 "%s\n", subvol,
586 strerror(-ret));
587 goto out;
589 if (strcmp(send.root_path, mount_root) != 0) {
590 ret = -EINVAL;
591 fprintf(stderr, "ERROR: all subvols must be from the "
592 "same fs.\n");
593 goto out;
595 free(mount_root);
597 ret = is_subvol_ro(&send, subvol);
598 if (ret < 0)
599 goto out;
600 if (!ret) {
601 ret = -EINVAL;
602 fprintf(stderr, "ERROR: %s is not read-only.\n",
603 subvol);
604 goto out;
608 for (i = optind; i < argc; i++) {
609 int is_first_subvol;
610 int is_last_subvol;
612 free(subvol);
613 subvol = argv[i];
615 fprintf(stderr, "At subvol %s\n", subvol);
617 subvol = realpath(subvol, NULL);
618 if (!subvol) {
619 ret = -errno;
620 fprintf(stderr, "ERROR: realpath %s failed. "
621 "%s\n", argv[i], strerror(-ret));
622 goto out;
625 if (!full_send && !parent_root_id) {
626 ret = find_good_parent(&send, root_id, &parent_root_id);
627 if (ret < 0) {
628 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
629 root_id);
630 goto out;
634 ret = is_subvol_ro(&send, subvol);
635 if (ret < 0)
636 goto out;
637 if (!ret) {
638 ret = -EINVAL;
639 fprintf(stderr, "ERROR: %s is not read-only.\n",
640 subvol);
641 goto out;
644 if (new_end_cmd_semantic) {
645 /* require new kernel */
646 is_first_subvol = (i == optind);
647 is_last_subvol = (i == argc - 1);
648 } else {
649 /* be compatible to old and new kernel */
650 is_first_subvol = 1;
651 is_last_subvol = 1;
653 ret = do_send(&send, parent_root_id, is_first_subvol,
654 is_last_subvol, subvol);
655 if (ret < 0)
656 goto out;
658 /* done with this subvol, so add it to the clone sources */
659 add_clone_source(&send, root_id);
661 parent_root_id = 0;
662 full_send = 0;
665 ret = 0;
667 out:
668 free(subvol);
669 free(snapshot_parent);
670 free(send.clone_sources);
671 if (send.mnt_fd >= 0)
672 close(send.mnt_fd);
673 free(send.root_path);
674 subvol_uuid_search_finit(&send.sus);
675 return !!ret;
678 const char * const cmd_send_usage[] = {
679 "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
680 "Send the subvolume(s) to stdout.",
681 "Sends the subvolume(s) specified by <subvol> to stdout.",
682 "By default, this will send the whole subvolume. To do an incremental",
683 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
684 "any additional local snapshots, use '-c <clone-src>' (multiple times",
685 "where applicable). You must not specify clone sources unless you",
686 "guarantee that these snapshots are exactly in the same state on both",
687 "sides, the sender and the receiver. It is allowed to omit the",
688 "'-p <parent>' option when '-c <clone-src>' options are given, in",
689 "which case 'btrfs send' will determine a suitable parent among the",
690 "clone sources itself.",
691 "\n",
692 "-v Enable verbose debug output. Each occurrence of",
693 " this option increases the verbose level more.",
694 "-e If sending multiple subvols at once, use the new",
695 " format and omit the end-cmd between the subvols.",
696 "-p <parent> Send an incremental stream from <parent> to",
697 " <subvol>.",
698 "-c <clone-src> Use this snapshot as a clone source for an ",
699 " incremental send (multiple allowed)",
700 "-f <outfile> Output is normally written to stdout. To write to",
701 " a file, use this option. An alternative would be to",
702 " use pipes.",
703 NULL