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