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