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