Bug Summary

File:.build-ci/../plugins/exclusion/exclusion-nvme.c
Warning:line 593, column 10
An undefined value may be read from 'errno'

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 exclusion-nvme.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 static -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 nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I shared -I ../shared -I /usr/include/json-c -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=gnu99 -ferror-limit 19 -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-08-08-045408-589-1 -x c ../plugins/exclusion/exclusion-nvme.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries.
5 *
6 * Authors: Martin Belanger <martin.belanger@dell.com>
7 */
8#include <errno(*__errno_location ()).h>
9#include <fcntl.h>
10#include <limits.h>
11#include <stdint.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/stat.h>
16#include <sys/wait.h>
17#include <unistd.h>
18
19#include <libnvme.h>
20
21#include "common.h"
22#include "nvme.h"
23#include "nvme-print.h"
24#include "src/cleanup.h"
25
26#define CREATE_CMD
27#include "exclusion-nvme.h"
28
29/*
30 * Mutating exclusion commands write the root-owned exclusion lists under
31 * /etc/nvme. Fail early with a clear message rather than letting the write
32 * fail later with a bare -EACCES.
33 */
34static int require_root(void)
35{
36 if (geteuid() != 0) {
37 nvme_show_error("this command requires root privileges (try sudo)")nvme_show_message(1, "this command requires root privileges (try sudo)"
)
;
38 return -EPERM1;
39 }
40 return 0;
41}
42
43static int excl_create(int argc, char **argv, struct command *acmd,
44 struct plugin *plugin)
45{
46 const char *desc = "Create a new NVMeoF exclusion list.";
47 const char *name_help = "exclusion list name (omit for the default list)";
48 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
49 int ret;
50
51 struct config {
52 char *name;
53 } cfg = { 0 };
54
55 NVME_ARGS(opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
56 OPT_STRING("name", 'N', "NAME", &cfg.name, name_help))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
57
58 ret = parse_args(argc, argv, desc, opts);
59 if (ret)
60 return ret;
61
62 ret = require_root();
63 if (ret)
64 return ret;
65
66 ret = nvme_create_global_ctx(&ctx);
67 if (ret)
68 return ret;
69 ret = libnvmf_exclusion_create(ctx, cfg.name);
70 if (ret == -EEXIST17)
71 nvme_show_error("exclusion list '%s' already exists", cfg.name)nvme_show_message(1, "exclusion list '%s' already exists", cfg
.name)
;
72 else if (ret)
73 nvme_show_error("create failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "create failed: %s", libnvme_strerror(-ret
))
;
74 return ret;
75}
76
77static int excl_delete(int argc, char **argv, struct command *acmd,
78 struct plugin *plugin)
79{
80 const char *desc = "Delete an NVMeoF exclusion list entirely.";
81 const char *name_help = "exclusion list name (omit for the default list)";
82 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
83 int ret;
84
85 struct config {
86 char *name;
87 } cfg = { 0 };
88
89 NVME_ARGS(opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
90 OPT_STRING("name", 'N', "NAME", &cfg.name, name_help))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
91
92 ret = parse_args(argc, argv, desc, opts);
93 if (ret)
94 return ret;
95
96 ret = require_root();
97 if (ret)
98 return ret;
99
100 ret = nvme_create_global_ctx(&ctx);
101 if (ret)
102 return ret;
103 ret = libnvmf_exclusion_delete(ctx, cfg.name);
104 if (ret == -ENOENT2)
105 nvme_show_error("exclusion list '%s' not found", cfg.name)nvme_show_message(1, "exclusion list '%s' not found", cfg.name
)
;
106 else if (ret)
107 nvme_show_error("delete failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "delete failed: %s", libnvme_strerror(-ret
))
;
108 return ret;
109}
110
111static void print_list_name(const char *name, void *user_data __attribute__((unused)))
112{
113 printf("%s\n", name);
114}
115
116static void print_entry(const char *entry, void *user_data __attribute__((unused)))
117{
118 printf(" %s\n", entry);
119}
120
121static int excl_list(int argc, char **argv, struct command *acmd,
122 struct plugin *plugin)
123{
124 const char *desc = "List exclusion lists, or entries within a named list.";
125 const char *name_help = "exclusion list name (omit to list all lists)";
126 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
127 int ret;
128
129 struct config {
130 char *name;
131 } cfg = { 0 };
132
133 NVME_ARGS(opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
134 OPT_STRING("name", 'N', "NAME", &cfg.name, name_help))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
135
136 ret = parse_args(argc, argv, desc, opts);
137 if (ret)
138 return ret;
139
140 ret = nvme_create_global_ctx(&ctx);
141 if (ret)
142 return ret;
143
144 if (!cfg.name) {
145 ret = libnvmf_exclusion_list_for_each(ctx, print_list_name, NULL((void*)0));
146 if (ret)
147 nvme_show_error("list failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "list failed: %s", libnvme_strerror(-ret
))
;
148 return ret;
149 }
150
151 ret = libnvmf_exclusion_entry_for_each(ctx, cfg.name, print_entry, NULL((void*)0));
152 if (ret == -ENOENT2)
153 nvme_show_error("exclusion list '%s' not found", cfg.name)nvme_show_message(1, "exclusion list '%s' not found", cfg.name
)
;
154 else if (ret)
155 nvme_show_error("list failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "list failed: %s", libnvme_strerror(-ret
))
;
156 return ret;
157}
158
159static int excl_add(int argc, char **argv, struct command *acmd,
160 struct plugin *plugin)
161{
162 const char *desc = "Add an entry to an NVMeoF exclusion list.";
163 const char *name_help = "exclusion list name (omit for the default list)";
164 const char *entry_help = "semicolon-separated key=value entry "
165 "(e.g. \"transport=tcp;traddr=192.168.1.1\")";
166 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
167 int ret;
168
169 struct config {
170 char *name;
171 char *entry;
172 } cfg = { 0 };
173
174 NVME_ARGS(opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"entry", 'e', "ENTRY", CFG_STRING, &cfg.entry, 1
, entry_help, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, (
(void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose", 'v'
, "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet
, 0, "suppress output messages, overwrites verbose mode", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
175 OPT_STRING("name", 'N', "NAME", &cfg.name, name_help),nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"entry", 'e', "ENTRY", CFG_STRING, &cfg.entry, 1
, entry_help, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, (
(void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose", 'v'
, "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet
, 0, "suppress output messages, overwrites verbose mode", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
176 OPT_STRING("entry", 'e', "ENTRY", &cfg.entry, entry_help))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"entry", 'e', "ENTRY", CFG_STRING, &cfg.entry, 1
, entry_help, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, (
(void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose", 'v'
, "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet
, 0, "suppress output messages, overwrites verbose mode", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
177
178 ret = parse_args(argc, argv, desc, opts);
179 if (ret)
180 return ret;
181
182 if (!cfg.entry) {
183 nvme_show_error("--entry required")nvme_show_message(1, "--entry required");
184 return -EINVAL22;
185 }
186
187 ret = require_root();
188 if (ret)
189 return ret;
190
191 ret = nvme_create_global_ctx(&ctx);
192 if (ret)
193 return ret;
194 if (!libnvmf_exclusion_entry_valid(ctx, cfg.entry)) {
195 nvme_show_error("invalid entry: %s", cfg.entry)nvme_show_message(1, "invalid entry: %s", cfg.entry);
196 return -EINVAL22;
197 }
198
199 ret = libnvmf_exclusion_add(ctx, cfg.name, cfg.entry);
200 if (ret == -EINVAL22)
201 nvme_show_error("invalid list name: %s", cfg.name)nvme_show_message(1, "invalid list name: %s", cfg.name);
202 else if (ret)
203 nvme_show_error("add failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "add failed: %s", libnvme_strerror(-ret)
)
;
204 return ret;
205}
206
207struct entry_collection {
208 char **entries;
209 size_t count, cap;
210};
211
212static void collect_entry(const char *entry, void *user_data)
213{
214 struct entry_collection *ec = user_data;
215
216 if (ec->count == ec->cap) {
217 size_t newcap = ec->cap ? ec->cap * 2 : 16;
218 char **newarr = realloc(ec->entries, newcap * sizeof(*newarr));
219
220 if (!newarr)
221 return;
222 ec->entries = newarr;
223 ec->cap = newcap;
224 }
225 ec->entries[ec->count] = strdup(entry);
226 if (ec->entries[ec->count])
227 ec->count++;
228}
229
230static int cmp_entry(const void *a, const void *b)
231{
232 return strcmp(*(const char * const *)a, *(const char * const *)b);
233}
234
235static int excl_remove(int argc, char **argv, struct command *acmd,
236 struct plugin *plugin)
237{
238 const char *desc = "Interactively remove an entry from an NVMeoF exclusion list.";
239 const char *name_help = "exclusion list name (omit for the default list)";
240 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
241 struct entry_collection ec = { 0 };
242 char answer[32];
243 size_t i, choice;
244 int ret;
245
246 struct config {
247 char *name;
248 } cfg = { 0 };
249
250 NVME_ARGS(opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
251 OPT_STRING("name", 'N', "NAME", &cfg.name, name_help))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
252
253 ret = parse_args(argc, argv, desc, opts);
254 if (ret)
255 return ret;
256
257 ret = require_root();
258 if (ret)
259 return ret;
260
261 ret = nvme_create_global_ctx(&ctx);
262 if (ret)
263 return ret;
264 ret = libnvmf_exclusion_entry_for_each(ctx, cfg.name, collect_entry, &ec);
265 if (ret) {
266 if (ret == -ENOENT2)
267 nvme_show_error("exclusion list '%s' not found", cfg.name)nvme_show_message(1, "exclusion list '%s' not found", cfg.name
)
;
268 else
269 nvme_show_error("list failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "list failed: %s", libnvme_strerror(-ret
))
;
270 return ret;
271 }
272
273 if (ec.count == 0) {
274 printf("exclusion list '%s' has no entries\n", cfg.name);
275 free(ec.entries);
276 return 0;
277 }
278
279 qsort(ec.entries, ec.count, sizeof(*ec.entries), cmp_entry);
280
281 for (i = 0; i < ec.count; i++)
282 printf("%3zu. %s\n", i + 1, ec.entries[i]);
283
284 printf("Which entry do you want to delete? [1-%zu, or empty to cancel] ",
285 ec.count);
286 fflush(stdoutstdout);
287
288 if (!fgets(answer, sizeof(answer), stdinstdin) ||
289 sscanf(answer, "%zu", &choice) != 1 ||
290 choice < 1 || choice > ec.count) {
291 printf("cancelled\n");
292 ret = 0;
293 goto out;
294 }
295
296 ret = libnvmf_exclusion_remove(ctx, cfg.name, ec.entries[choice - 1]);
297 if (ret)
298 nvme_show_error("remove failed: %s", libnvme_strerror(-ret))nvme_show_message(1, "remove failed: %s", libnvme_strerror(-ret
))
;
299
300out:
301 for (i = 0; i < ec.count; i++)
302 free(ec.entries[i]);
303 free(ec.entries);
304 return ret;
305}
306
307static int run_editor(const char *path)
308{
309 const char *editor = getenv("EDITOR");
310 pid_t pid;
311 int status;
312
313 if (!editor || !*editor)
314 editor = "vi";
315
316 pid = fork();
317 if (pid < 0)
318 return -errno(*__errno_location ());
319 if (pid == 0) {
320 char cmd[PATH_MAX4096 + 64];
321
322 /*
323 * Pass the path as $1 and reference it in the command so the
324 * editor actually opens the file, while still allowing $EDITOR
325 * to carry arguments (word-split by the shell).
326 */
327 snprintf(cmd, sizeof(cmd), "%s \"$1\"", editor);
328 execl("/bin/sh", "sh", "-c", cmd, "sh", path, (char *)NULL((void*)0));
329 _exit(127);
330 }
331 if (waitpid(pid, &status, 0) < 0)
332 return -errno(*__errno_location ());
333 return WIFEXITED(status)(((status) & 0x7f) == 0) && WEXITSTATUS(status)(((status) & 0xff00) >> 8) == 0 ? 0 : -EIO5;
334}
335
336/*
337 * Validate a file: every "exclusion = ..." line must be a valid entry and
338 * must sit inside the [exclusions] section. Mirrors libnvme's write-side
339 * validation but reports each offense with its line number.
340 * Returns 0 if the file is valid, -EINVAL and prints errors otherwise.
341 */
342static int validate_conf_file(struct libnvme_global_ctx *ctx, const char *path)
343{
344 FILE *f;
345 char line[4096];
346 unsigned lineno = 0;
347 bool_Bool in_excl = false0;
348 int errors = 0;
349
350 f = fopen(path, "r");
351 if (!f)
352 return -errno(*__errno_location ());
353
354 while (fgets(line, sizeof(line), f)) {
355 char *s, *eq, *key, *val;
356
357 lineno++;
358 s = line;
359 while (*s == ' ' || *s == '\t')
360 s++;
361 if (!*s || *s == '#' || *s == '\n')
362 continue;
363
364 if (*s == '[') {
365 char *end = strchr(s, ']')_Generic (0 ? (s) : (void *) 1, const void *: (const char *) (
strchr (s, ']')), default: strchr (s, ']'))
;
366
367 if (!end) {
368 nvme_show_error(nvme_show_message(1, "line %u: malformed section header\n", lineno
)
369 "line %u: malformed section header\n",nvme_show_message(1, "line %u: malformed section header\n", lineno
)
370 lineno)nvme_show_message(1, "line %u: malformed section header\n", lineno
)
;
371 errors++;
372 in_excl = false0;
373 continue;
374 }
375 *end = '\0';
376 s++;
377 while (*s == ' ' || *s == '\t')
378 s++;
379 while (end > s && (end[-1] == ' ' || end[-1] == '\t'))
380 *--end = '\0';
381 in_excl = !strcmp(s, "exclusions");
382 continue;
383 }
384
385 eq = strchr(s, '=')_Generic (0 ? (s) : (void *) 1, const void *: (const char *) (
strchr (s, '=')), default: strchr (s, '='))
;
386 if (!eq)
387 continue;
388 *eq = '\0';
389 key = s;
390 while (*key == ' ' || *key == '\t')
391 key++;
392 char *ke = key + strlen(key);
393 while (ke > key && (ke[-1] == ' ' || ke[-1] == '\t'))
394 ke--;
395 *ke = '\0';
396
397 if (strcmp(key, "exclusion"))
398 continue;
399
400 if (!in_excl) {
401 nvme_show_error(nvme_show_message(1, "line %u: entry outside the [exclusions] section\n"
, lineno)
402 "line %u: entry outside the [exclusions] section\n",nvme_show_message(1, "line %u: entry outside the [exclusions] section\n"
, lineno)
403 lineno)nvme_show_message(1, "line %u: entry outside the [exclusions] section\n"
, lineno)
;
404 errors++;
405 continue;
406 }
407
408 val = eq + 1;
409 while (*val == ' ' || *val == '\t')
410 val++;
411 char *ve = val + strlen(val);
412 while (ve > val && (ve[-1] == '\n' || ve[-1] == ' ' || ve[-1] == '\t'))
413 ve--;
414 *ve = '\0';
415
416 /* Pure check — no filesystem side effects. */
417 if (!libnvmf_exclusion_entry_valid(ctx, val)) {
418 nvme_show_error("line %u: invalid entry: %s",nvme_show_message(1, "line %u: invalid entry: %s", lineno, val
)
419 lineno, val)nvme_show_message(1, "line %u: invalid entry: %s", lineno, val
)
;
420 errors++;
421 }
422 }
423 fclose(f);
424 return errors ? -EINVAL22 : 0;
425}
426
427/*
428 * Read a whole file into a newly allocated, NUL-terminated string (caller
429 * frees). Returns NULL on error (errno set).
430 */
431static char *read_file(const char *path)
432{
433 FILE *f = fopen(path, "r");
434 long sz;
435 char *buf;
436 size_t n;
437
438 if (!f
14.1
'f' is non-null
)
15
Taking false branch
439 return NULL((void*)0);
440 if (fseek(f, 0, SEEK_END2) < 0) {
16
Taking false branch
441 fclose(f);
442 return NULL((void*)0);
443 }
444 sz = ftell(f);
445 if (sz
16.1
'sz' is < 0
< 0) {
17
Taking true branch
446 fclose(f);
18
Assuming that 'fclose' is successful; 'errno' becomes undefined after the call
447 return NULL((void*)0);
448 }
449 rewind(f);
450
451 buf = malloc(sz + 1);
452 if (!buf) {
453 fclose(f);
454 return NULL((void*)0);
455 }
456 n = fread(buf, 1, sz, f);
457 if (ferror(f)) {
458 free(buf);
459 fclose(f);
460 return NULL((void*)0);
461 }
462 buf[n] = '\0';
463 fclose(f);
464 return buf;
465}
466
467/*
468 * excl_edit() - the "nvme exclusion edit" command.
469 *
470 * Opens an exclusion list in the user's $EDITOR for hand-editing, in the same
471 * spirit as "visudo" or "crontab -e". It is a read-modify-write cycle with
472 * optimistic concurrency:
473 *
474 * 1. Read the current list through libnvme (libnvmf_exclusion_read), which
475 * also returns an opaque @version token snapshotting the on-disk content.
476 * 2. Copy the text to a private scratch file under $TMPDIR (0600). The
477 * editor needs a real path, but we never let it touch the live file in
478 * the exclusion directory.
479 * 3. run_editor() forks $EDITOR on the scratch file and waits for it to exit.
480 * 4. Validate the result; if it has bad entries, offer to re-open the editor
481 * (the re_edit loop) so the user can fix them without losing work.
482 * 5. Write the edited text back by list name (libnvmf_exclusion_write),
483 * passing @version. If the list changed on disk in the meantime the
484 * write fails with -ESTALE and nothing is clobbered.
485 *
486 * Whenever the changes are not saved (validation given up on, or -ESTALE), the
487 * scratch file is deliberately left in place and its path printed, so the
488 * user's edits are recoverable. Requires root.
489 */
490static int excl_edit(int argc, char **argv, struct command *acmd,
491 struct plugin *plugin)
492{
493 const char *desc = "Interactively edit an NVMeoF exclusion list.";
494 const char *name_help = "exclusion list name (omit for the default list)";
495 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
496 __cleanup_free__attribute__((cleanup(shr_freep))) char *text = NULL((void*)0);
497 const char *tmpdir;
498 char tmp_path[PATH_MAX4096];
499 uint64_t version;
500 int fd, ret;
501
502 struct config {
503 char *name;
504 } cfg = { 0 };
505
506 NVME_ARGS(opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
507 OPT_STRING("name", 'N', "NAME", &cfg.name, name_help))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"name", 'N', "NAME", CFG_STRING, &cfg.name, 1, name_help
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"quiet", 0, ((void*)0), CFG_FLAG, &nvme_args.quiet, 0,
"suppress output messages, overwrites verbose mode", 0, }, {
"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary", 0, }, {"timeout", 0
, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, {"set-options", 0
, "KEY=VALUE", CFG_STRING, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
508
509 ret = parse_args(argc, argv, desc, opts);
510 if (ret)
1
Assuming 'ret' is 0
2
Taking false branch
511 return ret;
512
513 ret = require_root();
514 if (ret
2.1
'ret' is 0
)
3
Taking false branch
515 return ret;
516
517 ret = nvme_create_global_ctx(&ctx);
518 if (ret)
4
Assuming 'ret' is 0
5
Taking false branch
519 return ret;
520
521 /*
522 * Read the current list through libnvme: the plugin never needs to know
523 * where exclusion lists live. A missing list reads as empty so it can
524 * be created here. @version is an opaque token used at save time to
525 * detect a concurrent editor.
526 */
527 ret = libnvmf_exclusion_read(ctx, cfg.name, &text, &version);
528 if (ret) {
6
Assuming 'ret' is 0
7
Taking false branch
529 nvme_show_error("cannot read '%s': %s",nvme_show_message(1, "cannot read '%s': %s", cfg.name, libnvme_strerror
(-ret))
530 cfg.name, libnvme_strerror(-ret))nvme_show_message(1, "cannot read '%s': %s", cfg.name, libnvme_strerror
(-ret))
;
531 return ret;
532 }
533
534 /*
535 * Edit a private scratch copy under $TMPDIR -- NOT in the exclusion dir.
536 * The editor needs a real path, but the result is installed by name via
537 * libnvmf_exclusion_write(). mkstemp() creates the copy 0600.
538 */
539 tmpdir = getenv("TMPDIR");
540 if (!tmpdir
7.1
'tmpdir' is null
|| !*tmpdir)
541 tmpdir = "/tmp";
542 snprintf(tmp_path, sizeof(tmp_path), "%s/nvme-excl-%s.XXXXXX",
543 tmpdir, cfg.name);
544
545 fd = mkstemp(tmp_path);
546 if (fd
7.2
'fd' is >= 0
< 0) {
8
Taking false branch
547 ret = -errno(*__errno_location ());
548 nvme_show_error("cannot create temp file: %s",nvme_show_message(1, "cannot create temp file: %s", libnvme_strerror
(-ret))
549 libnvme_strerror(-ret))nvme_show_message(1, "cannot create temp file: %s", libnvme_strerror
(-ret))
;
550 return ret;
551 }
552 {
553 FILE *dst = fdopen(fd, "w");
554
555 if (!dst
8.1
'dst' is non-null
) {
9
Taking false branch
556 ret = -errno(*__errno_location ());
557 close(fd);
558 unlink(tmp_path);
559 return ret;
560 }
561 fputs(text, dst);
562 fclose(dst);
563 }
564
565re_edit:
566 ret = run_editor(tmp_path);
567 if (ret) {
10
Assuming 'ret' is 0
11
Taking false branch
568 nvme_show_error("editor failed")nvme_show_message(1, "editor failed");
569 unlink(tmp_path);
570 return ret;
571 }
572
573 ret = validate_conf_file(ctx, tmp_path);
574 if (ret) {
12
Assuming 'ret' is 0
13
Taking false branch
575 char ans[8];
576
577 nvme_show_error("File has errors. Re-edit? [y/N] ")nvme_show_message(1, "File has errors. Re-edit? [y/N] ");
578 fflush(stderrstderr);
579 if (fgets(ans, sizeof(ans), stdinstdin) &&
580 (ans[0] == 'y' || ans[0] == 'Y'))
581 goto re_edit;
582
583 nvme_show_error(nvme_show_message(1, "Discarding changes -- your edits are kept at %s\n"
, tmp_path)
584 "Discarding changes -- your edits are kept at %s\n",nvme_show_message(1, "Discarding changes -- your edits are kept at %s\n"
, tmp_path)
585 tmp_path)nvme_show_message(1, "Discarding changes -- your edits are kept at %s\n"
, tmp_path)
;
586 return -EINVAL22;
587 }
588
589 {
590 __cleanup_free__attribute__((cleanup(shr_freep))) char *edited = read_file(tmp_path);
14
Calling 'read_file'
19
Returning from 'read_file'
591
592 if (!edited
19.1
'edited' is null
) {
593 ret = errno(*__errno_location ()) ? -errno(*__errno_location ()) : -EIO5;
20
Taking true branch
21
An undefined value may be read from 'errno'
594 nvme_show_error("cannot read back temp file: %s",nvme_show_message(1, "cannot read back temp file: %s", libnvme_strerror
(-ret))
595 libnvme_strerror(-ret))nvme_show_message(1, "cannot read back temp file: %s", libnvme_strerror
(-ret))
;
596 unlink(tmp_path);
597 return ret;
598 }
599 ret = libnvmf_exclusion_write(ctx, cfg.name, edited, version);
600 }
601
602 if (ret == -ESTALE116) {
603 nvme_show_error(nvme_show_message(1, "the list changed on disk since you opened it; your "
"edits were NOT saved -- kept at %s\n", tmp_path)
604 "the list changed on disk since you opened it; your "nvme_show_message(1, "the list changed on disk since you opened it; your "
"edits were NOT saved -- kept at %s\n", tmp_path)
605 "edits were NOT saved -- kept at %s\n", tmp_path)nvme_show_message(1, "the list changed on disk since you opened it; your "
"edits were NOT saved -- kept at %s\n", tmp_path)
;
606 return ret;
607 }
608 if (ret) {
609 nvme_show_error("cannot save: %s -- your edits are kept at %s",nvme_show_message(1, "cannot save: %s -- your edits are kept at %s"
, libnvme_strerror(-ret), tmp_path)
610 libnvme_strerror(-ret), tmp_path)nvme_show_message(1, "cannot save: %s -- your edits are kept at %s"
, libnvme_strerror(-ret), tmp_path)
;
611 return ret;
612 }
613
614 unlink(tmp_path);
615 return 0;
616}