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