1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 #include <sys/debug.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <umem.h>
21 #include <inttypes.h>
22
23 #include <sys/krrp.h>
24 #include "libkrrp.h"
25 #include "libkrrp_impl.h"
26
27 /*
28 * The initial size of buffer that will be
29 * passed to kernel when a KRRP_IOCTL is called
30 */
31 #define LIBKRRP_INIT_BUF_SIZE 4096
32
33 static int krrp_ioctl_call(libkrrp_handle_t *, krrp_ioctl_cmd_t,
34 krrp_ioctl_data_t *);
35 static int krrp_ioctl_alloc_data(libkrrp_handle_t *,
36 krrp_ioctl_data_t **, size_t);
37 static void krrp_ioctl_free_data(krrp_ioctl_data_t *);
38 static int krrp_ioctl_data_from_nvl(libkrrp_handle_t *,
39 krrp_ioctl_data_t **, nvlist_t *, size_t);
40 static int krrp_ioctl_data_to_nvl(libkrrp_handle_t *,
41 krrp_ioctl_data_t *, nvlist_t **);
42
43 int
44 krrp_ioctl_perform(libkrrp_handle_t *hdl, krrp_ioctl_cmd_t cmd,
45 nvlist_t *in_params, nvlist_t **out_params)
46 {
47 int rc;
48 size_t buf_sz, attempt = 0;
49 krrp_ioctl_data_t *ioctl_data = NULL;
50 nvlist_t *result_nvl = NULL;
51
52 VERIFY(hdl != NULL);
53
54 hdl->libkrrp_last_cmd = cmd;
55
56 /*
57 * To get result from kernel we pass to it a buffer.
58 * If kernel has more data than the size of the buffer
59 * then it returns ENOSPC. In this case IOCTL will be
60 * called no more than 10 times and on each iteration
61 * the size of the buffer will be doubled.
62 */
63 buf_sz = LIBKRRP_INIT_BUF_SIZE;
64 for (;;) {
65 rc = krrp_ioctl_data_from_nvl(hdl,
66 &ioctl_data, in_params, buf_sz);
67 if (rc != 0)
68 return (-1);
69
70 rc = krrp_ioctl_call(hdl, cmd, ioctl_data);
71 if (rc != 0) {
72 const libkrrp_error_t *err = libkrrp_error(hdl);
73 if (err->unix_errno == ENOSPC) {
74 if (++attempt > 10)
75 goto fini;
76
77 krrp_ioctl_free_data(ioctl_data);
78 ioctl_data = NULL;
79 buf_sz *= 2;
80 continue;
81 }
82
83 goto fini;
84 }
85
86 break;
87 }
88
89 VERIFY0(rc);
90
91 rc = krrp_ioctl_data_to_nvl(hdl, ioctl_data, &result_nvl);
92 if (rc != 0)
93 goto fini;
94
95 if (ioctl_data->out_flags & KRRP_IOCTL_FLAG_ERROR) {
96 VERIFY0(libkrrp_error_from_nvl(result_nvl,
97 &hdl->libkrrp_error));
98 rc = -1;
99 fnvlist_free(result_nvl);
100 } else if (ioctl_data->out_flags & KRRP_IOCTL_FLAG_RESULT) {
101 VERIFY(out_params != NULL && *out_params == NULL);
102 *out_params = result_nvl;
103 }
104
105 fini:
106 krrp_ioctl_free_data(ioctl_data);
107 return (rc);
108 }
109
110 static int
111 krrp_ioctl_call(libkrrp_handle_t *hdl, krrp_ioctl_cmd_t cmd,
112 krrp_ioctl_data_t *ioctl_data)
113 {
114 int rc;
115
116 rc = ioctl(hdl->libkrrp_fd, cmd, ioctl_data);
117
118 if (rc != 0) {
119 switch (errno) {
120 case ENOTACTIVE:
121 libkrrp_error_set(&hdl->libkrrp_error,
122 LIBKRRP_ERRNO_SVCNOTACTIVE, 0, 0);
123 break;
124 case EALREADY:
125 libkrrp_error_set(&hdl->libkrrp_error,
126 LIBKRRP_ERRNO_SVCACTIVE, 0, 0);
127 break;
128 case ENOTSUP:
129 libkrrp_error_set(&hdl->libkrrp_error,
130 LIBKRRP_ERRNO_NOTSUP, 0, 0);
131 break;
132 default:
133 libkrrp_error_set(&hdl->libkrrp_error,
134 LIBKRRP_ERRNO_IOCTLFAIL, errno, 0);
135 }
136 return (-1);
137 }
138
139 return (0);
140 }
141
142 static int
143 krrp_ioctl_alloc_data(libkrrp_handle_t *hdl, krrp_ioctl_data_t **res_ioctl_data,
144 size_t buf_size)
145 {
146 krrp_ioctl_data_t *ioctl_data;
147
148 VERIFY(buf_size <= UINT32_MAX);
149
150 ioctl_data = umem_alloc(sizeof (krrp_ioctl_data_t) + buf_size,
151 UMEM_DEFAULT);
152
153 if (ioctl_data == NULL) {
154 libkrrp_error_set(&hdl->libkrrp_error, LIBKRRP_ERRNO_NOMEM,
155 errno, 0);
156 return (-1);
157 }
158
159 (void) memset(ioctl_data, 0, sizeof (krrp_ioctl_data_t));
160
161 ioctl_data->buf_size = buf_size;
162
163 *res_ioctl_data = ioctl_data;
164
165 return (0);
166 }
167
168 static void
169 krrp_ioctl_free_data(krrp_ioctl_data_t *ioctl_data)
170 {
171 umem_free(ioctl_data, sizeof (krrp_ioctl_data_t) +
172 ioctl_data->buf_size);
173 }
174
175 static int
176 krrp_ioctl_data_from_nvl(libkrrp_handle_t *hdl,
177 krrp_ioctl_data_t **res_ioctl_data, nvlist_t *params,
178 size_t add_size)
179 {
180 int rc;
181 size_t packed_size = 0;
182 krrp_ioctl_data_t *ioctl_data = NULL;
183
184 if (params != NULL)
185 packed_size = fnvlist_size(params);
186
187 rc = krrp_ioctl_alloc_data(hdl, &ioctl_data,
188 packed_size + add_size);
189 if (rc != 0)
190 return (-1);
191
192 if (params != NULL) {
193 char *buf = ioctl_data->buf;
194 VERIFY0(nvlist_pack(params, &buf,
195 &packed_size, NV_ENCODE_NATIVE, 0));
196 ioctl_data->data_size = packed_size;
197 }
198
199 *res_ioctl_data = ioctl_data;
200
201 return (0);
202 }
203
204 static int
205 krrp_ioctl_data_to_nvl(libkrrp_handle_t *hdl, krrp_ioctl_data_t *ioctl_data,
206 nvlist_t **result_nvl)
207 {
208 nvlist_t *nvl = NULL;
209 int rc;
210
211 if (!(ioctl_data->out_flags & KRRP_IOCTL_FLAG_ERROR) &&
212 !(ioctl_data->out_flags & KRRP_IOCTL_FLAG_RESULT))
213 return (0);
214
215 VERIFY(*result_nvl == NULL && result_nvl != NULL);
216 VERIFY(ioctl_data->data_size != 0);
217
218 rc = nvlist_unpack(ioctl_data->buf,
219 ioctl_data->data_size, &nvl, 0);
220
221 if (rc == ENOMEM) {
222 libkrrp_error_set(&hdl->libkrrp_error,
223 LIBKRRP_ERRNO_NOMEM, rc, 0);
224 } else if (rc != 0) {
225 libkrrp_error_set(&hdl->libkrrp_error,
226 LIBKRRP_ERRNO_IOCTLDATAFAIL, rc, 0);
227 return (-1);
228 }
229
230 *result_nvl = nvl;
231 return (0);
232 }