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 <[email protected]>. All rights reserved.
23  * Copyright 2013 Martin Matuska <[email protected]>. 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 <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
39 
40 /*
41  * FreeBSD zfs_cmd compatibility with older binaries
42  * appropriately remap/extend the zfs_cmd_t structure
43  */
44 void
zfs_cmd_compat_get(zfs_cmd_t * zc,caddr_t addr,const int cflag)45 zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
46 {
47 
48 }
49 #if 0
50 static int
51 zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag,
52     nvlist_t **nvp)
53 {
54 	char *packed;
55 	int error;
56 	nvlist_t *list = NULL;
57 
58 	/*
59 	 * Read in and unpack the user-supplied nvlist.
60 	 */
61 	if (size == 0)
62 		return (EINVAL);
63 
64 #ifdef _KERNEL
65 	packed = kmem_alloc(size, KM_SLEEP);
66 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
67 	    iflag)) != 0) {
68 		kmem_free(packed, size);
69 		return (error);
70 	}
71 #else
72 	packed = (void *)(uintptr_t)nvl;
73 #endif
74 
75 	error = nvlist_unpack(packed, size, &list, 0);
76 
77 #ifdef _KERNEL
78 	kmem_free(packed, size);
79 #endif
80 
81 	if (error != 0)
82 		return (error);
83 
84 	*nvp = list;
85 	return (0);
86 }
87 
88 static int
89 zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
90 {
91 	char *packed = NULL;
92 	int error = 0;
93 	size_t size;
94 
95 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
96 
97 #ifdef _KERNEL
98 	packed = kmem_alloc(size, KM_SLEEP);
99 	VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
100 	    KM_SLEEP) == 0);
101 
102 	if (ddi_copyout(packed,
103 	    (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0)
104 		error = EFAULT;
105 	kmem_free(packed, size);
106 #else
107 	packed = (void *)(uintptr_t)zc->zc_nvlist_dst;
108 	VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
109 	    0) == 0);
110 #endif
111 
112 	zc->zc_nvlist_dst_size = size;
113 	return (error);
114 }
115 
116 static void
117 zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl)
118 {
119 	nvlist_t **child;
120 	nvlist_t *nvroot = NULL;
121 	vdev_stat_t *vs;
122 	uint_t c, children, nelem;
123 
124 	if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
125 	    &child, &children) == 0) {
126 		for (c = 0; c < children; c++) {
127 			zfs_ioctl_compat_fix_stats_nvlist(child[c]);
128 		}
129 	}
130 
131 	if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE,
132 	    &nvroot) == 0)
133 		zfs_ioctl_compat_fix_stats_nvlist(nvroot);
134 	if ((nvlist_lookup_uint64_array(nvl, "stats",
135 	    (uint64_t **)&vs, &nelem) == 0)) {
136 		nvlist_add_uint64_array(nvl,
137 		    ZPOOL_CONFIG_VDEV_STATS,
138 		    (uint64_t *)vs, nelem);
139 		nvlist_remove(nvl, "stats",
140 		    DATA_TYPE_UINT64_ARRAY);
141 	}
142 }
143 
144 
145 static int
146 zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int nc)
147 {
148 	nvlist_t *nv, *nvp = NULL;
149 	nvpair_t *elem;
150 	int error;
151 
152 	if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
153 	    zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
154 		return (error);
155 
156 	if (nc == 5) { /* ZFS_IOC_POOL_STATS */
157 		elem = NULL;
158 		while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
159 			if (nvpair_value_nvlist(elem, &nvp) == 0)
160 				zfs_ioctl_compat_fix_stats_nvlist(nvp);
161 		}
162 		elem = NULL;
163 	} else
164 		zfs_ioctl_compat_fix_stats_nvlist(nv);
165 
166 	error = zfs_ioctl_compat_put_nvlist(zc, nv);
167 
168 	nvlist_free(nv);
169 
170 	return (error);
171 }
172 
173 static int
174 zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
175 {
176 	nvlist_t *nv, *nva = NULL;
177 	int error;
178 
179 	if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
180 	    zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
181 		return (error);
182 
183 	if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) {
184 		nvlist_add_nvlist(nv, "allocated", nva);
185 		nvlist_remove(nv, "used", DATA_TYPE_NVLIST);
186 	}
187 
188 	if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) {
189 		nvlist_add_nvlist(nv, "free", nva);
190 		nvlist_remove(nv, "available", DATA_TYPE_NVLIST);
191 	}
192 
193 	error = zfs_ioctl_compat_put_nvlist(zc, nv);
194 
195 	nvlist_free(nv);
196 
197 	return (error);
198 }
199 #endif
200 
201 #ifdef _KERNEL
202 int
zfs_ioctl_compat_pre(zfs_cmd_t * zc,int * vec,const int cflag)203 zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
204 {
205 	int error = 0;
206 
207 	/* are we creating a clone? */
208 	if (*vec == ZFS_IOC_CREATE && zc->zc_value[0] != '\0')
209 		*vec = ZFS_IOC_CLONE;
210 
211 	if (cflag == ZFS_CMD_COMPAT_V15) {
212 		switch (*vec) {
213 
214 		case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
215 			zc->zc_cookie = POOL_SCAN_SCRUB;
216 			break;
217 		}
218 	}
219 
220 	return (error);
221 }
222 
223 void
zfs_ioctl_compat_post(zfs_cmd_t * zc,int vec,const int cflag)224 zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
225 {
226 	if (cflag == ZFS_CMD_COMPAT_V15) {
227 		switch (vec) {
228 		case ZFS_IOC_POOL_CONFIGS:
229 		case ZFS_IOC_POOL_STATS:
230 		case ZFS_IOC_POOL_TRYIMPORT:
231 			zfs_ioctl_compat_fix_stats(zc, vec);
232 			break;
233 		case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
234 			zfs_ioctl_compat_pool_get_props(zc);
235 			break;
236 		}
237 	}
238 }
239 
240 nvlist_t *
zfs_ioctl_compat_innvl(zfs_cmd_t * zc,nvlist_t * innvl,const int vec,const int cflag)241 zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t *innvl, const int vec,
242     const int cflag)
243 {
244 	nvlist_t *nvl, *tmpnvl, *hnvl;
245 	nvpair_t *elem;
246 	char *poolname, *snapname;
247 	int err;
248 
249 	if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
250 	    cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
251 	    cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
252 		goto out;
253 
254 	switch (vec) {
255 	case ZFS_IOC_CREATE:
256 		nvl = fnvlist_alloc();
257 		fnvlist_add_int32(nvl, "type", zc->zc_objset_type);
258 		if (innvl != NULL) {
259 			fnvlist_add_nvlist(nvl, "props", innvl);
260 			nvlist_free(innvl);
261 		}
262 		return (nvl);
263 	break;
264 	case ZFS_IOC_CLONE:
265 		nvl = fnvlist_alloc();
266 		fnvlist_add_string(nvl, "origin", zc->zc_value);
267 		if (innvl != NULL) {
268 			fnvlist_add_nvlist(nvl, "props", innvl);
269 			nvlist_free(innvl);
270 		}
271 		return (nvl);
272 	break;
273 	case ZFS_IOC_SNAPSHOT:
274 		if (innvl == NULL)
275 			goto out;
276 		nvl = fnvlist_alloc();
277 		fnvlist_add_nvlist(nvl, "props", innvl);
278 		tmpnvl = fnvlist_alloc();
279 		snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
280 		fnvlist_add_boolean(tmpnvl, snapname);
281 		kmem_free(snapname, strlen(snapname + 1));
282 		/* check if we are doing a recursive snapshot */
283 		if (zc->zc_cookie)
284 			dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value,
285 			    tmpnvl);
286 		fnvlist_add_nvlist(nvl, "snaps", tmpnvl);
287 		fnvlist_free(tmpnvl);
288 		nvlist_free(innvl);
289 		/* strip dataset part from zc->zc_name */
290 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
291 		return (nvl);
292 	break;
293 	case ZFS_IOC_SPACE_SNAPS:
294 		nvl = fnvlist_alloc();
295 		fnvlist_add_string(nvl, "firstsnap", zc->zc_value);
296 		if (innvl != NULL)
297 			nvlist_free(innvl);
298 		return (nvl);
299 	break;
300 	case ZFS_IOC_DESTROY_SNAPS:
301 		if (innvl == NULL && cflag == ZFS_CMD_COMPAT_DEADMAN)
302 			goto out;
303 		nvl = fnvlist_alloc();
304 		if (innvl != NULL) {
305 			fnvlist_add_nvlist(nvl, "snaps", innvl);
306 		} else {
307 			/*
308 			 * We are probably called by even older binaries,
309 			 * allocate and populate nvlist with recursive
310 			 * snapshots
311 			 */
312 			if (zfs_component_namecheck(zc->zc_value, NULL,
313 			    NULL) == 0) {
314 				tmpnvl = fnvlist_alloc();
315 				if (dmu_get_recursive_snaps_nvl(zc->zc_name,
316 				    zc->zc_value, tmpnvl) == 0)
317 					fnvlist_add_nvlist(nvl, "snaps",
318 					    tmpnvl);
319 				nvlist_free(tmpnvl);
320 			}
321 		}
322 		if (innvl != NULL)
323 			nvlist_free(innvl);
324 		/* strip dataset part from zc->zc_name */
325 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
326 		return (nvl);
327 	break;
328 	case ZFS_IOC_HOLD:
329 		nvl = fnvlist_alloc();
330 		tmpnvl = fnvlist_alloc();
331 		if (zc->zc_cleanup_fd != -1)
332 			fnvlist_add_int32(nvl, "cleanup_fd",
333 			    (int32_t)zc->zc_cleanup_fd);
334 		if (zc->zc_cookie) {
335 			hnvl = fnvlist_alloc();
336 			if (dmu_get_recursive_snaps_nvl(zc->zc_name,
337 			    zc->zc_value, hnvl) == 0) {
338 				elem = NULL;
339 				while ((elem = nvlist_next_nvpair(hnvl,
340 				    elem)) != NULL) {
341 					nvlist_add_string(tmpnvl,
342 					    nvpair_name(elem), zc->zc_string);
343 				}
344 			}
345 			nvlist_free(hnvl);
346 		} else {
347 			snapname = kmem_asprintf("%s@%s", zc->zc_name,
348 			    zc->zc_value);
349 			nvlist_add_string(tmpnvl, snapname, zc->zc_string);
350 			kmem_free(snapname, strlen(snapname + 1));
351 		}
352 		fnvlist_add_nvlist(nvl, "holds", tmpnvl);
353 		nvlist_free(tmpnvl);
354 		if (innvl != NULL)
355 			nvlist_free(innvl);
356 		/* strip dataset part from zc->zc_name */
357 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
358 		return (nvl);
359 	break;
360 	case ZFS_IOC_RELEASE:
361 		nvl = fnvlist_alloc();
362 		tmpnvl = fnvlist_alloc();
363 		if (zc->zc_cookie) {
364 			hnvl = fnvlist_alloc();
365 			if (dmu_get_recursive_snaps_nvl(zc->zc_name,
366 			    zc->zc_value, hnvl) == 0) {
367 				elem = NULL;
368 				while ((elem = nvlist_next_nvpair(hnvl,
369 				    elem)) != NULL) {
370 					fnvlist_add_boolean(tmpnvl,
371 					    zc->zc_string);
372 					fnvlist_add_nvlist(nvl,
373 					    nvpair_name(elem), tmpnvl);
374 				}
375 			}
376 			nvlist_free(hnvl);
377 		} else {
378 			snapname = kmem_asprintf("%s@%s", zc->zc_name,
379 			    zc->zc_value);
380 			fnvlist_add_boolean(tmpnvl, zc->zc_string);
381 			fnvlist_add_nvlist(nvl, snapname, tmpnvl);
382 			kmem_free(snapname, strlen(snapname + 1));
383 		}
384 		nvlist_free(tmpnvl);
385 		if (innvl != NULL)
386 			nvlist_free(innvl);
387 		/* strip dataset part from zc->zc_name */
388 		zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
389 		return (nvl);
390 	break;
391 	}
392 out:
393 	return (innvl);
394 }
395 
396 nvlist_t *
zfs_ioctl_compat_outnvl(zfs_cmd_t * zc,nvlist_t * outnvl,const int vec,const int cflag)397 zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t *outnvl, const int vec,
398     const int cflag)
399 {
400 	nvlist_t *tmpnvl;
401 
402 	if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
403 	    cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
404 	    cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
405 		return (outnvl);
406 
407 	switch (vec) {
408 	case ZFS_IOC_SPACE_SNAPS:
409 		(void) nvlist_lookup_uint64(outnvl, "used", &zc->zc_cookie);
410 		(void) nvlist_lookup_uint64(outnvl, "compressed",
411 		    &zc->zc_objset_type);
412 		(void) nvlist_lookup_uint64(outnvl, "uncompressed",
413 		    &zc->zc_perm_action);
414 		nvlist_free(outnvl);
415 		/* return empty outnvl */
416 		tmpnvl = fnvlist_alloc();
417 		return (tmpnvl);
418 	break;
419 	case ZFS_IOC_CREATE:
420 	case ZFS_IOC_CLONE:
421 	case ZFS_IOC_HOLD:
422 	case ZFS_IOC_RELEASE:
423 		nvlist_free(outnvl);
424 		/* return empty outnvl */
425 		tmpnvl = fnvlist_alloc();
426 		return (tmpnvl);
427 	break;
428 	}
429 
430 	return (outnvl);
431 }
432 #endif /* KERNEL */
433