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