btrfs-progs: Receive: preserve ownership for symlinks, by using 'lchown'
[platform/upstream/btrfs-progs.git] / cmds-receive.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 #define _POSIX_C_SOURCE 200809
21 #define _XOPEN_SOURCE 700
22 #define _BSD_SOURCE
23
24 #include "kerncompat.h"
25
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <dirent.h>
29 #include <fcntl.h>
30 #include <pthread.h>
31 #include <math.h>
32 #include <ftw.h>
33 #include <wait.h>
34
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/xattr.h>
41 #include <uuid/uuid.h>
42
43 #include "ctree.h"
44 #include "ioctl.h"
45 #include "commands.h"
46 #include "list.h"
47
48 #include "send.h"
49 #include "send-stream.h"
50 #include "send-utils.h"
51
52 static int g_verbose = 0;
53
54 struct btrfs_receive
55 {
56         int mnt_fd;
57
58         int write_fd;
59         char *write_path;
60
61         char *root_path;
62         char *full_subvol_path;
63
64         struct subvol_info *cur_subvol;
65         struct subvol_info *parent_subvol;
66
67         struct subvol_uuid_search sus;
68 };
69
70 static int finish_subvol(struct btrfs_receive *r)
71 {
72         int ret;
73         int subvol_fd = -1;
74         int info_fd = -1;
75         struct btrfs_ioctl_received_subvol_args rs_args;
76         char uuid_str[128];
77         u64 flags;
78
79         if (r->cur_subvol == NULL)
80                 return 0;
81
82         subvol_fd = openat(r->mnt_fd, r->cur_subvol->path,
83                         O_RDONLY | O_NOATIME);
84         if (subvol_fd < 0) {
85                 ret = -errno;
86                 fprintf(stderr, "ERROR: open %s failed. %s\n",
87                                 r->cur_subvol->path, strerror(-ret));
88                 goto out;
89         }
90
91         memset(&rs_args, 0, sizeof(rs_args));
92         memcpy(rs_args.uuid, r->cur_subvol->received_uuid, BTRFS_UUID_SIZE);
93         rs_args.stransid = r->cur_subvol->stransid;
94
95         if (g_verbose >= 1) {
96                 uuid_unparse((u8*)rs_args.uuid, uuid_str);
97                 fprintf(stderr, "BTRFS_IOC_SET_RECEIVED_SUBVOL uuid=%s, "
98                                 "stransid=%llu\n", uuid_str, rs_args.stransid);
99         }
100
101         ret = ioctl(subvol_fd, BTRFS_IOC_SET_RECEIVED_SUBVOL, &rs_args);
102         if (ret < 0) {
103                 ret = -errno;
104                 fprintf(stderr, "ERROR: BTRFS_IOC_SET_RECEIVED_SUBVOL failed. %s\n",
105                                 strerror(-ret));
106                 goto out;
107         }
108         r->cur_subvol->rtransid = rs_args.rtransid;
109
110         ret = ioctl(subvol_fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags);
111         if (ret < 0) {
112                 ret = -errno;
113                 fprintf(stderr, "ERROR: BTRFS_IOC_SUBVOL_GETFLAGS failed. %s\n",
114                                 strerror(-ret));
115                 goto out;
116         }
117
118         flags |= BTRFS_SUBVOL_RDONLY;
119
120         ret = ioctl(subvol_fd, BTRFS_IOC_SUBVOL_SETFLAGS, &flags);
121         if (ret < 0) {
122                 ret = -errno;
123                 fprintf(stderr, "ERROR: failed to make subvolume read only. "
124                                 "%s\n", strerror(-ret));
125                 goto out;
126         }
127
128         subvol_uuid_search_add(&r->sus, r->cur_subvol);
129         r->cur_subvol = NULL;
130         ret = 0;
131
132 out:
133         if (subvol_fd != -1)
134                 close(subvol_fd);
135         if (info_fd != -1)
136                 close(info_fd);
137         return ret;
138 }
139
140 static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
141                           void *user)
142 {
143         int ret;
144         struct btrfs_receive *r = user;
145         struct btrfs_ioctl_vol_args args_v1;
146         char uuid_str[128];
147
148         ret = finish_subvol(r);
149         if (ret < 0)
150                 goto out;
151
152         r->cur_subvol = calloc(1, sizeof(*r->cur_subvol));
153         r->parent_subvol = NULL;
154
155         r->cur_subvol->path = strdup(path);
156         r->full_subvol_path = path_cat(r->root_path, path);
157
158         fprintf(stderr, "At subvol %s\n", path);
159
160         memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE);
161         r->cur_subvol->stransid = ctransid;
162
163         if (g_verbose) {
164                 uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str);
165                 fprintf(stderr, "receiving subvol %s uuid=%s, stransid=%llu\n",
166                                 path, uuid_str,
167                                 r->cur_subvol->stransid);
168         }
169
170         memset(&args_v1, 0, sizeof(args_v1));
171         strcpy(args_v1.name, path);
172         ret = ioctl(r->mnt_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);
173         if (ret < 0) {
174                 ret = -errno;
175                 fprintf(stderr, "ERROR: creating subvolume %s failed. "
176                                 "%s\n", path, strerror(-ret));
177                 goto out;
178         }
179
180 out:
181         return ret;
182 }
183
184 static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
185                             const u8 *parent_uuid, u64 parent_ctransid,
186                             void *user)
187 {
188         int ret;
189         struct btrfs_receive *r = user;
190         char uuid_str[128];
191         struct btrfs_ioctl_vol_args_v2 args_v2;
192
193         ret = finish_subvol(r);
194         if (ret < 0)
195                 goto out;
196
197         r->cur_subvol = calloc(1, sizeof(*r->cur_subvol));
198         r->parent_subvol = NULL;
199
200         r->cur_subvol->path = strdup(path);
201         r->full_subvol_path = path_cat(r->root_path, path);
202
203         fprintf(stderr, "At snapshot %s\n", path);
204
205         memcpy(r->cur_subvol->received_uuid, uuid, BTRFS_UUID_SIZE);
206         r->cur_subvol->stransid = ctransid;
207
208         if (g_verbose) {
209                 uuid_unparse((u8*)r->cur_subvol->received_uuid, uuid_str);
210                 fprintf(stderr, "receiving snapshot %s uuid=%s, "
211                                 "ctransid=%llu ", path, uuid_str,
212                                 r->cur_subvol->stransid);
213                 uuid_unparse(parent_uuid, uuid_str);
214                 fprintf(stderr, "parent_uuid=%s, parent_ctransid=%llu\n",
215                                 uuid_str, parent_ctransid);
216         }
217
218         memset(&args_v2, 0, sizeof(args_v2));
219         strcpy(args_v2.name, path);
220
221         r->parent_subvol = subvol_uuid_search(&r->sus, 0, parent_uuid,
222                         parent_ctransid, NULL, subvol_search_by_received_uuid);
223         if (!r->parent_subvol) {
224                 ret = -ENOENT;
225                 fprintf(stderr, "ERROR: could not find parent subvolume\n");
226                 goto out;
227         }
228
229         /*if (rs_args.ctransid > rs_args.rtransid) {
230                 if (!r->force) {
231                         ret = -EINVAL;
232                         fprintf(stderr, "ERROR: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
233                         goto out;
234                 } else {
235                         fprintf(stderr, "WARNING: subvolume %s was modified after it was received.\n", r->subvol_parent_name);
236                 }
237         }*/
238
239         args_v2.fd = openat(r->mnt_fd, r->parent_subvol->path,
240                         O_RDONLY | O_NOATIME);
241         if (args_v2.fd < 0) {
242                 ret = -errno;
243                 fprintf(stderr, "ERROR: open %s failed. %s\n",
244                                 r->parent_subvol->path, strerror(-ret));
245                 goto out;
246         }
247
248         ret = ioctl(r->mnt_fd, BTRFS_IOC_SNAP_CREATE_V2, &args_v2);
249         close(args_v2.fd);
250         if (ret < 0) {
251                 ret = -errno;
252                 fprintf(stderr, "ERROR: creating snapshot %s -> %s "
253                                 "failed. %s\n", r->parent_subvol->path,
254                                 path, strerror(-ret));
255                 goto out;
256         }
257
258 out:
259         return ret;
260 }
261
262 static int process_mkfile(const char *path, void *user)
263 {
264         int ret;
265         struct btrfs_receive *r = user;
266         char *full_path = path_cat(r->full_subvol_path, path);
267
268         if (g_verbose >= 1)
269                 fprintf(stderr, "mkfile %s\n", path);
270
271         ret = creat(full_path, 0600);
272         if (ret < 0) {
273                 ret = -errno;
274                 fprintf(stderr, "ERROR: mkfile %s failed. %s\n", path,
275                                 strerror(-ret));
276                 goto out;
277         }
278         close(ret);
279         ret = 0;
280
281 out:
282         free(full_path);
283         return ret;
284 }
285
286 static int process_mkdir(const char *path, void *user)
287 {
288         int ret;
289         struct btrfs_receive *r = user;
290         char *full_path = path_cat(r->full_subvol_path, path);
291
292         if (g_verbose >= 1)
293                 fprintf(stderr, "mkdir %s\n", path);
294
295         ret = mkdir(full_path, 0700);
296         if (ret < 0) {
297                 ret = -errno;
298                 fprintf(stderr, "ERROR: mkdir %s failed. %s\n", path,
299                                 strerror(-ret));
300         }
301
302         free(full_path);
303         return ret;
304 }
305
306 static int process_mknod(const char *path, u64 mode, u64 dev, void *user)
307 {
308         int ret;
309         struct btrfs_receive *r = user;
310         char *full_path = path_cat(r->full_subvol_path, path);
311
312         if (g_verbose >= 1)
313                 fprintf(stderr, "mknod %s mode=%llu, dev=%llu\n",
314                                 path, mode, dev);
315
316         ret = mknod(full_path, mode & S_IFMT, dev);
317         if (ret < 0) {
318                 ret = -errno;
319                 fprintf(stderr, "ERROR: mknod %s failed. %s\n", path,
320                                 strerror(-ret));
321         }
322
323         free(full_path);
324         return ret;
325 }
326
327 static int process_mkfifo(const char *path, void *user)
328 {
329         int ret;
330         struct btrfs_receive *r = user;
331         char *full_path = path_cat(r->full_subvol_path, path);
332
333         if (g_verbose >= 1)
334                 fprintf(stderr, "mkfifo %s\n", path);
335
336         ret = mkfifo(full_path, 0600);
337         if (ret < 0) {
338                 ret = -errno;
339                 fprintf(stderr, "ERROR: mkfifo %s failed. %s\n", path,
340                                 strerror(-ret));
341         }
342
343         free(full_path);
344         return ret;
345 }
346
347 static int process_mksock(const char *path, void *user)
348 {
349         int ret;
350         struct btrfs_receive *r = user;
351         char *full_path = path_cat(r->full_subvol_path, path);
352
353         if (g_verbose >= 1)
354                 fprintf(stderr, "mksock %s\n", path);
355
356         ret = mknod(full_path, 0600 | S_IFSOCK, 0);
357         if (ret < 0) {
358                 ret = -errno;
359                 fprintf(stderr, "ERROR: mknod %s failed. %s\n", path,
360                                 strerror(-ret));
361         }
362
363         free(full_path);
364         return ret;
365 }
366
367 static int process_symlink(const char *path, const char *lnk, void *user)
368 {
369         int ret;
370         struct btrfs_receive *r = user;
371         char *full_path = path_cat(r->full_subvol_path, path);
372
373         if (g_verbose >= 1)
374                 fprintf(stderr, "symlink %s -> %s\n", path, lnk);
375
376         ret = symlink(lnk, full_path);
377         if (ret < 0) {
378                 ret = -errno;
379                 fprintf(stderr, "ERROR: symlink %s -> %s failed. %s\n", path,
380                                 lnk, strerror(-ret));
381         }
382
383         free(full_path);
384         return ret;
385 }
386
387 static int process_rename(const char *from, const char *to, void *user)
388 {
389         int ret;
390         struct btrfs_receive *r = user;
391         char *full_from = path_cat(r->full_subvol_path, from);
392         char *full_to = path_cat(r->full_subvol_path, to);
393
394         if (g_verbose >= 1)
395                 fprintf(stderr, "rename %s -> %s\n", from, to);
396
397         ret = rename(full_from, full_to);
398         if (ret < 0) {
399                 ret = -errno;
400                 fprintf(stderr, "ERROR: rename %s -> %s failed. %s\n", from,
401                                 to, strerror(-ret));
402         }
403
404         free(full_from);
405         free(full_to);
406         return ret;
407 }
408
409 static int process_link(const char *path, const char *lnk, void *user)
410 {
411         int ret;
412         struct btrfs_receive *r = user;
413         char *full_path = path_cat(r->full_subvol_path, path);
414         char *full_link_path = path_cat(r->full_subvol_path, lnk);
415
416         if (g_verbose >= 1)
417                 fprintf(stderr, "link %s -> %s\n", path, lnk);
418
419         ret = link(full_link_path, full_path);
420         if (ret < 0) {
421                 ret = -errno;
422                 fprintf(stderr, "ERROR: link %s -> %s failed. %s\n", path,
423                                 lnk, strerror(-ret));
424         }
425
426         free(full_path);
427         free(full_link_path);
428         return ret;
429 }
430
431
432 static int process_unlink(const char *path, void *user)
433 {
434         int ret;
435         struct btrfs_receive *r = user;
436         char *full_path = path_cat(r->full_subvol_path, path);
437
438         if (g_verbose >= 1)
439                 fprintf(stderr, "unlink %s\n", path);
440
441         ret = unlink(full_path);
442         if (ret < 0) {
443                 ret = -errno;
444                 fprintf(stderr, "ERROR: unlink %s failed. %s\n", path,
445                                 strerror(-ret));
446         }
447
448         free(full_path);
449         return ret;
450 }
451
452 static int process_rmdir(const char *path, void *user)
453 {
454         int ret;
455         struct btrfs_receive *r = user;
456         char *full_path = path_cat(r->full_subvol_path, path);
457
458         if (g_verbose >= 1)
459                 fprintf(stderr, "rmdir %s\n", path);
460
461         ret = rmdir(full_path);
462         if (ret < 0) {
463                 ret = -errno;
464                 fprintf(stderr, "ERROR: rmdir %s failed. %s\n", path,
465                                 strerror(-ret));
466         }
467
468         free(full_path);
469         return ret;
470 }
471
472
473 static int open_inode_for_write(struct btrfs_receive *r, const char *path)
474 {
475         int ret = 0;
476
477         if (r->write_fd != -1) {
478                 if (strcmp(r->write_path, path) == 0)
479                         goto out;
480                 close(r->write_fd);
481                 r->write_fd = -1;
482         }
483
484         r->write_fd = open(path, O_RDWR);
485         if (r->write_fd < 0) {
486                 ret = -errno;
487                 fprintf(stderr, "ERROR: open %s failed. %s\n", path,
488                                 strerror(-ret));
489                 goto out;
490         }
491         free(r->write_path);
492         r->write_path = strdup(path);
493
494 out:
495         return ret;
496 }
497
498 static int close_inode_for_write(struct btrfs_receive *r)
499 {
500         int ret = 0;
501
502         if(r->write_fd == -1)
503                 goto out;
504
505         close(r->write_fd);
506         r->write_fd = -1;
507         r->write_path[0] = 0;
508
509 out:
510         return ret;
511 }
512
513 static int process_write(const char *path, const void *data, u64 offset,
514                          u64 len, void *user)
515 {
516         int ret = 0;
517         struct btrfs_receive *r = user;
518         char *full_path = path_cat(r->full_subvol_path, path);
519         u64 pos = 0;
520         int w;
521
522         ret = open_inode_for_write(r, full_path);
523         if (ret < 0)
524                 goto out;
525
526         while (pos < len) {
527                 w = pwrite(r->write_fd, (char*)data + pos, len - pos,
528                                 offset + pos);
529                 if (w < 0) {
530                         ret = -errno;
531                         fprintf(stderr, "ERROR: writing to %s failed. %s\n",
532                                         path, strerror(-ret));
533                         goto out;
534                 }
535                 pos += w;
536         }
537
538 out:
539         free(full_path);
540         return ret;
541 }
542
543 static int process_clone(const char *path, u64 offset, u64 len,
544                          const u8 *clone_uuid, u64 clone_ctransid,
545                          const char *clone_path, u64 clone_offset,
546                          void *user)
547 {
548         int ret = 0;
549         struct btrfs_receive *r = user;
550         struct btrfs_ioctl_clone_range_args clone_args;
551         struct subvol_info *si = NULL;
552         char *full_path = path_cat(r->full_subvol_path, path);
553         char *subvol_path = NULL;
554         char *full_clone_path = NULL;
555         int clone_fd = -1;
556
557         ret = open_inode_for_write(r, full_path);
558         if (ret < 0)
559                 goto out;
560
561         si = subvol_uuid_search(&r->sus, 0, clone_uuid, clone_ctransid, NULL,
562                         subvol_search_by_received_uuid);
563         if (!si) {
564                 if (memcmp(clone_uuid, r->cur_subvol->received_uuid,
565                                 BTRFS_FSID_SIZE) == 0) {
566                         /* TODO check generation of extent */
567                         subvol_path = strdup(r->cur_subvol->path);
568                 } else {
569                         ret = -ENOENT;
570                         fprintf(stderr, "ERROR: did not find source subvol.\n");
571                         goto out;
572                 }
573         } else {
574                 /*if (rs_args.ctransid > rs_args.rtransid) {
575                         if (!r->force) {
576                                 ret = -EINVAL;
577                                 fprintf(stderr, "ERROR: subvolume %s was "
578                                                 "modified after it was "
579                                                 "received.\n",
580                                                 r->subvol_parent_name);
581                                 goto out;
582                         } else {
583                                 fprintf(stderr, "WARNING: subvolume %s was "
584                                                 "modified after it was "
585                                                 "received.\n",
586                                                 r->subvol_parent_name);
587                         }
588                 }*/
589                 subvol_path = strdup(si->path);
590         }
591
592         full_clone_path = path_cat3(r->root_path, subvol_path, clone_path);
593
594         clone_fd = open(full_clone_path, O_RDONLY | O_NOATIME);
595         if (clone_fd < 0) {
596                 ret = -errno;
597                 fprintf(stderr, "ERROR: failed to open %s. %s\n",
598                                 full_clone_path, strerror(-ret));
599                 goto out;
600         }
601
602         clone_args.src_fd = clone_fd;
603         clone_args.src_offset = clone_offset;
604         clone_args.src_length = len;
605         clone_args.dest_offset = offset;
606         ret = ioctl(r->write_fd, BTRFS_IOC_CLONE_RANGE, &clone_args);
607         if (ret) {
608                 ret = -errno;
609                 fprintf(stderr, "ERROR: failed to clone extents to %s\n%s\n",
610                                 path, strerror(-ret));
611                 goto out;
612         }
613
614 out:
615         free(full_path);
616         free(full_clone_path);
617         free(subvol_path);
618         if (clone_fd != -1)
619                 close(clone_fd);
620         return ret;
621 }
622
623
624 static int process_set_xattr(const char *path, const char *name,
625                              const void *data, int len, void *user)
626 {
627         int ret = 0;
628         struct btrfs_receive *r = user;
629         char *full_path = path_cat(r->full_subvol_path, path);
630
631         if (g_verbose >= 1) {
632                 fprintf(stderr, "set_xattr %s - name=%s data_len=%d "
633                                 "data=%.*s\n", path, name, len,
634                                 len, (char*)data);
635         }
636
637         ret = lsetxattr(full_path, name, data, len, 0);
638         if (ret < 0) {
639                 ret = -errno;
640                 fprintf(stderr, "ERROR: lsetxattr %s %s=%.*s failed. %s\n",
641                                 path, name, len, (char*)data, strerror(-ret));
642                 goto out;
643         }
644
645 out:
646         free(full_path);
647         return ret;
648 }
649
650 static int process_remove_xattr(const char *path, const char *name, void *user)
651 {
652         int ret = 0;
653         struct btrfs_receive *r = user;
654         char *full_path = path_cat(r->full_subvol_path, path);
655
656         if (g_verbose >= 1) {
657                 fprintf(stderr, "remove_xattr %s - name=%s\n",
658                                 path, name);
659         }
660
661         ret = lremovexattr(full_path, name);
662         if (ret < 0) {
663                 ret = -errno;
664                 fprintf(stderr, "ERROR: lremovexattr %s %s failed. %s\n",
665                                 path, name, strerror(-ret));
666                 goto out;
667         }
668
669 out:
670         free(full_path);
671         return ret;
672 }
673
674 static int process_truncate(const char *path, u64 size, void *user)
675 {
676         int ret = 0;
677         struct btrfs_receive *r = user;
678         char *full_path = path_cat(r->full_subvol_path, path);
679
680         if (g_verbose >= 1)
681                 fprintf(stderr, "truncate %s size=%llu\n", path, size);
682
683         ret = truncate(full_path, size);
684         if (ret < 0) {
685                 ret = -errno;
686                 fprintf(stderr, "ERROR: truncate %s failed. %s\n",
687                                 path, strerror(-ret));
688                 goto out;
689         }
690
691 out:
692         free(full_path);
693         return ret;
694 }
695
696 static int process_chmod(const char *path, u64 mode, void *user)
697 {
698         int ret = 0;
699         struct btrfs_receive *r = user;
700         char *full_path = path_cat(r->full_subvol_path, path);
701
702         if (g_verbose >= 1)
703                 fprintf(stderr, "chmod %s - mode=0%o\n", path, (int)mode);
704
705         ret = chmod(full_path, mode);
706         if (ret < 0) {
707                 ret = -errno;
708                 fprintf(stderr, "ERROR: chmod %s failed. %s\n",
709                                 path, strerror(-ret));
710                 goto out;
711         }
712
713 out:
714         free(full_path);
715         return ret;
716 }
717
718 static int process_chown(const char *path, u64 uid, u64 gid, void *user)
719 {
720         int ret = 0;
721         struct btrfs_receive *r = user;
722         char *full_path = path_cat(r->full_subvol_path, path);
723
724         if (g_verbose >= 1)
725                 fprintf(stderr, "chown %s - uid=%llu, gid=%llu\n", path,
726                                 uid, gid);
727
728         ret = lchown(full_path, uid, gid);
729         if (ret < 0) {
730                 ret = -errno;
731                 fprintf(stderr, "ERROR: chown %s failed. %s\n",
732                                 path, strerror(-ret));
733                 goto out;
734         }
735
736 out:
737         free(full_path);
738         return ret;
739 }
740
741 static int process_utimes(const char *path, struct timespec *at,
742                           struct timespec *mt, struct timespec *ct,
743                           void *user)
744 {
745         int ret = 0;
746         struct btrfs_receive *r = user;
747         char *full_path = path_cat(r->full_subvol_path, path);
748         struct timespec tv[2];
749
750         if (g_verbose >= 1)
751                 fprintf(stderr, "utimes %s\n", path);
752
753         tv[0] = *at;
754         tv[1] = *mt;
755         ret = utimensat(-1, full_path, tv, AT_SYMLINK_NOFOLLOW);
756         if (ret < 0) {
757                 ret = -errno;
758                 fprintf(stderr, "ERROR: utimes %s failed. %s\n",
759                                 path, strerror(-ret));
760                 goto out;
761         }
762
763 out:
764         free(full_path);
765         return ret;
766 }
767
768
769 struct btrfs_send_ops send_ops = {
770         .subvol = process_subvol,
771         .snapshot = process_snapshot,
772         .mkfile = process_mkfile,
773         .mkdir = process_mkdir,
774         .mknod = process_mknod,
775         .mkfifo = process_mkfifo,
776         .mksock = process_mksock,
777         .symlink = process_symlink,
778         .rename = process_rename,
779         .link = process_link,
780         .unlink = process_unlink,
781         .rmdir = process_rmdir,
782         .write = process_write,
783         .clone = process_clone,
784         .set_xattr = process_set_xattr,
785         .remove_xattr = process_remove_xattr,
786         .truncate = process_truncate,
787         .chmod = process_chmod,
788         .chown = process_chown,
789         .utimes = process_utimes,
790 };
791
792 int do_receive(struct btrfs_receive *r, const char *tomnt, int r_fd)
793 {
794         int ret;
795         int end = 0;
796
797         r->root_path = strdup(tomnt);
798         r->mnt_fd = open(tomnt, O_RDONLY | O_NOATIME);
799         if (r->mnt_fd < 0) {
800                 ret = -errno;
801                 fprintf(stderr, "ERROR: failed to open %s. %s\n", tomnt,
802                                 strerror(-ret));
803                 goto out;
804         }
805
806         ret = subvol_uuid_search_init(r->mnt_fd, &r->sus);
807         if (ret < 0)
808                 return ret;
809
810         r->write_fd = -1;
811
812         while (!end) {
813                 ret = btrfs_read_and_process_send_stream(r_fd, &send_ops, r);
814                 if (ret < 0)
815                         goto out;
816                 if (ret)
817                         end = 1;
818
819                 ret = close_inode_for_write(r);
820                 if (ret < 0)
821                         goto out;
822                 ret = finish_subvol(r);
823                 if (ret < 0)
824                         goto out;
825         }
826         ret = 0;
827
828 out:
829         return ret;
830 }
831
832 static int do_cmd_receive(int argc, char **argv)
833 {
834         int c;
835         char *tomnt = NULL;
836         char *fromfile = NULL;
837         struct btrfs_receive r;
838         int receive_fd = fileno(stdin);
839
840         int ret;
841
842         memset(&r, 0, sizeof(r));
843
844         while ((c = getopt(argc, argv, "vf:")) != -1) {
845                 switch (c) {
846                 case 'v':
847                         g_verbose++;
848                         break;
849                 case 'f':
850                         fromfile = optarg;
851                         break;
852                 case '?':
853                 default:
854                         fprintf(stderr, "ERROR: receive args invalid.\n");
855                         return 1;
856                 }
857         }
858
859         if (optind + 1 != argc) {
860                 fprintf(stderr, "ERROR: receive needs path to subvolume\n");
861                 return 1;
862         }
863
864         tomnt = argv[optind];
865
866         if (fromfile) {
867                 receive_fd = open(fromfile, O_RDONLY | O_NOATIME);
868                 if (receive_fd < 0) {
869                         fprintf(stderr, "ERROR: failed to open %s\n", fromfile);
870                         return -errno;
871                 }
872         }
873
874         ret = do_receive(&r, tomnt, receive_fd);
875
876         return ret;
877 }
878
879 static const char * const receive_cmd_group_usage[] = {
880         "btrfs receive <command> <args>",
881         NULL
882 };
883
884 static const char * const cmd_receive_usage[] = {
885         "btrfs receive [-v] [-i <infile>] <mount>",
886         "Receive subvolumes from stdin.",
887         "Receives one or more subvolumes that were previously ",
888         "sent with btrfs send. The received subvolumes are stored",
889         "into <mount>.",
890         "btrfs receive will fail in case a receiving subvolume",
891         "already exists. It will also fail in case a previously",
892         "received subvolume was changed after it was received.",
893         "After receiving a subvolume, it is immediately set to",
894         "read only.\n",
895         "-v               Enable verbose debug output. Each",
896         "                 occurrency of this option increases the",
897         "                 verbose level more.",
898         "-f <infile>      By default, btrfs receive uses stdin",
899         "                 to receive the subvolumes. Use this",
900         "                 option to specify a file to use instead.",
901         NULL
902 };
903
904 const struct cmd_group receive_cmd_group = {
905         receive_cmd_group_usage, NULL, {
906                 { "receive", do_cmd_receive, cmd_receive_usage, NULL, 0 },
907                 { 0, 0, 0, 0, 0 },
908         },
909 };
910
911 int cmd_receive(int argc, char **argv)
912 {
913         return do_cmd_receive(argc, argv);
914 }