]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
fabrics: Introduce simple URI parser
authorTomas Bzatek <tbzatek@redhat.com>
Wed, 17 Apr 2024 16:04:34 +0000 (18:04 +0200)
committerDaniel Wagner <wagi@monom.org>
Wed, 15 May 2024 17:55:21 +0000 (19:55 +0200)
A very simple URI parser implementing URI syntax described
in the Boot Specification, rev. 1.0.

Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
src/libnvme.map
src/nvme/fabrics.c
src/nvme/fabrics.h

index b610a8ffeddc81ea2f6f63ac9da99088d47d5196..748c4f7a39a00af4f700036e3b95cae783d7f139 100644 (file)
@@ -2,6 +2,8 @@
 LIBNVME_1.10 {
        global:
                nvme_init_default_logging;
+               nvme_parse_uri;
+               nvme_free_uri;
 };
 
 LIBNVME_1.9 {
index 6738e9dc42ec2d5ef58aa1974a3188fa8a42ff06..324a7321d803c8c1cf17c7bd26c763fa2611c658 100644 (file)
@@ -1703,3 +1703,119 @@ int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result)
         */
        return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL, result);
 }
+
+struct nvme_fabrics_uri *nvme_parse_uri(const char *str)
+{
+       struct nvme_fabrics_uri *uri;
+       _cleanup_free_ char *scheme = NULL;
+       _cleanup_free_ char *authority = NULL;
+       _cleanup_free_ char *path = NULL;
+       const char *host;
+       int i;
+
+       /* As defined in Boot Specification rev. 1.0:
+        *
+        * section 1.5.7: NVMe-oF URI Format
+        *  nvme+tcp://192.168.1.1:4420/
+        *  nvme+tcp://[FE80::1010]:4420/
+        *
+        * section 3.1.2.5.3: DHCP Root-Path - a hierarchical NVMe-oF URI Format
+        *  NVME<+PROTOCOL>://<SERVERNAME/IP>[:TRANSPORT PORT]/<SUBSYS NQN>/<NID>
+        * or
+        *  NVME<+PROTOCOL>://<DISCOVERY CONTROLLER ADDRESS>[:DISCOVERY-
+        *  -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/<NID>
+        */
+
+       /* TODO: unescape? */
+
+       uri = calloc(1, sizeof(struct nvme_fabrics_uri));
+       if (!uri)
+               return NULL;
+
+       if (sscanf(str, "%m[^:/]://%m[^/?#]%ms",
+                  &scheme, &authority, &path) < 2) {
+               nvme_free_uri(uri);
+               errno = EINVAL;
+               return NULL;
+       }
+
+       if (sscanf(scheme, "%m[^+]+%ms",
+                  &uri->scheme, &uri->protocol) < 1) {
+               nvme_free_uri(uri);
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /* split userinfo */
+       host = strrchr(authority, '@');
+       if (host) {
+               host++;
+               uri->userinfo = strndup(authority, host - authority);
+       } else
+               host = authority;
+
+       /* try matching IPv6 address first */
+       if (sscanf(host, "[%m[^]]]:%d",
+                  &uri->host, &uri->port) < 1)
+               /* treat it as IPv4/hostname */
+               if (sscanf(host, "%m[^:]:%d",
+                          &uri->host, &uri->port) < 1) {
+                       nvme_free_uri(uri);
+                       errno = EINVAL;
+                       return NULL;
+               }
+
+       /* split path into elements */
+       if (path) {
+               char *e, *elem;
+
+               /* separate the fragment */
+               e = strrchr(path, '#');
+               if (e) {
+                       uri->fragment = strdup(e + 1);
+                       *e = '\0';
+               }
+               /* separate the query string */
+               e = strrchr(path, '?');
+               if (e) {
+                       uri->query = strdup(e + 1);
+                       *e = '\0';
+               }
+
+               /* count elements first */
+               for (i = 0, e = path; *e; e++)
+                       if (*e == '/' && *(e + 1) != '/')
+                               i++;
+               uri->path_segments = calloc(i + 2, sizeof(char *));
+
+               i = 0;
+               elem = strtok_r(path, "/", &e);
+               if (elem)
+                       uri->path_segments[i++] = strdup(elem);
+               while (elem && strlen(elem)) {
+                       elem = strtok_r(NULL, "/", &e);
+                       if (elem)
+                               uri->path_segments[i++] = strdup(elem);
+               }
+       }
+
+       return uri;
+}
+
+void nvme_free_uri(struct nvme_fabrics_uri *uri)
+{
+       char **s;
+
+       if (!uri)
+               return;
+       free(uri->scheme);
+       free(uri->protocol);
+       free(uri->userinfo);
+       free(uri->host);
+       for (s = uri->path_segments; s && *s; s++)
+               free(*s);
+       free(uri->path_segments);
+       free(uri->query);
+       free(uri->fragment);
+       free(uri);
+}
index 4ebeb35eae4835e431ec54f9e8feca1862e1b841..3be35310bc5b913d1af5d4f53a2e2c3a848d8a9f 100644 (file)
@@ -67,6 +67,28 @@ struct nvme_fabrics_config {
        bool concat;
 };
 
+/**
+ * struct nvme_fabrics_uri - Parsed URI structure
+ * @scheme:            Scheme name (typically 'nvme')
+ * @protocol:          Optional protocol/transport (e.g. 'tcp')
+ * @userinfo:          Optional user information component of the URI authority
+ * @host:              Host transport address
+ * @port:              The port subcomponent or 0 if not specified
+ * @path_segments:     NULL-terminated array of path segments
+ * @query:             Optional query string component (separated by '?')
+ * @fragment:          Optional fragment identifier component (separated by '#')
+ */
+struct nvme_fabrics_uri {
+       char *scheme;
+       char *protocol;
+       char *userinfo;
+       char *host;
+       int port;
+       char **path_segments;
+       char *query;
+       char *fragment;
+};
+
 /**
  * nvmf_trtype_str() - Decode TRTYPE field
  * @trtype: value to be decoded
@@ -324,4 +346,26 @@ bool nvmf_is_registration_supported(nvme_ctrl_t c);
  */
 int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result);
 
+/**
+ * nvme_parse_uri() - Parse the URI string
+ * @str:       URI string
+ *
+ * Parse the URI string as defined in the NVM Express Boot Specification.
+ * Supported URI elements looks as follows:
+ *
+ *   nvme+tcp://user@host:port/subsys_nqn/nid?query=val#fragment
+ *
+ * Return: &nvme_fabrics_uri structure on success; NULL on failure with errno
+ * set.
+ */
+struct nvme_fabrics_uri *nvme_parse_uri(const char *str);
+
+/**
+ * nvme_free_uri() - Free the URI structure
+ * @uri:       &nvme_fabrics_uri structure
+ *
+ * Free an &nvme_fabrics_uri structure.
+ */
+void nvme_free_uri(struct nvme_fabrics_uri *uri);
+
 #endif /* _LIBNVME_FABRICS_H */