From 103205acdc91bee80bc2343cfeb2eb8f3fc9abe9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 16 Apr 2026 10:17:39 +0000 Subject: [PATCH 1/2] generate-accessors: add char double-pointer accessor support The next data structure to be moved out of the public API contains string arrays. Extend the generator to create getter/setters for this type. Signed-off-by: Daniel Wagner --- libnvme/tools/generator/generate-accessors.md | 21 ++-- libnvme/tools/generator/generate-accessors.py | 115 +++++++++++++++--- 2 files changed, 111 insertions(+), 25 deletions(-) diff --git a/libnvme/tools/generator/generate-accessors.md b/libnvme/tools/generator/generate-accessors.md index ee25f01c7c..5b918e16be 100644 --- a/libnvme/tools/generator/generate-accessors.md +++ b/libnvme/tools/generator/generate-accessors.md @@ -289,19 +289,20 @@ __public const char *person_get_role(const struct person *p) - `typedef struct` is not supported. - Nested structs (a `struct` member whose type is also a `struct`) are skipped. -- Only `char *` pointer members are supported; other pointer types are skipped. +- Only `char *` and `char **` pointer members are supported; other pointer types are skipped. ------ ## Notes 1. **Dynamic strings** (`char *`) — setters store a `strdup()` copy; passing `NULL` clears the field. -2. **Fixed char arrays** (`char foo[N]`) — setters use `snprintf`, always NUL-terminated. -3. **`const` members** — only a getter is generated, no setter (applies regardless of any annotation). -4. **`//!accessors:readonly`** — same effect as `const`: getter only. -5. **`//!accessors:writeonly`** — setter only; getter is suppressed. -6. **`//!accessors:readwrite`** — both getter and setter; overrides a restrictive struct-level default. -7. **`//!accessors:none`** — member is completely ignored by the generator. -8. **Struct-level mode** — the qualifier on `generate-accessors` sets the default for every member in the struct; per-member annotations override the struct default. -9. **`--prefix`** — prepended to every function name (e.g. `--prefix nvme_` turns `ctrl_set_name` into `nvme_ctrl_set_name`). -10. **Line length** — generated code is automatically wrapped to stay within the 80-column limit required by `checkpatch.pl`. +2. **String arrays** (`char **`) — setters deep-copy NULL-terminated arrays (each element and the container). +3. **Fixed char arrays** (`char foo[N]`) — setters use `snprintf`, always NUL-terminated. +4. **`const` members** — only a getter is generated, no setter (applies regardless of any annotation). +5. **`//!accessors:readonly`** — same effect as `const`: getter only. +6. **`//!accessors:writeonly`** — setter only; getter is suppressed. +7. **`//!accessors:readwrite`** — both getter and setter; overrides a restrictive struct-level default. +8. **`//!accessors:none`** — member is completely ignored by the generator. +9. **Struct-level mode** — the qualifier on `generate-accessors` sets the default for every member in the struct; per-member annotations override the struct default. +10. **`--prefix`** — prepended to every function name (e.g. `--prefix nvme_` turns `ctrl_set_name` into `nvme_ctrl_set_name`). +11. **Line length** — generated code is automatically wrapped to stay within the 80-column limit required by `checkpatch.pl`. diff --git a/libnvme/tools/generator/generate-accessors.py b/libnvme/tools/generator/generate-accessors.py index 3d4470584e..fd23669b7a 100755 --- a/libnvme/tools/generator/generate-accessors.py +++ b/libnvme/tools/generator/generate-accessors.py @@ -203,16 +203,17 @@ class Member: """Represents one member of a parsed C struct.""" __slots__ = ('name', 'type', 'gen_getter', 'gen_setter', - 'is_char_array', 'array_size') + 'is_char_array', 'is_char_ptr_array', 'array_size') def __init__(self, name, type_str, gen_getter, gen_setter, - is_char_array, array_size): + is_char_array, is_char_ptr_array, array_size): self.name = name self.type = type_str # e.g. "const char *", "int", "__u32" self.gen_getter = gen_getter # True → emit getter self.gen_setter = gen_setter # True → emit setter self.is_char_array = is_char_array - self.array_size = array_size # only valid when is_char_array is True + self.is_char_ptr_array = is_char_ptr_array + self.array_size = array_size # only for fixed-size char arrays (char[N]) # --------------------------------------------------------------------------- @@ -274,6 +275,7 @@ def parse_members(struct_name, raw_body, struct_mode, verbose): gen_getter=gen_getter, gen_setter=gen_setter and not is_const_qual, is_char_array=True, + is_char_ptr_array=False, array_size=m.group(3), )) continue @@ -286,11 +288,16 @@ def parse_members(struct_name, raw_body, struct_mode, verbose): ptr_part = m.group(3) name = m.group(4) - is_ptr = '*' in ptr_part - if is_ptr: + ptr_depth = ptr_part.count('*') + if ptr_depth: if type_base != 'char': continue # only char* pointers are supported - type_str = 'const char *' + if ptr_depth == 1: + type_str = 'const char *' + elif ptr_depth == 2: + type_str = 'const char *const *' + else: + continue else: type_str = type_base @@ -300,6 +307,7 @@ def parse_members(struct_name, raw_body, struct_mode, verbose): gen_getter=gen_getter, gen_setter=gen_setter and not is_const_qual, is_char_array=False, + is_char_ptr_array=(ptr_depth == 2), array_size=None, )) @@ -418,6 +426,28 @@ def emit_hdr_setter_str(f, prefix, sname, mname, is_dyn_str): ) +def emit_hdr_setter_str_array(f, prefix, sname, mname): + """Emit a header declaration for a string-array setter.""" + f.write( + f'/**\n' + f' * {_set_name(prefix, sname, mname)}() - Set {mname}.\n' + f' * @p: The &struct {sname} instance to update.\n' + f' * @{mname}: New NULL-terminated string array; deep-copied.\n' + f' */\n' + ) + + single = (f'void {_set_name(prefix, sname, mname)}' + f'(struct {sname} *p, const char *const *{mname});') + if fits_80(single): + f.write(single + '\n\n') + else: + f.write( + f'void {_set_name(prefix, sname, mname)}(\n' + f'\t\tstruct {sname} *p,\n' + f'\t\tconst char *const *{mname});\n\n' + ) + + def emit_hdr_setter_val(f, prefix, sname, mname, mtype): """Emit a header declaration for a value setter.""" f.write( @@ -468,9 +498,12 @@ def generate_hdr(f, prefix, struct_name, members): """Write header declarations for all members of one struct.""" for member in members: is_dyn_str = (not member.is_char_array and + not member.is_char_ptr_array and member.type == 'const char *') if member.gen_setter: - if member.is_char_array or is_dyn_str: + if member.is_char_ptr_array: + emit_hdr_setter_str_array(f, prefix, struct_name, member.name) + elif member.is_char_array or is_dyn_str: emit_hdr_setter_str(f, prefix, struct_name, member.name, is_dyn_str) else: @@ -533,6 +566,49 @@ def emit_src_setter_chararray(f, prefix, sname, mname, array_size): ) +def emit_src_setter_str_array(f, prefix, sname, mname): + """Emit a NULL-terminated string-array setter (deep copy).""" + sig = (f'{PUB}void {_set_name(prefix, sname, mname)}' + f'(struct {sname} *p, const char *const *{mname})') + if fits_80(sig): + f.write(sig + '\n') + else: + f.write( + f'{PUB}void {_set_name(prefix, sname, mname)}(\n' + f'\t\tstruct {sname} *p,\n' + f'\t\tconst char *const *{mname})\n' + ) + + f.write( + '{\n' + '\tchar **new_array = NULL;\n' + '\tsize_t i;\n\n' + f'\tif ({mname}) {{\n' + f'\t\tfor (i = 0; {mname}[i]; i++)\n' + '\t\t\t;\n' + '\n' + '\t\tnew_array = calloc(i + 1, sizeof(char *));\n' + '\t\tif (new_array != NULL) {\n' + f'\t\t\tfor (i = 0; {mname}[i]; i++) {{\n' + f'\t\t\t\tnew_array[i] = strdup({mname}[i]);\n' + '\t\t\t\tif (!new_array[i]) {\n' + '\t\t\t\t\twhile (i > 0)\n' + '\t\t\t\t\t\tfree(new_array[--i]);\n' + '\t\t\t\t\tfree(new_array);\n' + '\t\t\t\t\tnew_array = NULL;\n' + '\t\t\t\t\tbreak;\n' + '\t\t\t\t}\n' + '\t\t\t}\n' + '\t\t}\n' + '\t}\n\n' + f'\tfor (i = 0; p->{mname} && p->{mname}[i]; i++)\n' + f'\t\tfree(p->{mname}[i]);\n' + f'\tfree(p->{mname});\n' + f'\tp->{mname} = new_array;\n' + '}\n\n' + ) + + def emit_src_setter_val(f, prefix, sname, mname, mtype): """Emit a value setter (direct assignment).""" sig = (f'{PUB}void {_set_name(prefix, sname, mname)}' @@ -551,21 +627,25 @@ def emit_src_setter_val(f, prefix, sname, mname, mtype): ) -def emit_src_getter(f, prefix, sname, mname, mtype): - """Emit a getter (return member value).""" +def emit_src_getter(f, prefix, sname, mname, mtype, cast=None): + """Emit a getter (return member value). + + *cast* is an optional C cast expression (e.g. ``'(const char *const *)'``) + inserted before ``p->mname`` in the return statement. Required when the + declared return type differs from the member's raw storage type (e.g. a + ``char **`` field exposed as ``const char *const *``). + """ sep = type_sep(mtype) sig = (f'{PUB}{mtype}{sep}{_get_name(prefix, sname, mname)}' f'(const struct {sname} *p)') + ret = f'\treturn {cast}p->{mname};\n' if cast else f'\treturn p->{mname};\n' if fits_80(sig): - f.write( - sig + '\n' - f'{{\n\treturn p->{mname};\n}}\n\n' - ) + f.write(sig + '\n' f'{{\n{ret}}}\n\n') else: f.write( f'{PUB}{mtype}{sep}{_get_name(prefix, sname, mname)}(\n' f'\t\tconst struct {sname} *p)\n' - f'{{\n\treturn p->{mname};\n}}\n\n' + f'{{\n{ret}}}\n\n' ) @@ -573,10 +653,13 @@ def generate_src(f, prefix, struct_name, members): """Write source implementations for all members of one struct.""" for member in members: is_dyn_str = (not member.is_char_array and + not member.is_char_ptr_array and member.type == 'const char *') if member.gen_setter: if is_dyn_str: emit_src_setter_dynstr(f, prefix, struct_name, member.name) + elif member.is_char_ptr_array: + emit_src_setter_str_array(f, prefix, struct_name, member.name) elif member.is_char_array: emit_src_setter_chararray(f, prefix, struct_name, member.name, member.array_size) @@ -584,7 +667,9 @@ def generate_src(f, prefix, struct_name, members): emit_src_setter_val(f, prefix, struct_name, member.name, member.type) if member.gen_getter: - emit_src_getter(f, prefix, struct_name, member.name, member.type) + cast = '(const char *const *)' if member.is_char_ptr_array else None + emit_src_getter(f, prefix, struct_name, member.name, member.type, + cast=cast) # --------------------------------------------------------------------------- From 193ac7e65c4e4f63fb887c933b185f1f5c3ee9ea Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 16 Apr 2026 12:28:48 +0200 Subject: [PATCH 2/2] fabrics: move URI data struct to private API Make the URI data structure opaque and provide getter/setters to allow modification and extension later. While at it, also rename it to match the naming scheme and update the helper functions accordingly. Signed-off-by: Daniel Wagner --- libnvme/src/accessors-fabrics.ld | 18 +++- libnvme/src/libnvme.ld | 1 - libnvme/src/libnvmf.ld | 3 +- libnvme/src/nvme/accessors-fabrics.c | 124 ++++++++++++++++++++++++++ libnvme/src/nvme/accessors-fabrics.h | 127 +++++++++++++++++++++++++++ libnvme/src/nvme/fabrics.c | 32 +++---- libnvme/src/nvme/fabrics.h | 40 +++------ libnvme/src/nvme/private-fabrics.h | 23 +++++ libnvme/test/uriparser.c | 75 +++++++++------- util/cleanup.h | 4 +- 10 files changed, 365 insertions(+), 82 deletions(-) diff --git a/libnvme/src/accessors-fabrics.ld b/libnvme/src/accessors-fabrics.ld index a5a7155721..59e9c0d6f7 100644 --- a/libnvme/src/accessors-fabrics.ld +++ b/libnvme/src/accessors-fabrics.ld @@ -11,7 +11,23 @@ LIBNVMF_ACCESSORS_3 { global: libnvmf_discovery_args_get_lsp; - libnvmf_discovery_args_set_lsp; libnvmf_discovery_args_get_max_retries; + libnvmf_discovery_args_set_lsp; libnvmf_discovery_args_set_max_retries; + libnvmf_uri_get_fragment; + libnvmf_uri_get_host; + libnvmf_uri_get_path_segments; + libnvmf_uri_get_port; + libnvmf_uri_get_protocol; + libnvmf_uri_get_query; + libnvmf_uri_get_scheme; + libnvmf_uri_get_userinfo; + libnvmf_uri_set_fragment; + libnvmf_uri_set_host; + libnvmf_uri_set_path_segments; + libnvmf_uri_set_port; + libnvmf_uri_set_protocol; + libnvmf_uri_set_query; + libnvmf_uri_set_scheme; + libnvmf_uri_set_userinfo; }; diff --git a/libnvme/src/libnvme.ld b/libnvme/src/libnvme.ld index c197112437..d3ddc77d1e 100644 --- a/libnvme/src/libnvme.ld +++ b/libnvme/src/libnvme.ld @@ -136,7 +136,6 @@ LIBNVME_3 { libnvme_ns_write_uncorrectable; libnvme_ns_write_zeros; libnvme_open; - libnvme_parse_uri; libnvme_path_get_ctrl; libnvme_path_get_ns; libnvme_path_get_queue_depth; diff --git a/libnvme/src/libnvmf.ld b/libnvme/src/libnvmf.ld index f93f21d2b6..f9b54da071 100644 --- a/libnvme/src/libnvmf.ld +++ b/libnvme/src/libnvmf.ld @@ -31,7 +31,6 @@ LIBNVMF_3 { libnvmf_discovery_nbft; libnvmf_eflags_str; libnvmf_exat_ptr_next; - libnvmf_free_uri; libnvmf_get_default_trsvcid; libnvmf_get_discovery_log; libnvmf_is_registration_supported; @@ -44,6 +43,8 @@ LIBNVMF_3 { libnvmf_subtype_str; libnvmf_treq_str; libnvmf_trtype_str; + libnvmf_uri_free; + libnvmf_uri_parse; local: *; }; diff --git a/libnvme/src/nvme/accessors-fabrics.c b/libnvme/src/nvme/accessors-fabrics.c index 23695d76f9..eef1f58863 100644 --- a/libnvme/src/nvme/accessors-fabrics.c +++ b/libnvme/src/nvme/accessors-fabrics.c @@ -54,3 +54,127 @@ __public __u8 libnvmf_discovery_args_get_lsp( return p->lsp; } +/**************************************************************************** + * Accessors for: struct libnvmf_uri + ****************************************************************************/ + +__public void libnvmf_uri_set_scheme(struct libnvmf_uri *p, const char *scheme) +{ + free(p->scheme); + p->scheme = scheme ? strdup(scheme) : NULL; +} + +__public const char *libnvmf_uri_get_scheme(const struct libnvmf_uri *p) +{ + return p->scheme; +} + +__public void libnvmf_uri_set_protocol( + struct libnvmf_uri *p, + const char *protocol) +{ + free(p->protocol); + p->protocol = protocol ? strdup(protocol) : NULL; +} + +__public const char *libnvmf_uri_get_protocol(const struct libnvmf_uri *p) +{ + return p->protocol; +} + +__public void libnvmf_uri_set_userinfo( + struct libnvmf_uri *p, + const char *userinfo) +{ + free(p->userinfo); + p->userinfo = userinfo ? strdup(userinfo) : NULL; +} + +__public const char *libnvmf_uri_get_userinfo(const struct libnvmf_uri *p) +{ + return p->userinfo; +} + +__public void libnvmf_uri_set_host(struct libnvmf_uri *p, const char *host) +{ + free(p->host); + p->host = host ? strdup(host) : NULL; +} + +__public const char *libnvmf_uri_get_host(const struct libnvmf_uri *p) +{ + return p->host; +} + +__public void libnvmf_uri_set_port(struct libnvmf_uri *p, int port) +{ + p->port = port; +} + +__public int libnvmf_uri_get_port(const struct libnvmf_uri *p) +{ + return p->port; +} + +__public void libnvmf_uri_set_path_segments( + struct libnvmf_uri *p, + const char *const *path_segments) +{ + char **new_array = NULL; + size_t i; + + if (path_segments) { + for (i = 0; path_segments[i]; i++) + ; + + new_array = calloc(i + 1, sizeof(char *)); + if (new_array != NULL) { + for (i = 0; path_segments[i]; i++) { + new_array[i] = strdup(path_segments[i]); + if (!new_array[i]) { + while (i > 0) + free(new_array[--i]); + free(new_array); + new_array = NULL; + break; + } + } + } + } + + for (i = 0; p->path_segments && p->path_segments[i]; i++) + free(p->path_segments[i]); + free(p->path_segments); + p->path_segments = new_array; +} + +__public const char *const *libnvmf_uri_get_path_segments( + const struct libnvmf_uri *p) +{ + return (const char *const *)p->path_segments; +} + +__public void libnvmf_uri_set_query(struct libnvmf_uri *p, const char *query) +{ + free(p->query); + p->query = query ? strdup(query) : NULL; +} + +__public const char *libnvmf_uri_get_query(const struct libnvmf_uri *p) +{ + return p->query; +} + +__public void libnvmf_uri_set_fragment( + struct libnvmf_uri *p, + const char *fragment) +{ + free(p->fragment); + p->fragment = fragment ? strdup(fragment) : NULL; +} + +__public const char *libnvmf_uri_get_fragment(const struct libnvmf_uri *p) +{ + return p->fragment; +} + diff --git a/libnvme/src/nvme/accessors-fabrics.h b/libnvme/src/nvme/accessors-fabrics.h index b52458361c..660171d7e3 100644 --- a/libnvme/src/nvme/accessors-fabrics.h +++ b/libnvme/src/nvme/accessors-fabrics.h @@ -29,6 +29,7 @@ /* Forward declarations. These are internal (opaque) structs. */ struct libnvmf_discovery_args; +struct libnvmf_uri; /**************************************************************************** * Accessors for: struct libnvmf_discovery_args @@ -67,4 +68,130 @@ void libnvmf_discovery_args_set_lsp(struct libnvmf_discovery_args *p, __u8 lsp); */ __u8 libnvmf_discovery_args_get_lsp(const struct libnvmf_discovery_args *p); +/**************************************************************************** + * Accessors for: struct libnvmf_uri + ****************************************************************************/ + +/** + * libnvmf_uri_set_scheme() - Set scheme. + * @p: The &struct libnvmf_uri instance to update. + * @scheme: New string; a copy is stored. Pass NULL to clear. + */ +void libnvmf_uri_set_scheme(struct libnvmf_uri *p, const char *scheme); + +/** + * libnvmf_uri_get_scheme() - Get scheme. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the scheme field, or NULL if not set. + */ +const char *libnvmf_uri_get_scheme(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_protocol() - Set protocol. + * @p: The &struct libnvmf_uri instance to update. + * @protocol: New string; a copy is stored. Pass NULL to clear. + */ +void libnvmf_uri_set_protocol(struct libnvmf_uri *p, const char *protocol); + +/** + * libnvmf_uri_get_protocol() - Get protocol. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the protocol field, or NULL if not set. + */ +const char *libnvmf_uri_get_protocol(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_userinfo() - Set userinfo. + * @p: The &struct libnvmf_uri instance to update. + * @userinfo: New string; a copy is stored. Pass NULL to clear. + */ +void libnvmf_uri_set_userinfo(struct libnvmf_uri *p, const char *userinfo); + +/** + * libnvmf_uri_get_userinfo() - Get userinfo. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the userinfo field, or NULL if not set. + */ +const char *libnvmf_uri_get_userinfo(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_host() - Set host. + * @p: The &struct libnvmf_uri instance to update. + * @host: New string; a copy is stored. Pass NULL to clear. + */ +void libnvmf_uri_set_host(struct libnvmf_uri *p, const char *host); + +/** + * libnvmf_uri_get_host() - Get host. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the host field, or NULL if not set. + */ +const char *libnvmf_uri_get_host(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_port() - Set port. + * @p: The &struct libnvmf_uri instance to update. + * @port: Value to assign to the port field. + */ +void libnvmf_uri_set_port(struct libnvmf_uri *p, int port); + +/** + * libnvmf_uri_get_port() - Get port. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the port field. + */ +int libnvmf_uri_get_port(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_path_segments() - Set path_segments. + * @p: The &struct libnvmf_uri instance to update. + * @path_segments: New NULL-terminated string array; deep-copied. + */ +void libnvmf_uri_set_path_segments( + struct libnvmf_uri *p, + const char *const *path_segments); + +/** + * libnvmf_uri_get_path_segments() - Get path_segments. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the path_segments field. + */ +const char *const *libnvmf_uri_get_path_segments(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_query() - Set query. + * @p: The &struct libnvmf_uri instance to update. + * @query: New string; a copy is stored. Pass NULL to clear. + */ +void libnvmf_uri_set_query(struct libnvmf_uri *p, const char *query); + +/** + * libnvmf_uri_get_query() - Get query. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the query field, or NULL if not set. + */ +const char *libnvmf_uri_get_query(const struct libnvmf_uri *p); + +/** + * libnvmf_uri_set_fragment() - Set fragment. + * @p: The &struct libnvmf_uri instance to update. + * @fragment: New string; a copy is stored. Pass NULL to clear. + */ +void libnvmf_uri_set_fragment(struct libnvmf_uri *p, const char *fragment); + +/** + * libnvmf_uri_get_fragment() - Get fragment. + * @p: The &struct libnvmf_uri instance to query. + * + * Return: The value of the fragment field, or NULL if not set. + */ +const char *libnvmf_uri_get_fragment(const struct libnvmf_uri *p); + #endif /* _ACCESSORS_FABRICS_H_ */ diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 06beda1fdc..0820ca9aed 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -42,10 +42,10 @@ const char *nvmf_dev = "/dev/nvme-fabrics"; -static inline void free_uri(struct libnvme_fabrics_uri **uri) +static inline void free_uri(struct libnvmf_uri **uri) { if (*uri) - libnvmf_free_uri(*uri); + libnvmf_uri_free(*uri); } #define __cleanup_uri __cleanup(free_uri) @@ -1858,9 +1858,9 @@ static char *unescape_uri(const char *str, int len) return dst; } -__public int libnvme_parse_uri(const char *str, struct libnvme_fabrics_uri **urip) +__public int libnvmf_uri_parse(const char *str, struct libnvmf_uri **urip) { - struct libnvme_fabrics_uri *uri; + __cleanup_uri struct libnvmf_uri *uri = NULL; __cleanup_free char *scheme = NULL; __cleanup_free char *authority = NULL; __cleanup_free char *path = NULL; @@ -1881,21 +1881,17 @@ __public int libnvme_parse_uri(const char *str, struct libnvme_fabrics_uri **uri * -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/ */ - uri = calloc(1, sizeof(struct libnvme_fabrics_uri)); + uri = calloc(1, sizeof(struct libnvmf_uri)); if (!uri) return -ENOMEM; if (sscanf(str, "%m[^:/]://%m[^/?#]%ms", - &scheme, &authority, &path) < 2) { - libnvmf_free_uri(uri); + &scheme, &authority, &path) < 2) return -EINVAL; - } if (sscanf(scheme, "%m[^+]+%ms", - &uri->scheme, &uri->protocol) < 1) { - libnvmf_free_uri(uri); + &uri->scheme, &uri->protocol) < 1) return -EINVAL; - } /* split userinfo */ host = strrchr(authority, '@'); @@ -1910,10 +1906,8 @@ __public int libnvme_parse_uri(const char *str, struct libnvme_fabrics_uri **uri &uri->host, &uri->port) < 1) { /* treat it as IPv4/hostname */ if (sscanf(host, "%m[^:]:%d", - &h, &uri->port) < 1) { - libnvmf_free_uri(uri); + &h, &uri->port) < 1) return -EINVAL; - } uri->host = unescape_uri(h, 0); } @@ -1952,10 +1946,12 @@ __public int libnvme_parse_uri(const char *str, struct libnvme_fabrics_uri **uri } *urip = uri; + uri = NULL; + return 0; } -__public void libnvmf_free_uri(struct libnvme_fabrics_uri *uri) +__public void libnvmf_uri_free(struct libnvmf_uri *uri) { char **s; @@ -2655,7 +2651,7 @@ __public void libnvmf_nbft_free(struct libnvme_global_ctx *ctx, struct nbft_file static bool validate_uri(struct libnvme_global_ctx *ctx, struct nbft_info_discovery *dd, - struct libnvme_fabrics_uri *uri) + struct libnvmf_uri *uri) { if (!uri) { libnvme_msg(ctx, LIBNVME_LOG_ERR, @@ -2954,7 +2950,7 @@ __public int libnvmf_discovery_nbft(struct libnvme_global_ctx *ctx, /* Discovery Descriptor List */ for (dd = entry->nbft->discovery_list; dd && *dd; dd++) { - __cleanup_uri struct libnvme_fabrics_uri *uri = NULL; + __cleanup_uri struct libnvmf_uri *uri = NULL; __cleanup_free char *trsvcid = NULL; struct libnvmf_context nfctx = *fctx; bool persistent = false; @@ -2979,7 +2975,7 @@ __public int libnvmf_discovery_nbft(struct libnvme_global_ctx *ctx, continue; hfi = (*dd)->hfi; - ret = libnvme_parse_uri((*dd)->uri, &uri); + ret = libnvmf_uri_parse((*dd)->uri, &uri); if (ret) continue; if (!validate_uri(ctx, *dd, uri)) diff --git a/libnvme/src/nvme/fabrics.h b/libnvme/src/nvme/fabrics.h index 95a9749f9b..0c8c911be6 100644 --- a/libnvme/src/nvme/fabrics.h +++ b/libnvme/src/nvme/fabrics.h @@ -31,28 +31,6 @@ */ struct libnvmf_context; -/** - * struct libnvme_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 libnvme_fabrics_uri { - char *scheme; - char *protocol; - char *userinfo; - char *host; - int port; - char **path_segments; - char *query; - char *fragment; -}; - /** * libnvmf_trtype_str() - Decode TRTYPE field * @trtype: value to be decoded @@ -185,6 +163,11 @@ int libnvmf_connect_ctrl(libnvme_ctrl_t c); */ struct libnvmf_discovery_args; +/* + * struct libnvmf_uri - Opaque data struct for URI + */ +struct libnvmf_uri; + /** * libnvmf_discovery_args_create() - Allocate a discovery args object * @argsp: On success, set to the newly allocated object @@ -249,7 +232,7 @@ bool libnvmf_is_registration_supported(libnvme_ctrl_t c); int libnvmf_register_ctrl(libnvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result); /** - * libnvme_parse_uri() - Parse the URI string + * libnvmf_uri_parse() - Parse the URI string * @str: URI string * @uri: URI object to return * @@ -258,18 +241,17 @@ int libnvmf_register_ctrl(libnvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result * * nvme+tcp://user@host:port/subsys_nqn/nid?query=val#fragment * - * Return: &libnvme_fabrics_uri structure on success; NULL on failure with errno - * set. + * Return: 0 on success, or a negative error code on failure. */ -int libnvme_parse_uri(const char *str, struct libnvme_fabrics_uri **uri); +int libnvmf_uri_parse(const char *str, struct libnvmf_uri **uri); /** - * libnvmf_free_uri() - Free the URI structure + * libnvmf_uri_free() - Free the URI structure * @uri: &libnvme_fabrics_uri structure * - * Free an &libnvme_fabrics_uri structure. + * Free an &libnvmf_uri structure. */ -void libnvmf_free_uri(struct libnvme_fabrics_uri *uri); +void libnvmf_uri_free(struct libnvmf_uri *uri); /** * libnvmf_get_default_trsvcid() - Get default transport service ID diff --git a/libnvme/src/nvme/private-fabrics.h b/libnvme/src/nvme/private-fabrics.h index 5279399707..a56ab29287 100644 --- a/libnvme/src/nvme/private-fabrics.h +++ b/libnvme/src/nvme/private-fabrics.h @@ -83,6 +83,29 @@ struct libnvmf_discovery_args { /*!generate-accessors*/ __u8 lsp; }; +/** + * struct libnvmf_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 libnvmf_uri { //!generate-accessors + char *scheme; + char *protocol; + char *userinfo; + char *host; + int port; + char **path_segments; + char *query; + char *fragment; +}; + bool traddr_is_hostname(struct libnvme_global_ctx *ctx, const char *transport, const char *traddr); diff --git a/libnvme/test/uriparser.c b/libnvme/test/uriparser.c index 25077cab99..02695eb612 100644 --- a/libnvme/test/uriparser.c +++ b/libnvme/test/uriparser.c @@ -154,44 +154,59 @@ static void test_uriparser(void) printf("Testing URI parser:\n"); for (int i = 0; i < ARRAY_SIZE(test_data); i++) { const struct test_data *d = &test_data[i]; - struct libnvme_fabrics_uri *parsed_data; - char **s; - int i; + struct libnvmf_uri *parsed_data; + const char * const *segments; + const char *str; + int j; printf(" '%s'...", d->uri); - assert(!libnvme_parse_uri(d->uri, &parsed_data)); + assert(!libnvmf_uri_parse(d->uri, &parsed_data)); - assert(strcmp(d->scheme, parsed_data->scheme) == 0); + assert(strcmp(d->scheme, + libnvmf_uri_get_scheme(parsed_data)) == 0); + + str = libnvmf_uri_get_protocol(parsed_data); if (d->proto) { - assert(parsed_data->protocol != NULL); - assert(strcmp(d->proto, parsed_data->protocol) == 0); - } else - assert(d->proto == parsed_data->protocol); - assert(strcmp(d->host, parsed_data->host) == 0); - assert(d->port == parsed_data->port); - - if (!parsed_data->path_segments) + assert(str != NULL); + assert(strcmp(d->proto, str) == 0); + } else { + assert(d->proto == str); + } + + assert(strcmp(d->host, + libnvmf_uri_get_host(parsed_data)) == 0); + + assert(d->port == libnvmf_uri_get_port(parsed_data)); + + segments = libnvmf_uri_get_path_segments(parsed_data); + if (!segments) { assert(d->path[0] == NULL); - else { - for (i = 0, s = parsed_data->path_segments; - s && *s; s++, i++) { - assert(d->path[i] != NULL); - assert(strcmp(d->path[i], *s) == 0); + } else { + for (j = 0; segments && segments[j]; j++) { + assert(d->path[j] != NULL); + assert(strcmp(d->path[j], segments[j]) == 0); } /* trailing NULL element */ - assert(d->path[i] == parsed_data->path_segments[i]); + assert(d->path[j] == segments[j]); } + + str = libnvmf_uri_get_query(parsed_data); if (d->query) { - assert(parsed_data->query != NULL); - assert(strcmp(d->query, parsed_data->query) == 0); - } else - assert(d->query == parsed_data->query); + assert(str != NULL); + assert(strcmp(d->query, str) == 0); + } else { + assert(d->query == str); + } + + str = libnvmf_uri_get_fragment(parsed_data); if (d->frag) { - assert(parsed_data->fragment != NULL); - assert(strcmp(d->frag, parsed_data->fragment) == 0); - } else - assert(d->frag == parsed_data->fragment); - libnvmf_free_uri(parsed_data); + assert(str != NULL); + assert(strcmp(d->frag, str) == 0); + } else { + assert(d->frag == str); + } + + libnvmf_uri_free(parsed_data); printf(" OK\n"); } } @@ -200,10 +215,10 @@ static void test_uriparser_bad(void) { printf("Testing malformed URI strings:\n"); for (int i = 0; i < ARRAY_SIZE(test_data_bad); i++) { - struct libnvme_fabrics_uri *parsed_data = NULL; + struct libnvmf_uri *parsed_data = NULL; printf(" '%s'...", test_data_bad[i]); - assert(libnvme_parse_uri(test_data_bad[i], &parsed_data)); + assert(libnvmf_uri_parse(test_data_bad[i], &parsed_data)); assert(parsed_data == NULL); printf(" OK\n"); } diff --git a/util/cleanup.h b/util/cleanup.h index 01423eeaed..72acb4fa88 100644 --- a/util/cleanup.h +++ b/util/cleanup.h @@ -46,10 +46,10 @@ static inline DEFINE_CLEANUP_FUNC(cleanup_nvme_ctrl, libnvme_ctrl_t, libnvme_fre #define __cleanup_nvme_ctrl __cleanup(cleanup_nvme_ctrl) #ifdef CONFIG_FABRICS -static inline void free_uri(struct libnvme_fabrics_uri **uri) +static inline void free_uri(struct libnvmf_uri **uri) { if (*uri) - libnvmf_free_uri(*uri); + libnvmf_uri_free(*uri); } #define __cleanup_uri __cleanup(free_uri)