btrfs-progs: send: cleanup use of ctransid delta temporary variable in find_good_parent
[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 int cmd_send(int argc, char **argv)
421 {
422         char *subvol = NULL;
423         int ret;
424         char outname[PATH_MAX];
425         struct btrfs_send send;
426         u32 i;
427         char *mount_root = NULL;
428         char *snapshot_parent = NULL;
429         u64 root_id = 0;
430         u64 parent_root_id = 0;
431         int full_send = 1;
432         int new_end_cmd_semantic = 0;
433         u64 send_flags = 0;
434
435         memset(&send, 0, sizeof(send));
436         send.dump_fd = fileno(stdout);
437         outname[0] = 0;
438
439         while (1) {
440                 enum { GETOPT_VAL_SEND_NO_DATA = 256 };
441                 static const struct option long_options[] = {
442                         { "verbose", no_argument, NULL, 'v' },
443                         { "quiet", no_argument, NULL, 'q' },
444                         { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA }
445                 };
446                 int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL);
447
448                 if (c < 0)
449                         break;
450
451                 switch (c) {
452                 case 'v':
453                         g_verbose++;
454                         break;
455                 case 'q':
456                         g_verbose = 0;
457                         break;
458                 case 'e':
459                         new_end_cmd_semantic = 1;
460                         break;
461                 case 'c':
462                         subvol = realpath(optarg, NULL);
463                         if (!subvol) {
464                                 ret = -errno;
465                                 error("realpath %s failed: %s\n", optarg, strerror(-ret));
466                                 goto out;
467                         }
468
469                         ret = init_root_path(&send, subvol);
470                         if (ret < 0)
471                                 goto out;
472
473                         ret = get_root_id(&send,
474                                 subvol_strip_mountpoint(send.root_path, subvol),
475                                 &root_id);
476                         if (ret < 0) {
477                                 error("cannot resolve rootid for %s", subvol);
478                                 goto out;
479                         }
480
481                         ret = is_subvol_ro(&send, subvol);
482                         if (ret < 0)
483                                 goto out;
484                         if (!ret) {
485                                 ret = -EINVAL;
486                                 error("cloned subvolume %s is not read-only", subvol);
487                                 goto out;
488                         }
489
490                         ret = add_clone_source(&send, root_id);
491                         if (ret < 0) {
492                                 error("cannot add clone source: %s", strerror(-ret));
493                                 goto out;
494                         }
495                         subvol_uuid_search_finit(&send.sus);
496                         free(subvol);
497                         subvol = NULL;
498                         if (send.mnt_fd >= 0) {
499                                 close(send.mnt_fd);
500                                 send.mnt_fd = -1;
501                         }
502                         free(send.root_path);
503                         send.root_path = NULL;
504                         full_send = 0;
505                         break;
506                 case 'f':
507                         if (arg_copy_path(outname, optarg, sizeof(outname))) {
508                                 error("output file path too long (%zu)", strlen(optarg));
509                                 ret = 1;
510                                 goto out;
511                         }
512                         break;
513                 case 'p':
514                         if (snapshot_parent) {
515                                 error("you cannot have more than one parent (-p)");
516                                 ret = 1;
517                                 goto out;
518                         }
519                         snapshot_parent = realpath(optarg, NULL);
520                         if (!snapshot_parent) {
521                                 ret = -errno;
522                                 error("realpath %s failed: %s", optarg, strerror(-ret));
523                                 goto out;
524                         }
525
526                         ret = is_subvol_ro(&send, snapshot_parent);
527                         if (ret < 0)
528                                 goto out;
529                         if (!ret) {
530                                 ret = -EINVAL;
531                                 error("parent subvolume %s is not read-only",
532                                         snapshot_parent);
533                                 goto out;
534                         }
535
536                         full_send = 0;
537                         break;
538                 case 'i':
539                         error("option -i was removed, use -c instead");
540                         ret = 1;
541                         goto out;
542                 case GETOPT_VAL_SEND_NO_DATA:
543                         send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA;
544                         break;
545                 case '?':
546                 default:
547                         error("send arguments invalid");
548                         ret = 1;
549                         goto out;
550                 }
551         }
552
553         if (check_argc_min(argc - optind, 1))
554                 usage(cmd_send_usage);
555
556         if (outname[0]) {
557                 send.dump_fd = creat(outname, 0600);
558                 if (send.dump_fd == -1) {
559                         ret = -errno;
560                         error("cannot create '%s': %s", outname, strerror(-ret));
561                         goto out;
562                 }
563         }
564
565         if (isatty(send.dump_fd)) {
566                 error(
567             "not dumping send stream into a terminal, redirect it into a file");
568                 ret = 1;
569                 goto out;
570         }
571
572         /* use first send subvol to determine mount_root */
573         subvol = realpath(argv[optind], NULL);
574         if (!subvol) {
575                 ret = -errno;
576                 error("unable to resolve %s", argv[optind]);
577                 goto out;
578         }
579
580         ret = init_root_path(&send, subvol);
581         if (ret < 0)
582                 goto out;
583
584         if (snapshot_parent != NULL) {
585                 ret = get_root_id(&send,
586                         subvol_strip_mountpoint(send.root_path, snapshot_parent),
587                         &parent_root_id);
588                 if (ret < 0) {
589                         error("could not resolve rootid for %s", snapshot_parent);
590                         goto out;
591                 }
592
593                 ret = add_clone_source(&send, parent_root_id);
594                 if (ret < 0) {
595                         error("cannot add clone source: %s", strerror(-ret));
596                         goto out;
597                 }
598         }
599
600         for (i = optind; i < argc; i++) {
601                 free(subvol);
602                 subvol = realpath(argv[i], NULL);
603                 if (!subvol) {
604                         ret = -errno;
605                         error("unable to resolve %s", argv[i]);
606                         goto out;
607                 }
608
609                 ret = find_mount_root(subvol, &mount_root);
610                 if (ret < 0) {
611                         error("find_mount_root failed on %s: %s", subvol,
612                                 strerror(-ret));
613                         goto out;
614                 }
615                 if (ret > 0) {
616                         error("%s does not belong to btrfs mount point",
617                                 subvol);
618                         ret = -EINVAL;
619                         goto out;
620                 }
621                 if (strcmp(send.root_path, mount_root) != 0) {
622                         ret = -EINVAL;
623                         error("all subvolumes must be from the same filesystem");
624                         goto out;
625                 }
626                 free(mount_root);
627
628                 ret = is_subvol_ro(&send, subvol);
629                 if (ret < 0)
630                         goto out;
631                 if (!ret) {
632                         ret = -EINVAL;
633                         error("subvolume %s is not read-only", subvol);
634                         goto out;
635                 }
636         }
637
638         if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1)
639                 if (g_verbose > 1)
640                         fprintf(stderr, "Mode NO_FILE_DATA enabled\n");
641
642         for (i = optind; i < argc; i++) {
643                 int is_first_subvol;
644                 int is_last_subvol;
645
646                 free(subvol);
647                 subvol = argv[i];
648
649                 if (g_verbose > 0)
650                         fprintf(stderr, "At subvol %s\n", subvol);
651
652                 subvol = realpath(subvol, NULL);
653                 if (!subvol) {
654                         ret = -errno;
655                         error("realpath %s failed: %s", argv[i], strerror(-ret));
656                         goto out;
657                 }
658
659                 if (!full_send && root_id) {
660                         ret = find_good_parent(&send, root_id, &parent_root_id);
661                         if (ret < 0) {
662                                 error("parent determination failed for %lld",
663                                         root_id);
664                                 goto out;
665                         }
666                 }
667
668                 if (new_end_cmd_semantic) {
669                         /* require new kernel */
670                         is_first_subvol = (i == optind);
671                         is_last_subvol = (i == argc - 1);
672                 } else {
673                         /* be compatible to old and new kernel */
674                         is_first_subvol = 1;
675                         is_last_subvol = 1;
676                 }
677                 ret = do_send(&send, parent_root_id, is_first_subvol,
678                               is_last_subvol, subvol, send_flags);
679                 if (ret < 0)
680                         goto out;
681
682                 if (!full_send && root_id) {
683                         /* done with this subvol, so add it to the clone sources */
684                         ret = add_clone_source(&send, root_id);
685                         if (ret < 0) {
686                                 error("cannot add clone source: %s", strerror(-ret));
687                                 goto out;
688                         }
689                 }
690         }
691
692         ret = 0;
693
694 out:
695         free(subvol);
696         free(snapshot_parent);
697         free(send.clone_sources);
698         if (send.mnt_fd >= 0)
699                 close(send.mnt_fd);
700         free(send.root_path);
701         subvol_uuid_search_finit(&send.sus);
702         return !!ret;
703 }
704
705 const char * const cmd_send_usage[] = {
706         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
707         "Send the subvolume(s) to stdout.",
708         "Sends the subvolume(s) specified by <subvol> to stdout.",
709         "<subvol> should be read-only here.",
710         "By default, this will send the whole subvolume. To do an incremental",
711         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
712         "any additional local snapshots, use '-c <clone-src>' (multiple times",
713         "where applicable). You must not specify clone sources unless you",
714         "guarantee that these snapshots are exactly in the same state on both",
715         "sides, the sender and the receiver. It is allowed to omit the",
716         "'-p <parent>' option when '-c <clone-src>' options are given, in",
717         "which case 'btrfs send' will determine a suitable parent among the",
718         "clone sources itself.",
719         "\n",
720         "-e               If sending multiple subvols at once, use the new",
721         "                 format and omit the end-cmd between the subvols.",
722         "-p <parent>      Send an incremental stream from <parent> to",
723         "                 <subvol>.",
724         "-c <clone-src>   Use this snapshot as a clone source for an ",
725         "                 incremental send (multiple allowed)",
726         "-f <outfile>     Output is normally written to stdout. To write to",
727         "                 a file, use this option. An alternative would be to",
728         "                 use pipes.",
729         "--no-data        send in NO_FILE_DATA mode, Note: the output stream",
730         "                 does not contain any file data and thus cannot be used",
731         "                 to transfer changes. This mode is faster and useful to",
732         "                 show the differences in metadata.",
733         "-v|--verbose     enable verbose output to stderr, each occurrence of",
734         "                 this option increases verbosity",
735         "-q|--quiet       suppress all messages, except errors",
736         NULL
737 };