Merge branch 'for-chris' of git://repo.or.cz/btrfs-progs-unstable/devel into raid56
[btrfs-progs-unstable/devel.git] / cmds-send.c
blob69e9bcea511cb3be21fb6f9f64adb3a51ad2414d
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>
35 #include <uuid/uuid.h>
37 #include "ctree.h"
38 #include "ioctl.h"
39 #include "commands.h"
40 #include "list.h"
42 #include "send.h"
43 #include "send-utils.h"
45 static int g_verbose = 0;
47 struct btrfs_send {
48 int send_fd;
49 int dump_fd;
50 int mnt_fd;
52 u64 *clone_sources;
53 u64 clone_sources_count;
55 char *root_path;
56 struct subvol_uuid_search sus;
59 int find_mount_root(const char *path, char **mount_root)
61 FILE *mnttab;
62 int fd;
63 struct mntent *ent;
64 int len;
65 int longest_matchlen = 0;
66 char *longest_match = NULL;
68 fd = open(path, O_RDONLY | O_NOATIME);
69 if (fd < 0)
70 return -errno;
71 close(fd);
73 mnttab = fopen("/etc/mtab", "r");
74 while ((ent = getmntent(mnttab))) {
75 len = strlen(ent->mnt_dir);
76 if (strncmp(ent->mnt_dir, path, len) == 0) {
77 /* match found */
78 if (longest_matchlen < len) {
79 free(longest_match);
80 longest_matchlen = len;
81 longest_match = strdup(ent->mnt_dir);
86 *mount_root = realpath(longest_match, NULL);
87 free(longest_match);
89 return 0;
92 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
94 struct subvol_info *si;
96 si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
97 subvol_search_by_path);
98 if (!si)
99 return -ENOENT;
100 *root_id = si->root_id;
101 return 0;
104 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
106 struct subvol_info *si;
108 si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
109 subvol_search_by_root_id);
110 if (!si)
111 return NULL;
113 si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
114 subvol_search_by_uuid);
115 if (!si)
116 return NULL;
117 return si;
120 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
122 int ret;
123 struct subvol_info *parent;
124 struct subvol_info *parent2;
125 struct subvol_info *best_parent = NULL;
126 __s64 tmp;
127 u64 best_diff = (u64)-1;
128 int i;
130 parent = get_parent(s, root_id);
131 if (!parent) {
132 ret = -ENOENT;
133 goto out;
136 for (i = 0; i < s->clone_sources_count; i++) {
137 if (s->clone_sources[i] == parent->root_id) {
138 best_parent = parent;
139 goto out_found;
143 for (i = 0; i < s->clone_sources_count; i++) {
144 parent2 = get_parent(s, s->clone_sources[i]);
145 if (parent2 != parent)
146 continue;
148 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
149 0, NULL, subvol_search_by_root_id);
151 tmp = parent2->ctransid - parent->ctransid;
152 if (tmp < 0)
153 tmp *= -1;
154 if (tmp < best_diff) {
155 best_parent = parent2;
156 best_diff = tmp;
160 if (!best_parent) {
161 ret = -ENOENT;
162 goto out;
165 out_found:
166 *found = best_parent->root_id;
167 ret = 0;
169 out:
170 return ret;
173 static void add_clone_source(struct btrfs_send *s, u64 root_id)
175 s->clone_sources = realloc(s->clone_sources,
176 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
177 s->clone_sources[s->clone_sources_count++] = root_id;
180 static int write_buf(int fd, const void *buf, int size)
182 int ret;
183 int pos = 0;
185 while (pos < size) {
186 ret = write(fd, (char*)buf + pos, size - pos);
187 if (ret < 0) {
188 ret = -errno;
189 fprintf(stderr, "ERROR: failed to dump stream. %s",
190 strerror(-ret));
191 goto out;
193 if (!ret) {
194 ret = -EIO;
195 fprintf(stderr, "ERROR: failed to dump stream. %s",
196 strerror(-ret));
197 goto out;
199 pos += ret;
201 ret = 0;
203 out:
204 return ret;
207 static void *dump_thread(void *arg_)
209 int ret;
210 struct btrfs_send *s = (struct btrfs_send*)arg_;
211 char buf[4096];
212 int readed;
214 while (1) {
215 readed = read(s->send_fd, buf, sizeof(buf));
216 if (readed < 0) {
217 ret = -errno;
218 fprintf(stderr, "ERROR: failed to read stream from "
219 "kernel. %s\n", strerror(-ret));
220 goto out;
222 if (!readed) {
223 ret = 0;
224 goto out;
226 ret = write_buf(s->dump_fd, buf, readed);
227 if (ret < 0)
228 goto out;
231 out:
232 if (ret < 0) {
233 exit(-ret);
236 return ERR_PTR(ret);
239 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root_id)
241 int ret;
242 pthread_t t_read;
243 pthread_attr_t t_attr;
244 struct btrfs_ioctl_send_args io_send;
245 struct subvol_info *si;
246 void *t_err = NULL;
247 int subvol_fd = -1;
248 int pipefd[2];
250 si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
251 subvol_search_by_root_id);
252 if (!si) {
253 ret = -ENOENT;
254 fprintf(stderr, "ERROR: could not find subvol info for %llu",
255 root_id);
256 goto out;
259 subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
260 if (subvol_fd < 0) {
261 ret = -errno;
262 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
263 strerror(-ret));
264 goto out;
267 ret = pthread_attr_init(&t_attr);
269 ret = pipe(pipefd);
270 if (ret < 0) {
271 ret = -errno;
272 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
273 goto out;
276 memset(&io_send, 0, sizeof(io_send));
277 io_send.send_fd = pipefd[1];
278 send->send_fd = pipefd[0];
280 if (!ret)
281 ret = pthread_create(&t_read, &t_attr, dump_thread,
282 send);
283 if (ret) {
284 ret = -ret;
285 fprintf(stderr, "ERROR: thread setup failed: %s\n",
286 strerror(-ret));
287 goto out;
290 io_send.clone_sources = (__u64*)send->clone_sources;
291 io_send.clone_sources_count = send->clone_sources_count;
292 io_send.parent_root = parent_root_id;
293 ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
294 if (ret) {
295 ret = -errno;
296 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
297 strerror(-ret));
298 goto out;
300 if (g_verbose > 0)
301 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
303 if (g_verbose > 0)
304 fprintf(stderr, "joining genl thread\n");
306 close(pipefd[1]);
307 pipefd[1] = 0;
309 ret = pthread_join(t_read, &t_err);
310 if (ret) {
311 ret = -ret;
312 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
313 strerror(-ret));
314 goto out;
316 if (t_err) {
317 ret = (long int)t_err;
318 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
319 "(%s)\n", (long int)t_err, strerror(-ret));
320 goto out;
323 pthread_attr_destroy(&t_attr);
325 ret = 0;
327 out:
328 if (subvol_fd != -1)
329 close(subvol_fd);
330 if (pipefd[0])
331 close(pipefd[0]);
332 if (pipefd[1])
333 close(pipefd[1]);
334 return ret;
337 char *get_subvol_name(char *mnt, char *full_path)
339 int len = strlen(mnt);
340 if (!len)
341 return full_path;
342 if (mnt[len - 1] != '/')
343 len += 1;
345 return full_path + len;
348 static int init_root_path(struct btrfs_send *s, const char *subvol)
350 int ret = 0;
352 if (s->root_path)
353 goto out;
355 ret = find_mount_root(subvol, &s->root_path);
356 if (ret < 0) {
357 ret = -EINVAL;
358 fprintf(stderr, "ERROR: failed to determine mount point "
359 "for %s\n", subvol);
360 goto out;
363 s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
364 if (s->mnt_fd < 0) {
365 ret = -errno;
366 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
367 strerror(-ret));
368 goto out;
371 ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
372 if (ret < 0) {
373 fprintf(stderr, "ERROR: failed to initialize subvol search. "
374 "%s\n", strerror(-ret));
375 goto out;
378 out:
379 return ret;
383 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
385 int ret;
386 u64 flags;
387 int fd = -1;
389 fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
390 if (fd < 0) {
391 ret = -errno;
392 fprintf(stderr, "ERROR: failed to open %s. %s\n",
393 subvol, strerror(-ret));
394 goto out;
397 ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
398 if (ret < 0) {
399 ret = -errno;
400 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
401 "%s\n", strerror(-ret));
402 goto out;
405 if (flags & BTRFS_SUBVOL_RDONLY)
406 ret = 1;
407 else
408 ret = 0;
410 out:
411 if (fd != -1)
412 close(fd);
414 return ret;
417 int cmd_send_start(int argc, char **argv)
419 char *subvol = NULL;
420 int c;
421 int ret;
422 char *outname = NULL;
423 struct btrfs_send send;
424 u32 i;
425 char *mount_root = NULL;
426 char *snapshot_parent = NULL;
427 u64 root_id;
428 u64 parent_root_id = 0;
429 int full_send = 1;
431 memset(&send, 0, sizeof(send));
432 send.dump_fd = fileno(stdout);
434 while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
435 switch (c) {
436 case 'v':
437 g_verbose++;
438 break;
439 case 'c':
440 subvol = realpath(optarg, NULL);
441 if (!subvol) {
442 ret = -errno;
443 fprintf(stderr, "ERROR: realpath %s failed. "
444 "%s\n", optarg, strerror(-ret));
445 goto out;
448 ret = init_root_path(&send, subvol);
449 if (ret < 0)
450 goto out;
452 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
453 &root_id);
454 if (ret < 0) {
455 fprintf(stderr, "ERROR: could not resolve "
456 "root_id for %s\n", subvol);
457 goto out;
459 add_clone_source(&send, root_id);
460 free(subvol);
461 full_send = 0;
462 break;
463 case 'f':
464 outname = optarg;
465 break;
466 case 'p':
467 if (snapshot_parent) {
468 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
469 return 1;
471 snapshot_parent = realpath(optarg, NULL);
472 if (!snapshot_parent) {
473 ret = -errno;
474 fprintf(stderr, "ERROR: realpath %s failed. "
475 "%s\n", optarg, strerror(-ret));
476 goto out;
478 full_send = 0;
479 break;
480 case 'i':
481 fprintf(stderr,
482 "ERROR: -i was removed, use -c instead\n");
483 return 1;
484 case '?':
485 default:
486 fprintf(stderr, "ERROR: send args invalid.\n");
487 return 1;
491 if (optind == argc) {
492 fprintf(stderr, "ERROR: send needs path to snapshot\n");
493 return 1;
496 if (outname != NULL) {
497 send.dump_fd = creat(outname, 0600);
498 if (send.dump_fd == -1) {
499 ret = -errno;
500 fprintf(stderr, "ERROR: can't create '%s': %s\n",
501 outname, strerror(-ret));
502 goto out;
506 if (isatty(send.dump_fd)) {
507 fprintf(stderr,
508 "ERROR: not dumping send stream into a terminal, "
509 "redirect it into a file\n");
510 return 1;
513 /* use first send subvol to determine mount_root */
514 subvol = argv[optind];
516 subvol = realpath(argv[optind], NULL);
517 if (!subvol) {
518 ret = -errno;
519 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
520 goto out;
523 ret = init_root_path(&send, subvol);
524 if (ret < 0)
525 goto out;
527 if (snapshot_parent != NULL) {
528 ret = get_root_id(&send,
529 get_subvol_name(send.root_path, snapshot_parent),
530 &parent_root_id);
531 if (ret < 0) {
532 fprintf(stderr, "ERROR: could not resolve root_id "
533 "for %s\n", snapshot_parent);
534 goto out;
537 add_clone_source(&send, parent_root_id);
540 for (i = optind; i < argc; i++) {
541 subvol = realpath(argv[i], NULL);
542 if (!subvol) {
543 ret = -errno;
544 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
545 goto out;
548 ret = find_mount_root(subvol, &mount_root);
549 if (ret < 0) {
550 fprintf(stderr, "ERROR: find_mount_root failed on %s: "
551 "%s\n", subvol,
552 strerror(-ret));
553 goto out;
555 if (strcmp(send.root_path, mount_root) != 0) {
556 ret = -EINVAL;
557 fprintf(stderr, "ERROR: all subvols must be from the "
558 "same fs.\n");
559 goto out;
561 free(mount_root);
563 ret = is_subvol_ro(&send, subvol);
564 if (ret < 0)
565 goto out;
566 if (!ret) {
567 ret = -EINVAL;
568 fprintf(stderr, "ERROR: %s is not read-only.\n",
569 subvol);
570 goto out;
572 free(subvol);
575 for (i = optind; i < argc; i++) {
576 subvol = argv[i];
578 fprintf(stderr, "At subvol %s\n", subvol);
580 subvol = realpath(subvol, NULL);
581 if (!subvol) {
582 ret = -errno;
583 fprintf(stderr, "ERROR: realpath %s failed. "
584 "%s\n", argv[i], strerror(-ret));
585 goto out;
588 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
589 &root_id);
590 if (ret < 0) {
591 fprintf(stderr, "ERROR: could not resolve root_id "
592 "for %s\n", subvol);
593 goto out;
596 if (!full_send && !parent_root_id) {
597 ret = find_good_parent(&send, root_id, &parent_root_id);
598 if (ret < 0) {
599 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
600 root_id);
601 goto out;
605 ret = is_subvol_ro(&send, subvol);
606 if (ret < 0)
607 goto out;
608 if (!ret) {
609 ret = -EINVAL;
610 fprintf(stderr, "ERROR: %s is not read-only.\n",
611 subvol);
612 goto out;
615 ret = do_send(&send, root_id, parent_root_id);
616 if (ret < 0)
617 goto out;
619 /* done with this subvol, so add it to the clone sources */
620 add_clone_source(&send, root_id);
622 parent_root_id = 0;
623 full_send = 0;
624 free(subvol);
627 ret = 0;
629 out:
630 if (send.mnt_fd >= 0)
631 close(send.mnt_fd);
632 return ret;
635 static const char * const send_cmd_group_usage[] = {
636 "btrfs send <command> <args>",
637 NULL
640 const char * const cmd_send_usage[] = {
641 "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
642 "Send the subvolume to stdout.",
643 "Sends the subvolume specified by <subvol> to stdout.",
644 "By default, this will send the whole subvolume. To do an incremental",
645 "send, use '-p <parent>'. If you want to allow btrfs to clone from",
646 "any additional local snapshots, use -c <clone-src> (multiple times",
647 "where applicable). You must not specify clone sources unless you",
648 "guarantee that these snapshots are exactly in the same state on both",
649 "sides, the sender and the receiver. It is allowed to omit the",
650 "'-p <parent>' option when '-c <clone-src>' options are given, in",
651 "which case 'btrfs send' will determine a suitable parent among the",
652 "clone sources itself.",
653 "\n",
654 "-v Enable verbose debug output. Each occurrence of",
655 " this option increases the verbose level more.",
656 "-p <parent> Send an incremental stream from <parent> to",
657 " <subvol>.",
658 "-c <clone-src> Use this snapshot as a clone source for an ",
659 " incremental send (multiple allowed)",
660 "-f <outfile> Output is normally written to stdout. To write to",
661 " a file, use this option. An alternative would be to",
662 " use pipes.",
663 NULL
666 int cmd_send(int argc, char **argv)
668 return cmd_send_start(argc, argv);