Btrfs-progs: Use /proc/mounts instead of /etc/mtab
[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("/proc/mounts", "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 = parent2;
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_id)
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] = {-1, -1};
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         memset(&io_send, 0, sizeof(io_send));
277         io_send.send_fd = pipefd[1];
278         send->send_fd = pipefd[0];
279
280         if (!ret)
281                 ret = pthread_create(&t_read, &t_attr, dump_thread,
282                                         send);
283         if (ret) {
284                 ret = -ret;
285                 fprintf(stderr, "ERROR: thread setup failed: %s\n",
286                         strerror(-ret));
287                 goto out;
288         }
289
290         io_send.clone_sources = (__u64*)send->clone_sources;
291         io_send.clone_sources_count = send->clone_sources_count;
292         io_send.parent_root = parent_root_id;
293         ret = ioctl(subvol_fd, BTRFS_IOC_SEND, &io_send);
294         if (ret) {
295                 ret = -errno;
296                 fprintf(stderr, "ERROR: send ioctl failed with %d: %s\n", ret,
297                         strerror(-ret));
298                 goto out;
299         }
300         if (g_verbose > 0)
301                 fprintf(stderr, "BTRFS_IOC_SEND returned %d\n", ret);
302
303         if (g_verbose > 0)
304                 fprintf(stderr, "joining genl thread\n");
305
306         close(pipefd[1]);
307         pipefd[1] = 0;
308
309         ret = pthread_join(t_read, &t_err);
310         if (ret) {
311                 ret = -ret;
312                 fprintf(stderr, "ERROR: pthread_join failed: %s\n",
313                         strerror(-ret));
314                 goto out;
315         }
316         if (t_err) {
317                 ret = (long int)t_err;
318                 fprintf(stderr, "ERROR: failed to process send stream, ret=%ld "
319                         "(%s)\n", (long int)t_err, strerror(-ret));
320                 goto out;
321         }
322
323         pthread_attr_destroy(&t_attr);
324
325         ret = 0;
326
327 out:
328         if (subvol_fd != -1)
329                 close(subvol_fd);
330         if (pipefd[0] != -1)
331                 close(pipefd[0]);
332         if (pipefd[1] != -1)
333                 close(pipefd[1]);
334         return ret;
335 }
336
337 char *get_subvol_name(char *mnt, char *full_path)
338 {
339         int len = strlen(mnt);
340         if (!len)
341                 return full_path;
342         if (mnt[len - 1] != '/')
343                 len += 1;
344
345         return full_path + len;
346 }
347
348 static int init_root_path(struct btrfs_send *s, const char *subvol)
349 {
350         int ret = 0;
351
352         if (s->root_path)
353                 goto out;
354
355         ret = find_mount_root(subvol, &s->root_path);
356         if (ret < 0) {
357                 ret = -EINVAL;
358                 fprintf(stderr, "ERROR: failed to determine mount point "
359                                 "for %s\n", subvol);
360                 goto out;
361         }
362
363         s->mnt_fd = open(s->root_path, O_RDONLY | O_NOATIME);
364         if (s->mnt_fd < 0) {
365                 ret = -errno;
366                 fprintf(stderr, "ERROR: can't open '%s': %s\n", s->root_path,
367                         strerror(-ret));
368                 goto out;
369         }
370
371         ret = subvol_uuid_search_init(s->mnt_fd, &s->sus);
372         if (ret < 0) {
373                 fprintf(stderr, "ERROR: failed to initialize subvol search. "
374                                 "%s\n", strerror(-ret));
375                 goto out;
376         }
377
378 out:
379         return ret;
380
381 }
382
383 static int is_subvol_ro(struct btrfs_send *s, char *subvol)
384 {
385         int ret;
386         u64 flags;
387         int fd = -1;
388
389         fd = openat(s->mnt_fd, subvol, O_RDONLY | O_NOATIME);
390         if (fd < 0) {
391                 ret = -errno;
392                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
393                                 subvol, strerror(-ret));
394                 goto out;
395         }
396
397         ret = ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
398         if (ret < 0) {
399                 ret = -errno;
400                 fprintf(stderr, "ERROR: failed to get flags for subvolume. "
401                                 "%s\n", strerror(-ret));
402                 goto out;
403         }
404
405         if (flags & BTRFS_SUBVOL_RDONLY)
406                 ret = 1;
407         else
408                 ret = 0;
409
410 out:
411         if (fd != -1)
412                 close(fd);
413
414         return ret;
415 }
416
417 int cmd_send_start(int argc, char **argv)
418 {
419         char *subvol = NULL;
420         int c;
421         int ret;
422         char *outname = NULL;
423         struct btrfs_send send;
424         u32 i;
425         char *mount_root = NULL;
426         char *snapshot_parent = NULL;
427         u64 root_id;
428         u64 parent_root_id = 0;
429         int full_send = 1;
430
431         memset(&send, 0, sizeof(send));
432         send.dump_fd = fileno(stdout);
433
434         while ((c = getopt(argc, argv, "vc:f:i:p:")) != -1) {
435                 switch (c) {
436                 case 'v':
437                         g_verbose++;
438                         break;
439                 case 'c':
440                         subvol = realpath(optarg, NULL);
441                         if (!subvol) {
442                                 ret = -errno;
443                                 fprintf(stderr, "ERROR: realpath %s failed. "
444                                                 "%s\n", optarg, strerror(-ret));
445                                 goto out;
446                         }
447
448                         ret = init_root_path(&send, subvol);
449                         if (ret < 0)
450                                 goto out;
451
452                         ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
453                                         &root_id);
454                         if (ret < 0) {
455                                 fprintf(stderr, "ERROR: could not resolve "
456                                                 "root_id for %s\n", subvol);
457                                 goto out;
458                         }
459                         add_clone_source(&send, root_id);
460                         free(subvol);
461                         full_send = 0;
462                         break;
463                 case 'f':
464                         outname = optarg;
465                         break;
466                 case 'p':
467                         if (snapshot_parent) {
468                                 fprintf(stderr, "ERROR: you cannot have more than one parent (-p)\n");
469                                 return 1;
470                         }
471                         snapshot_parent = realpath(optarg, NULL);
472                         if (!snapshot_parent) {
473                                 ret = -errno;
474                                 fprintf(stderr, "ERROR: realpath %s failed. "
475                                                 "%s\n", optarg, strerror(-ret));
476                                 goto out;
477                         }
478                         full_send = 0;
479                         break;
480                 case 'i':
481                         fprintf(stderr,
482                                 "ERROR: -i was removed, use -c instead\n");
483                         return 1;
484                 case '?':
485                 default:
486                         fprintf(stderr, "ERROR: send args invalid.\n");
487                         return 1;
488                 }
489         }
490
491         if (optind == argc) {
492                 fprintf(stderr, "ERROR: send needs path to snapshot\n");
493                 return 1;
494         }
495
496         if (outname != NULL) {
497                 send.dump_fd = creat(outname, 0600);
498                 if (send.dump_fd == -1) {
499                         ret = -errno;
500                         fprintf(stderr, "ERROR: can't create '%s': %s\n",
501                                         outname, strerror(-ret));
502                         goto out;
503                 }
504         }
505
506         if (isatty(send.dump_fd)) {
507                 fprintf(stderr, 
508                         "ERROR: not dumping send stream into a terminal, "
509                         "redirect it into a file\n");
510                 return 1;
511         }
512
513         /* use first send subvol to determine mount_root */
514         subvol = argv[optind];
515
516         subvol = realpath(argv[optind], NULL);
517         if (!subvol) {
518                 ret = -errno;
519                 fprintf(stderr, "ERROR: unable to resolve %s\n", argv[optind]);
520                 goto out;
521         }
522
523         ret = init_root_path(&send, subvol);
524         if (ret < 0)
525                 goto out;
526
527         if (snapshot_parent != NULL) {
528                 ret = get_root_id(&send,
529                                 get_subvol_name(send.root_path, snapshot_parent),
530                                 &parent_root_id);
531                 if (ret < 0) {
532                         fprintf(stderr, "ERROR: could not resolve root_id "
533                                         "for %s\n", snapshot_parent);
534                         goto out;
535                 }
536
537                 add_clone_source(&send, parent_root_id);
538         }
539
540         for (i = optind; i < argc; i++) {
541                 subvol = realpath(argv[i], NULL);
542                 if (!subvol) {
543                         ret = -errno;
544                         fprintf(stderr, "ERROR: unable to resolve %s\n", argv[i]);
545                         goto out;
546                 }
547
548                 ret = find_mount_root(subvol, &mount_root);
549                 if (ret < 0) {
550                         fprintf(stderr, "ERROR: find_mount_root failed on %s: "
551                                         "%s\n", subvol,
552                                 strerror(-ret));
553                         goto out;
554                 }
555                 if (strcmp(send.root_path, mount_root) != 0) {
556                         ret = -EINVAL;
557                         fprintf(stderr, "ERROR: all subvols must be from the "
558                                         "same fs.\n");
559                         goto out;
560                 }
561                 free(mount_root);
562
563                 ret = is_subvol_ro(&send, subvol);
564                 if (ret < 0)
565                         goto out;
566                 if (!ret) {
567                         ret = -EINVAL;
568                         fprintf(stderr, "ERROR: %s is not read-only.\n",
569                                         subvol);
570                         goto out;
571                 }
572                 free(subvol);
573         }
574
575         for (i = optind; i < argc; i++) {
576                 subvol = argv[i];
577
578                 fprintf(stderr, "At subvol %s\n", subvol);
579
580                 subvol = realpath(subvol, NULL);
581                 if (!subvol) {
582                         ret = -errno;
583                         fprintf(stderr, "ERROR: realpath %s failed. "
584                                         "%s\n", argv[i], strerror(-ret));
585                         goto out;
586                 }
587
588                 ret = get_root_id(&send, get_subvol_name(send.root_path, subvol),
589                                 &root_id);
590                 if (ret < 0) {
591                         fprintf(stderr, "ERROR: could not resolve root_id "
592                                         "for %s\n", subvol);
593                         goto out;
594                 }
595
596                 if (!full_send && !parent_root_id) {
597                         ret = find_good_parent(&send, root_id, &parent_root_id);
598                         if (ret < 0) {
599                                 fprintf(stderr, "ERROR: parent determination failed for %lld\n",
600                                         root_id);
601                                 goto out;
602                         }
603                 }
604
605                 ret = is_subvol_ro(&send, subvol);
606                 if (ret < 0)
607                         goto out;
608                 if (!ret) {
609                         ret = -EINVAL;
610                         fprintf(stderr, "ERROR: %s is not read-only.\n",
611                                         subvol);
612                         goto out;
613                 }
614
615                 ret = do_send(&send, root_id, parent_root_id);
616                 if (ret < 0)
617                         goto out;
618
619                 /* done with this subvol, so add it to the clone sources */
620                 add_clone_source(&send, root_id);
621
622                 parent_root_id = 0;
623                 full_send = 0;
624                 free(subvol);
625         }
626
627         ret = 0;
628
629 out:
630         if (send.mnt_fd >= 0)
631                 close(send.mnt_fd);
632         return ret;
633 }
634
635 static const char * const send_cmd_group_usage[] = {
636         "btrfs send <command> <args>",
637         NULL
638 };
639
640 const char * const cmd_send_usage[] = {
641         "btrfs send [-v] [-p <parent>] [-c <clone-src>] <subvol>",
642         "Send the subvolume to stdout.",
643         "Sends the subvolume specified by <subvol> to stdout.",
644         "By default, this will send the whole subvolume. To do an incremental",
645         "send, use '-p <parent>'. If you want to allow btrfs to clone from",
646         "any additional local snapshots, use -c <clone-src> (multiple times",
647         "where applicable). You must not specify clone sources unless you",
648         "guarantee that these snapshots are exactly in the same state on both",
649         "sides, the sender and the receiver. It is allowed to omit the",
650         "'-p <parent>' option when '-c <clone-src>' options are given, in",
651         "which case 'btrfs send' will determine a suitable parent among the",
652         "clone sources itself.",
653         "\n",
654         "-v               Enable verbose debug output. Each occurrence of",
655         "                 this option increases the verbose level more.",
656         "-p <parent>      Send an incremental stream from <parent> to",
657         "                 <subvol>.",
658         "-c <clone-src>   Use this snapshot as a clone source for an ",
659         "                 incremental send (multiple allowed)",
660         "-f <outfile>     Output is normally written to stdout. To write to",
661         "                 a file, use this option. An alternative would be to",
662         "                 use pipes.",
663         NULL
664 };
665
666 int cmd_send(int argc, char **argv)
667 {
668         return cmd_send_start(argc, argv);
669 }