Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -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/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/src/libnvme3.so.3.0.0.p -I libnvme/src -I ../libnvme/src -I ccan -I ../ccan -I . -I .. -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-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 <libnvme.h>
26
27#include "cleanup.h"
28#include "cleanup-linux.h"
29#include "private.h"
30#include "private-tree.h"
31#include "util.h"
32#include "compiler-attributes.h"
33
34__libnvme_public__attribute__((visibility("default"))) int libnvme_host_get_ids(struct libnvme_global_ctx *ctx,
35 const char *hostnqn_arg, const char *hostid_arg,
36 char **hostnqn, char **hostid)
37{
38 __cleanup_free__attribute__((cleanup(freep))) char *nqn = NULL((void*)0);
39 __cleanup_free__attribute__((cleanup(freep))) char *hid = NULL((void*)0);
40 __cleanup_free__attribute__((cleanup(freep))) char *hnqn = NULL((void*)0);
41 libnvme_host_t h;
42
43 /* command line argumments */
44 if (hostid_arg)
5
Assuming 'hostid_arg' is null
6
Taking false branch
45 hid = strdup(hostid_arg);
46 if (hostnqn_arg)
7
Assuming 'hostnqn_arg' is non-null
8
Taking true branch
47 hnqn = strdup(hostnqn_arg);
9
Memory is allocated
48
49 /* JSON config: assume the first entry is the default host */
50 h = libnvme_first_host(ctx);
51 if (h) {
10
Assuming 'h' is null
11
Taking false branch
52 if (!hid)
53 hid = xstrdup(libnvme_host_get_hostid(h));
54 if (!hnqn)
55 hnqn = xstrdup(libnvme_host_get_hostnqn(h));
56 }
57
58 /* /etc/nvme/hostid and/or /etc/nvme/hostnqn */
59 if (!hid
11.1
'hid' is null
)
12
Taking true branch
60 hid = libnvme_read_hostid();
61 if (!hnqn)
13
Assuming 'hnqn' is non-null
62 hnqn = libnvme_read_hostnqn();
63
64 /* incomplete configuration, thus derive hostid from hostnqn */
65 if (!hid && hnqn
14.1
'hnqn' is non-null
)
14
Assuming 'hid' is null
15
Taking true branch
66 hid = libnvme_hostid_from_hostnqn(hnqn);
67
68 /*
69 * fallback to use either DMI information or device-tree. If all
70 * fails generate one
71 */
72 if (!hid) {
16
Assuming 'hid' is null
17
Taking true branch
73 hid = libnvme_generate_hostid();
74 if (!hid)
18
Assuming 'hid' is null
19
Taking true branch
75 return -ENOMEM12;
20
Potential leak of memory pointed to by 'hnqn'
76
77 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: using auto generated hostid and hostnqn\n"
)
78 "warning: using auto generated hostid and hostnqn\n")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: using auto generated hostid and hostnqn\n"
)
;
79 }
80
81 /* incomplete configuration, thus derive hostnqn from hostid */
82 if (!hnqn) {
83 hnqn = libnvme_generate_hostnqn_from_hostid(hid);
84 if (!hnqn)
85 return -ENOMEM12;
86 }
87
88 /* sanity checks */
89 nqn = libnvme_hostid_from_hostnqn(hnqn);
90 if (nqn && strcmp(nqn, hid)) {
91 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n"
, hid, hnqn)
92 "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n"
, hid, hnqn)
93 hid, hnqn)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n"
, hid, hnqn)
;
94 }
95
96 *hostid = hid;
97 *hostnqn = hnqn;
98 hid = NULL((void*)0);
99 hnqn = NULL((void*)0);
100
101 return 0;
102}
103
104__libnvme_public__attribute__((visibility("default"))) int libnvme_get_host(
105 struct libnvme_global_ctx *ctx, const char *hostnqn,
106 const char *hostid, libnvme_host_t *host)
107{
108 __cleanup_free__attribute__((cleanup(freep))) char *hnqn = NULL((void*)0);
109 __cleanup_free__attribute__((cleanup(freep))) char *hid = NULL((void*)0);
110 struct libnvme_host *h;
111 int err;
112
113 err = libnvme_host_get_ids(ctx, hostnqn, hostid, &hnqn, &hid);
4
Calling 'libnvme_host_get_ids'
114 if (err)
115 return err;
116
117 h = libnvme_lookup_host(ctx, hnqn, hid);
118 if (!h)
119 return -ENOMEM12;
120
121 libnvme_host_set_hostsymname(h, NULL((void*)0));
122
123 *host = h;
124 return 0;
125}
126
127__libnvme_public__attribute__((visibility("default"))) const char *libnvme_ctrl_get_state(libnvme_ctrl_t c)
128{
129 char *state = c->state;
130
131 c->state = libnvme_get_ctrl_attr(c, "state");
132 free(state);
133 return c->state;
134}
135
136static int libnvme_ctrl_lookup_subsystem_name(struct libnvme_global_ctx *ctx,
137 const char *ctrl_name, char **name)
138{
139 const char *subsys_dir = libnvme_subsys_sysfs_dir();
140 __cleanup_dirents__attribute__((cleanup(cleanup_dirents))) struct dirents subsys = {};
141 int i;
142
143 subsys.num = libnvme_scan_subsystems(&subsys.ents);
144 if (subsys.num < 0)
145 return subsys.num;
146
147 for (i = 0; i < subsys.num; i++) {
148 struct stat st;
149 __cleanup_free__attribute__((cleanup(freep))) char *path = NULL((void*)0);
150
151 if (asprintf(&path, "%s/%s/%s", subsys_dir,
152 subsys.ents[i]->d_name, ctrl_name) < 0)
153 return -ENOMEM12;
154 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "lookup subsystem %s\n", path)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup subsystem %s\n"
, path)
;
155 if (stat(path, &st) < 0) {
156 continue;
157 }
158
159 *name = strdup(subsys.ents[i]->d_name);
160 if (!*name)
161 return -ENOMEM12;
162
163 return 0;
164 }
165 return -ENOENT2;
166}
167
168static int libnvme_ctrl_lookup_phy_slot(struct libnvme_global_ctx *ctx,
169 libnvme_ctrl_t c)
170{
171 const char *slots_sysfs_dir = libnvme_slots_sysfs_dir();
172 __cleanup_free__attribute__((cleanup(freep))) char *target_addr = NULL((void*)0);
173 __cleanup_dir__attribute__((cleanup(cleanup_dir))) DIR *slots_dir = NULL((void*)0);
174 struct dirent *entry;
175 char *slot;
176 int ret;
177
178 if (!c->address)
179 return -EINVAL22;
180
181 slots_dir = opendir(slots_sysfs_dir);
182 if (!slots_dir) {
183 libnvme_msg(ctx, LIBNVME_LOG_WARN, "failed to open slots dir %s\n",__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to open slots dir %s\n"
, slots_sysfs_dir)
184 slots_sysfs_dir)__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to open slots dir %s\n"
, slots_sysfs_dir)
;
185 return -errno(*__errno_location ());
186 }
187
188 target_addr = strndup(c->address, 10);
189 while ((entry = readdir(slots_dir))) {
190 if (entry->d_type == DT_DIRDT_DIR &&
191 strncmp(entry->d_name, ".", 1) != 0 &&
192 strncmp(entry->d_name, "..", 2) != 0) {
193 __cleanup_free__attribute__((cleanup(freep))) char *path = NULL((void*)0);
194 __cleanup_free__attribute__((cleanup(freep))) char *addr = NULL((void*)0);
195
196 ret = asprintf(&path, "%s/%s",
197 slots_sysfs_dir, entry->d_name);
198 if (ret < 0)
199 return -ENOMEM12;
200 addr = libnvme_get_attr(path, "address");
201
202 /* some directories don't have an address entry */
203 if (!addr)
204 continue;
205 if (strcmp(addr, target_addr))
206 continue;
207
208 slot = strdup(entry->d_name);
209 if (!slot)
210 return -ENOMEM12;
211
212 c->phy_slot = slot;
213 return 0;
214 }
215 }
216 return -ENOENT2;
217}
218
219int libnvme_reconfigure_ctrl(struct libnvme_global_ctx *ctx,
220 libnvme_ctrl_t c, const char *path, const char *name)
221{
222 DIR *d;
223
224 /*
225 * It's necesssary to release any resources first because a ctrl
226 * can be reused.
227 */
228 libnvme_ctrl_release_transport_handle(c);
229 FREE_CTRL_ATTR(c->name)do { free(c->name); (c->name) = ((void*)0); } while (0);
230 FREE_CTRL_ATTR(c->sysfs_dir)do { free(c->sysfs_dir); (c->sysfs_dir) = ((void*)0); }
while (0)
;
231 FREE_CTRL_ATTR(c->firmware)do { free(c->firmware); (c->firmware) = ((void*)0); } while
(0)
;
232 FREE_CTRL_ATTR(c->model)do { free(c->model); (c->model) = ((void*)0); } while (
0)
;
233 FREE_CTRL_ATTR(c->state)do { free(c->state); (c->state) = ((void*)0); } while (
0)
;
234 FREE_CTRL_ATTR(c->numa_node)do { free(c->numa_node); (c->numa_node) = ((void*)0); }
while (0)
;
235 FREE_CTRL_ATTR(c->queue_count)do { free(c->queue_count); (c->queue_count) = ((void*)0
); } while (0)
;
236 FREE_CTRL_ATTR(c->serial)do { free(c->serial); (c->serial) = ((void*)0); } while
(0)
;
237 FREE_CTRL_ATTR(c->sqsize)do { free(c->sqsize); (c->sqsize) = ((void*)0); } while
(0)
;
238 FREE_CTRL_ATTR(c->cntrltype)do { free(c->cntrltype); (c->cntrltype) = ((void*)0); }
while (0)
;
239 FREE_CTRL_ATTR(c->cntlid)do { free(c->cntlid); (c->cntlid) = ((void*)0); } while
(0)
;
240 FREE_CTRL_ATTR(c->dctype)do { free(c->dctype); (c->dctype) = ((void*)0); } while
(0)
;
241 FREE_CTRL_ATTR(c->phy_slot)do { free(c->phy_slot); (c->phy_slot) = ((void*)0); } while
(0)
;
242
243 d = opendir(path);
244 if (!d) {
245 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 ()))
246 "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 ()))
;
247 return -ENODEV19;
248 }
249 closedir(d);
250
251 c->hdl = NULL((void*)0);
252 c->name = xstrdup(name);
253 c->sysfs_dir = xstrdup(path);
254 c->firmware = libnvme_get_ctrl_attr(c, "firmware_rev");
255 c->model = libnvme_get_ctrl_attr(c, "model");
256 c->state = libnvme_get_ctrl_attr(c, "state");
257 c->numa_node = libnvme_get_ctrl_attr(c, "numa_node");
258 c->queue_count = libnvme_get_ctrl_attr(c, "queue_count");
259 c->serial = libnvme_get_ctrl_attr(c, "serial");
260 c->sqsize = libnvme_get_ctrl_attr(c, "sqsize");
261 c->cntrltype = libnvme_get_ctrl_attr(c, "cntrltype");
262 c->cntlid = libnvme_get_ctrl_attr(c, "cntlid");
263 c->dctype = libnvme_get_ctrl_attr(c, "dctype");
264 libnvme_ctrl_lookup_phy_slot(ctx, c);
265 libnvmf_read_sysfs_fabrics_attrs(ctx, c);
266
267 return 0;
268}
269
270__libnvme_public__attribute__((visibility("default"))) int libnvme_init_ctrl(
271 libnvme_host_t h, libnvme_ctrl_t c, int instance)
272{
273 __cleanup_free__attribute__((cleanup(freep))) char *subsys_name = NULL((void*)0), *name = NULL((void*)0), *path = NULL((void*)0);
274 libnvme_subsystem_t s;
275 int ret;
276
277 ret = asprintf(&name, "nvme%d", instance);
278 if (ret < 0)
279 return -ENOMEM12;
280
281 ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(), name);
282 if (ret < 0)
283 return -ENOMEM12;
284
285 ret = libnvme_reconfigure_ctrl(h->ctx, c, path, name);
286 if (ret < 0)
287 return ret;
288
289 c->address = libnvme_get_attr(path, "address");
290 if (!c->address && strcmp(c->transport, "loop"))
291 return -ENVME_CONNECT_INVAL_TR;
292
293 ret = libnvme_ctrl_lookup_subsystem_name(h->ctx, name, &subsys_name);
294 if (ret) {
295 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)
296 "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)
297 c->name)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to lookup subsystem name for %s\n"
, c->name)
;
298 return ENVME_CONNECT_LOOKUP_SUBSYS_NAME;
299 }
300
301 s = libnvme_lookup_subsystem(h, subsys_name, c->subsysnqn);
302 if (!s)
303 return -ENVME_CONNECT_LOOKUP_SUBSYS;
304
305 if (s->subsystype && !strcmp(s->subsystype, "discovery"))
306 c->discovery_ctrl = true1;
307
308 c->s = s;
309 list_add_tail(&s->ctrls, &c->entry)list_add_tail_(&s->ctrls, &c->entry, "../libnvme/src/nvme/tree-linux.c"
":" "309")
;
310
311 return ret;
312}
313
314__libnvme_public__attribute__((visibility("default"))) int libnvme_scan_ctrl(
315 struct libnvme_global_ctx *ctx, const char *name,
316 libnvme_ctrl_t *cp)
317{
318 __cleanup_free__attribute__((cleanup(freep))) char *subsysnqn = NULL((void*)0), *subsysname = NULL((void*)0);
319 __cleanup_free__attribute__((cleanup(freep))) char *hostnqn = NULL((void*)0), *hostid = NULL((void*)0);
320 __cleanup_free__attribute__((cleanup(freep))) char *path = NULL((void*)0);
321 char *host_key;
322 libnvme_host_t h;
323 libnvme_subsystem_t s;
324 libnvme_ctrl_t c;
325 int ret;
326
327 libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "scan controller %s\n", name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "scan controller %s\n"
, name)
;
328 ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(), name);
329 if (ret < 0)
1
Assuming 'ret' is >= 0
2
Taking false branch
330 return -ENOMEM12;
331
332 hostnqn = libnvme_get_attr(path, "hostnqn");
333 hostid = libnvme_get_attr(path, "hostid");
334 ret = libnvme_get_host(ctx, hostnqn, hostid, &h);
3
Calling 'libnvme_get_host'
335 if (ret)
336 return ret;
337
338 host_key = libnvme_get_attr(path, "dhchap_secret");
339 if (host_key && strcmp(host_key, "none")) {
340 free(h->dhchap_host_key);
341 h->dhchap_host_key = host_key;
342 host_key = NULL((void*)0);
343 }
344 free(host_key);
345
346 subsysnqn = libnvme_get_attr(path, "subsysnqn");
347 if (!subsysnqn)
348 return -ENXIO6;
349
350 ret = libnvme_ctrl_lookup_subsystem_name(ctx, name, &subsysname);
351 if (ret) {
352 libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
353 "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)
354 name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "failed to lookup subsystem for controller %s\n"
, name)
;
355 return ret;
356 }
357
358 s = libnvme_lookup_subsystem(h, subsysname, subsysnqn);
359 if (!s)
360 return -ENOMEM12;
361
362 ret = libnvme_ctrl_alloc(ctx, s, path, name, &c);
363 if (ret)
364 return ret;
365
366 ret = libnvme_ctrl_scan_paths(ctx, c);
367 if (ret) {
368 libnvme_free_ctrl(c);
369 return ret;
370 }
371
372 ret = libnvme_ctrl_scan_namespaces(ctx, c);
373 if (ret) {
374 libnvme_free_ctrl(c);
375 return ret;
376 }
377
378 *cp = c;
379 return 0;
380}
381
382static int libnvme_strtou64(const char *str, void *res)
383{
384 char *endptr;
385 __u64 v;
386
387 errno(*__errno_location ()) = 0;
388 v = strtoull(str, &endptr, 0);
389
390 if (errno(*__errno_location ()) != 0)
391 return -errno(*__errno_location ());
392
393 if (endptr == str) {
394 /* no digits found */
395 return -EINVAL22;
396 }
397
398 *(__u64 *)res = v;
399 return 0;
400}
401
402static int libnvme_strtou32(const char *str, void *res)
403{
404 char *endptr;
405 __u32 v;
406
407 errno(*__errno_location ()) = 0;
408 v = strtol(str, &endptr, 0);
409
410 if (errno(*__errno_location ()) != 0)
411 return -errno(*__errno_location ());
412
413 if (endptr == str) {
414 /* no digits found */
415 return -EINVAL22;
416 }
417
418 *(__u32 *)res = v;
419 return 0;
420}
421
422static int libnvme_strtoi(const char *str, void *res)
423{
424 char *endptr;
425 int v;
426
427 errno(*__errno_location ()) = 0;
428 v = strtol(str, &endptr, 0);
429
430 if (errno(*__errno_location ()) != 0)
431 return -errno(*__errno_location ());
432
433 if (endptr == str) {
434 /* no digits found */
435 return -EINVAL22;
436 }
437
438 *(int *)res = v;
439 return 0;
440}
441
442static int libnvme_strtoeuid(const char *str, void *res)
443{
444 memcpy(res, str, 8);
445 return 0;
446}
447
448static int libnvme_strtouuid(const char *str, void *res)
449{
450 memcpy(res, str, NVME_UUID_LEN16);
451 return 0;
452}
453
454struct sysfs_attr_table {
455 void *var;
456 int (*parse)(const char *str, void *res);
457 bool_Bool mandatory;
458 const char *name;
459};
460
461#define GETSHIFT(x)(__builtin_ffsll(x) - 1) (__builtin_ffsll(x) - 1)
462#define ARRAY_SIZE(arr)(sizeof(arr) / sizeof((arr)[0])) (sizeof(arr) / sizeof((arr)[0]))
463
464static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size)
465{
466 char *str;
467 int ret, i;
468
469 for (i = 0; i < size; i++) {
470 struct sysfs_attr_table *e = &tbl[i];
471
472 str = libnvme_get_attr(path, e->name);
473 if (!str) {
474 if (!e->mandatory)
475 continue;
476 return -ENOENT2;
477 }
478 ret = e->parse(str, e->var);
479 free(str);
480 if (ret)
481 return ret;
482 }
483
484 return 0;
485}
486
487int libnvme_ns_init(const char *path, struct libnvme_ns *ns)
488{
489 __cleanup_free__attribute__((cleanup(freep))) char *attr = NULL((void*)0);
490 struct stat sb;
491 uint64_t size;
492 int ret;
493
494 struct sysfs_attr_table base[] = {
495 { &ns->nsid, libnvme_strtou32, true1, "nsid" },
496 { &size, libnvme_strtou64, true1, "size" },
497 { &ns->lba_size, libnvme_strtou32, true1, "queue/logical_block_size" },
498 { ns->eui64, libnvme_strtoeuid, false0, "eui" },
499 { ns->nguid, libnvme_strtouuid, false0, "nguid" },
500 { ns->uuid, libnvme_strtouuid, false0, "uuid" }
501 };
502
503 ret = parse_attrs(path, base, ARRAY_SIZE(base)(sizeof(base) / sizeof((base)[0])));
504 if (ret)
505 return ret;
506
507 ns->lba_shift = GETSHIFT(ns->lba_size)(__builtin_ffsll(ns->lba_size) - 1);
508 /*
509 * size is in 512 bytes units and lba_count is in lba_size which are not
510 * necessarily the same.
511 */
512 ns->lba_count = size >> (ns->lba_shift - SECTOR_SHIFT9);
513
514 if (asprintf(&attr, "%s/csi", path) < 0)
515 return -ENOMEM12;
516
517 ret = stat(attr, &sb);
518 if (ret == 0) {
519 /* only available on kernels >= 6.8 */
520 struct sysfs_attr_table ext[] = {
521 { &ns->csi, libnvme_strtoi, true1, "csi" },
522 { &ns->lba_util, libnvme_strtou64, true1, "nuse" },
523 { &ns->meta_size, libnvme_strtoi, true1, "metadata_bytes"},
524
525 };
526
527 ret = parse_attrs(path, ext, ARRAY_SIZE(ext)(sizeof(ext) / sizeof((ext)[0])));
528 if (ret)
529 return ret;
530 } else {
531 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ns *id = NULL((void*)0);
532 uint8_t flbas;
533
534 id = libnvme_alloc(sizeof(*id));
535 if (!id)
536 return -ENOMEM12;
537
538 ret = libnvme_ns_identify(ns, id);
539 if (ret)
540 return ret;
541
542 nvme_id_ns_flbas_to_lbaf_inuse(id->flbas, &flbas);
543 ns->lba_count = le64_to_cpu(id->nsze);
544 ns->lba_util = le64_to_cpu(id->nuse);
545 ns->meta_size = le16_to_cpu(id->lbaf[flbas].ms);
546 }
547
548 return 0;
549}
550
551static void libnvme_ns_set_generic_name(struct libnvme_ns *n, const char *name)
552{
553 char generic_name[PATH_MAX4096];
554 int instance, head_instance;
555 int ret;
556
557 ret = sscanf(name, "nvme%dn%d", &instance, &head_instance);
558 if (ret != 2)
559 return;
560
561 sprintf(generic_name, "ng%dn%d", instance, head_instance);
562 n->generic_name = strdup(generic_name);
563}
564
565int libnvme_ns_open(struct libnvme_global_ctx *ctx, const char *sys_path,
566 const char *name, libnvme_ns_t *ns)
567{
568 int ret;
569 struct libnvme_ns *n;
570 struct libnvme_ns_head *head;
571 struct stat arg;
572 __cleanup_free__attribute__((cleanup(freep))) char *path = NULL((void*)0);
573
574 n = calloc(1, sizeof(*n));
575 if (!n)
576 return -ENOMEM12;
577
578 head = calloc(1, sizeof(*head));
579 if (!head) {
580 free(n);
581 return -ENOMEM12;
582 }
583
584 head->n = n;
585 list_head_init(&head->paths);
586 ret = asprintf(&path, "%s/%s", sys_path, "multipath");
587 if (ret < 0) {
588 ret = -ENOMEM12;
589 goto free_ns_head;
590 }
591
592 /*
593 * The sysfs-dir "multipath" is available only when nvme multipath
594 * is configured and we're running kernel version >= 6.14.
595 */
596 ret = stat(path, &arg);
597 if (ret == 0) {
598 head->sysfs_dir = path;
599 path = NULL((void*)0);
600 } else
601 head->sysfs_dir = NULL((void*)0);
602
603 n->ctx = ctx;
604 n->head = head;
605 n->hdl = NULL((void*)0);
606 n->name = strdup(name);
607
608 libnvme_ns_set_generic_name(n, name);
609
610 ret = libnvme_ns_init(sys_path, n);
611 if (ret)
612 goto free_ns;
613
614 list_node_init(&n->entry);
615
616 libnvme_ns_release_transport_handle(n);
617
618 *ns = n;
619 return 0;
620
621free_ns:
622 free(n->generic_name);
623 free(n->name);
624free_ns_head:
625 free(head);
626 free(n);
627 return ret;
628}
629
630static inline bool_Bool libnvme_ns_is_generic(const char *name)
631{
632 int instance, head_instance;
633
634 if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2)
635 return false0;
636 return true1;
637}
638
639static char *libnvme_ns_generic_to_blkdev(const char *generic)
640{
641
642 int instance, head_instance;
643 char blkdev[PATH_MAX4096];
644
645 if (!libnvme_ns_is_generic(generic))
646 return strdup(generic);
647
648 sscanf(generic, "ng%dn%d", &instance, &head_instance);
649 sprintf(blkdev, "nvme%dn%d", instance, head_instance);
650
651 return strdup(blkdev);
652}
653
654int __libnvme_scan_namespace(struct libnvme_global_ctx *ctx,
655 const char *sysfs_dir, const char *name, libnvme_ns_t *ns)
656{
657 __cleanup_free__attribute__((cleanup(freep))) char *blkdev = NULL((void*)0);
658 __cleanup_free__attribute__((cleanup(freep))) char *path = NULL((void*)0);
659 struct libnvme_ns *n = NULL((void*)0);
660 int ret;
661
662 blkdev = libnvme_ns_generic_to_blkdev(name);
663 if (!blkdev)
664 return -ENOMEM12;
665
666 ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev);
667 if (ret < 0)
668 return -ENOMEM12;
669
670 ret = libnvme_ns_open(ctx, path, blkdev, &n);
671 if (ret)
672 return ret;
673
674 n->sysfs_dir = path;
675 path = NULL((void*)0);
676
677 *ns = n;
678 return 0;
679}
680
681int libnvme_get_ctrl_transport(const char *path, const char *name,
682 char **transport, char **traddr, char **addr, char **trsvcid,
683 char **host_traddr, char **host_iface)
684{
685 char *a = NULL((void*)0), *e = NULL((void*)0);
686
687 *transport = libnvme_get_attr(path, "transport");
688 if (!*transport)
689 return -ENXIO6;
690
691 /* Parse 'address' string into components */
692 *addr = libnvme_get_attr(path, "address");
693 if (!*addr) {
694 __cleanup_free__attribute__((cleanup(freep))) char *rpath = NULL((void*)0);
695 char *p = NULL((void*)0), *_a = NULL((void*)0);
696
697 /* loop transport might not have an address */
698 if (!strcmp(*transport, "loop"))
699 goto skip_address;
700
701 /* Older kernels don't support pcie transport addresses */
702 if (strcmp(*transport, "pcie") &&
703 strcmp(*transport, "apple-nvme"))
704 return -ENXIO6;
705 /* Figure out the PCI address from the attribute path */
706 rpath = realpath(path, NULL((void*)0));
707 if (!rpath)
708 return -ENOMEM12;
709 a = strtok_r(rpath, "/", &e);
710 while (a && strlen(a)) {
711 if (_a)
712 p = _a;
713 _a = a;
714 if (!strncmp(a, "nvme", 4))
715 break;
716 a = strtok_r(NULL((void*)0), "/", &e);
717 }
718 if (p)
719 *addr = strdup(p);
720 } else if (!strcmp(*transport, "pcie") ||
721 !strcmp(*transport, "apple-nvme")) {
722 /* The 'address' string is the transport address */
723 *traddr = strdup(*addr);
724 if (!*traddr)
725 return -ENOMEM12;
726 } else {
727 __cleanup_free__attribute__((cleanup(freep))) char *address = strdup(*addr);
728 if (!address)
729 return -ENOMEM12;
730
731 a = strtok_r(address, ",", &e);
732 while (a && strlen(a)) {
733 if (!strncmp(a, "traddr=", 7))
734 *traddr = strdup(a + 7);
735 else if (!strncmp(a, "trsvcid=", 8))
736 *trsvcid = strdup(a + 8);
737 else if (!strncmp(a, "host_traddr=", 12))
738 *host_traddr = strdup(a + 12);
739 else if (!strncmp(a, "host_iface=", 11))
740 *host_iface = strdup(a + 11);
741 a = strtok_r(NULL((void*)0), ",", &e);
742 }
743 }
744skip_address:
745 return 0;
746}
747
748int libnvme_init_subsystem(libnvme_subsystem_t s, const char *name)
749{
750 char *path;
751
752 if (asprintf(&path, "%s/%s", libnvme_subsys_sysfs_dir(), name) < 0)
753 return -ENOMEM12;
754
755 s->model = libnvme_get_attr(path, "model");
756 if (!s->model)
757 s->model = strdup("undefined");
758 s->serial = libnvme_get_attr(path, "serial");
759 s->firmware = libnvme_get_attr(path, "firmware_rev");
760 s->subsystype = libnvme_get_attr(path, "subsystype");
761 if (!s->subsystype) {
762 if (!strcmp(s->subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"))
763 s->subsystype = strdup("discovery");
764 else
765 s->subsystype = strdup("nvm");
766 }
767 s->name = strdup(name);
768 s->sysfs_dir = (char *)path;
769 s->iopolicy = libnvme_get_attr(path, "iopolicy");
770
771 return 0;
772}