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