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