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