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