btrfs-progs: fix floating point exception for btrfs-calc-size
[btrfs-progs-unstable/devel.git] / send-stream.c
blob66c04884a9ff8c6fa08ad182576b7a628583857a
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"
26 struct btrfs_send_stream {
27 int fd;
28 char read_buf[BTRFS_SEND_BUF_SIZE];
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;
35 struct btrfs_send_ops *ops;
36 void *user;
39 static int read_buf(struct btrfs_send_stream *s, void *buf, int len)
41 int ret;
42 int pos = 0;
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;
52 if (ret == 0) {
53 ret = 1;
54 goto out;
56 pos += ret;
59 ret = 0;
61 out:
62 return ret;
66 * Reads a single command from kernel space and decodes the TLV's into
67 * s->cmd_attrs
69 static int read_cmd(struct btrfs_send_stream *s)
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;
82 memset(s->cmd_attrs, 0, sizeof(s->cmd_attrs));
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;
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);
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;
107 crc = le32_to_cpu(s->cmd_hdr->crc);
108 s->cmd_hdr->crc = 0;
110 crc2 = crc32c(0, (unsigned char*)s->read_buf,
111 sizeof(*s->cmd_hdr) + cmd_len);
113 if (crc != crc2) {
114 ret = -EINVAL;
115 fprintf(stderr, "ERROR: crc32 mismatch in command.\n");
116 goto out;
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);
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;
134 s->cmd_attrs[tlv_type] = tlv_hdr;
136 data += sizeof(*tlv_hdr) + tlv_len;
137 pos += sizeof(*tlv_hdr) + tlv_len;
140 s->cmd = cmd;
141 ret = 0;
143 out:
144 return ret;
147 static int tlv_get(struct btrfs_send_stream *s, int attr, void **data, int *len)
149 int ret;
150 struct btrfs_tlv_header *h;
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;
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;
168 *len = le16_to_cpu(h->tlv_len);
169 *data = h + 1;
171 ret = 0;
173 out:
174 return ret;
177 #define __TLV_GOTO_FAIL(expr) \
178 if ((ret = expr) < 0) \
179 goto tlv_get_failed;
181 #define __TLV_DO_WHILE_GOTO_FAIL(expr) \
182 do { \
183 __TLV_GOTO_FAIL(expr) \
184 } while (0)
187 #define TLV_GET(s, attr, data, len) \
188 __TLV_DO_WHILE_GOTO_FAIL(tlv_get(s, attr, data, len))
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; \
199 } while (0)
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 = get_unaligned_le##bits(__tmp); \
208 } while (0)
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)
215 static int tlv_get_string(struct btrfs_send_stream *s, int attr, char **str)
217 int ret;
218 void *data;
219 int len = 0;
221 TLV_GET(s, attr, &data, &len);
223 *str = malloc(len + 1);
224 if (!*str)
225 return -ENOMEM;
227 memcpy(*str, data, len);
228 (*str)[len] = 0;
229 ret = 0;
231 tlv_get_failed:
232 return ret;
234 #define TLV_GET_STRING(s, attr, str) \
235 __TLV_DO_WHILE_GOTO_FAIL(tlv_get_string(s, attr, str))
237 static int tlv_get_timespec(struct btrfs_send_stream *s,
238 int attr, struct timespec *ts)
240 int ret;
241 int len;
242 struct btrfs_timespec *bts;
244 TLV_GET(s, attr, (void**)&bts, &len);
245 TLV_CHECK_LEN(sizeof(*bts), len);
247 ts->tv_sec = le64_to_cpu(bts->sec);
248 ts->tv_nsec = le32_to_cpu(bts->nsec);
249 ret = 0;
251 tlv_get_failed:
252 return ret;
254 #define TLV_GET_TIMESPEC(s, attr, ts) \
255 __TLV_DO_WHILE_GOTO_FAIL(tlv_get_timespec(s, attr, ts))
257 static int tlv_get_uuid(struct btrfs_send_stream *s, int attr, u8 *uuid)
259 int ret;
260 int len;
261 void *data;
263 TLV_GET(s, attr, &data, &len);
264 TLV_CHECK_LEN(BTRFS_UUID_SIZE, len);
265 memcpy(uuid, data, BTRFS_UUID_SIZE);
267 ret = 0;
269 tlv_get_failed:
270 return ret;
272 #define TLV_GET_UUID(s, attr, uuid) \
273 __TLV_DO_WHILE_GOTO_FAIL(tlv_get_uuid(s, attr, uuid))
275 static int read_and_process_cmd(struct btrfs_send_stream *s)
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;
300 ret = read_cmd(s);
301 if (ret)
302 goto out;
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;
429 tlv_get_failed:
430 out:
431 free(path);
432 free(path_to);
433 free(clone_path);
434 free(xattr_name);
435 return ret;
439 * If max_errors is 0, then don't stop processing the stream if one of the
440 * callbacks in btrfs_send_ops structure returns an error. If greater than
441 * zero, stop after max_errors errors happened.
443 int btrfs_read_and_process_send_stream(int fd,
444 struct btrfs_send_ops *ops, void *user,
445 int honor_end_cmd,
446 u64 max_errors)
448 int ret;
449 struct btrfs_send_stream s;
450 struct btrfs_stream_header hdr;
451 u64 errors = 0;
452 int last_err = 0;
454 s.fd = fd;
455 s.ops = ops;
456 s.user = user;
458 ret = read_buf(&s, &hdr, sizeof(hdr));
459 if (ret < 0)
460 goto out;
461 if (ret) {
462 ret = 1;
463 goto out;
466 if (strcmp(hdr.magic, BTRFS_SEND_STREAM_MAGIC)) {
467 ret = -EINVAL;
468 fprintf(stderr, "ERROR: Unexpected header\n");
469 goto out;
472 s.version = le32_to_cpu(hdr.version);
473 if (s.version > BTRFS_SEND_STREAM_VERSION) {
474 ret = -EINVAL;
475 fprintf(stderr, "ERROR: Stream version %d not supported. "
476 "Please upgrade btrfs-progs\n", s.version);
477 goto out;
480 while (1) {
481 ret = read_and_process_cmd(&s);
482 if (ret < 0) {
483 last_err = ret;
484 errors++;
485 if (max_errors > 0 && errors >= max_errors)
486 goto out;
487 } else if (ret > 0) {
488 if (!honor_end_cmd)
489 ret = 0;
490 goto out;
494 out:
495 if (last_err && !ret)
496 ret = last_err;
498 return ret;