xref: /trueos/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c (revision 24296fa9c3fafa12e63adf716c279bd90f416649)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2013 Xin Li <delphij@FreeBSD.org>. All rights reserved.
23  * Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
24  * Portions Copyright 2005, 2010, Oracle and/or its affiliates.
25  * All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/cred.h>
32 #include <sys/dmu.h>
33 #include <sys/zio.h>
34 #include <sys/nvpair.h>
35 #include <sys/dsl_deleg.h>
36 #include <sys/zfs_ioctl.h>
37 #include "zfs_namecheck.h"
38 #include "zfs_ioctl_compat.h"
39 
40 static int zfs_version_ioctl = ZFS_IOCVER_CURRENT;
41 SYSCTL_DECL(_vfs_zfs_version);
42 SYSCTL_INT(_vfs_zfs_version, OID_AUTO, ioctl, CTLFLAG_RD, &zfs_version_ioctl,
43     0, "ZFS_IOCTL_VERSION");
44 
45 /*
46  * FreeBSD zfs_cmd compatibility with older binaries
47  * appropriately remap/extend the zfs_cmd_t structure
48  */
49 void
zfs_cmd_compat_get(zfs_cmd_t * zc,caddr_t addr,const int cflag)50 zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
51 {
52 	zfs_cmd_v15_t *zc_c;
53 	zfs_cmd_v28_t *zc28_c;
54 	zfs_cmd_deadman_t *zcdm_c;
55 	zfs_cmd_zcmd_t *zcmd_c;
56 
57 	switch (cflag) {
58 	case ZFS_CMD_COMPAT_ZCMD:
59 		zcmd_c = (void *)addr;
60 		/* zc */
61 		strlcpy(zc->zc_name, zcmd_c->zc_name, MAXPATHLEN);
62 		strlcpy(zc->zc_value, zcmd_c->zc_value, MAXPATHLEN * 2);
63 		strlcpy(zc->zc_string, zcmd_c->zc_string, MAXPATHLEN);
64 
65 #define ZCMD_COPY(field) zc->field = zcmd_c->field
66 		ZCMD_COPY(zc_nvlist_src);
67 		ZCMD_COPY(zc_nvlist_src_size);
68 		ZCMD_COPY(zc_nvlist_dst);
69 		ZCMD_COPY(zc_nvlist_dst_size);
70 		ZCMD_COPY(zc_nvlist_dst_filled);
71 		ZCMD_COPY(zc_pad2);
72 		ZCMD_COPY(zc_history);
73 		ZCMD_COPY(zc_guid);
74 		ZCMD_COPY(zc_nvlist_conf);
75 		ZCMD_COPY(zc_nvlist_conf_size);
76 		ZCMD_COPY(zc_cookie);
77 		ZCMD_COPY(zc_objset_type);
78 		ZCMD_COPY(zc_perm_action);
79 		ZCMD_COPY(zc_history_len);
80 		ZCMD_COPY(zc_history_offset);
81 		ZCMD_COPY(zc_obj);
82 		ZCMD_COPY(zc_iflags);
83 		ZCMD_COPY(zc_share);
84 		ZCMD_COPY(zc_jailid);
85 		ZCMD_COPY(zc_objset_stats);
86 
87 		/*
88 		 * zc_begin_record, zc_inject_record didn't change in embedeed-data
89 		 * block pointers
90 		 *
91 		 * TODO: CTASSERT?
92 		 */
93 		ZCMD_COPY(zc_begin_record);
94 		ZCMD_COPY(zc_inject_record);
95 
96 		/* boolean_t -> uint32_t */
97 		zc->zc_defer_destroy = (uint32_t)(zcmd_c->zc_defer_destroy);
98 		zc->zc_flags = 0;
99 
100 		ZCMD_COPY(zc_action_handle);
101 		ZCMD_COPY(zc_cleanup_fd);
102 		ZCMD_COPY(zc_simple);
103 		bcopy(zcmd_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad));
104 		ZCMD_COPY(zc_sendobj);
105 		ZCMD_COPY(zc_fromobj);
106 		ZCMD_COPY(zc_createtxg);
107 		ZCMD_COPY(zc_stat);
108 #undef ZCMD_COPY
109 
110 		break;
111 
112 	case ZFS_CMD_COMPAT_DEADMAN:
113 		zcdm_c = (void *)addr;
114 		/* zc */
115 		strlcpy(zc->zc_name, zcdm_c->zc_name, MAXPATHLEN);
116 		strlcpy(zc->zc_value, zcdm_c->zc_value, MAXPATHLEN * 2);
117 		strlcpy(zc->zc_string, zcdm_c->zc_string, MAXPATHLEN);
118 		zc->zc_guid = zcdm_c->zc_guid;
119 		zc->zc_nvlist_conf = zcdm_c->zc_nvlist_conf;
120 		zc->zc_nvlist_conf_size = zcdm_c->zc_nvlist_conf_size;
121 		zc->zc_nvlist_src = zcdm_c->zc_nvlist_src;
122 		zc->zc_nvlist_src_size = zcdm_c->zc_nvlist_src_size;
123 		zc->zc_nvlist_dst = zcdm_c->zc_nvlist_dst;
124 		zc->zc_nvlist_dst_size = zcdm_c->zc_nvlist_dst_size;
125 		zc->zc_cookie = zcdm_c->zc_cookie;
126 		zc->zc_objset_type = zcdm_c->zc_objset_type;
127 		zc->zc_perm_action = zcdm_c->zc_perm_action;
128 		zc->zc_history = zcdm_c->zc_history;
129 		zc->zc_history_len = zcdm_c->zc_history_len;
130 		zc->zc_history_offset = zcdm_c->zc_history_offset;
131 		zc->zc_obj = zcdm_c->zc_obj;
132 		zc->zc_iflags = zcdm_c->zc_iflags;
133 		zc->zc_share = zcdm_c->zc_share;
134 		zc->zc_jailid = zcdm_c->zc_jailid;
135 		zc->zc_objset_stats = zcdm_c->zc_objset_stats;
136 		zc->zc_begin_record = zcdm_c->zc_begin_record;
137 		zc->zc_defer_destroy = zcdm_c->zc_defer_destroy;
138 		(void)zcdm_c->zc_temphold;
139 		zc->zc_action_handle = zcdm_c->zc_action_handle;
140 		zc->zc_cleanup_fd = zcdm_c->zc_cleanup_fd;
141 		zc->zc_simple = zcdm_c->zc_simple;
142 		bcopy(zcdm_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad));
143 		zc->zc_sendobj = zcdm_c->zc_sendobj;
144 		zc->zc_fromobj = zcdm_c->zc_fromobj;
145 		zc->zc_createtxg = zcdm_c->zc_createtxg;
146 		zc->zc_stat = zcdm_c->zc_stat;
147 
148 		/* zc_inject_record doesn't change in libzfs_core */
149 		zcdm_c->zc_inject_record = zc->zc_inject_record;
150 
151 		/* we always assume zc_nvlist_dst_filled is true */
152 		zc->zc_nvlist_dst_filled = B_TRUE;
153 		break;
154 
155 	case ZFS_CMD_COMPAT_V28:
156 		zc28_c = (void *)addr;
157 
158 		/* zc */
159 		strlcpy(zc->zc_name, zc28_c->zc_name, MAXPATHLEN);
160 		strlcpy(zc->zc_value, zc28_c->zc_value, MAXPATHLEN * 2);
161 		strlcpy(zc->zc_string, zc28_c->zc_string, MAXPATHLEN);
162 		zc->zc_guid = zc28_c->zc_guid;
163 		zc->zc_nvlist_conf = zc28_c->zc_nvlist_conf;
164 		zc->zc_nvlist_conf_size = zc28_c->zc_nvlist_conf_size;
165 		zc->zc_nvlist_src = zc28_c->zc_nvlist_src;
166 		zc->zc_nvlist_src_size = zc28_c->zc_nvlist_src_size;
167 		zc->zc_nvlist_dst = zc28_c->zc_nvlist_dst;
168 		zc->zc_nvlist_dst_size = zc28_c->zc_nvlist_dst_size;
169 		zc->zc_cookie = zc28_c->zc_cookie;
170 		zc->zc_objset_type = zc28_c->zc_objset_type;
171 		zc->zc_perm_action = zc28_c->zc_perm_action;
172 		zc->zc_history = zc28_c->zc_history;
173 		zc->zc_history_len = zc28_c->zc_history_len;
174 		zc->zc_history_offset = zc28_c->zc_history_offset;
175 		zc->zc_obj = zc28_c->zc_obj;
176 		zc->zc_iflags = zc28_c->zc_iflags;
177 		zc->zc_share = zc28_c->zc_share;
178 		zc->zc_jailid = zc28_c->zc_jailid;
179 		zc->zc_objset_stats = zc28_c->zc_objset_stats;
180 		zc->zc_begin_record = zc28_c->zc_begin_record;
181 		zc->zc_defer_destroy = zc28_c->zc_defer_destroy;
182 		(void)zc28_c->zc_temphold;
183 		zc->zc_action_handle = zc28_c->zc_action_handle;
184 		zc->zc_cleanup_fd = zc28_c->zc_cleanup_fd;
185 		zc->zc_simple = zc28_c->zc_simple;
186 		bcopy(zc28_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad));
187 		zc->zc_sendobj = zc28_c->zc_sendobj;
188 		zc->zc_fromobj = zc28_c->zc_fromobj;
189 		zc->zc_createtxg = zc28_c->zc_createtxg;
190 		zc->zc_stat = zc28_c->zc_stat;
191 
192 		/* zc->zc_inject_record */
193 		zc->zc_inject_record.zi_objset =
194 		    zc28_c->zc_inject_record.zi_objset;
195 		zc->zc_inject_record.zi_object =
196 		    zc28_c->zc_inject_record.zi_object;
197 		zc->zc_inject_record.zi_start =
198 		    zc28_c->zc_inject_record.zi_start;
199 		zc->zc_inject_record.zi_end =
200 		    zc28_c->zc_inject_record.zi_end;
201 		zc->zc_inject_record.zi_guid =
202 		    zc28_c->zc_inject_record.zi_guid;
203 		zc->zc_inject_record.zi_level =
204 		    zc28_c->zc_inject_record.zi_level;
205 		zc->zc_inject_record.zi_error =
206 		    zc28_c->zc_inject_record.zi_error;
207 		zc->zc_inject_record.zi_type =
208 		    zc28_c->zc_inject_record.zi_type;
209 		zc->zc_inject_record.zi_freq =
210 		    zc28_c->zc_inject_record.zi_freq;
211 		zc->zc_inject_record.zi_failfast =
212 		    zc28_c->zc_inject_record.zi_failfast;
213 		strlcpy(zc->zc_inject_record.zi_func,
214 		    zc28_c->zc_inject_record.zi_func, MAXNAMELEN);
215 		zc->zc_inject_record.zi_iotype =
216 		    zc28_c->zc_inject_record.zi_iotype;
217 		zc->zc_inject_record.zi_duration =
218 		    zc28_c->zc_inject_record.zi_duration;
219 		zc->zc_inject_record.zi_timer =
220 		    zc28_c->zc_inject_record.zi_timer;
221 		zc->zc_inject_record.zi_cmd = ZINJECT_UNINITIALIZED;
222 		zc->zc_inject_record.zi_pad = 0;
223 		break;
224 
225 	case ZFS_CMD_COMPAT_V15:
226 		zc_c = (void *)addr;
227 
228 		/* zc */
229 		strlcpy(zc->zc_name, zc_c->zc_name, MAXPATHLEN);
230 		strlcpy(zc->zc_value, zc_c->zc_value, MAXPATHLEN);
231 		strlcpy(zc->zc_string, zc_c->zc_string, MAXPATHLEN);
232 		zc->zc_guid = zc_c->zc_guid;
233 		zc->zc_nvlist_conf = zc_c->zc_nvlist_conf;
234 		zc->zc_nvlist_conf_size = zc_c->zc_nvlist_conf_size;
235 		zc->zc_nvlist_src = zc_c->zc_nvlist_src;
236 		zc->zc_nvlist_src_size = zc_c->zc_nvlist_src_size;
237 		zc->zc_nvlist_dst = zc_c->zc_nvlist_dst;
238 		zc->zc_nvlist_dst_size = zc_c->zc_nvlist_dst_size;
239 		zc->zc_cookie = zc_c->zc_cookie;
240 		zc->zc_objset_type = zc_c->zc_objset_type;
241 		zc->zc_perm_action = zc_c->zc_perm_action;
242 		zc->zc_history = zc_c->zc_history;
243 		zc->zc_history_len = zc_c->zc_history_len;
244 		zc->zc_history_offset = zc_c->zc_history_offset;
245 		zc->zc_obj = zc_c->zc_obj;
246 		zc->zc_share = zc_c->zc_share;
247 		zc->zc_jailid = zc_c->zc_jailid;
248 		zc->zc_objset_stats = zc_c->zc_objset_stats;
249 		zc->zc_begin_record = zc_c->zc_begin_record;
250 
251 		/* zc->zc_inject_record */
252 		zc->zc_inject_record.zi_objset =
253 		    zc_c->zc_inject_record.zi_objset;
254 		zc->zc_inject_record.zi_object =
255 		    zc_c->zc_inject_record.zi_object;
256 		zc->zc_inject_record.zi_start =
257 		    zc_c->zc_inject_record.zi_start;
258 		zc->zc_inject_record.zi_end =
259 		    zc_c->zc_inject_record.zi_end;
260 		zc->zc_inject_record.zi_guid =
261 		    zc_c->zc_inject_record.zi_guid;
262 		zc->zc_inject_record.zi_level =
263 		    zc_c->zc_inject_record.zi_level;
264 		zc->zc_inject_record.zi_error =
265 		    zc_c->zc_inject_record.zi_error;
266 		zc->zc_inject_record.zi_type =
267 		    zc_c->zc_inject_record.zi_type;
268 		zc->zc_inject_record.zi_freq =
269 		    zc_c->zc_inject_record.zi_freq;
270 		zc->zc_inject_record.zi_failfast =
271 		    zc_c->zc_inject_record.zi_failfast;
272 		break;
273 	}
274 }
275 
276 void
zfs_cmd_compat_put(zfs_cmd_t * zc,caddr_t addr,const int request,const int cflag)277 zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request,
278     const int cflag)
279 {
280 	zfs_cmd_v15_t *zc_c;
281 	zfs_cmd_v28_t *zc28_c;
282 	zfs_cmd_deadman_t *zcdm_c;
283 	zfs_cmd_zcmd_t *zcmd_c;
284 
285 	switch (cflag) {
286 	case ZFS_CMD_COMPAT_ZCMD:
287 		zcmd_c = (void *)addr;
288 		/* zc */
289 		strlcpy(zcmd_c->zc_name, zc->zc_name, MAXPATHLEN);
290 		strlcpy(zcmd_c->zc_value, zc->zc_value, MAXPATHLEN * 2);
291 		strlcpy(zcmd_c->zc_string, zc->zc_string, MAXPATHLEN);
292 
293 #define ZCMD_COPY(field) zcmd_c->field = zc->field
294 		ZCMD_COPY(zc_nvlist_src);
295 		ZCMD_COPY(zc_nvlist_src_size);
296 		ZCMD_COPY(zc_nvlist_dst);
297 		ZCMD_COPY(zc_nvlist_dst_size);
298 		ZCMD_COPY(zc_nvlist_dst_filled);
299 		ZCMD_COPY(zc_pad2);
300 		ZCMD_COPY(zc_history);
301 		ZCMD_COPY(zc_guid);
302 		ZCMD_COPY(zc_nvlist_conf);
303 		ZCMD_COPY(zc_nvlist_conf_size);
304 		ZCMD_COPY(zc_cookie);
305 		ZCMD_COPY(zc_objset_type);
306 		ZCMD_COPY(zc_perm_action);
307 		ZCMD_COPY(zc_history_len);
308 		ZCMD_COPY(zc_history_offset);
309 		ZCMD_COPY(zc_obj);
310 		ZCMD_COPY(zc_iflags);
311 		ZCMD_COPY(zc_share);
312 		ZCMD_COPY(zc_jailid);
313 		ZCMD_COPY(zc_objset_stats);
314 
315 		/*
316 		 * zc_begin_record, zc_inject_record didn't change in embedeed-data
317 		 * block pointers
318 		 *
319 		 * TODO: CTASSERT?
320 		 */
321 		ZCMD_COPY(zc_begin_record);
322 		ZCMD_COPY(zc_inject_record);
323 
324 		/* boolean_t -> uint32_t */
325 		zcmd_c->zc_defer_destroy = (uint32_t)(zc->zc_defer_destroy);
326 		zcmd_c->zc_temphold = 0;
327 
328 		ZCMD_COPY(zc_action_handle);
329 		ZCMD_COPY(zc_cleanup_fd);
330 		ZCMD_COPY(zc_simple);
331 		bcopy(zc->zc_pad, zcmd_c->zc_pad, sizeof(zcmd_c->zc_pad));
332 		ZCMD_COPY(zc_sendobj);
333 		ZCMD_COPY(zc_fromobj);
334 		ZCMD_COPY(zc_createtxg);
335 		ZCMD_COPY(zc_stat);
336 #undef ZCMD_COPY
337 
338 		break;
339 
340 	case ZFS_CMD_COMPAT_DEADMAN:
341 		zcdm_c = (void *)addr;
342 
343 		strlcpy(zcdm_c->zc_name, zc->zc_name, MAXPATHLEN);
344 		strlcpy(zcdm_c->zc_value, zc->zc_value, MAXPATHLEN * 2);
345 		strlcpy(zcdm_c->zc_string, zc->zc_string, MAXPATHLEN);
346 		zcdm_c->zc_guid = zc->zc_guid;
347 		zcdm_c->zc_nvlist_conf = zc->zc_nvlist_conf;
348 		zcdm_c->zc_nvlist_conf_size = zc->zc_nvlist_conf_size;
349 		zcdm_c->zc_nvlist_src = zc->zc_nvlist_src;
350 		zcdm_c->zc_nvlist_src_size = zc->zc_nvlist_src_size;
351 		zcdm_c->zc_nvlist_dst = zc->zc_nvlist_dst;
352 		zcdm_c->zc_nvlist_dst_size = zc->zc_nvlist_dst_size;
353 		zcdm_c->zc_cookie = zc->zc_cookie;
354 		zcdm_c->zc_objset_type = zc->zc_objset_type;
355 		zcdm_c->zc_perm_action = zc->zc_perm_action;
356 		zcdm_c->zc_history = zc->zc_history;
357 		zcdm_c->zc_history_len = zc->zc_history_len;
358 		zcdm_c->zc_history_offset = zc->zc_history_offset;
359 		zcdm_c->zc_obj = zc->zc_obj;
360 		zcdm_c->zc_iflags = zc->zc_iflags;
361 		zcdm_c->zc_share = zc->zc_share;
362 		zcdm_c->zc_jailid = zc->zc_jailid;
363 		zcdm_c->zc_objset_stats = zc->zc_objset_stats;
364 		zcdm_c->zc_begin_record = zc->zc_begin_record;
365 		zcdm_c->zc_defer_destroy = zc->zc_defer_destroy;
366 		zcdm_c->zc_temphold = 0;
367 		zcdm_c->zc_action_handle = zc->zc_action_handle;
368 		zcdm_c->zc_cleanup_fd = zc->zc_cleanup_fd;
369 		zcdm_c->zc_simple = zc->zc_simple;
370 		bcopy(zc->zc_pad, zcdm_c->zc_pad, sizeof(zcdm_c->zc_pad));
371 		zcdm_c->zc_sendobj = zc->zc_sendobj;
372 		zcdm_c->zc_fromobj = zc->zc_fromobj;
373 		zcdm_c->zc_createtxg = zc->zc_createtxg;
374 		zcdm_c->zc_stat = zc->zc_stat;
375 
376 		/* zc_inject_record doesn't change in libzfs_core */
377 		zc->zc_inject_record = zcdm_c->zc_inject_record;
378 #ifndef _KERNEL
379 		if (request == ZFS_IOC_RECV)
380 			strlcpy(zcdm_c->zc_top_ds,
381 			    zc->zc_value + strlen(zc->zc_value) + 1,
382 			    (MAXPATHLEN * 2) - strlen(zc->zc_value) - 1);
383 #endif
384 		break;
385 
386 	case ZFS_CMD_COMPAT_V28:
387 		zc28_c = (void *)addr;
388 
389 		strlcpy(zc28_c->zc_name, zc->zc_name, MAXPATHLEN);
390 		strlcpy(zc28_c->zc_value, zc->zc_value, MAXPATHLEN * 2);
391 		strlcpy(zc28_c->zc_string, zc->zc_string, MAXPATHLEN);
392 		zc28_c->zc_guid = zc->zc_guid;
393 		zc28_c->zc_nvlist_conf = zc->zc_nvlist_conf;
394 		zc28_c->zc_nvlist_conf_size = zc->zc_nvlist_conf_size;
395 		zc28_c->zc_nvlist_src = zc->zc_nvlist_src;
396 		zc28_c->zc_nvlist_src_size = zc->zc_nvlist_src_size;
397 		zc28_c->zc_nvlist_dst = zc->zc_nvlist_dst;
398 		zc28_c->zc_nvlist_dst_size = zc->zc_nvlist_dst_size;
399 		zc28_c->zc_cookie = zc->zc_cookie;
400 		zc28_c->zc_objset_type = zc->zc_objset_type;
401 		zc28_c->zc_perm_action = zc->zc_perm_action;
402 		zc28_c->zc_history = zc->zc_history;
403 		zc28_c->zc_history_len = zc->zc_history_len;
404 		zc28_c->zc_history_offset = zc->zc_history_offset;
405 		zc28_c->zc_obj = zc->zc_obj;
406 		zc28_c->zc_iflags = zc->zc_iflags;
407 		zc28_c->zc_share = zc->zc_share;
408 		zc28_c->zc_jailid = zc->zc_jailid;
409 		zc28_c->zc_objset_stats = zc->zc_objset_stats;
410 		zc28_c->zc_begin_record = zc->zc_begin_record;
411 		zc28_c->zc_defer_destroy = zc->zc_defer_destroy;
412 		zc28_c->zc_temphold = 0;
413 		zc28_c->zc_action_handle = zc->zc_action_handle;
414 		zc28_c->zc_cleanup_fd = zc->zc_cleanup_fd;
415 		zc28_c->zc_simple = zc->zc_simple;
416 		bcopy(zc->zc_pad, zc28_c->zc_pad, sizeof(zc28_c->zc_pad));
417 		zc28_c->zc_sendobj = zc->zc_sendobj;
418 		zc28_c->zc_fromobj = zc->zc_fromobj;
419 		zc28_c->zc_createtxg = zc->zc_createtxg;
420 		zc28_c->zc_stat = zc->zc_stat;
421 #ifndef _KERNEL
422 		if (request == ZFS_IOC_RECV)
423 			strlcpy(zc28_c->zc_top_ds,
424 			    zc->zc_value + strlen(zc->zc_value) + 1,
425 			    MAXPATHLEN * 2 - strlen(zc->zc_value) - 1);
426 #endif
427 		/* zc_inject_record */
428 		zc28_c->zc_inject_record.zi_objset =
429 		    zc->zc_inject_record.zi_objset;
430 		zc28_c->zc_inject_record.zi_object =
431 		    zc->zc_inject_record.zi_object;
432 		zc28_c->zc_inject_record.zi_start =
433 		    zc->zc_inject_record.zi_start;
434 		zc28_c->zc_inject_record.zi_end =
435 		    zc->zc_inject_record.zi_end;
436 		zc28_c->zc_inject_record.zi_guid =
437 		    zc->zc_inject_record.zi_guid;
438 		zc28_c->zc_inject_record.zi_level =
439 		    zc->zc_inject_record.zi_level;
440 		zc28_c->zc_inject_record.zi_error =
441 		    zc->zc_inject_record.zi_error;
442 		zc28_c->zc_inject_record.zi_type =
443 		    zc->zc_inject_record.zi_type;
444 		zc28_c->zc_inject_record.zi_freq =
445 		    zc->zc_inject_record.zi_freq;
446 		zc28_c->zc_inject_record.zi_failfast =
447 		    zc->zc_inject_record.zi_failfast;
448 		strlcpy(zc28_c->zc_inject_record.zi_func,
449 		    zc->zc_inject_record.zi_func, MAXNAMELEN);
450 		zc28_c->zc_inject_record.zi_iotype =
451 		    zc->zc_inject_record.zi_iotype;
452 		zc28_c->zc_inject_record.zi_duration =
453 		    zc->zc_inject_record.zi_duration;
454 		zc28_c->zc_inject_record.zi_timer =
455 		    zc->zc_inject_record.zi_timer;
456 		break;
457 
458 	case ZFS_CMD_COMPAT_V15:
459 		zc_c = (void *)addr;
460 
461 		/* zc */
462 		strlcpy(zc_c->zc_name, zc->zc_name, MAXPATHLEN);
463 		strlcpy(zc_c->zc_value, zc->zc_value, MAXPATHLEN);
464 		strlcpy(zc_c->zc_string, zc->zc_string, MAXPATHLEN);
465 		zc_c->zc_guid = zc->zc_guid;
466 		zc_c->zc_nvlist_conf = zc->zc_nvlist_conf;
467 		zc_c->zc_nvlist_conf_size = zc->zc_nvlist_conf_size;
468 		zc_c->zc_nvlist_src = zc->zc_nvlist_src;
469 		zc_c->zc_nvlist_src_size = zc->zc_nvlist_src_size;
470 		zc_c->zc_nvlist_dst = zc->zc_nvlist_dst;
471 		zc_c->zc_nvlist_dst_size = zc->zc_nvlist_dst_size;
472 		zc_c->zc_cookie = zc->zc_cookie;
473 		zc_c->zc_objset_type = zc->zc_objset_type;
474 		zc_c->zc_perm_action = zc->zc_perm_action;
475 		zc_c->zc_history = zc->zc_history;
476 		zc_c->zc_history_len = zc->zc_history_len;
477 		zc_c->zc_history_offset = zc->zc_history_offset;
478 		zc_c->zc_obj = zc->zc_obj;
479 		zc_c->zc_share = zc->zc_share;
480 		zc_c->zc_jailid = zc->zc_jailid;
481 		zc_c->zc_objset_stats = zc->zc_objset_stats;
482 		zc_c->zc_begin_record = zc->zc_begin_record;
483 
484 		/* zc_inject_record */
485 		zc_c->zc_inject_record.zi_objset =
486 		    zc->zc_inject_record.zi_objset;
487 		zc_c->zc_inject_record.zi_object =
488 		    zc->zc_inject_record.zi_object;
489 		zc_c->zc_inject_record.zi_start =
490 		    zc->zc_inject_record.zi_start;
491 		zc_c->zc_inject_record.zi_end =
492 		    zc->zc_inject_record.zi_end;
493 		zc_c->zc_inject_record.zi_guid =
494 		    zc->zc_inject_record.zi_guid;
495 		zc_c->zc_inject_record.zi_level =
496 		    zc->zc_inject_record.zi_level;
497 		zc_c->zc_inject_record.zi_error =
498 		    zc->zc_inject_record.zi_error;
499 		zc_c->zc_inject_record.zi_type =
500 		    zc->zc_inject_record.zi_type;
501 		zc_c->zc_inject_record.zi_freq =
502 		    zc->zc_inject_record.zi_freq;
503 		zc_c->zc_inject_record.zi_failfast =
504 		    zc->zc_inject_record.zi_failfast;
505 
506 		break;
507 	}
508 }
509 
510 static int
zfs_ioctl_compat_get_nvlist(uint64_t nvl,size_t size,int iflag,nvlist_t ** nvp)511 zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag,
512     nvlist_t **nvp)
513 {
514 	char *packed;
515 	int error;
516 	nvlist_t *list = NULL;
517 
518 	/*
519 	 * Read in and unpack the user-supplied nvlist.
520 	 */
521 	if (size == 0)
522 		return (EINVAL);
523 
524 #ifdef _KERNEL
525 	packed = kmem_alloc(size, KM_SLEEP);
526 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
527 	    iflag)) != 0) {
528 		kmem_free(packed, size);
529 		return (error);
530 	}
531 #else
532 	packed = (void *)(uintptr_t)nvl;
533 #endif
534 
535 	error = nvlist_unpack(packed, size, &list, 0);
536 
537 #ifdef _KERNEL
538 	kmem_free(packed, size);
539 #endif
540 
541 	if (error != 0)
542 		return (error);
543 
544 	*nvp = list;
545 	return (0);
546 }
547 
548 static int
zfs_ioctl_compat_put_nvlist(zfs_cmd_t * zc,nvlist_t * nvl)549 zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
550 {
551 	char *packed = NULL;
552 	int error = 0;
553 	size_t size;
554 
555 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
556 
557 #ifdef _KERNEL
558 	packed = kmem_alloc(size, KM_SLEEP);
559 	VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
560 	    KM_SLEEP) == 0);
561 
562 	if (ddi_copyout(packed,
563 	    (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0)
564 		error = EFAULT;
565 	kmem_free(packed, size);
566 #else
567 	packed = (void *)(uintptr_t)zc->zc_nvlist_dst;
568 	VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
569 	    0) == 0);
570 #endif
571 
572 	zc->zc_nvlist_dst_size = size;
573 	return (error);
574 }
575 
576 static void
zfs_ioctl_compat_fix_stats_nvlist(nvlist_t * nvl)577 zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl)
578 {
579 	nvlist_t **child;
580 	nvlist_t *nvroot = NULL;
581 	vdev_stat_t *vs;
582 	uint_t c, children, nelem;
583 
584 	if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
585 	    &child, &children) == 0) {
586 		for (c = 0; c < children; c++) {
587 			zfs_ioctl_compat_fix_stats_nvlist(child[c]);
588 		}
589 	}
590 
591 	if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE,
592 	    &nvroot) == 0)
593 		zfs_ioctl_compat_fix_stats_nvlist(nvroot);
594 #ifdef _KERNEL
595 	if ((nvlist_lookup_uint64_array(nvl, ZPOOL_CONFIG_VDEV_STATS,
596 #else
597 	if ((nvlist_lookup_uint64_array(nvl, "stats",
598 #endif
599 
600 	    (uint64_t **)&vs, &nelem) == 0)) {
601 		nvlist_add_uint64_array(nvl,
602 #ifdef _KERNEL
603 		    "stats",
604 #else
605 		    ZPOOL_CONFIG_VDEV_STATS,
606 #endif
607 		    (uint64_t *)vs, nelem);
608 #ifdef _KERNEL
609 		nvlist_remove(nvl, ZPOOL_CONFIG_VDEV_STATS,
610 #else
611 		nvlist_remove(nvl, "stats",
612 #endif
613 		    DATA_TYPE_UINT64_ARRAY);
614 	}
615 }
616 
617 static int
zfs_ioctl_compat_fix_stats(zfs_cmd_t * zc,const int nc)618 zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int nc)
619 {
620 	nvlist_t *nv, *nvp = NULL;
621 	nvpair_t *elem;
622 	int error;
623 
624 	if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
625 	    zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
626 		return (error);
627 
628 	if (nc == 5) { /* ZFS_IOC_POOL_STATS */
629 		elem = NULL;
630 		while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
631 			if (nvpair_value_nvlist(elem, &nvp) == 0)
632 				zfs_ioctl_compat_fix_stats_nvlist(nvp);
633 		}
634 		elem = NULL;
635 	} else
636 		zfs_ioctl_compat_fix_stats_nvlist(nv);
637 
638 	error = zfs_ioctl_compat_put_nvlist(zc, nv);
639 
640 	nvlist_free(nv);
641 
642 	return (error);
643 }
644 
645 static int
zfs_ioctl_compat_pool_get_props(zfs_cmd_t * zc)646 zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
647 {
648 	nvlist_t *nv, *nva = NULL;
649 	int error;
650 
651 	if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
652 	    zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
653 		return (error);
654 
655 #ifdef _KERNEL
656 	if (nvlist_lookup_nvlist(nv, "allocated", &nva) == 0) {
657 		nvlist_add_nvlist(nv, "used", nva);
658 		nvlist_remove(nv, "allocated", DATA_TYPE_NVLIST);
659 	}
660 
661 	if (nvlist_lookup_nvlist(nv, "free", &nva) == 0) {
662 		nvlist_add_nvlist(nv, "available", nva);
663 		nvlist_remove(nv, "free", DATA_TYPE_NVLIST);
664 	}
665 #else
666 	if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) {
667 		nvlist_add_nvlist(nv, "allocated", nva);
668 		nvlist_remove(nv, "used", DATA_TYPE_NVLIST);
669 	}
670 
671 	if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) {
672 		nvlist_add_nvlist(nv, "free", nva);
673 		nvlist_remove(nv, "available", DATA_TYPE_NVLIST);
674 	}
675 #endif
676 
677 	error = zfs_ioctl_compat_put_nvlist(zc, nv);
678 
679 	nvlist_free(nv);
680 
681 	return (error);
682 }
683 
684 #ifndef _KERNEL
685 int
zcmd_ioctl_compat(int fd,int request,zfs_cmd_t * zc,const int cflag)686 zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
687 {
688 	int nc, ret;
689 	void *zc_c;
690 	unsigned long ncmd;
691 	zfs_iocparm_t zp;
692 
693 	switch (cflag) {
694 	case ZFS_CMD_COMPAT_NONE:
695 		ncmd = _IOWR('Z', request, struct zfs_iocparm);
696 		zp.zfs_cmd = (uint64_t)zc;
697 		zp.zfs_cmd_size = sizeof(zfs_cmd_t);
698 		zp.zfs_ioctl_version = ZFS_IOCVER_CURRENT;
699 		return (ioctl(fd, ncmd, &zp));
700 	case ZFS_CMD_COMPAT_ZCMD:
701 		ncmd = _IOWR('Z', request, struct zfs_iocparm);
702 		zp.zfs_cmd = (uint64_t)zc;
703 		zp.zfs_cmd_size = sizeof(zfs_cmd_zcmd_t);
704 		zp.zfs_ioctl_version = ZFS_IOCVER_ZCMD;
705 		return (ioctl(fd, ncmd, &zp));
706 	case ZFS_CMD_COMPAT_LZC:
707 		ncmd = _IOWR('Z', request, struct zfs_cmd);
708 		return (ioctl(fd, ncmd, zc));
709 	case ZFS_CMD_COMPAT_DEADMAN:
710 		zc_c = malloc(sizeof(zfs_cmd_deadman_t));
711 		ncmd = _IOWR('Z', request, struct zfs_cmd_deadman);
712 		break;
713 	case ZFS_CMD_COMPAT_V28:
714 		zc_c = malloc(sizeof(zfs_cmd_v28_t));
715 		ncmd = _IOWR('Z', request, struct zfs_cmd_v28);
716 		break;
717 	case ZFS_CMD_COMPAT_V15:
718 		nc = zfs_ioctl_v28_to_v15[request];
719 		zc_c = malloc(sizeof(zfs_cmd_v15_t));
720 		ncmd = _IOWR('Z', nc, struct zfs_cmd_v15);
721 		break;
722 	default:
723 		return (EINVAL);
724 	}
725 
726 	if (ZFS_IOCREQ(ncmd) == ZFS_IOC_COMPAT_FAIL)
727 		return (ENOTSUP);
728 
729 	zfs_cmd_compat_put(zc, (caddr_t)zc_c, request, cflag);
730 
731 	ret = ioctl(fd, ncmd, zc_c);
732 	if (cflag == ZFS_CMD_COMPAT_V15 &&
733 	    nc == ZFS_IOC_POOL_IMPORT)
734 		ret = ioctl(fd, _IOWR('Z', ZFS_IOC_POOL_CONFIGS,
735 		    struct zfs_cmd_v15), zc_c);
736 	zfs_cmd_compat_get(zc, (caddr_t)zc_c, cflag);
737 	free(zc_c);
738 
739 	if (cflag == ZFS_CMD_COMPAT_V15) {
740 		switch (nc) {
741 		case ZFS_IOC_POOL_IMPORT:
742 		case ZFS_IOC_POOL_CONFIGS:
743 		case ZFS_IOC_POOL_STATS:
744 		case ZFS_IOC_POOL_TRYIMPORT:
745 			zfs_ioctl_compat_fix_stats(zc, nc);
746 			break;
747 		case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
748 			zfs_ioctl_compat_pool_get_props(zc);
749 			break;
750 		}
751 	}
752 
753 	return (ret);
754 }
755 #else /* _KERNEL */
756 int
zfs_ioctl_compat_pre(zfs_cmd_t * zc,int * vec,const int cflag)757 zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
758 {
759 	int error = 0;
760 
761 	/* are we creating a clone? */
762 	if (*vec == ZFS_IOC_CREATE && zc->zc_value[0] != '\0')
763 		*vec = ZFS_IOC_CLONE;
764 
765 	if (cflag == ZFS_CMD_COMPAT_V15) {
766 		switch (*vec) {
767 
768 		case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
769 			zc->zc_cookie = POOL_SCAN_SCRUB;
770 			break;
771 		}
772 	}
773 
774 	return (error);
775 }
776 
777 void
zfs_ioctl_compat_post(zfs_cmd_t * zc,int vec,const int cflag)778 zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
779 {
780 	if (cflag == ZFS_CMD_COMPAT_V15) {
781 		switch (vec) {
782 		case ZFS_IOC_POOL_CONFIGS:
783 		case ZFS_IOC_POOL_STATS:
784 		case ZFS_IOC_POOL_TRYIMPORT:
785 			zfs_ioctl_compat_fix_stats(zc, vec);
786 			break;
787 		case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
788 			zfs_ioctl_compat_pool_get_props(zc);
789 			break;
790 		}
791 	}
792 }
793 
794 nvlist_t *
zfs_ioctl_compat_innvl(zfs_cmd_t * zc,nvlist_t * innvl,const int vec,const int cflag)795 zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t * innvl, const int vec,
796     const int cflag)
797 {
798 	nvlist_t *nvl, *tmpnvl, *hnvl;
799 	nvpair_t *elem;
800 	char *poolname, *snapname;
801 	int err;
802 
803 	if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
804 	    cflag == ZFS_CMD_COMPAT_ZCMD)
805 		goto out;
806 
807 	switch (vec) {
808 	case ZFS_IOC_CREATE:
809 		nvl = fnvlist_alloc();
810 		fnvlist_add_int32(nvl, "type", zc->zc_objset_type);
811 		if (innvl != NULL) {
812 			fnvlist_add_nvlist(nvl, "props", innvl);
813 			nvlist_free(innvl);
814 		}
815 		return (nvl);
816 	break;
817 	case ZFS_IOC_CLONE:
818 		nvl = fnvlist_alloc();
819 		fnvlist_add_string(nvl, "origin", zc->zc_value);
820 		if (innvl != NULL) {
821 			fnvlist_add_nvlist(nvl, "props", innvl);
822 			nvlist_free(innvl);
823 		}
824 		return (nvl);
825 	break;
826 	case ZFS_IOC_SNAPSHOT:
827 		if (innvl == NULL)
828 			goto out;
829 		nvl = fnvlist_alloc();
830 		fnvlist_add_nvlist(nvl, "props", innvl);
831 		tmpnvl = fnvlist_alloc();
832 		snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
833 		fnvlist_add_boolean(tmpnvl, snapname);
834 		kmem_free(snapname, strlen(snapname + 1));
835 		/* check if we are doing a recursive snapshot */
836 		if (zc->zc_cookie)
837 			dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value,
838 			    tmpnvl);
839 		fnvlist_add_nvlist(nvl, "snaps", tmpnvl);
840 		fnvlist_free(tmpnvl);
841 		nvlist_free(innvl);
842 		/* strip dataset part from zc->zc_name */
843 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
844 		return (nvl);
845 	break;
846 	case ZFS_IOC_SPACE_SNAPS:
847 		nvl = fnvlist_alloc();
848 		fnvlist_add_string(nvl, "firstsnap", zc->zc_value);
849 		if (innvl != NULL)
850 			nvlist_free(innvl);
851 		return (nvl);
852 	break;
853 	case ZFS_IOC_DESTROY_SNAPS:
854 		if (innvl == NULL && cflag == ZFS_CMD_COMPAT_DEADMAN)
855 			goto out;
856 		nvl = fnvlist_alloc();
857 		if (innvl != NULL) {
858 			fnvlist_add_nvlist(nvl, "snaps", innvl);
859 		} else {
860 			/*
861 			 * We are probably called by even older binaries,
862 			 * allocate and populate nvlist with recursive
863 			 * snapshots
864 			 */
865 			if (zfs_component_namecheck(zc->zc_value, NULL,
866 			    NULL) == 0) {
867 				tmpnvl = fnvlist_alloc();
868 				if (dmu_get_recursive_snaps_nvl(zc->zc_name,
869 				    zc->zc_value, tmpnvl) == 0)
870 					fnvlist_add_nvlist(nvl, "snaps",
871 					    tmpnvl);
872 				nvlist_free(tmpnvl);
873 			}
874 		}
875 		if (innvl != NULL)
876 			nvlist_free(innvl);
877 		/* strip dataset part from zc->zc_name */
878 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
879 		return (nvl);
880 	break;
881 	case ZFS_IOC_HOLD:
882 		nvl = fnvlist_alloc();
883 		tmpnvl = fnvlist_alloc();
884 		if (zc->zc_cleanup_fd != -1)
885 			fnvlist_add_int32(nvl, "cleanup_fd",
886 			    (int32_t)zc->zc_cleanup_fd);
887 		if (zc->zc_cookie) {
888 			hnvl = fnvlist_alloc();
889 			if (dmu_get_recursive_snaps_nvl(zc->zc_name,
890 			    zc->zc_value, hnvl) == 0) {
891 				elem = NULL;
892 				while ((elem = nvlist_next_nvpair(hnvl,
893 				    elem)) != NULL) {
894 					nvlist_add_string(tmpnvl,
895 					    nvpair_name(elem), zc->zc_string);
896 				}
897 			}
898 			nvlist_free(hnvl);
899 		} else {
900 			snapname = kmem_asprintf("%s@%s", zc->zc_name,
901 			    zc->zc_value);
902 			nvlist_add_string(tmpnvl, snapname, zc->zc_string);
903 			kmem_free(snapname, strlen(snapname + 1));
904 		}
905 		fnvlist_add_nvlist(nvl, "holds", tmpnvl);
906 		nvlist_free(tmpnvl);
907 		if (innvl != NULL)
908 			nvlist_free(innvl);
909 		/* strip dataset part from zc->zc_name */
910 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
911 		return (nvl);
912 	break;
913 	case ZFS_IOC_RELEASE:
914 		nvl = fnvlist_alloc();
915 		tmpnvl = fnvlist_alloc();
916 		if (zc->zc_cookie) {
917 			hnvl = fnvlist_alloc();
918 			if (dmu_get_recursive_snaps_nvl(zc->zc_name,
919 			    zc->zc_value, hnvl) == 0) {
920 				elem = NULL;
921 				while ((elem = nvlist_next_nvpair(hnvl,
922 				    elem)) != NULL) {
923 					fnvlist_add_boolean(tmpnvl,
924 					    zc->zc_string);
925 					fnvlist_add_nvlist(nvl,
926 					    nvpair_name(elem), tmpnvl);
927 				}
928 			}
929 			nvlist_free(hnvl);
930 		} else {
931 			snapname = kmem_asprintf("%s@%s", zc->zc_name,
932 			    zc->zc_value);
933 			fnvlist_add_boolean(tmpnvl, zc->zc_string);
934 			fnvlist_add_nvlist(nvl, snapname, tmpnvl);
935 			kmem_free(snapname, strlen(snapname + 1));
936 		}
937 		nvlist_free(tmpnvl);
938 		if (innvl != NULL)
939 			nvlist_free(innvl);
940 		/* strip dataset part from zc->zc_name */
941 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
942 		return (nvl);
943 	break;
944 	}
945 out:
946 	return (innvl);
947 }
948 
949 nvlist_t *
zfs_ioctl_compat_outnvl(zfs_cmd_t * zc,nvlist_t * outnvl,const int vec,const int cflag)950 zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec,
951     const int cflag)
952 {
953 	nvlist_t *tmpnvl;
954 
955 	if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
956 	    cflag == ZFS_CMD_COMPAT_ZCMD)
957 		return (outnvl);
958 
959 	switch (vec) {
960 	case ZFS_IOC_SPACE_SNAPS:
961 		(void) nvlist_lookup_uint64(outnvl, "used", &zc->zc_cookie);
962 		(void) nvlist_lookup_uint64(outnvl, "compressed",
963 		    &zc->zc_objset_type);
964 		(void) nvlist_lookup_uint64(outnvl, "uncompressed",
965 		    &zc->zc_perm_action);
966 		nvlist_free(outnvl);
967 		/* return empty outnvl */
968 		tmpnvl = fnvlist_alloc();
969 		return (tmpnvl);
970 	break;
971 	case ZFS_IOC_CREATE:
972 	case ZFS_IOC_CLONE:
973 	case ZFS_IOC_HOLD:
974 	case ZFS_IOC_RELEASE:
975 		nvlist_free(outnvl);
976 		/* return empty outnvl */
977 		tmpnvl = fnvlist_alloc();
978 		return (tmpnvl);
979 	break;
980 	}
981 
982 	return (outnvl);
983 }
984 #endif /* KERNEL */
985