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