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