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