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