Btrfs-prog/send: fix wrong best-parent assignment in, find_good_parent()
[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 = parent2;
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_id)
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_id;
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         int full_send = 1;
430
431         memset(&send, 0, sizeof(send));
432         send.dump_fd = fileno(stdout);
433
434         if (isatty(send.dump_fd)) {
435                 fprintf(stderr, "ERROR: not dumping send stream into a terminal, redirect it into a file\n");
436                 return 1;
437         }
438
439         while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
440                 switch (c) {
441                 case 'v':
442                         g_verbose++;
443                         break;
444                 case 'c':
445                         subvol = realpath(optarg, NULL);
446                         if (!subvol) {
447                                 ret = -errno;
448                                 fprintf(stderr, "ERROR: realpath %s failed. "
449                                                 "%s\n", optarg, strerror(-ret));
450                                 goto out;
451                         }
452
453                         ret = init_root_path(&send, subvol);
454                         if (ret < 0)
455                                 goto out;
456
457                         ret = get_root_id(&send, get_subvol_name(&send, subvol),
458                                         &root_id);
459                         if (ret < 0) {
460                                 fprintf(stderr, "ERROR: could not resolve "
461                                                 "root_id for %s\n", subvol);
462                                 goto out;
463                         }
464                         add_clone_source(&send, root_id);
465                         free(subvol);
466                         full_send = 0;
467                         break;
468                 case 'f':
469                         outname = optarg;
470                         break;
471                 case 'p':
472                         if (snapshot_parent) {
473                                 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
474                                 return 1;
475                         }
476                         snapshot_parent = realpath(optarg, NULL);
477                         if (!snapshot_parent) {
478                                 ret = -errno;
479                                 fprintf(stderr, "ERROR: realpath %s failed. "
480                                                 "%s\n", optarg, strerror(-ret));
481                                 goto out;
482                         }
483                         full_send = 0;
484                         break;
485                 case 'i':
486                         fprintf(stderr,
487                                 "ERROR: -i was removed, use -c instead\n");
488                         return 1;
489                 case '?':
490                 default:
491                         fprintf(stderr, "ERROR: send args invalid.\n");
492                         return 1;
493                 }
494         }
495
496         if (optind == argc) {
497                 fprintf(stderr, "ERROR: send needs path to snapshot\n");
498                 return 1;
499         }
500
501         if (outname != NULL) {
502                 send.dump_fd = creat(outname, 0600);
503                 if (send.dump_fd == -1) {
504                         ret = -errno;
505                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
506                                         outname, strerror(-ret));
507                         goto out;
508                 }
509         }
510
511         /* use first send subvol to determine mount_root */
512         subvol = argv[optind];
513
514         subvol = realpath(argv[optind], NULL);
515         if (!subvol) {
516                 ret = -errno;
517                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
518                 goto out;
519         }
520
521         ret = init_root_path(&send, subvol);
522         if (ret < 0)
523                 goto out;
524
525         if (snapshot_parent != NULL) {
526                 ret = get_root_id(&send,
527                                 get_subvol_name(&send, snapshot_parent),
528                                 &parent_root_id);
529                 if (ret < 0) {
530                         fprintf(stderr, "ERROR: could not resolve root_id "
531                                         "for %s\n", snapshot_parent);
532                         goto out;
533                 }
534
535                 add_clone_source(&send, parent_root_id);
536         }
537
538         for (i = optind; i < argc; i++) {
539                 subvol = realpath(argv[i], NULL);
540                 if (!subvol) {
541                         ret = -errno;
542                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
543                         goto out;
544                 }
545
546                 ret = find_mount_root(subvol, &mount_root);
547                 if (ret < 0) {
548                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
549                                         "%s\n", subvol,
550                                 strerror(-ret));
551                         goto out;
552                 }
553                 if (strcmp(send.root_path, mount_root) != 0) {
554                         ret = -EINVAL;
555                         fprintf(stderr, "ERROR: all subvols must be from the "
556                                         "same fs.\n");
557                         goto out;
558                 }
559                 free(mount_root);
560
561                 ret = is_subvol_ro(&send, subvol);
562                 if (ret < 0)
563                         goto out;
564                 if (!ret) {
565                         ret = -EINVAL;
566                         fprintf(stderr, "ERROR: %s is not read-only.\n",
567                                         subvol);
568                         goto out;
569                 }
570                 free(subvol);
571         }
572
573         for (i = optind; i < argc; i++) {
574                 subvol = argv[i];
575
576                 fprintf(stderr, "At subvol %s\n", subvol);
577
578                 subvol = realpath(subvol, NULL);
579                 if (!subvol) {
580                         ret = -errno;
581                         fprintf(stderr, "ERROR: realpath %s failed. "
582                                         "%s\n", argv[i], strerror(-ret));
583                         goto out;
584                 }
585
586                 ret = get_root_id(&send, get_subvol_name(&send, subvol),
587                                 &root_id);
588                 if (ret < 0) {
589                         fprintf(stderr, "ERROR: could not resolve root_id "
590                                         "for %s\n", subvol);
591                         goto out;
592                 }
593
594                 if (!full_send && !parent_root_id) {
595                         ret = find_good_parent(&send, root_id, &parent_root_id);
596                         if (ret < 0) {
597                                 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
598                                         root_id);
599                                 goto out;
600                         }
601                 }
602
603                 ret = is_subvol_ro(&send, subvol);
604                 if (ret < 0)
605                         goto out;
606                 if (!ret) {
607                         ret = -EINVAL;
608                         fprintf(stderr, "ERROR: %s is not read-only.\n",
609                                         subvol);
610                         goto out;
611                 }
612
613                 ret = do_send(&send, root_id, parent_root_id);
614                 if (ret < 0)
615                         goto out;
616
617                 /* done with this subvol, so add it to the clone sources */
618                 add_clone_source(&send, root_id);
619
620                 parent_root_id = 0;
621                 full_send = 0;
622                 free(subvol);
623         }
624
625         ret = 0;
626
627 out:
628         if (send.mnt_fd >= 0)
629                 close(send.mnt_fd);
630         return ret;
631 }
632
633 static const char * const send_cmd_group_usage[] = {
634         "btrfs send <command> <args>",
635         NULL
636 };
637
638 static const char * const cmd_send_usage[] = {
639         "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
640         "Send the subvolume to stdout.",
641         "Sends the subvolume specified by <subvol> to stdout.",
642         "By default, this will send the whole subvolume. To do an incremental",
643         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
644         "any additional local snapshots, use -c <clone-src> (multiple times",
645         "where applicable). You must not specify clone sources unless you",
646         "guarantee that these snapshots are exactly in the same state on both",
647         "sides, the sender and the receiver. It is allowed to omit the",
648         "'-p <parent>' option when '-c <clone-src>' options are given, in",
649         "which case 'btrfs send' will determine a suitable parent among the",
650         "clone sources itself.",
651         "\n",
652         "-v               Enable verbose debug output. Each occurrency of",
653         "                 this option increases the verbose level more.",
654         "-p <parent>      Send an incremental stream from <parent> to",
655         "                 <subvol>.",
656         "-c <clone-src>   Use this snapshot as a clone source for an ",
657         "                 incremental send (multiple allowed)",
658         "-f <outfile>     Output is normally written to stdout. To write to",
659         "                 a file, use this option. An alternative would be to",
660         "                 use pipes.",
661         NULL
662 };
663
664 const struct cmd_group send_cmd_group = {
665         send_cmd_group_usage, NULL, {
666                 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
667                 { 0, 0, 0, 0, 0 },
668         },
669 };
670
671 int cmd_send(int argc, char **argv)
672 {
673         return cmd_send_start(argc, argv);
674 }