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 #include "libxl_osdeps.h" /* must come before any other headers */
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, TOSTRING(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 xlu__disk_err(dpc,str,"unknown value for backendtype");
126 }
127
128 /* Sets ->colo-port from the string. COLO need this. */
setcoloport(DiskParseContext * dpc,const char * str)129 static void setcoloport(DiskParseContext *dpc, const char *str) {
130 int port = atoi(str);
131 if (port) {
132 dpc->disk->colo_port = port;
133 } else {
134 xlu__disk_err(dpc,str,"unknown value for colo_port");
135 }
136 }
137
138 #define DEPRECATE(usewhatinstead) /* not currently reported */
139
140 /* Handles a vdev positional parameter which includes a devtype. */
vdev_and_devtype(DiskParseContext * dpc,char * str)141 static int vdev_and_devtype(DiskParseContext *dpc, char *str) {
142 /* returns 1 if it was <vdev>:<devtype>, 0 (doing nothing) otherwise */
143 char *colon = strrchr(str, ':');
144 if (!colon)
145 return 0;
146
147 DEPRECATE("use `devtype=...'");
148 *colon++ = 0;
149 SAVESTRING("vdev", vdev, str);
150
151 if (!strcmp(colon,"cdrom")) {
152 DPC->disk->is_cdrom = 1;
153 } else if (!strcmp(colon,"disk")) {
154 DPC->disk->is_cdrom = 0;
155 } else {
156 xlu__disk_err(DPC,colon,"unknown deprecated type");
157 }
158 return 1;
159 }
160
161 #undef DPC /* needs to be defined differently the actual lexer */
162 #define DPC ((DiskParseContext*)yyextra)
163
164 %}
165
166 %option warn
167 %option nodefault
168 %option batch
169 %option 8bit
170 %option noyywrap
171 %option reentrant
172 %option prefix="xlu__disk_yy"
173 %option nounput
174
175 %x LEXERR
176
177 %%
178
179 /*----- the scanner rules which do the parsing -----*/
180
181 [ \t\n]+/([^ \t\n].*)? { /* ignore whitespace before parameters */ }
182
183 /* ordinary parameters setting enums or strings */
184
185 format=[^,]*,? { STRIP(','); setformat(DPC, FROMEQUALS); }
186
187 cdrom,? { DPC->disk->is_cdrom = 1; }
188 devtype=cdrom,? { DPC->disk->is_cdrom = 1; }
189 devtype=disk,? { DPC->disk->is_cdrom = 0; }
190 devtype=[^,]*,? { xlu__disk_err(DPC,yytext,"unknown value for type"); }
191
192 access=[^,]*,? { STRIP(','); setaccess(DPC, FROMEQUALS); }
193 backend=[^,]*,? { STRIP(','); SAVESTRING("backend", backend_domname, FROMEQUALS); }
194 backendtype=[^,]*,? { STRIP(','); setbackendtype(DPC,FROMEQUALS); }
195
196 vdev=[^,]*,? { STRIP(','); SAVESTRING("vdev", vdev, FROMEQUALS); }
197 script=[^,]*,? { STRIP(','); SAVESTRING("script", script, FROMEQUALS); }
198 direct-io-safe,? { DPC->disk->direct_io_safe = 1; }
199 discard,? { libxl_defbool_set(&DPC->disk->discard_enable, true); }
200 no-discard,? { libxl_defbool_set(&DPC->disk->discard_enable, false); }
201 /* Note that the COLO configuration settings should be considered unstable.
202 * They may change incompatibly in future versions of Xen. */
203 colo,? { libxl_defbool_set(&DPC->disk->colo_enable, true); }
204 no-colo,? { libxl_defbool_set(&DPC->disk->colo_enable, false); }
205 colo-host=[^,]*,? { STRIP(','); SAVESTRING("colo-host", colo_host, FROMEQUALS); }
206 colo-port=[^,]*,? { STRIP(','); setcoloport(DPC, FROMEQUALS); }
207 colo-export=[^,]*,? { STRIP(','); SAVESTRING("colo-export", colo_export, FROMEQUALS); }
208 active-disk=[^,]*,? { STRIP(','); SAVESTRING("active-disk", active_disk, FROMEQUALS); }
209 hidden-disk=[^,]*,? { STRIP(','); SAVESTRING("hidden-disk", hidden_disk, FROMEQUALS); }
210
211 /* the target magic parameter, eats the rest of the string */
212
213 target=.* { STRIP(','); SAVESTRING("target", pdev_path, FROMEQUALS); }
214
215 /* unknown parameters */
216
217 [a-z][-a-z0-9]*=[^,],? { xlu__disk_err(DPC,yytext,"unknown parameter"); }
218
219 /* deprecated prefixes */
220
221 /* the "/.*" in these patterns ensures that they count as if they
222 * matched the whole string, so these patterns take precedence */
223
224 (raw|qcow2?|vhd):/.* {
225 STRIP(':');
226 DPC->had_depr_prefix=1; DEPRECATE("use `[format=]...,'");
227 setformat(DPC, yytext);
228 }
229
230 (iscsi|e?nbd|drbd):/.* {
231 char *newscript;
232 STRIP(':');
233 DPC->had_depr_prefix=1; DEPRECATE("use `script=...'");
234 if (asprintf(&newscript, "block-%s", yytext) < 0) {
235 xlu__disk_err(DPC,yytext,"unable to format script");
236 return 0;
237 }
238 savestring(DPC, "script respecified",
239 &DPC->disk->script, newscript);
240 free(newscript);
241 }
242
243 tapdisk:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
244 tap2?:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
245 aio:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
246 ioemu:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
247 file:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
248 phy:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); }
249
250 [a-z][a-z0-9]*:/([^a-z0-9].*)? {
251 xlu__disk_err(DPC,yytext,"unknown deprecated disk prefix");
252 return 0;
253 }
254
255 /* positional parameters */
256
257 [^=,]*,|[^=,]+,? {
258 STRIP(',');
259
260 if (DPC->err) {
261 /* previous errors may just lead to subsequent ones */
262 } else if (!DPC->disk->pdev_path) {
263 SAVESTRING("target", pdev_path, yytext);
264 } else if (!DPC->had_depr_prefix &&
265 DPC->disk->format == LIBXL_DISK_FORMAT_UNKNOWN) {
266 if (!*DPC->disk->pdev_path && vdev_and_devtype(DPC,yytext)) {
267 DPC->disk->format = LIBXL_DISK_FORMAT_EMPTY;
268 } else {
269 setformat(DPC,yytext);
270 }
271 } else if (!DPC->disk->vdev) {
272 if (!vdev_and_devtype(DPC,yytext))
273 SAVESTRING("vdev", vdev, yytext);
274 } else if (!DPC->access_set) {
275 DPC->access_set = 1;
276 setaccess(DPC,yytext);
277 } else {
278 xlu__disk_err(DPC,yytext,"too many positional parameters");
279 return 0; /* don't print any more errors */
280 }
281 }
282
283 . {
284 BEGIN(LEXERR);
285 yymore();
286 }
287 <LEXERR>.* {
288 xlu__disk_err(DPC,yytext,"bad disk syntax"); return 0;
289 }
290