Btrfs-progs: add btrfs send/receive commands
[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 <unistd.h>
22 #include <stdint.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <pthread.h>
26 #include <math.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
30
31 #include <uuid/uuid.h>
32
33 #include "ctree.h"
34 #include "ioctl.h"
35 #include "commands.h"
36 #include "list.h"
37
38 #include "send.h"
39 #include "send-utils.h"
40
41 static int g_verbose = 0;
42
43 struct btrfs_send {
44         int send_fd;
45         int dump_fd;
46         int mnt_fd;
47
48         u64 *clone_sources;
49         u64 clone_sources_count;
50
51         char *root_path;
52         struct subvol_uuid_search sus;
53 };
54
55 int find_mount_root(const char *path, char **mount_root)
56 {
57         int ret;
58         char cur[BTRFS_PATH_NAME_MAX];
59         char fsid[BTRFS_FSID_SIZE];
60         int fd;
61         struct stat st;
62         int pos;
63         char *tmp;
64
65         struct btrfs_ioctl_fs_info_args args;
66
67         fd = open(path, O_RDONLY | O_NOATIME);
68         if (fd < 0) {
69                 ret = -errno;
70                 goto out;
71         }
72
73         ret = fstat(fd, &st);
74         if (fd < 0) {
75                 ret = -errno;
76                 goto out;
77         }
78         if (!S_ISDIR(st.st_mode)) {
79                 ret = -ENOTDIR;
80                 goto out;
81         }
82
83         ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
84         if (fd < 0) {
85                 ret = -errno;
86                 goto out;
87         }
88         memcpy(fsid, args.fsid, BTRFS_FSID_SIZE);
89         close(fd);
90         fd = -1;
91
92         strcpy(cur, path);
93         while (1) {
94                 tmp = strrchr(cur, '/');
95                 if (!tmp)
96                         break;
97                 if (tmp == cur)
98                         break;
99                 pos = tmp - cur;
100                 cur[pos] = 0;
101
102                 fd = open(cur, O_RDONLY | O_NOATIME);
103                 if (fd < 0) {
104                         ret = -errno;
105                         goto out;
106                 }
107
108                 ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args);
109                 close(fd);
110                 fd = -1;
111                 if (ret < 0) {
112                         cur[pos] = '/';
113                         break;
114                 }
115                 if (memcmp(fsid, args.fsid, BTRFS_FSID_SIZE) != 0) {
116                         cur[pos] = '/';
117                         break;
118                 }
119         }
120
121         ret = 0;
122         *mount_root = realpath(cur, NULL);
123
124 out:
125         if (fd != -1)
126                 close(fd);
127         return ret;
128 }
129
130 static int get_root_id(struct btrfs_send *s, const char *path, u64 *root_id)
131 {
132         struct subvol_info *si;
133
134         si = subvol_uuid_search(&s->sus, 0, NULL, 0, path,
135                         subvol_search_by_path);
136         if (!si)
137                 return -ENOENT;
138         *root_id = si->root_id;
139         return 0;
140 }
141
142 static struct subvol_info *get_parent(struct btrfs_send *s, u64 root_id)
143 {
144         struct subvol_info *si;
145
146         si = subvol_uuid_search(&s->sus, root_id, NULL, 0, NULL,
147                         subvol_search_by_root_id);
148         if (!si)
149                 return NULL;
150
151         si = subvol_uuid_search(&s->sus, 0, si->parent_uuid, 0, NULL,
152                         subvol_search_by_uuid);
153         if (!si)
154                 return NULL;
155         return si;
156 }
157
158 static int find_good_parent(struct btrfs_send *s, u64 root_id, u64 *found)
159 {
160         int ret;
161         struct subvol_info *parent;
162         struct subvol_info *parent2;
163         struct subvol_info *best_parent = NULL;
164         __s64 tmp;
165         u64 best_diff = (u64)-1;
166         int i;
167
168         parent = get_parent(s, root_id);
169         if (!parent) {
170                 ret = -ENOENT;
171                 goto out;
172         }
173
174         for (i = 0; i < s->clone_sources_count; i++) {
175                 if (s->clone_sources[i] == parent->root_id) {
176                         best_parent = parent;
177                         goto out_found;
178                 }
179         }
180
181         for (i = 0; i < s->clone_sources_count; i++) {
182                 parent2 = get_parent(s, s->clone_sources[i]);
183                 if (parent2 != parent)
184                         continue;
185
186                 parent2 = subvol_uuid_search(&s->sus, s->clone_sources[i], NULL,
187                                 0, NULL, subvol_search_by_root_id);
188
189                 tmp = parent2->ctransid - parent->ctransid;
190                 if (tmp < 0)
191                         tmp *= -1;
192                 if (tmp < best_diff) {
193                         best_parent = parent;
194                         best_diff = tmp;
195                 }
196         }
197
198         if (!best_parent) {
199                 ret = -ENOENT;
200                 goto out;
201         }
202
203 out_found:
204         *found = best_parent->root_id;
205         ret = 0;
206
207 out:
208         return ret;
209 }
210
211 static void add_clone_source(struct btrfs_send *s, u64 root_id)
212 {
213         s->clone_sources = realloc(s->clone_sources,
214                 sizeof(*s->clone_sources) * (s->clone_sources_count + 1));
215         s->clone_sources[s->clone_sources_count++] = root_id;
216 }
217
218 static int write_buf(int fd, const void *buf, int size)
219 {
220         int ret;
221         int pos = 0;
222
223         while (pos < size) {
224                 ret = write(fd, (char*)buf + pos, size - pos);
225                 if (ret < 0) {
226                         ret = -errno;
227                         fprintf(stderr, "ERROR: failed to dump stream. %s",
228                                         strerror(-ret));
229                         goto out;
230                 }
231                 if (!ret) {
232                         ret = -EIO;
233                         fprintf(stderr, "ERROR: failed to dump stream. %s",
234                                         strerror(-ret));
235                         goto out;
236                 }
237                 pos += ret;
238         }
239         ret = 0;
240
241 out:
242         return ret;
243 }
244
245 static void *dump_thread(void *arg_)
246 {
247         int ret;
248         struct btrfs_send *s = (struct btrfs_send*)arg_;
249         char buf[4096];
250         int readed;
251
252         while (1) {
253                 readed = read(s->send_fd, buf, sizeof(buf));
254                 if (readed < 0) {
255                         ret = -errno;
256                         fprintf(stderr, "ERROR: failed to read stream from "
257                                         "kernel. %s\n", strerror(-ret));
258                         goto out;
259                 }
260                 if (!readed) {
261                         ret = 0;
262                         goto out;
263                 }
264                 ret = write_buf(s->dump_fd, buf, readed);
265                 if (ret < 0)
266                         goto out;
267         }
268
269 out:
270         if (ret < 0) {
271                 exit(-ret);
272         }
273
274         return ERR_PTR(ret);
275 }
276
277 static int do_send(struct btrfs_send *send, u64 root_id, u64 parent_root)
278 {
279         int ret;
280         pthread_t t_read;
281         pthread_attr_t t_attr;
282         struct btrfs_ioctl_send_args io_send;
283         struct subvol_info *si;
284         void *t_err = NULL;
285         int subvol_fd = -1;
286         int pipefd[2];
287
288         si = subvol_uuid_search(&send->sus, root_id, NULL, 0, NULL,
289                         subvol_search_by_root_id);
290         if (!si) {
291                 ret = -ENOENT;
292                 fprintf(stderr, "ERROR: could not find subvol info for %llu",
293                                 root_id);
294                 goto out;
295         }
296
297         subvol_fd = openat(send->mnt_fd, si->path, O_RDONLY | O_NOATIME);
298         if (subvol_fd < 0) {
299                 ret = -errno;
300                 fprintf(stderr, "ERROR: open %s failed. %s\n", si->path,
301                                 strerror(-ret));
302                 goto out;
303         }
304
305         ret = pthread_attr_init(&t_attr);
306
307         ret = pipe(pipefd);
308         if (ret < 0) {
309                 ret = -errno;
310                 fprintf(stderr, "ERROR: pipe failed. %s\n", strerror(-ret));
311                 goto out;
312         }
313
314         io_send.send_fd = pipefd[1];
315         send->send_fd = pipefd[0];
316
317         if (!ret)
318                 ret = pthread_create(&t_read, &t_attr, dump_thread,
319                                         send);
320         if (ret) {
321                 ret = -ret;
322                 fprintf(stderr, "ERROR: thread setup failed: %s\n",
323                         strerror(-ret));
324                 goto out;
325         }
326
327         io_send.clone_sources = (__u64*)send->clone_sources;
328         io_send.clone_sources_count = send->clone_sources_count;
329         io_send.parent_root = parent_root;
330         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
331         if (ret) {
332                 ret = -errno;
333                 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
334                         strerror(-ret));
335                 goto out;
336         }
337         if (g_verbose > 0)
338                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
339
340         if (g_verbose > 0)
341                 fprintf(stderr, "joining genl thread\n");
342
343         close(pipefd[1]);
344         pipefd[1] = 0;
345
346         ret = pthread_join(t_read, &t_err);
347         if (ret) {
348                 ret = -ret;
349                 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
350                         strerror(-ret));
351                 goto out;
352         }
353         if (t_err) {
354                 ret = (long int)t_err;
355                 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
356                         "(%s)\n", (long int)t_err, strerror(-ret));
357                 goto out;
358         }
359
360         pthread_attr_destroy(&t_attr);
361
362         ret = 0;
363
364 out:
365         if (subvol_fd != -1)
366                 close(subvol_fd);
367         if (pipefd[0])
368                 close(pipefd[0]);
369         if (pipefd[1])
370                 close(pipefd[1]);
371         return ret;
372 }
373
374 static const char *get_subvol_name(struct btrfs_send *s, const char *full_path)
375 {
376         return full_path + strlen(s->root_path) + 1;
377 }
378
379 static int init_root_path(struct btrfs_send *s, const char *subvol)
380 {
381         int ret = 0;
382
383         if (s->root_path)
384                 goto out;
385
386         ret = find_mount_root(subvol, &s->root_path);
387         if (ret < 0) {
388                 ret = -EINVAL;
389                 fprintf(stderr, "ERROR: failed to determine mount point "
390                                 "for %s\n", subvol);
391                 goto out;
392         }
393
394         s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
395         if (s->mnt_fd < 0) {
396                 ret = -errno;
397                 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
398                         strerror(-ret));
399                 goto out;
400         }
401
402         ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
403         if (ret < 0) {
404                 fprintf(stderr, "ERROR: failed to initialize subvol search. "
405                                 "%s\n", strerror(-ret));
406                 goto out;
407         }
408
409 out:
410         return ret;
411
412 }
413
414 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
415 {
416         int ret;
417         u64 flags;
418         int fd = -1;
419
420         fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
421         if (fd < 0) {
422                 ret = -errno;
423                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
424                                 subvol, strerror(-ret));
425                 goto out;
426         }
427
428         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
429         if (ret < 0) {
430                 ret = -errno;
431                 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
432                                 "%s\n", strerror(-ret));
433                 goto out;
434         }
435
436         if (flags & BTRFS_SUBVOL_RDONLY)
437                 ret = 1;
438         else
439                 ret = 0;
440
441 out:
442         if (fd != -1)
443                 close(fd);
444
445         return ret;
446 }
447
448 int cmd_send_start(int argc, char **argv)
449 {
450         char *subvol = NULL;
451         char c;
452         int ret;
453         char *outname = NULL;
454         struct btrfs_send send;
455         u32 i;
456         char *mount_root = NULL;
457         char *snapshot_parent = NULL;
458         u64 root_id;
459         u64 parent_root_id = 0;
460
461         memset(&send, 0, sizeof(send));
462         send.dump_fd = fileno(stdout);
463
464         while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
465                 switch (c) {
466                 case 'v':
467                         g_verbose++;
468                         break;
469                 case 'i': {
470                         subvol = realpath(optarg, NULL);
471                         if (!subvol) {
472                                 ret = -errno;
473                                 fprintf(stderr, "ERROR: realpath %s failed. "
474                                                 "%s\n", optarg, strerror(-ret));
475                                 goto out;
476                         }
477
478                         ret = init_root_path(&send, subvol);
479                         if (ret < 0)
480                                 goto out;
481
482                         ret = get_root_id(&send, get_subvol_name(&send, subvol),
483                                         &root_id);
484                         if (ret < 0) {
485                                 fprintf(stderr, "ERROR: could not resolve "
486                                                 "root_id for %s\n", subvol);
487                                 goto out;
488                         }
489                         add_clone_source(&send, root_id);
490                         free(subvol);
491                         break;
492                 }
493                 case 'f':
494                         outname = optarg;
495                         break;
496                 case 'p':
497                         snapshot_parent = realpath(optarg, NULL);
498                         if (!snapshot_parent) {
499                                 ret = -errno;
500                                 fprintf(stderr, "ERROR: realpath %s failed. "
501                                                 "%s\n", optarg, strerror(-ret));
502                                 goto out;
503                         }
504                         break;
505                 case '?':
506                 default:
507                         fprintf(stderr, "ERROR: send args invalid.\n");
508                         return 1;
509                 }
510         }
511
512         if (optind == argc) {
513                 fprintf(stderr, "ERROR: send needs path to snapshot\n");
514                 return 1;
515         }
516
517         if (outname != NULL) {
518                 send.dump_fd = creat(outname, 0600);
519                 if (send.dump_fd == -1) {
520                         ret = -errno;
521                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
522                                         outname, strerror(-ret));
523                         goto out;
524                 }
525         }
526
527         /* use first send subvol to determine mount_root */
528         subvol = argv[optind];
529
530         ret = init_root_path(&send, subvol);
531         if (ret < 0)
532                 goto out;
533
534         if (snapshot_parent != NULL) {
535                 ret = get_root_id(&send,
536                                 get_subvol_name(&send, snapshot_parent),
537                                 &parent_root_id);
538                 if (ret < 0) {
539                         fprintf(stderr, "ERROR: could not resolve root_id "
540                                         "for %s\n", snapshot_parent);
541                         goto out;
542                 }
543
544                 add_clone_source(&send, parent_root_id);
545         }
546
547         for (i = optind; i < argc; i++) {
548                 subvol = argv[i];
549
550                 ret = find_mount_root(subvol, &mount_root);
551                 if (ret < 0) {
552                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
553                                         "%s\n", subvol,
554                                 strerror(-ret));
555                         goto out;
556                 }
557                 if (strcmp(send.root_path, mount_root) != 0) {
558                         ret = -EINVAL;
559                         fprintf(stderr, "ERROR: all subvols must be from the "
560                                         "same fs.\n");
561                         goto out;
562                 }
563                 free(mount_root);
564
565                 ret = is_subvol_ro(&send, subvol);
566                 if (ret < 0)
567                         goto out;
568                 if (!ret) {
569                         ret = -EINVAL;
570                         fprintf(stderr, "ERROR: %s is not read-only.\n",
571                                         subvol);
572                         goto out;
573                 }
574         }
575
576         for (i = optind; i < argc; i++) {
577                 subvol = argv[i];
578
579                 fprintf(stderr, "At subvol %s\n", subvol);
580
581                 subvol = realpath(subvol, NULL);
582                 if (!subvol) {
583                         ret = -errno;
584                         fprintf(stderr, "ERROR: realpath %s failed. "
585                                         "%s\n", argv[i], strerror(-ret));
586                         goto out;
587                 }
588
589                 ret = get_root_id(&send, get_subvol_name(&send, subvol),
590                                 &root_id);
591                 if (ret < 0) {
592                         fprintf(stderr, "ERROR: could not resolve root_id "
593                                         "for %s\n", subvol);
594                         goto out;
595                 }
596
597                 if (!parent_root_id) {
598                         ret = find_good_parent(&send, root_id, &parent_root_id);
599                         if (ret < 0)
600                                 parent_root_id = 0;
601                 }
602
603                 ret = is_subvol_ro(&send, subvol);
604                 if (ret < 0)
605                         goto out;
606                 if (!ret) {
607                         ret = -EINVAL;
608                         fprintf(stderr, "ERROR: %s is not read-only.\n",
609                                         subvol);
610                         goto out;
611                 }
612
613                 ret = do_send(&send, root_id, parent_root_id);
614                 if (ret < 0)
615                         goto out;
616
617                 /* done with this subvol, so add it to the clone sources */
618                 add_clone_source(&send, root_id);
619
620                 parent_root_id = 0;
621                 free(subvol);
622         }
623
624         ret = 0;
625
626 out:
627         if (send.mnt_fd >= 0)
628                 close(send.mnt_fd);
629         return ret;
630 }
631
632 static const char * const send_cmd_group_usage[] = {
633         "btrfs send <command> <args>",
634         NULL
635 };
636
637 static const char * const cmd_send_usage[] = {
638         "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
639         "Send the subvolume to stdout.",
640         "Sends the subvolume specified by <subvol> to stdout.",
641         "By default, this will send the whole subvolume. To do",
642         "an incremental send, one or multiple '-i <clone_source>'",
643         "arguments have to be specified. A 'clone source' is",
644         "a subvolume that is known to exist on the receiving",
645         "side in exactly the same state as on the sending side.\n",
646         "Normally, a good snapshot parent is searched automatically",
647         "in the list of 'clone sources'. To override this, use",
648         "'-p <parent>' to manually specify a snapshot parent.",
649         "A manually specified snapshot parent is also regarded",
650         "as 'clone source'.\n",
651         "-v               Enable verbose debug output. Each",
652         "                 occurrency of this option increases the",
653         "                 verbose level more.",
654         "-i <subvol>      Informs btrfs send that this subvolume,",
655         "                 can be taken as 'clone source'. This can",
656         "                 be used for incremental sends.",
657         "-p <subvol>      Disable automatic snaphot parent",
658         "                 determination and use <subvol> as parent.",
659         "                 This subvolume is also added to the list",
660         "                 of 'clone sources' (see -i).",
661         "-f <outfile>     Output is normally written to stdout.",
662         "                 To write to a file, use this option.",
663         "                 An alternative would be to use pipes.",
664         NULL
665 };
666
667 const struct cmd_group send_cmd_group = {
668         send_cmd_group_usage, NULL, {
669                 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
670                 { 0, 0, 0, 0, 0 },
671         },
672 };
673
674 int cmd_send(int argc, char **argv)
675 {
676         return cmd_send_start(argc, argv);
677 }