1 /* -*- fundamental -*- */
2 /*
3 * libxlu_disk_l.l - parser for disk specification strings
4 *
5 * Copyright (C) 2011 Citrix Ltd.
6 * Author Ian Jackson <ian.jackson@eu.citrix.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published
10 * by the Free Software Foundation; version 2.1 only. with the special
11 * exception on linking described in file LICENSE.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 */
18
19 /*
20 * Parsing the old xm/xend/xl-4.1 disk specs is a tricky problem,
21 * because the target string might in theory contain "," which is the
22 * delimiter we use for stripping off things on the RHS, and ":",
23 * which is the delimiter we use for stripping off things on the LHS.
24 *
25 * In this parser we do not support such target strings in the old
26 * syntax; if the target string has to contain "," or ":" the new
27 * syntax's "target=" should be used.
28 */
29
30 %top{
31 #define _GNU_SOURCE
32 }
33
34 %{
35 #include "libxlu_disk_i.h"
36
37 #define YY_NO_INPUT
38
39 /* The code generated by flex is missing braces in single line expressions and
40 * is not properly indented, which triggers the clang misleading-indentation
41 * check that has been made part of -Wall since clang 10. In order to safely
42 * disable it on clang versions that don't have the diagnostic implemented
43 * also disable the unknown option and pragma warning. */
44 #ifdef __clang__
45 # pragma clang diagnostic ignored "-Wunknown-pragmas"
46 # pragma clang diagnostic ignored "-Wunknown-warning-option"
47 # pragma clang diagnostic ignored "-Wmisleading-indentation"
48 #endif
49
50 /* Some versions of flex have a bug (Fedora bugzilla 612465) which causes
51 * it to fail to declare these functions, which it defines. So declare
52 * them ourselves. Hopefully we won't have to simultaneously support
53 * a flex version which declares these differently somehow. */
54 int xlu__disk_yyget_column(yyscan_t yyscanner);
55 void xlu__disk_yyset_column(int column_no, yyscan_t yyscanner);
56
57
58 /*----- useful macros and functions used in actions -----
59 * we use macros in the actual rules to keep the actions short
60 * and particularly to avoid repeating boilerplate values such as
61 * DPC->disk, yytext, etc. */
62
63 /* Sets an enum, checking it hasn't already been set to a different value */
64 #define DSET(dpc,member,enumname,str,valname) do{ \
65 if (dpc->disk->member != LIBXL_DISK_##enumname##_UNKNOWN && \
66 dpc->disk->member != LIBXL_DISK_##enumname##_##valname) { \
67 xlu__disk_err(dpc, str, STR(member) " respecified"); \
68 } else { \
69 dpc->disk->member = LIBXL_DISK_##enumname##_##valname; \
70 } \
71 }while(0)
72
73 /* For actions whose patterns contain '=', finds the start of the value */
74 #define FROMEQUALS (strchr(yytext,'=')+1)
75
76 /* Chops the delimiter off, modifying yytext and yyleng. */
77 #define STRIP(delim) do{ \
78 if (yyleng>0 && yytext[yyleng-1]==(delim)) \
79 yytext[--yyleng] = 0; \
80 }while(0)
81
82 /* Sets a string value, checking it hasn't been set already. */
83 #define SAVESTRING(what,loc,val) do{ \
84 savestring(DPC, what " respecified", &DPC->disk->loc, (val)); \
85 }while(0)
savestring(DiskParseContext * dpc,const char * what_respecified,char ** update,const char * value)86 static void savestring(DiskParseContext *dpc, const char *what_respecified,
87 char **update, const char *value) {
88 if (*update) {
89 if (**update) { xlu__disk_err(dpc,value,what_respecified); return; }
90 free(*update); /* do not complain about overwriting empty strings */
91 }
92 *update = strdup(value);
93 }
94
95 #define DPC dpc /* our convention in lexer helper functions */
96
97 /* Sets ->readwrite from the string. This ought to be an enum, perhaps. */
setaccess(DiskParseContext * dpc,const char * str)98 static void setaccess(DiskParseContext *dpc, const char *str) {
99 if (!strcmp(str, "r") || !strcmp(str, "ro")) {
100 dpc->disk->readwrite = 0;
101 } else if (!strcmp(str, "rw") || !strcmp(str, "w") || !strcmp(str,"")) {
102 dpc->disk->readwrite = 1;
103 } else {
104 xlu__disk_err(dpc,str,"unknown value for access");
105 }
106 }
107
108 /* Sets ->format from the string. IDL should provide something for this. */
setformat(DiskParseContext * dpc,const char * str)109 static void setformat(DiskParseContext *dpc, const char *str) {
110 if (!strcmp(str,"")) DSET(dpc,format,FORMAT,str,RAW);
111 else if (!strcmp(str,"raw")) DSET(dpc,format,FORMAT,str,RAW);
112 else if (!strcmp(str,"qcow")) DSET(dpc,format,FORMAT,str,QCOW);
113 else if (!strcmp(str,"qcow2")) DSET(dpc,format,FORMAT,str,QCOW2);
114 else if (!strcmp(str,"vhd")) DSET(dpc,format,FORMAT,str,VHD);
115 else if (!strcmp(str,"empty")) DSET(dpc,format,FORMAT,str,EMPTY);
116 else if (!strcmp(str,"qed")) DSET(dpc,format,FORMAT,str,QED);
117 else xlu__disk_err(dpc,str,"unknown value for format");
118 }
119
120 /* Sets ->backend from the string. IDL should provide something for this. */
setbackendtype(DiskParseContext * dpc,const char * str)121 static void setbackendtype(DiskParseContext *dpc, const char *str) {
122 if ( !strcmp(str,"phy")) DSET(dpc,backend,BACKEND,str,PHY);
123 else if (!strcmp(str,"tap")) DSET(dpc,backend,BACKEND,str,TAP);
124 else if (!strcmp(str,"qdisk")) DSET(dpc,backend,BACKEND,str,QDISK);
125 else if (!strcmp(str,"standalone")) DSET(dpc,backend,BACKEND,str,STANDALONE);
126 else xlu__disk_err(dpc,str,"unknown value for backendtype");
127 }
128
129 /* Sets ->specification from the string. IDL should provide something for this. */
setspecification(DiskParseContext * dpc,const char * str)130 static void setspecification(DiskParseContext *dpc, const char *str) {
131 if (!strcmp(str,"xen")) DSET(dpc,specification,SPECIFICATION,str,XEN);
132 else if (!strcmp(str,"virtio")) DSET(dpc,specification,SPECIFICATION,str,VIRTIO);
133 else xlu__disk_err(dpc,str,"unknown value for specification");
134 }
135
136 /* Sets ->colo-port from the string. COLO need this. */
setcoloport(DiskParseContext * dpc,const char * str)137 static void setcoloport(DiskParseContext *dpc, const char *str) {
138 int port = atoi(str);
139 if (port) {
140 dpc->disk->colo_port = port;
141 } else {
142 xlu__disk_err(dpc,str,"unknown value for colo_port");
143 }
144 }
145
146 #define DEPRECATE(usewhatinstead) /* not currently reported */
147
148 /* Handles a vdev positional parameter which includes a devtype. */
vdev_and_devtype(DiskParseContext * dpc,char * str)149 static int vdev_and_devtype(DiskParseContext *dpc, char *str) {
150 /* returns 1 if it was <vdev>:<devtype>, 0 (doing nothing) otherwise */
151 char *colon = strrchr(str, ':');
152 if (!colon)
153 return 0;
154
155 DEPRECATE("use `devtype=...'");
156 *colon++ = 0;
157 SAVESTRING("vdev", vdev, str);
158
159 if (!strcmp(colon,"cdrom")) {
160 DPC->disk->is_cdrom = 1;
161 } else if (!strcmp(colon,"disk")) {
162 DPC->disk->is_cdrom = 0;
163 } else {
164 xlu__disk_err(DPC,colon,"unknown deprecated type");
165 }
166 return 1;
167 }
168
169 #undef DPC /* needs to be defined differently the actual lexer */
170 #define DPC ((DiskParseContext*)yyextra)
171
172 %}
173
174 %option warn
175 %option nodefault
176 %option batch
177 %option 8bit
178 %option noyywrap
179 %option reentrant
180 %option prefix="xlu__disk_yy"
181 %option nounput
182
183 %x LEXERR
184
185 %%
186
187 /*----- the scanner rules which do the parsing -----*/
188
189 [ \t\n]+/([^ \t\n].*)? { /* ignore whitespace before parameters */ }
190
191 /* ordinary parameters setting enums or strings */
192
193 format=[^,]*,? { STRIP(','); setformat(DPC, FROMEQUALS); }
194
195 cdrom,? { DPC->disk->is_cdrom = 1; }
196 devtype=cdrom,? { DPC->disk->is_cdrom = 1; }
197 devtype=disk,? { DPC->disk->is_cdrom = 0; }
198 devtype=[^,]*,? { xlu__disk_err(DPC,yytext,"unknown value for type"); }
199
200 access=[^,]*,? { STRIP(','); setaccess(DPC, FROMEQUALS); }
201 backend=[^,]*,? { STRIP(','); SAVESTRING("backend", backend_domname, FROMEQUALS); }
202 backendtype=[^,]*,? { STRIP(','); setbackendtype(DPC,FROMEQUALS); }
203 specification=[^,]*,? { STRIP(','); setspecification(DPC,FROMEQUALS); }
204
205 vdev=[^,]*,? { STRIP(','); SAVESTRING("vdev", vdev, FROMEQUALS); }
206 script=[^,]*,? { STRIP(','); SAVESTRING("script", script, FROMEQUALS); }
207 direct-io-safe,? { DPC->disk->direct_io_safe = 1; }
208 discard,? { libxl_defbool_set(&DPC->disk->discard_enable, true); }
209 no-discard,? { libxl_defbool_set(&DPC->disk->discard_enable, false); }
210 /* Note that the COLO configuration settings should be considered unstable.
211 * They may change incompatibly in future versions of Xen. */
212 colo,? { libxl_defbool_set(&DPC->disk->colo_enable, true); }
213 no-colo,? { libxl_defbool_set(&DPC->disk->colo_enable, false); }
214 colo-host=[^,]*,? { STRIP(','); SAVESTRING("colo-host", colo_host, FROMEQUALS); }
215 colo-port=[^,]*,? { STRIP(','); setcoloport(DPC, FROMEQUALS); }
216 colo-export=[^,]*,? { STRIP(','); SAVESTRING("colo-export", colo_export, FROMEQUALS); }
217 active-disk=[^,]*,? { STRIP(','); SAVESTRING("active-disk", active_disk, FROMEQUALS); }
218 hidden-disk=[^,]*,? { STRIP(','); SAVESTRING("hidden-disk", hidden_disk, FROMEQUALS); }
219
220 trusted,? { libxl_defbool_set(&DPC->disk->trusted, true); }
221 untrusted,? { libxl_defbool_set(&DPC->disk->trusted, false); }
222
223 grant_usage=1,? { libxl_defbool_set(&DPC->disk->grant_usage, true); }
224 grant_usage=0,? { libxl_defbool_set(&DPC->disk->grant_usage, false); }
225
226 /* the target magic parameter, eats the rest of the string */
227
228 target=.* { STRIP(','); SAVESTRING("target", pdev_path, FROMEQUALS); }
229
230 /* unknown parameters */
231
232 [a-z][-a-z0-9]*=[^,],? { xlu__disk_err(DPC,yytext,"unknown parameter"); }
233
234 /* deprecated prefixes */
235
236 /* the "/.*" in these patterns ensures that they count as if they
237 * matched the whole string, so these patterns take precedence */
238
239 (raw|qcow2?|vhd):/.* {
240 STRIP(':');
241 DPC->had_depr_prefix=1; DEPRECATE("use `[format=]...,'");
242 setformat(DPC, yytext);
243 }
244
245 (iscsi|e?nbd|drbd):/.* {
246 char *newscript;
247 STRIP(':');
248 DPC->had_depr_prefix=1; DEPRECATE("use `script=...'");
249 if (asprintf(&newscript, "block-%s", yytext) < 0) {
250 xlu__disk_err(DPC,yytext,"unable to format script");
251 return 0;
252 }
253 savestring(DPC, "script respecified",
254 &DPC->disk->script, newscript);
255 free(newscript);
256 }
257
258 tapdisk:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
259 tap2?:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
260 aio:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
261 ioemu:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
262 file:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
263 phy:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
264
265 [a-z][a-z0-9]*:/([^a-z0-9].*)? {
266 xlu__disk_err(DPC,yytext,"unknown deprecated disk prefix");
267 return 0;
268 }
269
270 /* positional parameters */
271
272 [^=,]*,|[^=,]+,? {
273 STRIP(',');
274
275 if (DPC->err) {
276 /* previous errors may just lead to subsequent ones */
277 } else if (!DPC->disk->pdev_path) {
278 SAVESTRING("target", pdev_path, yytext);
279 } else if (!DPC->had_depr_prefix &&
280 DPC->disk->format == LIBXL_DISK_FORMAT_UNKNOWN) {
281 if (!*DPC->disk->pdev_path && vdev_and_devtype(DPC,yytext)) {
282 DPC->disk->format = LIBXL_DISK_FORMAT_EMPTY;
283 } else {
284 setformat(DPC,yytext);
285 }
286 } else if (!DPC->disk->vdev) {
287 if (!vdev_and_devtype(DPC,yytext))
288 SAVESTRING("vdev", vdev, yytext);
289 } else if (!DPC->access_set) {
290 DPC->access_set = 1;
291 setaccess(DPC,yytext);
292 } else {
293 xlu__disk_err(DPC,yytext,"too many positional parameters");
294 return 0; /* don't print any more errors */
295 }
296 }
297
298 . {
299 BEGIN(LEXERR);
300 yymore();
301 }
302 <LEXERR>.* {
303 xlu__disk_err(DPC,yytext,"bad disk syntax"); return 0;
304 }
305