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