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