1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5fa9e4066Sahrens * Common Development and Distribution License, Version 1.0 only 6fa9e4066Sahrens * (the "License"). You may not use this file except in compliance 7fa9e4066Sahrens * with the License. 8fa9e4066Sahrens * 9fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 11fa9e4066Sahrens * See the License for the specific language governing permissions 12fa9e4066Sahrens * and limitations under the License. 13fa9e4066Sahrens * 14fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 15fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 17fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 18fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 19fa9e4066Sahrens * 20fa9e4066Sahrens * CDDL HEADER END 21fa9e4066Sahrens */ 22fa9e4066Sahrens /* 23fa9e4066Sahrens * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24fa9e4066Sahrens * Use is subject to license terms. 25fa9e4066Sahrens */ 26fa9e4066Sahrens 27fa9e4066Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28fa9e4066Sahrens 29fa9e4066Sahrens #include <sys/types.h> 30fa9e4066Sahrens #include <sys/param.h> 31fa9e4066Sahrens #include <sys/errno.h> 32fa9e4066Sahrens #include <sys/uio.h> 33fa9e4066Sahrens #include <sys/buf.h> 34fa9e4066Sahrens #include <sys/modctl.h> 35fa9e4066Sahrens #include <sys/open.h> 36fa9e4066Sahrens #include <sys/file.h> 37fa9e4066Sahrens #include <sys/kmem.h> 38fa9e4066Sahrens #include <sys/conf.h> 39fa9e4066Sahrens #include <sys/cmn_err.h> 40fa9e4066Sahrens #include <sys/stat.h> 41fa9e4066Sahrens #include <sys/zfs_ioctl.h> 42fa9e4066Sahrens #include <sys/zap.h> 43fa9e4066Sahrens #include <sys/spa.h> 44fa9e4066Sahrens #include <sys/vdev.h> 45fa9e4066Sahrens #include <sys/dmu.h> 46fa9e4066Sahrens #include <sys/dsl_dir.h> 47fa9e4066Sahrens #include <sys/dsl_dataset.h> 48fa9e4066Sahrens #include <sys/dsl_prop.h> 49fa9e4066Sahrens #include <sys/ddi.h> 50fa9e4066Sahrens #include <sys/sunddi.h> 51fa9e4066Sahrens #include <sys/sunldi.h> 52fa9e4066Sahrens #include <sys/policy.h> 53fa9e4066Sahrens #include <sys/zone.h> 54fa9e4066Sahrens #include <sys/nvpair.h> 55fa9e4066Sahrens #include <sys/pathname.h> 56fa9e4066Sahrens #include <sys/mount.h> 57fa9e4066Sahrens #include <sys/sdt.h> 58fa9e4066Sahrens #include <sys/fs/zfs.h> 59fa9e4066Sahrens #include <sys/zfs_ctldir.h> 60fa9e4066Sahrens 61fa9e4066Sahrens #include "zfs_namecheck.h" 62fa9e4066Sahrens 63fa9e4066Sahrens extern struct modlfs zfs_modlfs; 64fa9e4066Sahrens 65fa9e4066Sahrens extern void zfs_init(void); 66fa9e4066Sahrens extern void zfs_fini(void); 67fa9e4066Sahrens 68fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 69fa9e4066Sahrens dev_info_t *zfs_dip; 70fa9e4066Sahrens 71fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *); 72fa9e4066Sahrens typedef int zfs_secpolicy_func_t(const char *, const char *, cred_t *); 73fa9e4066Sahrens 74fa9e4066Sahrens typedef struct zfs_ioc_vec { 75fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 76fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 77fa9e4066Sahrens enum { 78fa9e4066Sahrens no_name, 79fa9e4066Sahrens pool_name, 80fa9e4066Sahrens dataset_name 81fa9e4066Sahrens } zvec_namecheck; 82fa9e4066Sahrens } zfs_ioc_vec_t; 83fa9e4066Sahrens 84fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 85fa9e4066Sahrens void 86fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 87fa9e4066Sahrens { 88fa9e4066Sahrens const char *newfile; 89fa9e4066Sahrens char buf[256]; 90fa9e4066Sahrens va_list adx; 91fa9e4066Sahrens 92fa9e4066Sahrens /* 93fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 94fa9e4066Sahrens */ 95fa9e4066Sahrens newfile = strrchr(file, '/'); 96fa9e4066Sahrens if (newfile != NULL) { 97fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 98fa9e4066Sahrens } else { 99fa9e4066Sahrens newfile = file; 100fa9e4066Sahrens } 101fa9e4066Sahrens 102fa9e4066Sahrens va_start(adx, fmt); 103fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 104fa9e4066Sahrens va_end(adx); 105fa9e4066Sahrens 106fa9e4066Sahrens /* 107fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 108fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 109fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 110fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 111fa9e4066Sahrens * arg0 = file name 112fa9e4066Sahrens * arg1 = function name 113fa9e4066Sahrens * arg2 = line number 114fa9e4066Sahrens * arg3 = message 115fa9e4066Sahrens */ 116fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 117fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 118fa9e4066Sahrens } 119fa9e4066Sahrens 120fa9e4066Sahrens /* 121fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 122fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 123fa9e4066Sahrens */ 124fa9e4066Sahrens /* ARGSUSED */ 125fa9e4066Sahrens static int 126fa9e4066Sahrens zfs_secpolicy_none(const char *unused1, const char *unused2, cred_t *cr) 127fa9e4066Sahrens { 128fa9e4066Sahrens return (0); 129fa9e4066Sahrens } 130fa9e4066Sahrens 131fa9e4066Sahrens /* 132fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 133fa9e4066Sahrens * no privileges, but must be visible in the local zone. 134fa9e4066Sahrens */ 135fa9e4066Sahrens /* ARGSUSED */ 136fa9e4066Sahrens static int 137fa9e4066Sahrens zfs_secpolicy_read(const char *dataset, const char *unused, cred_t *cr) 138fa9e4066Sahrens { 139fa9e4066Sahrens if (INGLOBALZONE(curproc) || 140fa9e4066Sahrens zone_dataset_visible(dataset, NULL)) 141fa9e4066Sahrens return (0); 142fa9e4066Sahrens 143fa9e4066Sahrens return (ENOENT); 144fa9e4066Sahrens } 145fa9e4066Sahrens 146fa9e4066Sahrens static int 147fa9e4066Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr) 148fa9e4066Sahrens { 149fa9e4066Sahrens uint64_t zoned; 150fa9e4066Sahrens int writable = 1; 151fa9e4066Sahrens 152fa9e4066Sahrens /* 153fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 154fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 155fa9e4066Sahrens */ 156fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 157fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 158fa9e4066Sahrens return (ENOENT); 159fa9e4066Sahrens 160fa9e4066Sahrens if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 161fa9e4066Sahrens return (ENOENT); 162fa9e4066Sahrens 163fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 164fa9e4066Sahrens /* 165fa9e4066Sahrens * If the fs is zoned, only root can access it from the 166fa9e4066Sahrens * global zone. 167fa9e4066Sahrens */ 168fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 169fa9e4066Sahrens return (EPERM); 170fa9e4066Sahrens } else { 171fa9e4066Sahrens /* 172fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 173fa9e4066Sahrens */ 174fa9e4066Sahrens if (!zoned) 175fa9e4066Sahrens return (EPERM); 176fa9e4066Sahrens 177fa9e4066Sahrens /* must be writable by this zone */ 178fa9e4066Sahrens if (!writable) 179fa9e4066Sahrens return (EPERM); 180fa9e4066Sahrens } 181fa9e4066Sahrens return (0); 182fa9e4066Sahrens } 183fa9e4066Sahrens 184fa9e4066Sahrens /* 185fa9e4066Sahrens * Policy for dataset write operations (create children, set properties, etc). 186fa9e4066Sahrens * Requires SYS_MOUNT privilege, and must be writable in the local zone. 187fa9e4066Sahrens */ 188fa9e4066Sahrens /* ARGSUSED */ 189fa9e4066Sahrens int 190fa9e4066Sahrens zfs_secpolicy_write(const char *dataset, const char *unused, cred_t *cr) 191fa9e4066Sahrens { 192fa9e4066Sahrens int error; 193fa9e4066Sahrens 194fa9e4066Sahrens if (error = zfs_dozonecheck(dataset, cr)) 195fa9e4066Sahrens return (error); 196fa9e4066Sahrens 197fa9e4066Sahrens return (secpolicy_zfs(cr)); 198fa9e4066Sahrens } 199fa9e4066Sahrens 200fa9e4066Sahrens /* 201fa9e4066Sahrens * Policy for operations that want to write a dataset's parent: 202fa9e4066Sahrens * create, destroy, snapshot, clone, restore. 203fa9e4066Sahrens */ 204fa9e4066Sahrens static int 205fa9e4066Sahrens zfs_secpolicy_parent(const char *dataset, const char *unused, cred_t *cr) 206fa9e4066Sahrens { 207fa9e4066Sahrens char parentname[MAXNAMELEN]; 208fa9e4066Sahrens char *cp; 209fa9e4066Sahrens 210fa9e4066Sahrens /* 211fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 212fa9e4066Sahrens */ 213fa9e4066Sahrens (void) strncpy(parentname, dataset, sizeof (parentname)); 214fa9e4066Sahrens cp = strrchr(parentname, '@'); 215fa9e4066Sahrens if (cp != NULL) { 216fa9e4066Sahrens cp[0] = '\0'; 217fa9e4066Sahrens } else { 218fa9e4066Sahrens cp = strrchr(parentname, '/'); 219fa9e4066Sahrens if (cp == NULL) 220fa9e4066Sahrens return (ENOENT); 221fa9e4066Sahrens cp[0] = '\0'; 222fa9e4066Sahrens 223fa9e4066Sahrens } 224fa9e4066Sahrens 225fa9e4066Sahrens return (zfs_secpolicy_write(parentname, unused, cr)); 226fa9e4066Sahrens } 227fa9e4066Sahrens 228fa9e4066Sahrens /* 229fa9e4066Sahrens * Policy for dataset write operations (create children, set properties, etc). 230fa9e4066Sahrens * Requires SYS_MOUNT privilege, and must be writable in the local zone. 231fa9e4066Sahrens */ 232fa9e4066Sahrens static int 233fa9e4066Sahrens zfs_secpolicy_setprop(const char *dataset, const char *prop, cred_t *cr) 234fa9e4066Sahrens { 235fa9e4066Sahrens int error; 236fa9e4066Sahrens 237fa9e4066Sahrens if (error = zfs_dozonecheck(dataset, cr)) 238fa9e4066Sahrens return (error); 239fa9e4066Sahrens 240fa9e4066Sahrens if (strcmp(prop, "zoned") == 0) { 241fa9e4066Sahrens /* 242fa9e4066Sahrens * Disallow setting of 'zoned' from within a local zone. 243fa9e4066Sahrens */ 244fa9e4066Sahrens if (!INGLOBALZONE(curproc)) 245fa9e4066Sahrens return (EPERM); 246fa9e4066Sahrens } 247fa9e4066Sahrens 248fa9e4066Sahrens return (secpolicy_zfs(cr)); 249fa9e4066Sahrens } 250fa9e4066Sahrens 251fa9e4066Sahrens /* 252fa9e4066Sahrens * Security policy for setting the quota. This is the same as 253fa9e4066Sahrens * zfs_secpolicy_write, except that the local zone may not change the quota at 254fa9e4066Sahrens * the zone-property setpoint. 255fa9e4066Sahrens */ 256fa9e4066Sahrens /* ARGSUSED */ 257fa9e4066Sahrens static int 258fa9e4066Sahrens zfs_secpolicy_quota(const char *dataset, const char *unused, cred_t *cr) 259fa9e4066Sahrens { 260fa9e4066Sahrens int error; 261fa9e4066Sahrens 262fa9e4066Sahrens if (error = zfs_dozonecheck(dataset, cr)) 263fa9e4066Sahrens return (error); 264fa9e4066Sahrens 265fa9e4066Sahrens if (!INGLOBALZONE(curproc)) { 266fa9e4066Sahrens uint64_t zoned; 267fa9e4066Sahrens char setpoint[MAXNAMELEN]; 268fa9e4066Sahrens int dslen; 269fa9e4066Sahrens /* 270fa9e4066Sahrens * Unprivileged users are allowed to modify the quota 271fa9e4066Sahrens * on things *under* (ie. contained by) the thing they 272fa9e4066Sahrens * own. 273fa9e4066Sahrens */ 274fa9e4066Sahrens if (dsl_prop_get_integer(dataset, "zoned", &zoned, setpoint)) 275fa9e4066Sahrens return (EPERM); 276fa9e4066Sahrens if (!zoned) /* this shouldn't happen */ 277fa9e4066Sahrens return (EPERM); 278fa9e4066Sahrens dslen = strlen(dataset); 279fa9e4066Sahrens if (dslen <= strlen(setpoint)) 280fa9e4066Sahrens return (EPERM); 281fa9e4066Sahrens } 282fa9e4066Sahrens 283fa9e4066Sahrens return (secpolicy_zfs(cr)); 284fa9e4066Sahrens } 285fa9e4066Sahrens 286fa9e4066Sahrens /* 287fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 288fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 289fa9e4066Sahrens */ 290fa9e4066Sahrens /* ARGSUSED */ 291fa9e4066Sahrens static int 292fa9e4066Sahrens zfs_secpolicy_config(const char *unused, const char *unused2, cred_t *cr) 293fa9e4066Sahrens { 294fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 295fa9e4066Sahrens return (EPERM); 296fa9e4066Sahrens 297fa9e4066Sahrens return (0); 298fa9e4066Sahrens } 299fa9e4066Sahrens 300fa9e4066Sahrens /* 301fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 302fa9e4066Sahrens */ 303fa9e4066Sahrens static int 304fa9e4066Sahrens get_config(zfs_cmd_t *zc, nvlist_t **nvp) 305fa9e4066Sahrens { 306fa9e4066Sahrens char *packed; 307fa9e4066Sahrens size_t size; 308fa9e4066Sahrens int error; 309fa9e4066Sahrens nvlist_t *config = NULL; 310fa9e4066Sahrens 311fa9e4066Sahrens /* 312fa9e4066Sahrens * Read in and unpack the user-supplied nvlist. By this point, we know 313fa9e4066Sahrens * that the user has the SYS_CONFIG privilege, so allocating arbitrary 314fa9e4066Sahrens * sized regions of memory should not be a problem. 315fa9e4066Sahrens */ 316fa9e4066Sahrens if ((size = zc->zc_config_src_size) == 0) 317fa9e4066Sahrens return (EINVAL); 318fa9e4066Sahrens 319fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 320fa9e4066Sahrens 321fa9e4066Sahrens if ((error = xcopyin((void *)(uintptr_t)zc->zc_config_src, packed, 322fa9e4066Sahrens size)) != 0) { 323fa9e4066Sahrens kmem_free(packed, size); 324fa9e4066Sahrens return (error); 325fa9e4066Sahrens } 326fa9e4066Sahrens 327fa9e4066Sahrens if ((error = nvlist_unpack(packed, size, &config, 0)) != 0) { 328fa9e4066Sahrens kmem_free(packed, size); 329fa9e4066Sahrens return (error); 330fa9e4066Sahrens } 331fa9e4066Sahrens 332fa9e4066Sahrens kmem_free(packed, size); 333fa9e4066Sahrens 334fa9e4066Sahrens *nvp = config; 335fa9e4066Sahrens return (0); 336fa9e4066Sahrens } 337fa9e4066Sahrens 338fa9e4066Sahrens static int 339fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 340fa9e4066Sahrens { 341fa9e4066Sahrens int error; 342fa9e4066Sahrens nvlist_t *config; 343fa9e4066Sahrens 344fa9e4066Sahrens if ((error = get_config(zc, &config)) != 0) 345fa9e4066Sahrens return (error); 346fa9e4066Sahrens 347fa9e4066Sahrens error = spa_create(zc->zc_name, config, zc->zc_root[0] == '\0' ? 348fa9e4066Sahrens NULL : zc->zc_root); 349fa9e4066Sahrens 350fa9e4066Sahrens nvlist_free(config); 351fa9e4066Sahrens 352fa9e4066Sahrens return (error); 353fa9e4066Sahrens } 354fa9e4066Sahrens 355fa9e4066Sahrens static int 356fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 357fa9e4066Sahrens { 358fa9e4066Sahrens return (spa_destroy(zc->zc_name)); 359fa9e4066Sahrens } 360fa9e4066Sahrens 361fa9e4066Sahrens static int 362fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 363fa9e4066Sahrens { 364fa9e4066Sahrens int error; 365fa9e4066Sahrens nvlist_t *config; 366fa9e4066Sahrens uint64_t guid; 367fa9e4066Sahrens 368fa9e4066Sahrens if ((error = get_config(zc, &config)) != 0) 369fa9e4066Sahrens return (error); 370fa9e4066Sahrens 371fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 372fa9e4066Sahrens guid != zc->zc_pool_guid) 373fa9e4066Sahrens error = EINVAL; 374fa9e4066Sahrens else 375fa9e4066Sahrens error = spa_import(zc->zc_name, config, 376fa9e4066Sahrens zc->zc_root[0] == '\0' ? NULL : zc->zc_root); 377fa9e4066Sahrens 378fa9e4066Sahrens nvlist_free(config); 379fa9e4066Sahrens 380fa9e4066Sahrens return (error); 381fa9e4066Sahrens } 382fa9e4066Sahrens 383fa9e4066Sahrens static int 384fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 385fa9e4066Sahrens { 386fa9e4066Sahrens return (spa_export(zc->zc_name)); 387fa9e4066Sahrens } 388fa9e4066Sahrens 389fa9e4066Sahrens static int 390fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 391fa9e4066Sahrens { 392fa9e4066Sahrens nvlist_t *configs; 393fa9e4066Sahrens char *packed = NULL; 394fa9e4066Sahrens size_t size = 0; 395fa9e4066Sahrens int error; 396fa9e4066Sahrens 397fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 398fa9e4066Sahrens return (EEXIST); 399fa9e4066Sahrens 400fa9e4066Sahrens VERIFY(nvlist_pack(configs, &packed, &size, NV_ENCODE_NATIVE, 0) == 0); 401fa9e4066Sahrens 402fa9e4066Sahrens if (size > zc->zc_config_dst_size) 403fa9e4066Sahrens error = ENOMEM; 404fa9e4066Sahrens else 405fa9e4066Sahrens error = xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst, 406fa9e4066Sahrens size); 407fa9e4066Sahrens 408fa9e4066Sahrens zc->zc_config_dst_size = size; 409fa9e4066Sahrens 410fa9e4066Sahrens kmem_free(packed, size); 411fa9e4066Sahrens nvlist_free(configs); 412fa9e4066Sahrens 413fa9e4066Sahrens return (error); 414fa9e4066Sahrens } 415fa9e4066Sahrens 416fa9e4066Sahrens static int 417fa9e4066Sahrens zfs_ioc_pool_guid(zfs_cmd_t *zc) 418fa9e4066Sahrens { 419fa9e4066Sahrens spa_t *spa; 420fa9e4066Sahrens int error; 421fa9e4066Sahrens 422fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 423fa9e4066Sahrens if (error == 0) { 424fa9e4066Sahrens zc->zc_pool_guid = spa_guid(spa); 425fa9e4066Sahrens spa_close(spa, FTAG); 426fa9e4066Sahrens } 427fa9e4066Sahrens return (error); 428fa9e4066Sahrens } 429fa9e4066Sahrens 430fa9e4066Sahrens static int 431fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 432fa9e4066Sahrens { 433fa9e4066Sahrens nvlist_t *config; 434fa9e4066Sahrens char *packed = NULL; 435fa9e4066Sahrens size_t size = 0; 436fa9e4066Sahrens int error; 437fa9e4066Sahrens 438fa9e4066Sahrens error = spa_get_stats(zc->zc_name, &config); 439fa9e4066Sahrens 440fa9e4066Sahrens if (config != NULL) { 441fa9e4066Sahrens VERIFY(nvlist_pack(config, &packed, &size, 442fa9e4066Sahrens NV_ENCODE_NATIVE, 0) == 0); 443fa9e4066Sahrens 444fa9e4066Sahrens if (size > zc->zc_config_dst_size) 445fa9e4066Sahrens error = ENOMEM; 446fa9e4066Sahrens else if (xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst, 447fa9e4066Sahrens size)) 448fa9e4066Sahrens error = EFAULT; 449fa9e4066Sahrens 450fa9e4066Sahrens zc->zc_config_dst_size = size; 451fa9e4066Sahrens 452fa9e4066Sahrens kmem_free(packed, size); 453fa9e4066Sahrens nvlist_free(config); 454fa9e4066Sahrens } else { 455fa9e4066Sahrens ASSERT(error != 0); 456fa9e4066Sahrens } 457fa9e4066Sahrens 458fa9e4066Sahrens return (error); 459fa9e4066Sahrens } 460fa9e4066Sahrens 461fa9e4066Sahrens /* 462fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 463fa9e4066Sahrens * user land knows which devices are available and overall pool health. 464fa9e4066Sahrens */ 465fa9e4066Sahrens static int 466fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 467fa9e4066Sahrens { 468fa9e4066Sahrens nvlist_t *tryconfig, *config; 469fa9e4066Sahrens char *packed = NULL; 470fa9e4066Sahrens size_t size = 0; 471fa9e4066Sahrens int error; 472fa9e4066Sahrens 473fa9e4066Sahrens if ((error = get_config(zc, &tryconfig)) != 0) 474fa9e4066Sahrens return (error); 475fa9e4066Sahrens 476fa9e4066Sahrens config = spa_tryimport(tryconfig); 477fa9e4066Sahrens 478fa9e4066Sahrens nvlist_free(tryconfig); 479fa9e4066Sahrens 480fa9e4066Sahrens if (config == NULL) 481fa9e4066Sahrens return (EINVAL); 482fa9e4066Sahrens 483fa9e4066Sahrens VERIFY(nvlist_pack(config, &packed, &size, NV_ENCODE_NATIVE, 0) == 0); 484fa9e4066Sahrens 485fa9e4066Sahrens if (size > zc->zc_config_dst_size) 486fa9e4066Sahrens error = ENOMEM; 487fa9e4066Sahrens else 488fa9e4066Sahrens error = xcopyout(packed, (void *)(uintptr_t)zc->zc_config_dst, 489fa9e4066Sahrens size); 490fa9e4066Sahrens 491fa9e4066Sahrens zc->zc_config_dst_size = size; 492fa9e4066Sahrens 493fa9e4066Sahrens kmem_free(packed, size); 494fa9e4066Sahrens nvlist_free(config); 495fa9e4066Sahrens 496fa9e4066Sahrens return (error); 497fa9e4066Sahrens } 498fa9e4066Sahrens 499fa9e4066Sahrens static int 500fa9e4066Sahrens zfs_ioc_pool_scrub(zfs_cmd_t *zc) 501fa9e4066Sahrens { 502fa9e4066Sahrens spa_t *spa; 503fa9e4066Sahrens int error; 504fa9e4066Sahrens 505fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 506fa9e4066Sahrens if (error == 0) { 507fa9e4066Sahrens error = spa_scrub(spa, zc->zc_cookie, B_FALSE); 508fa9e4066Sahrens spa_close(spa, FTAG); 509fa9e4066Sahrens } 510fa9e4066Sahrens return (error); 511fa9e4066Sahrens } 512fa9e4066Sahrens 513fa9e4066Sahrens static int 514fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 515fa9e4066Sahrens { 516fa9e4066Sahrens spa_t *spa; 517fa9e4066Sahrens int error; 518fa9e4066Sahrens 519fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 520fa9e4066Sahrens if (error == 0) { 521fa9e4066Sahrens spa_freeze(spa); 522fa9e4066Sahrens spa_close(spa, FTAG); 523fa9e4066Sahrens } 524fa9e4066Sahrens return (error); 525fa9e4066Sahrens } 526fa9e4066Sahrens 527fa9e4066Sahrens static int 528fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 529fa9e4066Sahrens { 530fa9e4066Sahrens spa_t *spa; 531fa9e4066Sahrens int error; 532fa9e4066Sahrens nvlist_t *config; 533fa9e4066Sahrens 534fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 535fa9e4066Sahrens if (error != 0) 536fa9e4066Sahrens return (error); 537fa9e4066Sahrens 538fa9e4066Sahrens if ((error = get_config(zc, &config)) == 0) { 539fa9e4066Sahrens error = spa_vdev_add(spa, config); 540fa9e4066Sahrens nvlist_free(config); 541fa9e4066Sahrens } 542fa9e4066Sahrens 543fa9e4066Sahrens spa_close(spa, FTAG); 544fa9e4066Sahrens return (error); 545fa9e4066Sahrens } 546fa9e4066Sahrens 547fa9e4066Sahrens /* ARGSUSED */ 548fa9e4066Sahrens static int 549fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 550fa9e4066Sahrens { 551fa9e4066Sahrens return (ENOTSUP); 552fa9e4066Sahrens } 553fa9e4066Sahrens 554fa9e4066Sahrens static int 555fa9e4066Sahrens zfs_ioc_vdev_online(zfs_cmd_t *zc) 556fa9e4066Sahrens { 557fa9e4066Sahrens spa_t *spa; 558fa9e4066Sahrens char *path = zc->zc_prop_value; 559fa9e4066Sahrens int error; 560fa9e4066Sahrens 561fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 562fa9e4066Sahrens if (error != 0) 563fa9e4066Sahrens return (error); 564fa9e4066Sahrens error = vdev_online(spa, path); 565fa9e4066Sahrens spa_close(spa, FTAG); 566fa9e4066Sahrens return (error); 567fa9e4066Sahrens } 568fa9e4066Sahrens 569fa9e4066Sahrens static int 570fa9e4066Sahrens zfs_ioc_vdev_offline(zfs_cmd_t *zc) 571fa9e4066Sahrens { 572fa9e4066Sahrens spa_t *spa; 573fa9e4066Sahrens char *path = zc->zc_prop_value; 574fa9e4066Sahrens int error; 575fa9e4066Sahrens 576fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 577fa9e4066Sahrens if (error != 0) 578fa9e4066Sahrens return (error); 579fa9e4066Sahrens error = vdev_offline(spa, path); 580fa9e4066Sahrens spa_close(spa, FTAG); 581fa9e4066Sahrens return (error); 582fa9e4066Sahrens } 583fa9e4066Sahrens 584fa9e4066Sahrens static int 585fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 586fa9e4066Sahrens { 587fa9e4066Sahrens spa_t *spa; 588fa9e4066Sahrens char *path = zc->zc_prop_value; 589fa9e4066Sahrens int replacing = zc->zc_cookie; 590fa9e4066Sahrens nvlist_t *config; 591fa9e4066Sahrens int error; 592fa9e4066Sahrens 593fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 594fa9e4066Sahrens if (error != 0) 595fa9e4066Sahrens return (error); 596fa9e4066Sahrens 597fa9e4066Sahrens if ((error = get_config(zc, &config)) == 0) { 598fa9e4066Sahrens error = spa_vdev_attach(spa, path, config, replacing); 599fa9e4066Sahrens nvlist_free(config); 600fa9e4066Sahrens } 601fa9e4066Sahrens 602fa9e4066Sahrens spa_close(spa, FTAG); 603fa9e4066Sahrens return (error); 604fa9e4066Sahrens } 605fa9e4066Sahrens 606fa9e4066Sahrens static int 607fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 608fa9e4066Sahrens { 609fa9e4066Sahrens spa_t *spa; 610fa9e4066Sahrens char *path = zc->zc_prop_value; 611fa9e4066Sahrens int error; 612fa9e4066Sahrens 613fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 614fa9e4066Sahrens if (error != 0) 615fa9e4066Sahrens return (error); 616fa9e4066Sahrens 617fa9e4066Sahrens error = spa_vdev_detach(spa, path, 0, B_FALSE); 618fa9e4066Sahrens 619fa9e4066Sahrens spa_close(spa, FTAG); 620fa9e4066Sahrens return (error); 621fa9e4066Sahrens } 622fa9e4066Sahrens 623fa9e4066Sahrens static int 624fa9e4066Sahrens zfs_get_stats(zfs_cmd_t *zc) 625fa9e4066Sahrens { 626fa9e4066Sahrens char *name = zc->zc_name; 627fa9e4066Sahrens zfs_stats_t *zs = &zc->zc_zfs_stats; 628fa9e4066Sahrens int error; 629fa9e4066Sahrens 630fa9e4066Sahrens bzero(zs, sizeof (zfs_stats_t)); 631fa9e4066Sahrens 632fa9e4066Sahrens if ((error = dsl_prop_get_integer(name, "atime", 633fa9e4066Sahrens &zs->zs_atime, zs->zs_atime_setpoint)) != 0 || 634fa9e4066Sahrens (error = dsl_prop_get_integer(name, "recordsize", 635fa9e4066Sahrens &zs->zs_recordsize, zs->zs_recordsize_setpoint)) != 0 || 636fa9e4066Sahrens (error = dsl_prop_get_integer(name, "readonly", 637fa9e4066Sahrens &zs->zs_readonly, zs->zs_readonly_setpoint)) != 0 || 638fa9e4066Sahrens (error = dsl_prop_get_integer(name, "devices", 639fa9e4066Sahrens &zs->zs_devices, zs->zs_devices_setpoint)) != 0 || 640fa9e4066Sahrens (error = dsl_prop_get_integer(name, "setuid", 641fa9e4066Sahrens &zs->zs_setuid, zs->zs_setuid_setpoint)) != 0 || 642fa9e4066Sahrens (error = dsl_prop_get_integer(name, "exec", 643fa9e4066Sahrens &zs->zs_exec, zs->zs_exec_setpoint)) != 0 || 644fa9e4066Sahrens (error = dsl_prop_get_string(name, "mountpoint", zs->zs_mountpoint, 645fa9e4066Sahrens sizeof (zs->zs_mountpoint), zs->zs_mountpoint_setpoint)) != 0 || 646fa9e4066Sahrens (error = dsl_prop_get_string(name, "sharenfs", zs->zs_sharenfs, 647fa9e4066Sahrens sizeof (zs->zs_sharenfs), zs->zs_sharenfs_setpoint)) != 0 || 648fa9e4066Sahrens (error = dsl_prop_get_integer(name, "aclmode", 649fa9e4066Sahrens &zs->zs_acl_mode, zs->zs_acl_mode_setpoint)) != 0 || 650fa9e4066Sahrens (error = dsl_prop_get_integer(name, "snapdir", 651fa9e4066Sahrens &zs->zs_snapdir, zs->zs_snapdir_setpoint)) != 0 || 652fa9e4066Sahrens (error = dsl_prop_get_integer(name, "aclinherit", 653fa9e4066Sahrens &zs->zs_acl_inherit, zs->zs_acl_inherit_setpoint)) != 0) 654fa9e4066Sahrens return (error); 655fa9e4066Sahrens 656fa9e4066Sahrens return (0); 657fa9e4066Sahrens } 658fa9e4066Sahrens 659fa9e4066Sahrens static int 660fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc) 661fa9e4066Sahrens { 662fa9e4066Sahrens objset_t *os = NULL; 663fa9e4066Sahrens int error; 664fa9e4066Sahrens 665fa9e4066Sahrens retry: 666fa9e4066Sahrens error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, 667fa9e4066Sahrens DS_MODE_STANDARD | DS_MODE_READONLY, &os); 668fa9e4066Sahrens if (error != 0) { 669fa9e4066Sahrens /* 670fa9e4066Sahrens * This is ugly: dmu_objset_open() can return EBUSY if 671fa9e4066Sahrens * the objset is held exclusively. Fortunately this hold is 672fa9e4066Sahrens * only for a short while, so we retry here. 673fa9e4066Sahrens * This avoids user code having to handle EBUSY, 674fa9e4066Sahrens * for example for a "zfs list". 675fa9e4066Sahrens */ 676fa9e4066Sahrens if (error == EBUSY) { 677fa9e4066Sahrens delay(1); 678fa9e4066Sahrens goto retry; 679fa9e4066Sahrens } 680fa9e4066Sahrens return (error); 681fa9e4066Sahrens } 682fa9e4066Sahrens 683fa9e4066Sahrens dmu_objset_stats(os, &zc->zc_objset_stats); 684fa9e4066Sahrens 685fa9e4066Sahrens switch (zc->zc_objset_stats.dds_type) { 686fa9e4066Sahrens 687fa9e4066Sahrens case DMU_OST_ZFS: 688fa9e4066Sahrens error = zfs_get_stats(zc); 689fa9e4066Sahrens break; 690fa9e4066Sahrens 691fa9e4066Sahrens case DMU_OST_ZVOL: 692fa9e4066Sahrens error = zvol_get_stats(zc, os); 693fa9e4066Sahrens break; 694fa9e4066Sahrens } 695fa9e4066Sahrens 696fa9e4066Sahrens dmu_objset_close(os); 697fa9e4066Sahrens return (error); 698fa9e4066Sahrens } 699fa9e4066Sahrens 700fa9e4066Sahrens static int 701fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 702fa9e4066Sahrens { 70387e5029aSahrens objset_t *os; 704fa9e4066Sahrens int error; 705fa9e4066Sahrens char *p; 706fa9e4066Sahrens 70787e5029aSahrens retry: 70887e5029aSahrens error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, 70987e5029aSahrens DS_MODE_STANDARD | DS_MODE_READONLY, &os); 71087e5029aSahrens if (error != 0) { 71187e5029aSahrens /* 71287e5029aSahrens * This is ugly: dmu_objset_open() can return EBUSY if 71387e5029aSahrens * the objset is held exclusively. Fortunately this hold is 71487e5029aSahrens * only for a short while, so we retry here. 71587e5029aSahrens * This avoids user code having to handle EBUSY, 71687e5029aSahrens * for example for a "zfs list". 71787e5029aSahrens */ 71887e5029aSahrens if (error == EBUSY) { 71987e5029aSahrens delay(1); 72087e5029aSahrens goto retry; 72187e5029aSahrens } 72287e5029aSahrens if (error == ENOENT) 72387e5029aSahrens error = ESRCH; 72487e5029aSahrens return (error); 725fa9e4066Sahrens } 726fa9e4066Sahrens 727fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 728fa9e4066Sahrens if (p == NULL || p[1] != '\0') 729fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 730fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 731fa9e4066Sahrens 732fa9e4066Sahrens do { 73387e5029aSahrens error = dmu_dir_list_next(os, 73487e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 73587e5029aSahrens NULL, &zc->zc_cookie); 736fa9e4066Sahrens if (error == ENOENT) 737fa9e4066Sahrens error = ESRCH; 73887e5029aSahrens } while (error == 0 && !INGLOBALZONE(curproc) && 739fa9e4066Sahrens !zone_dataset_visible(zc->zc_name, NULL)); 740fa9e4066Sahrens 741fa9e4066Sahrens /* 74287e5029aSahrens * If it's a hidden dataset (ie. with a '$' in its name), don't 74387e5029aSahrens * try to get stats for it. Userland will skip over it. 744fa9e4066Sahrens */ 74587e5029aSahrens if (error == 0 && strchr(zc->zc_name, '$') == NULL) 74687e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 747fa9e4066Sahrens 74887e5029aSahrens dmu_objset_close(os); 749fa9e4066Sahrens return (error); 750fa9e4066Sahrens } 751fa9e4066Sahrens 752fa9e4066Sahrens static int 753fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 754fa9e4066Sahrens { 75587e5029aSahrens objset_t *os; 756fa9e4066Sahrens int error; 757fa9e4066Sahrens 758fa9e4066Sahrens retry: 75987e5029aSahrens error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, 76087e5029aSahrens DS_MODE_STANDARD | DS_MODE_READONLY, &os); 76187e5029aSahrens if (error != 0) { 762fa9e4066Sahrens /* 76387e5029aSahrens * This is ugly: dmu_objset_open() can return EBUSY if 764fa9e4066Sahrens * the objset is held exclusively. Fortunately this hold is 765fa9e4066Sahrens * only for a short while, so we retry here. 766fa9e4066Sahrens * This avoids user code having to handle EBUSY, 76787e5029aSahrens * for example for a "zfs list". 768fa9e4066Sahrens */ 769fa9e4066Sahrens if (error == EBUSY) { 770fa9e4066Sahrens delay(1); 771fa9e4066Sahrens goto retry; 772fa9e4066Sahrens } 773fa9e4066Sahrens if (error == ENOENT) 77487e5029aSahrens error = ESRCH; 775fa9e4066Sahrens return (error); 776fa9e4066Sahrens } 777fa9e4066Sahrens 778b81d61a6Slling /* 779b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 780b81d61a6Slling * so exit immediately. 781b81d61a6Slling */ 782b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 78387e5029aSahrens dmu_objset_close(os); 784b81d61a6Slling return (ESRCH); 785fa9e4066Sahrens } 786fa9e4066Sahrens 78787e5029aSahrens error = dmu_snapshot_list_next(os, 78887e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 78987e5029aSahrens zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie); 79087e5029aSahrens if (error == ENOENT) 79187e5029aSahrens error = ESRCH; 792fa9e4066Sahrens 79387e5029aSahrens if (error == 0) 79487e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 795fa9e4066Sahrens 79687e5029aSahrens dmu_objset_close(os); 797fa9e4066Sahrens return (error); 798fa9e4066Sahrens } 799fa9e4066Sahrens 800fa9e4066Sahrens static int 801fa9e4066Sahrens zfs_ioc_set_prop(zfs_cmd_t *zc) 802fa9e4066Sahrens { 803fa9e4066Sahrens return (dsl_prop_set(zc->zc_name, zc->zc_prop_name, 804fa9e4066Sahrens zc->zc_intsz, zc->zc_numints, zc->zc_prop_value)); 805fa9e4066Sahrens } 806fa9e4066Sahrens 807fa9e4066Sahrens static int 808fa9e4066Sahrens zfs_ioc_set_quota(zfs_cmd_t *zc) 809fa9e4066Sahrens { 810fa9e4066Sahrens return (dsl_dir_set_quota(zc->zc_name, zc->zc_cookie)); 811fa9e4066Sahrens } 812fa9e4066Sahrens 813fa9e4066Sahrens static int 814fa9e4066Sahrens zfs_ioc_set_reservation(zfs_cmd_t *zc) 815fa9e4066Sahrens { 816fa9e4066Sahrens return (dsl_dir_set_reservation(zc->zc_name, zc->zc_cookie)); 817fa9e4066Sahrens } 818fa9e4066Sahrens 819fa9e4066Sahrens static int 820fa9e4066Sahrens zfs_ioc_set_volsize(zfs_cmd_t *zc) 821fa9e4066Sahrens { 822fa9e4066Sahrens return (zvol_set_volsize(zc)); 823fa9e4066Sahrens } 824fa9e4066Sahrens 825fa9e4066Sahrens static int 826fa9e4066Sahrens zfs_ioc_set_volblocksize(zfs_cmd_t *zc) 827fa9e4066Sahrens { 828fa9e4066Sahrens return (zvol_set_volblocksize(zc)); 829fa9e4066Sahrens } 830fa9e4066Sahrens 831fa9e4066Sahrens static int 832fa9e4066Sahrens zfs_ioc_create_minor(zfs_cmd_t *zc) 833fa9e4066Sahrens { 834fa9e4066Sahrens return (zvol_create_minor(zc)); 835fa9e4066Sahrens } 836fa9e4066Sahrens 837fa9e4066Sahrens static int 838fa9e4066Sahrens zfs_ioc_remove_minor(zfs_cmd_t *zc) 839fa9e4066Sahrens { 840fa9e4066Sahrens return (zvol_remove_minor(zc)); 841fa9e4066Sahrens } 842fa9e4066Sahrens 843fa9e4066Sahrens /* 844fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 845fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 846fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 847fa9e4066Sahrens */ 848fa9e4066Sahrens static vfs_t * 849fa9e4066Sahrens zfs_get_vfs(const char *resource) 850fa9e4066Sahrens { 851fa9e4066Sahrens struct vfs *vfsp; 852fa9e4066Sahrens struct vfs *vfs_found = NULL; 853fa9e4066Sahrens 854fa9e4066Sahrens vfs_list_read_lock(); 855fa9e4066Sahrens vfsp = rootvfs; 856fa9e4066Sahrens do { 857fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 858fa9e4066Sahrens VFS_HOLD(vfsp); 859fa9e4066Sahrens vfs_found = vfsp; 860fa9e4066Sahrens break; 861fa9e4066Sahrens } 862fa9e4066Sahrens vfsp = vfsp->vfs_next; 863fa9e4066Sahrens } while (vfsp != rootvfs); 864fa9e4066Sahrens vfs_list_unlock(); 865fa9e4066Sahrens return (vfs_found); 866fa9e4066Sahrens } 867fa9e4066Sahrens 868fa9e4066Sahrens static void 869fa9e4066Sahrens zfs_create_cb(objset_t *os, void *arg, dmu_tx_t *tx) 870fa9e4066Sahrens { 871fa9e4066Sahrens zfs_cmd_t *zc = arg; 872fa9e4066Sahrens zfs_create_fs(os, (cred_t *)(uintptr_t)zc->zc_cred, tx); 873fa9e4066Sahrens } 874fa9e4066Sahrens 875fa9e4066Sahrens static int 876fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc) 877fa9e4066Sahrens { 878fa9e4066Sahrens objset_t *clone; 879fa9e4066Sahrens int error = 0; 880fa9e4066Sahrens void (*cbfunc)(objset_t *os, void *arg, dmu_tx_t *tx); 881fa9e4066Sahrens dmu_objset_type_t type = zc->zc_objset_type; 882fa9e4066Sahrens 883fa9e4066Sahrens switch (type) { 884fa9e4066Sahrens 885fa9e4066Sahrens case DMU_OST_ZFS: 886fa9e4066Sahrens cbfunc = zfs_create_cb; 887fa9e4066Sahrens break; 888fa9e4066Sahrens 889fa9e4066Sahrens case DMU_OST_ZVOL: 890fa9e4066Sahrens cbfunc = zvol_create_cb; 891fa9e4066Sahrens break; 892fa9e4066Sahrens 893fa9e4066Sahrens default: 894fa9e4066Sahrens return (EINVAL); 895fa9e4066Sahrens } 896fa9e4066Sahrens 897fa9e4066Sahrens if (zc->zc_filename[0] != '\0') { 898fa9e4066Sahrens /* 899fa9e4066Sahrens * We're creating a clone of an existing snapshot. 900fa9e4066Sahrens */ 901fa9e4066Sahrens zc->zc_filename[sizeof (zc->zc_filename) - 1] = '\0'; 902fa9e4066Sahrens if (dataset_namecheck(zc->zc_filename, NULL, NULL) != 0) 903fa9e4066Sahrens return (EINVAL); 904fa9e4066Sahrens 905fa9e4066Sahrens error = dmu_objset_open(zc->zc_filename, type, 906fa9e4066Sahrens DS_MODE_STANDARD | DS_MODE_READONLY, &clone); 907fa9e4066Sahrens if (error) 908fa9e4066Sahrens return (error); 909fa9e4066Sahrens error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL); 910fa9e4066Sahrens dmu_objset_close(clone); 911fa9e4066Sahrens } else if (strchr(zc->zc_name, '@') != 0) { 912fa9e4066Sahrens /* 913fa9e4066Sahrens * We're taking a snapshot of an existing dataset. 914fa9e4066Sahrens */ 915fa9e4066Sahrens error = dmu_objset_create(zc->zc_name, type, NULL, NULL, NULL); 916fa9e4066Sahrens } else { 917fa9e4066Sahrens /* 918fa9e4066Sahrens * We're creating a new dataset. 919fa9e4066Sahrens */ 920fa9e4066Sahrens if (type == DMU_OST_ZVOL) { 921*5c5460e9Seschrock 922fa9e4066Sahrens if ((error = zvol_check_volblocksize(zc)) != 0) 923fa9e4066Sahrens return (error); 924*5c5460e9Seschrock 925*5c5460e9Seschrock if ((error = zvol_check_volsize(zc, 926*5c5460e9Seschrock zc->zc_volblocksize)) != 0) 927*5c5460e9Seschrock return (error); 928fa9e4066Sahrens } 929fa9e4066Sahrens error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, zc); 930fa9e4066Sahrens } 931fa9e4066Sahrens return (error); 932fa9e4066Sahrens } 933fa9e4066Sahrens 934fa9e4066Sahrens static int 935fa9e4066Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 936fa9e4066Sahrens { 937fa9e4066Sahrens if (strchr(zc->zc_name, '@') != NULL && 938fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 939fa9e4066Sahrens vfs_t *vfsp; 940fa9e4066Sahrens int err; 941fa9e4066Sahrens 942fa9e4066Sahrens /* 943fa9e4066Sahrens * Snapshots under .zfs control must be unmounted 944fa9e4066Sahrens * before they can be destroyed. 945fa9e4066Sahrens */ 946fa9e4066Sahrens if ((vfsp = zfs_get_vfs(zc->zc_name)) != NULL) { 947fa9e4066Sahrens /* 948fa9e4066Sahrens * Always force the unmount for snapshots. 949fa9e4066Sahrens */ 950fa9e4066Sahrens int flag = MS_FORCE; 951fa9e4066Sahrens 952fa9e4066Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 953fa9e4066Sahrens VFS_RELE(vfsp); 954fa9e4066Sahrens return (err); 955fa9e4066Sahrens } 956fa9e4066Sahrens VFS_RELE(vfsp); 957fa9e4066Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0) 958fa9e4066Sahrens return (err); 959fa9e4066Sahrens } 960fa9e4066Sahrens } 961fa9e4066Sahrens 962fa9e4066Sahrens return (dmu_objset_destroy(zc->zc_name)); 963fa9e4066Sahrens } 964fa9e4066Sahrens 965fa9e4066Sahrens static int 966fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc) 967fa9e4066Sahrens { 968fa9e4066Sahrens return (dmu_objset_rollback(zc->zc_name)); 969fa9e4066Sahrens } 970fa9e4066Sahrens 971fa9e4066Sahrens static int 972fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 973fa9e4066Sahrens { 974fa9e4066Sahrens zc->zc_prop_value[sizeof (zc->zc_prop_value) - 1] = '\0'; 975fa9e4066Sahrens if (dataset_namecheck(zc->zc_prop_value, NULL, NULL) != 0) 976fa9e4066Sahrens return (EINVAL); 977fa9e4066Sahrens 978fa9e4066Sahrens if (strchr(zc->zc_name, '@') != NULL && 979fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 980fa9e4066Sahrens vfs_t *vfsp; 981fa9e4066Sahrens int err; 982fa9e4066Sahrens 983fa9e4066Sahrens /* 984fa9e4066Sahrens * Snapshots under .zfs control must be unmounted 985fa9e4066Sahrens * before they can be renamed. 986fa9e4066Sahrens */ 987fa9e4066Sahrens if ((vfsp = zfs_get_vfs(zc->zc_name)) != NULL) { 988fa9e4066Sahrens /* 989fa9e4066Sahrens * Always force the unmount for snapshots. 990fa9e4066Sahrens */ 991fa9e4066Sahrens int flag = MS_FORCE; 992fa9e4066Sahrens 993fa9e4066Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 994fa9e4066Sahrens VFS_RELE(vfsp); 995fa9e4066Sahrens return (err); 996fa9e4066Sahrens } 997fa9e4066Sahrens VFS_RELE(vfsp); 998fa9e4066Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0) 999fa9e4066Sahrens return (err); 1000fa9e4066Sahrens } 1001fa9e4066Sahrens } 1002fa9e4066Sahrens 1003fa9e4066Sahrens return (dmu_objset_rename(zc->zc_name, zc->zc_prop_value)); 1004fa9e4066Sahrens } 1005fa9e4066Sahrens 1006fa9e4066Sahrens static int 1007fa9e4066Sahrens zfs_ioc_recvbackup(zfs_cmd_t *zc) 1008fa9e4066Sahrens { 1009fa9e4066Sahrens file_t *fp; 1010fa9e4066Sahrens int error, fd; 1011fa9e4066Sahrens 1012fa9e4066Sahrens fd = zc->zc_cookie; 1013fa9e4066Sahrens fp = getf(fd); 1014fa9e4066Sahrens if (fp == NULL) 1015fa9e4066Sahrens return (EBADF); 1016fa9e4066Sahrens error = dmu_recvbackup(&zc->zc_begin_record, &zc->zc_cookie, 1017fa9e4066Sahrens fp->f_vnode, fp->f_offset); 1018fa9e4066Sahrens releasef(fd); 1019fa9e4066Sahrens return (error); 1020fa9e4066Sahrens } 1021fa9e4066Sahrens 1022fa9e4066Sahrens static int 1023fa9e4066Sahrens zfs_ioc_sendbackup(zfs_cmd_t *zc) 1024fa9e4066Sahrens { 1025fa9e4066Sahrens objset_t *fromsnap = NULL; 1026fa9e4066Sahrens objset_t *tosnap; 1027fa9e4066Sahrens file_t *fp; 1028fa9e4066Sahrens int error; 1029fa9e4066Sahrens 1030fa9e4066Sahrens error = dmu_objset_open(zc->zc_name, DMU_OST_ANY, 1031fa9e4066Sahrens DS_MODE_STANDARD | DS_MODE_READONLY, &tosnap); 1032fa9e4066Sahrens if (error) 1033fa9e4066Sahrens return (error); 1034fa9e4066Sahrens 1035fa9e4066Sahrens if (zc->zc_prop_value[0] != '\0') { 1036fa9e4066Sahrens error = dmu_objset_open(zc->zc_prop_value, DMU_OST_ANY, 1037fa9e4066Sahrens DS_MODE_STANDARD | DS_MODE_READONLY, &fromsnap); 1038fa9e4066Sahrens if (error) { 1039fa9e4066Sahrens dmu_objset_close(tosnap); 1040fa9e4066Sahrens return (error); 1041fa9e4066Sahrens } 1042fa9e4066Sahrens } 1043fa9e4066Sahrens 1044fa9e4066Sahrens fp = getf(zc->zc_cookie); 1045fa9e4066Sahrens if (fp == NULL) { 1046fa9e4066Sahrens dmu_objset_close(tosnap); 1047fa9e4066Sahrens if (fromsnap) 1048fa9e4066Sahrens dmu_objset_close(fromsnap); 1049fa9e4066Sahrens return (EBADF); 1050fa9e4066Sahrens } 1051fa9e4066Sahrens 1052fa9e4066Sahrens error = dmu_sendbackup(tosnap, fromsnap, fp->f_vnode); 1053fa9e4066Sahrens 1054fa9e4066Sahrens releasef(zc->zc_cookie); 1055fa9e4066Sahrens if (fromsnap) 1056fa9e4066Sahrens dmu_objset_close(fromsnap); 1057fa9e4066Sahrens dmu_objset_close(tosnap); 1058fa9e4066Sahrens return (error); 1059fa9e4066Sahrens } 1060fa9e4066Sahrens 1061fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = { 1062fa9e4066Sahrens { zfs_ioc_pool_create, zfs_secpolicy_config, pool_name }, 1063fa9e4066Sahrens { zfs_ioc_pool_destroy, zfs_secpolicy_config, pool_name }, 1064fa9e4066Sahrens { zfs_ioc_pool_import, zfs_secpolicy_config, pool_name }, 1065fa9e4066Sahrens { zfs_ioc_pool_export, zfs_secpolicy_config, pool_name }, 1066fa9e4066Sahrens { zfs_ioc_pool_configs, zfs_secpolicy_none, no_name }, 1067fa9e4066Sahrens { zfs_ioc_pool_guid, zfs_secpolicy_read, pool_name }, 1068fa9e4066Sahrens { zfs_ioc_pool_stats, zfs_secpolicy_read, pool_name }, 1069fa9e4066Sahrens { zfs_ioc_pool_tryimport, zfs_secpolicy_config, no_name }, 1070fa9e4066Sahrens { zfs_ioc_pool_scrub, zfs_secpolicy_config, pool_name }, 1071fa9e4066Sahrens { zfs_ioc_pool_freeze, zfs_secpolicy_config, no_name }, 1072fa9e4066Sahrens { zfs_ioc_vdev_add, zfs_secpolicy_config, pool_name }, 1073fa9e4066Sahrens { zfs_ioc_vdev_remove, zfs_secpolicy_config, pool_name }, 1074fa9e4066Sahrens { zfs_ioc_vdev_online, zfs_secpolicy_config, pool_name }, 1075fa9e4066Sahrens { zfs_ioc_vdev_offline, zfs_secpolicy_config, pool_name }, 1076fa9e4066Sahrens { zfs_ioc_vdev_attach, zfs_secpolicy_config, pool_name }, 1077fa9e4066Sahrens { zfs_ioc_vdev_detach, zfs_secpolicy_config, pool_name }, 1078fa9e4066Sahrens { zfs_ioc_objset_stats, zfs_secpolicy_read, dataset_name }, 1079fa9e4066Sahrens { zfs_ioc_dataset_list_next, zfs_secpolicy_read, dataset_name }, 1080fa9e4066Sahrens { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, dataset_name }, 1081fa9e4066Sahrens { zfs_ioc_set_prop, zfs_secpolicy_setprop, dataset_name }, 1082fa9e4066Sahrens { zfs_ioc_set_quota, zfs_secpolicy_quota, dataset_name }, 1083fa9e4066Sahrens { zfs_ioc_set_reservation, zfs_secpolicy_write, dataset_name }, 1084fa9e4066Sahrens { zfs_ioc_set_volsize, zfs_secpolicy_config, dataset_name }, 1085fa9e4066Sahrens { zfs_ioc_set_volblocksize, zfs_secpolicy_config, dataset_name }, 1086fa9e4066Sahrens { zfs_ioc_create_minor, zfs_secpolicy_config, dataset_name }, 1087fa9e4066Sahrens { zfs_ioc_remove_minor, zfs_secpolicy_config, dataset_name }, 1088fa9e4066Sahrens { zfs_ioc_create, zfs_secpolicy_parent, dataset_name }, 1089fa9e4066Sahrens { zfs_ioc_destroy, zfs_secpolicy_parent, dataset_name }, 1090fa9e4066Sahrens { zfs_ioc_rollback, zfs_secpolicy_write, dataset_name }, 1091fa9e4066Sahrens { zfs_ioc_rename, zfs_secpolicy_write, dataset_name }, 1092fa9e4066Sahrens { zfs_ioc_recvbackup, zfs_secpolicy_write, dataset_name }, 1093fa9e4066Sahrens { zfs_ioc_sendbackup, zfs_secpolicy_write, dataset_name }, 1094fa9e4066Sahrens }; 1095fa9e4066Sahrens 1096fa9e4066Sahrens static int 1097fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 1098fa9e4066Sahrens { 1099fa9e4066Sahrens zfs_cmd_t *zc; 1100fa9e4066Sahrens uint_t vec; 1101fa9e4066Sahrens int error; 1102fa9e4066Sahrens 1103fa9e4066Sahrens if (getminor(dev) != 0) 1104fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 1105fa9e4066Sahrens 1106fa9e4066Sahrens vec = cmd - ZFS_IOC; 1107fa9e4066Sahrens 1108fa9e4066Sahrens if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 1109fa9e4066Sahrens return (EINVAL); 1110fa9e4066Sahrens 1111fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 1112fa9e4066Sahrens 1113fa9e4066Sahrens error = xcopyin((void *)arg, zc, sizeof (zfs_cmd_t)); 1114fa9e4066Sahrens 1115fa9e4066Sahrens if (error == 0) { 1116fa9e4066Sahrens zc->zc_cred = (uintptr_t)cr; 1117fa9e4066Sahrens zc->zc_dev = dev; 1118fa9e4066Sahrens error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, 1119fa9e4066Sahrens zc->zc_prop_name, cr); 1120fa9e4066Sahrens } 1121fa9e4066Sahrens 1122fa9e4066Sahrens /* 1123fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 1124fa9e4066Sahrens * the lower layers. 1125fa9e4066Sahrens */ 1126fa9e4066Sahrens if (error == 0) { 1127fa9e4066Sahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 1128fa9e4066Sahrens switch (zfs_ioc_vec[vec].zvec_namecheck) { 1129fa9e4066Sahrens case pool_name: 1130fa9e4066Sahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 1131fa9e4066Sahrens error = EINVAL; 1132fa9e4066Sahrens break; 1133fa9e4066Sahrens 1134fa9e4066Sahrens case dataset_name: 1135fa9e4066Sahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 1136fa9e4066Sahrens error = EINVAL; 1137fa9e4066Sahrens break; 1138fa9e4066Sahrens } 1139fa9e4066Sahrens } 1140fa9e4066Sahrens 1141fa9e4066Sahrens if (error == 0) 1142fa9e4066Sahrens error = zfs_ioc_vec[vec].zvec_func(zc); 1143fa9e4066Sahrens 1144fa9e4066Sahrens if (error == 0 || error == ENOMEM) { 1145fa9e4066Sahrens int rc = xcopyout(zc, (void *)arg, sizeof (zfs_cmd_t)); 1146fa9e4066Sahrens if (error == 0) 1147fa9e4066Sahrens error = rc; 1148fa9e4066Sahrens } 1149fa9e4066Sahrens 1150fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 1151fa9e4066Sahrens return (error); 1152fa9e4066Sahrens } 1153fa9e4066Sahrens 1154fa9e4066Sahrens static int 1155fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1156fa9e4066Sahrens { 1157fa9e4066Sahrens if (cmd != DDI_ATTACH) 1158fa9e4066Sahrens return (DDI_FAILURE); 1159fa9e4066Sahrens 1160fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 1161fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 1162fa9e4066Sahrens return (DDI_FAILURE); 1163fa9e4066Sahrens 1164fa9e4066Sahrens zfs_dip = dip; 1165fa9e4066Sahrens 1166fa9e4066Sahrens ddi_report_dev(dip); 1167fa9e4066Sahrens 1168fa9e4066Sahrens return (DDI_SUCCESS); 1169fa9e4066Sahrens } 1170fa9e4066Sahrens 1171fa9e4066Sahrens static int 1172fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1173fa9e4066Sahrens { 1174fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 1175fa9e4066Sahrens return (DDI_FAILURE); 1176fa9e4066Sahrens 1177fa9e4066Sahrens if (cmd != DDI_DETACH) 1178fa9e4066Sahrens return (DDI_FAILURE); 1179fa9e4066Sahrens 1180fa9e4066Sahrens zfs_dip = NULL; 1181fa9e4066Sahrens 1182fa9e4066Sahrens ddi_prop_remove_all(dip); 1183fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 1184fa9e4066Sahrens 1185fa9e4066Sahrens return (DDI_SUCCESS); 1186fa9e4066Sahrens } 1187fa9e4066Sahrens 1188fa9e4066Sahrens /*ARGSUSED*/ 1189fa9e4066Sahrens static int 1190fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 1191fa9e4066Sahrens { 1192fa9e4066Sahrens switch (infocmd) { 1193fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 1194fa9e4066Sahrens *result = zfs_dip; 1195fa9e4066Sahrens return (DDI_SUCCESS); 1196fa9e4066Sahrens 1197fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 1198a0965f35Sbonwick *result = (void *)0; 1199fa9e4066Sahrens return (DDI_SUCCESS); 1200fa9e4066Sahrens } 1201fa9e4066Sahrens 1202fa9e4066Sahrens return (DDI_FAILURE); 1203fa9e4066Sahrens } 1204fa9e4066Sahrens 1205fa9e4066Sahrens /* 1206fa9e4066Sahrens * OK, so this is a little weird. 1207fa9e4066Sahrens * 1208fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 1209fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 1210fa9e4066Sahrens * 1211fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 1212fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 1213fa9e4066Sahrens */ 1214fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 1215fa9e4066Sahrens zvol_open, /* open */ 1216fa9e4066Sahrens zvol_close, /* close */ 1217fa9e4066Sahrens zvol_strategy, /* strategy */ 1218fa9e4066Sahrens nodev, /* print */ 1219fa9e4066Sahrens nodev, /* dump */ 1220fa9e4066Sahrens zvol_read, /* read */ 1221fa9e4066Sahrens zvol_write, /* write */ 1222fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 1223fa9e4066Sahrens nodev, /* devmap */ 1224fa9e4066Sahrens nodev, /* mmap */ 1225fa9e4066Sahrens nodev, /* segmap */ 1226fa9e4066Sahrens nochpoll, /* poll */ 1227fa9e4066Sahrens ddi_prop_op, /* prop_op */ 1228fa9e4066Sahrens NULL, /* streamtab */ 1229fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 1230fa9e4066Sahrens CB_REV, /* version */ 1231fa9e4066Sahrens zvol_aread, /* async read */ 1232fa9e4066Sahrens zvol_awrite, /* async write */ 1233fa9e4066Sahrens }; 1234fa9e4066Sahrens 1235fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 1236fa9e4066Sahrens DEVO_REV, /* version */ 1237fa9e4066Sahrens 0, /* refcnt */ 1238fa9e4066Sahrens zfs_info, /* info */ 1239fa9e4066Sahrens nulldev, /* identify */ 1240fa9e4066Sahrens nulldev, /* probe */ 1241fa9e4066Sahrens zfs_attach, /* attach */ 1242fa9e4066Sahrens zfs_detach, /* detach */ 1243fa9e4066Sahrens nodev, /* reset */ 1244fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 1245fa9e4066Sahrens NULL /* no bus operations */ 1246fa9e4066Sahrens }; 1247fa9e4066Sahrens 1248fa9e4066Sahrens static struct modldrv zfs_modldrv = { 1249fa9e4066Sahrens &mod_driverops, "ZFS storage pool version 1", &zfs_dev_ops 1250fa9e4066Sahrens }; 1251fa9e4066Sahrens 1252fa9e4066Sahrens static struct modlinkage modlinkage = { 1253fa9e4066Sahrens MODREV_1, 1254fa9e4066Sahrens (void *)&zfs_modlfs, 1255fa9e4066Sahrens (void *)&zfs_modldrv, 1256fa9e4066Sahrens NULL 1257fa9e4066Sahrens }; 1258fa9e4066Sahrens 1259fa9e4066Sahrens int 1260fa9e4066Sahrens _init(void) 1261fa9e4066Sahrens { 1262fa9e4066Sahrens int error; 1263fa9e4066Sahrens 1264a0965f35Sbonwick spa_init(FREAD | FWRITE); 1265a0965f35Sbonwick zfs_init(); 1266a0965f35Sbonwick zvol_init(); 1267a0965f35Sbonwick 1268a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 1269a0965f35Sbonwick zvol_fini(); 1270a0965f35Sbonwick zfs_fini(); 1271a0965f35Sbonwick spa_fini(); 1272fa9e4066Sahrens return (error); 1273a0965f35Sbonwick } 1274fa9e4066Sahrens 1275fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 1276fa9e4066Sahrens ASSERT(error == 0); 1277fa9e4066Sahrens 1278fa9e4066Sahrens return (0); 1279fa9e4066Sahrens } 1280fa9e4066Sahrens 1281fa9e4066Sahrens int 1282fa9e4066Sahrens _fini(void) 1283fa9e4066Sahrens { 1284fa9e4066Sahrens int error; 1285fa9e4066Sahrens 1286fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 1287fa9e4066Sahrens return (EBUSY); 1288fa9e4066Sahrens 1289fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 1290fa9e4066Sahrens return (error); 1291fa9e4066Sahrens 1292fa9e4066Sahrens zvol_fini(); 1293fa9e4066Sahrens zfs_fini(); 1294fa9e4066Sahrens spa_fini(); 1295fa9e4066Sahrens 1296fa9e4066Sahrens ldi_ident_release(zfs_li); 1297fa9e4066Sahrens zfs_li = NULL; 1298fa9e4066Sahrens 1299fa9e4066Sahrens return (error); 1300fa9e4066Sahrens } 1301fa9e4066Sahrens 1302fa9e4066Sahrens int 1303fa9e4066Sahrens _info(struct modinfo *modinfop) 1304fa9e4066Sahrens { 1305fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 1306fa9e4066Sahrens } 1307