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