Bug Summary

File:.build-ci/../libnvme/src/nvme/tree-linux.c
Warning:line 609, 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-09-23-073103-589-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 *make_sysfs_dir(struct libnvme_global_ctx *ctx,
46 const char *path)
47{
48 char *str;
49
50 if (!ctx || !ctx->test_sysfs_dir)
51 return path;
52
53 if (asprintf(&str, "%s%s", ctx->test_sysfs_dir, path) < 0)
54 return NULL((void*)0);
55
56 return str;
57}
58
59const char *libnvme_subsys_sysfs_dir(struct libnvme_global_ctx *ctx)
60{
61 static const char *str;
62
63 if (str)
64 return str;
65
66 return str = make_sysfs_dir(ctx, PATH_SYSFS_NVME_SUBSYSTEM"/sys/class/nvme-subsystem");
67}
68
69const char *libnvme_ctrl_sysfs_dir(struct libnvme_global_ctx *ctx)
70{
71 static const char *str;
72
73 if (str)
74 return str;
75
76 return str = make_sysfs_dir(ctx, PATH_SYSFS_NVME"/sys/class/nvme");
77}
78
79const char *libnvme_ns_sysfs_dir(struct libnvme_global_ctx *ctx)
80{
81 static const char *str;
82
83 if (str)
84 return str;
85
86 return str = make_sysfs_dir(ctx, PATH_SYSFS_BLOCK"/sys/block");
87}
88
89const char *libnvme_slots_sysfs_dir(struct libnvme_global_ctx *ctx)
90{
91 static const char *str;
92
93 if (str)
94 return str;
95
96 return str = make_sysfs_dir(ctx, PATH_SYSFS_SLOTS"/sys/bus/pci/slots");
97}
98
99const char *libnvme_uuid_ibm_filename(struct libnvme_global_ctx *ctx)
100{
101 static const char *str;
102
103 if (str)
104 return str;
105
106 return str = make_sysfs_dir(ctx, PATH_UUID_IBM"/proc/device-tree/ibm,partition-uuid");
107}
108
109const char *libnvme_dmi_entries_dir(struct libnvme_global_ctx *ctx)
110{
111 static const char *str;
112
113 if (str)
114 return str;
115
116 return str = make_sysfs_dir(ctx, PATH_DMI_ENTRIES"/sys/firmware/dmi/entries");
117}
118
119const char *libnvme_dmi_product_uuid_filename(struct libnvme_global_ctx *ctx)
120{
121 static const char *str;
122
123 if (str)
124 return str;
125
126 return str = make_sysfs_dir(ctx, PATH_DMI_PROD_UUID"/sys/class/dmi/id/product_uuid");
127}
128
129const char *libnvme_machine_id_filename(struct libnvme_global_ctx *ctx)
130{
131 static const char *str;
132
133 if (str)
134 return str;
135
136 return str = make_sysfs_dir(ctx, PATH_MACHINE_ID"/etc/machine-id");
137}
138
139static int __nvme_set_attr(const char *path, const char *value)
140{
141 __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1;
142
143 fd = open(path, O_WRONLY01);
144 if (fd < 0) {
145#if 0
146 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 ())))
147 strerror(errno))__libnvme_msg(LIBNVME_LOG_DEBUG, "Failed to open %s: %s\n", (
(void*)0), path, strerror((*__errno_location ())))
;
148#endif
149 return -errno(*__errno_location ());
150 }
151 return write(fd, value, strlen(value));
152}
153
154int libnvme_set_attr(const char *dir, const char *attr, const char *value)
155{
156 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
157 int ret;
158
159 ret = asprintf(&path, "%s/%s", dir, attr);
160 if (ret < 0)
161 return -ENOMEM12;
162
163 return __nvme_set_attr(path, value);
164}
165
166static char *__nvme_get_attr(const char *path)
167{
168 char value[4096] = { 0 };
169 int ret, fd;
170 int saved_errno;
171
172 fd = open(path, O_RDONLY00);
173 if (fd < 0)
174 return NULL((void*)0);
175
176 ret = read(fd, value, sizeof(value) - 1);
177 saved_errno = errno(*__errno_location ());
178 close(fd);
179 if (ret < 0) {
180 errno(*__errno_location ()) = saved_errno;
181 return NULL((void*)0);
182 }
183 errno(*__errno_location ()) = 0;
184 if (!strlen(value))
185 return NULL((void*)0);
186
187 if (value[strlen(value) - 1] == '\n')
188 value[strlen(value) - 1] = '\0';
189 while (strlen(value) > 0 && value[strlen(value) - 1] == ' ')
190 value[strlen(value) - 1] = '\0';
191
192 return strlen(value) ? strdup(value) : NULL((void*)0);
193}
194
195__shr_public__attribute__((visibility("default"))) char *libnvme_get_attr(const char *dir, const char *attr)
196{
197 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
198 int ret;
199
200 ret = asprintf(&path, "%s/%s", dir, attr);
201 if (ret < 0)
202 return NULL((void*)0);
203
204 return __nvme_get_attr(path);
205}
206
207__shr_public__attribute__((visibility("default"))) char *libnvme_get_subsys_attr(
208 struct libnvme_subsystem *s, const char *attr)
209{
210 return libnvme_get_attr(libnvme_subsystem_get_sysfs_dir(s), attr);
211}
212
213__shr_public__attribute__((visibility("default"))) char *libnvme_get_ctrl_attr(struct libnvme_ctrl *c, const char *attr)
214{
215 return libnvme_get_attr(libnvme_ctrl_get_sysfs_dir(c), attr);
216}
217
218__shr_public__attribute__((visibility("default"))) char *libnvme_get_ns_attr(struct libnvme_ns *n, const char *attr)
219{
220 return libnvme_get_attr(libnvme_ns_get_sysfs_dir(n), attr);
221}
222
223__shr_public__attribute__((visibility("default"))) char *libnvme_get_path_attr(struct libnvme_path *p, const char *attr)
224{
225 return libnvme_get_attr(libnvme_path_get_sysfs_dir(p), attr);
226}
227
228__shr_public__attribute__((visibility("default"))) const char *libnvme_ctrl_get_state(struct libnvme_ctrl *c)
229{
230 char *state = c->state;
231
232 c->state = libnvme_get_ctrl_attr(c, "state");
233 free(state);
234 return c->state;
235}
236
237static int libnvme_ctrl_lookup_subsystem_name(struct libnvme_global_ctx *ctx,
238 const char *ctrl_name, char **name)
239{
240 const char *subsys_dir = libnvme_subsys_sysfs_dir(ctx);
241 __cleanup_dirents__attribute__((cleanup(cleanup_dirents))) struct dirents subsys = {};
242 int i;
243
244 subsys.num = libnvme_scan_subsystems(ctx, &subsys.ents);
245 if (subsys.num < 0)
246 return subsys.num;
247
248 for (i = 0; i < subsys.num; i++) {
249 struct stat st;
250 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
251
252 if (asprintf(&path, "%s/%s/%s", subsys_dir,
253 subsys.ents[i]->d_name, ctrl_name) < 0)
254 return -ENOMEM12;
255 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "lookup subsystem %s\n", path)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup subsystem %s\n"
, path)
;
256 if (stat(path, &st) < 0) {
257 continue;
258 }
259
260 *name = strdup(subsys.ents[i]->d_name);
261 if (!*name)
262 return -ENOMEM12;
263
264 return 0;
265 }
266 return -ENOENT2;
267}
268
269int libnvme_reconfigure_ctrl(struct libnvme_global_ctx *ctx,
270 struct libnvme_ctrl *c, const char *path, const char *name)
271{
272 DIR *d;
273
274 /*
275 * It's necesssary to release any resources first because a ctrl
276 * can be reused.
277 */
278 libnvme_ctrl_release_transport_handle(c);
279 FREE_CTRL_ATTR(c->name)do { free(c->name); (c->name) = ((void*)0); } while (0);
280 FREE_CTRL_ATTR(c->sysfs_dir)do { free(c->sysfs_dir); (c->sysfs_dir) = ((void*)0); }
while (0)
;
281 FREE_CTRL_ATTR(c->state)do { free(c->state); (c->state) = ((void*)0); } while (
0)
;
282 libnvme_ctrl_attrs_reset(c->attrs);
283
284 d = opendir(path);
285 if (!d) {
286 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 ()))
287 "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 ()))
;
288 return -ENODEV19;
289 }
290 closedir(d);
291
292 c->hdl = NULL((void*)0);
293 c->name = shr_xstrdup(name);
294 c->sysfs_dir = shr_xstrdup(path);
295 c->state = libnvme_get_ctrl_attr(c, "state");
296 libnvmf_read_sysfs_fabrics_attrs(ctx, c);
297
298 return 0;
299}
300
301__shr_public__attribute__((visibility("default"))) int libnvme_init_ctrl(
302 struct libnvme_host *h, struct libnvme_ctrl *c, int instance)
303{
304 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsys_name = NULL((void*)0), *name = NULL((void*)0), *path = NULL((void*)0);
305 struct libnvme_subsystem *s;
306 int ret;
307
308 ret = asprintf(&name, "nvme%d", instance);
309 if (ret < 0)
310 return -ENOMEM12;
311
312 ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(h->ctx), name);
313 if (ret < 0)
314 return -ENOMEM12;
315
316 ret = libnvme_reconfigure_ctrl(h->ctx, c, path, name);
317 if (ret < 0)
318 return ret;
319
320 c->address = libnvme_get_attr(path, "address");
321 if (!c->address && strcmp(c->transport, "loop"))
322 return -ENVME_CONNECT_INVAL_TR;
323
324 ret = libnvme_ctrl_lookup_subsystem_name(h->ctx, name, &subsys_name);
325 if (ret) {
326 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)
327 "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)
328 c->name)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem name for %s\n"
, c->name)
;
329 return ENVME_CONNECT_LOOKUP_SUBSYS_NAME;
330 }
331
332 ret = libnvme_get_subsystem(h->ctx, h, subsys_name, c->subsysnqn, &s);
333 if (ret) {
334 libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem %s\n"
, subsys_name)
335 "Failed to lookup subsystem %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem %s\n"
, subsys_name)
336 subsys_name)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem %s\n"
, subsys_name)
;
337 return -ENVME_CONNECT_LOOKUP_SUBSYS;
338 }
339
340 if (s->subsystype && !strcmp(s->subsystype, "discovery"))
341 c->discovery_ctrl = true1;
342
343 c->s = s;
344 list_add_tail(&s->ctrls, &c->entry)list_add_tail_(&s->ctrls, &c->entry, "../libnvme/src/nvme/tree-linux.c"
":" "344")
;
345
346 return ret;
347}
348
349__shr_public__attribute__((visibility("default"))) int libnvme_scan_ctrl(
350 struct libnvme_global_ctx *ctx, const char *name,
351 struct libnvme_ctrl **cp)
352{
353 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsysnqn = NULL((void*)0), *subsysname = NULL((void*)0);
354 __cleanup_free__attribute__((cleanup(shr_freep))) char *hostnqn = NULL((void*)0), *hostid = NULL((void*)0);
355 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
356 char *host_key;
357 struct libnvme_host *h;
358 struct libnvme_subsystem *s;
359 struct libnvme_ctrl *c;
360 int ret;
361
362 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "scan controller %s\n", name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "scan controller %s\n"
, name)
;
363 ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(ctx), name);
364 if (ret < 0)
365 return -ENOMEM12;
366
367 hostnqn = libnvme_get_attr(path, "hostnqn");
368 hostid = libnvme_get_attr(path, "hostid");
369 if (!hostnqn)
370 hostnqn = shr_xstrdup(ctx->hostnqn);
371 if (!hostid)
372 hostid = shr_xstrdup(ctx->hostid);
373 ret = libnvme_get_host(ctx, hostnqn, hostid, &h);
374 if (ret)
375 return ret;
376
377 host_key = libnvme_get_attr(path, "dhchap_secret");
378 if (host_key && strcmp(host_key, "none")) {
379 free(h->kxchap_host_key);
380 h->kxchap_host_key = host_key;
381 host_key = NULL((void*)0);
382 }
383 free(host_key);
384
385 subsysnqn = libnvme_get_attr(path, "subsysnqn");
386 if (!subsysnqn)
387 return -ENXIO6;
388
389 ret = libnvme_ctrl_lookup_subsystem_name(ctx, name, &subsysname);
390 if (ret) {
391 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
392 "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)
393 name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
;
394 return ret;
395 }
396
397 ret = libnvme_get_subsystem(h->ctx, h, subsysname, subsysnqn, &s);
398 if (ret)
399 return ret;
400
401 ret = libnvme_ctrl_alloc(ctx, s, path, name, &c);
402 if (ret)
403 return ret;
404
405 ret = libnvme_ctrl_scan_paths(ctx, c);
406 if (ret) {
407 libnvme_free_ctrl(c);
408 return ret;
409 }
410
411 ret = libnvme_ctrl_scan_namespaces(ctx, c);
412 if (ret) {
413 libnvme_free_ctrl(c);
414 return ret;
415 }
416
417 *cp = c;
418 return 0;
419}
420
421static int libnvme_strtou32(const char *str, void *res)
422{
423 char *endptr;
424 __u32 v;
425
426 errno(*__errno_location ()) = 0;
427 v = strtol(str, &endptr, 0);
428
429 if (errno(*__errno_location ()) != 0)
430 return -errno(*__errno_location ());
431
432 if (endptr == str) {
433 /* no digits found */
434 return -EINVAL22;
435 }
436
437 *(__u32 *)res = v;
438 return 0;
439}
440
441struct sysfs_attr_table {
442 void *var;
443 int (*parse)(const char *str, void *res);
444 bool_Bool mandatory;
445 const char *name;
446};
447
448#define ARRAY_SIZE(arr)(sizeof(arr) / sizeof((arr)[0])) (sizeof(arr) / sizeof((arr)[0]))
449
450static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size)
451{
452 char *str;
453 int ret, i;
454
455 for (i = 0; i < size; i++) {
456 struct sysfs_attr_table *e = &tbl[i];
457
458 str = libnvme_get_attr(path, e->name);
459 if (!str) {
460 if (!e->mandatory)
461 continue;
462 return -ENOENT2;
463 }
464 ret = e->parse(str, e->var);
465 free(str);
466 if (ret)
467 return ret;
468 }
469
470 return 0;
471}
472
473int libnvme_ns_init(const char *path, struct libnvme_ns *ns)
474{
475 int ret;
476
477 struct sysfs_attr_table base[] = {
478 { &ns->nsid, libnvme_strtou32, true1, "nsid" },
479 };
480
481 ret = parse_attrs(path, base, ARRAY_SIZE(base)(sizeof(base) / sizeof((base)[0])));
482 if (ret)
483 return ret;
484
485 ns->attrs = libnvme_ns_attrs_alloc();
486 if (!ns->attrs)
487 return -ENOMEM12;
488
489 return 0;
490}
491
492static void libnvme_ns_set_generic_name(struct libnvme_ns *n, const char *name)
493{
494 char generic_name[PATH_MAX4096];
495 int instance, head_instance;
496 int ret;
497
498 ret = sscanf(name, "nvme%dn%d", &instance, &head_instance);
499 if (ret != 2)
500 return;
501
502 sprintf(generic_name, "ng%dn%d", instance, head_instance);
503 n->generic_name = strdup(generic_name);
504}
505
506int libnvme_ns_open(struct libnvme_global_ctx *ctx, const char *sys_path,
507 const char *name, struct libnvme_ns **ns)
508{
509 int ret;
510 struct libnvme_ns *n;
511 struct libnvme_ns_head *head;
512 struct stat arg;
513 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
514
515 n = calloc(1, sizeof(*n));
516 if (!n)
517 return -ENOMEM12;
518
519 head = calloc(1, sizeof(*head));
520 if (!head) {
521 free(n);
522 return -ENOMEM12;
523 }
524
525 head->n = n;
526 list_head_init(&head->paths);
527 ret = asprintf(&path, "%s/%s", sys_path, "multipath");
528 if (ret < 0) {
529 ret = -ENOMEM12;
530 goto free_ns_head;
531 }
532
533 /*
534 * The sysfs-dir "multipath" is available only when nvme multipath
535 * is configured and we're running kernel version >= 6.14.
536 */
537 ret = stat(path, &arg);
538 if (ret == 0) {
539 head->sysfs_dir = path;
540 path = NULL((void*)0);
541 } else
542 head->sysfs_dir = NULL((void*)0);
543
544 n->ctx = ctx;
545 n->head = head;
546 n->hdl = NULL((void*)0);
547 n->name = strdup(name);
548
549 libnvme_ns_set_generic_name(n, name);
550
551 ret = libnvme_ns_init(sys_path, n);
552 if (ret)
553 goto free_ns;
554
555 list_node_init(&n->entry);
556
557 libnvme_ns_release_transport_handle(n);
558
559 *ns = n;
560 return 0;
561
562free_ns:
563 free(n->generic_name);
564 free(n->name);
565free_ns_head:
566 free(head);
567 free(n);
568 return ret;
569}
570
571static inline bool_Bool libnvme_ns_is_generic(const char *name)
572{
573 int instance, head_instance;
574
575 if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2)
576 return false0;
577 return true1;
578}
579
580static char *libnvme_ns_generic_to_blkdev(const char *generic)
581{
582 int instance, head_instance;
583 char blkdev[PATH_MAX4096];
584
585 if (!libnvme_ns_is_generic(generic))
2
Taking true branch
586 return strdup(generic);
3
Memory is allocated
587
588 if (sscanf(generic, "ng%dn%d", &instance, &head_instance) != 2)
589 return NULL((void*)0);
590 sprintf(blkdev, "nvme%dn%d", instance, head_instance);
591
592 return strdup(blkdev);
593}
594
595int __libnvme_scan_namespace(struct libnvme_global_ctx *ctx,
596 const char *sysfs_dir, const char *name, struct libnvme_ns **ns)
597{
598 __cleanup_free__attribute__((cleanup(shr_freep))) char *blkdev = NULL((void*)0);
599 __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0);
600 struct libnvme_ns *n = NULL((void*)0);
601 int ret;
602
603 blkdev = libnvme_ns_generic_to_blkdev(name);
1
Calling 'libnvme_ns_generic_to_blkdev'
4
Returned allocated memory
604 if (!blkdev)
5
Assuming 'blkdev' is non-null
6
Taking false branch
605 return -ENOMEM12;
606
607 ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev);
608 if (ret < 0)
7
Assuming 'ret' is < 0
8
Taking true branch
609 return -ENOMEM12;
9
Potential leak of memory pointed to by 'blkdev'
610
611 ret = libnvme_ns_open(ctx, path, blkdev, &n);
612 if (ret)
613 return ret;
614
615 n->sysfs_dir = path;
616 path = NULL((void*)0);
617
618 *ns = n;
619 return 0;
620}
621
622int libnvme_get_ctrl_transport(__shr_unused__attribute__((__unused__)) struct libnvme_global_ctx *ctx,
623 const char *path, const char *name,
624 char **transport, char **traddr, char **addr, char **trsvcid,
625 char **host_traddr, char **host_iface)
626{
627 char *a = NULL((void*)0), *e = NULL((void*)0);
628
629 *transport = libnvme_get_attr(path, "transport");
630 if (!*transport)
631 return -ENXIO6;
632
633 /* Parse 'address' string into components */
634 *addr = libnvme_get_attr(path, "address");
635 if (!*addr) {
636 __cleanup_free__attribute__((cleanup(shr_freep))) char *rpath = NULL((void*)0);
637 char *p = NULL((void*)0), *_a = NULL((void*)0);
638
639 /* loop transport might not have an address */
640 if (!strcmp(*transport, "loop"))
641 goto skip_address;
642
643 /* Older kernels don't support pcie transport addresses */
644 if (strcmp(*transport, "pcie") &&
645 strcmp(*transport, "apple-nvme"))
646 return -ENXIO6;
647 /* Figure out the PCI address from the attribute path */
648 rpath = realpath(path, NULL((void*)0));
649 if (!rpath)
650 return -ENOMEM12;
651 a = strtok_r(rpath, "/", &e);
652 while (a && strlen(a)) {
653 if (_a)
654 p = _a;
655 _a = a;
656 if (!strncmp(a, "nvme", 4))
657 break;
658 a = strtok_r(NULL((void*)0), "/", &e);
659 }
660 if (p)
661 *addr = strdup(p);
662 } else if (!strcmp(*transport, "pcie") ||
663 !strcmp(*transport, "apple-nvme")) {
664 /* The 'address' string is the transport address */
665 *traddr = strdup(*addr);
666 if (!*traddr)
667 return -ENOMEM12;
668 } else {
669 __cleanup_free__attribute__((cleanup(shr_freep))) char *address = strdup(*addr);
670 if (!address)
671 return -ENOMEM12;
672
673 a = strtok_r(address, ",", &e);
674 while (a && strlen(a)) {
675 if (!strncmp(a, "traddr=", 7))
676 *traddr = strdup(a + 7);
677 else if (!strncmp(a, "trsvcid=", 8))
678 *trsvcid = strdup(a + 8);
679 else if (!strncmp(a, "host_traddr=", 12))
680 *host_traddr = strdup(a + 12);
681 else if (!strncmp(a, "host_iface=", 11))
682 *host_iface = strdup(a + 11);
683 a = strtok_r(NULL((void*)0), ",", &e);
684 }
685 }
686skip_address:
687 return 0;
688}
689
690int libnvme_init_subsystem(struct libnvme_subsystem *s, const char *name)
691{
692 char *path;
693
694 if (asprintf(&path, "%s/%s",
695 libnvme_subsys_sysfs_dir(s->h->ctx), name) < 0)
696 return -ENOMEM12;
697
698 s->subsystype = libnvme_get_attr(path, "subsystype");
699 if (!s->subsystype) {
700 if (!strcmp(s->subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"))
701 s->subsystype = strdup("discovery");
702 else
703 s->subsystype = strdup("nvm");
704 }
705 s->name = strdup(name);
706 s->sysfs_dir = (char *)path;
707
708 return 0;
709}