e59ba50637c3d1f0f4a88ece0a4a4721fe6aaa4a
[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
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 {
248         int ret;
249         pthread_t t_read;
250         struct btrfs_ioctl_send_args io_send;
251         void *t_err = NULL;
252         int subvol_fd = -1;
253         int pipefd[2] = {-1, -1};
254
255         subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
256         if (subvol_fd < 0) {
257                 ret = -errno;
258                 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
259                                 strerror(-ret));
260                 goto out;
261         }
262
263         ret = pipe(pipefd);
264         if (ret < 0) {
265                 ret = -errno;
266                 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
267                 goto out;
268         }
269
270         memset(&io_send, 0, sizeof(io_send));
271         io_send.send_fd = pipefd[1];
272         send->send_fd = pipefd[0];
273
274         if (!ret)
275                 ret = pthread_create(&t_read, NULL, dump_thread,
276                                         send);
277         if (ret) {
278                 ret = -ret;
279                 fprintf(stderr, "ERROR: thread setup failed: %s\n",
280                         strerror(-ret));
281                 goto out;
282         }
283
284         io_send.clone_sources = (__u64*)send->clone_sources;
285         io_send.clone_sources_count = send->clone_sources_count;
286         io_send.parent_root = parent_root_id;
287         if (!is_first_subvol)
288                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_STREAM_HEADER;
289         if (!is_last_subvol)
290                 io_send.flags |= BTRFS_SEND_FLAG_OMIT_END_CMD;
291         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
292         if (ret) {
293                 ret = -errno;
294                 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
295                         strerror(-ret));
296                 if (ret == -EINVAL && (!is_first_subvol || !is_last_subvol))
297                         fprintf(stderr,
298                                 "Try upgrading your kernel or don't use -e.\n");
299                 goto out;
300         }
301         if (g_verbose > 0)
302                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
303
304         if (g_verbose > 0)
305                 fprintf(stderr, "joining genl thread\n");
306
307         close(pipefd[1]);
308         pipefd[1] = -1;
309
310         ret = pthread_join(t_read, &t_err);
311         if (ret) {
312                 ret = -ret;
313                 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
314                         strerror(-ret));
315                 goto out;
316         }
317         if (t_err) {
318                 ret = (long int)t_err;
319                 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
320                         "(%s)\n", (long int)t_err, strerror(-ret));
321                 goto out;
322         }
323
324         ret = 0;
325
326 out:
327         if (subvol_fd != -1)
328                 close(subvol_fd);
329         if (pipefd[0] != -1)
330                 close(pipefd[0]);
331         if (pipefd[1] != -1)
332                 close(pipefd[1]);
333         return ret;
334 }
335
336 char *get_subvol_name(char *mnt, char *full_path)
337 {
338         int len = strlen(mnt);
339         if (!len)
340                 return full_path;
341         if (mnt[len - 1] != '/')
342                 len += 1;
343
344         return full_path + len;
345 }
346
347 static int init_root_path(struct btrfs_send *s, const char *subvol)
348 {
349         int ret = 0;
350
351         if (s->root_path)
352                 goto out;
353
354         ret = find_mount_root(subvol, &s->root_path);
355         if (ret < 0) {
356                 fprintf(stderr,
357                         "ERROR: failed to determine mount point for %s: %s\n",
358                         subvol, strerror(-ret));
359                 ret = -EINVAL;
360                 goto out;
361         }
362         if (ret > 0) {
363                 fprintf(stderr,
364                         "ERROR: %s doesn't belong to btrfs mount point\n",
365                         subvol);
366                 ret = -EINVAL;
367                 goto out;
368         }
369
370         s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
371         if (s->mnt_fd < 0) {
372                 ret = -errno;
373                 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
374                         strerror(-ret));
375                 goto out;
376         }
377
378         ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
379         if (ret < 0) {
380                 fprintf(stderr, "ERROR: failed to initialize subvol search. "
381                                 "%s\n", strerror(-ret));
382                 goto out;
383         }
384
385 out:
386         return ret;
387
388 }
389
390 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
391 {
392         int ret;
393         u64 flags;
394         int fd = -1;
395
396         fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
397         if (fd < 0) {
398                 ret = -errno;
399                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
400                                 subvol, strerror(-ret));
401                 goto out;
402         }
403
404         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
405         if (ret < 0) {
406                 ret = -errno;
407                 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
408                                 "%s\n", strerror(-ret));
409                 goto out;
410         }
411
412         if (flags & BTRFS_SUBVOL_RDONLY)
413                 ret = 1;
414         else
415                 ret = 0;
416
417 out:
418         if (fd != -1)
419                 close(fd);
420
421         return ret;
422 }
423
424 int cmd_send(int argc, char **argv)
425 {
426         char *subvol = NULL;
427         int c;
428         int ret;
429         char *outname = NULL;
430         struct btrfs_send send;
431         u32 i;
432         char *mount_root = NULL;
433         char *snapshot_parent = NULL;
434         u64 root_id = 0;
435         u64 parent_root_id = 0;
436         int full_send = 1;
437         int new_end_cmd_semantic = 0;
438
439         memset(&send, 0, sizeof(send));
440         send.dump_fd = fileno(stdout);
441
442         while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
443                 switch (c) {
444                 case 'v':
445                         g_verbose++;
446                         break;
447                 case 'e':
448                         new_end_cmd_semantic = 1;
449                         break;
450                 case 'c':
451                         subvol = realpath(optarg, NULL);
452                         if (!subvol) {
453                                 ret = -errno;
454                                 fprintf(stderr, "ERROR: realpath %s failed. "
455                                                 "%s\n", optarg, strerror(-ret));
456                                 goto out;
457                         }
458
459                         ret = init_root_path(&send, subvol);
460                         if (ret < 0)
461                                 goto out;
462
463                         ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
464                                         &root_id);
465                         if (ret < 0) {
466                                 fprintf(stderr, "ERROR: could not resolve "
467                                                 "root_id for %s\n", subvol);
468                                 goto out;
469                         }
470
471                         ret = is_subvol_ro(&send, subvol);
472                         if (ret < 0)
473                                 goto out;
474                         if (!ret) {
475                                 ret = -EINVAL;
476                                 fprintf(stderr,
477                                 "ERROR: cloned subvol %s is not read-only.\n",
478                                         subvol);
479                                 goto out;
480                         }
481
482                         ret = add_clone_source(&send, root_id);
483                         if (ret < 0) {
484                                 fprintf(stderr, "ERROR: not enough memory\n");
485                                 goto out;
486                         }
487                         subvol_uuid_search_finit(&send.sus);
488                         free(subvol);
489                         subvol = NULL;
490                         if (send.mnt_fd >= 0) {
491                                 close(send.mnt_fd);
492                                 send.mnt_fd = -1;
493                         }
494                         free(send.root_path);
495                         send.root_path = NULL;
496                         full_send = 0;
497                         break;
498                 case 'f':
499                         outname = optarg;
500                         break;
501                 case 'p':
502                         if (snapshot_parent) {
503                                 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
504                                 ret = 1;
505                                 goto out;
506                         }
507                         snapshot_parent = realpath(optarg, NULL);
508                         if (!snapshot_parent) {
509                                 ret = -errno;
510                                 fprintf(stderr, "ERROR: realpath %s failed. "
511                                                 "%s\n", optarg, strerror(-ret));
512                                 goto out;
513                         }
514
515                         ret = is_subvol_ro(&send, snapshot_parent);
516                         if (ret < 0)
517                                 goto out;
518                         if (!ret) {
519                                 ret = -EINVAL;
520                                 fprintf(stderr,
521                                         "ERROR: parent %s is not read-only.\n",
522                                         snapshot_parent);
523                                 goto out;
524                         }
525
526                         full_send = 0;
527                         break;
528                 case 'i':
529                         fprintf(stderr,
530                                 "ERROR: -i was removed, use -c instead\n");
531                         ret = 1;
532                         goto out;
533                 case '?':
534                 default:
535                         fprintf(stderr, "ERROR: send args invalid.\n");
536                         ret = 1;
537                         goto out;
538                 }
539         }
540
541         if (check_argc_min(argc - optind, 1))
542                 usage(cmd_send_usage);
543
544         if (outname != NULL) {
545                 send.dump_fd = creat(outname, 0600);
546                 if (send.dump_fd == -1) {
547                         ret = -errno;
548                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
549                                         outname, strerror(-ret));
550                         goto out;
551                 }
552         }
553
554         if (isatty(send.dump_fd)) {
555                 fprintf(stderr, 
556                         "ERROR: not dumping send stream into a terminal, "
557                         "redirect it into a file\n");
558                 ret = 1;
559                 goto out;
560         }
561
562         /* use first send subvol to determine mount_root */
563         subvol = argv[optind];
564
565         subvol = realpath(argv[optind], NULL);
566         if (!subvol) {
567                 ret = -errno;
568                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
569                 goto out;
570         }
571
572         ret = init_root_path(&send, subvol);
573         if (ret < 0)
574                 goto out;
575
576         if (snapshot_parent != NULL) {
577                 ret = get_root_id(&send,
578                                 get_subvol_name(send.root_path, snapshot_parent),
579                                 &parent_root_id);
580                 if (ret < 0) {
581                         fprintf(stderr, "ERROR: could not resolve root_id "
582                                         "for %s\n", snapshot_parent);
583                         goto out;
584                 }
585
586                 ret = add_clone_source(&send, parent_root_id);
587                 if (ret < 0) {
588                         fprintf(stderr, "ERROR: not enough memory\n");
589                         goto out;
590                 }
591         }
592
593         for (i = optind; i < argc; i++) {
594                 free(subvol);
595                 subvol = realpath(argv[i], NULL);
596                 if (!subvol) {
597                         ret = -errno;
598                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
599                         goto out;
600                 }
601
602                 ret = find_mount_root(subvol, &mount_root);
603                 if (ret < 0) {
604                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
605                                         "%s\n", subvol,
606                                 strerror(-ret));
607                         goto out;
608                 }
609                 if (ret > 0) {
610                         fprintf(stderr,
611                         "ERROR: %s doesn't belong to btrfs mount point\n",
612                                 subvol);
613                         ret = -EINVAL;
614                         goto out;
615                 }
616                 if (strcmp(send.root_path, mount_root) != 0) {
617                         ret = -EINVAL;
618                         fprintf(stderr, "ERROR: all subvols must be from the "
619                                         "same fs.\n");
620                         goto out;
621                 }
622                 free(mount_root);
623
624                 ret = is_subvol_ro(&send, subvol);
625                 if (ret < 0)
626                         goto out;
627                 if (!ret) {
628                         ret = -EINVAL;
629                         fprintf(stderr, "ERROR: %s is not read-only.\n",
630                                         subvol);
631                         goto out;
632                 }
633         }
634
635         for (i = optind; i < argc; i++) {
636                 int is_first_subvol;
637                 int is_last_subvol;
638
639                 free(subvol);
640                 subvol = argv[i];
641
642                 fprintf(stderr, "At subvol %s\n", subvol);
643
644                 subvol = realpath(subvol, NULL);
645                 if (!subvol) {
646                         ret = -errno;
647                         fprintf(stderr, "ERROR: realpath %s failed. "
648                                         "%s\n", argv[i], strerror(-ret));
649                         goto out;
650                 }
651
652                 if (!full_send && !parent_root_id) {
653                         ret = find_good_parent(&send, root_id, &parent_root_id);
654                         if (ret < 0) {
655                                 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
656                                         root_id);
657                                 goto out;
658                         }
659                 }
660
661                 ret = is_subvol_ro(&send, subvol);
662                 if (ret < 0)
663                         goto out;
664                 if (!ret) {
665                         ret = -EINVAL;
666                         fprintf(stderr, "ERROR: %s is not read-only.\n",
667                                         subvol);
668                         goto out;
669                 }
670
671                 if (new_end_cmd_semantic) {
672                         /* require new kernel */
673                         is_first_subvol = (i == optind);
674                         is_last_subvol = (i == argc - 1);
675                 } else {
676                         /* be compatible to old and new kernel */
677                         is_first_subvol = 1;
678                         is_last_subvol = 1;
679                 }
680                 ret = do_send(&send, parent_root_id, is_first_subvol,
681                               is_last_subvol, subvol);
682                 if (ret < 0)
683                         goto out;
684
685                 /* done with this subvol, so add it to the clone sources */
686                 ret = add_clone_source(&send, root_id);
687                 if (ret < 0) {
688                         fprintf(stderr, "ERROR: not enough memory\n");
689                         goto out;
690                 }
691
692                 parent_root_id = 0;
693                 full_send = 0;
694         }
695
696         ret = 0;
697
698 out:
699         free(subvol);
700         free(snapshot_parent);
701         free(send.clone_sources);
702         if (send.mnt_fd >= 0)
703                 close(send.mnt_fd);
704         free(send.root_path);
705         subvol_uuid_search_finit(&send.sus);
706         return !!ret;
707 }
708
709 const char * const cmd_send_usage[] = {
710         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
711         "Send the subvolume(s) to stdout.",
712         "Sends the subvolume(s) specified by <subvol> to stdout.",
713         "By default, this will send the whole subvolume. To do an incremental",
714         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
715         "any additional local snapshots, use '-c <clone-src>' (multiple times",
716         "where applicable). You must not specify clone sources unless you",
717         "guarantee that these snapshots are exactly in the same state on both",
718         "sides, the sender and the receiver. It is allowed to omit the",
719         "'-p <parent>' option when '-c <clone-src>' options are given, in",
720         "which case 'btrfs send' will determine a suitable parent among the",
721         "clone sources itself.",
722         "\n",
723         "-v               Enable verbose debug output. Each occurrence of",
724         "                 this option increases the verbose level more.",
725         "-e               If sending multiple subvols at once, use the new",
726         "                 format and omit the end-cmd between the subvols.",
727         "-p <parent>      Send an incremental stream from <parent> to",
728         "                 <subvol>.",
729         "-c <clone-src>   Use this snapshot as a clone source for an ",
730         "                 incremental send (multiple allowed)",
731         "-f <outfile>     Output is normally written to stdout. To write to",
732         "                 a file, use this option. An alternative would be to",
733         "                 use pipes.",
734         NULL
735 };