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