1 /*
2 * storage_source_backingstore.c: helpers for parsing backing store strings
3 *
4 * Copyright (C) 2007-2017 Red Hat, Inc.
5 * Copyright (C) 2007-2008 Daniel P. Berrange
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23
24 #include "internal.h"
25
26 #include "storage_source_backingstore.h"
27
28 #include "viruri.h"
29 #include "virstring.h"
30 #include "virjson.h"
31 #include "virlog.h"
32 #include "viralloc.h"
33
34 #define VIR_FROM_THIS VIR_FROM_STORAGE
35
36 VIR_LOG_INIT("storage_source_backingstore");
37
38
39 int
virStorageSourceParseBackingURI(virStorageSource * src,const char * uristr)40 virStorageSourceParseBackingURI(virStorageSource *src,
41 const char *uristr)
42 {
43 g_autoptr(virURI) uri = NULL;
44 const char *path = NULL;
45 g_auto(GStrv) scheme = NULL;
46
47 if (!(uri = virURIParse(uristr))) {
48 virReportError(VIR_ERR_INTERNAL_ERROR,
49 _("failed to parse backing file location '%s'"),
50 uristr);
51 return -1;
52 }
53
54 src->hosts = g_new0(virStorageNetHostDef, 1);
55 src->nhosts = 1;
56
57 if (!(scheme = g_strsplit(uri->scheme, "+", 2)))
58 return -1;
59
60 if (!scheme[0] ||
61 (src->protocol = virStorageNetProtocolTypeFromString(scheme[0])) < 0) {
62 virReportError(VIR_ERR_INTERNAL_ERROR,
63 _("invalid backing protocol '%s'"),
64 NULLSTR(scheme[0]));
65 return -1;
66 }
67
68 if (scheme[1] &&
69 (src->hosts->transport = virStorageNetHostTransportTypeFromString(scheme[1])) < 0) {
70 virReportError(VIR_ERR_INTERNAL_ERROR,
71 _("invalid protocol transport type '%s'"),
72 scheme[1]);
73 return -1;
74 }
75
76 if (uri->query) {
77 if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
78 src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS) {
79 src->query = g_strdup(uri->query);
80 } else {
81 /* handle socket stored as a query */
82 if (STRPREFIX(uri->query, "socket="))
83 src->hosts->socket = g_strdup(STRSKIP(uri->query, "socket="));
84 }
85 }
86
87 /* uri->path is NULL if the URI does not contain slash after host:
88 * transport://host:port */
89 if (uri->path)
90 path = uri->path;
91 else
92 path = "";
93
94 /* possibly skip the leading slash */
95 if (g_path_is_absolute(path))
96 path++;
97
98 /* NBD allows empty export name (path) */
99 if (src->protocol == VIR_STORAGE_NET_PROTOCOL_NBD &&
100 path[0] == '\0')
101 path = NULL;
102
103 src->path = g_strdup(path);
104
105 if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) {
106 char *tmp;
107
108 if (!src->path) {
109 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
110 _("missing volume name and path for gluster volume"));
111 return -1;
112 }
113
114 if (!(tmp = strchr(src->path, '/')) ||
115 tmp == src->path) {
116 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
117 _("missing volume name or file name in "
118 "gluster source path '%s'"), src->path);
119 return -1;
120 }
121
122 src->volume = src->path;
123
124 src->path = g_strdup(tmp + 1);
125
126 tmp[0] = '\0';
127 }
128
129 src->hosts->port = uri->port;
130
131 src->hosts->name = g_strdup(uri->server);
132
133 /* Libvirt doesn't handle inline authentication. Make the caller aware. */
134 if (uri->user)
135 return 1;
136
137 return 0;
138 }
139
140
141 static int
virStorageSourceRBDAddHost(virStorageSource * src,char * hostport)142 virStorageSourceRBDAddHost(virStorageSource *src,
143 char *hostport)
144 {
145 char *port;
146 size_t skip;
147 g_auto(GStrv) parts = NULL;
148
149 VIR_EXPAND_N(src->hosts, src->nhosts, 1);
150
151 if ((port = strchr(hostport, ']'))) {
152 /* ipv6, strip brackets */
153 hostport += 1;
154 skip = 3;
155 } else {
156 port = strstr(hostport, "\\:");
157 skip = 2;
158 }
159
160 if (port) {
161 *port = '\0';
162 port += skip;
163 if (virStringParsePort(port, &src->hosts[src->nhosts - 1].port) < 0)
164 goto error;
165 }
166
167 parts = g_strsplit(hostport, "\\:", 0);
168 if (!parts)
169 goto error;
170 src->hosts[src->nhosts-1].name = g_strjoinv(":", parts);
171
172 src->hosts[src->nhosts-1].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
173 src->hosts[src->nhosts-1].socket = NULL;
174
175 return 0;
176
177 error:
178 VIR_FREE(src->hosts[src->nhosts-1].name);
179 return -1;
180 }
181
182
183 int
virStorageSourceParseRBDColonString(const char * rbdstr,virStorageSource * src)184 virStorageSourceParseRBDColonString(const char *rbdstr,
185 virStorageSource *src)
186 {
187 char *p, *e, *next;
188 g_autofree char *options = NULL;
189 g_autoptr(virStorageAuthDef) authdef = NULL;
190
191 /* optionally skip the "rbd:" prefix if provided */
192 if (STRPREFIX(rbdstr, "rbd:"))
193 rbdstr += strlen("rbd:");
194
195 src->path = g_strdup(rbdstr);
196
197 p = strchr(src->path, ':');
198 if (p) {
199 options = g_strdup(p + 1);
200 *p = '\0';
201 }
202
203 /* snapshot name */
204 if ((p = strchr(src->path, '@'))) {
205 src->snapshot = g_strdup(p + 1);
206 *p = '\0';
207 }
208
209 /* pool vs. image name */
210 if ((p = strchr(src->path, '/'))) {
211 src->volume = g_steal_pointer(&src->path);
212 src->path = g_strdup(p + 1);
213 *p = '\0';
214 }
215
216 /* options */
217 if (!options)
218 return 0; /* all done */
219
220 p = options;
221 while (*p) {
222 /* find : delimiter or end of string */
223 for (e = p; *e && *e != ':'; ++e) {
224 if (*e == '\\') {
225 e++;
226 if (*e == '\0')
227 break;
228 }
229 }
230 if (*e == '\0') {
231 next = e; /* last kv pair */
232 } else {
233 next = e + 1;
234 *e = '\0';
235 }
236
237 if (STRPREFIX(p, "id=")) {
238 /* formulate authdef for src->auth */
239 if (src->auth) {
240 virReportError(VIR_ERR_INTERNAL_ERROR,
241 _("duplicate 'id' found in '%s'"), src->path);
242 return -1;
243 }
244
245 authdef = g_new0(virStorageAuthDef, 1);
246
247 authdef->username = g_strdup(p + strlen("id="));
248
249 authdef->secrettype = g_strdup(virSecretUsageTypeToString(VIR_SECRET_USAGE_TYPE_CEPH));
250 src->auth = g_steal_pointer(&authdef);
251
252 /* Cannot formulate a secretType (eg, usage or uuid) given
253 * what is provided.
254 */
255 }
256 if (STRPREFIX(p, "mon_host=")) {
257 char *h, *sep;
258
259 h = p + strlen("mon_host=");
260 while (h < e) {
261 for (sep = h; sep < e; ++sep) {
262 if (*sep == '\\' && (sep[1] == ',' ||
263 sep[1] == ';' ||
264 sep[1] == ' ')) {
265 *sep = '\0';
266 sep += 2;
267 break;
268 }
269 }
270
271 if (virStorageSourceRBDAddHost(src, h) < 0)
272 return -1;
273
274 h = sep;
275 }
276 }
277
278 if (STRPREFIX(p, "conf="))
279 src->configFile = g_strdup(p + strlen("conf="));
280
281 p = next;
282 }
283 return 0;
284 }
285
286
287 static int
virStorageSourceParseNBDColonString(const char * nbdstr,virStorageSource * src)288 virStorageSourceParseNBDColonString(const char *nbdstr,
289 virStorageSource *src)
290 {
291 g_autofree char *nbd = g_strdup(nbdstr);
292 char *export_name;
293 char *host_spec;
294 char *unixpath;
295 char *port;
296
297 src->hosts = g_new0(virStorageNetHostDef, 1);
298 src->nhosts = 1;
299
300 /* We extract the parameters in a similar way qemu does it */
301
302 /* format: [] denotes optional sections, uppercase are variable strings
303 * nbd:unix:/PATH/TO/SOCKET[:exportname=EXPORTNAME]
304 * nbd:HOSTNAME:PORT[:exportname=EXPORTNAME]
305 */
306
307 /* first look for ':exportname=' and cut it off */
308 if ((export_name = strstr(nbd, ":exportname="))) {
309 src->path = g_strdup(export_name + strlen(":exportname="));
310 export_name[0] = '\0';
311 }
312
313 /* Verify the prefix and contents. Note that we require a
314 * "host_spec" part to be present. */
315 if (!(host_spec = STRSKIP(nbd, "nbd:")) || host_spec[0] == '\0')
316 goto malformed;
317
318 if ((unixpath = STRSKIP(host_spec, "unix:"))) {
319 src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
320
321 if (unixpath[0] == '\0')
322 goto malformed;
323
324 src->hosts->socket = g_strdup(unixpath);
325 } else {
326 src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
327
328 if (host_spec[0] == ':') {
329 /* no host given */
330 goto malformed;
331 } else if (host_spec[0] == '[') {
332 host_spec++;
333 /* IPv6 addr */
334 if (!(port = strstr(host_spec, "]:")))
335 goto malformed;
336
337 port[0] = '\0';
338 port += 2;
339
340 if (host_spec[0] == '\0')
341 goto malformed;
342 } else {
343 if (!(port = strchr(host_spec, ':')))
344 goto malformed;
345
346 port[0] = '\0';
347 port++;
348 }
349
350 if (virStringParsePort(port, &src->hosts->port) < 0)
351 return -1;
352
353 src->hosts->name = g_strdup(host_spec);
354 }
355
356 return 0;
357
358 malformed:
359 virReportError(VIR_ERR_INTERNAL_ERROR,
360 _("malformed nbd string '%s'"), nbdstr);
361 return -1;
362 }
363
364
365 int
virStorageSourceParseBackingColon(virStorageSource * src,const char * path)366 virStorageSourceParseBackingColon(virStorageSource *src,
367 const char *path)
368 {
369 const char *p;
370 g_autofree char *protocol = NULL;
371
372 if (!(p = strchr(path, ':'))) {
373 virReportError(VIR_ERR_INTERNAL_ERROR,
374 _("invalid backing protocol string '%s'"),
375 path);
376 return -1;
377 }
378
379 protocol = g_strndup(path, p - path);
380
381 if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) < 0) {
382 virReportError(VIR_ERR_INTERNAL_ERROR,
383 _("invalid backing protocol '%s'"),
384 protocol);
385 return -1;
386 }
387
388 switch ((virStorageNetProtocol) src->protocol) {
389 case VIR_STORAGE_NET_PROTOCOL_NBD:
390 if (virStorageSourceParseNBDColonString(path, src) < 0)
391 return -1;
392 break;
393
394 case VIR_STORAGE_NET_PROTOCOL_RBD:
395 if (virStorageSourceParseRBDColonString(path, src) < 0)
396 return -1;
397 break;
398
399 case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
400 case VIR_STORAGE_NET_PROTOCOL_LAST:
401 case VIR_STORAGE_NET_PROTOCOL_NONE:
402 virReportError(VIR_ERR_INTERNAL_ERROR,
403 _("backing store parser is not implemented for protocol %s"),
404 protocol);
405 return -1;
406
407 case VIR_STORAGE_NET_PROTOCOL_HTTP:
408 case VIR_STORAGE_NET_PROTOCOL_HTTPS:
409 case VIR_STORAGE_NET_PROTOCOL_FTP:
410 case VIR_STORAGE_NET_PROTOCOL_FTPS:
411 case VIR_STORAGE_NET_PROTOCOL_TFTP:
412 case VIR_STORAGE_NET_PROTOCOL_ISCSI:
413 case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
414 case VIR_STORAGE_NET_PROTOCOL_SSH:
415 case VIR_STORAGE_NET_PROTOCOL_VXHS:
416 case VIR_STORAGE_NET_PROTOCOL_NFS:
417 virReportError(VIR_ERR_INTERNAL_ERROR,
418 _("malformed backing store path for protocol %s"),
419 protocol);
420 return -1;
421 }
422
423 return 0;
424 }
425
426
427 static int
428 virStorageSourceParseBackingJSONInternal(virStorageSource *src,
429 virJSONValue *json,
430 const char *jsonstr,
431 bool allowformat);
432
433
434 static int
virStorageSourceParseBackingJSONPath(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int type)435 virStorageSourceParseBackingJSONPath(virStorageSource *src,
436 virJSONValue *json,
437 const char *jsonstr G_GNUC_UNUSED,
438 int type)
439 {
440 const char *path;
441
442 if (!(path = virJSONValueObjectGetString(json, "filename"))) {
443 virReportError(VIR_ERR_INVALID_ARG, "%s",
444 _("missing 'filename' field in JSON backing volume "
445 "definition"));
446 return -1;
447 }
448
449 src->path = g_strdup(path);
450
451 src->type = type;
452 return 0;
453 }
454
455
456 static int
virStorageSourceParseBackingJSONUriStr(virStorageSource * src,const char * uri,int protocol)457 virStorageSourceParseBackingJSONUriStr(virStorageSource *src,
458 const char *uri,
459 int protocol)
460 {
461 int rc;
462
463 if ((rc = virStorageSourceParseBackingURI(src, uri)) < 0)
464 return -1;
465
466 if (src->protocol != protocol) {
467 virReportError(VIR_ERR_INVALID_ARG,
468 _("expected protocol '%s' but got '%s' in URI JSON volume "
469 "definition"),
470 virStorageNetProtocolTypeToString(protocol),
471 virStorageNetProtocolTypeToString(src->protocol));
472 return -1;
473 }
474
475 return rc;
476 }
477
478
479 static int
virStorageSourceParseBackingJSONUriCookies(virStorageSource * src,virJSONValue * json,const char * jsonstr)480 virStorageSourceParseBackingJSONUriCookies(virStorageSource *src,
481 virJSONValue *json,
482 const char *jsonstr)
483 {
484 const char *cookiestr;
485 g_auto(GStrv) cookies = NULL;
486 size_t i;
487
488 if (!virJSONValueObjectHasKey(json, "cookie"))
489 return 0;
490
491 if (!(cookiestr = virJSONValueObjectGetString(json, "cookie"))) {
492 virReportError(VIR_ERR_INVALID_ARG,
493 _("wrong format of 'cookie' field in backing store definition '%s'"),
494 jsonstr);
495 return -1;
496 }
497
498 if (!(cookies = g_strsplit(cookiestr, ";", 0)))
499 return -1;
500
501 src->ncookies = g_strv_length(cookies);
502 src->cookies = g_new0(virStorageNetCookieDef *, src->ncookies);
503
504 for (i = 0; i < src->ncookies; i++) {
505 char *cookiename = cookies[i];
506 char *cookievalue;
507
508 virSkipSpaces((const char **) &cookiename);
509
510 if (!(cookievalue = strchr(cookiename, '='))) {
511 virReportError(VIR_ERR_INVALID_ARG,
512 _("malformed http cookie '%s' in backing store definition '%s'"),
513 cookies[i], jsonstr);
514 return -1;
515 }
516
517 *cookievalue = '\0';
518 cookievalue++;
519
520 src->cookies[i] = g_new0(virStorageNetCookieDef, 1);
521 src->cookies[i]->name = g_strdup(cookiename);
522 src->cookies[i]->value = g_strdup(cookievalue);
523 }
524
525 return 0;
526 }
527
528
529 static int
virStorageSourceParseBackingJSONUri(virStorageSource * src,virJSONValue * json,const char * jsonstr,int protocol)530 virStorageSourceParseBackingJSONUri(virStorageSource *src,
531 virJSONValue *json,
532 const char *jsonstr,
533 int protocol)
534 {
535 const char *uri;
536
537 if (!(uri = virJSONValueObjectGetString(json, "url"))) {
538 virReportError(VIR_ERR_INVALID_ARG, "%s",
539 _("missing 'url' in JSON backing volume definition"));
540 return -1;
541 }
542
543 if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
544 protocol == VIR_STORAGE_NET_PROTOCOL_FTPS) {
545 if (virJSONValueObjectHasKey(json, "sslverify")) {
546 const char *tmpstr;
547 bool tmp;
548
549 /* libguestfs still uses undocumented legacy value of 'off' */
550 if ((tmpstr = virJSONValueObjectGetString(json, "sslverify")) &&
551 STREQ(tmpstr, "off")) {
552 src->sslverify = VIR_TRISTATE_BOOL_NO;
553 } else {
554 if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp) < 0) {
555 virReportError(VIR_ERR_INVALID_ARG,
556 _("malformed 'sslverify' field in backing store definition '%s'"),
557 jsonstr);
558 return -1;
559 }
560
561 src->sslverify = virTristateBoolFromBool(tmp);
562 }
563 }
564 }
565
566 if (protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
567 protocol == VIR_STORAGE_NET_PROTOCOL_HTTP) {
568 if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr) < 0)
569 return -1;
570 }
571
572 if (virJSONValueObjectHasKey(json, "readahead") &&
573 virJSONValueObjectGetNumberUlong(json, "readahead", &src->readahead) < 0) {
574 virReportError(VIR_ERR_INVALID_ARG,
575 _("malformed 'readahead' field in backing store definition '%s'"),
576 jsonstr);
577 return -1;
578 }
579
580 if (virJSONValueObjectHasKey(json, "timeout") &&
581 virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout) < 0) {
582 virReportError(VIR_ERR_INVALID_ARG,
583 _("malformed 'timeout' field in backing store definition '%s'"),
584 jsonstr);
585 return -1;
586 }
587
588 return virStorageSourceParseBackingJSONUriStr(src, uri, protocol);
589 }
590
591
592 static int
virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDef * host,virJSONValue * json)593 virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDef *host,
594 virJSONValue *json)
595 {
596 const char *hostname;
597 const char *port;
598
599 if (!json) {
600 virReportError(VIR_ERR_INVALID_ARG, "%s",
601 _("missing remote server specification in JSON "
602 "backing volume definition"));
603 return -1;
604 }
605
606 hostname = virJSONValueObjectGetString(json, "host");
607 port = virJSONValueObjectGetString(json, "port");
608
609 if (!hostname) {
610 virReportError(VIR_ERR_INVALID_ARG, "%s",
611 _("missing hostname for tcp backing server in "
612 "JSON backing volume definition"));
613 return -1;
614 }
615
616 host->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
617 host->name = g_strdup(hostname);
618
619 if (virStringParsePort(port, &host->port) < 0)
620 return -1;
621
622 return 0;
623 }
624
625
626 static int
virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDef * host,virJSONValue * json)627 virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDef *host,
628 virJSONValue *json)
629 {
630 const char *type;
631 const char *socket;
632
633 if (!json) {
634 virReportError(VIR_ERR_INVALID_ARG, "%s",
635 _("missing remote server specification in JSON "
636 "backing volume definition"));
637 return -1;
638 }
639
640 if (!(type = virJSONValueObjectGetString(json, "type"))) {
641 virReportError(VIR_ERR_INVALID_ARG, "%s",
642 _("missing socket address type in "
643 "JSON backing volume definition"));
644 return -1;
645 }
646
647 if (STREQ(type, "tcp") || STREQ(type, "inet")) {
648 return virStorageSourceParseBackingJSONInetSocketAddress(host, json);
649
650 } else if (STREQ(type, "unix")) {
651 host->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
652
653 socket = virJSONValueObjectGetString(json, "path");
654
655 /* check for old spelling for gluster protocol */
656 if (!socket)
657 socket = virJSONValueObjectGetString(json, "socket");
658
659 if (!socket) {
660 virReportError(VIR_ERR_INVALID_ARG, "%s",
661 _("missing socket path for udp backing server in "
662 "JSON backing volume definition"));
663 return -1;
664 }
665
666 host->socket = g_strdup(socket);
667 } else {
668 virReportError(VIR_ERR_INTERNAL_ERROR,
669 _("backing store protocol '%s' is not yet supported"),
670 type);
671 return -1;
672 }
673
674 return 0;
675 }
676
677
678 static int
virStorageSourceParseBackingJSONGluster(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)679 virStorageSourceParseBackingJSONGluster(virStorageSource *src,
680 virJSONValue *json,
681 const char *jsonstr G_GNUC_UNUSED,
682 int opaque G_GNUC_UNUSED)
683 {
684 const char *uri = virJSONValueObjectGetString(json, "filename");
685 const char *volume = virJSONValueObjectGetString(json, "volume");
686 const char *path = virJSONValueObjectGetString(json, "path");
687 virJSONValue *server = virJSONValueObjectGetArray(json, "server");
688 size_t nservers;
689 size_t i;
690
691 /* legacy URI based syntax passed via 'filename' option */
692 if (uri)
693 return virStorageSourceParseBackingJSONUriStr(src, uri,
694 VIR_STORAGE_NET_PROTOCOL_GLUSTER);
695
696 if (!volume || !path || !server) {
697 virReportError(VIR_ERR_INVALID_ARG, "%s",
698 _("missing 'volume', 'path' or 'server' attribute in "
699 "JSON backing definition for gluster volume"));
700 return -1;
701 }
702
703 src->type = VIR_STORAGE_TYPE_NETWORK;
704 src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
705
706 src->volume = g_strdup(volume);
707 src->path = g_strdup(path);
708
709 nservers = virJSONValueArraySize(server);
710 if (nservers == 0) {
711 virReportError(VIR_ERR_INVALID_ARG, "%s",
712 _("at least 1 server is necessary in "
713 "JSON backing definition for gluster volume"));
714
715 return -1;
716 }
717
718 src->hosts = g_new0(virStorageNetHostDef, nservers);
719 src->nhosts = nservers;
720
721 for (i = 0; i < nservers; i++) {
722 if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i,
723 virJSONValueArrayGet(server, i)) < 0)
724 return -1;
725 }
726
727 return 0;
728 }
729
730
731 static int
virStorageSourceParseBackingJSONiSCSI(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)732 virStorageSourceParseBackingJSONiSCSI(virStorageSource *src,
733 virJSONValue *json,
734 const char *jsonstr G_GNUC_UNUSED,
735 int opaque G_GNUC_UNUSED)
736 {
737 const char *transport = virJSONValueObjectGetString(json, "transport");
738 const char *portal = virJSONValueObjectGetString(json, "portal");
739 const char *target = virJSONValueObjectGetString(json, "target");
740 const char *lun = virJSONValueObjectGetStringOrNumber(json, "lun");
741 const char *uri;
742 char *port;
743
744 /* legacy URI based syntax passed via 'filename' option */
745 if ((uri = virJSONValueObjectGetString(json, "filename")))
746 return virStorageSourceParseBackingJSONUriStr(src, uri,
747 VIR_STORAGE_NET_PROTOCOL_ISCSI);
748
749 src->type = VIR_STORAGE_TYPE_NETWORK;
750 src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI;
751
752 if (!lun)
753 lun = "0";
754
755 src->hosts = g_new0(virStorageNetHostDef, 1);
756 src->nhosts = 1;
757
758 if (STRNEQ_NULLABLE(transport, "tcp")) {
759 virReportError(VIR_ERR_INVALID_ARG, "%s",
760 _("only TCP transport is supported for iSCSI volumes"));
761 return -1;
762 }
763
764 src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
765
766 if (!portal) {
767 virReportError(VIR_ERR_INVALID_ARG, "%s",
768 _("missing 'portal' address in iSCSI backing definition"));
769 return -1;
770 }
771
772 if (!target) {
773 virReportError(VIR_ERR_INVALID_ARG, "%s",
774 _("missing 'target' in iSCSI backing definition"));
775 return -1;
776 }
777
778 src->hosts->name = g_strdup(portal);
779
780 if ((port = strrchr(src->hosts->name, ':')) &&
781 !strchr(port, ']')) {
782 if (virStringParsePort(port + 1, &src->hosts->port) < 0)
783 return -1;
784
785 *port = '\0';
786 }
787
788 src->path = g_strdup_printf("%s/%s", target, lun);
789
790 /* Libvirt doesn't handle inline authentication. Make the caller aware. */
791 if (virJSONValueObjectGetString(json, "user") ||
792 virJSONValueObjectGetString(json, "password"))
793 return 1;
794
795 return 0;
796 }
797
798
799 static int
virStorageSourceParseBackingJSONNbd(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)800 virStorageSourceParseBackingJSONNbd(virStorageSource *src,
801 virJSONValue *json,
802 const char *jsonstr G_GNUC_UNUSED,
803 int opaque G_GNUC_UNUSED)
804 {
805 const char *path = virJSONValueObjectGetString(json, "path");
806 const char *host = virJSONValueObjectGetString(json, "host");
807 const char *port = virJSONValueObjectGetString(json, "port");
808 const char *export = virJSONValueObjectGetString(json, "export");
809 virJSONValue *server = virJSONValueObjectGetObject(json, "server");
810
811 if (!path && !host && !server) {
812 virReportError(VIR_ERR_INVALID_ARG, "%s",
813 _("missing host specification of NBD server in JSON "
814 "backing volume definition"));
815 return -1;
816 }
817
818 src->type = VIR_STORAGE_TYPE_NETWORK;
819 src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
820
821 src->path = g_strdup(export);
822
823 src->hosts = g_new0(virStorageNetHostDef, 1);
824 src->nhosts = 1;
825
826 if (server) {
827 if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0)
828 return -1;
829 } else {
830 if (path) {
831 src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
832 src->hosts[0].socket = g_strdup(path);
833 } else {
834 src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
835 src->hosts[0].name = g_strdup(host);
836
837 if (virStringParsePort(port, &src->hosts[0].port) < 0)
838 return -1;
839 }
840 }
841
842 return 0;
843 }
844
845
846 static int
virStorageSourceParseBackingJSONSheepdog(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)847 virStorageSourceParseBackingJSONSheepdog(virStorageSource *src,
848 virJSONValue *json,
849 const char *jsonstr G_GNUC_UNUSED,
850 int opaque G_GNUC_UNUSED)
851 {
852 const char *filename;
853 const char *vdi = virJSONValueObjectGetString(json, "vdi");
854 virJSONValue *server = virJSONValueObjectGetObject(json, "server");
855
856 /* legacy URI based syntax passed via 'filename' option */
857 if ((filename = virJSONValueObjectGetString(json, "filename"))) {
858 if (strstr(filename, "://"))
859 return virStorageSourceParseBackingJSONUriStr(src, filename,
860 VIR_STORAGE_NET_PROTOCOL_SHEEPDOG);
861
862 /* libvirt doesn't implement a parser for the legacy non-URI syntax */
863 virReportError(VIR_ERR_INVALID_ARG, "%s",
864 _("missing sheepdog URI in JSON backing volume definition"));
865 return -1;
866 }
867
868 src->type = VIR_STORAGE_TYPE_NETWORK;
869 src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
870
871 if (!vdi) {
872 virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi name"));
873 return -1;
874 }
875
876 src->path = g_strdup(vdi);
877
878 src->hosts = g_new0(virStorageNetHostDef, 1);
879 src->nhosts = 1;
880
881 if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) < 0)
882 return -1;
883
884 return 0;
885 }
886
887
888 static int
virStorageSourceParseBackingJSONSSH(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)889 virStorageSourceParseBackingJSONSSH(virStorageSource *src,
890 virJSONValue *json,
891 const char *jsonstr G_GNUC_UNUSED,
892 int opaque G_GNUC_UNUSED)
893 {
894 const char *path = virJSONValueObjectGetString(json, "path");
895 const char *host = virJSONValueObjectGetString(json, "host");
896 const char *port = virJSONValueObjectGetString(json, "port");
897 const char *user = virJSONValueObjectGetString(json, "user");
898 const char *host_key_check = virJSONValueObjectGetString(json, "host_key_check");
899 virJSONValue *server = virJSONValueObjectGetObject(json, "server");
900
901 if (!(host || server) || !path) {
902 virReportError(VIR_ERR_INVALID_ARG, "%s",
903 _("missing host/server or path of SSH JSON backing "
904 "volume definition"));
905 return -1;
906 }
907
908 src->type = VIR_STORAGE_TYPE_NETWORK;
909 src->protocol = VIR_STORAGE_NET_PROTOCOL_SSH;
910
911 src->path = g_strdup(path);
912
913 src->hosts = g_new0(virStorageNetHostDef, 1);
914 src->nhosts = 1;
915
916 if (server) {
917 if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
918 server) < 0)
919 return -1;
920 } else {
921 src->hosts[0].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
922 src->hosts[0].name = g_strdup(host);
923
924 if (virStringParsePort(port, &src->hosts[0].port) < 0)
925 return -1;
926 }
927
928 /* these two are parsed just to be passed back as we don't model them yet */
929 src->ssh_user = g_strdup(user);
930 if (STREQ_NULLABLE(host_key_check, "no"))
931 src->ssh_host_key_check_disabled = true;
932
933 return 0;
934 }
935
936
937 static int
virStorageSourceParseBackingJSONRBD(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)938 virStorageSourceParseBackingJSONRBD(virStorageSource *src,
939 virJSONValue *json,
940 const char *jsonstr G_GNUC_UNUSED,
941 int opaque G_GNUC_UNUSED)
942 {
943 const char *filename;
944 const char *pool = virJSONValueObjectGetString(json, "pool");
945 const char *image = virJSONValueObjectGetString(json, "image");
946 const char *conf = virJSONValueObjectGetString(json, "conf");
947 const char *snapshot = virJSONValueObjectGetString(json, "snapshot");
948 virJSONValue *servers = virJSONValueObjectGetArray(json, "server");
949 size_t nservers;
950 size_t i;
951
952 src->type = VIR_STORAGE_TYPE_NETWORK;
953 src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
954
955 /* legacy syntax passed via 'filename' option */
956 if ((filename = virJSONValueObjectGetString(json, "filename")))
957 return virStorageSourceParseRBDColonString(filename, src);
958
959 if (!pool || !image) {
960 virReportError(VIR_ERR_INVALID_ARG, "%s",
961 _("missing pool or image name in ceph backing volume "
962 "JSON specification"));
963 return -1;
964 }
965
966 src->volume = g_strdup(pool);
967 src->path = g_strdup(image);
968 src->snapshot = g_strdup(snapshot);
969 src->configFile = g_strdup(conf);
970
971 if (servers) {
972 nservers = virJSONValueArraySize(servers);
973
974 src->hosts = g_new0(virStorageNetHostDef, nservers);
975 src->nhosts = nservers;
976
977 for (i = 0; i < nservers; i++) {
978 if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
979 virJSONValueArrayGet(servers, i)) < 0)
980 return -1;
981 }
982 }
983
984 return 0;
985 }
986
987 static int
virStorageSourceParseBackingJSONRaw(virStorageSource * src,virJSONValue * json,const char * jsonstr,int opaque G_GNUC_UNUSED)988 virStorageSourceParseBackingJSONRaw(virStorageSource *src,
989 virJSONValue *json,
990 const char *jsonstr,
991 int opaque G_GNUC_UNUSED)
992 {
993 bool has_offset = virJSONValueObjectHasKey(json, "offset");
994 bool has_size = virJSONValueObjectHasKey(json, "size");
995 virJSONValue *file;
996
997 if (has_offset || has_size) {
998 src->sliceStorage = g_new0(virStorageSourceSlice, 1);
999
1000 if (has_offset &&
1001 virJSONValueObjectGetNumberUlong(json, "offset", &src->sliceStorage->offset) < 0) {
1002 virReportError(VIR_ERR_INVALID_ARG, "%s",
1003 _("malformed 'offset' property of 'raw' driver"));
1004 return -1;
1005 }
1006
1007 if (has_size &&
1008 virJSONValueObjectGetNumberUlong(json, "size", &src->sliceStorage->size) < 0) {
1009 virReportError(VIR_ERR_INVALID_ARG, "%s",
1010 _("malformed 'size' property of 'raw' driver"));
1011 return -1;
1012 }
1013 }
1014
1015 /* 'raw' is a format driver so it can have protocol driver children */
1016 if (!(file = virJSONValueObjectGetObject(json, "file"))) {
1017 virReportError(VIR_ERR_INVALID_ARG,
1018 _("JSON backing volume definition '%s' lacks 'file' object"),
1019 jsonstr);
1020 return -1;
1021 }
1022
1023 return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, false);
1024 }
1025
1026
1027 static int
virStorageSourceParseBackingJSONVxHS(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)1028 virStorageSourceParseBackingJSONVxHS(virStorageSource *src,
1029 virJSONValue *json,
1030 const char *jsonstr G_GNUC_UNUSED,
1031 int opaque G_GNUC_UNUSED)
1032 {
1033 const char *vdisk_id = virJSONValueObjectGetString(json, "vdisk-id");
1034 virJSONValue *server = virJSONValueObjectGetObject(json, "server");
1035
1036 if (!vdisk_id || !server) {
1037 virReportError(VIR_ERR_INVALID_ARG, "%s",
1038 _("missing 'vdisk-id' or 'server' attribute in "
1039 "JSON backing definition for VxHS volume"));
1040 return -1;
1041 }
1042
1043 src->type = VIR_STORAGE_TYPE_NETWORK;
1044 src->protocol = VIR_STORAGE_NET_PROTOCOL_VXHS;
1045
1046 src->path = g_strdup(vdisk_id);
1047
1048 src->hosts = g_new0(virStorageNetHostDef, 1);
1049 src->nhosts = 1;
1050
1051 if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
1052 server) < 0)
1053 return -1;
1054
1055 return 0;
1056 }
1057
1058
1059 static int
virStorageSourceParseBackingJSONNFS(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)1060 virStorageSourceParseBackingJSONNFS(virStorageSource *src,
1061 virJSONValue *json,
1062 const char *jsonstr G_GNUC_UNUSED,
1063 int opaque G_GNUC_UNUSED)
1064 {
1065 virJSONValue *server = virJSONValueObjectGetObject(json, "server");
1066 int uidStore = -1;
1067 int gidStore = -1;
1068 int gotUID = virJSONValueObjectGetNumberInt(json, "user", &uidStore);
1069 int gotGID = virJSONValueObjectGetNumberInt(json, "group", &gidStore);
1070
1071 if (!server) {
1072 virReportError(VIR_ERR_INVALID_ARG, "%s",
1073 _("missing 'server' attribute in JSON backing definition for NFS volume"));
1074 return -1;
1075 }
1076
1077 if (gotUID < 0 || gotGID < 0) {
1078 virReportError(VIR_ERR_INVALID_ARG, "%s",
1079 _("missing 'user' or 'group' attribute in JSON backing definition for NFS volume"));
1080 return -1;
1081 }
1082
1083 src->path = g_strdup(virJSONValueObjectGetString(json, "path"));
1084 if (!src->path) {
1085 virReportError(VIR_ERR_INVALID_ARG, "%s",
1086 _("missing 'path' attribute in JSON backing definition for NFS volume"));
1087 return -1;
1088 }
1089
1090 src->nfs_user = g_strdup_printf("+%d", uidStore);
1091 src->nfs_group = g_strdup_printf("+%d", gidStore);
1092
1093 src->type = VIR_STORAGE_TYPE_NETWORK;
1094 src->protocol = VIR_STORAGE_NET_PROTOCOL_NFS;
1095
1096 src->hosts = g_new0(virStorageNetHostDef, 1);
1097 src->nhosts = 1;
1098
1099 if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts,
1100 server) < 0)
1101 return -1;
1102
1103 return 0;
1104 }
1105
1106
1107 static int
virStorageSourceParseBackingJSONNVMe(virStorageSource * src,virJSONValue * json,const char * jsonstr G_GNUC_UNUSED,int opaque G_GNUC_UNUSED)1108 virStorageSourceParseBackingJSONNVMe(virStorageSource *src,
1109 virJSONValue *json,
1110 const char *jsonstr G_GNUC_UNUSED,
1111 int opaque G_GNUC_UNUSED)
1112 {
1113 g_autoptr(virStorageSourceNVMeDef) nvme = g_new0(virStorageSourceNVMeDef, 1);
1114 const char *device = virJSONValueObjectGetString(json, "device");
1115
1116 if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAddr) < 0) {
1117 virReportError(VIR_ERR_INVALID_ARG, "%s",
1118 _("missing or malformed 'device' field of 'nvme' storage"));
1119 return -1;
1120 }
1121
1122 if (virJSONValueObjectGetNumberUlong(json, "namespace", &nvme->namespc) < 0 ||
1123 nvme->namespc == 0) {
1124 virReportError(VIR_ERR_INVALID_ARG, "%s",
1125 _("missing or malformed 'namespace' field of 'nvme' storage"));
1126 return -1;
1127 }
1128
1129 src->type = VIR_STORAGE_TYPE_NVME;
1130 src->nvme = g_steal_pointer(&nvme);
1131
1132 return 0;
1133 }
1134
1135
1136 struct virStorageSourceJSONDriverParser {
1137 const char *drvname;
1138 bool formatdriver;
1139 /**
1140 * The callback gets a pre-allocated storage source @src and the JSON
1141 * object to parse. The callback shall return -1 on error and report error
1142 * 0 on success and 1 in cases when the configuration itself is valid, but
1143 * can't be converted to libvirt's configuration (e.g. inline authentication
1144 * credentials are present).
1145 */
1146 int (*func)(virStorageSource *src, virJSONValue *json, const char *jsonstr, int opaque);
1147 int opaque;
1148 };
1149
1150 static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
1151 {"file", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_FILE},
1152 {"host_device", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_BLOCK},
1153 {"host_cdrom", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_BLOCK},
1154 {"http", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_HTTP},
1155 {"https", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_HTTPS},
1156 {"ftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_FTP},
1157 {"ftps", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_FTPS},
1158 {"tftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PROTOCOL_TFTP},
1159 {"gluster", false, virStorageSourceParseBackingJSONGluster, 0},
1160 {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0},
1161 {"nbd", false, virStorageSourceParseBackingJSONNbd, 0},
1162 {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
1163 {"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
1164 {"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
1165 {"raw", true, virStorageSourceParseBackingJSONRaw, 0},
1166 {"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
1167 {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
1168 {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0},
1169 };
1170
1171
1172
1173 static int
virStorageSourceParseBackingJSONInternal(virStorageSource * src,virJSONValue * json,const char * jsonstr,bool allowformat)1174 virStorageSourceParseBackingJSONInternal(virStorageSource *src,
1175 virJSONValue *json,
1176 const char *jsonstr,
1177 bool allowformat)
1178 {
1179 const char *drvname;
1180 size_t i;
1181
1182 if (!(drvname = virJSONValueObjectGetString(json, "driver"))) {
1183 virReportError(VIR_ERR_INVALID_ARG,
1184 _("JSON backing volume definition '%s' lacks driver name"),
1185 jsonstr);
1186 return -1;
1187 }
1188
1189 for (i = 0; i < G_N_ELEMENTS(jsonParsers); i++) {
1190 if (STRNEQ(drvname, jsonParsers[i].drvname))
1191 continue;
1192
1193 if (jsonParsers[i].formatdriver && !allowformat) {
1194 virReportError(VIR_ERR_INVALID_ARG,
1195 _("JSON backing volume definition '%s' must not have nested format drivers"),
1196 jsonstr);
1197 return -1;
1198 }
1199
1200 return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaque);
1201 }
1202
1203 virReportError(VIR_ERR_INTERNAL_ERROR,
1204 _("missing parser implementation for JSON backing volume "
1205 "driver '%s'"), drvname);
1206 return -1;
1207 }
1208
1209
1210 int
virStorageSourceParseBackingJSON(virStorageSource * src,const char * json)1211 virStorageSourceParseBackingJSON(virStorageSource *src,
1212 const char *json)
1213 {
1214 g_autoptr(virJSONValue) root = NULL;
1215 g_autoptr(virJSONValue) deflattened = NULL;
1216 virJSONValue *file = NULL;
1217
1218 if (!(root = virJSONValueFromString(json)))
1219 return -1;
1220
1221 if (!(deflattened = virJSONValueObjectDeflatten(root)))
1222 return -1;
1223
1224 /* There are 2 possible syntaxes:
1225 * 1) json:{"file":{"driver":...}}
1226 * 2) json:{"driver":...}
1227 * Remove the 'file' wrapper object in case 1.
1228 */
1229 if (!virJSONValueObjectHasKey(deflattened, "driver"))
1230 file = virJSONValueObjectGetObject(deflattened, "file");
1231
1232 if (!file)
1233 file = deflattened;
1234
1235 return virStorageSourceParseBackingJSONInternal(src, file, json, true);
1236 }
1237