btrfs-progs: move find_mount_root to utils.[ch]
[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                 assert(parent2);
133                 tmp = parent2->ctransid - parent->ctransid;
134                 if (tmp < 0)
135                         tmp *= -1;
136                 if (tmp < best_diff) {
137                         if (best_parent) {
138                                 free(best_parent->path);
139                                 free(best_parent);
140                         }
141                         best_parent = parent2;
142                         parent2 = NULL;
143                         best_diff = tmp;
144                 } else {
145                         free(parent2->path);
146                         free(parent2);
147                         parent2 = NULL;
148                 }
149         }
150
151         if (!best_parent) {
152                 ret = -ENOENT;
153                 goto out;
154         }
155
156 out_found:
157         *found = best_parent->root_id;
158         ret = 0;
159
160 out:
161         if (parent) {
162                 free(parent->path);
163                 free(parent);
164         }
165         if (best_parent) {
166                 free(best_parent->path);
167                 free(best_parent);
168         }
169         return ret;
170 }
171
172 static void add_clone_source(struct btrfs_send *s, u64 root_id)
173 {
174         s->clone_sources = realloc(s->clone_sources,
175                 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
176         s->clone_sources[s->clone_sources_count++] = root_id;
177 }
178
179 static int write_buf(int fd, const void *buf, int size)
180 {
181         int ret;
182         int pos = 0;
183
184         while (pos < size) {
185                 ret = write(fd, (char*)buf + pos, size - pos);
186                 if (ret < 0) {
187                         ret = -errno;
188                         fprintf(stderr, "ERROR: failed to dump stream. %s",
189                                         strerror(-ret));
190                         goto out;
191                 }
192                 if (!ret) {
193                         ret = -EIO;
194                         fprintf(stderr, "ERROR: failed to dump stream. %s",
195                                         strerror(-ret));
196                         goto out;
197                 }
198                 pos += ret;
199         }
200         ret = 0;
201
202 out:
203         return ret;
204 }
205
206 static void *dump_thread(void *arg_)
207 {
208         int ret;
209         struct btrfs_send *s = (struct btrfs_send*)arg_;
210         char buf[4096];
211         int readed;
212
213         while (1) {
214                 readed = read(s->send_fd, buf, sizeof(buf));
215                 if (readed < 0) {
216                         ret = -errno;
217                         fprintf(stderr, "ERROR: failed to read stream from "
218                                         "kernel. %s\n", strerror(-ret));
219                         goto out;
220                 }
221                 if (!readed) {
222                         ret = 0;
223                         goto out;
224                 }
225                 ret = write_buf(s->dump_fd, buf, readed);
226                 if (ret < 0)
227                         goto out;
228         }
229
230 out:
231         if (ret < 0) {
232                 exit(-ret);
233         }
234
235         return ERR_PTR(ret);
236 }
237
238 static int do_send(struct btrfs_send *send, u64 parent_root_id,
239                    int is_first_subvol, int is_last_subvol, char *subvol)
240 {
241         int ret;
242         pthread_t t_read;
243         pthread_attr_t t_attr;
244         struct btrfs_ioctl_send_args io_send;
245         void *t_err = NULL;
246         int subvol_fd = -1;
247         int pipefd[2] = {-1, -1};
248
249         subvol_fd = openat(send->mnt_fd, subvol, O_RDONLY | O_NOATIME);
250         if (subvol_fd < 0) {
251                 ret = -errno;
252                 fprintf(stderr, "ERROR: open %s failed. %s\n", subvol,
253                                 strerror(-ret));
254                 goto out;
255         }
256
257         ret = pthread_attr_init(&t_attr);
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, &t_attr, 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         pthread_attr_destroy(&t_attr);
321
322         ret = 0;
323
324 out:
325         if (subvol_fd != -1)
326                 close(subvol_fd);
327         if (pipefd[0] != -1)
328                 close(pipefd[0]);
329         if (pipefd[1] != -1)
330                 close(pipefd[1]);
331         return ret;
332 }
333
334 char *get_subvol_name(char *mnt, char *full_path)
335 {
336         int len = strlen(mnt);
337         if (!len)
338                 return full_path;
339         if (mnt[len - 1] != '/')
340                 len += 1;
341
342         return full_path + len;
343 }
344
345 static int init_root_path(struct btrfs_send *s, const char *subvol)
346 {
347         int ret = 0;
348
349         if (s->root_path)
350                 goto out;
351
352         ret = find_mount_root(subvol, &s->root_path);
353         if (ret < 0) {
354                 ret = -EINVAL;
355                 fprintf(stderr, "ERROR: failed to determine mount point "
356                                 "for %s\n", subvol);
357                 goto out;
358         }
359
360         s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
361         if (s->mnt_fd < 0) {
362                 ret = -errno;
363                 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
364                         strerror(-ret));
365                 goto out;
366         }
367
368         ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
369         if (ret < 0) {
370                 fprintf(stderr, "ERROR: failed to initialize subvol search. "
371                                 "%s\n", strerror(-ret));
372                 goto out;
373         }
374
375 out:
376         return ret;
377
378 }
379
380 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
381 {
382         int ret;
383         u64 flags;
384         int fd = -1;
385
386         fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
387         if (fd < 0) {
388                 ret = -errno;
389                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
390                                 subvol, strerror(-ret));
391                 goto out;
392         }
393
394         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
395         if (ret < 0) {
396                 ret = -errno;
397                 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
398                                 "%s\n", strerror(-ret));
399                 goto out;
400         }
401
402         if (flags & BTRFS_SUBVOL_RDONLY)
403                 ret = 1;
404         else
405                 ret = 0;
406
407 out:
408         if (fd != -1)
409                 close(fd);
410
411         return ret;
412 }
413
414 int cmd_send(int argc, char **argv)
415 {
416         char *subvol = NULL;
417         int c;
418         int ret;
419         char *outname = NULL;
420         struct btrfs_send send;
421         u32 i;
422         char *mount_root = NULL;
423         char *snapshot_parent = NULL;
424         u64 root_id;
425         u64 parent_root_id = 0;
426         int full_send = 1;
427         int new_end_cmd_semantic = 0;
428
429         memset(&send, 0, sizeof(send));
430         send.dump_fd = fileno(stdout);
431
432         while ((c = getopt(argc, argv, "vec:f:i:p:")) != -1) {
433                 switch (c) {
434                 case 'v':
435                         g_verbose++;
436                         break;
437                 case 'e':
438                         new_end_cmd_semantic = 1;
439                         break;
440                 case 'c':
441                         subvol = realpath(optarg, NULL);
442                         if (!subvol) {
443                                 ret = -errno;
444                                 fprintf(stderr, "ERROR: realpath %s failed. "
445                                                 "%s\n", optarg, strerror(-ret));
446                                 goto out;
447                         }
448
449                         ret = init_root_path(&send, subvol);
450                         if (ret < 0)
451                                 goto out;
452
453                         ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
454                                         &root_id);
455                         if (ret < 0) {
456                                 fprintf(stderr, "ERROR: could not resolve "
457                                                 "root_id for %s\n", subvol);
458                                 goto out;
459                         }
460
461                         ret = is_subvol_ro(&send, subvol);
462                         if (ret < 0)
463                                 goto out;
464                         if (!ret) {
465                                 ret = -EINVAL;
466                                 fprintf(stderr,
467                                 "ERROR: cloned subvol %s is not read-only.\n",
468                                         subvol);
469                                 goto out;
470                         }
471
472                         add_clone_source(&send, root_id);
473                         subvol_uuid_search_finit(&send.sus);
474                         free(subvol);
475                         subvol = NULL;
476                         if (send.mnt_fd >= 0) {
477                                 close(send.mnt_fd);
478                                 send.mnt_fd = -1;
479                         }
480                         free(send.root_path);
481                         send.root_path = NULL;
482                         full_send = 0;
483                         break;
484                 case 'f':
485                         outname = optarg;
486                         break;
487                 case 'p':
488                         if (snapshot_parent) {
489                                 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
490                                 ret = 1;
491                                 goto out;
492                         }
493                         snapshot_parent = realpath(optarg, NULL);
494                         if (!snapshot_parent) {
495                                 ret = -errno;
496                                 fprintf(stderr, "ERROR: realpath %s failed. "
497                                                 "%s\n", optarg, strerror(-ret));
498                                 goto out;
499                         }
500
501                         ret = is_subvol_ro(&send, snapshot_parent);
502                         if (ret < 0)
503                                 goto out;
504                         if (!ret) {
505                                 ret = -EINVAL;
506                                 fprintf(stderr,
507                                         "ERROR: parent %s is not read-only.\n",
508                                         snapshot_parent);
509                                 goto out;
510                         }
511
512                         full_send = 0;
513                         break;
514                 case 'i':
515                         fprintf(stderr,
516                                 "ERROR: -i was removed, use -c instead\n");
517                         ret = 1;
518                         goto out;
519                 case '?':
520                 default:
521                         fprintf(stderr, "ERROR: send args invalid.\n");
522                         ret = 1;
523                         goto out;
524                 }
525         }
526
527         if (optind == argc) {
528                 fprintf(stderr, "ERROR: send needs path to snapshot\n");
529                 ret = 1;
530                 goto out;
531         }
532
533         if (outname != NULL) {
534                 send.dump_fd = creat(outname, 0600);
535                 if (send.dump_fd == -1) {
536                         ret = -errno;
537                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
538                                         outname, strerror(-ret));
539                         goto out;
540                 }
541         }
542
543         if (isatty(send.dump_fd)) {
544                 fprintf(stderr, 
545                         "ERROR: not dumping send stream into a terminal, "
546                         "redirect it into a file\n");
547                 ret = 1;
548                 goto out;
549         }
550
551         /* use first send subvol to determine mount_root */
552         subvol = argv[optind];
553
554         subvol = realpath(argv[optind], NULL);
555         if (!subvol) {
556                 ret = -errno;
557                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
558                 goto out;
559         }
560
561         ret = init_root_path(&send, subvol);
562         if (ret < 0)
563                 goto out;
564
565         if (snapshot_parent != NULL) {
566                 ret = get_root_id(&send,
567                                 get_subvol_name(send.root_path, snapshot_parent),
568                                 &parent_root_id);
569                 if (ret < 0) {
570                         fprintf(stderr, "ERROR: could not resolve root_id "
571                                         "for %s\n", snapshot_parent);
572                         goto out;
573                 }
574
575                 add_clone_source(&send, parent_root_id);
576         }
577
578         for (i = optind; i < argc; i++) {
579                 free(subvol);
580                 subvol = realpath(argv[i], NULL);
581                 if (!subvol) {
582                         ret = -errno;
583                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
584                         goto out;
585                 }
586
587                 ret = find_mount_root(subvol, &mount_root);
588                 if (ret < 0) {
589                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
590                                         "%s\n", subvol,
591                                 strerror(-ret));
592                         goto out;
593                 }
594                 if (strcmp(send.root_path, mount_root) != 0) {
595                         ret = -EINVAL;
596                         fprintf(stderr, "ERROR: all subvols must be from the "
597                                         "same fs.\n");
598                         goto out;
599                 }
600                 free(mount_root);
601
602                 ret = is_subvol_ro(&send, subvol);
603                 if (ret < 0)
604                         goto out;
605                 if (!ret) {
606                         ret = -EINVAL;
607                         fprintf(stderr, "ERROR: %s is not read-only.\n",
608                                         subvol);
609                         goto out;
610                 }
611         }
612
613         for (i = optind; i < argc; i++) {
614                 int is_first_subvol;
615                 int is_last_subvol;
616
617                 free(subvol);
618                 subvol = argv[i];
619
620                 fprintf(stderr, "At subvol %s\n", subvol);
621
622                 subvol = realpath(subvol, NULL);
623                 if (!subvol) {
624                         ret = -errno;
625                         fprintf(stderr, "ERROR: realpath %s failed. "
626                                         "%s\n", argv[i], strerror(-ret));
627                         goto out;
628                 }
629
630                 if (!full_send && !parent_root_id) {
631                         ret = find_good_parent(&send, root_id, &parent_root_id);
632                         if (ret < 0) {
633                                 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
634                                         root_id);
635                                 goto out;
636                         }
637                 }
638
639                 ret = is_subvol_ro(&send, subvol);
640                 if (ret < 0)
641                         goto out;
642                 if (!ret) {
643                         ret = -EINVAL;
644                         fprintf(stderr, "ERROR: %s is not read-only.\n",
645                                         subvol);
646                         goto out;
647                 }
648
649                 if (new_end_cmd_semantic) {
650                         /* require new kernel */
651                         is_first_subvol = (i == optind);
652                         is_last_subvol = (i == argc - 1);
653                 } else {
654                         /* be compatible to old and new kernel */
655                         is_first_subvol = 1;
656                         is_last_subvol = 1;
657                 }
658                 ret = do_send(&send, parent_root_id, is_first_subvol,
659                               is_last_subvol, subvol);
660                 if (ret < 0)
661                         goto out;
662
663                 /* done with this subvol, so add it to the clone sources */
664                 add_clone_source(&send, root_id);
665
666                 parent_root_id = 0;
667                 full_send = 0;
668         }
669
670         ret = 0;
671
672 out:
673         free(subvol);
674         free(snapshot_parent);
675         free(send.clone_sources);
676         if (send.mnt_fd >= 0)
677                 close(send.mnt_fd);
678         free(send.root_path);
679         subvol_uuid_search_finit(&send.sus);
680         return !!ret;
681 }
682
683 const char * const cmd_send_usage[] = {
684         "btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
685         "Send the subvolume(s) to stdout.",
686         "Sends the subvolume(s) specified by <subvol> to stdout.",
687         "By default, this will send the whole subvolume. To do an incremental",
688         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
689         "any additional local snapshots, use '-c <clone-src>' (multiple times",
690         "where applicable). You must not specify clone sources unless you",
691         "guarantee that these snapshots are exactly in the same state on both",
692         "sides, the sender and the receiver. It is allowed to omit the",
693         "'-p <parent>' option when '-c <clone-src>' options are given, in",
694         "which case 'btrfs send' will determine a suitable parent among the",
695         "clone sources itself.",
696         "\n",
697         "-v               Enable verbose debug output. Each occurrence of",
698         "                 this option increases the verbose level more.",
699         "-e               If sending multiple subvols at once, use the new",
700         "                 format and omit the end-cmd between the subvols.",
701         "-p <parent>      Send an incremental stream from <parent> to",
702         "                 <subvol>.",
703         "-c <clone-src>   Use this snapshot as a clone source for an ",
704         "                 incremental send (multiple allowed)",
705         "-f <outfile>     Output is normally written to stdout. To write to",
706         "                 a file, use this option. An alternative would be to",
707         "                 use pipes.",
708         NULL
709 };