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 <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 */ 44void 45zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) 46{ 47 48} 49#if 0 50static int 51zfs_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 88static int 89zfs_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 116static void 117zfs_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 145static int 146zfs_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 173static int 174zfs_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 202int 203zfs_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 223void 224zfs_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 240nvlist_t * 241zfs_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 } 392out: 393 return (innvl); 394} 395 396nvlist_t * 397zfs_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