]> git.proxmox.com Git - mirror_qemu.git/blame - qapi/string-input-visitor.c
exec.c: correct the maximum skip value during compact
[mirror_qemu.git] / qapi / string-input-visitor.c
CommitLineData
a020f980
PB
1/*
2 * String parsing visitor
3 *
08f9541d 4 * Copyright Red Hat, Inc. 2012-2016
a020f980
PB
5 *
6 * Author: Paolo Bonzini <pbonzini@redhat.com>
c9fba9de 7 * David Hildenbrand <david@redhat.com>
a020f980
PB
8 *
9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10 * See the COPYING.LIB file in the top-level directory.
a020f980
PB
11 */
12
cbf21151 13#include "qemu/osdep.h"
da34e65c 14#include "qapi/error.h"
7b1b5d19
PB
15#include "qapi/string-input-visitor.h"
16#include "qapi/visitor-impl.h"
17#include "qapi/qmp/qerror.h"
84be629d 18#include "qapi/qmp/qnull.h"
a5829ccf 19#include "qemu/option.h"
4b69d4c3 20#include "qemu/cutils.h"
659268ff 21
c9fba9de
DH
22typedef enum ListMode {
23 /* no list parsing active / no list expected */
24 LM_NONE,
25 /* we have an unparsed string remaining */
26 LM_UNPARSED,
27 /* we have an unfinished int64 range */
28 LM_INT64_RANGE,
29 /* we have an unfinished uint64 range */
30 LM_UINT64_RANGE,
31 /* we have parsed the string completely and no range is remaining */
32 LM_END,
33} ListMode;
34
35/* protect against DOS attacks, limit the amount of elements per range */
36#define RANGE_MAX_ELEMENTS 65536
37
38typedef union RangeElement {
39 int64_t i64;
40 uint64_t u64;
41} RangeElement;
a020f980
PB
42
43struct StringInputVisitor
44{
45 Visitor visitor;
659268ff 46
c9fba9de
DH
47 /* List parsing state */
48 ListMode lm;
49 RangeElement rangeNext;
50 RangeElement rangeEnd;
51 const char *unparsed_string;
52 void *list;
659268ff 53
c9fba9de 54 /* The original string to parse */
a020f980
PB
55 const char *string;
56};
57
d7bea75d
EB
58static StringInputVisitor *to_siv(Visitor *v)
59{
60 return container_of(v, StringInputVisitor, visitor);
61}
62
c9fba9de
DH
63static void start_list(Visitor *v, const char *name, GenericList **list,
64 size_t size, Error **errp)
659268ff 65{
d7bea75d 66 StringInputVisitor *siv = to_siv(v);
659268ff 67
c9fba9de 68 assert(siv->lm == LM_NONE);
1158bb2a 69 siv->list = list;
c9fba9de 70 siv->unparsed_string = siv->string;
d9f62dde 71
c9fba9de
DH
72 if (!siv->string[0]) {
73 if (list) {
74 *list = NULL;
659268ff 75 }
c9fba9de 76 siv->lm = LM_END;
d9f62dde 77 } else {
c9fba9de
DH
78 if (list) {
79 *list = g_malloc0(size);
80 }
81 siv->lm = LM_UNPARSED;
659268ff
HT
82 }
83}
84
d9f62dde 85static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
659268ff 86{
d7bea75d 87 StringInputVisitor *siv = to_siv(v);
659268ff 88
c9fba9de
DH
89 switch (siv->lm) {
90 case LM_END:
659268ff 91 return NULL;
c9fba9de
DH
92 case LM_INT64_RANGE:
93 case LM_UINT64_RANGE:
94 case LM_UNPARSED:
95 /* we have an unparsed string or something left in a range */
96 break;
97 default:
98 abort();
659268ff
HT
99 }
100
d9f62dde
EB
101 tail->next = g_malloc0(size);
102 return tail->next;
659268ff
HT
103}
104
a4a1c70d
MA
105static void check_list(Visitor *v, Error **errp)
106{
107 const StringInputVisitor *siv = to_siv(v);
a4a1c70d 108
c9fba9de
DH
109 switch (siv->lm) {
110 case LM_INT64_RANGE:
111 case LM_UINT64_RANGE:
112 case LM_UNPARSED:
113 error_setg(errp, "Fewer list elements expected");
a4a1c70d 114 return;
c9fba9de 115 case LM_END:
a4a1c70d 116 return;
c9fba9de
DH
117 default:
118 abort();
a4a1c70d 119 }
a4a1c70d
MA
120}
121
1158bb2a 122static void end_list(Visitor *v, void **obj)
659268ff 123{
1158bb2a
EB
124 StringInputVisitor *siv = to_siv(v);
125
c9fba9de 126 assert(siv->lm != LM_NONE);
1158bb2a 127 assert(siv->list == obj);
c9fba9de
DH
128 siv->list = NULL;
129 siv->unparsed_string = NULL;
130 siv->lm = LM_NONE;
131}
132
133static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
134{
135 const char *endptr;
136 int64_t start, end;
137
138 /* parse a simple int64 or range */
139 if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
140 return -EINVAL;
141 }
142 end = start;
143
144 switch (endptr[0]) {
145 case '\0':
146 siv->unparsed_string = endptr;
147 break;
148 case ',':
149 siv->unparsed_string = endptr + 1;
150 break;
151 case '-':
152 /* parse the end of the range */
153 if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
154 return -EINVAL;
155 }
156 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
157 return -EINVAL;
158 }
159 switch (endptr[0]) {
160 case '\0':
161 siv->unparsed_string = endptr;
162 break;
163 case ',':
164 siv->unparsed_string = endptr + 1;
165 break;
166 default:
167 return -EINVAL;
168 }
169 break;
170 default:
171 return -EINVAL;
172 }
173
174 /* we have a proper range (with maybe only one element) */
175 siv->lm = LM_INT64_RANGE;
176 siv->rangeNext.i64 = start;
177 siv->rangeEnd.i64 = end;
178 return 0;
659268ff
HT
179}
180
0b2a0d6b 181static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
4c40314a 182 Error **errp)
a020f980 183{
d7bea75d 184 StringInputVisitor *siv = to_siv(v);
c9fba9de
DH
185 int64_t val;
186
187 switch (siv->lm) {
188 case LM_NONE:
189 /* just parse a simple int64, bail out if not completely consumed */
190 if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
191 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
192 name ? name : "null", "int64");
193 return;
194 }
195 *obj = val;
74f24cb6 196 return;
c9fba9de
DH
197 case LM_UNPARSED:
198 if (try_parse_int64_list_entry(siv, obj)) {
199 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
200 "list of int64 values or ranges");
201 return;
202 }
203 assert(siv->lm == LM_INT64_RANGE);
204 /* fall through */
205 case LM_INT64_RANGE:
206 /* return the next element in the range */
207 assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
208 *obj = siv->rangeNext.i64++;
209
210 if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
211 /* end of range, check if there is more to parse */
212 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
213 }
214 return;
215 case LM_END:
216 error_setg(errp, "Fewer list elements expected");
217 return;
218 default:
219 abort();
74f24cb6 220 }
c9fba9de 221}
659268ff 222
c9fba9de
DH
223static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
224{
225 const char *endptr;
226 uint64_t start, end;
659268ff 227
c9fba9de
DH
228 /* parse a simple uint64 or range */
229 if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
230 return -EINVAL;
231 }
232 end = start;
233
234 switch (endptr[0]) {
235 case '\0':
236 siv->unparsed_string = endptr;
237 break;
238 case ',':
239 siv->unparsed_string = endptr + 1;
240 break;
241 case '-':
242 /* parse the end of the range */
243 if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
244 return -EINVAL;
659268ff 245 }
c9fba9de
DH
246 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
247 return -EINVAL;
659268ff 248 }
c9fba9de
DH
249 switch (endptr[0]) {
250 case '\0':
251 siv->unparsed_string = endptr;
252 break;
253 case ',':
254 siv->unparsed_string = endptr + 1;
255 break;
256 default:
257 return -EINVAL;
258 }
259 break;
260 default:
261 return -EINVAL;
659268ff
HT
262 }
263
c9fba9de
DH
264 /* we have a proper range (with maybe only one element) */
265 siv->lm = LM_UINT64_RANGE;
266 siv->rangeNext.u64 = start;
267 siv->rangeEnd.u64 = end;
268 return 0;
a020f980
PB
269}
270
0b2a0d6b 271static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
f755dea7
EB
272 Error **errp)
273{
c9fba9de
DH
274 StringInputVisitor *siv = to_siv(v);
275 uint64_t val;
276
277 switch (siv->lm) {
278 case LM_NONE:
279 /* just parse a simple uint64, bail out if not completely consumed */
280 if (qemu_strtou64(siv->string, NULL, 0, &val)) {
281 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
282 "uint64");
283 return;
284 }
285 *obj = val;
286 return;
287 case LM_UNPARSED:
288 if (try_parse_uint64_list_entry(siv, obj)) {
289 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
290 "list of uint64 values or ranges");
291 return;
292 }
293 assert(siv->lm == LM_UINT64_RANGE);
294 /* fall through */
295 case LM_UINT64_RANGE:
296 /* return the next element in the range */
297 assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
298 *obj = siv->rangeNext.u64++;
299
300 if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
301 /* end of range, check if there is more to parse */
302 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
303 }
304 return;
305 case LM_END:
306 error_setg(errp, "Fewer list elements expected");
307 return;
308 default:
309 abort();
f755dea7
EB
310 }
311}
312
0b2a0d6b 313static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
a5829ccf
PB
314 Error **errp)
315{
d7bea75d 316 StringInputVisitor *siv = to_siv(v);
a5829ccf
PB
317 Error *err = NULL;
318 uint64_t val;
319
c9fba9de 320 assert(siv->lm == LM_NONE);
f332e830 321 parse_option_size(name, siv->string, &val, &err);
a5829ccf
PB
322 if (err) {
323 error_propagate(errp, err);
324 return;
325 }
326
327 *obj = val;
328}
329
0b2a0d6b 330static void parse_type_bool(Visitor *v, const char *name, bool *obj,
a020f980
PB
331 Error **errp)
332{
d7bea75d 333 StringInputVisitor *siv = to_siv(v);
a020f980 334
c9fba9de 335 assert(siv->lm == LM_NONE);
f332e830
MA
336 if (!strcasecmp(siv->string, "on") ||
337 !strcasecmp(siv->string, "yes") ||
338 !strcasecmp(siv->string, "true")) {
339 *obj = true;
340 return;
341 }
342 if (!strcasecmp(siv->string, "off") ||
343 !strcasecmp(siv->string, "no") ||
344 !strcasecmp(siv->string, "false")) {
345 *obj = false;
346 return;
a020f980
PB
347 }
348
c6bd8c70
MA
349 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
350 "boolean");
a020f980
PB
351}
352
0b2a0d6b 353static void parse_type_str(Visitor *v, const char *name, char **obj,
a020f980
PB
354 Error **errp)
355{
d7bea75d 356 StringInputVisitor *siv = to_siv(v);
f332e830 357
c9fba9de 358 assert(siv->lm == LM_NONE);
f332e830 359 *obj = g_strdup(siv->string);
a020f980
PB
360}
361
0b2a0d6b 362static void parse_type_number(Visitor *v, const char *name, double *obj,
a020f980
PB
363 Error **errp)
364{
d7bea75d 365 StringInputVisitor *siv = to_siv(v);
a020f980
PB
366 double val;
367
c9fba9de 368 assert(siv->lm == LM_NONE);
4b69d4c3 369 if (qemu_strtod_finite(siv->string, NULL, &val)) {
c6bd8c70
MA
370 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
371 "number");
a020f980
PB
372 return;
373 }
374
375 *obj = val;
376}
377
d2f95f4d
MA
378static void parse_type_null(Visitor *v, const char *name, QNull **obj,
379 Error **errp)
a7333712
GK
380{
381 StringInputVisitor *siv = to_siv(v);
382
c9fba9de 383 assert(siv->lm == LM_NONE);
d2f95f4d
MA
384 *obj = NULL;
385
c9fba9de 386 if (siv->string[0]) {
a7333712
GK
387 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
388 "null");
d2f95f4d 389 return;
a7333712 390 }
d2f95f4d
MA
391
392 *obj = qnull();
a7333712
GK
393}
394
2c0ef9f4
EB
395static void string_input_free(Visitor *v)
396{
397 StringInputVisitor *siv = to_siv(v);
398
7a0525c7 399 g_free(siv);
a020f980
PB
400}
401
7a0525c7 402Visitor *string_input_visitor_new(const char *str)
a020f980
PB
403{
404 StringInputVisitor *v;
405
f332e830 406 assert(str);
a020f980
PB
407 v = g_malloc0(sizeof(*v));
408
983f52d4 409 v->visitor.type = VISITOR_INPUT;
4c40314a 410 v->visitor.type_int64 = parse_type_int64;
f755dea7 411 v->visitor.type_uint64 = parse_type_uint64;
a5829ccf 412 v->visitor.type_size = parse_type_size;
a020f980
PB
413 v->visitor.type_bool = parse_type_bool;
414 v->visitor.type_str = parse_type_str;
415 v->visitor.type_number = parse_type_number;
a7333712 416 v->visitor.type_null = parse_type_null;
659268ff
HT
417 v->visitor.start_list = start_list;
418 v->visitor.next_list = next_list;
a4a1c70d 419 v->visitor.check_list = check_list;
659268ff 420 v->visitor.end_list = end_list;
2c0ef9f4 421 v->visitor.free = string_input_free;
a020f980
PB
422
423 v->string = str;
c9fba9de 424 v->lm = LM_NONE;
7a0525c7 425 return &v->visitor;
a020f980 426}