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