btrfs-progs: Replace the overkill assert with normal error message.
[platform/upstream/btrfs-progs.git] / cmds-send.c
1 /*
2  * Copyright (C) 2012 Alexander Block.  All rights reserved.
3  *
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.
7  *
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.
12  *
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.
17  */
18
19 #define _GNU_SOURCE
20
21 #include "kerncompat.h"
22
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>
35
36 #include <uuid/uuid.h>
37
38 #include "ctree.h"
39 #include "ioctl.h"
40 #include "commands.h"
41 #include "list.h"
42 #include "utils.h"
43
44 #include "send.h"
45 #include "send-utils.h"
46
47 static int g_verbose = 0;
48
49 struct btrfs_send {
50         int send_fd;
51         int dump_fd;
52         int mnt_fd;
53
54         u64 *clone_sources;
55         u64 clone_sources_count;
56
57         char *root_path;
58         struct subvol_uuid_search sus;
59 };
60
61 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
62 {
63         struct subvol_info *si;
64
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;
73 }
74
75 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
76 {
77         struct subvol_info *si_tmp;
78         struct subvol_info *si;
79
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;
84
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;
90 }
91
92 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
93 {
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;
101
102         parent = get_parent(s, root_id);
103         if (!parent) {
104                 ret = -ENOENT;
105                 goto out;
106         }
107
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;
113                 }
114         }
115
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;
125                 }
126
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);
131
132                 assert(parent2);
133                 tmp = parent2->ctransid - parent->ctransid;
134                 if (tmp < 0)
135                         tmp *= -1;
136                 if (tmp < best_diff) {
137                         if (best_parent) {
138                                 free(best_parent->path);
139                                 free(best_parent);
140                         }
141                         best_parent = parent2;
142                         parent2 = NULL;
143                         best_diff = tmp;
144                 } else {
145                         free(parent2->path);
146                         free(parent2);
147                         parent2 = NULL;
148                 }
149         }
150
151         if (!best_parent) {
152                 ret = -ENOENT;
153                 goto out;
154         }
155
156 out_found:
157         *found = best_parent->root_id;
158         ret = 0;
159
160 out:
161         if (parent) {
162                 free(parent->path);
163                 free(parent);
164         }
165         if (best_parent) {
166                 free(best_parent->path);
167                 free(best_parent);
168         }
169         return ret;
170 }
171
172 static void add_clone_source(struct btrfs_send *s, u64 root_id)
173 {
174         s->clone_sources = realloc(s->clone_sources,
175                 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
176         s->clone_sources[s->clone_sources_count++] = root_id;
177 }
178
179 static int write_buf(int fd, const void *buf, int size)
180 {
181         int ret;
182         int pos = 0;
183
184         while (pos < size) {
185                 ret = write(fd, (char*)buf + pos, size - pos);
186                 if (ret < 0) {
187                         ret = -errno;
188                         fprintf(stderr, "ERROR: failed to dump stream. %s",
189                                         strerror(-ret));
190                         goto out;
191                 }
192                 if (!ret) {
193                         ret = -EIO;
194                         fprintf(stderr, "ERROR: failed to dump stream. %s",
195                                         strerror(-ret));
196                         goto out;
197                 }
198                 pos += ret;
199         }
200         ret = 0;
201
202 out:
203         return ret;
204 }
205
206 static void *dump_thread(void *arg_)
207 {
208         int ret;
209         struct btrfs_send *s = (struct btrfs_send*)arg_;
210         char buf[4096];
211         int readed;
212
213         while (1) {
214                 readed = read(s->send_fd, buf, sizeof(buf));
215                 if (readed < 0) {
216                         ret = -errno;
217                         fprintf(stderr, "ERROR: failed to read stream from "
218                                         "kernel. %s\n", strerror(-ret));
219                         goto out;
220                 }
221                 if (!readed) {
222                         ret = 0;
223                         goto out;
224                 }
225                 ret = write_buf(s->dump_fd, buf, readed);
226                 if (ret < 0)
227                         goto out;
228         }
229
230 out:
231         if (ret < 0) {
232                 exit(-ret);
233         }
234
235         return ERR_PTR(ret);
236 }
237
238 static int do_send(struct btrfs_send *send, u64 parent_root_id,
239                    int is_first_subvol, int is_last_subvol, char *subvol)
240 {
241         int ret;
242         pthread_t t_read;
243         struct btrfs_ioctl_send_args io_send;
244         void *t_err = NULL;
245         int subvol_fd = -1;
246         int pipefd[2] = {-1, -1};
247
248         subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
249         if (subvol_fd < 0) {
250                 ret = -errno;
251                 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
252                                 strerror(-ret));
253                 goto out;
254         }
255
256         ret = pipe(pipefd);
257         if (ret < 0) {
258                 ret = -errno;
259                 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
260                 goto out;
261         }
262
263         memset(&io_send, 0, sizeof(io_send));
264         io_send.send_fd = pipefd[1];
265         send->send_fd = pipefd[0];
266
267         if (!ret)
268                 ret = pthread_create(&t_read, NULL, dump_thread,
269                                         send);
270         if (ret) {
271                 ret = -ret;
272                 fprintf(stderr, "ERROR: thread setup failed: %s\n",
273                         strerror(-ret));
274                 goto out;
275         }
276
277         io_send.clone_sources = (__u64*)send->clone_sources;
278         io_send.clone_sources_count = send->clone_sources_count;
279         io_send.parent_root = parent_root_id;
280         if (!is_first_subvol)
281                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
282         if (!is_last_subvol)
283                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
284         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
285         if (ret) {
286                 ret = -errno;
287                 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
288                         strerror(-ret));
289                 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
290                         fprintf(stderr,
291                                 "Try upgrading your kernel or don't use -e.\n");
292                 goto out;
293         }
294         if (g_verbose > 0)
295                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
296
297         if (g_verbose > 0)
298                 fprintf(stderr, "joining genl thread\n");
299
300         close(pipefd[1]);
301         pipefd[1] = -1;
302
303         ret = pthread_join(t_read, &t_err);
304         if (ret) {
305                 ret = -ret;
306                 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
307                         strerror(-ret));
308                 goto out;
309         }
310         if (t_err) {
311                 ret = (long int)t_err;
312                 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
313                         "(%s)\n", (long int)t_err, strerror(-ret));
314                 goto out;
315         }
316
317         ret = 0;
318
319 out:
320         if (subvol_fd != -1)
321                 close(subvol_fd);
322         if (pipefd[0] != -1)
323                 close(pipefd[0]);
324         if (pipefd[1] != -1)
325                 close(pipefd[1]);
326         return ret;
327 }
328
329 char *get_subvol_name(char *mnt, char *full_path)
330 {
331         int len = strlen(mnt);
332         if (!len)
333                 return full_path;
334         if (mnt[len - 1] != '/')
335                 len += 1;
336
337         return full_path + len;
338 }
339
340 static int init_root_path(struct btrfs_send *s, const char *subvol)
341 {
342         int ret = 0;
343
344         if (s->root_path)
345                 goto out;
346
347         ret = find_mount_root(subvol, &s->root_path);
348         if (ret < 0) {
349                 ret = -EINVAL;
350                 fprintf(stderr, "ERROR: failed to determine mount point "
351                                 "for %s\n", subvol);
352                 goto out;
353         }
354
355         s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
356         if (s->mnt_fd < 0) {
357                 ret = -errno;
358                 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
359                         strerror(-ret));
360                 goto out;
361         }
362
363         ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
364         if (ret < 0) {
365                 fprintf(stderr, "ERROR: failed to initialize subvol search. "
366                                 "%s\n", strerror(-ret));
367                 goto out;
368         }
369
370 out:
371         return ret;
372
373 }
374
375 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
376 {
377         int ret;
378         u64 flags;
379         int fd = -1;
380
381         fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
382         if (fd < 0) {
383                 ret = -errno;
384                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
385                                 subvol, strerror(-ret));
386                 goto out;
387         }
388
389         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
390         if (ret < 0) {
391                 ret = -errno;
392                 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
393                                 "%s\n", strerror(-ret));
394                 goto out;
395         }
396
397         if (flags & BTRFS_SUBVOL_RDONLY)
398                 ret = 1;
399         else
400                 ret = 0;
401
402 out:
403         if (fd != -1)
404                 close(fd);
405
406         return ret;
407 }
408
409 int cmd_send(int argc, char **argv)
410 {
411         char *subvol = NULL;
412         int c;
413         int ret;
414         char *outname = NULL;
415         struct btrfs_send send;
416         u32 i;
417         char *mount_root = NULL;
418         char *snapshot_parent = NULL;
419         u64 root_id;
420         u64 parent_root_id = 0;
421         int full_send = 1;
422         int new_end_cmd_semantic = 0;
423
424         memset(&send, 0, sizeof(send));
425         send.dump_fd = fileno(stdout);
426
427         while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
428                 switch (c) {
429                 case 'v':
430                         g_verbose++;
431                         break;
432                 case 'e':
433                         new_end_cmd_semantic = 1;
434                         break;
435                 case 'c':
436                         subvol = realpath(optarg, NULL);
437                         if (!subvol) {
438                                 ret = -errno;
439                                 fprintf(stderr, "ERROR: realpath %s failed. "
440                                                 "%s\n", optarg, strerror(-ret));
441                                 goto out;
442                         }
443
444                         ret = init_root_path(&send, subvol);
445                         if (ret < 0)
446                                 goto out;
447
448                         ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
449                                         &root_id);
450                         if (ret < 0) {
451                                 fprintf(stderr, "ERROR: could not resolve "
452                                                 "root_id for %s\n", subvol);
453                                 goto out;
454                         }
455
456                         ret = is_subvol_ro(&send, subvol);
457                         if (ret < 0)
458                                 goto out;
459                         if (!ret) {
460                                 ret = -EINVAL;
461                                 fprintf(stderr,
462                                 "ERROR: cloned subvol %s is not read-only.\n",
463                                         subvol);
464                                 goto out;
465                         }
466
467                         add_clone_source(&send, root_id);
468                         subvol_uuid_search_finit(&send.sus);
469                         free(subvol);
470                         subvol = NULL;
471                         if (send.mnt_fd >= 0) {
472                                 close(send.mnt_fd);
473                                 send.mnt_fd = -1;
474                         }
475                         free(send.root_path);
476                         send.root_path = NULL;
477                         full_send = 0;
478                         break;
479                 case 'f':
480                         outname = optarg;
481                         break;
482                 case 'p':
483                         if (snapshot_parent) {
484                                 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
485                                 ret = 1;
486                                 goto out;
487                         }
488                         snapshot_parent = realpath(optarg, NULL);
489                         if (!snapshot_parent) {
490                                 ret = -errno;
491                                 fprintf(stderr, "ERROR: realpath %s failed. "
492                                                 "%s\n", optarg, strerror(-ret));
493                                 goto out;
494                         }
495
496                         ret = is_subvol_ro(&send, snapshot_parent);
497                         if (ret < 0)
498                                 goto out;
499                         if (!ret) {
500                                 ret = -EINVAL;
501                                 fprintf(stderr,
502                                         "ERROR: parent %s is not read-only.\n",
503                                         snapshot_parent);
504                                 goto out;
505                         }
506
507                         full_send = 0;
508                         break;
509                 case 'i':
510                         fprintf(stderr,
511                                 "ERROR: -i was removed, use -c instead\n");
512                         ret = 1;
513                         goto out;
514                 case '?':
515                 default:
516                         fprintf(stderr, "ERROR: send args invalid.\n");
517                         ret = 1;
518                         goto out;
519                 }
520         }
521
522         if (optind == argc)
523                 usage(cmd_send_usage);
524
525         if (outname != NULL) {
526                 send.dump_fd = creat(outname, 0600);
527                 if (send.dump_fd == -1) {
528                         ret = -errno;
529                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
530                                         outname, strerror(-ret));
531                         goto out;
532                 }
533         }
534
535         if (isatty(send.dump_fd)) {
536                 fprintf(stderr, 
537                         "ERROR: not dumping send stream into a terminal, "
538                         "redirect it into a file\n");
539                 ret = 1;
540                 goto out;
541         }
542
543         /* use first send subvol to determine mount_root */
544         subvol = argv[optind];
545
546         subvol = realpath(argv[optind], NULL);
547         if (!subvol) {
548                 ret = -errno;
549                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
550                 goto out;
551         }
552
553         ret = init_root_path(&send, subvol);
554         if (ret < 0)
555                 goto out;
556
557         if (snapshot_parent != NULL) {
558                 ret = get_root_id(&send,
559                                 get_subvol_name(send.root_path, snapshot_parent),
560                                 &parent_root_id);
561                 if (ret < 0) {
562                         fprintf(stderr, "ERROR: could not resolve root_id "
563                                         "for %s\n", snapshot_parent);
564                         goto out;
565                 }
566
567                 add_clone_source(&send, parent_root_id);
568         }
569
570         for (i = optind; i < argc; i++) {
571                 free(subvol);
572                 subvol = realpath(argv[i], NULL);
573                 if (!subvol) {
574                         ret = -errno;
575                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
576                         goto out;
577                 }
578
579                 ret = find_mount_root(subvol, &mount_root);
580                 if (ret < 0) {
581                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
582                                         "%s\n", subvol,
583                                 strerror(-ret));
584                         goto out;
585                 }
586                 if (strcmp(send.root_path, mount_root) != 0) {
587                         ret = -EINVAL;
588                         fprintf(stderr, "ERROR: all subvols must be from the "
589                                         "same fs.\n");
590                         goto out;
591                 }
592                 free(mount_root);
593
594                 ret = is_subvol_ro(&send, subvol);
595                 if (ret < 0)
596                         goto out;
597                 if (!ret) {
598                         ret = -EINVAL;
599                         fprintf(stderr, "ERROR: %s is not read-only.\n",
600                                         subvol);
601                         goto out;
602                 }
603         }
604
605         for (i = optind; i < argc; i++) {
606                 int is_first_subvol;
607                 int is_last_subvol;
608
609                 free(subvol);
610                 subvol = argv[i];
611
612                 fprintf(stderr, "At subvol %s\n", subvol);
613
614                 subvol = realpath(subvol, NULL);
615                 if (!subvol) {
616                         ret = -errno;
617                         fprintf(stderr, "ERROR: realpath %s failed. "
618                                         "%s\n", argv[i], strerror(-ret));
619                         goto out;
620                 }
621
622                 if (!full_send && !parent_root_id) {
623                         ret = find_good_parent(&send, root_id, &parent_root_id);
624                         if (ret < 0) {
625                                 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
626                                         root_id);
627                                 goto out;
628                         }
629                 }
630
631                 ret = is_subvol_ro(&send, subvol);
632                 if (ret < 0)
633                         goto out;
634                 if (!ret) {
635                         ret = -EINVAL;
636                         fprintf(stderr, "ERROR: %s is not read-only.\n",
637                                         subvol);
638                         goto out;
639                 }
640
641                 if (new_end_cmd_semantic) {
642                         /* require new kernel */
643                         is_first_subvol = (i == optind);
644                         is_last_subvol = (i == argc - 1);
645                 } else {
646                         /* be compatible to old and new kernel */
647                         is_first_subvol = 1;
648                         is_last_subvol = 1;
649                 }
650                 ret = do_send(&send, parent_root_id, is_first_subvol,
651                               is_last_subvol, subvol);
652                 if (ret < 0)
653                         goto out;
654
655                 /* done with this subvol, so add it to the clone sources */
656                 add_clone_source(&send, root_id);
657
658                 parent_root_id = 0;
659                 full_send = 0;
660         }
661
662         ret = 0;
663
664 out:
665         free(subvol);
666         free(snapshot_parent);
667         free(send.clone_sources);
668         if (send.mnt_fd >= 0)
669                 close(send.mnt_fd);
670         free(send.root_path);
671         subvol_uuid_search_finit(&send.sus);
672         return !!ret;
673 }
674
675 const char * const cmd_send_usage[] = {
676         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
677         "Send the subvolume(s) to stdout.",
678         "Sends the subvolume(s) specified by <subvol> to stdout.",
679         "By default, this will send the whole subvolume. To do an incremental",
680         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
681         "any additional local snapshots, use '-c <clone-src>' (multiple times",
682         "where applicable). You must not specify clone sources unless you",
683         "guarantee that these snapshots are exactly in the same state on both",
684         "sides, the sender and the receiver. It is allowed to omit the",
685         "'-p <parent>' option when '-c <clone-src>' options are given, in",
686         "which case 'btrfs send' will determine a suitable parent among the",
687         "clone sources itself.",
688         "\n",
689         "-v               Enable verbose debug output. Each occurrence of",
690         "                 this option increases the verbose level more.",
691         "-e               If sending multiple subvols at once, use the new",
692         "                 format and omit the end-cmd between the subvols.",
693         "-p <parent>      Send an incremental stream from <parent> to",
694         "                 <subvol>.",
695         "-c <clone-src>   Use this snapshot as a clone source for an ",
696         "                 incremental send (multiple allowed)",
697         "-f <outfile>     Output is normally written to stdout. To write to",
698         "                 a file, use this option. An alternative would be to",
699         "                 use pipes.",
700         NULL
701 };