]> git.proxmox.com Git - mirror_qemu.git/blob - qapi/string-input-visitor.c
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[mirror_qemu.git] / qapi / string-input-visitor.c
1 /*
2 * String parsing visitor
3 *
4 * Copyright Red Hat, Inc. 2012-2016
5 *
6 * Author: Paolo Bonzini <pbonzini@redhat.com>
7 * David Hildenbrand <david@redhat.com>
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.
11 */
12
13 #include "qemu/osdep.h"
14 #include "qapi/error.h"
15 #include "qapi/string-input-visitor.h"
16 #include "qapi/visitor-impl.h"
17 #include "qapi/qmp/qerror.h"
18 #include "qapi/qmp/qnull.h"
19 #include "qemu/option.h"
20 #include "qemu/cutils.h"
21
22 typedef 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
38 typedef union RangeElement {
39 int64_t i64;
40 uint64_t u64;
41 } RangeElement;
42
43 struct StringInputVisitor
44 {
45 Visitor visitor;
46
47 /* List parsing state */
48 ListMode lm;
49 RangeElement rangeNext;
50 RangeElement rangeEnd;
51 const char *unparsed_string;
52 void *list;
53
54 /* The original string to parse */
55 const char *string;
56 };
57
58 static StringInputVisitor *to_siv(Visitor *v)
59 {
60 return container_of(v, StringInputVisitor, visitor);
61 }
62
63 static void start_list(Visitor *v, const char *name, GenericList **list,
64 size_t size, Error **errp)
65 {
66 StringInputVisitor *siv = to_siv(v);
67
68 assert(siv->lm == LM_NONE);
69 siv->list = list;
70 siv->unparsed_string = siv->string;
71
72 if (!siv->string[0]) {
73 if (list) {
74 *list = NULL;
75 }
76 siv->lm = LM_END;
77 } else {
78 if (list) {
79 *list = g_malloc0(size);
80 }
81 siv->lm = LM_UNPARSED;
82 }
83 }
84
85 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
86 {
87 StringInputVisitor *siv = to_siv(v);
88
89 switch (siv->lm) {
90 case LM_END:
91 return NULL;
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();
99 }
100
101 tail->next = g_malloc0(size);
102 return tail->next;
103 }
104
105 static void check_list(Visitor *v, Error **errp)
106 {
107 const StringInputVisitor *siv = to_siv(v);
108
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");
114 return;
115 case LM_END:
116 return;
117 default:
118 abort();
119 }
120 }
121
122 static void end_list(Visitor *v, void **obj)
123 {
124 StringInputVisitor *siv = to_siv(v);
125
126 assert(siv->lm != LM_NONE);
127 assert(siv->list == obj);
128 siv->list = NULL;
129 siv->unparsed_string = NULL;
130 siv->lm = LM_NONE;
131 }
132
133 static 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;
179 }
180
181 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
182 Error **errp)
183 {
184 StringInputVisitor *siv = to_siv(v);
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;
196 return;
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();
220 }
221 }
222
223 static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
224 {
225 const char *endptr;
226 uint64_t start, end;
227
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;
245 }
246 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
247 return -EINVAL;
248 }
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;
262 }
263
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;
269 }
270
271 static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
272 Error **errp)
273 {
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();
310 }
311 }
312
313 static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
314 Error **errp)
315 {
316 StringInputVisitor *siv = to_siv(v);
317 Error *err = NULL;
318 uint64_t val;
319
320 assert(siv->lm == LM_NONE);
321 parse_option_size(name, siv->string, &val, &err);
322 if (err) {
323 error_propagate(errp, err);
324 return;
325 }
326
327 *obj = val;
328 }
329
330 static void parse_type_bool(Visitor *v, const char *name, bool *obj,
331 Error **errp)
332 {
333 StringInputVisitor *siv = to_siv(v);
334
335 assert(siv->lm == LM_NONE);
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;
347 }
348
349 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
350 "boolean");
351 }
352
353 static void parse_type_str(Visitor *v, const char *name, char **obj,
354 Error **errp)
355 {
356 StringInputVisitor *siv = to_siv(v);
357
358 assert(siv->lm == LM_NONE);
359 *obj = g_strdup(siv->string);
360 }
361
362 static void parse_type_number(Visitor *v, const char *name, double *obj,
363 Error **errp)
364 {
365 StringInputVisitor *siv = to_siv(v);
366 double val;
367
368 assert(siv->lm == LM_NONE);
369 if (qemu_strtod_finite(siv->string, NULL, &val)) {
370 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
371 "number");
372 return;
373 }
374
375 *obj = val;
376 }
377
378 static void parse_type_null(Visitor *v, const char *name, QNull **obj,
379 Error **errp)
380 {
381 StringInputVisitor *siv = to_siv(v);
382
383 assert(siv->lm == LM_NONE);
384 *obj = NULL;
385
386 if (siv->string[0]) {
387 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
388 "null");
389 return;
390 }
391
392 *obj = qnull();
393 }
394
395 static void string_input_free(Visitor *v)
396 {
397 StringInputVisitor *siv = to_siv(v);
398
399 g_free(siv);
400 }
401
402 Visitor *string_input_visitor_new(const char *str)
403 {
404 StringInputVisitor *v;
405
406 assert(str);
407 v = g_malloc0(sizeof(*v));
408
409 v->visitor.type = VISITOR_INPUT;
410 v->visitor.type_int64 = parse_type_int64;
411 v->visitor.type_uint64 = parse_type_uint64;
412 v->visitor.type_size = parse_type_size;
413 v->visitor.type_bool = parse_type_bool;
414 v->visitor.type_str = parse_type_str;
415 v->visitor.type_number = parse_type_number;
416 v->visitor.type_null = parse_type_null;
417 v->visitor.start_list = start_list;
418 v->visitor.next_list = next_list;
419 v->visitor.check_list = check_list;
420 v->visitor.end_list = end_list;
421 v->visitor.free = string_input_free;
422
423 v->string = str;
424 v->lm = LM_NONE;
425 return &v->visitor;
426 }