btrfs-progs: Fix getopt on arm platforms
[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         int 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         if (isatty(send.dump_fd)) {
433                 fprintf(stderr, "ERROR: not dumping send stream into a terminal, redirect it into a file\n");
434                 return 1;
435         }
436
437         while ((c = getopt(argc, argv, "vf:i:p:")) != -1) {
438                 switch (c) {
439                 case 'v':
440                         g_verbose++;
441                         break;
442                 case 'i': {
443                         subvol = realpath(optarg, NULL);
444                         if (!subvol) {
445                                 ret = -errno;
446                                 fprintf(stderr, "ERROR: realpath %s failed. "
447                                                 "%s\n", optarg, strerror(-ret));
448                                 goto out;
449                         }
450
451                         ret = init_root_path(&send, subvol);
452                         if (ret < 0)
453                                 goto out;
454
455                         ret = get_root_id(&send, get_subvol_name(&send, subvol),
456                                         &root_id);
457                         if (ret < 0) {
458                                 fprintf(stderr, "ERROR: could not resolve "
459                                                 "root_id for %s\n", subvol);
460                                 goto out;
461                         }
462                         add_clone_source(&send, root_id);
463                         free(subvol);
464                         break;
465                 }
466                 case 'f':
467                         outname = optarg;
468                         break;
469                 case 'p':
470                         snapshot_parent = realpath(optarg, NULL);
471                         if (!snapshot_parent) {
472                                 ret = -errno;
473                                 fprintf(stderr, "ERROR: realpath %s failed. "
474                                                 "%s\n", optarg, strerror(-ret));
475                                 goto out;
476                         }
477                         break;
478                 case '?':
479                 default:
480                         fprintf(stderr, "ERROR: send args invalid.\n");
481                         return 1;
482                 }
483         }
484
485         if (optind == argc) {
486                 fprintf(stderr, "ERROR: send needs path to snapshot\n");
487                 return 1;
488         }
489
490         if (outname != NULL) {
491                 send.dump_fd = creat(outname, 0600);
492                 if (send.dump_fd == -1) {
493                         ret = -errno;
494                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
495                                         outname, strerror(-ret));
496                         goto out;
497                 }
498         }
499
500         /* use first send subvol to determine mount_root */
501         subvol = argv[optind];
502
503         subvol = realpath(argv[optind], NULL);
504         if (!subvol) {
505                 ret = -errno;
506                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
507                 goto out;
508         }
509
510         ret = init_root_path(&send, subvol);
511         if (ret < 0)
512                 goto out;
513
514         if (snapshot_parent != NULL) {
515                 ret = get_root_id(&send,
516                                 get_subvol_name(&send, snapshot_parent),
517                                 &parent_root_id);
518                 if (ret < 0) {
519                         fprintf(stderr, "ERROR: could not resolve root_id "
520                                         "for %s\n", snapshot_parent);
521                         goto out;
522                 }
523
524                 add_clone_source(&send, parent_root_id);
525         }
526
527         for (i = optind; i < argc; i++) {
528                 subvol = realpath(argv[i], NULL);
529                 if (!subvol) {
530                         ret = -errno;
531                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
532                         goto out;
533                 }
534
535                 ret = find_mount_root(subvol, &mount_root);
536                 if (ret < 0) {
537                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
538                                         "%s\n", subvol,
539                                 strerror(-ret));
540                         goto out;
541                 }
542                 if (strcmp(send.root_path, mount_root) != 0) {
543                         ret = -EINVAL;
544                         fprintf(stderr, "ERROR: all subvols must be from the "
545                                         "same fs.\n");
546                         goto out;
547                 }
548                 free(mount_root);
549
550                 ret = is_subvol_ro(&send, subvol);
551                 if (ret < 0)
552                         goto out;
553                 if (!ret) {
554                         ret = -EINVAL;
555                         fprintf(stderr, "ERROR: %s is not read-only.\n",
556                                         subvol);
557                         goto out;
558                 }
559                 free(subvol);
560         }
561
562         for (i = optind; i < argc; i++) {
563                 subvol = argv[i];
564
565                 fprintf(stderr, "At subvol %s\n", subvol);
566
567                 subvol = realpath(subvol, NULL);
568                 if (!subvol) {
569                         ret = -errno;
570                         fprintf(stderr, "ERROR: realpath %s failed. "
571                                         "%s\n", argv[i], strerror(-ret));
572                         goto out;
573                 }
574
575                 ret = get_root_id(&send, get_subvol_name(&send, subvol),
576                                 &root_id);
577                 if (ret < 0) {
578                         fprintf(stderr, "ERROR: could not resolve root_id "
579                                         "for %s\n", subvol);
580                         goto out;
581                 }
582
583                 if (!parent_root_id) {
584                         ret = find_good_parent(&send, root_id, &parent_root_id);
585                         if (ret < 0)
586                                 parent_root_id = 0;
587                 }
588
589                 ret = is_subvol_ro(&send, subvol);
590                 if (ret < 0)
591                         goto out;
592                 if (!ret) {
593                         ret = -EINVAL;
594                         fprintf(stderr, "ERROR: %s is not read-only.\n",
595                                         subvol);
596                         goto out;
597                 }
598
599                 ret = do_send(&send, root_id, parent_root_id);
600                 if (ret < 0)
601                         goto out;
602
603                 /* done with this subvol, so add it to the clone sources */
604                 add_clone_source(&send, root_id);
605
606                 parent_root_id = 0;
607                 free(subvol);
608         }
609
610         ret = 0;
611
612 out:
613         if (send.mnt_fd >= 0)
614                 close(send.mnt_fd);
615         return ret;
616 }
617
618 static const char * const send_cmd_group_usage[] = {
619         "btrfs send <command> <args>",
620         NULL
621 };
622
623 static const char * const cmd_send_usage[] = {
624         "btrfs send [-v] [-i <subvol>] [-p <parent>] <subvol>",
625         "Send the subvolume to stdout.",
626         "Sends the subvolume specified by <subvol> to stdout.",
627         "By default, this will send the whole subvolume. To do",
628         "an incremental send, one or multiple '-i <clone_source>'",
629         "arguments have to be specified. A 'clone source' is",
630         "a subvolume that is known to exist on the receiving",
631         "side in exactly the same state as on the sending side.\n",
632         "Normally, a good snapshot parent is searched automatically",
633         "in the list of 'clone sources'. To override this, use",
634         "'-p <parent>' to manually specify a snapshot parent.",
635         "A manually specified snapshot parent is also regarded",
636         "as 'clone source'.\n",
637         "-v               Enable verbose debug output. Each",
638         "                 occurrency of this option increases the",
639         "                 verbose level more.",
640         "-i <subvol>      Informs btrfs send that this subvolume,",
641         "                 can be taken as 'clone source'. This can",
642         "                 be used for incremental sends.",
643         "-p <subvol>      Disable automatic snaphot parent",
644         "                 determination and use <subvol> as parent.",
645         "                 This subvolume is also added to the list",
646         "                 of 'clone sources' (see -i).",
647         "-f <outfile>     Output is normally written to stdout.",
648         "                 To write to a file, use this option.",
649         "                 An alternative would be to use pipes.",
650         NULL
651 };
652
653 const struct cmd_group send_cmd_group = {
654         send_cmd_group_usage, NULL, {
655                 { "send", cmd_send_start, cmd_send_usage, NULL, 0 },
656                 { 0, 0, 0, 0, 0 },
657         },
658 };
659
660 int cmd_send(int argc, char **argv)
661 {
662         return cmd_send_start(argc, argv);
663 }