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