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