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