btrfs-progs: don't leak fd in test_dev_for_mkfs() error paths
[platform/upstream/btrfs-progs.git] / send-stream.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 #include <uuid/uuid.h>
20 #include <unistd.h>
21
22 #include "send.h"
23 #include "send-stream.h"
24 #include "crc32c.h"
25
26 struct btrfs_send_stream {
27         int fd;
28         char read_buf[BTRFS_SEND_BUF_SIZE];
29
30         int cmd;
31         struct btrfs_cmd_header *cmd_hdr;
32         struct btrfs_tlv_header *cmd_attrs[BTRFS_SEND_A_MAX + 1];
33         u32 version;
34
35         struct btrfs_send_ops *ops;
36         void *user;
37 };
38
39 static int read_buf(struct btrfs_send_stream *s, void *buf, int len)
40 {
41         int ret;
42         int pos = 0;
43
44         while (pos < len) {
45                 ret = read(s->fd, (char*)buf + pos, len - pos);
46                 if (ret < 0) {
47                         ret = -errno;
48                         fprintf(stderr, "ERROR: read from stream failed. %s\n",
49                                         strerror(-ret));
50                         goto out;
51                 }
52                 if (ret == 0) {
53                         ret = 1;
54                         goto out;
55                 }
56                 pos += ret;
57         }
58
59         ret = 0;
60
61 out:
62         return ret;
63 }
64
65 /*
66  * Reads a single command from kernel space and decodes the TLV's into
67  * s->cmd_attrs
68  */
69 static int read_cmd(struct btrfs_send_stream *s)
70 {
71         int ret;
72         int cmd;
73         int cmd_len;
74         int tlv_type;
75         int tlv_len;
76         char *data;
77         int pos;
78         struct btrfs_tlv_header *tlv_hdr;
79         u32 crc;
80         u32 crc2;
81
82         memset(s->cmd_attrs, 0, sizeof(s->cmd_attrs));
83
84         ret = read_buf(s, s->read_buf, sizeof(*s->cmd_hdr));
85         if (ret < 0)
86                 goto out;
87         if (ret) {
88                 ret = -EINVAL;
89                 fprintf(stderr, "ERROR: unexpected EOF in stream.\n");
90                 goto out;
91         }
92
93         s->cmd_hdr = (struct btrfs_cmd_header *)s->read_buf;
94         cmd = le16_to_cpu(s->cmd_hdr->cmd);
95         cmd_len = le32_to_cpu(s->cmd_hdr->len);
96
97         data = s->read_buf + sizeof(*s->cmd_hdr);
98         ret = read_buf(s, data, cmd_len);
99         if (ret < 0)
100                 goto out;
101         if (ret) {
102                 ret = -EINVAL;
103                 fprintf(stderr, "ERROR: unexpected EOF in stream.\n");
104                 goto out;
105         }
106
107         crc = le32_to_cpu(s->cmd_hdr->crc);
108         s->cmd_hdr->crc = 0;
109
110         crc2 = crc32c(0, (unsigned char*)s->read_buf,
111                         sizeof(*s->cmd_hdr) + cmd_len);
112
113         if (crc != crc2) {
114                 ret = -EINVAL;
115                 fprintf(stderr, "ERROR: crc32 mismatch in command.\n");
116                 goto out;
117         }
118
119         pos = 0;
120         while (pos < cmd_len) {
121                 tlv_hdr = (struct btrfs_tlv_header *)data;
122                 tlv_type = le16_to_cpu(tlv_hdr->tlv_type);
123                 tlv_len = le16_to_cpu(tlv_hdr->tlv_len);
124
125                 if (tlv_type <= 0 || tlv_type > BTRFS_SEND_A_MAX ||
126                     tlv_len < 0 || tlv_len > BTRFS_SEND_BUF_SIZE) {
127                         fprintf(stderr, "ERROR: invalid tlv in cmd. "
128                                         "tlv_type = %d, tlv_len = %d\n",
129                                         tlv_type, tlv_len);
130                         ret = -EINVAL;
131                         goto out;
132                 }
133
134                 s->cmd_attrs[tlv_type] = tlv_hdr;
135
136                 data += sizeof(*tlv_hdr) + tlv_len;
137                 pos += sizeof(*tlv_hdr) + tlv_len;
138         }
139
140         s->cmd = cmd;
141         ret = 0;
142
143 out:
144         return ret;
145 }
146
147 static int tlv_get(struct btrfs_send_stream *s, int attr, void **data, int *len)
148 {
149         int ret;
150         struct btrfs_tlv_header *h;
151
152         if (attr <= 0 || attr > BTRFS_SEND_A_MAX) {
153                 fprintf(stderr, "ERROR: invalid attribute requested. "
154                                 "attr = %d\n",
155                                 attr);
156                 ret = -EINVAL;
157                 goto out;
158         }
159
160         h = s->cmd_attrs[attr];
161         if (!h) {
162                 fprintf(stderr, "ERROR: attribute %d requested "
163                                 "but not present.\n", attr);
164                 ret = -ENOENT;
165                 goto out;
166         }
167
168         *len = le16_to_cpu(h->tlv_len);
169         *data = h + 1;
170
171         ret = 0;
172
173 out:
174         return ret;
175 }
176
177 #define __TLV_GOTO_FAIL(expr) \
178         if ((ret = expr) < 0) \
179                 goto tlv_get_failed;
180
181 #define __TLV_DO_WHILE_GOTO_FAIL(expr) \
182         do { \
183                 __TLV_GOTO_FAIL(expr) \
184         } while (0)
185
186
187 #define TLV_GET(s, attr, data, len) \
188         __TLV_DO_WHILE_GOTO_FAIL(tlv_get(s, attr, data, len))
189
190 #define TLV_CHECK_LEN(expected, got) \
191         do { \
192                 if (expected != got) { \
193                         fprintf(stderr, "ERROR: invalid size for attribute. " \
194                                         "expected = %d, got = %d\n", \
195                                         (int)expected, (int)got); \
196                         ret = -EINVAL; \
197                         goto tlv_get_failed; \
198                 } \
199         } while (0)
200
201 #define TLV_GET_INT(s, attr, bits, v) \
202         do { \
203                 __le##bits *__tmp; \
204                 int __len; \
205                 TLV_GET(s, attr, (void**)&__tmp, &__len); \
206                 TLV_CHECK_LEN(sizeof(*__tmp), __len); \
207                 *v = le##bits##_to_cpu(*__tmp); \
208         } while (0)
209
210 #define TLV_GET_U8(s, attr, v) TLV_GET_INT(s, attr, 8, v)
211 #define TLV_GET_U16(s, attr, v) TLV_GET_INT(s, attr, 16, v)
212 #define TLV_GET_U32(s, attr, v) TLV_GET_INT(s, attr, 32, v)
213 #define TLV_GET_U64(s, attr, v) TLV_GET_INT(s, attr, 64, v)
214
215 static int tlv_get_string(struct btrfs_send_stream *s, int attr, char **str)
216 {
217         int ret;
218         void *data;
219         int len;
220
221         TLV_GET(s, attr, &data, &len);
222
223         *str = malloc(len + 1);
224         if (!*str)
225                 return -ENOMEM;
226
227         memcpy(*str, data, len);
228         (*str)[len] = 0;
229         ret = 0;
230
231 tlv_get_failed:
232         return ret;
233 }
234 #define TLV_GET_STRING(s, attr, str) \
235         __TLV_DO_WHILE_GOTO_FAIL(tlv_get_string(s, attr, str))
236
237 static int tlv_get_timespec(struct btrfs_send_stream *s,
238                             int attr, struct timespec *ts)
239 {
240         int ret;
241         int len;
242         struct btrfs_timespec *bts;
243
244         TLV_GET(s, attr, (void**)&bts, &len);
245         TLV_CHECK_LEN(sizeof(*bts), len);
246
247         ts->tv_sec = le64_to_cpu(bts->sec);
248         ts->tv_nsec = le32_to_cpu(bts->nsec);
249         ret = 0;
250
251 tlv_get_failed:
252         return ret;
253 }
254 #define TLV_GET_TIMESPEC(s, attr, ts) \
255         __TLV_DO_WHILE_GOTO_FAIL(tlv_get_timespec(s, attr, ts))
256
257 static int tlv_get_uuid(struct btrfs_send_stream *s, int attr, u8 *uuid)
258 {
259         int ret;
260         int len;
261         void *data;
262
263         TLV_GET(s, attr, &data, &len);
264         TLV_CHECK_LEN(BTRFS_UUID_SIZE, len);
265         memcpy(uuid, data, BTRFS_UUID_SIZE);
266
267         ret = 0;
268
269 tlv_get_failed:
270         return ret;
271 }
272 #define TLV_GET_UUID(s, attr, uuid) \
273         __TLV_DO_WHILE_GOTO_FAIL(tlv_get_uuid(s, attr, uuid))
274
275 static int read_and_process_cmd(struct btrfs_send_stream *s)
276 {
277         int ret;
278         char *path = NULL;
279         char *path_to = NULL;
280         char *clone_path = NULL;
281         char *xattr_name = NULL;
282         void *xattr_data = NULL;
283         void *data = NULL;
284         struct timespec at;
285         struct timespec ct;
286         struct timespec mt;
287         u8 uuid[BTRFS_UUID_SIZE];
288         u8 clone_uuid[BTRFS_UUID_SIZE];
289         u64 tmp;
290         u64 tmp2;
291         u64 ctransid;
292         u64 clone_ctransid;
293         u64 mode;
294         u64 dev;
295         u64 clone_offset;
296         u64 offset;
297         int len;
298         int xattr_len;
299
300         ret = read_cmd(s);
301         if (ret)
302                 goto out;
303
304         switch (s->cmd) {
305         case BTRFS_SEND_C_SUBVOL:
306                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
307                 TLV_GET_UUID(s, BTRFS_SEND_A_UUID, uuid);
308                 TLV_GET_U64(s, BTRFS_SEND_A_CTRANSID, &ctransid);
309                 ret = s->ops->subvol(path, uuid, ctransid, s->user);
310                 break;
311         case BTRFS_SEND_C_SNAPSHOT:
312                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
313                 TLV_GET_UUID(s, BTRFS_SEND_A_UUID, uuid);
314                 TLV_GET_U64(s, BTRFS_SEND_A_CTRANSID, &ctransid);
315                 TLV_GET_UUID(s, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
316                 TLV_GET_U64(s, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
317                 ret = s->ops->snapshot(path, uuid, ctransid, clone_uuid,
318                                 clone_ctransid, s->user);
319                 break;
320         case BTRFS_SEND_C_MKFILE:
321                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
322                 ret = s->ops->mkfile(path, s->user);
323                 break;
324         case BTRFS_SEND_C_MKDIR:
325                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
326                 ret = s->ops->mkdir(path, s->user);
327                 break;
328         case BTRFS_SEND_C_MKNOD:
329                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
330                 TLV_GET_U64(s, BTRFS_SEND_A_MODE, &mode);
331                 TLV_GET_U64(s, BTRFS_SEND_A_RDEV, &dev);
332                 ret = s->ops->mknod(path, mode, dev, s->user);
333                 break;
334         case BTRFS_SEND_C_MKFIFO:
335                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
336                 ret = s->ops->mkfifo(path, s->user);
337                 break;
338         case BTRFS_SEND_C_MKSOCK:
339                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
340                 ret = s->ops->mksock(path, s->user);
341                 break;
342         case BTRFS_SEND_C_SYMLINK:
343                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
344                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH_LINK, &path_to);
345                 ret = s->ops->symlink(path, path_to, s->user);
346                 break;
347         case BTRFS_SEND_C_RENAME:
348                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
349                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH_TO, &path_to);
350                 ret = s->ops->rename(path, path_to, s->user);
351                 break;
352         case BTRFS_SEND_C_LINK:
353                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
354                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH_LINK, &path_to);
355                 ret = s->ops->link(path, path_to, s->user);
356                 break;
357         case BTRFS_SEND_C_UNLINK:
358                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
359                 ret = s->ops->unlink(path, s->user);
360                 break;
361         case BTRFS_SEND_C_RMDIR:
362                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
363                 ret = s->ops->rmdir(path, s->user);
364                 break;
365         case BTRFS_SEND_C_WRITE:
366                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
367                 TLV_GET_U64(s, BTRFS_SEND_A_FILE_OFFSET, &offset);
368                 TLV_GET(s, BTRFS_SEND_A_DATA, &data, &len);
369                 ret = s->ops->write(path, data, offset, len, s->user);
370                 break;
371         case BTRFS_SEND_C_CLONE:
372                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
373                 TLV_GET_U64(s, BTRFS_SEND_A_FILE_OFFSET, &offset);
374                 TLV_GET_U64(s, BTRFS_SEND_A_CLONE_LEN, &len);
375                 TLV_GET_UUID(s, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
376                 TLV_GET_U64(s, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
377                 TLV_GET_STRING(s, BTRFS_SEND_A_CLONE_PATH, &clone_path);
378                 TLV_GET_U64(s, BTRFS_SEND_A_CLONE_OFFSET, &clone_offset);
379                 ret = s->ops->clone(path, offset, len, clone_uuid,
380                                 clone_ctransid, clone_path, clone_offset,
381                                 s->user);
382                 break;
383         case BTRFS_SEND_C_SET_XATTR:
384                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
385                 TLV_GET_STRING(s, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
386                 TLV_GET(s, BTRFS_SEND_A_XATTR_DATA, &xattr_data, &xattr_len);
387                 ret = s->ops->set_xattr(path, xattr_name, xattr_data,
388                                 xattr_len, s->user);
389                 break;
390         case BTRFS_SEND_C_REMOVE_XATTR:
391                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
392                 TLV_GET_STRING(s, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
393                 ret = s->ops->remove_xattr(path, xattr_name, s->user);
394                 break;
395         case BTRFS_SEND_C_TRUNCATE:
396                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
397                 TLV_GET_U64(s, BTRFS_SEND_A_SIZE, &tmp);
398                 ret = s->ops->truncate(path, tmp, s->user);
399                 break;
400         case BTRFS_SEND_C_CHMOD:
401                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
402                 TLV_GET_U64(s, BTRFS_SEND_A_MODE, &tmp);
403                 ret = s->ops->chmod(path, tmp, s->user);
404                 break;
405         case BTRFS_SEND_C_CHOWN:
406                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
407                 TLV_GET_U64(s, BTRFS_SEND_A_UID, &tmp);
408                 TLV_GET_U64(s, BTRFS_SEND_A_GID, &tmp2);
409                 ret = s->ops->chown(path, tmp, tmp2, s->user);
410                 break;
411         case BTRFS_SEND_C_UTIMES:
412                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
413                 TLV_GET_TIMESPEC(s, BTRFS_SEND_A_ATIME, &at);
414                 TLV_GET_TIMESPEC(s, BTRFS_SEND_A_MTIME, &mt);
415                 TLV_GET_TIMESPEC(s, BTRFS_SEND_A_CTIME, &ct);
416                 ret = s->ops->utimes(path, &at, &mt, &ct, s->user);
417                 break;
418         case BTRFS_SEND_C_UPDATE_EXTENT:
419                 TLV_GET_STRING(s, BTRFS_SEND_A_PATH, &path);
420                 TLV_GET_U64(s, BTRFS_SEND_A_FILE_OFFSET, &offset);
421                 TLV_GET_U64(s, BTRFS_SEND_A_SIZE, &tmp);
422                 ret = s->ops->update_extent(path, offset, tmp, s->user);
423                 break;
424         case BTRFS_SEND_C_END:
425                 ret = 1;
426                 break;
427         }
428
429 tlv_get_failed:
430 out:
431         free(path);
432         free(path_to);
433         free(clone_path);
434         free(xattr_name);
435         return ret;
436 }
437
438 int btrfs_read_and_process_send_stream(int fd,
439                                        struct btrfs_send_ops *ops, void *user,
440                                        int honor_end_cmd)
441 {
442         int ret;
443         struct btrfs_send_stream s;
444         struct btrfs_stream_header hdr;
445
446         s.fd = fd;
447         s.ops = ops;
448         s.user = user;
449
450         ret = read_buf(&s, &hdr, sizeof(hdr));
451         if (ret < 0)
452                 goto out;
453         if (ret) {
454                 ret = 1;
455                 goto out;
456         }
457
458         if (strcmp(hdr.magic, BTRFS_SEND_STREAM_MAGIC)) {
459                 ret = -EINVAL;
460                 fprintf(stderr, "ERROR: Unexpected header\n");
461                 goto out;
462         }
463
464         s.version = le32_to_cpu(hdr.version);
465         if (s.version > BTRFS_SEND_STREAM_VERSION) {
466                 ret = -EINVAL;
467                 fprintf(stderr, "ERROR: Stream version %d not supported. "
468                                 "Please upgrade btrfs-progs\n", s.version);
469                 goto out;
470         }
471
472         while (1) {
473                 ret = read_and_process_cmd(&s);
474                 if (ret < 0)
475                         goto out;
476                 if (ret) {
477                         if (!honor_end_cmd)
478                                 ret = 0;
479                         goto out;
480                 }
481         }
482
483 out:
484         return ret;
485 }