lib/pattern: Support binary pattern buffers on windows
[fio.git] / lib / pattern.c
CommitLineData
fada2fcd
TK
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <limits.h>
5#include <errno.h>
6#include <assert.h>
a1554f65
SB
7#include <fcntl.h>
8#include <unistd.h>
fada2fcd 9
634bd210
RP
10#include "strntol.h"
11#include "pattern.h"
fada2fcd 12#include "../minmax.h"
a328eb9a 13#include "../oslib/strcasestr.h"
b29c71c4 14#include "../oslib/strndup.h"
634bd210 15
a1554f65
SB
16/**
17 * parse_file() - parses binary file to fill buffer
18 * @beg - string input, extract filename from this
19 * @out - output buffer where parsed number should be put
20 * @out_len - length of the output buffer
21 * @filled - pointer where number of bytes successfully
22 * parsed will be put
23 *
24 * Returns the end pointer where parsing has been stopped.
25 * In case of parsing error or lack of bytes in output buffer
26 * NULL will be returned.
27 */
28static const char *parse_file(const char *beg, char *out,
29 unsigned int out_len,
30 unsigned int *filled)
31{
32 const char *end;
33 char *file;
34 int fd;
accccb19 35 ssize_t rc, count = 0;
a1554f65
SB
36
37 if (!out_len)
38 goto err_out;
39
40 assert(*beg == '\'');
41 beg++;
42 end = strchr(beg, '\'');
43 if (!end)
44 goto err_out;
45
46 file = strndup(beg, end - beg);
47 if (file == NULL)
48 goto err_out;
49
1fb215e9
LG
50#ifdef _WIN32
51 fd = open(file, O_RDONLY | O_BINARY);
52#else
a1554f65 53 fd = open(file, O_RDONLY);
1fb215e9 54#endif
a1554f65
SB
55 if (fd < 0)
56 goto err_free_out;
57
6c939739 58 if (out) {
accccb19
LG
59 while (1) {
60 rc = read(fd, out, out_len - count);
61 if (rc == 0)
62 break;
63 if (rc == -1)
64 goto err_free_close_out;
65
66 count += rc;
67 out += rc;
68 }
6c939739
LG
69 } else {
70 count = lseek(fd, 0, SEEK_END);
71 if (count == -1)
72 goto err_free_close_out;
73 if (count >= out_len)
74 count = out_len;
75 }
a1554f65
SB
76
77 *filled = count;
78 close(fd);
79 free(file);
80
81 /* Catch up quote */
82 return end + 1;
83
84err_free_close_out:
85 close(fd);
86err_free_out:
87 free(file);
88err_out:
89 return NULL;
90
91}
92
634bd210
RP
93/**
94 * parse_string() - parses string in double quotes, like "abc"
95 * @beg - string input
96 * @out - output buffer where parsed number should be put
97 * @out_len - length of the output buffer
98 * @filled - pointer where number of bytes successfully
99 * parsed will be put
100 *
101 * Returns the end pointer where parsing has been stopped.
102 * In case of parsing error or lack of bytes in output buffer
103 * NULL will be returned.
104 */
105static const char *parse_string(const char *beg, char *out,
106 unsigned int out_len,
107 unsigned int *filled)
108{
109 const char *end;
110
111 if (!out_len)
112 return NULL;
113
114 assert(*beg == '"');
115 beg++;
116 end = strchr(beg, '"');
117 if (!end)
118 return NULL;
119 if (end - beg > out_len)
120 return NULL;
121
6c939739
LG
122 if (out)
123 memcpy(out, beg, end - beg);
634bd210
RP
124 *filled = end - beg;
125
126 /* Catch up quote */
127 return end + 1;
128}
129
130/**
131 * parse_number() - parses numbers
132 * @beg - string input
133 * @out - output buffer where parsed number should be put
134 * @out_len - length of the output buffer
135 * @filled - pointer where number of bytes successfully
136 * parsed will be put
137 *
138 * Supports decimals in the range [INT_MIN, INT_MAX] and
139 * hexidecimals of any size, which should be started with
140 * prefix 0x or 0X.
141 *
142 * Returns the end pointer where parsing has been stopped.
143 * In case of parsing error or lack of bytes in output buffer
144 * NULL will be returned.
145 */
146static const char *parse_number(const char *beg, char *out,
147 unsigned int out_len,
148 unsigned int *filled)
149{
150 const char *end;
151 unsigned int val;
152 long lval;
153 int num, i;
154
155 if (!out_len)
156 return NULL;
157
158 num = 0;
159 sscanf(beg, "0%*[xX]%*[0-9a-fA-F]%n", &num);
160 if (num == 0) {
161 /* Here we are trying to parse decimal */
162
163 char *_end;
164
165 /* Looking ahead */
166 _end = strcasestr(beg, "0x");
167 if (_end)
168 num = _end - beg;
169 if (num)
170 lval = strntol(beg, num, &_end, 10);
171 else
172 lval = strtol(beg, &_end, 10);
173 if (beg == _end || lval > INT_MAX || lval < INT_MIN)
174 return NULL;
175 end = _end;
176 i = 0;
177 if (!lval) {
178 num = 0;
6c939739
LG
179 if (out)
180 out[i] = 0x00;
634bd210
RP
181 i = 1;
182 } else {
183 val = (unsigned int)lval;
184 for (; val && out_len; out_len--, i++, val >>= 8)
6c939739
LG
185 if (out)
186 out[i] = val & 0xff;
634bd210
RP
187 if (val)
188 return NULL;
189 }
190 } else {
191 assert(num > 2);
192
193 /* Catch up 0x prefix */
194 num -= 2;
195 beg += 2;
196
197 /* Look back, handle this combined string: 0xff0x14 */
198 if (beg[num] && !strncasecmp(&beg[num - 1], "0x", 2))
199 num--;
200
201 end = beg + num;
202
203 for (i = 0; num && out_len;
204 out_len--, i++, num -= 2, beg += 2) {
205 const char *fmt;
206
207 fmt = (num & 1 ? "%1hhx" : "%2hhx");
6c939739
LG
208 if (out)
209 sscanf(beg, fmt, &out[i]);
634bd210
RP
210 if (num & 1) {
211 num++;
212 beg--;
213 }
214 }
215 if (num)
216 return NULL;
217 }
218
219 *filled = i;
220 return end;
221
222}
223
224/**
225 * parse_format() - parses formats, like %o, etc
226 * @in - string input
227 * @out - output buffer where space for format should be reserved
228 * @parsed - number of bytes which were already parsed so far
229 * @out_len - length of the output buffer
44031abf 230 * @fmt_desc - format descriptor array, what we expect to find
634bd210
RP
231 * @fmt - format array, the output
232 * @fmt_sz - size of format array
233 *
234 * This function tries to find formats, e.g.:
235 * %o - offset of the block
236 *
fc002f14 237 * In case of successful parsing it fills the format param
634bd210
RP
238 * with proper offset and the size of the expected value, which
239 * should be pasted into buffer using the format 'func' callback.
240 *
241 * Returns the end pointer where parsing has been stopped.
242 * In case of parsing error or lack of bytes in output buffer
243 * NULL will be returned.
244 */
245static const char *parse_format(const char *in, char *out, unsigned int parsed,
246 unsigned int out_len, unsigned int *filled,
247 const struct pattern_fmt_desc *fmt_desc,
634bd210
RP
248 struct pattern_fmt *fmt, unsigned int fmt_sz)
249{
250 int i;
251 struct pattern_fmt *f = NULL;
252 unsigned int len = 0;
253
969b9fbb 254 if (!out_len || !fmt_desc || !fmt || !fmt_sz)
634bd210
RP
255 return NULL;
256
257 assert(*in == '%');
258
969b9fbb 259 for (i = 0; fmt_desc[i].fmt; i++) {
634bd210
RP
260 const struct pattern_fmt_desc *desc;
261
262 desc = &fmt_desc[i];
263 len = strlen(desc->fmt);
264 if (0 == strncmp(in, desc->fmt, len)) {
265 fmt->desc = desc;
266 fmt->off = parsed;
267 f = fmt;
268 break;
269 }
270 }
271
272 if (!f)
273 return NULL;
274 if (f->desc->len > out_len)
275 return NULL;
276
6c939739
LG
277 if (out)
278 memset(out, '\0', f->desc->len);
634bd210
RP
279 *filled = f->desc->len;
280
281 return in + len;
282}
283
284/**
285 * parse_and_fill_pattern() - Parses combined input, which consists of strings,
286 * numbers and pattern formats.
287 * @in - string input
288 * @in_len - size of the input string
6c939739
LG
289 * @out - output buffer where parsed result will be put, may be NULL
290 * in which case this function just calculates the required
291 * length of the buffer
634bd210
RP
292 * @out_len - lengths of the output buffer
293 * @fmt_desc - array of pattern format descriptors [input]
634bd210
RP
294 * @fmt - array of pattern formats [output]
295 * @fmt_sz - pointer where the size of pattern formats array stored [input],
fc002f14 296 * after successful parsing this pointer will contain the number
634bd210
RP
297 * of parsed formats if any [output].
298 *
299 * strings:
300 * bytes sequence in double quotes, e.g. "123".
301 * NOTE: there is no way to escape quote, so "123\"abc" does not work.
302 *
303 * numbers:
fc002f14 304 * hexadecimal - sequence of hex bytes starting from 0x or 0X prefix,
634bd210
RP
305 * e.g. 0xff12ceff1100ff
306 * decimal - decimal number in range [INT_MIN, INT_MAX]
307 *
308 * formats:
309 * %o - offset of block, reserved 8 bytes.
310 *
311 * Explicit examples of combined string:
312 * #1 #2 #3 #4
313 * in="abcd" in=-1024 in=66 in=0xFF0X1
314 * out=61 62 63 64 out=00 fc ff ff out=42 out=ff 01
315 *
316 * #5 #6
317 * in=%o in="123"0xFFeeCC
318 * out=00 00 00 00 00 00 00 00 out=31 32 33 ff ec cc
319 *
320 * #7
321 * in=-100xab"1"%o"2"
322 * out=f6 ff ff ff ab 31 00 00 00 00 00 00 00 00 32
323 *
324 * #9
325 * in=%o0xdeadbeef%o
326 * out=00 00 00 00 00 00 00 00 de ad be ef 00 00 00 00 00 00 00 00
327 *
328 * #10
329 * in=0xfefefefefefefefefefefefefefefefefefefefefe
330 * out=fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
331 *
332 * Returns number of bytes filled or err < 0 in case of failure.
333 */
1dc47d6b
LG
334static int parse_and_fill_pattern(const char *in, unsigned int in_len,
335 char *out, unsigned int out_len,
336 const struct pattern_fmt_desc *fmt_desc,
337 struct pattern_fmt *fmt,
338 unsigned int *fmt_sz_out)
634bd210
RP
339{
340 const char *beg, *end, *out_beg = out;
341 unsigned int total = 0, fmt_rem = 0;
342
6c939739 343 if (!in || !in_len || !out_len)
634bd210
RP
344 return -EINVAL;
345 if (fmt_sz_out)
346 fmt_rem = *fmt_sz_out;
347
348 beg = in;
349 do {
350 unsigned int filled;
351 int parsed_fmt;
352
353 filled = 0;
354 parsed_fmt = 0;
355
356 switch (*beg) {
a1554f65
SB
357 case '\'':
358 end = parse_file(beg, out, out_len, &filled);
359 break;
634bd210
RP
360 case '"':
361 end = parse_string(beg, out, out_len, &filled);
362 break;
363 case '%':
364 end = parse_format(beg, out, out - out_beg, out_len,
969b9fbb 365 &filled, fmt_desc, fmt, fmt_rem);
634bd210
RP
366 parsed_fmt = 1;
367 break;
368 default:
369 end = parse_number(beg, out, out_len, &filled);
370 break;
371 }
372
373 if (!end)
374 return -EINVAL;
375
376 if (parsed_fmt) {
377 assert(fmt_rem);
378 fmt_rem--;
379 fmt++;
380 }
381
382 assert(end - beg <= in_len);
383 in_len -= end - beg;
384 beg = end;
385
386 assert(filled);
387 assert(filled <= out_len);
388 out_len -= filled;
389 out += filled;
390 total += filled;
391
392 } while (in_len);
393
394 if (fmt_sz_out)
395 *fmt_sz_out -= fmt_rem;
396 return total;
397}
398
1dc47d6b
LG
399/**
400 * parse_and_fill_pattern_alloc() - Parses combined input, which consists of
401 * strings, numbers and pattern formats and
402 * allocates a buffer for the result.
403 *
404 * @in - string input
405 * @in_len - size of the input string
406 * @out - pointer to the output buffer pointer, this will be set to the newly
407 * allocated pattern buffer which must be freed by the caller
408 * @fmt_desc - array of pattern format descriptors [input]
409 * @fmt - array of pattern formats [output]
410 * @fmt_sz - pointer where the size of pattern formats array stored [input],
411 * after successful parsing this pointer will contain the number
412 * of parsed formats if any [output].
413 *
414 * See documentation on parse_and_fill_pattern() above for a description
415 * of the functionality.
416 *
417 * Returns number of bytes filled or err < 0 in case of failure.
418 */
419int parse_and_fill_pattern_alloc(const char *in, unsigned int in_len,
420 char **out, const struct pattern_fmt_desc *fmt_desc,
421 struct pattern_fmt *fmt, unsigned int *fmt_sz_out)
422{
423 int count;
424
425 count = parse_and_fill_pattern(in, in_len, NULL, MAX_PATTERN_SIZE,
426 fmt_desc, fmt, fmt_sz_out);
427 if (count < 0)
428 return count;
429
430 *out = malloc(count);
431 count = parse_and_fill_pattern(in, in_len, *out, count, fmt_desc,
432 fmt, fmt_sz_out);
433 if (count < 0) {
434 free(*out);
435 *out = NULL;
436 }
437
438 return count;
439}
440
634bd210
RP
441/**
442 * dup_pattern() - Duplicates part of the pattern all over the buffer.
443 *
444 * Returns 0 in case of success or errno < 0 in case of failure.
445 */
446static int dup_pattern(char *out, unsigned int out_len, unsigned int pattern_len)
447{
448 unsigned int left, len, off;
449
450 if (out_len <= pattern_len)
451 /* Normal case */
452 return 0;
453
454 off = pattern_len;
455 left = (out_len - off);
456 len = min(left, off);
457
458 /* Duplicate leftover */
459 while (left) {
460 memcpy(out + off, out, len);
461 left -= len;
462 off <<= 1;
463 len = min(left, off);
464 }
465
466 return 0;
467}
468
469/**
470 * cpy_pattern() - Copies pattern to the buffer.
471 *
472 * Function copies pattern along the whole buffer.
473 *
474 * Returns 0 in case of success or errno < 0 in case of failure.
475 */
476int cpy_pattern(const char *pattern, unsigned int pattern_len,
477 char *out, unsigned int out_len)
478{
479 unsigned int len;
480
481 if (!pattern || !pattern_len || !out || !out_len)
482 return -EINVAL;
483
484 /* Copy pattern */
485 len = min(pattern_len, out_len);
486 memcpy(out, pattern, len);
487
488 /* Spread filled chunk all over the buffer */
489 return dup_pattern(out, out_len, pattern_len);
490}
491
492/**
493 * cmp_pattern() - Compares pattern and buffer.
494 *
495 * For the sake of performance this function avoids any loops.
496 * Firstly it tries to compare the buffer itself, checking that
497 * buffer consists of repeating patterns along the buffer size.
498 *
499 * If the difference is not found then the function tries to compare
500 * buffer and pattern.
501 *
502 * Returns 0 in case of success or errno < 0 in case of failure.
503 */
504int cmp_pattern(const char *pattern, unsigned int pattern_size,
505 unsigned int off, const char *buf, unsigned int len)
506{
507 int rc;
508 unsigned int size;
509
510 /* Find the difference in buffer */
511 if (len > pattern_size) {
512 rc = memcmp(buf, buf + pattern_size, len - pattern_size);
513 if (rc)
514 return -EILSEQ;
515 }
516 /* Compare second part of the pattern with buffer */
517 if (off) {
518 size = min(len, pattern_size - off);
519 rc = memcmp(buf, pattern + off, size);
520 if (rc)
521 return -EILSEQ;
522 buf += size;
523 len -= size;
524 }
525 /* Compare first part of the pattern or the whole pattern
526 * with buffer */
527 if (len) {
528 size = min(len, (off ? off : pattern_size));
529 rc = memcmp(buf, pattern, size);
530 if (rc)
531 return -EILSEQ;
532 }
533
534 return 0;
535}
536
537/**
538 * paste_format_inplace() - Pastes parsed formats to the pattern.
539 *
540 * This function pastes formats to the pattern. If @fmt_sz is 0
541 * function does nothing and pattern buffer is left untouched.
542 *
543 * Returns 0 in case of success or errno < 0 in case of failure.
544 */
545int paste_format_inplace(char *pattern, unsigned int pattern_len,
546 struct pattern_fmt *fmt, unsigned int fmt_sz,
547 void *priv)
548{
549 int i, rc;
550 unsigned int len;
551
552 if (!pattern || !pattern_len || !fmt)
553 return -EINVAL;
554
555 /* Paste formats for first pattern chunk */
556 for (i = 0; i < fmt_sz; i++) {
557 struct pattern_fmt *f;
558
559 f = &fmt[i];
560 if (pattern_len <= f->off)
561 break;
562 len = min(pattern_len - f->off, f->desc->len);
563 rc = f->desc->paste(pattern + f->off, len, priv);
564 if (rc)
565 return rc;
566 }
567
568 return 0;
569}
570
571/**
572 * paste_format() - Pastes parsed formats to the buffer.
573 *
574 * This function copies pattern to the buffer, pastes format
575 * into it and then duplicates pattern all over the buffer size.
576 *
577 * Returns 0 in case of success or errno < 0 in case of failure.
578 */
579int paste_format(const char *pattern, unsigned int pattern_len,
580 struct pattern_fmt *fmt, unsigned int fmt_sz,
581 char *out, unsigned int out_len, void *priv)
582{
583 int rc;
584 unsigned int len;
585
586 if (!pattern || !pattern_len || !out || !out_len)
587 return -EINVAL;
588
589 /* Copy pattern */
590 len = min(pattern_len, out_len);
591 memcpy(out, pattern, len);
592
593 rc = paste_format_inplace(out, len, fmt, fmt_sz, priv);
594 if (rc)
595 return rc;
596
597 /* Spread filled chunk all over the buffer */
598 return dup_pattern(out, out_len, pattern_len);
599}