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