btrfs-progs: mute coverity warnings about deadcode
[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
20 #include "kerncompat.h"
21
22 #include <unistd.h>
23 #include <stdint.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <pthread.h>
27 #include <math.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <libgen.h>
32 #include <mntent.h>
33 #include <assert.h>
34 #include <getopt.h>
35 #include <uuid/uuid.h>
36 #include <limits.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                 if (!parent2) {
133                         ret = -ENOENT;
134                         goto out;
135                 }
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);
143                         }
144                         best_parent = parent2;
145                         parent2 = NULL;
146                         best_diff = tmp;
147                 } else {
148                         free(parent2->path);
149                         free(parent2);
150                         parent2 = NULL;
151                 }
152         }
153
154         if (!best_parent) {
155                 ret = -ENOENT;
156                 goto out;
157         }
158
159 out_found:
160         *found = best_parent->root_id;
161         ret = 0;
162
163 out:
164         if (parent) {
165                 free(parent->path);
166                 free(parent);
167         }
168         if (best_parent) {
169                 free(best_parent->path);
170                 free(best_parent);
171         }
172         return ret;
173 }
174
175 static int add_clone_source(struct btrfs_send *s, u64 root_id)
176 {
177         void *tmp;
178
179         tmp = s->clone_sources;
180         s->clone_sources = realloc(s->clone_sources,
181                 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
182
183         if (!s->clone_sources) {
184                 free(tmp);
185                 return -ENOMEM;
186         }
187         s->clone_sources[s->clone_sources_count++] = root_id;
188
189         return 0;
190 }
191
192 static int write_buf(int fd, const void *buf, int size)
193 {
194         int ret;
195         int pos = 0;
196
197         while (pos < size) {
198                 ret = write(fd, (char*)buf + pos, size - pos);
199                 if (ret < 0) {
200                         ret = -errno;
201                         fprintf(stderr, "ERROR: failed to dump stream. %s\n",
202                                         strerror(-ret));
203                         goto out;
204                 }
205                 if (!ret) {
206                         ret = -EIO;
207                         fprintf(stderr, "ERROR: failed to dump stream. %s\n",
208                                         strerror(-ret));
209                         goto out;
210                 }
211                 pos += ret;
212         }
213         ret = 0;
214
215 out:
216         return ret;
217 }
218
219 static void *dump_thread(void *arg_)
220 {
221         int ret;
222         struct btrfs_send *s = (struct btrfs_send*)arg_;
223         char buf[4096];
224         int readed;
225
226         while (1) {
227                 readed = read(s->send_fd, buf, sizeof(buf));
228                 if (readed < 0) {
229                         ret = -errno;
230                         fprintf(stderr, "ERROR: failed to read stream from "
231                                         "kernel. %s\n", strerror(-ret));
232                         goto out;
233                 }
234                 if (!readed) {
235                         ret = 0;
236                         goto out;
237                 }
238                 ret = write_buf(s->dump_fd, buf, readed);
239                 if (ret < 0)
240                         goto out;
241         }
242
243 out:
244         if (ret < 0) {
245                 exit(-ret);
246         }
247
248         return ERR_PTR(ret);
249 }
250
251 static int do_send(struct btrfs_send *send, u64 parent_root_id,
252                    int is_first_subvol, int is_last_subvol, char *subvol,
253                    u64 flags)
254 {
255         int ret;
256         pthread_t t_read;
257         struct btrfs_ioctl_send_args io_send;
258         void *t_err = NULL;
259         int subvol_fd = -1;
260         int pipefd[2] = {-1, -1};
261
262         subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
263         if (subvol_fd < 0) {
264                 ret = -errno;
265                 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
266                                 strerror(-ret));
267                 goto out;
268         }
269
270         ret = pipe(pipefd);
271         if (ret < 0) {
272                 ret = -errno;
273                 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
274                 goto out;
275         }
276
277         memset(&io_send, 0, sizeof(io_send));
278         io_send.send_fd = pipefd[1];
279         send->send_fd = pipefd[0];
280
281         if (!ret)
282                 ret = pthread_create(&t_read, NULL, dump_thread,
283                                         send);
284         if (ret) {
285                 ret = -ret;
286                 fprintf(stderr, "ERROR: thread setup failed: %s\n",
287                         strerror(-ret));
288                 goto out;
289         }
290
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;
297         if (!is_last_subvol)
298                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
299         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
300         if (ret) {
301                 ret = -errno;
302                 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
303                         strerror(-ret));
304                 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
305                         fprintf(stderr,
306                                 "Try upgrading your kernel or don't use -e.\n");
307                 goto out;
308         }
309         if (g_verbose > 0)
310                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
311
312         if (g_verbose > 0)
313                 fprintf(stderr, "joining genl thread\n");
314
315         close(pipefd[1]);
316         pipefd[1] = -1;
317
318         ret = pthread_join(t_read, &t_err);
319         if (ret) {
320                 ret = -ret;
321                 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
322                         strerror(-ret));
323                 goto out;
324         }
325         if (t_err) {
326                 ret = (long int)t_err;
327                 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
328                         "(%s)\n", (long int)t_err, strerror(-ret));
329                 goto out;
330         }
331
332         ret = 0;
333
334 out:
335         if (subvol_fd != -1)
336                 close(subvol_fd);
337         if (pipefd[0] != -1)
338                 close(pipefd[0]);
339         if (pipefd[1] != -1)
340                 close(pipefd[1]);
341         return ret;
342 }
343
344 char *get_subvol_name(char *mnt, char *full_path)
345 {
346         int len = strlen(mnt);
347         if (!len)
348                 return full_path;
349         if (mnt[len - 1] != '/')
350                 len += 1;
351
352         return full_path + len;
353 }
354
355 static int init_root_path(struct btrfs_send *s, const char *subvol)
356 {
357         int ret = 0;
358
359         if (s->root_path)
360                 goto out;
361
362         ret = find_mount_root(subvol, &s->root_path);
363         if (ret < 0) {
364                 fprintf(stderr,
365                         "ERROR: failed to determine mount point for %s: %s\n",
366                         subvol, strerror(-ret));
367                 ret = -EINVAL;
368                 goto out;
369         }
370         if (ret > 0) {
371                 fprintf(stderr,
372                         "ERROR: %s doesn't belong to btrfs mount point\n",
373                         subvol);
374                 ret = -EINVAL;
375                 goto out;
376         }
377
378         s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
379         if (s->mnt_fd < 0) {
380                 ret = -errno;
381                 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
382                         strerror(-ret));
383                 goto out;
384         }
385
386         ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
387         if (ret < 0) {
388                 fprintf(stderr, "ERROR: failed to initialize subvol search. "
389                                 "%s\n", strerror(-ret));
390                 goto out;
391         }
392
393 out:
394         return ret;
395
396 }
397
398 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
399 {
400         int ret;
401         u64 flags;
402         int fd = -1;
403
404         fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
405         if (fd < 0) {
406                 ret = -errno;
407                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
408                                 subvol, strerror(-ret));
409                 goto out;
410         }
411
412         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
413         if (ret < 0) {
414                 ret = -errno;
415                 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
416                                 "%s\n", strerror(-ret));
417                 goto out;
418         }
419
420         if (flags & BTRFS_SUBVOL_RDONLY)
421                 ret = 1;
422         else
423                 ret = 0;
424
425 out:
426         if (fd != -1)
427                 close(fd);
428
429         return ret;
430 }
431
432 int cmd_send(int argc, char **argv)
433 {
434         char *subvol = NULL;
435         int ret;
436         char outname[PATH_MAX];
437         struct btrfs_send send;
438         u32 i;
439         char *mount_root = NULL;
440         char *snapshot_parent = NULL;
441         u64 root_id = 0;
442         u64 parent_root_id = 0;
443         int full_send = 1;
444         int new_end_cmd_semantic = 0;
445         u64 send_flags = 0;
446
447         memset(&send, 0, sizeof(send));
448         send.dump_fd = fileno(stdout);
449         outname[0] = 0;
450
451         while (1) {
452                 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
453                 static const struct option long_options[] = {
454                         { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
455                 };
456                 int c = getopt_long(argc, argv, "vec:f:i:p:", long_options, NULL);
457
458                 if (c < 0)
459                         break;
460
461                 switch (c) {
462                 case 'v':
463                         g_verbose++;
464                         break;
465                 case 'e':
466                         new_end_cmd_semantic = 1;
467                         break;
468                 case 'c':
469                         subvol = realpath(optarg, NULL);
470                         if (!subvol) {
471                                 ret = -errno;
472                                 fprintf(stderr, "ERROR: realpath %s failed. "
473                                                 "%s\n", optarg, strerror(-ret));
474                                 goto out;
475                         }
476
477                         ret = init_root_path(&send, subvol);
478                         if (ret < 0)
479                                 goto out;
480
481                         ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
482                                         &root_id);
483                         if (ret < 0) {
484                                 fprintf(stderr, "ERROR: could not resolve "
485                                                 "root_id for %s\n", subvol);
486                                 goto out;
487                         }
488
489                         ret = is_subvol_ro(&send, subvol);
490                         if (ret < 0)
491                                 goto out;
492                         if (!ret) {
493                                 ret = -EINVAL;
494                                 fprintf(stderr,
495                                 "ERROR: cloned subvol %s is not read-only.\n",
496                                         subvol);
497                                 goto out;
498                         }
499
500                         ret = add_clone_source(&send, root_id);
501                         if (ret < 0) {
502                                 fprintf(stderr, "ERROR: not enough memory\n");
503                                 goto out;
504                         }
505                         subvol_uuid_search_finit(&send.sus);
506                         free(subvol);
507                         subvol = NULL;
508                         if (send.mnt_fd >= 0) {
509                                 close(send.mnt_fd);
510                                 send.mnt_fd = -1;
511                         }
512                         free(send.root_path);
513                         send.root_path = NULL;
514                         full_send = 0;
515                         break;
516                 case 'f':
517                         if (arg_copy_path(outname, optarg, sizeof(outname))) {
518                                 fprintf(stderr,
519                                     "ERROR: output file path too long (%zu)\n",
520                                     strlen(optarg));
521                                 ret = 1;
522                                 goto out;
523                         }
524                         break;
525                 case 'p':
526                         if (snapshot_parent) {
527                                 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
528                                 ret = 1;
529                                 goto out;
530                         }
531                         snapshot_parent = realpath(optarg, NULL);
532                         if (!snapshot_parent) {
533                                 ret = -errno;
534                                 fprintf(stderr, "ERROR: realpath %s failed. "
535                                                 "%s\n", optarg, strerror(-ret));
536                                 goto out;
537                         }
538
539                         ret = is_subvol_ro(&send, snapshot_parent);
540                         if (ret < 0)
541                                 goto out;
542                         if (!ret) {
543                                 ret = -EINVAL;
544                                 fprintf(stderr,
545                                         "ERROR: parent %s is not read-only.\n",
546                                         snapshot_parent);
547                                 goto out;
548                         }
549
550                         full_send = 0;
551                         break;
552                 case 'i':
553                         fprintf(stderr,
554                                 "ERROR: -i was removed, use -c instead\n");
555                         ret = 1;
556                         goto out;
557                 case GETOPT_VAL_SEND_NO_DATA:
558                         send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
559                         break;
560                 case '?':
561                 default:
562                         fprintf(stderr, "ERROR: send args invalid.\n");
563                         ret = 1;
564                         goto out;
565                 }
566         }
567
568         if (check_argc_min(argc - optind, 1))
569                 usage(cmd_send_usage);
570
571         if (outname[0]) {
572                 send.dump_fd = creat(outname, 0600);
573                 if (send.dump_fd == -1) {
574                         ret = -errno;
575                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
576                                         outname, strerror(-ret));
577                         goto out;
578                 }
579         }
580
581         if (isatty(send.dump_fd)) {
582                 fprintf(stderr, 
583                         "ERROR: not dumping send stream into a terminal, "
584                         "redirect it into a file\n");
585                 ret = 1;
586                 goto out;
587         }
588
589         /* use first send subvol to determine mount_root */
590         subvol = argv[optind];
591
592         subvol = realpath(argv[optind], NULL);
593         if (!subvol) {
594                 ret = -errno;
595                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
596                 goto out;
597         }
598
599         ret = init_root_path(&send, subvol);
600         if (ret < 0)
601                 goto out;
602
603         if (snapshot_parent != NULL) {
604                 ret = get_root_id(&send,
605                                 get_subvol_name(send.root_path, snapshot_parent),
606                                 &parent_root_id);
607                 if (ret < 0) {
608                         fprintf(stderr, "ERROR: could not resolve root_id "
609                                         "for %s\n", snapshot_parent);
610                         goto out;
611                 }
612
613                 ret = add_clone_source(&send, parent_root_id);
614                 if (ret < 0) {
615                         fprintf(stderr, "ERROR: not enough memory\n");
616                         goto out;
617                 }
618         }
619
620         for (i = optind; i < argc; i++) {
621                 free(subvol);
622                 subvol = realpath(argv[i], NULL);
623                 if (!subvol) {
624                         ret = -errno;
625                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
626                         goto out;
627                 }
628
629                 ret = find_mount_root(subvol, &mount_root);
630                 if (ret < 0) {
631                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
632                                         "%s\n", subvol,
633                                 strerror(-ret));
634                         goto out;
635                 }
636                 if (ret > 0) {
637                         fprintf(stderr,
638                         "ERROR: %s doesn't belong to btrfs mount point\n",
639                                 subvol);
640                         ret = -EINVAL;
641                         goto out;
642                 }
643                 if (strcmp(send.root_path, mount_root) != 0) {
644                         ret = -EINVAL;
645                         fprintf(stderr, "ERROR: all subvols must be from the "
646                                         "same fs.\n");
647                         goto out;
648                 }
649                 free(mount_root);
650
651                 ret = is_subvol_ro(&send, subvol);
652                 if (ret < 0)
653                         goto out;
654                 if (!ret) {
655                         ret = -EINVAL;
656                         fprintf(stderr, "ERROR: %s is not read-only.\n",
657                                         subvol);
658                         goto out;
659                 }
660         }
661
662         if (send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA)
663                 printf("Mode NO_FILE_DATA enabled\n");
664
665         for (i = optind; i < argc; i++) {
666                 int is_first_subvol;
667                 int is_last_subvol;
668
669                 free(subvol);
670                 subvol = argv[i];
671
672                 fprintf(stderr, "At subvol %s\n", subvol);
673
674                 subvol = realpath(subvol, NULL);
675                 if (!subvol) {
676                         ret = -errno;
677                         fprintf(stderr, "ERROR: realpath %s failed. "
678                                         "%s\n", argv[i], strerror(-ret));
679                         goto out;
680                 }
681
682                 if (!full_send && !parent_root_id) {
683                         ret = find_good_parent(&send, root_id, &parent_root_id);
684                         if (ret < 0) {
685                                 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
686                                         root_id);
687                                 goto out;
688                         }
689                 }
690
691                 ret = is_subvol_ro(&send, subvol);
692                 if (ret < 0)
693                         goto out;
694                 if (!ret) {
695                         ret = -EINVAL;
696                         fprintf(stderr, "ERROR: %s is not read-only.\n",
697                                         subvol);
698                         goto out;
699                 }
700
701                 if (new_end_cmd_semantic) {
702                         /* require new kernel */
703                         is_first_subvol = (i == optind);
704                         is_last_subvol = (i == argc - 1);
705                 } else {
706                         /* be compatible to old and new kernel */
707                         is_first_subvol = 1;
708                         is_last_subvol = 1;
709                 }
710                 ret = do_send(&send, parent_root_id, is_first_subvol,
711                               is_last_subvol, subvol, send_flags);
712                 if (ret < 0)
713                         goto out;
714
715                 /* done with this subvol, so add it to the clone sources */
716                 ret = add_clone_source(&send, root_id);
717                 if (ret < 0) {
718                         fprintf(stderr, "ERROR: not enough memory\n");
719                         goto out;
720                 }
721
722                 parent_root_id = 0;
723                 full_send = 0;
724         }
725
726         ret = 0;
727
728 out:
729         free(subvol);
730         free(snapshot_parent);
731         free(send.clone_sources);
732         if (send.mnt_fd >= 0)
733                 close(send.mnt_fd);
734         free(send.root_path);
735         subvol_uuid_search_finit(&send.sus);
736         return !!ret;
737 }
738
739 const char * const cmd_send_usage[] = {
740         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
741         "Send the subvolume(s) to stdout.",
742         "Sends the subvolume(s) specified by <subvol> to stdout.",
743         "By default, this will send the whole subvolume. To do an incremental",
744         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
745         "any additional local snapshots, use '-c <clone-src>' (multiple times",
746         "where applicable). You must not specify clone sources unless you",
747         "guarantee that these snapshots are exactly in the same state on both",
748         "sides, the sender and the receiver. It is allowed to omit the",
749         "'-p <parent>' option when '-c <clone-src>' options are given, in",
750         "which case 'btrfs send' will determine a suitable parent among the",
751         "clone sources itself.",
752         "\n",
753         "-v               Enable verbose debug output. Each occurrence of",
754         "                 this option increases the verbose level more.",
755         "-e               If sending multiple subvols at once, use the new",
756         "                 format and omit the end-cmd between the subvols.",
757         "-p <parent>      Send an incremental stream from <parent> to",
758         "                 <subvol>.",
759         "-c <clone-src>   Use this snapshot as a clone source for an ",
760         "                 incremental send (multiple allowed)",
761         "-f <outfile>     Output is normally written to stdout. To write to",
762         "                 a file, use this option. An alternative would be to",
763         "                 use pipes.",
764         "--no-data        send in NO_FILE_DATA mode, Note: the output stream",
765         "                 does not contain any file data and thus cannot be used",
766         "                 to transfer changes. This mode is faster and useful to",
767         "                 show the differences in metadata.",
768         NULL
769 };