X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=send-stream.c;h=78f2571ae40be82cbce1b7eb40e80c5293acd6c8;hb=f37ae8d275c2f988c9fc967f8272648fb0118d3d;hp=7b64dc221b6c2d08b9d3f3e7d259b4a8381e4e26;hpb=194666a67290a0b15224598826f487d2ce4d37b0;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/send-stream.c b/send-stream.c index 7b64dc2..78f2571 100644 --- a/send-stream.c +++ b/send-stream.c @@ -33,32 +33,54 @@ struct btrfs_send_stream { struct btrfs_tlv_header *cmd_attrs[BTRFS_SEND_A_MAX + 1]; u32 version; + /* + * end of last successful read, equivalent to start of current + * malformated part of block + */ + size_t stream_pos; + struct btrfs_send_ops *ops; void *user; }; -static int read_buf(struct btrfs_send_stream *sctx, void *buf, size_t len) +/* + * Read len bytes to buf. + * Return: + * 0 - success + * < 0 - negative errno in case of error + * > 0 - no data read, EOF + */ +static int read_buf(struct btrfs_send_stream *sctx, char *buf, size_t len) { int ret; size_t pos = 0; while (pos < len) { - ret = read(sctx->fd, (char*)buf + pos, len - pos); - if (ret < 0) { + ssize_t rbytes; + + rbytes = read(sctx->fd, buf + pos, len - pos); + if (rbytes < 0) { ret = -errno; error("read from stream failed: %s", strerror(-ret)); goto out; } - if (ret == 0) { + if (rbytes == 0) { ret = 1; - goto out; + goto out_eof; } - pos += ret; + pos += rbytes; } - ret = 0; +out_eof: + if (0 < pos && pos < len) { + error("short read from stream: expected %zu read %zu", len, pos); + ret = -EIO; + } else { + sctx->stream_pos += pos; + } + out: return ret; } @@ -66,22 +88,24 @@ out: /* * Reads a single command from kernel space and decodes the TLV's into * sctx->cmd_attrs + * + * Returns: + * 0 - success + * < 0 - an error in the command */ static int read_cmd(struct btrfs_send_stream *sctx) { int ret; - int cmd; - int cmd_len; - int tlv_type; - int tlv_len; + u16 cmd; + u32 cmd_len; char *data; - int pos; - struct btrfs_tlv_header *tlv_hdr; + u32 pos; u32 crc; u32 crc2; memset(sctx->cmd_attrs, 0, sizeof(sctx->cmd_attrs)); + ASSERT(sizeof(*sctx->cmd_hdr) <= sizeof(sctx->read_buf)); ret = read_buf(sctx, sctx->read_buf, sizeof(*sctx->cmd_hdr)); if (ret < 0) goto out; @@ -95,6 +119,13 @@ static int read_cmd(struct btrfs_send_stream *sctx) cmd = le16_to_cpu(sctx->cmd_hdr->cmd); cmd_len = le32_to_cpu(sctx->cmd_hdr->len); + if (cmd_len + sizeof(*sctx->cmd_hdr) >= sizeof(sctx->read_buf)) { + ret = -EINVAL; + error("command length %u too big for buffer %zu", + cmd_len, sizeof(sctx->read_buf)); + goto out; + } + data = sctx->read_buf + sizeof(*sctx->cmd_hdr); ret = read_buf(sctx, data, cmd_len); if (ret < 0) @@ -119,13 +150,17 @@ static int read_cmd(struct btrfs_send_stream *sctx) pos = 0; while (pos < cmd_len) { + struct btrfs_tlv_header *tlv_hdr; + u16 tlv_type; + u16 tlv_len; + tlv_hdr = (struct btrfs_tlv_header *)data; tlv_type = le16_to_cpu(tlv_hdr->tlv_type); tlv_len = le16_to_cpu(tlv_hdr->tlv_len); - if (tlv_type <= 0 || tlv_type > BTRFS_SEND_A_MAX || - tlv_len < 0 || tlv_len > BTRFS_SEND_BUF_SIZE) { - error("invalid tlv in cmd tlv_type = %d, tlv_len = %d", + if (tlv_type == 0 || tlv_type > BTRFS_SEND_A_MAX + || tlv_len > BTRFS_SEND_BUF_SIZE) { + error("invalid tlv in cmd tlv_type = %hu, tlv_len = %hu", tlv_type, tlv_len); ret = -EINVAL; goto out; @@ -147,7 +182,7 @@ out: static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *len) { int ret; - struct btrfs_tlv_header *h; + struct btrfs_tlv_header *hdr; if (attr <= 0 || attr > BTRFS_SEND_A_MAX) { error("invalid attribute requested, attr = %d", attr); @@ -155,15 +190,15 @@ static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *l goto out; } - h = sctx->cmd_attrs[attr]; - if (!h) { + hdr = sctx->cmd_attrs[attr]; + if (!hdr) { error("attribute %d requested but not present", attr); ret = -ENOENT; goto out; } - *len = le16_to_cpu(h->tlv_len); - *data = h + 1; + *len = le16_to_cpu(hdr->tlv_len); + *data = hdr + 1; ret = 0; @@ -451,12 +486,13 @@ int btrfs_read_and_process_send_stream(int fd, sctx.fd = fd; sctx.ops = ops; sctx.user = user; + sctx.stream_pos = 0; - ret = read_buf(&sctx, &hdr, sizeof(hdr)); + ret = read_buf(&sctx, (char*)&hdr, sizeof(hdr)); if (ret < 0) goto out; if (ret) { - ret = 1; + ret = -ENODATA; goto out; }