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