1/******************************************************
2Copyright (c) 2011-2017 Percona LLC and/or its affiliates.
3
4The xbstream format reader implementation.
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18
19*******************************************************/
20
21#include <my_global.h>
22#include <my_base.h>
23#include <zlib.h>
24#include "common.h"
25#include "xbstream.h"
26#include "crc_glue.h"
27
28/* Allocate 1 MB for the payload buffer initially */
29#define INIT_BUFFER_LEN (1024 * 1024)
30
31#ifndef MY_OFF_T_MAX
32#define MY_OFF_T_MAX (~(my_off_t)0UL)
33#endif
34
35struct xb_rstream_struct {
36 my_off_t offset;
37 File fd;
38};
39
40xb_rstream_t *
41xb_stream_read_new(void)
42{
43 xb_rstream_t *stream;
44
45 stream = (xb_rstream_t *) my_malloc(sizeof(xb_rstream_t), MYF(MY_FAE));
46
47#ifdef __WIN__
48 setmode(fileno(stdin), _O_BINARY);
49#endif
50
51 stream->fd = my_fileno(stdin);
52 stream->offset = 0;
53
54 return stream;
55}
56
57static inline
58xb_chunk_type_t
59validate_chunk_type(uchar code)
60{
61 switch ((xb_chunk_type_t) code) {
62 case XB_CHUNK_TYPE_PAYLOAD:
63 case XB_CHUNK_TYPE_EOF:
64 return (xb_chunk_type_t) code;
65 default:
66 return XB_CHUNK_TYPE_UNKNOWN;
67 }
68}
69
70int
71xb_stream_validate_checksum(xb_rstream_chunk_t *chunk)
72{
73 ulong checksum;
74
75 checksum = crc32_iso3309(0, chunk->data, (uint)chunk->length);
76 if (checksum != chunk->checksum) {
77 msg("xb_stream_read_chunk(): invalid checksum at offset "
78 "0x%llx: expected 0x%lx, read 0x%lx.\n",
79 (ulonglong) chunk->checksum_offset, chunk->checksum,
80 checksum);
81 return XB_STREAM_READ_ERROR;
82 }
83
84 return XB_STREAM_READ_CHUNK;
85}
86
87#define F_READ(buf,len) \
88 do { \
89 if (xb_read_full(fd, buf, len) < len) { \
90 msg("xb_stream_read_chunk(): my_read() failed.\n"); \
91 goto err; \
92 } \
93 } while (0)
94
95xb_rstream_result_t
96xb_stream_read_chunk(xb_rstream_t *stream, xb_rstream_chunk_t *chunk)
97{
98 uchar tmpbuf[16];
99 uchar *ptr = tmpbuf;
100 uint pathlen;
101 size_t tbytes;
102 ulonglong ullval;
103 File fd = stream->fd;
104
105 xb_ad(sizeof(tmpbuf) >= CHUNK_HEADER_CONSTANT_LEN);
106
107 /* This is the only place where we expect EOF, so read with
108 xb_read_full() rather than F_READ() */
109 tbytes = xb_read_full(fd, ptr, CHUNK_HEADER_CONSTANT_LEN);
110 if (tbytes == 0) {
111 return XB_STREAM_READ_EOF;
112 } else if (tbytes < CHUNK_HEADER_CONSTANT_LEN) {
113 msg("xb_stream_read_chunk(): unexpected end of stream at "
114 "offset 0x%llx.\n", stream->offset);
115 goto err;
116 }
117
118 ptr = tmpbuf;
119
120 /* Chunk magic value */
121 if (memcmp(tmpbuf, XB_STREAM_CHUNK_MAGIC, 8)) {
122 msg("xb_stream_read_chunk(): wrong chunk magic at offset "
123 "0x%llx.\n", (ulonglong) stream->offset);
124 goto err;
125 }
126 ptr += 8;
127 stream->offset += 8;
128
129 /* Chunk flags */
130 chunk->flags = *ptr++;
131 stream->offset++;
132
133 /* Chunk type, ignore unknown ones if ignorable flag is set */
134 chunk->type = validate_chunk_type(*ptr);
135 if (chunk->type == XB_CHUNK_TYPE_UNKNOWN &&
136 !(chunk->flags & XB_STREAM_FLAG_IGNORABLE)) {
137 msg("xb_stream_read_chunk(): unknown chunk type 0x%lu at "
138 "offset 0x%llx.\n", (ulong) *ptr,
139 (ulonglong) stream->offset);
140 goto err;
141 }
142 ptr++;
143 stream->offset++;
144
145 /* Path length */
146 pathlen = uint4korr(ptr);
147 if (pathlen >= FN_REFLEN) {
148 msg("xb_stream_read_chunk(): path length (%lu) is too large at "
149 "offset 0x%llx.\n", (ulong) pathlen, stream->offset);
150 goto err;
151 }
152 chunk->pathlen = pathlen;
153 stream->offset +=4;
154
155 xb_ad((ptr + 4 - tmpbuf) == CHUNK_HEADER_CONSTANT_LEN);
156
157 /* Path */
158 if (chunk->pathlen > 0) {
159 F_READ((uchar *) chunk->path, pathlen);
160 stream->offset += pathlen;
161 }
162 chunk->path[pathlen] = '\0';
163
164 if (chunk->type == XB_CHUNK_TYPE_EOF) {
165 return XB_STREAM_READ_CHUNK;
166 }
167
168 /* Payload length */
169 F_READ(tmpbuf, 16);
170 ullval = uint8korr(tmpbuf);
171 if (ullval > (ulonglong) SIZE_T_MAX) {
172 msg("xb_stream_read_chunk(): chunk length is too large at "
173 "offset 0x%llx: 0x%llx.\n", (ulonglong) stream->offset,
174 ullval);
175 goto err;
176 }
177 chunk->length = (size_t) ullval;
178 stream->offset += 8;
179
180 /* Payload offset */
181 ullval = uint8korr(tmpbuf + 8);
182 if (ullval > (ulonglong) MY_OFF_T_MAX) {
183 msg("xb_stream_read_chunk(): chunk offset is too large at "
184 "offset 0x%llx: 0x%llx.\n", (ulonglong) stream->offset,
185 ullval);
186 goto err;
187 }
188 chunk->offset = (my_off_t) ullval;
189 stream->offset += 8;
190
191 /* Reallocate the buffer if needed */
192 if (chunk->length > chunk->buflen) {
193 chunk->data = my_realloc(chunk->data, chunk->length,
194 MYF(MY_WME | MY_ALLOW_ZERO_PTR));
195 if (chunk->data == NULL) {
196 msg("xb_stream_read_chunk(): failed to increase buffer "
197 "to %lu bytes.\n", (ulong) chunk->length);
198 goto err;
199 }
200 chunk->buflen = chunk->length;
201 }
202
203 /* Checksum */
204 F_READ(tmpbuf, 4);
205 chunk->checksum = uint4korr(tmpbuf);
206 chunk->checksum_offset = stream->offset;
207
208 /* Payload */
209 if (chunk->length > 0) {
210 F_READ(chunk->data, chunk->length);
211 stream->offset += chunk->length;
212 }
213
214 stream->offset += 4;
215
216 return XB_STREAM_READ_CHUNK;
217
218err:
219 return XB_STREAM_READ_ERROR;
220}
221
222int
223xb_stream_read_done(xb_rstream_t *stream)
224{
225 my_free(stream);
226
227 return 0;
228}
229