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