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