zfs_ioctl_compat.c revision 220447
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 2010 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
23 * Portions Copyright 2005, 2010, Oracle and/or its affiliates.
24 * All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#include <sys/types.h>
29#include <sys/param.h>
30#include <sys/cred.h>
31#include <sys/dmu.h>
32#include <sys/zio.h>
33#include <sys/nvpair.h>
34#include <sys/dsl_deleg.h>
35#include <sys/zfs_ioctl.h>
36#include "zfs_ioctl_compat.h"
37
38/*
39 * FreeBSD zfs_cmd compatibility with v15 and older binaries
40 * appropriately remap/extend the zfs_cmd_t structure
41 */
42void
43zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
44{
45	zfs_cmd_v15_t *zc_c;
46
47	if (cflag == ZFS_CMD_COMPAT_V15) {
48		zc_c = (void *)addr;
49
50		/* zc */
51		strlcpy(zc->zc_name,zc_c->zc_name,MAXPATHLEN);
52		strlcpy(zc->zc_value,zc_c->zc_value,MAXPATHLEN);
53		strlcpy(zc->zc_string,zc_c->zc_string,MAXPATHLEN);
54		zc->zc_guid = zc_c->zc_guid;
55		zc->zc_nvlist_conf = zc_c->zc_nvlist_conf;
56		zc->zc_nvlist_conf_size = zc_c->zc_nvlist_conf_size;
57		zc->zc_nvlist_src = zc_c->zc_nvlist_src;
58		zc->zc_nvlist_src_size = zc_c->zc_nvlist_src_size;
59		zc->zc_nvlist_dst = zc_c->zc_nvlist_dst;
60		zc->zc_nvlist_dst_size = zc_c->zc_nvlist_dst_size;
61		zc->zc_cookie = zc_c->zc_cookie;
62		zc->zc_objset_type = zc_c->zc_objset_type;
63		zc->zc_perm_action = zc_c->zc_perm_action;
64		zc->zc_history = zc_c->zc_history;
65		zc->zc_history_len = zc_c->zc_history_len;
66		zc->zc_history_offset = zc_c->zc_history_offset;
67		zc->zc_obj = zc_c->zc_obj;
68		zc->zc_share = zc_c->zc_share;
69		zc->zc_jailid = zc_c->zc_jailid;
70		zc->zc_objset_stats = zc_c->zc_objset_stats;
71		zc->zc_begin_record = zc_c->zc_begin_record;
72
73		/* zc->zc_inject_record */
74		zc->zc_inject_record.zi_objset =
75		    zc_c->zc_inject_record.zi_objset;
76		zc->zc_inject_record.zi_object =
77		    zc_c->zc_inject_record.zi_object;
78		zc->zc_inject_record.zi_start =
79		    zc_c->zc_inject_record.zi_start;
80		zc->zc_inject_record.zi_end =
81		    zc_c->zc_inject_record.zi_end;
82		zc->zc_inject_record.zi_guid =
83		    zc_c->zc_inject_record.zi_guid;
84		zc->zc_inject_record.zi_level =
85		    zc_c->zc_inject_record.zi_level;
86		zc->zc_inject_record.zi_error =
87		    zc_c->zc_inject_record.zi_error;
88		zc->zc_inject_record.zi_type =
89		    zc_c->zc_inject_record.zi_type;
90		zc->zc_inject_record.zi_freq =
91		    zc_c->zc_inject_record.zi_freq;
92		zc->zc_inject_record.zi_failfast =
93		    zc_c->zc_inject_record.zi_failfast;
94	}
95}
96
97void
98zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int cflag)
99{
100	zfs_cmd_v15_t *zc_c;
101
102	switch (cflag) {
103	case ZFS_CMD_COMPAT_V15:
104		zc_c = (void *)addr;
105
106		/* zc */
107		strlcpy(zc_c->zc_name,zc->zc_name,MAXPATHLEN);
108		strlcpy(zc_c->zc_value,zc->zc_value,MAXPATHLEN);
109		strlcpy(zc_c->zc_string,zc->zc_string,MAXPATHLEN);
110		zc_c->zc_guid = zc->zc_guid;
111		zc_c->zc_nvlist_conf = zc->zc_nvlist_conf;
112		zc_c->zc_nvlist_conf_size = zc->zc_nvlist_conf_size;
113		zc_c->zc_nvlist_src = zc->zc_nvlist_src;
114		zc_c->zc_nvlist_src_size = zc->zc_nvlist_src_size;
115		zc_c->zc_nvlist_dst = zc->zc_nvlist_dst;
116		zc_c->zc_nvlist_dst_size = zc->zc_nvlist_dst_size;
117		zc_c->zc_cookie = zc->zc_cookie;
118		zc_c->zc_objset_type = zc->zc_objset_type;
119		zc_c->zc_perm_action = zc->zc_perm_action;
120		zc_c->zc_history = zc->zc_history;
121		zc_c->zc_history_len = zc->zc_history_len;
122		zc_c->zc_history_offset = zc->zc_history_offset;
123		zc_c->zc_obj = zc->zc_obj;
124		zc_c->zc_share = zc->zc_share;
125		zc_c->zc_jailid = zc->zc_jailid;
126		zc_c->zc_objset_stats = zc->zc_objset_stats;
127		zc_c->zc_begin_record = zc->zc_begin_record;
128
129		/* zc_inject_record */
130		zc_c->zc_inject_record.zi_objset =
131		    zc->zc_inject_record.zi_objset;
132		zc_c->zc_inject_record.zi_object =
133		    zc->zc_inject_record.zi_object;
134		zc_c->zc_inject_record.zi_start =
135		    zc->zc_inject_record.zi_start;
136		zc_c->zc_inject_record.zi_end =
137		    zc->zc_inject_record.zi_end;
138		zc_c->zc_inject_record.zi_guid =
139		    zc->zc_inject_record.zi_guid;
140		zc_c->zc_inject_record.zi_level =
141		    zc->zc_inject_record.zi_level;
142		zc_c->zc_inject_record.zi_error =
143		    zc->zc_inject_record.zi_error;
144		zc_c->zc_inject_record.zi_type =
145		    zc->zc_inject_record.zi_type;
146		zc_c->zc_inject_record.zi_freq =
147		    zc->zc_inject_record.zi_freq;
148		zc_c->zc_inject_record.zi_failfast =
149		    zc->zc_inject_record.zi_failfast;
150
151		break;
152	}
153}
154
155static int
156zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag,
157    nvlist_t **nvp)
158{
159	char *packed;
160	int error;
161	nvlist_t *list = NULL;
162
163	/*
164	 * Read in and unpack the user-supplied nvlist.
165	 */
166	if (size == 0)
167		return (EINVAL);
168
169#ifdef _KERNEL
170	packed = kmem_alloc(size, KM_SLEEP);
171	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
172	    iflag)) != 0) {
173		kmem_free(packed, size);
174		return (error);
175	}
176#else
177	packed = (void *)(uintptr_t)nvl;
178#endif
179
180	error = nvlist_unpack(packed, size, &list, 0);
181
182#ifdef _KERNEL
183	kmem_free(packed, size);
184#endif
185
186	if (error != 0)
187		return (error);
188
189	*nvp = list;
190	return (0);
191}
192
193static int
194zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
195{
196	char *packed = NULL;
197	int error = 0;
198	size_t size;
199
200	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
201
202#ifdef _KERNEL
203	packed = kmem_alloc(size, KM_SLEEP);
204	VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
205	    KM_SLEEP) == 0);
206
207	if (ddi_copyout(packed,
208	    (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0)
209		error = EFAULT;
210	kmem_free(packed, size);
211#else
212	packed = (void *)(uintptr_t)zc->zc_nvlist_dst;
213	VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
214	    0) == 0);
215#endif
216
217	zc->zc_nvlist_dst_size = size;
218	return (error);
219}
220
221static void
222zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl)
223{
224	nvlist_t **child;
225	nvlist_t *nvroot = NULL;
226	vdev_stat_t *vs;
227	uint_t c, children, nelem;
228
229	if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
230	    &child, &children) == 0) {
231		for (c = 0; c < children; c++) {
232			zfs_ioctl_compat_fix_stats_nvlist(child[c]);
233		}
234	}
235
236	if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE,
237	    &nvroot) == 0)
238		zfs_ioctl_compat_fix_stats_nvlist(nvroot);
239#ifdef _KERNEL
240	if ((nvlist_lookup_uint64_array(nvl, ZPOOL_CONFIG_VDEV_STATS,
241#else
242	if ((nvlist_lookup_uint64_array(nvl, "stats",
243#endif
244
245	    (uint64_t **)&vs, &nelem) == 0)) {
246		nvlist_add_uint64_array(nvl,
247#ifdef _KERNEL
248		    "stats",
249#else
250		    ZPOOL_CONFIG_VDEV_STATS,
251#endif
252		    (uint64_t *)vs, nelem);
253#ifdef _KERNEL
254		nvlist_remove(nvl, ZPOOL_CONFIG_VDEV_STATS,
255#else
256		nvlist_remove(nvl, "stats",
257#endif
258		    DATA_TYPE_UINT64_ARRAY);
259	}
260}
261
262static int
263zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int cflag)
264{
265	nvlist_t *nv, *nvp = NULL;
266	nvpair_t *elem;
267	int error;
268
269	if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
270	    zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
271		return (error);
272
273	if (cflag == 5) { /* ZFS_IOC_POOL_STATS */
274		elem = NULL;
275		while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
276			if (nvpair_value_nvlist(elem, &nvp) == 0)
277				zfs_ioctl_compat_fix_stats_nvlist(nvp);
278		}
279		elem = NULL;
280	} else
281		zfs_ioctl_compat_fix_stats_nvlist(nv);
282
283	error = zfs_ioctl_compat_put_nvlist(zc, nv);
284
285	nvlist_free(nv);
286
287	return (error);
288}
289
290static int
291zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
292{
293	nvlist_t *nv, *nva = NULL;
294	int error;
295
296	if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
297	    zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
298		return (error);
299
300#ifdef _KERNEL
301	if (nvlist_lookup_nvlist(nv, "allocated", &nva) == 0) {
302		nvlist_add_nvlist(nv, "used", nva);
303		nvlist_remove(nv, "allocated", DATA_TYPE_NVLIST);
304	}
305
306	if (nvlist_lookup_nvlist(nv, "free", &nva) == 0) {
307		nvlist_add_nvlist(nv, "available", nva);
308		nvlist_remove(nv, "free", DATA_TYPE_NVLIST);
309	}
310#else
311	if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) {
312		nvlist_add_nvlist(nv, "allocated", nva);
313		nvlist_remove(nv, "used", DATA_TYPE_NVLIST);
314	}
315
316	if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) {
317		nvlist_add_nvlist(nv, "free", nva);
318		nvlist_remove(nv, "available", DATA_TYPE_NVLIST);
319	}
320#endif
321
322	error = zfs_ioctl_compat_put_nvlist(zc, nv);
323
324	nvlist_free(nv);
325
326	return (error);
327}
328
329#ifndef _KERNEL
330int
331zcmd_ioctl_compat(int fd, unsigned long cmd, zfs_cmd_t *zc, const int cflag)
332{
333	int nc, ret;
334	void *zc_c;
335	unsigned long ncmd;
336
337	if (cflag == ZFS_CMD_COMPAT_NONE) {
338		ret = ioctl(fd, cmd, zc);
339		return (ret);
340	}
341
342	if (cflag == ZFS_CMD_COMPAT_V15) {
343		nc = zfs_ioctl_v28_to_v15[ZFS_IOC(cmd)];
344		zc_c = malloc(sizeof(zfs_cmd_v15_t));
345		ncmd = _IOWR('Z', nc, struct zfs_cmd_v15);
346	} else
347		return (EINVAL);
348
349	if (ZFS_IOC(ncmd) == ZFS_IOC_COMPAT_FAIL)
350		return (ENOTSUP);
351
352	zfs_cmd_compat_put(zc, (caddr_t)zc_c, cflag);
353	ret = ioctl(fd, ncmd, zc_c);
354	if (cflag == ZFS_CMD_COMPAT_V15 &&
355	    nc == 2 /* ZFS_IOC_POOL_IMPORT */)
356		ret = ioctl(fd, _IOWR('Z', 4 /* ZFS_IOC_POOL_CONFIGS */,
357		    struct zfs_cmd_v15), zc_c);
358	zfs_cmd_compat_get(zc, (caddr_t)zc_c, cflag);
359	free(zc_c);
360
361	switch (nc) {
362	case 2:	/* ZFS_IOC_POOL_IMPORT */
363	case 4: /* ZFS_IOC_POOL_CONFIGS */
364	case 5: /* ZFS_IOC_POOL_STATS */
365	case 6: /* ZFS_IOC_POOL_TRYIMPORT */
366		zfs_ioctl_compat_fix_stats(zc, nc);
367		break;
368	case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
369		zfs_ioctl_compat_pool_get_props(zc);
370		break;
371	}
372
373	return (ret);
374}
375#else /* _KERNEL */
376void
377zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
378{
379	if (cflag == ZFS_CMD_COMPAT_V15)
380		switch (*vec) {
381
382		case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
383			zc->zc_cookie = POOL_SCAN_SCRUB;
384			break;
385		}
386}
387
388void
389zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
390{
391	if (cflag == ZFS_CMD_COMPAT_V15) {
392		switch (vec) {
393		case 4:	/* ZFS_IOC_POOL_CONFIGS */
394		case 5:	/* ZFS_IOC_POOL_STATS */
395		case 6:	/* ZFS_IOC_POOL_TRYIMPORT */
396			zfs_ioctl_compat_fix_stats(zc, vec);
397			break;
398		case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
399			zfs_ioctl_compat_pool_get_props(zc);
400			break;
401		}
402	}
403}
404#endif /* KERNEL */
405