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