Bug Summary

File:.build-ci/../libnvme/src/nvme/tree-linux.c
Warning:line 577, column 11
Potential leak of memory pointed to by 'blkdev'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tree-linux.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/src/libnvme3.so.1.0.0.p -I libnvme/src -I ../libnvme/src -I ccan -I ../ccan -I . -I .. -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-11-02-081605-642-1 -x c ../libnvme/src/nvme/tree-linux.c
1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * This file is part of libnvme.
4 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
5 *
6 * Authors: Keith Busch <keith.busch@wdc.com>
7 * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
8 */
9#include <errno(*__errno_location ()).h>
10#include <fcntl.h>
11#include <libgen.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <time.h>
18
19#include <sys/stat.h>
20#include <sys/types.h>
21
22#include <ccan/endian/endian.h>
23#include <ccan/list/list.h>
24
25#include <shared/compiler-attributes-util.h>
26
27#include <libnvme.h>
28
29#include "cleanup-linux.h"
30#include "cleanup.h"
31#include "private-fabrics.h"
32#include "private-tree.h"
33#include "private.h"
34#include "util.h"
35
36#define PATH_UUID_IBM"/proc/device-tree/ibm,partition-uuid" "/proc/device-tree/ibm,partition-uuid"
37#define PATH_SYSFS_BLOCK"/sys/block" "/sys/block"
38#define PATH_SYSFS_SLOTS"/sys/bus/pci/slots" "/sys/bus/pci/slots"
39#define PATH_SYSFS_NVME_SUBSYSTEM"/sys/class/nvme-subsystem" "/sys/class/nvme-subsystem"
40#define PATH_SYSFS_NVME"/sys/class/nvme" "/sys/class/nvme"
41#define PATH_DMI_ENTRIES"/sys/firmware/dmi/entries" "/sys/firmware/dmi/entries"
42#define PATH_DMI_PROD_UUID"/sys/class/dmi/id/product_uuid" "/sys/class/dmi/id/product_uuid"
43#define PATH_MACHINE_ID"/etc/machine-id" "/etc/machine-id" /* no SYSCONFDIR */
44
45static const char *const sysfs_paths[LIBNVME_SYSFS_PATH_MAX] = {
46 [LIBNVME_SYSFS_SUBSYS] = PATH_SYSFS_NVME_SUBSYSTEM"/sys/class/nvme-subsystem",
47 [LIBNVME_SYSFS_CTRL] = PATH_SYSFS_NVME"/sys/class/nvme",
48 [LIBNVME_SYSFS_NS] = PATH_SYSFS_BLOCK"/sys/block",
49 [LIBNVME_SYSFS_SLOTS] = PATH_SYSFS_SLOTS"/sys/bus/pci/slots",
50 [LIBNVME_SYSFS_UUID_IBM] = PATH_UUID_IBM"/proc/device-tree/ibm,partition-uuid",
51 [LIBNVME_SYSFS_DMI_ENTRIES] = PATH_DMI_ENTRIES"/sys/firmware/dmi/entries",
52 [LIBNVME_SYSFS_DMI_PROD_UUID] = PATH_DMI_PROD_UUID"/sys/class/dmi/id/product_uuid",
53 [LIBNVME_SYSFS_MACHINE_ID] = PATH_MACHINE_ID"/etc/machine-id",
54};
55
56/*
57 * The path @p, below the test sysfs directory of @ctx if it has one. The
58 * context keeps it: another context may have another test directory.
59 */
60static const char *libnvme_sysfs_path(struct libnvme_global_ctx *ctx,
61 enum libnvme_sysfs_path p)
62{
63 char *str;
64
65 if (!ctx || !ctx->test_sysfs_dir)
66 return sysfs_paths[p];
67 if (ctx->sysfs_paths[p])
68 return ctx->sysfs_paths[p];
69
70 if (asprintf(&str, "%s%s", ctx->test_sysfs_dir, sysfs_paths[p]) < 0)
71 return NULL((void*)0);
72
73 return ctx->sysfs_paths[p] = str;
74}
75
76const char *libnvme_subsys_sysfs_dir(struct libnvme_global_ctx *ctx)
77{
78 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_SUBSYS);
79}
80
81const char *libnvme_ctrl_sysfs_dir(struct libnvme_global_ctx *ctx)
82{
83 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_CTRL);
84}
85
86const char *libnvme_ns_sysfs_dir(struct libnvme_global_ctx *ctx)
87{
88 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_NS);
89}
90
91const char *libnvme_slots_sysfs_dir(struct libnvme_global_ctx *ctx)
92{
93 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_SLOTS);
94}
95
96const char *libnvme_uuid_ibm_filename(struct libnvme_global_ctx *ctx)
97{
98 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_UUID_IBM);
99}
100
101const char *libnvme_dmi_entries_dir(struct libnvme_global_ctx *ctx)
102{
103 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_DMI_ENTRIES);
104}
105
106const char *libnvme_dmi_product_uuid_filename(struct libnvme_global_ctx *ctx)
107{
108 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_DMI_PROD_UUID);
109}
110
111const char *libnvme_machine_id_filename(struct libnvme_global_ctx *ctx)
112{
113 return libnvme_sysfs_path(ctx, LIBNVME_SYSFS_MACHINE_ID);
114}
115
116static int __nvme_set_attr(const char *path, const char *value)
117{
118 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1;
119
120 fd = open(path, O_WRONLY01);
121 if (fd < 0) {
122#if 0
123 libnvme_msg(LIBNVME_LOG_DEBUG, "Failed to open %s: %s\n", path,__libnvme_msg(LIBNVME_LOG_DEBUG, "Failed to open %s: %s\n", (
(void*)0), path, strerror((*__errno_location ())))
124 strerror(errno))__libnvme_msg(LIBNVME_LOG_DEBUG, "Failed to open %s: %s\n", (
(void*)0), path, strerror((*__errno_location ())))
;
125#endif
126 return -errno(*__errno_location ());
127 }
128 return write(fd, value, strlen(value));
129}
130
131int libnvme_set_attr(const char *dir, const char *attr, const char *value)
132{
133 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
134 int ret;
135
136 ret = asprintf(&path, "%s/%s", dir, attr);
137 if (ret < 0)
138 return -ENOMEM12;
139
140 return __nvme_set_attr(path, value);
141}
142
143static char *__nvme_get_attr(const char *path)
144{
145 char value[4096] = { 0 };
146 int ret, fd;
147 int saved_errno;
148
149 fd = open(path, O_RDONLY00);
150 if (fd < 0)
151 return NULL((void*)0);
152
153 ret = read(fd, value, sizeof(value) - 1);
154 saved_errno = errno(*__errno_location ());
155 close(fd);
156 if (ret < 0) {
157 errno(*__errno_location ()) = saved_errno;
158 return NULL((void*)0);
159 }
160 errno(*__errno_location ()) = 0;
161 if (!strlen(value))
162 return NULL((void*)0);
163
164 if (value[strlen(value) - 1] == '\n')
165 value[strlen(value) - 1] = '\0';
166 while (strlen(value) > 0 && value[strlen(value) - 1] == ' ')
167 value[strlen(value) - 1] = '\0';
168
169 return strlen(value) ? strdup(value) : NULL((void*)0);
170}
171
172__shr_public__attribute__((visibility("default"))) char *libnvme_get_attr(const char *dir, const char *attr)
173{
174 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
175 int ret;
176
177 ret = asprintf(&path, "%s/%s", dir, attr);
178 if (ret < 0)
179 return NULL((void*)0);
180
181 return __nvme_get_attr(path);
182}
183
184__shr_public__attribute__((visibility("default"))) char *libnvme_get_subsys_attr(
185 struct libnvme_subsystem *s, const char *attr)
186{
187 return libnvme_get_attr(libnvme_subsystem_get_sysfs_dir(s), attr);
188}
189
190__shr_public__attribute__((visibility("default"))) char *libnvme_get_ctrl_attr(struct libnvme_ctrl *c, const char *attr)
191{
192 return libnvme_get_attr(libnvme_ctrl_get_sysfs_dir(c), attr);
193}
194
195__shr_public__attribute__((visibility("default"))) char *libnvme_get_ns_attr(struct libnvme_ns *n, const char *attr)
196{
197 return libnvme_get_attr(libnvme_ns_get_sysfs_dir(n), attr);
198}
199
200__shr_public__attribute__((visibility("default"))) char *libnvme_get_path_attr(struct libnvme_path *p, const char *attr)
201{
202 return libnvme_get_attr(libnvme_path_get_sysfs_dir(p), attr);
203}
204
205__shr_public__attribute__((visibility("default"))) const char *libnvme_ctrl_get_state(struct libnvme_ctrl *c)
206{
207 char *state = c->state;
208
209 c->state = libnvme_get_ctrl_attr(c, "state");
210 free(state);
211 return c->state;
212}
213
214static int libnvme_ctrl_lookup_subsystem_name(struct libnvme_global_ctx *ctx,
215 const char *ctrl_name, char **name)
216{
217 const char *subsys_dir = libnvme_subsys_sysfs_dir(ctx);
218 __cleanup_dirents__attribute__((cleanup(cleanup_dirents))) struct dirents subsys = {};
219 int i;
220
221 subsys.num = libnvme_scan_subsystems(ctx, &subsys.ents);
222 if (subsys.num < 0)
223 return subsys.num;
224
225 for (i = 0; i < subsys.num; i++) {
226 struct stat st;
227 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
228
229 if (asprintf(&path, "%s/%s/%s", subsys_dir,
230 subsys.ents[i]->d_name, ctrl_name) < 0)
231 return -ENOMEM12;
232 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "lookup subsystem %s\n", path)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup subsystem %s\n"
, path)
;
233 if (stat(path, &st) < 0) {
234 continue;
235 }
236
237 *name = strdup(subsys.ents[i]->d_name);
238 if (!*name)
239 return -ENOMEM12;
240
241 return 0;
242 }
243 return -ENOENT2;
244}
245
246int libnvme_reconfigure_ctrl(struct libnvme_global_ctx *ctx,
247 struct libnvme_ctrl *c, const char *path, const char *name)
248{
249 DIR *d;
250
251 /*
252 * It's necesssary to release any resources first because a ctrl
253 * can be reused.
254 */
255 libnvme_ctrl_release_transport_handle(c);
256 FREE_CTRL_ATTR(c->name)do { free(c->name); (c->name) = ((void*)0); } while (0);
257 FREE_CTRL_ATTR(c->sysfs_dir)do { free(c->sysfs_dir); (c->sysfs_dir) = ((void*)0); }
while (0)
;
258 FREE_CTRL_ATTR(c->state)do { free(c->state); (c->state) = ((void*)0); } while (
0)
;
259 libnvme_ctrl_attrs_reset(c->attrs);
260
261 d = opendir(path);
262 if (!d) {
263 libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open ctrl dir %s, error %d\n"
, path, (*__errno_location ()))
264 "Failed to open ctrl dir %s, error %d\n", path, errno)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open ctrl dir %s, error %d\n"
, path, (*__errno_location ()))
;
265 return -ENODEV19;
266 }
267 closedir(d);
268
269 c->hdl = NULL((void*)0);
270 c->name = shr_xstrdup(name);
271 c->sysfs_dir = shr_xstrdup(path);
272 c->state = libnvme_get_ctrl_attr(c, "state");
273 libnvmf_read_sysfs_fabrics_attrs(ctx, c);
274
275 return 0;
276}
277
278__shr_public__attribute__((visibility("default"))) int libnvme_init_ctrl(
279 struct libnvme_host *h, struct libnvme_ctrl *c, int instance)
280{
281 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsys_name = NULL((void*)0), *name = NULL((void*)0), *path = NULL((void*)0);
282 struct libnvme_subsystem *s;
283 int ret;
284
285 ret = asprintf(&name, "nvme%d", instance);
286 if (ret < 0)
287 return -ENOMEM12;
288
289 ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(h->ctx), name);
290 if (ret < 0)
291 return -ENOMEM12;
292
293 ret = libnvme_reconfigure_ctrl(h->ctx, c, path, name);
294 if (ret < 0)
295 return ret;
296
297 c->address = libnvme_get_attr(path, "address");
298 if (!c->address && strcmp(c->transport, "loop"))
299 return -ENVME_CONNECT_INVAL_TR;
300
301 ret = libnvme_ctrl_lookup_subsystem_name(h->ctx, name, &subsys_name);
302 if (ret) {
303 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem name for %s\n"
, c->name)
304 "Failed to lookup subsystem name for %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem name for %s\n"
, c->name)
305 c->name)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem name for %s\n"
, c->name)
;
306 return ENVME_CONNECT_LOOKUP_SUBSYS_NAME;
307 }
308
309 ret = libnvme_get_subsystem(h->ctx, h, subsys_name, c->subsysnqn, &s);
310 if (ret) {
311 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem %s\n"
, subsys_name)
312 "Failed to lookup subsystem %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem %s\n"
, subsys_name)
313 subsys_name)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem %s\n"
, subsys_name)
;
314 return -ENVME_CONNECT_LOOKUP_SUBSYS;
315 }
316
317 if (s->subsystype && !strcmp(s->subsystype, "discovery"))
318 c->discovery_ctrl = true1;
319
320 c->s = s;
321 list_add_tail(&s->ctrls, &c->entry)list_add_tail_(&s->ctrls, &c->entry, "../libnvme/src/nvme/tree-linux.c"
":" "321")
;
322
323 return ret;
324}
325
326__shr_public__attribute__((visibility("default"))) int libnvme_scan_ctrl(
327 struct libnvme_global_ctx *ctx, const char *name,
328 struct libnvme_ctrl **cp)
329{
330 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsysnqn = NULL((void*)0), *subsysname = NULL((void*)0);
331 __cleanup_free__attribute__((cleanup(shr_freep))) char *hostnqn = NULL((void*)0), *hostid = NULL((void*)0);
332 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
333 struct libnvme_host *h;
334 struct libnvme_subsystem *s;
335 struct libnvme_ctrl *c;
336 int ret;
337
338 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "scan controller %s\n", name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "scan controller %s\n"
, name)
;
339 ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(ctx), name);
340 if (ret < 0)
341 return -ENOMEM12;
342
343 hostnqn = libnvme_get_attr(path, "hostnqn");
344 hostid = libnvme_get_attr(path, "hostid");
345 if (!hostnqn)
346 hostnqn = shr_xstrdup(ctx->hostnqn);
347 if (!hostid)
348 hostid = shr_xstrdup(ctx->hostid);
349 ret = libnvme_get_host(ctx, hostnqn, hostid, &h);
350 if (ret)
351 return ret;
352
353 subsysnqn = libnvme_get_attr(path, "subsysnqn");
354 if (!subsysnqn)
355 return -ENXIO6;
356
357 ret = libnvme_ctrl_lookup_subsystem_name(ctx, name, &subsysname);
358 if (ret) {
359 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
360 "failed to lookup subsystem for controller %s\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
361 name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
;
362 return ret;
363 }
364
365 ret = libnvme_get_subsystem(h->ctx, h, subsysname, subsysnqn, &s);
366 if (ret)
367 return ret;
368
369 ret = libnvme_ctrl_alloc(ctx, s, path, name, &c);
370 if (ret)
371 return ret;
372
373 ret = libnvme_ctrl_scan_paths(ctx, c);
374 if (ret) {
375 libnvme_free_ctrl(c);
376 return ret;
377 }
378
379 ret = libnvme_ctrl_scan_namespaces(ctx, c);
380 if (ret) {
381 libnvme_free_ctrl(c);
382 return ret;
383 }
384
385 *cp = c;
386 return 0;
387}
388
389static int libnvme_strtou32(const char *str, void *res)
390{
391 char *endptr;
392 __u32 v;
393
394 errno(*__errno_location ()) = 0;
395 v = strtol(str, &endptr, 0);
396
397 if (errno(*__errno_location ()) != 0)
398 return -errno(*__errno_location ());
399
400 if (endptr == str) {
401 /* no digits found */
402 return -EINVAL22;
403 }
404
405 *(__u32 *)res = v;
406 return 0;
407}
408
409struct sysfs_attr_table {
410 void *var;
411 int (*parse)(const char *str, void *res);
412 bool_Bool mandatory;
413 const char *name;
414};
415
416#define ARRAY_SIZE(arr)(sizeof(arr) / sizeof((arr)[0])) (sizeof(arr) / sizeof((arr)[0]))
417
418static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size)
419{
420 char *str;
421 int ret, i;
422
423 for (i = 0; i < size; i++) {
424 struct sysfs_attr_table *e = &tbl[i];
425
426 str = libnvme_get_attr(path, e->name);
427 if (!str) {
428 if (!e->mandatory)
429 continue;
430 return -ENOENT2;
431 }
432 ret = e->parse(str, e->var);
433 free(str);
434 if (ret)
435 return ret;
436 }
437
438 return 0;
439}
440
441int libnvme_ns_init(const char *path, struct libnvme_ns *ns)
442{
443 int ret;
444
445 struct sysfs_attr_table base[] = {
446 { &ns->nsid, libnvme_strtou32, true1, "nsid" },
447 };
448
449 ret = parse_attrs(path, base, ARRAY_SIZE(base)(sizeof(base) / sizeof((base)[0])));
450 if (ret)
451 return ret;
452
453 ns->attrs = libnvme_ns_attrs_alloc();
454 if (!ns->attrs)
455 return -ENOMEM12;
456
457 return 0;
458}
459
460static void libnvme_ns_set_generic_name(struct libnvme_ns *n, const char *name)
461{
462 char generic_name[PATH_MAX4096];
463 int instance, head_instance;
464 int ret;
465
466 ret = sscanf(name, "nvme%dn%d", &instance, &head_instance);
467 if (ret != 2)
468 return;
469
470 sprintf(generic_name, "ng%dn%d", instance, head_instance);
471 n->generic_name = strdup(generic_name);
472}
473
474int libnvme_ns_open(struct libnvme_global_ctx *ctx, const char *sys_path,
475 const char *name, struct libnvme_ns **ns)
476{
477 int ret;
478 struct libnvme_ns *n;
479 struct libnvme_ns_head *head;
480 struct stat arg;
481 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
482
483 n = calloc(1, sizeof(*n));
484 if (!n)
485 return -ENOMEM12;
486
487 head = calloc(1, sizeof(*head));
488 if (!head) {
489 free(n);
490 return -ENOMEM12;
491 }
492
493 head->n = n;
494 list_head_init(&head->paths);
495 ret = asprintf(&path, "%s/%s", sys_path, "multipath");
496 if (ret < 0) {
497 ret = -ENOMEM12;
498 goto free_ns_head;
499 }
500
501 /*
502 * The sysfs-dir "multipath" is available only when nvme multipath
503 * is configured and we're running kernel version >= 6.14.
504 */
505 ret = stat(path, &arg);
506 if (ret == 0) {
507 head->sysfs_dir = path;
508 path = NULL((void*)0);
509 } else
510 head->sysfs_dir = NULL((void*)0);
511
512 n->ctx = ctx;
513 n->head = head;
514 n->hdl = NULL((void*)0);
515 n->name = strdup(name);
516
517 libnvme_ns_set_generic_name(n, name);
518
519 ret = libnvme_ns_init(sys_path, n);
520 if (ret)
521 goto free_ns;
522
523 list_node_init(&n->entry);
524
525 libnvme_ns_release_transport_handle(n);
526
527 *ns = n;
528 return 0;
529
530free_ns:
531 free(n->generic_name);
532 free(n->name);
533free_ns_head:
534 free(head);
535 free(n);
536 return ret;
537}
538
539static inline bool_Bool libnvme_ns_is_generic(const char *name)
540{
541 int instance, head_instance;
542
543 if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2)
544 return false0;
545 return true1;
546}
547
548static char *libnvme_ns_generic_to_blkdev(const char *generic)
549{
550 int instance, head_instance;
551 char blkdev[PATH_MAX4096];
552
553 if (!libnvme_ns_is_generic(generic))
2
Taking true branch
554 return strdup(generic);
3
Memory is allocated
555
556 if (sscanf(generic, "ng%dn%d", &instance, &head_instance) != 2)
557 return NULL((void*)0);
558 sprintf(blkdev, "nvme%dn%d", instance, head_instance);
559
560 return strdup(blkdev);
561}
562
563int __libnvme_scan_namespace(struct libnvme_global_ctx *ctx,
564 const char *sysfs_dir, const char *name, struct libnvme_ns **ns)
565{
566 __cleanup_free__attribute__((cleanup(shr_freep))) char *blkdev = NULL((void*)0);
567 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
568 struct libnvme_ns *n = NULL((void*)0);
569 int ret;
570
571 blkdev = libnvme_ns_generic_to_blkdev(name);
1
Calling 'libnvme_ns_generic_to_blkdev'
4
Returned allocated memory
572 if (!blkdev)
5
Assuming 'blkdev' is non-null
6
Taking false branch
573 return -ENOMEM12;
574
575 ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev);
576 if (ret < 0)
7
Assuming 'ret' is < 0
8
Taking true branch
577 return -ENOMEM12;
9
Potential leak of memory pointed to by 'blkdev'
578
579 ret = libnvme_ns_open(ctx, path, blkdev, &n);
580 if (ret)
581 return ret;
582
583 n->sysfs_dir = path;
584 path = NULL((void*)0);
585
586 *ns = n;
587 return 0;
588}
589
590int libnvme_get_ctrl_transport(__shr_unused__attribute__((__unused__)) struct libnvme_global_ctx *ctx,
591 const char *path, const char *name,
592 char **transport, char **traddr, char **addr, char **trsvcid,
593 char **host_traddr, char **host_iface)
594{
595 char *a = NULL((void*)0), *e = NULL((void*)0);
596
597 *transport = libnvme_get_attr(path, "transport");
598 if (!*transport)
599 return -ENXIO6;
600
601 /* Parse 'address' string into components */
602 *addr = libnvme_get_attr(path, "address");
603 if (!*addr) {
604 __cleanup_free__attribute__((cleanup(shr_freep))) char *rpath = NULL((void*)0);
605 char *p = NULL((void*)0), *_a = NULL((void*)0);
606
607 /* loop transport might not have an address */
608 if (!strcmp(*transport, "loop"))
609 goto skip_address;
610
611 /* Older kernels don't support pcie transport addresses */
612 if (strcmp(*transport, "pcie") &&
613 strcmp(*transport, "apple-nvme"))
614 return -ENXIO6;
615 /* Figure out the PCI address from the attribute path */
616 rpath = realpath(path, NULL((void*)0));
617 if (!rpath)
618 return -ENOMEM12;
619 a = strtok_r(rpath, "/", &e);
620 while (a && strlen(a)) {
621 if (_a)
622 p = _a;
623 _a = a;
624 if (!strncmp(a, "nvme", 4))
625 break;
626 a = strtok_r(NULL((void*)0), "/", &e);
627 }
628 if (p)
629 *addr = strdup(p);
630 } else if (!strcmp(*transport, "pcie") ||
631 !strcmp(*transport, "apple-nvme")) {
632 /* The 'address' string is the transport address */
633 *traddr = strdup(*addr);
634 if (!*traddr)
635 return -ENOMEM12;
636 } else {
637 __cleanup_free__attribute__((cleanup(shr_freep))) char *address = strdup(*addr);
638 if (!address)
639 return -ENOMEM12;
640
641 a = strtok_r(address, ",", &e);
642 while (a && strlen(a)) {
643 if (!strncmp(a, "traddr=", 7))
644 *traddr = strdup(a + 7);
645 else if (!strncmp(a, "trsvcid=", 8))
646 *trsvcid = strdup(a + 8);
647 else if (!strncmp(a, "host_traddr=", 12))
648 *host_traddr = strdup(a + 12);
649 else if (!strncmp(a, "host_iface=", 11))
650 *host_iface = strdup(a + 11);
651 a = strtok_r(NULL((void*)0), ",", &e);
652 }
653 }
654skip_address:
655 return 0;
656}
657
658int libnvme_init_subsystem(struct libnvme_subsystem *s, const char *name)
659{
660 char *path;
661
662 if (asprintf(&path, "%s/%s",
663 libnvme_subsys_sysfs_dir(s->h->ctx), name) < 0)
664 return -ENOMEM12;
665
666 s->subsystype = libnvme_get_attr(path, "subsystype");
667 if (!s->subsystype) {
668 if (!strcmp(s->subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"))
669 s->subsystype = strdup("discovery");
670 else
671 s->subsystype = strdup("nvm");
672 }
673 s->name = strdup(name);
674 s->sysfs_dir = (char *)path;
675
676 return 0;
677}