btrfs-progs: corrupt-block: Fix -D option
[btrfs-progs-unstable/devel.git] / send-stream.c
blob78f2571ae40be82cbce1b7eb40e80c5293acd6c8
1 /*
2 * Copyright (C) 2012 Alexander Block. All rights reserved.
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.
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.
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.
19 #include <uuid/uuid.h>
20 #include <unistd.h>
22 #include "send.h"
23 #include "send-stream.h"
24 #include "crc32c.h"
25 #include "utils.h"
27 struct btrfs_send_stream {
28 int fd;
29 char read_buf[BTRFS_SEND_BUF_SIZE];
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;
37 * end of last successful read, equivalent to start of current
38 * malformated part of block
40 size_t stream_pos;
42 struct btrfs_send_ops *ops;
43 void *user;
47 * Read len bytes to buf.
48 * Return:
49 * 0 - success
50 * < 0 - negative errno in case of error
51 * > 0 - no data read, EOF
53 static int read_buf(struct btrfs_send_stream *sctx, char *buf, size_t len)
55 int ret;
56 size_t pos = 0;
58 while (pos < len) {
59 ssize_t rbytes;
61 rbytes = read(sctx->fd, buf + pos, len - pos);
62 if (rbytes < 0) {
63 ret = -errno;
64 error("read from stream failed: %s",
65 strerror(-ret));
66 goto out;
68 if (rbytes == 0) {
69 ret = 1;
70 goto out_eof;
72 pos += rbytes;
74 ret = 0;
76 out_eof:
77 if (0 < pos && pos < len) {
78 error("short read from stream: expected %zu read %zu", len, pos);
79 ret = -EIO;
80 } else {
81 sctx->stream_pos += pos;
84 out:
85 return ret;
89 * Reads a single command from kernel space and decodes the TLV's into
90 * sctx->cmd_attrs
92 * Returns:
93 * 0 - success
94 * < 0 - an error in the command
96 static int read_cmd(struct btrfs_send_stream *sctx)
98 int ret;
99 u16 cmd;
100 u32 cmd_len;
101 char *data;
102 u32 pos;
103 u32 crc;
104 u32 crc2;
106 memset(sctx->cmd_attrs, 0, sizeof(sctx->cmd_attrs));
108 ASSERT(sizeof(*sctx->cmd_hdr) <= sizeof(sctx->read_buf));
109 ret = read_buf(sctx, sctx->read_buf, sizeof(*sctx->cmd_hdr));
110 if (ret < 0)
111 goto out;
112 if (ret) {
113 ret = -EINVAL;
114 error("unexpected EOF in stream");
115 goto out;
118 sctx->cmd_hdr = (struct btrfs_cmd_header *)sctx->read_buf;
119 cmd = le16_to_cpu(sctx->cmd_hdr->cmd);
120 cmd_len = le32_to_cpu(sctx->cmd_hdr->len);
122 if (cmd_len + sizeof(*sctx->cmd_hdr) >= sizeof(sctx->read_buf)) {
123 ret = -EINVAL;
124 error("command length %u too big for buffer %zu",
125 cmd_len, sizeof(sctx->read_buf));
126 goto out;
129 data = sctx->read_buf + sizeof(*sctx->cmd_hdr);
130 ret = read_buf(sctx, data, cmd_len);
131 if (ret < 0)
132 goto out;
133 if (ret) {
134 ret = -EINVAL;
135 error("unexpected EOF in stream");
136 goto out;
139 crc = le32_to_cpu(sctx->cmd_hdr->crc);
140 sctx->cmd_hdr->crc = 0;
142 crc2 = crc32c(0, (unsigned char*)sctx->read_buf,
143 sizeof(*sctx->cmd_hdr) + cmd_len);
145 if (crc != crc2) {
146 ret = -EINVAL;
147 error("crc32 mismatch in command");
148 goto out;
151 pos = 0;
152 while (pos < cmd_len) {
153 struct btrfs_tlv_header *tlv_hdr;
154 u16 tlv_type;
155 u16 tlv_len;
157 tlv_hdr = (struct btrfs_tlv_header *)data;
158 tlv_type = le16_to_cpu(tlv_hdr->tlv_type);
159 tlv_len = le16_to_cpu(tlv_hdr->tlv_len);
161 if (tlv_type == 0 || tlv_type > BTRFS_SEND_A_MAX
162 || tlv_len > BTRFS_SEND_BUF_SIZE) {
163 error("invalid tlv in cmd tlv_type = %hu, tlv_len = %hu",
164 tlv_type, tlv_len);
165 ret = -EINVAL;
166 goto out;
169 sctx->cmd_attrs[tlv_type] = tlv_hdr;
171 data += sizeof(*tlv_hdr) + tlv_len;
172 pos += sizeof(*tlv_hdr) + tlv_len;
175 sctx->cmd = cmd;
176 ret = 0;
178 out:
179 return ret;
182 static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *len)
184 int ret;
185 struct btrfs_tlv_header *hdr;
187 if (attr <= 0 || attr > BTRFS_SEND_A_MAX) {
188 error("invalid attribute requested, attr = %d", attr);
189 ret = -EINVAL;
190 goto out;
193 hdr = sctx->cmd_attrs[attr];
194 if (!hdr) {
195 error("attribute %d requested but not present", attr);
196 ret = -ENOENT;
197 goto out;
200 *len = le16_to_cpu(hdr->tlv_len);
201 *data = hdr + 1;
203 ret = 0;
205 out:
206 return ret;
209 #define __TLV_GOTO_FAIL(expr) \
210 if ((ret = expr) < 0) \
211 goto tlv_get_failed;
213 #define __TLV_DO_WHILE_GOTO_FAIL(expr) \
214 do { \
215 __TLV_GOTO_FAIL(expr) \
216 } while (0)
219 #define TLV_GET(s, attr, data, len) \
220 __TLV_DO_WHILE_GOTO_FAIL(tlv_get(s, attr, data, len))
222 #define TLV_CHECK_LEN(expected, got) \
223 do { \
224 if (expected != got) { \
225 error("invalid size for attribute, " \
226 "expected = %d, got = %d", \
227 (int)expected, (int)got); \
228 ret = -EINVAL; \
229 goto tlv_get_failed; \
231 } while (0)
233 #define TLV_GET_INT(s, attr, bits, v) \
234 do { \
235 __le##bits *__tmp; \
236 int __len; \
237 TLV_GET(s, attr, (void**)&__tmp, &__len); \
238 TLV_CHECK_LEN(sizeof(*__tmp), __len); \
239 *v = get_unaligned_le##bits(__tmp); \
240 } while (0)
242 #define TLV_GET_U8(s, attr, v) TLV_GET_INT(s, attr, 8, v)
243 #define TLV_GET_U16(s, attr, v) TLV_GET_INT(s, attr, 16, v)
244 #define TLV_GET_U32(s, attr, v) TLV_GET_INT(s, attr, 32, v)
245 #define TLV_GET_U64(s, attr, v) TLV_GET_INT(s, attr, 64, v)
247 static int tlv_get_string(struct btrfs_send_stream *sctx, int attr, char **str)
249 int ret;
250 void *data;
251 int len = 0;
253 TLV_GET(sctx, attr, &data, &len);
255 *str = malloc(len + 1);
256 if (!*str)
257 return -ENOMEM;
259 memcpy(*str, data, len);
260 (*str)[len] = 0;
261 ret = 0;
263 tlv_get_failed:
264 return ret;
266 #define TLV_GET_STRING(s, attr, str) \
267 __TLV_DO_WHILE_GOTO_FAIL(tlv_get_string(s, attr, str))
269 static int tlv_get_timespec(struct btrfs_send_stream *sctx,
270 int attr, struct timespec *ts)
272 int ret;
273 int len;
274 struct btrfs_timespec *bts;
276 TLV_GET(sctx, attr, (void**)&bts, &len);
277 TLV_CHECK_LEN(sizeof(*bts), len);
279 ts->tv_sec = le64_to_cpu(bts->sec);
280 ts->tv_nsec = le32_to_cpu(bts->nsec);
281 ret = 0;
283 tlv_get_failed:
284 return ret;
286 #define TLV_GET_TIMESPEC(s, attr, ts) \
287 __TLV_DO_WHILE_GOTO_FAIL(tlv_get_timespec(s, attr, ts))
289 static int tlv_get_uuid(struct btrfs_send_stream *sctx, int attr, u8 *uuid)
291 int ret;
292 int len;
293 void *data;
295 TLV_GET(sctx, attr, &data, &len);
296 TLV_CHECK_LEN(BTRFS_UUID_SIZE, len);
297 memcpy(uuid, data, BTRFS_UUID_SIZE);
299 ret = 0;
301 tlv_get_failed:
302 return ret;
304 #define TLV_GET_UUID(s, attr, uuid) \
305 __TLV_DO_WHILE_GOTO_FAIL(tlv_get_uuid(s, attr, uuid))
307 static int read_and_process_cmd(struct btrfs_send_stream *sctx)
309 int ret;
310 char *path = NULL;
311 char *path_to = NULL;
312 char *clone_path = NULL;
313 char *xattr_name = NULL;
314 void *xattr_data = NULL;
315 void *data = NULL;
316 struct timespec at;
317 struct timespec ct;
318 struct timespec mt;
319 u8 uuid[BTRFS_UUID_SIZE];
320 u8 clone_uuid[BTRFS_UUID_SIZE];
321 u64 tmp;
322 u64 tmp2;
323 u64 ctransid;
324 u64 clone_ctransid;
325 u64 mode;
326 u64 dev;
327 u64 clone_offset;
328 u64 offset;
329 int len;
330 int xattr_len;
332 ret = read_cmd(sctx);
333 if (ret)
334 goto out;
336 switch (sctx->cmd) {
337 case BTRFS_SEND_C_SUBVOL:
338 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
339 TLV_GET_UUID(sctx, BTRFS_SEND_A_UUID, uuid);
340 TLV_GET_U64(sctx, BTRFS_SEND_A_CTRANSID, &ctransid);
341 ret = sctx->ops->subvol(path, uuid, ctransid, sctx->user);
342 break;
343 case BTRFS_SEND_C_SNAPSHOT:
344 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
345 TLV_GET_UUID(sctx, BTRFS_SEND_A_UUID, uuid);
346 TLV_GET_U64(sctx, BTRFS_SEND_A_CTRANSID, &ctransid);
347 TLV_GET_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
348 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
349 ret = sctx->ops->snapshot(path, uuid, ctransid, clone_uuid,
350 clone_ctransid, sctx->user);
351 break;
352 case BTRFS_SEND_C_MKFILE:
353 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
354 ret = sctx->ops->mkfile(path, sctx->user);
355 break;
356 case BTRFS_SEND_C_MKDIR:
357 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
358 ret = sctx->ops->mkdir(path, sctx->user);
359 break;
360 case BTRFS_SEND_C_MKNOD:
361 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
362 TLV_GET_U64(sctx, BTRFS_SEND_A_MODE, &mode);
363 TLV_GET_U64(sctx, BTRFS_SEND_A_RDEV, &dev);
364 ret = sctx->ops->mknod(path, mode, dev, sctx->user);
365 break;
366 case BTRFS_SEND_C_MKFIFO:
367 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
368 ret = sctx->ops->mkfifo(path, sctx->user);
369 break;
370 case BTRFS_SEND_C_MKSOCK:
371 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
372 ret = sctx->ops->mksock(path, sctx->user);
373 break;
374 case BTRFS_SEND_C_SYMLINK:
375 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
376 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_LINK, &path_to);
377 ret = sctx->ops->symlink(path, path_to, sctx->user);
378 break;
379 case BTRFS_SEND_C_RENAME:
380 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
381 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_TO, &path_to);
382 ret = sctx->ops->rename(path, path_to, sctx->user);
383 break;
384 case BTRFS_SEND_C_LINK:
385 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
386 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_LINK, &path_to);
387 ret = sctx->ops->link(path, path_to, sctx->user);
388 break;
389 case BTRFS_SEND_C_UNLINK:
390 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
391 ret = sctx->ops->unlink(path, sctx->user);
392 break;
393 case BTRFS_SEND_C_RMDIR:
394 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
395 ret = sctx->ops->rmdir(path, sctx->user);
396 break;
397 case BTRFS_SEND_C_WRITE:
398 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
399 TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
400 TLV_GET(sctx, BTRFS_SEND_A_DATA, &data, &len);
401 ret = sctx->ops->write(path, data, offset, len, sctx->user);
402 break;
403 case BTRFS_SEND_C_CLONE:
404 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
405 TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
406 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_LEN, &len);
407 TLV_GET_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
408 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
409 TLV_GET_STRING(sctx, BTRFS_SEND_A_CLONE_PATH, &clone_path);
410 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_OFFSET, &clone_offset);
411 ret = sctx->ops->clone(path, offset, len, clone_uuid,
412 clone_ctransid, clone_path, clone_offset,
413 sctx->user);
414 break;
415 case BTRFS_SEND_C_SET_XATTR:
416 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
417 TLV_GET_STRING(sctx, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
418 TLV_GET(sctx, BTRFS_SEND_A_XATTR_DATA, &xattr_data, &xattr_len);
419 ret = sctx->ops->set_xattr(path, xattr_name, xattr_data,
420 xattr_len, sctx->user);
421 break;
422 case BTRFS_SEND_C_REMOVE_XATTR:
423 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
424 TLV_GET_STRING(sctx, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
425 ret = sctx->ops->remove_xattr(path, xattr_name, sctx->user);
426 break;
427 case BTRFS_SEND_C_TRUNCATE:
428 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
429 TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
430 ret = sctx->ops->truncate(path, tmp, sctx->user);
431 break;
432 case BTRFS_SEND_C_CHMOD:
433 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
434 TLV_GET_U64(sctx, BTRFS_SEND_A_MODE, &tmp);
435 ret = sctx->ops->chmod(path, tmp, sctx->user);
436 break;
437 case BTRFS_SEND_C_CHOWN:
438 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
439 TLV_GET_U64(sctx, BTRFS_SEND_A_UID, &tmp);
440 TLV_GET_U64(sctx, BTRFS_SEND_A_GID, &tmp2);
441 ret = sctx->ops->chown(path, tmp, tmp2, sctx->user);
442 break;
443 case BTRFS_SEND_C_UTIMES:
444 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
445 TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_ATIME, &at);
446 TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_MTIME, &mt);
447 TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_CTIME, &ct);
448 ret = sctx->ops->utimes(path, &at, &mt, &ct, sctx->user);
449 break;
450 case BTRFS_SEND_C_UPDATE_EXTENT:
451 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
452 TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
453 TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
454 ret = sctx->ops->update_extent(path, offset, tmp, sctx->user);
455 break;
456 case BTRFS_SEND_C_END:
457 ret = 1;
458 break;
461 tlv_get_failed:
462 out:
463 free(path);
464 free(path_to);
465 free(clone_path);
466 free(xattr_name);
467 return ret;
471 * If max_errors is 0, then don't stop processing the stream if one of the
472 * callbacks in btrfs_send_ops structure returns an error. If greater than
473 * zero, stop after max_errors errors happened.
475 int btrfs_read_and_process_send_stream(int fd,
476 struct btrfs_send_ops *ops, void *user,
477 int honor_end_cmd,
478 u64 max_errors)
480 int ret;
481 struct btrfs_send_stream sctx;
482 struct btrfs_stream_header hdr;
483 u64 errors = 0;
484 int last_err = 0;
486 sctx.fd = fd;
487 sctx.ops = ops;
488 sctx.user = user;
489 sctx.stream_pos = 0;
491 ret = read_buf(&sctx, (char*)&hdr, sizeof(hdr));
492 if (ret < 0)
493 goto out;
494 if (ret) {
495 ret = -ENODATA;
496 goto out;
499 if (strcmp(hdr.magic, BTRFS_SEND_STREAM_MAGIC)) {
500 ret = -EINVAL;
501 error("unexpected header");
502 goto out;
505 sctx.version = le32_to_cpu(hdr.version);
506 if (sctx.version > BTRFS_SEND_STREAM_VERSION) {
507 ret = -EINVAL;
508 error("stream version %d not supported, please use newer version",
509 sctx.version);
510 goto out;
513 while (1) {
514 ret = read_and_process_cmd(&sctx);
515 if (ret < 0) {
516 last_err = ret;
517 errors++;
518 if (max_errors > 0 && errors >= max_errors)
519 goto out;
520 } else if (ret > 0) {
521 if (!honor_end_cmd)
522 ret = 0;
523 goto out;
527 out:
528 if (last_err && !ret)
529 ret = last_err;
531 return ret;