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