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