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