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