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