1
2 /*
3 * Redistribution and use in source and binary forms, with or
4 * without modification, are permitted provided that the following
5 * conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above
8 * copyright notice, this list of conditions and the
9 * following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials
14 * provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <stdarg.h>
34 #include <string.h>
35
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <errno.h>
39
40 #include <third_party/crc32.h>
41
42 #include <tarantool.h>
43 #include <tnt_log.h>
44
tnt_log_guess(char * file)45 enum tnt_log_type tnt_log_guess(char *file) {
46 if (file == NULL)
47 return TNT_LOG_XLOG;
48 char *ext = strrchr(file, '.');
49 if (ext == NULL)
50 return TNT_LOG_NONE;
51 if (strcasecmp(ext, ".snap") == 0)
52 return TNT_LOG_SNAPSHOT;
53 if (strcasecmp(ext, ".xlog") == 0)
54 return TNT_LOG_XLOG;
55 return TNT_LOG_NONE;
56 }
57
58 inline static int
tnt_log_seterr(struct tnt_log * l,enum tnt_log_error e)59 tnt_log_seterr(struct tnt_log *l, enum tnt_log_error e) {
60 l->error = e;
61 if (e == TNT_LOG_ESYSTEM)
62 l->errno_ = errno;
63 return -1;
64 }
65
66 const uint32_t tnt_log_marker_v11 = 0xba0babed;
67 const uint32_t tnt_log_marker_eof_v11 = 0x10adab1e;
68
69 inline static int
tnt_log_eof(struct tnt_log * l,char * data)70 tnt_log_eof(struct tnt_log *l, char *data) {
71 uint32_t marker = 0;
72 if (data)
73 tnt_mem_free(data);
74 /* checking eof condition */
75 if (ftello(l->fd) == l->offset + sizeof(tnt_log_marker_eof_v11)) {
76 fseeko(l->fd, l->offset, SEEK_SET);
77 if (fread(&marker, sizeof(marker), 1, l->fd) != 1)
78 return tnt_log_seterr(l, TNT_LOG_ESYSTEM);
79 else
80 if (marker != tnt_log_marker_eof_v11)
81 return tnt_log_seterr(l, TNT_LOG_ECORRUPT);
82 l->offset = ftello(l->fd);
83 }
84 /* eof */
85 return 1;
86 }
87
tnt_log_read(struct tnt_log * l,char ** buf,uint32_t * size)88 static int tnt_log_read(struct tnt_log *l, char **buf, uint32_t *size)
89 {
90 /* current record offset (before marker) */
91 l->current_offset = ftello(l->fd);
92
93 /* reading marker */
94 char *data = NULL;
95 uint32_t marker = 0;
96 if (fread(&marker, sizeof(marker), 1, l->fd) != 1)
97 return tnt_log_eof(l, data);
98
99 /* seeking for marker if necessary */
100 while (marker != tnt_log_marker_v11) {
101 int c = fgetc(l->fd);
102 if (c == EOF)
103 return tnt_log_eof(l, data);
104 marker = marker >> 8 | ((uint32_t) c & 0xff) <<
105 (sizeof(marker) * 8 - 8);
106 }
107
108 /* reading header */
109 if (fread(&l->current.hdr, sizeof(l->current.hdr), 1, l->fd) != 1)
110 return tnt_log_eof(l, data);
111
112 /* updating offset */
113 l->offset = ftello(l->fd);
114
115 /* checking header crc, starting from lsn */
116 uint32_t crc32_hdr =
117 crc32c(0, (unsigned char*)&l->current.hdr + sizeof(uint32_t),
118 sizeof(struct tnt_log_header_v11) -
119 sizeof(uint32_t));
120 if (crc32_hdr != l->current.hdr.crc32_hdr)
121 return tnt_log_seterr(l, TNT_LOG_ECORRUPT);
122
123 /* allocating memory and reading data */
124 data = tnt_mem_alloc(l->current.hdr.len);
125 if (data == NULL)
126 return tnt_log_seterr(l, TNT_LOG_EMEMORY);
127 if (fread(data, l->current.hdr.len, 1, l->fd) != 1)
128 return tnt_log_eof(l, data);
129
130 /* checking data crc */
131 uint32_t crc32_data = crc32c(0, (unsigned char*)data, l->current.hdr.len);
132 if (crc32_data != l->current.hdr.crc32_data) {
133 tnt_mem_free(data);
134 return tnt_log_seterr(l, TNT_LOG_ECORRUPT);
135 }
136
137 *buf = data;
138 *size = l->current.hdr.len;
139 return 0;
140 }
141
142 static int
tnt_log_process_xlog(struct tnt_log * l,char * buf,uint32_t size,union tnt_log_value * value)143 tnt_log_process_xlog(struct tnt_log *l, char *buf, uint32_t size,
144 union tnt_log_value *value)
145 {
146 (void)size;
147 /* copying row data */
148 memcpy(&l->current.row, buf, sizeof(l->current.row));
149
150 /* preparing pseudo iproto header */
151 struct tnt_header hdr_iproto;
152 hdr_iproto.type = l->current.row.op;
153 hdr_iproto.len = l->current.hdr.len - sizeof(l->current.row);
154 hdr_iproto.reqid = 0;
155
156 /* deserializing operation */
157 tnt_request_init(&value->r);
158 size_t off = 0;
159 int rc = tnt_request(&value->r,
160 buf + sizeof(l->current.row),
161 l->current.hdr.len - sizeof(l->current.row),
162 &off,
163 &hdr_iproto);
164
165 /* in case of not completed request or parsing error */
166 if (rc != 0)
167 return tnt_log_seterr(l, TNT_LOG_ECORRUPT);
168 return 0;
169 }
170
171 static int
tnt_log_process_snapshot(struct tnt_log * l,char * buf,uint32_t size,union tnt_log_value * value)172 tnt_log_process_snapshot(struct tnt_log *l, char *buf, uint32_t size,
173 union tnt_log_value *value)
174 {
175 (void)size;
176
177 /* freeing previously allocated tuple */
178 tnt_tuple_free(&value->t);
179
180 /* copying snapshot row data */
181 memcpy(&l->current.row_snap, buf, sizeof(l->current.row_snap));
182
183 /* reading and validating tuple */
184 struct tnt_tuple *tu =
185 tnt_tuple_set_as(&value->t, buf + sizeof(l->current.row_snap),
186 l->current.row_snap.data_size,
187 l->current.row_snap.tuple_size);
188 if (tu == NULL)
189 return tnt_log_seterr(l, TNT_LOG_ECORRUPT);
190
191 return (tu) ? 0 : -1;
192 }
193
194 struct tnt_log_row*
tnt_log_next_to(struct tnt_log * l,union tnt_log_value * value)195 tnt_log_next_to(struct tnt_log *l, union tnt_log_value *value) {
196 char *buf = NULL;
197 uint32_t size = 0;
198 int rc = l->read(l, &buf, &size);
199 if (rc != 0)
200 return NULL;
201 rc = l->process(l, buf, size, value);
202 if (rc != 0) {
203 tnt_mem_free(buf);
204 return NULL;
205 }
206 if (l->type == TNT_LOG_XLOG) {
207 tnt_request_setorigin(&value->r, buf, size);
208 } else {
209 tnt_mem_free(buf);
210 }
211 return &l->current;
212 }
213
tnt_log_next(struct tnt_log * l)214 struct tnt_log_row *tnt_log_next(struct tnt_log *l) {
215 return tnt_log_next_to(l, &l->current_value);
216 }
217
218 inline static int
tnt_log_open_err(struct tnt_log * l,enum tnt_log_error e)219 tnt_log_open_err(struct tnt_log *l, enum tnt_log_error e) {
220 tnt_log_seterr(l, e);
221 tnt_log_close(l);
222 return -1;
223 }
224
225 enum tnt_log_error
tnt_log_open(struct tnt_log * l,char * file,enum tnt_log_type type)226 tnt_log_open(struct tnt_log *l, char *file, enum tnt_log_type type)
227 {
228 char filetype[32];
229 char version[32];
230 char *rc, *magic = "\0";
231 l->type = type;
232 /* trying to open file */
233 if (file) {
234 l->fd = fopen(file, "r");
235 if (l->fd == NULL)
236 return tnt_log_open_err(l, TNT_LOG_ESYSTEM);
237 } else {
238 l->fd = stdin;
239 }
240 /* reading xlog filetype */
241 rc = fgets(filetype, sizeof(filetype), l->fd);
242 if (rc == NULL)
243 return tnt_log_open_err(l, TNT_LOG_ESYSTEM);
244 /* reading log version */
245 rc = fgets(version, sizeof(version), l->fd);
246 if (rc == NULL)
247 return tnt_log_open_err(l, TNT_LOG_ESYSTEM);
248 /* checking file type and setting read/process
249 * interfaces */
250 l->read = tnt_log_read;
251 switch (type) {
252 case TNT_LOG_XLOG:
253 magic = TNT_LOG_MAGIC_XLOG;
254 l->process = tnt_log_process_xlog;
255 break;
256 case TNT_LOG_SNAPSHOT:
257 magic = TNT_LOG_MAGIC_SNAP;
258 l->process = tnt_log_process_snapshot;
259 break;
260 case TNT_LOG_NONE:
261 break;
262 }
263 if (strcmp(filetype, magic))
264 return tnt_log_open_err(l, TNT_LOG_ETYPE);
265 /* checking version */
266 if (strcmp(version, TNT_LOG_VERSION))
267 return tnt_log_open_err(l, TNT_LOG_EVERSION);
268 for (;;) {
269 char buf[256];
270 rc = fgets(buf, sizeof(buf), l->fd);
271 if (rc == NULL)
272 return tnt_log_open_err(l, TNT_LOG_EFAIL);
273 if (strcmp(rc, "\n") == 0 || strcmp(rc, "\r\n") == 0)
274 break;
275 }
276 /* getting current offset */
277 l->offset = ftello(l->fd);
278 l->current_offset = 0;
279 memset(&l->current_value, 0, sizeof(l->current_value));
280 return 0;
281 }
282
tnt_log_close(struct tnt_log * l)283 void tnt_log_close(struct tnt_log *l) {
284 if (l->fd && l->fd != stdin)
285 fclose(l->fd);
286 l->fd = NULL;
287 }
288
tnt_log_seek(struct tnt_log * l,off_t offset)289 int tnt_log_seek(struct tnt_log *l, off_t offset)
290 {
291 l->offset = offset;
292 return fseeko(l->fd, offset, SEEK_SET);
293 }
294
tnt_log_error(struct tnt_log * l)295 enum tnt_log_error tnt_log_error(struct tnt_log *l) {
296 return l->error;
297 }
298
299 struct tnt_log_error_desc {
300 enum tnt_log_error type;
301 char *desc;
302 };
303
304 static struct tnt_log_error_desc tnt_log_error_list[] =
305 {
306 { TNT_LOG_EOK, "ok" },
307 { TNT_LOG_EFAIL, "fail" },
308 { TNT_LOG_EMEMORY, "memory allocation failed" },
309 { TNT_LOG_ETYPE, "file type mismatch" },
310 { TNT_LOG_EVERSION, "file version mismatch" },
311 { TNT_LOG_ECORRUPT, "file crc failed or bad eof marker" },
312 { TNT_LOG_ESYSTEM, "system error" },
313 { TNT_LOG_LAST, NULL }
314 };
315
tnt_log_strerror(struct tnt_log * l)316 char *tnt_log_strerror(struct tnt_log *l) {
317 if (l->error == TNT_LOG_ESYSTEM) {
318 static char msg[256];
319 snprintf(msg, sizeof(msg), "%s (errno: %d)",
320 strerror(l->errno_),
321 l->errno_);
322 return msg;
323 }
324 return tnt_log_error_list[(int)l->error].desc;
325 }
326
tnt_log_errno(struct tnt_log * l)327 int tnt_log_errno(struct tnt_log *l) {
328 return l->errno_;
329 }
330