1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5441d80aaSlling * Common Development and Distribution License (the "License"). 6441d80aaSlling * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21fa9e4066Sahrens /* 223f9d6ad7SLin Ling * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23fa9e4066Sahrens */ 24fa9e4066Sahrens 25fa9e4066Sahrens #include <sys/types.h> 26fa9e4066Sahrens #include <sys/param.h> 27fa9e4066Sahrens #include <sys/errno.h> 28fa9e4066Sahrens #include <sys/uio.h> 29fa9e4066Sahrens #include <sys/buf.h> 30fa9e4066Sahrens #include <sys/modctl.h> 31fa9e4066Sahrens #include <sys/open.h> 32fa9e4066Sahrens #include <sys/file.h> 33fa9e4066Sahrens #include <sys/kmem.h> 34fa9e4066Sahrens #include <sys/conf.h> 35fa9e4066Sahrens #include <sys/cmn_err.h> 36fa9e4066Sahrens #include <sys/stat.h> 37fa9e4066Sahrens #include <sys/zfs_ioctl.h> 384201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 39da6c28aaSamw #include <sys/zfs_znode.h> 40fa9e4066Sahrens #include <sys/zap.h> 41fa9e4066Sahrens #include <sys/spa.h> 42b1b8ab34Slling #include <sys/spa_impl.h> 43fa9e4066Sahrens #include <sys/vdev.h> 444201a95eSRic Aleshire #include <sys/priv_impl.h> 45fa9e4066Sahrens #include <sys/dmu.h> 46fa9e4066Sahrens #include <sys/dsl_dir.h> 47fa9e4066Sahrens #include <sys/dsl_dataset.h> 48fa9e4066Sahrens #include <sys/dsl_prop.h> 49ecd6cf80Smarks #include <sys/dsl_deleg.h> 50ecd6cf80Smarks #include <sys/dmu_objset.h> 51fa9e4066Sahrens #include <sys/ddi.h> 52fa9e4066Sahrens #include <sys/sunddi.h> 53fa9e4066Sahrens #include <sys/sunldi.h> 54fa9e4066Sahrens #include <sys/policy.h> 55fa9e4066Sahrens #include <sys/zone.h> 56fa9e4066Sahrens #include <sys/nvpair.h> 57fa9e4066Sahrens #include <sys/pathname.h> 58fa9e4066Sahrens #include <sys/mount.h> 59fa9e4066Sahrens #include <sys/sdt.h> 60fa9e4066Sahrens #include <sys/fs/zfs.h> 61fa9e4066Sahrens #include <sys/zfs_ctldir.h> 62da6c28aaSamw #include <sys/zfs_dir.h> 63c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 64a2eea2e1Sahrens #include <sys/zvol.h> 653f9d6ad7SLin Ling #include <sys/dsl_scan.h> 66ecd6cf80Smarks #include <sharefs/share.h> 67f18faf3fSek #include <sys/dmu_objset.h> 68fa9e4066Sahrens 69fa9e4066Sahrens #include "zfs_namecheck.h" 70e9dbad6fSeschrock #include "zfs_prop.h" 71ecd6cf80Smarks #include "zfs_deleg.h" 720a586ceaSMark Shellenbaum #include "zfs_comutil.h" 73fa9e4066Sahrens 74fa9e4066Sahrens extern struct modlfs zfs_modlfs; 75fa9e4066Sahrens 76fa9e4066Sahrens extern void zfs_init(void); 77fa9e4066Sahrens extern void zfs_fini(void); 78fa9e4066Sahrens 79fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 80fa9e4066Sahrens dev_info_t *zfs_dip; 81fa9e4066Sahrens 82fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *); 83ecd6cf80Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *); 84fa9e4066Sahrens 8554d692b7SGeorge Wilson typedef enum { 8654d692b7SGeorge Wilson NO_NAME, 8754d692b7SGeorge Wilson POOL_NAME, 8854d692b7SGeorge Wilson DATASET_NAME 8954d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 9054d692b7SGeorge Wilson 91*f9af39baSGeorge Wilson typedef enum { 92*f9af39baSGeorge Wilson POOL_CHECK_NONE = 1 << 0, 93*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED = 1 << 1, 94*f9af39baSGeorge Wilson POOL_CHECK_READONLY = 1 << 2 95*f9af39baSGeorge Wilson } zfs_ioc_poolcheck_t; 96*f9af39baSGeorge Wilson 97fa9e4066Sahrens typedef struct zfs_ioc_vec { 98fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 99fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 10054d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 101ecd6cf80Smarks boolean_t zvec_his_log; 102*f9af39baSGeorge Wilson zfs_ioc_poolcheck_t zvec_pool_check; 103fa9e4066Sahrens } zfs_ioc_vec_t; 104fa9e4066Sahrens 10514843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 10614843421SMatthew Ahrens static const char *userquota_perms[] = { 10714843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 10814843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 10914843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 11014843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 11114843421SMatthew Ahrens }; 11214843421SMatthew Ahrens 11314843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 11492241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 11592241e0bSTom Erickson cred_t *cr); 11692241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 11792241e0bSTom Erickson nvlist_t **errors); 1180a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 1190a48a24eStimh boolean_t *); 12092241e0bSTom Erickson int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **); 1210a48a24eStimh 122fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 123fa9e4066Sahrens void 124fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 125fa9e4066Sahrens { 126fa9e4066Sahrens const char *newfile; 1273f9d6ad7SLin Ling char buf[512]; 128fa9e4066Sahrens va_list adx; 129fa9e4066Sahrens 130fa9e4066Sahrens /* 131fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 132fa9e4066Sahrens */ 133fa9e4066Sahrens newfile = strrchr(file, '/'); 134fa9e4066Sahrens if (newfile != NULL) { 135fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 136fa9e4066Sahrens } else { 137fa9e4066Sahrens newfile = file; 138fa9e4066Sahrens } 139fa9e4066Sahrens 140fa9e4066Sahrens va_start(adx, fmt); 141fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 142fa9e4066Sahrens va_end(adx); 143fa9e4066Sahrens 144fa9e4066Sahrens /* 145fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 146fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 147fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 148fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 149fa9e4066Sahrens * arg0 = file name 150fa9e4066Sahrens * arg1 = function name 151fa9e4066Sahrens * arg2 = line number 152fa9e4066Sahrens * arg3 = message 153fa9e4066Sahrens */ 154fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 155fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 156fa9e4066Sahrens } 157fa9e4066Sahrens 158ecd6cf80Smarks static void 159228975ccSek history_str_free(char *buf) 160228975ccSek { 161228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 162228975ccSek } 163228975ccSek 164228975ccSek static char * 165228975ccSek history_str_get(zfs_cmd_t *zc) 166ecd6cf80Smarks { 16740feaa91Sahrens char *buf; 168ecd6cf80Smarks 169ecd6cf80Smarks if (zc->zc_history == NULL) 170228975ccSek return (NULL); 171e7437265Sahrens 172ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 173ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 174ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 175228975ccSek history_str_free(buf); 176228975ccSek return (NULL); 177ecd6cf80Smarks } 178ecd6cf80Smarks 179ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 180ecd6cf80Smarks 181228975ccSek return (buf); 182228975ccSek } 183ecd6cf80Smarks 18415e6edf1Sgw /* 18515e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 18615e6edf1Sgw */ 18715e6edf1Sgw static boolean_t 18815e6edf1Sgw zfs_is_bootfs(const char *name) 18915e6edf1Sgw { 190503ad85cSMatthew Ahrens objset_t *os; 19115e6edf1Sgw 192503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 193503ad85cSMatthew Ahrens boolean_t ret; 194b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 195503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 196503ad85cSMatthew Ahrens return (ret); 19715e6edf1Sgw } 198503ad85cSMatthew Ahrens return (B_FALSE); 19915e6edf1Sgw } 20015e6edf1Sgw 201c2a93d44Stimh /* 2020a48a24eStimh * zfs_earlier_version 203c2a93d44Stimh * 204c2a93d44Stimh * Return non-zero if the spa version is less than requested version. 205c2a93d44Stimh */ 206da6c28aaSamw static int 2070a48a24eStimh zfs_earlier_version(const char *name, int version) 208da6c28aaSamw { 209da6c28aaSamw spa_t *spa; 210da6c28aaSamw 211da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 212da6c28aaSamw if (spa_version(spa) < version) { 213da6c28aaSamw spa_close(spa, FTAG); 214da6c28aaSamw return (1); 215da6c28aaSamw } 216da6c28aaSamw spa_close(spa, FTAG); 217da6c28aaSamw } 218da6c28aaSamw return (0); 219da6c28aaSamw } 220da6c28aaSamw 2219e6eda55Smarks /* 222745cd3c5Smaybee * zpl_earlier_version 2239e6eda55Smarks * 224745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 2259e6eda55Smarks */ 226745cd3c5Smaybee static boolean_t 227745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 2289e6eda55Smarks { 2299e6eda55Smarks objset_t *os; 230745cd3c5Smaybee boolean_t rc = B_TRUE; 2319e6eda55Smarks 232503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 233745cd3c5Smaybee uint64_t zplversion; 2349e6eda55Smarks 235503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 236503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 237503ad85cSMatthew Ahrens return (B_TRUE); 238503ad85cSMatthew Ahrens } 239503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 240745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 241745cd3c5Smaybee rc = zplversion < version; 242503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2439e6eda55Smarks } 2449e6eda55Smarks return (rc); 2459e6eda55Smarks } 2469e6eda55Smarks 247228975ccSek static void 248228975ccSek zfs_log_history(zfs_cmd_t *zc) 249228975ccSek { 250228975ccSek spa_t *spa; 251228975ccSek char *buf; 252ecd6cf80Smarks 253228975ccSek if ((buf = history_str_get(zc)) == NULL) 254228975ccSek return; 255228975ccSek 256228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 257228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 258228975ccSek (void) spa_history_log(spa, buf, LOG_CMD_NORMAL); 259228975ccSek spa_close(spa, FTAG); 260228975ccSek } 261228975ccSek history_str_free(buf); 262ecd6cf80Smarks } 263ecd6cf80Smarks 264fa9e4066Sahrens /* 265fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 266fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 267fa9e4066Sahrens */ 268fa9e4066Sahrens /* ARGSUSED */ 269fa9e4066Sahrens static int 270ecd6cf80Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr) 271fa9e4066Sahrens { 272fa9e4066Sahrens return (0); 273fa9e4066Sahrens } 274fa9e4066Sahrens 275fa9e4066Sahrens /* 276fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 277fa9e4066Sahrens * no privileges, but must be visible in the local zone. 278fa9e4066Sahrens */ 279fa9e4066Sahrens /* ARGSUSED */ 280fa9e4066Sahrens static int 281ecd6cf80Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr) 282fa9e4066Sahrens { 283fa9e4066Sahrens if (INGLOBALZONE(curproc) || 284ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 285fa9e4066Sahrens return (0); 286fa9e4066Sahrens 287fa9e4066Sahrens return (ENOENT); 288fa9e4066Sahrens } 289fa9e4066Sahrens 290fa9e4066Sahrens static int 291a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) 292fa9e4066Sahrens { 293fa9e4066Sahrens int writable = 1; 294fa9e4066Sahrens 295fa9e4066Sahrens /* 296fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 297fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 298fa9e4066Sahrens */ 299fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 300fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 301fa9e4066Sahrens return (ENOENT); 302fa9e4066Sahrens 303fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 304fa9e4066Sahrens /* 305fa9e4066Sahrens * If the fs is zoned, only root can access it from the 306fa9e4066Sahrens * global zone. 307fa9e4066Sahrens */ 308fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 309fa9e4066Sahrens return (EPERM); 310fa9e4066Sahrens } else { 311fa9e4066Sahrens /* 312fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 313fa9e4066Sahrens */ 314fa9e4066Sahrens if (!zoned) 315fa9e4066Sahrens return (EPERM); 316fa9e4066Sahrens 317fa9e4066Sahrens /* must be writable by this zone */ 318fa9e4066Sahrens if (!writable) 319fa9e4066Sahrens return (EPERM); 320fa9e4066Sahrens } 321fa9e4066Sahrens return (0); 322fa9e4066Sahrens } 323fa9e4066Sahrens 324a7f53a56SChris Kirby static int 325a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr) 326a7f53a56SChris Kirby { 327a7f53a56SChris Kirby uint64_t zoned; 328a7f53a56SChris Kirby 329a7f53a56SChris Kirby if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 330a7f53a56SChris Kirby return (ENOENT); 331a7f53a56SChris Kirby 332a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 333a7f53a56SChris Kirby } 334a7f53a56SChris Kirby 335a7f53a56SChris Kirby static int 336a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) 337a7f53a56SChris Kirby { 338a7f53a56SChris Kirby uint64_t zoned; 339a7f53a56SChris Kirby 340a7f53a56SChris Kirby rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER); 341a7f53a56SChris Kirby if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL)) { 342a7f53a56SChris Kirby rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); 343a7f53a56SChris Kirby return (ENOENT); 344a7f53a56SChris Kirby } 345a7f53a56SChris Kirby rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); 346a7f53a56SChris Kirby 347a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 348a7f53a56SChris Kirby } 349a7f53a56SChris Kirby 350fa9e4066Sahrens int 351ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 352fa9e4066Sahrens { 353fa9e4066Sahrens int error; 354fa9e4066Sahrens 355ecd6cf80Smarks error = zfs_dozonecheck(name, cr); 356ecd6cf80Smarks if (error == 0) { 357ecd6cf80Smarks error = secpolicy_zfs(cr); 358db870a07Sahrens if (error) 359ecd6cf80Smarks error = dsl_deleg_access(name, perm, cr); 360ecd6cf80Smarks } 361ecd6cf80Smarks return (error); 362ecd6cf80Smarks } 363ecd6cf80Smarks 364a7f53a56SChris Kirby int 365a7f53a56SChris Kirby zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, 366a7f53a56SChris Kirby const char *perm, cred_t *cr) 367a7f53a56SChris Kirby { 368a7f53a56SChris Kirby int error; 369a7f53a56SChris Kirby 370a7f53a56SChris Kirby error = zfs_dozonecheck_ds(name, ds, cr); 371a7f53a56SChris Kirby if (error == 0) { 372a7f53a56SChris Kirby error = secpolicy_zfs(cr); 373a7f53a56SChris Kirby if (error) 374a7f53a56SChris Kirby error = dsl_deleg_access_impl(ds, perm, cr); 375a7f53a56SChris Kirby } 376a7f53a56SChris Kirby return (error); 377a7f53a56SChris Kirby } 378a7f53a56SChris Kirby 3794201a95eSRic Aleshire /* 3804201a95eSRic Aleshire * Policy for setting the security label property. 3814201a95eSRic Aleshire * 3824201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 3834201a95eSRic Aleshire */ 3844201a95eSRic Aleshire static int 38592241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 3864201a95eSRic Aleshire { 3874201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 3884201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 3894201a95eSRic Aleshire boolean_t new_default = FALSE; 3904201a95eSRic Aleshire uint64_t zoned; 3914201a95eSRic Aleshire int needed_priv = -1; 3924201a95eSRic Aleshire int error; 3934201a95eSRic Aleshire 3944201a95eSRic Aleshire /* First get the existing dataset label. */ 3954201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 3964201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 3974201a95eSRic Aleshire if (error) 3984201a95eSRic Aleshire return (EPERM); 3994201a95eSRic Aleshire 4004201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 4014201a95eSRic Aleshire new_default = TRUE; 4024201a95eSRic Aleshire 4034201a95eSRic Aleshire /* The label must be translatable */ 4044201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 4054201a95eSRic Aleshire return (EINVAL); 4064201a95eSRic Aleshire 4074201a95eSRic Aleshire /* 4084201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 4094201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 4104201a95eSRic Aleshire * are needed. 4114201a95eSRic Aleshire */ 4124201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 4134201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 4144201a95eSRic Aleshire return (EPERM); 4154201a95eSRic Aleshire return (0); 4164201a95eSRic Aleshire } 4174201a95eSRic Aleshire 4184201a95eSRic Aleshire /* 4194201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 4204201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 4214201a95eSRic Aleshire * global zone. 4224201a95eSRic Aleshire */ 4234201a95eSRic Aleshire if (dsl_prop_get_integer(name, 4244201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 4254201a95eSRic Aleshire return (EPERM); 4264201a95eSRic Aleshire if (!zoned) { 4274201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 4284201a95eSRic Aleshire return (EPERM); 4294201a95eSRic Aleshire } 4304201a95eSRic Aleshire 4314201a95eSRic Aleshire /* 4324201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 4334201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 4344201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 4354201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 4364201a95eSRic Aleshire */ 4374201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 43892241e0bSTom Erickson objset_t *os; 43992241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 44092241e0bSTom Erickson 4414201a95eSRic Aleshire /* 4424201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 4434201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 4444201a95eSRic Aleshire */ 4454201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 44692241e0bSTom Erickson setsl_tag, &os); 4474201a95eSRic Aleshire if (error) 4484201a95eSRic Aleshire return (EPERM); 4494201a95eSRic Aleshire 45092241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 45192241e0bSTom Erickson 4524201a95eSRic Aleshire if (new_default) { 4534201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 4544201a95eSRic Aleshire goto out_check; 4554201a95eSRic Aleshire } 4564201a95eSRic Aleshire 4574201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 4584201a95eSRic Aleshire return (EPERM); 4594201a95eSRic Aleshire 4604201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 4614201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 4624201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 4634201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 4644201a95eSRic Aleshire } else { 4654201a95eSRic Aleshire /* dataset currently has a default label */ 4664201a95eSRic Aleshire if (!new_default) 4674201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 4684201a95eSRic Aleshire } 4694201a95eSRic Aleshire 4704201a95eSRic Aleshire out_check: 4714201a95eSRic Aleshire if (needed_priv != -1) 4724201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 4734201a95eSRic Aleshire return (0); 4744201a95eSRic Aleshire } 4754201a95eSRic Aleshire 476ecd6cf80Smarks static int 47792241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 47892241e0bSTom Erickson cred_t *cr) 479ecd6cf80Smarks { 48092241e0bSTom Erickson char *strval; 48192241e0bSTom Erickson 482ecd6cf80Smarks /* 483ecd6cf80Smarks * Check permissions for special properties. 484ecd6cf80Smarks */ 485ecd6cf80Smarks switch (prop) { 486ecd6cf80Smarks case ZFS_PROP_ZONED: 487ecd6cf80Smarks /* 488ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 489ecd6cf80Smarks */ 490ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 491ecd6cf80Smarks return (EPERM); 492ecd6cf80Smarks break; 493ecd6cf80Smarks 494ecd6cf80Smarks case ZFS_PROP_QUOTA: 495ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 496ecd6cf80Smarks uint64_t zoned; 497ecd6cf80Smarks char setpoint[MAXNAMELEN]; 498ecd6cf80Smarks /* 499ecd6cf80Smarks * Unprivileged users are allowed to modify the 500ecd6cf80Smarks * quota on things *under* (ie. contained by) 501ecd6cf80Smarks * the thing they own. 502ecd6cf80Smarks */ 50392241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 504ecd6cf80Smarks setpoint)) 505ecd6cf80Smarks return (EPERM); 50692241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 507ecd6cf80Smarks return (EPERM); 508ecd6cf80Smarks } 509db870a07Sahrens break; 5104201a95eSRic Aleshire 5114201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 5124201a95eSRic Aleshire if (!is_system_labeled()) 5134201a95eSRic Aleshire return (EPERM); 51492241e0bSTom Erickson 51592241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 51692241e0bSTom Erickson int err; 51792241e0bSTom Erickson 51892241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 51992241e0bSTom Erickson if (err != 0) 52092241e0bSTom Erickson return (err); 52192241e0bSTom Erickson } 5224201a95eSRic Aleshire break; 523ecd6cf80Smarks } 524ecd6cf80Smarks 52592241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 526ecd6cf80Smarks } 527ecd6cf80Smarks 528ecd6cf80Smarks int 529ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr) 530ecd6cf80Smarks { 531ecd6cf80Smarks int error; 532ecd6cf80Smarks 533ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 534ecd6cf80Smarks if (error) 535fa9e4066Sahrens return (error); 536fa9e4066Sahrens 537ecd6cf80Smarks /* 538ecd6cf80Smarks * permission to set permissions will be evaluated later in 539ecd6cf80Smarks * dsl_deleg_can_allow() 540ecd6cf80Smarks */ 541ecd6cf80Smarks return (0); 542ecd6cf80Smarks } 543ecd6cf80Smarks 544ecd6cf80Smarks int 545ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr) 546ecd6cf80Smarks { 547681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 548681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 549ecd6cf80Smarks } 550ecd6cf80Smarks 551ecd6cf80Smarks int 552ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr) 553ecd6cf80Smarks { 554a7f53a56SChris Kirby spa_t *spa; 555a7f53a56SChris Kirby dsl_pool_t *dp; 556a7f53a56SChris Kirby dsl_dataset_t *ds; 557a7f53a56SChris Kirby char *cp; 558a7f53a56SChris Kirby int error; 559a7f53a56SChris Kirby 560a7f53a56SChris Kirby /* 561a7f53a56SChris Kirby * Generate the current snapshot name from the given objsetid, then 562a7f53a56SChris Kirby * use that name for the secpolicy/zone checks. 563a7f53a56SChris Kirby */ 564a7f53a56SChris Kirby cp = strchr(zc->zc_name, '@'); 565a7f53a56SChris Kirby if (cp == NULL) 566a7f53a56SChris Kirby return (EINVAL); 567a7f53a56SChris Kirby error = spa_open(zc->zc_name, &spa, FTAG); 568a7f53a56SChris Kirby if (error) 569a7f53a56SChris Kirby return (error); 570a7f53a56SChris Kirby 571a7f53a56SChris Kirby dp = spa_get_dsl(spa); 572a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 573a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 574a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 575a7f53a56SChris Kirby spa_close(spa, FTAG); 576a7f53a56SChris Kirby if (error) 577a7f53a56SChris Kirby return (error); 578a7f53a56SChris Kirby 579a7f53a56SChris Kirby dsl_dataset_name(ds, zc->zc_name); 580a7f53a56SChris Kirby 581a7f53a56SChris Kirby error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, 582a7f53a56SChris Kirby ZFS_DELEG_PERM_SEND, cr); 583a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 584a7f53a56SChris Kirby 585a7f53a56SChris Kirby return (error); 586ecd6cf80Smarks } 587ecd6cf80Smarks 588743a77edSAlan Wright static int 589743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr) 590743a77edSAlan Wright { 591743a77edSAlan Wright vnode_t *vp; 592743a77edSAlan Wright int error; 593743a77edSAlan Wright 594743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 595743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 596743a77edSAlan Wright return (error); 597743a77edSAlan Wright 598743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 599743a77edSAlan Wright 600743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 601743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 602743a77edSAlan Wright zc->zc_name) != 0)) { 603743a77edSAlan Wright VN_RELE(vp); 604743a77edSAlan Wright return (EPERM); 605743a77edSAlan Wright } 606743a77edSAlan Wright 607743a77edSAlan Wright VN_RELE(vp); 608743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 609743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 610743a77edSAlan Wright } 611743a77edSAlan Wright 612ecd6cf80Smarks int 613ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr) 614ecd6cf80Smarks { 615ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 616ecd6cf80Smarks return (EPERM); 617ecd6cf80Smarks 6183cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 619ecd6cf80Smarks return (0); 620ecd6cf80Smarks } else { 621743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 622743a77edSAlan Wright } 623743a77edSAlan Wright } 624ecd6cf80Smarks 625743a77edSAlan Wright int 626743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr) 627743a77edSAlan Wright { 628743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 629743a77edSAlan Wright return (EPERM); 630ecd6cf80Smarks 631743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 632743a77edSAlan Wright return (0); 633743a77edSAlan Wright } else { 634743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 635ecd6cf80Smarks } 636fa9e4066Sahrens } 637fa9e4066Sahrens 638fa9e4066Sahrens static int 639ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 640fa9e4066Sahrens { 641fa9e4066Sahrens char *cp; 642fa9e4066Sahrens 643fa9e4066Sahrens /* 644fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 645fa9e4066Sahrens */ 646ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 647ecd6cf80Smarks cp = strrchr(parent, '@'); 648fa9e4066Sahrens if (cp != NULL) { 649fa9e4066Sahrens cp[0] = '\0'; 650fa9e4066Sahrens } else { 651ecd6cf80Smarks cp = strrchr(parent, '/'); 652fa9e4066Sahrens if (cp == NULL) 653fa9e4066Sahrens return (ENOENT); 654fa9e4066Sahrens cp[0] = '\0'; 655ecd6cf80Smarks } 656ecd6cf80Smarks 657ecd6cf80Smarks return (0); 658ecd6cf80Smarks } 659ecd6cf80Smarks 660ecd6cf80Smarks int 661ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 662ecd6cf80Smarks { 663ecd6cf80Smarks int error; 664ecd6cf80Smarks 665ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 666ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 667ecd6cf80Smarks return (error); 668ecd6cf80Smarks 669ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 670ecd6cf80Smarks } 671ecd6cf80Smarks 672ecd6cf80Smarks static int 673ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr) 674ecd6cf80Smarks { 675ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 676ecd6cf80Smarks } 677ecd6cf80Smarks 678cbf6f6aaSWilliam Gorrell /* 679cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 680cbf6f6aaSWilliam Gorrell * descendent mount and destroy permissions. 681cbf6f6aaSWilliam Gorrell * Reassemble the full filesystem@snap name so dsl_deleg_access() 682cbf6f6aaSWilliam Gorrell * can do the correct permission check. 683cbf6f6aaSWilliam Gorrell * 684cbf6f6aaSWilliam Gorrell * Since this routine is used when doing a recursive destroy of snapshots 685cbf6f6aaSWilliam Gorrell * and destroying snapshots requires descendent permissions, a successfull 686cbf6f6aaSWilliam Gorrell * check of the top level snapshot applies to snapshots of all descendent 687cbf6f6aaSWilliam Gorrell * datasets as well. 688cbf6f6aaSWilliam Gorrell */ 689cbf6f6aaSWilliam Gorrell static int 690cbf6f6aaSWilliam Gorrell zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr) 691cbf6f6aaSWilliam Gorrell { 692cbf6f6aaSWilliam Gorrell int error; 693cbf6f6aaSWilliam Gorrell char *dsname; 694cbf6f6aaSWilliam Gorrell 695cbf6f6aaSWilliam Gorrell dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value); 696cbf6f6aaSWilliam Gorrell 697cbf6f6aaSWilliam Gorrell error = zfs_secpolicy_destroy_perms(dsname, cr); 698cbf6f6aaSWilliam Gorrell 699cbf6f6aaSWilliam Gorrell strfree(dsname); 700cbf6f6aaSWilliam Gorrell return (error); 701cbf6f6aaSWilliam Gorrell } 702cbf6f6aaSWilliam Gorrell 703ecd6cf80Smarks int 704ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 705ecd6cf80Smarks { 70692241e0bSTom Erickson char parentname[MAXNAMELEN]; 707ecd6cf80Smarks int error; 708ecd6cf80Smarks 709ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 710ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 711ecd6cf80Smarks return (error); 712ecd6cf80Smarks 713ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 714ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 715ecd6cf80Smarks return (error); 716ecd6cf80Smarks 717ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 718ecd6cf80Smarks sizeof (parentname))) != 0) 719ecd6cf80Smarks return (error); 720ecd6cf80Smarks 721ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 722ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 723ecd6cf80Smarks return (error); 724ecd6cf80Smarks 725ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 726ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 727ecd6cf80Smarks return (error); 728ecd6cf80Smarks 729ecd6cf80Smarks return (error); 730ecd6cf80Smarks } 731ecd6cf80Smarks 732ecd6cf80Smarks static int 733ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr) 734ecd6cf80Smarks { 735ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 736ecd6cf80Smarks } 737ecd6cf80Smarks 738ecd6cf80Smarks static int 739ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr) 740ecd6cf80Smarks { 74192241e0bSTom Erickson char parentname[MAXNAMELEN]; 742ecd6cf80Smarks objset_t *clone; 743ecd6cf80Smarks int error; 744ecd6cf80Smarks 745ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 746ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 747ecd6cf80Smarks if (error) 748ecd6cf80Smarks return (error); 749ecd6cf80Smarks 750503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &clone); 751ecd6cf80Smarks 752ecd6cf80Smarks if (error == 0) { 753ecd6cf80Smarks dsl_dataset_t *pclone = NULL; 754ecd6cf80Smarks dsl_dir_t *dd; 755503ad85cSMatthew Ahrens dd = clone->os_dsl_dataset->ds_dir; 756ecd6cf80Smarks 757ecd6cf80Smarks rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 758745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 759745cd3c5Smaybee dd->dd_phys->dd_origin_obj, FTAG, &pclone); 760ecd6cf80Smarks rw_exit(&dd->dd_pool->dp_config_rwlock); 761ecd6cf80Smarks if (error) { 762503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 763ecd6cf80Smarks return (error); 764ecd6cf80Smarks } 765ecd6cf80Smarks 766ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 767ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 768ecd6cf80Smarks 769ecd6cf80Smarks dsl_dataset_name(pclone, parentname); 770503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 771745cd3c5Smaybee dsl_dataset_rele(pclone, FTAG); 772ecd6cf80Smarks if (error == 0) 773ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 774ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 775ecd6cf80Smarks } 776ecd6cf80Smarks return (error); 777ecd6cf80Smarks } 778ecd6cf80Smarks 779ecd6cf80Smarks static int 780ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr) 781ecd6cf80Smarks { 782ecd6cf80Smarks int error; 783ecd6cf80Smarks 784ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 785ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 786ecd6cf80Smarks return (error); 787ecd6cf80Smarks 788ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 789ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 790ecd6cf80Smarks return (error); 791ecd6cf80Smarks 792ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 793ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 794ecd6cf80Smarks } 795ecd6cf80Smarks 796ecd6cf80Smarks int 797ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 798ecd6cf80Smarks { 799681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 800681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 801ecd6cf80Smarks } 802ecd6cf80Smarks 803ecd6cf80Smarks static int 804ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr) 805ecd6cf80Smarks { 806ecd6cf80Smarks 807ecd6cf80Smarks return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr)); 808ecd6cf80Smarks } 809ecd6cf80Smarks 810ecd6cf80Smarks static int 811ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr) 812ecd6cf80Smarks { 81392241e0bSTom Erickson char parentname[MAXNAMELEN]; 81492241e0bSTom Erickson int error; 815ecd6cf80Smarks 816ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 817ecd6cf80Smarks sizeof (parentname))) != 0) 818ecd6cf80Smarks return (error); 819fa9e4066Sahrens 820ecd6cf80Smarks if (zc->zc_value[0] != '\0') { 821ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_value, 822ecd6cf80Smarks ZFS_DELEG_PERM_CLONE, cr)) != 0) 823ecd6cf80Smarks return (error); 824fa9e4066Sahrens } 825fa9e4066Sahrens 826ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 827ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 828ecd6cf80Smarks return (error); 829ecd6cf80Smarks 830ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 831ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 832ecd6cf80Smarks 833ecd6cf80Smarks return (error); 834ecd6cf80Smarks } 835ecd6cf80Smarks 836ecd6cf80Smarks static int 837ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr) 838ecd6cf80Smarks { 839ecd6cf80Smarks int error; 840ecd6cf80Smarks 841ecd6cf80Smarks error = secpolicy_fs_unmount(cr, NULL); 842ecd6cf80Smarks if (error) { 843ecd6cf80Smarks error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr); 844ecd6cf80Smarks } 845ecd6cf80Smarks return (error); 846fa9e4066Sahrens } 847fa9e4066Sahrens 848fa9e4066Sahrens /* 849fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 850fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 851fa9e4066Sahrens */ 852fa9e4066Sahrens /* ARGSUSED */ 853fa9e4066Sahrens static int 854ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr) 855fa9e4066Sahrens { 856fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 857fa9e4066Sahrens return (EPERM); 858fa9e4066Sahrens 859fa9e4066Sahrens return (0); 860fa9e4066Sahrens } 861fa9e4066Sahrens 86299d5e173STim Haley /* 86399d5e173STim Haley * Policy for object to name lookups. 86499d5e173STim Haley */ 86599d5e173STim Haley /* ARGSUSED */ 86699d5e173STim Haley static int 86799d5e173STim Haley zfs_secpolicy_diff(zfs_cmd_t *zc, cred_t *cr) 86899d5e173STim Haley { 86999d5e173STim Haley int error; 87099d5e173STim Haley 87199d5e173STim Haley if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0) 87299d5e173STim Haley return (0); 87399d5e173STim Haley 87499d5e173STim Haley error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr); 87599d5e173STim Haley return (error); 87699d5e173STim Haley } 87799d5e173STim Haley 878ea8dc4b6Seschrock /* 879ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 880ea8dc4b6Seschrock */ 881ea8dc4b6Seschrock /* ARGSUSED */ 882ea8dc4b6Seschrock static int 883ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr) 884ea8dc4b6Seschrock { 885ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 886ea8dc4b6Seschrock } 887ea8dc4b6Seschrock 888e45ce728Sahrens static int 889e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr) 890e45ce728Sahrens { 891e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 892e45ce728Sahrens 893990b4856Slling if (prop == ZPROP_INVAL) { 894e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 895e45ce728Sahrens return (EINVAL); 896e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 897e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 898e45ce728Sahrens } else { 89992241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 90092241e0bSTom Erickson NULL, cr)); 901e45ce728Sahrens } 902e45ce728Sahrens } 903e45ce728Sahrens 90414843421SMatthew Ahrens static int 90514843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr) 90614843421SMatthew Ahrens { 90714843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 90814843421SMatthew Ahrens if (err) 90914843421SMatthew Ahrens return (err); 91014843421SMatthew Ahrens 91114843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 91214843421SMatthew Ahrens return (EINVAL); 91314843421SMatthew Ahrens 91414843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 91514843421SMatthew Ahrens /* 91614843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 91714843421SMatthew Ahrens * themself, allow it. 91814843421SMatthew Ahrens */ 91914843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 92014843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 92114843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 92214843421SMatthew Ahrens return (0); 92314843421SMatthew Ahrens } else { 92414843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 92514843421SMatthew Ahrens return (0); 92614843421SMatthew Ahrens } 92714843421SMatthew Ahrens } 92814843421SMatthew Ahrens 92914843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 93014843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 93114843421SMatthew Ahrens } 93214843421SMatthew Ahrens 93314843421SMatthew Ahrens static int 93414843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr) 93514843421SMatthew Ahrens { 93614843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 93714843421SMatthew Ahrens if (err) 93814843421SMatthew Ahrens return (err); 93914843421SMatthew Ahrens 94014843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 94114843421SMatthew Ahrens return (EINVAL); 94214843421SMatthew Ahrens 94314843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 94414843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 94514843421SMatthew Ahrens } 94614843421SMatthew Ahrens 94714843421SMatthew Ahrens static int 94814843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr) 94914843421SMatthew Ahrens { 95092241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 95192241e0bSTom Erickson NULL, cr)); 95214843421SMatthew Ahrens } 95314843421SMatthew Ahrens 954842727c2SChris Kirby static int 955842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr) 956842727c2SChris Kirby { 957842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 958842727c2SChris Kirby ZFS_DELEG_PERM_HOLD, cr)); 959842727c2SChris Kirby } 960842727c2SChris Kirby 961842727c2SChris Kirby static int 962842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr) 963842727c2SChris Kirby { 964842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 965842727c2SChris Kirby ZFS_DELEG_PERM_RELEASE, cr)); 966842727c2SChris Kirby } 967842727c2SChris Kirby 96899d5e173STim Haley /* 96999d5e173STim Haley * Policy for allowing temporary snapshots to be taken or released 97099d5e173STim Haley */ 97199d5e173STim Haley static int 97299d5e173STim Haley zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, cred_t *cr) 97399d5e173STim Haley { 97499d5e173STim Haley /* 97599d5e173STim Haley * A temporary snapshot is the same as a snapshot, 97699d5e173STim Haley * hold, destroy and release all rolled into one. 97799d5e173STim Haley * Delegated diff alone is sufficient that we allow this. 97899d5e173STim Haley */ 97999d5e173STim Haley int error; 98099d5e173STim Haley 98199d5e173STim Haley if ((error = zfs_secpolicy_write_perms(zc->zc_name, 98299d5e173STim Haley ZFS_DELEG_PERM_DIFF, cr)) == 0) 98399d5e173STim Haley return (0); 98499d5e173STim Haley 98599d5e173STim Haley error = zfs_secpolicy_snapshot(zc, cr); 98699d5e173STim Haley if (!error) 98799d5e173STim Haley error = zfs_secpolicy_hold(zc, cr); 98899d5e173STim Haley if (!error) 98999d5e173STim Haley error = zfs_secpolicy_release(zc, cr); 99099d5e173STim Haley if (!error) 99199d5e173STim Haley error = zfs_secpolicy_destroy(zc, cr); 99299d5e173STim Haley return (error); 99399d5e173STim Haley } 99499d5e173STim Haley 995fa9e4066Sahrens /* 996fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 997fa9e4066Sahrens */ 998fa9e4066Sahrens static int 999478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 1000fa9e4066Sahrens { 1001fa9e4066Sahrens char *packed; 1002fa9e4066Sahrens int error; 1003990b4856Slling nvlist_t *list = NULL; 1004fa9e4066Sahrens 1005fa9e4066Sahrens /* 1006e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 1007fa9e4066Sahrens */ 1008990b4856Slling if (size == 0) 1009fa9e4066Sahrens return (EINVAL); 1010fa9e4066Sahrens 1011fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 1012fa9e4066Sahrens 1013478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 1014478ed9adSEric Taylor iflag)) != 0) { 1015fa9e4066Sahrens kmem_free(packed, size); 1016fa9e4066Sahrens return (error); 1017fa9e4066Sahrens } 1018fa9e4066Sahrens 1019990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 1020fa9e4066Sahrens kmem_free(packed, size); 1021fa9e4066Sahrens return (error); 1022fa9e4066Sahrens } 1023fa9e4066Sahrens 1024fa9e4066Sahrens kmem_free(packed, size); 1025fa9e4066Sahrens 1026990b4856Slling *nvp = list; 1027fa9e4066Sahrens return (0); 1028fa9e4066Sahrens } 1029fa9e4066Sahrens 103092241e0bSTom Erickson static int 103192241e0bSTom Erickson fit_error_list(zfs_cmd_t *zc, nvlist_t **errors) 103292241e0bSTom Erickson { 103392241e0bSTom Erickson size_t size; 103492241e0bSTom Erickson 103592241e0bSTom Erickson VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0); 103692241e0bSTom Erickson 103792241e0bSTom Erickson if (size > zc->zc_nvlist_dst_size) { 103892241e0bSTom Erickson nvpair_t *more_errors; 103992241e0bSTom Erickson int n = 0; 104092241e0bSTom Erickson 104192241e0bSTom Erickson if (zc->zc_nvlist_dst_size < 1024) 104292241e0bSTom Erickson return (ENOMEM); 104392241e0bSTom Erickson 104492241e0bSTom Erickson VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0); 104592241e0bSTom Erickson more_errors = nvlist_prev_nvpair(*errors, NULL); 104692241e0bSTom Erickson 104792241e0bSTom Erickson do { 104892241e0bSTom Erickson nvpair_t *pair = nvlist_prev_nvpair(*errors, 104992241e0bSTom Erickson more_errors); 105092241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(*errors, pair) == 0); 105192241e0bSTom Erickson n++; 105292241e0bSTom Erickson VERIFY(nvlist_size(*errors, &size, 105392241e0bSTom Erickson NV_ENCODE_NATIVE) == 0); 105492241e0bSTom Erickson } while (size > zc->zc_nvlist_dst_size); 105592241e0bSTom Erickson 105692241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0); 105792241e0bSTom Erickson VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0); 105892241e0bSTom Erickson ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0); 105992241e0bSTom Erickson ASSERT(size <= zc->zc_nvlist_dst_size); 106092241e0bSTom Erickson } 106192241e0bSTom Erickson 106292241e0bSTom Erickson return (0); 106392241e0bSTom Erickson } 106492241e0bSTom Erickson 1065e9dbad6fSeschrock static int 1066e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 1067e9dbad6fSeschrock { 1068e9dbad6fSeschrock char *packed = NULL; 10696e27f868SSam Falkner int error = 0; 1070e9dbad6fSeschrock size_t size; 1071e9dbad6fSeschrock 1072e9dbad6fSeschrock VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); 1073e9dbad6fSeschrock 1074e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 1075e9dbad6fSeschrock error = ENOMEM; 1076e9dbad6fSeschrock } else { 1077da165920Smarks packed = kmem_alloc(size, KM_SLEEP); 1078e9dbad6fSeschrock VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, 1079e9dbad6fSeschrock KM_SLEEP) == 0); 10806e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 10816e27f868SSam Falkner size, zc->zc_iflags) != 0) 10826e27f868SSam Falkner error = EFAULT; 1083e9dbad6fSeschrock kmem_free(packed, size); 1084e9dbad6fSeschrock } 1085e9dbad6fSeschrock 1086e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 1087e9dbad6fSeschrock return (error); 1088e9dbad6fSeschrock } 1089e9dbad6fSeschrock 109014843421SMatthew Ahrens static int 1091af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 109214843421SMatthew Ahrens { 109314843421SMatthew Ahrens objset_t *os; 109414843421SMatthew Ahrens int error; 109514843421SMatthew Ahrens 1096503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 109714843421SMatthew Ahrens if (error) 109814843421SMatthew Ahrens return (error); 1099503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1100503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1101503ad85cSMatthew Ahrens return (EINVAL); 1102503ad85cSMatthew Ahrens } 110314843421SMatthew Ahrens 1104503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 1105af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 1106af4c679fSSean McEnroe if (*zfvp) { 1107af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 110814843421SMatthew Ahrens } else { 110914843421SMatthew Ahrens error = ESRCH; 111014843421SMatthew Ahrens } 1111503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 1112503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 111314843421SMatthew Ahrens return (error); 111414843421SMatthew Ahrens } 111514843421SMatthew Ahrens 111614843421SMatthew Ahrens /* 111714843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 111814843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 111914843421SMatthew Ahrens */ 112014843421SMatthew Ahrens static int 11211412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) 112214843421SMatthew Ahrens { 112314843421SMatthew Ahrens int error = 0; 112414843421SMatthew Ahrens 1125af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1126af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 112714843421SMatthew Ahrens if (error == 0) { 11281412a1a2SMark Shellenbaum rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : 11291412a1a2SMark Shellenbaum RW_READER, tag); 1130af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 113114843421SMatthew Ahrens /* 113214843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 113314843421SMatthew Ahrens * thread should be just about to disassociate the 113414843421SMatthew Ahrens * objset from the zfsvfs. 113514843421SMatthew Ahrens */ 1136af4c679fSSean McEnroe rrw_exit(&(*zfvp)->z_teardown_lock, tag); 113714843421SMatthew Ahrens return (EBUSY); 113814843421SMatthew Ahrens } 113914843421SMatthew Ahrens } 114014843421SMatthew Ahrens return (error); 114114843421SMatthew Ahrens } 114214843421SMatthew Ahrens 114314843421SMatthew Ahrens static void 114414843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 114514843421SMatthew Ahrens { 114614843421SMatthew Ahrens rrw_exit(&zfsvfs->z_teardown_lock, tag); 114714843421SMatthew Ahrens 114814843421SMatthew Ahrens if (zfsvfs->z_vfs) { 114914843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 115014843421SMatthew Ahrens } else { 1151503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 115214843421SMatthew Ahrens zfsvfs_free(zfsvfs); 115314843421SMatthew Ahrens } 115414843421SMatthew Ahrens } 115514843421SMatthew Ahrens 1156fa9e4066Sahrens static int 1157fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1158fa9e4066Sahrens { 1159fa9e4066Sahrens int error; 1160990b4856Slling nvlist_t *config, *props = NULL; 11610a48a24eStimh nvlist_t *rootprops = NULL; 11620a48a24eStimh nvlist_t *zplprops = NULL; 1163228975ccSek char *buf; 1164fa9e4066Sahrens 1165990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1166478ed9adSEric Taylor zc->zc_iflags, &config)) 1167fa9e4066Sahrens return (error); 11682a6b87f0Sek 1169990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1170478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1171478ed9adSEric Taylor zc->zc_iflags, &props))) { 1172990b4856Slling nvlist_free(config); 1173990b4856Slling return (error); 1174990b4856Slling } 1175990b4856Slling 11760a48a24eStimh if (props) { 11770a48a24eStimh nvlist_t *nvl = NULL; 11780a48a24eStimh uint64_t version = SPA_VERSION; 11790a48a24eStimh 11800a48a24eStimh (void) nvlist_lookup_uint64(props, 11810a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 11820a48a24eStimh if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) { 11830a48a24eStimh error = EINVAL; 11840a48a24eStimh goto pool_props_bad; 11850a48a24eStimh } 11860a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 11870a48a24eStimh if (nvl) { 11880a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 11890a48a24eStimh if (error != 0) { 11900a48a24eStimh nvlist_free(config); 11910a48a24eStimh nvlist_free(props); 11920a48a24eStimh return (error); 11930a48a24eStimh } 11940a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 11950a48a24eStimh } 11960a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 11970a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 11980a48a24eStimh zplprops, NULL); 11990a48a24eStimh if (error) 12000a48a24eStimh goto pool_props_bad; 12010a48a24eStimh } 12020a48a24eStimh 12032a6b87f0Sek buf = history_str_get(zc); 1204fa9e4066Sahrens 12050a48a24eStimh error = spa_create(zc->zc_name, config, props, buf, zplprops); 12060a48a24eStimh 12070a48a24eStimh /* 12080a48a24eStimh * Set the remaining root properties 12090a48a24eStimh */ 121092241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 121192241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 12120a48a24eStimh (void) spa_destroy(zc->zc_name); 1213fa9e4066Sahrens 12142a6b87f0Sek if (buf != NULL) 12152a6b87f0Sek history_str_free(buf); 1216990b4856Slling 12170a48a24eStimh pool_props_bad: 12180a48a24eStimh nvlist_free(rootprops); 12190a48a24eStimh nvlist_free(zplprops); 1220fa9e4066Sahrens nvlist_free(config); 12210a48a24eStimh nvlist_free(props); 1222990b4856Slling 1223fa9e4066Sahrens return (error); 1224fa9e4066Sahrens } 1225fa9e4066Sahrens 1226fa9e4066Sahrens static int 1227fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1228fa9e4066Sahrens { 1229ecd6cf80Smarks int error; 1230ecd6cf80Smarks zfs_log_history(zc); 1231ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1232681d9761SEric Taylor if (error == 0) 1233681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1234ecd6cf80Smarks return (error); 1235fa9e4066Sahrens } 1236fa9e4066Sahrens 1237fa9e4066Sahrens static int 1238fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1239fa9e4066Sahrens { 1240990b4856Slling nvlist_t *config, *props = NULL; 1241fa9e4066Sahrens uint64_t guid; 1242468c413aSTim Haley int error; 1243fa9e4066Sahrens 1244990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1245478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1246990b4856Slling return (error); 1247990b4856Slling 1248990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1249478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1250478ed9adSEric Taylor zc->zc_iflags, &props))) { 1251990b4856Slling nvlist_free(config); 1252fa9e4066Sahrens return (error); 1253990b4856Slling } 1254fa9e4066Sahrens 1255fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1256ea8dc4b6Seschrock guid != zc->zc_guid) 1257fa9e4066Sahrens error = EINVAL; 1258fa9e4066Sahrens else 12594b964adaSGeorge Wilson error = spa_import(zc->zc_name, config, props, zc->zc_cookie); 1260fa9e4066Sahrens 12614b964adaSGeorge Wilson if (zc->zc_nvlist_dst != 0) { 12624b964adaSGeorge Wilson int err; 12634b964adaSGeorge Wilson 12644b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 12654b964adaSGeorge Wilson error = err; 12664b964adaSGeorge Wilson } 1267468c413aSTim Haley 1268fa9e4066Sahrens nvlist_free(config); 1269fa9e4066Sahrens 1270990b4856Slling if (props) 1271990b4856Slling nvlist_free(props); 1272990b4856Slling 1273fa9e4066Sahrens return (error); 1274fa9e4066Sahrens } 1275fa9e4066Sahrens 1276fa9e4066Sahrens static int 1277fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1278fa9e4066Sahrens { 1279ecd6cf80Smarks int error; 128089a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1281394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 128289a89ebfSlling 1283ecd6cf80Smarks zfs_log_history(zc); 1284394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1285681d9761SEric Taylor if (error == 0) 1286681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1287ecd6cf80Smarks return (error); 1288fa9e4066Sahrens } 1289fa9e4066Sahrens 1290fa9e4066Sahrens static int 1291fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1292fa9e4066Sahrens { 1293fa9e4066Sahrens nvlist_t *configs; 1294fa9e4066Sahrens int error; 1295fa9e4066Sahrens 1296fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1297fa9e4066Sahrens return (EEXIST); 1298fa9e4066Sahrens 1299e9dbad6fSeschrock error = put_nvlist(zc, configs); 1300fa9e4066Sahrens 1301fa9e4066Sahrens nvlist_free(configs); 1302fa9e4066Sahrens 1303fa9e4066Sahrens return (error); 1304fa9e4066Sahrens } 1305fa9e4066Sahrens 1306fa9e4066Sahrens static int 1307fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1308fa9e4066Sahrens { 1309fa9e4066Sahrens nvlist_t *config; 1310fa9e4066Sahrens int error; 1311ea8dc4b6Seschrock int ret = 0; 1312fa9e4066Sahrens 1313e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1314e9dbad6fSeschrock sizeof (zc->zc_value)); 1315fa9e4066Sahrens 1316fa9e4066Sahrens if (config != NULL) { 1317e9dbad6fSeschrock ret = put_nvlist(zc, config); 1318fa9e4066Sahrens nvlist_free(config); 1319ea8dc4b6Seschrock 1320ea8dc4b6Seschrock /* 1321ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1322ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1323ea8dc4b6Seschrock * in 'zc_cookie'. 1324ea8dc4b6Seschrock */ 1325ea8dc4b6Seschrock zc->zc_cookie = error; 1326fa9e4066Sahrens } else { 1327ea8dc4b6Seschrock ret = error; 1328fa9e4066Sahrens } 1329fa9e4066Sahrens 1330ea8dc4b6Seschrock return (ret); 1331fa9e4066Sahrens } 1332fa9e4066Sahrens 1333fa9e4066Sahrens /* 1334fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1335fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1336fa9e4066Sahrens */ 1337fa9e4066Sahrens static int 1338fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1339fa9e4066Sahrens { 1340fa9e4066Sahrens nvlist_t *tryconfig, *config; 1341fa9e4066Sahrens int error; 1342fa9e4066Sahrens 1343990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1344478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1345fa9e4066Sahrens return (error); 1346fa9e4066Sahrens 1347fa9e4066Sahrens config = spa_tryimport(tryconfig); 1348fa9e4066Sahrens 1349fa9e4066Sahrens nvlist_free(tryconfig); 1350fa9e4066Sahrens 1351fa9e4066Sahrens if (config == NULL) 1352fa9e4066Sahrens return (EINVAL); 1353fa9e4066Sahrens 1354e9dbad6fSeschrock error = put_nvlist(zc, config); 1355fa9e4066Sahrens nvlist_free(config); 1356fa9e4066Sahrens 1357fa9e4066Sahrens return (error); 1358fa9e4066Sahrens } 1359fa9e4066Sahrens 13603f9d6ad7SLin Ling /* 13613f9d6ad7SLin Ling * inputs: 13623f9d6ad7SLin Ling * zc_name name of the pool 13633f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 13643f9d6ad7SLin Ling */ 1365fa9e4066Sahrens static int 13663f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1367fa9e4066Sahrens { 1368fa9e4066Sahrens spa_t *spa; 1369fa9e4066Sahrens int error; 1370fa9e4066Sahrens 137106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 137206eeb2adSek return (error); 137306eeb2adSek 13743f9d6ad7SLin Ling if (zc->zc_cookie == POOL_SCAN_NONE) 13753f9d6ad7SLin Ling error = spa_scan_stop(spa); 13763f9d6ad7SLin Ling else 13773f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 137806eeb2adSek 137906eeb2adSek spa_close(spa, FTAG); 138006eeb2adSek 1381fa9e4066Sahrens return (error); 1382fa9e4066Sahrens } 1383fa9e4066Sahrens 1384fa9e4066Sahrens static int 1385fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1386fa9e4066Sahrens { 1387fa9e4066Sahrens spa_t *spa; 1388fa9e4066Sahrens int error; 1389fa9e4066Sahrens 1390fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1391fa9e4066Sahrens if (error == 0) { 1392fa9e4066Sahrens spa_freeze(spa); 1393fa9e4066Sahrens spa_close(spa, FTAG); 1394fa9e4066Sahrens } 1395fa9e4066Sahrens return (error); 1396fa9e4066Sahrens } 1397fa9e4066Sahrens 1398eaca9bbdSeschrock static int 1399eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1400eaca9bbdSeschrock { 1401eaca9bbdSeschrock spa_t *spa; 1402eaca9bbdSeschrock int error; 1403eaca9bbdSeschrock 140406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 140506eeb2adSek return (error); 140606eeb2adSek 1407558d2d50Slling if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) { 1408558d2d50Slling spa_close(spa, FTAG); 1409558d2d50Slling return (EINVAL); 1410558d2d50Slling } 1411558d2d50Slling 1412990b4856Slling spa_upgrade(spa, zc->zc_cookie); 141306eeb2adSek spa_close(spa, FTAG); 141406eeb2adSek 141506eeb2adSek return (error); 141606eeb2adSek } 141706eeb2adSek 141806eeb2adSek static int 141906eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 142006eeb2adSek { 142106eeb2adSek spa_t *spa; 142206eeb2adSek char *hist_buf; 142306eeb2adSek uint64_t size; 142406eeb2adSek int error; 142506eeb2adSek 142606eeb2adSek if ((size = zc->zc_history_len) == 0) 142706eeb2adSek return (EINVAL); 142806eeb2adSek 142906eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 143006eeb2adSek return (error); 143106eeb2adSek 1432e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1433d7306b64Sek spa_close(spa, FTAG); 1434d7306b64Sek return (ENOTSUP); 1435d7306b64Sek } 1436d7306b64Sek 143706eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 143806eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 143906eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1440478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1441478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1442478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 144306eeb2adSek } 144406eeb2adSek 144506eeb2adSek spa_close(spa, FTAG); 144606eeb2adSek kmem_free(hist_buf, size); 144706eeb2adSek return (error); 144806eeb2adSek } 144906eeb2adSek 145055434c77Sek static int 145155434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 145255434c77Sek { 145355434c77Sek int error; 145455434c77Sek 1455b1b8ab34Slling if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)) 145655434c77Sek return (error); 145755434c77Sek 145855434c77Sek return (0); 145955434c77Sek } 146055434c77Sek 1461503ad85cSMatthew Ahrens /* 1462503ad85cSMatthew Ahrens * inputs: 1463503ad85cSMatthew Ahrens * zc_name name of filesystem 1464503ad85cSMatthew Ahrens * zc_obj object to find 1465503ad85cSMatthew Ahrens * 1466503ad85cSMatthew Ahrens * outputs: 1467503ad85cSMatthew Ahrens * zc_value name of object 1468503ad85cSMatthew Ahrens */ 146955434c77Sek static int 147055434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 147155434c77Sek { 1472503ad85cSMatthew Ahrens objset_t *os; 147355434c77Sek int error; 147455434c77Sek 1475503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1476503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 147755434c77Sek return (error); 1478503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1479503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1480503ad85cSMatthew Ahrens return (EINVAL); 1481503ad85cSMatthew Ahrens } 1482503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 148355434c77Sek sizeof (zc->zc_value)); 1484503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 148555434c77Sek 148655434c77Sek return (error); 148755434c77Sek } 148855434c77Sek 148999d5e173STim Haley /* 149099d5e173STim Haley * inputs: 149199d5e173STim Haley * zc_name name of filesystem 149299d5e173STim Haley * zc_obj object to find 149399d5e173STim Haley * 149499d5e173STim Haley * outputs: 149599d5e173STim Haley * zc_stat stats on object 149699d5e173STim Haley * zc_value path to object 149799d5e173STim Haley */ 149899d5e173STim Haley static int 149999d5e173STim Haley zfs_ioc_obj_to_stats(zfs_cmd_t *zc) 150099d5e173STim Haley { 150199d5e173STim Haley objset_t *os; 150299d5e173STim Haley int error; 150399d5e173STim Haley 150499d5e173STim Haley /* XXX reading from objset not owned */ 150599d5e173STim Haley if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 150699d5e173STim Haley return (error); 150799d5e173STim Haley if (dmu_objset_type(os) != DMU_OST_ZFS) { 150899d5e173STim Haley dmu_objset_rele(os, FTAG); 150999d5e173STim Haley return (EINVAL); 151099d5e173STim Haley } 151199d5e173STim Haley error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value, 151299d5e173STim Haley sizeof (zc->zc_value)); 151399d5e173STim Haley dmu_objset_rele(os, FTAG); 151499d5e173STim Haley 151599d5e173STim Haley return (error); 151699d5e173STim Haley } 151799d5e173STim Haley 1518fa9e4066Sahrens static int 1519fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1520fa9e4066Sahrens { 1521fa9e4066Sahrens spa_t *spa; 1522fa9e4066Sahrens int error; 1523e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1524e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1525fa9e4066Sahrens 1526fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1527fa9e4066Sahrens if (error != 0) 1528fa9e4066Sahrens return (error); 1529fa9e4066Sahrens 1530fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1531478ed9adSEric Taylor zc->zc_iflags, &config); 1532fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1533fa94a07fSbrendan &l2cache, &nl2cache); 1534fa94a07fSbrendan 1535e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1536e7cbe64fSgw &spares, &nspares); 1537e7cbe64fSgw 1538b1b8ab34Slling /* 1539b1b8ab34Slling * A root pool with concatenated devices is not supported. 1540e7cbe64fSgw * Thus, can not add a device to a root pool. 1541e7cbe64fSgw * 1542e7cbe64fSgw * Intent log device can not be added to a rootpool because 1543e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1544e7cbe64fSgw * can not be accessed during the mountroot time. 1545e7cbe64fSgw * 1546e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1547b1b8ab34Slling */ 1548b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 15491195e687SMark J Musante nvlist_free(config); 1550b1b8ab34Slling spa_close(spa, FTAG); 1551b1b8ab34Slling return (EDOM); 1552b1b8ab34Slling } 1553b1b8ab34Slling 1554fa94a07fSbrendan if (error == 0) { 1555fa9e4066Sahrens error = spa_vdev_add(spa, config); 1556fa9e4066Sahrens nvlist_free(config); 1557fa9e4066Sahrens } 1558fa9e4066Sahrens spa_close(spa, FTAG); 1559fa9e4066Sahrens return (error); 1560fa9e4066Sahrens } 1561fa9e4066Sahrens 15623f9d6ad7SLin Ling /* 15633f9d6ad7SLin Ling * inputs: 15643f9d6ad7SLin Ling * zc_name name of the pool 15653f9d6ad7SLin Ling * zc_nvlist_conf nvlist of devices to remove 15663f9d6ad7SLin Ling * zc_cookie to stop the remove? 15673f9d6ad7SLin Ling */ 1568fa9e4066Sahrens static int 1569fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1570fa9e4066Sahrens { 157199653d4eSeschrock spa_t *spa; 157299653d4eSeschrock int error; 157399653d4eSeschrock 157499653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 157599653d4eSeschrock if (error != 0) 157699653d4eSeschrock return (error); 157799653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 157899653d4eSeschrock spa_close(spa, FTAG); 157999653d4eSeschrock return (error); 1580fa9e4066Sahrens } 1581fa9e4066Sahrens 1582fa9e4066Sahrens static int 15833d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1584fa9e4066Sahrens { 1585fa9e4066Sahrens spa_t *spa; 1586fa9e4066Sahrens int error; 15873d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1588fa9e4066Sahrens 158906eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1590fa9e4066Sahrens return (error); 15913d7072f8Seschrock switch (zc->zc_cookie) { 15923d7072f8Seschrock case VDEV_STATE_ONLINE: 15933d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 15943d7072f8Seschrock break; 1595fa9e4066Sahrens 15963d7072f8Seschrock case VDEV_STATE_OFFLINE: 15973d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 15983d7072f8Seschrock break; 1599fa9e4066Sahrens 16003d7072f8Seschrock case VDEV_STATE_FAULTED: 1601069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1602069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1603069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1604069f55e2SEric Schrock 1605069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 16063d7072f8Seschrock break; 16073d7072f8Seschrock 16083d7072f8Seschrock case VDEV_STATE_DEGRADED: 1609069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1610069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1611069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1612069f55e2SEric Schrock 1613069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 16143d7072f8Seschrock break; 16153d7072f8Seschrock 16163d7072f8Seschrock default: 16173d7072f8Seschrock error = EINVAL; 16183d7072f8Seschrock } 16193d7072f8Seschrock zc->zc_cookie = newstate; 1620fa9e4066Sahrens spa_close(spa, FTAG); 1621fa9e4066Sahrens return (error); 1622fa9e4066Sahrens } 1623fa9e4066Sahrens 1624fa9e4066Sahrens static int 1625fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1626fa9e4066Sahrens { 1627fa9e4066Sahrens spa_t *spa; 1628fa9e4066Sahrens int replacing = zc->zc_cookie; 1629fa9e4066Sahrens nvlist_t *config; 1630fa9e4066Sahrens int error; 1631fa9e4066Sahrens 163206eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1633fa9e4066Sahrens return (error); 1634fa9e4066Sahrens 1635990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1636478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1637ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1638fa9e4066Sahrens nvlist_free(config); 1639fa9e4066Sahrens } 1640fa9e4066Sahrens 1641fa9e4066Sahrens spa_close(spa, FTAG); 1642fa9e4066Sahrens return (error); 1643fa9e4066Sahrens } 1644fa9e4066Sahrens 1645fa9e4066Sahrens static int 1646fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1647fa9e4066Sahrens { 1648fa9e4066Sahrens spa_t *spa; 1649fa9e4066Sahrens int error; 1650fa9e4066Sahrens 165106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1652fa9e4066Sahrens return (error); 1653fa9e4066Sahrens 16548ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1655fa9e4066Sahrens 1656fa9e4066Sahrens spa_close(spa, FTAG); 1657fa9e4066Sahrens return (error); 1658fa9e4066Sahrens } 1659fa9e4066Sahrens 16601195e687SMark J Musante static int 16611195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 16621195e687SMark J Musante { 16631195e687SMark J Musante spa_t *spa; 16641195e687SMark J Musante nvlist_t *config, *props = NULL; 16651195e687SMark J Musante int error; 16661195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 16671195e687SMark J Musante 16681195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 16691195e687SMark J Musante return (error); 16701195e687SMark J Musante 16711195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 16721195e687SMark J Musante zc->zc_iflags, &config)) { 16731195e687SMark J Musante spa_close(spa, FTAG); 16741195e687SMark J Musante return (error); 16751195e687SMark J Musante } 16761195e687SMark J Musante 16771195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 16781195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 16791195e687SMark J Musante zc->zc_iflags, &props))) { 16801195e687SMark J Musante spa_close(spa, FTAG); 16811195e687SMark J Musante nvlist_free(config); 16821195e687SMark J Musante return (error); 16831195e687SMark J Musante } 16841195e687SMark J Musante 16851195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 16861195e687SMark J Musante 16871195e687SMark J Musante spa_close(spa, FTAG); 16881195e687SMark J Musante 16891195e687SMark J Musante nvlist_free(config); 16901195e687SMark J Musante nvlist_free(props); 16911195e687SMark J Musante 16921195e687SMark J Musante return (error); 16931195e687SMark J Musante } 16941195e687SMark J Musante 1695c67d9675Seschrock static int 1696c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 1697c67d9675Seschrock { 1698c67d9675Seschrock spa_t *spa; 1699e9dbad6fSeschrock char *path = zc->zc_value; 1700ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 1701c67d9675Seschrock int error; 1702c67d9675Seschrock 1703c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 1704c67d9675Seschrock if (error != 0) 1705c67d9675Seschrock return (error); 1706c67d9675Seschrock 1707c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 1708c67d9675Seschrock spa_close(spa, FTAG); 1709c67d9675Seschrock return (error); 1710c67d9675Seschrock } 1711c67d9675Seschrock 17126809eb4eSEric Schrock static int 17136809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 17146809eb4eSEric Schrock { 17156809eb4eSEric Schrock spa_t *spa; 17166809eb4eSEric Schrock char *fru = zc->zc_value; 17176809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 17186809eb4eSEric Schrock int error; 17196809eb4eSEric Schrock 17206809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 17216809eb4eSEric Schrock if (error != 0) 17226809eb4eSEric Schrock return (error); 17236809eb4eSEric Schrock 17246809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 17256809eb4eSEric Schrock spa_close(spa, FTAG); 17266809eb4eSEric Schrock return (error); 17276809eb4eSEric Schrock } 17286809eb4eSEric Schrock 1729fa9e4066Sahrens static int 1730a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) 1731fa9e4066Sahrens { 1732a7f53a56SChris Kirby int error = 0; 17337f7322feSeschrock nvlist_t *nv; 1734fa9e4066Sahrens 1735a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1736fa9e4066Sahrens 17375ad82045Snd if (zc->zc_nvlist_dst != 0 && 173892241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 1739a2eea2e1Sahrens dmu_objset_stats(os, nv); 1740432f72fdSahrens /* 1741bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 1742432f72fdSahrens * which we aren't supposed to do with a 1743745cd3c5Smaybee * DS_MODE_USER hold, because it could be 1744432f72fdSahrens * inconsistent. So this is a bit of a workaround... 1745503ad85cSMatthew Ahrens * XXX reading with out owning 1746432f72fdSahrens */ 1747e7437265Sahrens if (!zc->zc_objset_stats.dds_inconsistent) { 1748e7437265Sahrens if (dmu_objset_type(os) == DMU_OST_ZVOL) 1749e7437265Sahrens VERIFY(zvol_get_stats(os, nv) == 0); 1750e7437265Sahrens } 1751e9dbad6fSeschrock error = put_nvlist(zc, nv); 17527f7322feSeschrock nvlist_free(nv); 17537f7322feSeschrock } 1754fa9e4066Sahrens 1755a7f53a56SChris Kirby return (error); 1756a7f53a56SChris Kirby } 1757a7f53a56SChris Kirby 1758a7f53a56SChris Kirby /* 1759a7f53a56SChris Kirby * inputs: 1760a7f53a56SChris Kirby * zc_name name of filesystem 1761a7f53a56SChris Kirby * zc_nvlist_dst_size size of buffer for property nvlist 1762a7f53a56SChris Kirby * 1763a7f53a56SChris Kirby * outputs: 1764a7f53a56SChris Kirby * zc_objset_stats stats 1765a7f53a56SChris Kirby * zc_nvlist_dst property nvlist 1766a7f53a56SChris Kirby * zc_nvlist_dst_size size of property nvlist 1767a7f53a56SChris Kirby */ 1768a7f53a56SChris Kirby static int 1769a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc) 1770a7f53a56SChris Kirby { 1771a7f53a56SChris Kirby objset_t *os = NULL; 1772a7f53a56SChris Kirby int error; 1773a7f53a56SChris Kirby 1774a7f53a56SChris Kirby if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1775a7f53a56SChris Kirby return (error); 1776a7f53a56SChris Kirby 1777a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, os); 1778a7f53a56SChris Kirby 1779503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1780a7f53a56SChris Kirby 1781fa9e4066Sahrens return (error); 1782fa9e4066Sahrens } 1783fa9e4066Sahrens 178492241e0bSTom Erickson /* 178592241e0bSTom Erickson * inputs: 178692241e0bSTom Erickson * zc_name name of filesystem 178792241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 178892241e0bSTom Erickson * 178992241e0bSTom Erickson * outputs: 179092241e0bSTom Erickson * zc_nvlist_dst received property nvlist 179192241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 179292241e0bSTom Erickson * 179392241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 179492241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 179592241e0bSTom Erickson * local property values. 179692241e0bSTom Erickson */ 179792241e0bSTom Erickson static int 179892241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 179992241e0bSTom Erickson { 180092241e0bSTom Erickson objset_t *os = NULL; 180192241e0bSTom Erickson int error; 180292241e0bSTom Erickson nvlist_t *nv; 180392241e0bSTom Erickson 180492241e0bSTom Erickson if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 180592241e0bSTom Erickson return (error); 180692241e0bSTom Erickson 180792241e0bSTom Erickson /* 180892241e0bSTom Erickson * Without this check, we would return local property values if the 180992241e0bSTom Erickson * caller has not already received properties on or after 181092241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 181192241e0bSTom Erickson */ 181292241e0bSTom Erickson if (!dsl_prop_get_hasrecvd(os)) { 181392241e0bSTom Erickson dmu_objset_rele(os, FTAG); 181492241e0bSTom Erickson return (ENOTSUP); 181592241e0bSTom Erickson } 181692241e0bSTom Erickson 181792241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 181892241e0bSTom Erickson (error = dsl_prop_get_received(os, &nv)) == 0) { 181992241e0bSTom Erickson error = put_nvlist(zc, nv); 182092241e0bSTom Erickson nvlist_free(nv); 182192241e0bSTom Erickson } 182292241e0bSTom Erickson 182392241e0bSTom Erickson dmu_objset_rele(os, FTAG); 182492241e0bSTom Erickson return (error); 182592241e0bSTom Erickson } 182692241e0bSTom Erickson 1827de8267e0Stimh static int 1828de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 1829de8267e0Stimh { 1830de8267e0Stimh uint64_t value; 1831de8267e0Stimh int error; 1832de8267e0Stimh 1833de8267e0Stimh /* 1834de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 1835de8267e0Stimh * the default value (if there is one). 1836de8267e0Stimh */ 1837de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 1838de8267e0Stimh return (error); 1839de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 1840de8267e0Stimh return (0); 1841de8267e0Stimh } 1842de8267e0Stimh 18433cb34c60Sahrens /* 18443cb34c60Sahrens * inputs: 18453cb34c60Sahrens * zc_name name of filesystem 1846de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 18473cb34c60Sahrens * 18483cb34c60Sahrens * outputs: 1849de8267e0Stimh * zc_nvlist_dst zpl property nvlist 1850de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 18513cb34c60Sahrens */ 1852bd00f61bSrm static int 1853de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 1854bd00f61bSrm { 1855de8267e0Stimh objset_t *os; 1856de8267e0Stimh int err; 1857bd00f61bSrm 1858503ad85cSMatthew Ahrens /* XXX reading without owning */ 1859503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1860de8267e0Stimh return (err); 1861bd00f61bSrm 1862bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1863bd00f61bSrm 1864bd00f61bSrm /* 1865de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 1866745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 1867745cd3c5Smaybee * hold, because it could be inconsistent. 1868bd00f61bSrm */ 1869de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 1870de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 1871de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 1872de8267e0Stimh nvlist_t *nv; 1873de8267e0Stimh 1874de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1875de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 1876de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 1877de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 1878de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 1879de8267e0Stimh err = put_nvlist(zc, nv); 1880de8267e0Stimh nvlist_free(nv); 1881de8267e0Stimh } else { 1882de8267e0Stimh err = ENOENT; 1883de8267e0Stimh } 1884503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1885de8267e0Stimh return (err); 1886bd00f61bSrm } 1887bd00f61bSrm 188814843421SMatthew Ahrens static boolean_t 188914843421SMatthew Ahrens dataset_name_hidden(const char *name) 189014843421SMatthew Ahrens { 189114843421SMatthew Ahrens /* 189214843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 189314843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 189414843421SMatthew Ahrens * temporary datasets (which have a % in their name). 189514843421SMatthew Ahrens */ 189614843421SMatthew Ahrens if (strchr(name, '$') != NULL) 189714843421SMatthew Ahrens return (B_TRUE); 189814843421SMatthew Ahrens if (strchr(name, '%') != NULL) 189914843421SMatthew Ahrens return (B_TRUE); 190014843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 190114843421SMatthew Ahrens return (B_TRUE); 190214843421SMatthew Ahrens return (B_FALSE); 190314843421SMatthew Ahrens } 190414843421SMatthew Ahrens 1905de8267e0Stimh /* 1906de8267e0Stimh * inputs: 1907de8267e0Stimh * zc_name name of filesystem 1908de8267e0Stimh * zc_cookie zap cursor 1909de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 1910de8267e0Stimh * 1911de8267e0Stimh * outputs: 1912de8267e0Stimh * zc_name name of next filesystem 191314843421SMatthew Ahrens * zc_cookie zap cursor 1914de8267e0Stimh * zc_objset_stats stats 1915de8267e0Stimh * zc_nvlist_dst property nvlist 1916de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 1917de8267e0Stimh */ 1918fa9e4066Sahrens static int 1919fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 1920fa9e4066Sahrens { 192187e5029aSahrens objset_t *os; 1922fa9e4066Sahrens int error; 1923fa9e4066Sahrens char *p; 1924620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 1925fa9e4066Sahrens 1926620252bcSChris Kirby top: 1927503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 192887e5029aSahrens if (error == ENOENT) 192987e5029aSahrens error = ESRCH; 193087e5029aSahrens return (error); 1931fa9e4066Sahrens } 1932fa9e4066Sahrens 1933fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 1934fa9e4066Sahrens if (p == NULL || p[1] != '\0') 1935fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 1936fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 1937fa9e4066Sahrens 19385c0b6a79SRich Morris /* 19395c0b6a79SRich Morris * Pre-fetch the datasets. dmu_objset_prefetch() always returns 0 19405c0b6a79SRich Morris * but is not declared void because its called by dmu_objset_find(). 19415c0b6a79SRich Morris */ 19427f73c863SRich Morris if (zc->zc_cookie == 0) { 19437f73c863SRich Morris uint64_t cookie = 0; 19447f73c863SRich Morris int len = sizeof (zc->zc_name) - (p - zc->zc_name); 19457f73c863SRich Morris 19467f73c863SRich Morris while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) 19475c0b6a79SRich Morris (void) dmu_objset_prefetch(p, NULL); 19487f73c863SRich Morris } 19497f73c863SRich Morris 1950fa9e4066Sahrens do { 195187e5029aSahrens error = dmu_dir_list_next(os, 195287e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 195387e5029aSahrens NULL, &zc->zc_cookie); 1954fa9e4066Sahrens if (error == ENOENT) 1955fa9e4066Sahrens error = ESRCH; 1956681d9761SEric Taylor } while (error == 0 && dataset_name_hidden(zc->zc_name) && 1957681d9761SEric Taylor !(zc->zc_iflags & FKIOCTL)); 1958503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1959fa9e4066Sahrens 1960681d9761SEric Taylor /* 1961681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 1962681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 1963681d9761SEric Taylor */ 1964620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 196587e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1966620252bcSChris Kirby if (error == ENOENT) { 1967620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 1968620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 1969620252bcSChris Kirby goto top; 1970620252bcSChris Kirby } 1971620252bcSChris Kirby } 1972fa9e4066Sahrens return (error); 1973fa9e4066Sahrens } 1974fa9e4066Sahrens 19753cb34c60Sahrens /* 19763cb34c60Sahrens * inputs: 19773cb34c60Sahrens * zc_name name of filesystem 19783cb34c60Sahrens * zc_cookie zap cursor 19793cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 19803cb34c60Sahrens * 19813cb34c60Sahrens * outputs: 19823cb34c60Sahrens * zc_name name of next snapshot 19833cb34c60Sahrens * zc_objset_stats stats 19843cb34c60Sahrens * zc_nvlist_dst property nvlist 19853cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 19863cb34c60Sahrens */ 1987fa9e4066Sahrens static int 1988fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 1989fa9e4066Sahrens { 199087e5029aSahrens objset_t *os; 1991fa9e4066Sahrens int error; 1992fa9e4066Sahrens 1993620252bcSChris Kirby top: 19947cbf8b43SRich Morris if (zc->zc_cookie == 0) 19957cbf8b43SRich Morris (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, 19967cbf8b43SRich Morris NULL, DS_FIND_SNAPSHOTS); 19977cbf8b43SRich Morris 1998503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 1999745cd3c5Smaybee if (error) 2000745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 2001fa9e4066Sahrens 2002b81d61a6Slling /* 2003b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 2004b81d61a6Slling * so exit immediately. 2005b81d61a6Slling */ 2006b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 2007503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2008b81d61a6Slling return (ESRCH); 2009fa9e4066Sahrens } 2010fa9e4066Sahrens 201187e5029aSahrens error = dmu_snapshot_list_next(os, 201287e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 2013a7f53a56SChris Kirby zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, 2014a7f53a56SChris Kirby NULL); 2015a7f53a56SChris Kirby 2016620252bcSChris Kirby if (error == 0) { 2017a7f53a56SChris Kirby dsl_dataset_t *ds; 2018a7f53a56SChris Kirby dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; 2019a7f53a56SChris Kirby 2020a7f53a56SChris Kirby /* 2021a7f53a56SChris Kirby * Since we probably don't have a hold on this snapshot, 2022a7f53a56SChris Kirby * it's possible that the objsetid could have been destroyed 2023a7f53a56SChris Kirby * and reused for a new objset. It's OK if this happens during 2024a7f53a56SChris Kirby * a zfs send operation, since the new createtxg will be 2025a7f53a56SChris Kirby * beyond the range we're interested in. 2026a7f53a56SChris Kirby */ 2027a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 2028a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); 2029a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 2030a7f53a56SChris Kirby if (error) { 2031a7f53a56SChris Kirby if (error == ENOENT) { 2032a7f53a56SChris Kirby /* Racing with destroy, get the next one. */ 2033a7f53a56SChris Kirby *strchr(zc->zc_name, '@') = '\0'; 2034a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 2035a7f53a56SChris Kirby goto top; 2036a7f53a56SChris Kirby } 2037a7f53a56SChris Kirby } else { 2038a7f53a56SChris Kirby objset_t *ossnap; 2039a7f53a56SChris Kirby 2040a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &ossnap); 2041a7f53a56SChris Kirby if (error == 0) 2042a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, ossnap); 2043a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 2044620252bcSChris Kirby } 2045620252bcSChris Kirby } else if (error == ENOENT) { 2046745cd3c5Smaybee error = ESRCH; 2047620252bcSChris Kirby } 2048fa9e4066Sahrens 2049a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 20503cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 2051745cd3c5Smaybee if (error) 20523cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 2053fa9e4066Sahrens return (error); 2054fa9e4066Sahrens } 2055fa9e4066Sahrens 205692241e0bSTom Erickson static int 205792241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 2058fa9e4066Sahrens { 205992241e0bSTom Erickson const char *propname = nvpair_name(pair); 206092241e0bSTom Erickson uint64_t *valary; 206192241e0bSTom Erickson unsigned int vallen; 206292241e0bSTom Erickson const char *domain; 2063eeb85002STim Haley char *dash; 206492241e0bSTom Erickson zfs_userquota_prop_t type; 206592241e0bSTom Erickson uint64_t rid; 206692241e0bSTom Erickson uint64_t quota; 206792241e0bSTom Erickson zfsvfs_t *zfsvfs; 206892241e0bSTom Erickson int err; 206992241e0bSTom Erickson 207092241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 207192241e0bSTom Erickson nvlist_t *attrs; 207292241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2073eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2074eeb85002STim Haley &pair) != 0) 2075eeb85002STim Haley return (EINVAL); 207692241e0bSTom Erickson } 2077e9dbad6fSeschrock 2078ecd6cf80Smarks /* 2079eeb85002STim Haley * A correctly constructed propname is encoded as 208092241e0bSTom Erickson * userquota@<rid>-<domain>. 2081ecd6cf80Smarks */ 2082eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 2083eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 2084eeb85002STim Haley vallen != 3) 2085eeb85002STim Haley return (EINVAL); 2086eeb85002STim Haley 2087eeb85002STim Haley domain = dash + 1; 2088eeb85002STim Haley type = valary[0]; 2089eeb85002STim Haley rid = valary[1]; 2090eeb85002STim Haley quota = valary[2]; 2091e9dbad6fSeschrock 20921412a1a2SMark Shellenbaum err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); 209392241e0bSTom Erickson if (err == 0) { 209492241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 209592241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 209692241e0bSTom Erickson } 2097e9dbad6fSeschrock 209892241e0bSTom Erickson return (err); 209992241e0bSTom Erickson } 210014843421SMatthew Ahrens 210192241e0bSTom Erickson /* 210292241e0bSTom Erickson * If the named property is one that has a special function to set its value, 210392241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 210492241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 210592241e0bSTom Erickson * 2106eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 210792241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 210892241e0bSTom Erickson */ 210992241e0bSTom Erickson static int 211092241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 211192241e0bSTom Erickson nvpair_t *pair) 211292241e0bSTom Erickson { 211392241e0bSTom Erickson const char *propname = nvpair_name(pair); 211492241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 211592241e0bSTom Erickson uint64_t intval; 211692241e0bSTom Erickson int err; 2117fa9e4066Sahrens 211892241e0bSTom Erickson if (prop == ZPROP_INVAL) { 211992241e0bSTom Erickson if (zfs_prop_userquota(propname)) 212092241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 212192241e0bSTom Erickson return (-1); 212292241e0bSTom Erickson } 212314843421SMatthew Ahrens 212492241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 212592241e0bSTom Erickson nvlist_t *attrs; 212692241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 212792241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 212892241e0bSTom Erickson &pair) == 0); 212992241e0bSTom Erickson } 2130db870a07Sahrens 213192241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 213292241e0bSTom Erickson return (-1); 2133b24ab676SJeff Bonwick 213492241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 213540feaa91Sahrens 213692241e0bSTom Erickson switch (prop) { 213792241e0bSTom Erickson case ZFS_PROP_QUOTA: 213892241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 213992241e0bSTom Erickson break; 214092241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 214192241e0bSTom Erickson err = dsl_dataset_set_quota(dsname, source, intval); 214292241e0bSTom Erickson break; 214392241e0bSTom Erickson case ZFS_PROP_RESERVATION: 214492241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 214592241e0bSTom Erickson break; 214692241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 214792241e0bSTom Erickson err = dsl_dataset_set_reservation(dsname, source, intval); 214892241e0bSTom Erickson break; 214992241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 215092241e0bSTom Erickson err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip), 215192241e0bSTom Erickson intval); 215292241e0bSTom Erickson break; 215392241e0bSTom Erickson case ZFS_PROP_VERSION: 215492241e0bSTom Erickson { 215592241e0bSTom Erickson zfsvfs_t *zfsvfs; 21569e6eda55Smarks 21571412a1a2SMark Shellenbaum if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) 2158b24ab676SJeff Bonwick break; 2159b24ab676SJeff Bonwick 216092241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 216192241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 2162d0f3f37eSMark Shellenbaum 216392241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 2164b16da2e2SGeorge Wilson zfs_cmd_t *zc; 2165b16da2e2SGeorge Wilson 2166b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 2167b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 2168b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 2169b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 217040feaa91Sahrens } 217192241e0bSTom Erickson break; 2172ecd6cf80Smarks } 2173ecd6cf80Smarks 217492241e0bSTom Erickson default: 217592241e0bSTom Erickson err = -1; 217692241e0bSTom Erickson } 2177e9dbad6fSeschrock 217892241e0bSTom Erickson return (err); 217992241e0bSTom Erickson } 2180a9799022Sck 218192241e0bSTom Erickson /* 218292241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 218392241e0bSTom Erickson * it continues to set as many as it can and returns the first error 218492241e0bSTom Erickson * encountered. If the caller provides a non-NULL errlist, it also gives the 218592241e0bSTom Erickson * complete list of names of all the properties it failed to set along with the 218692241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 218792241e0bSTom Erickson * returned errlist. 218892241e0bSTom Erickson * 218992241e0bSTom Erickson * If every property is set successfully, zero is returned and the list pointed 219092241e0bSTom Erickson * at by errlist is NULL. 219192241e0bSTom Erickson */ 219292241e0bSTom Erickson int 219392241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 219492241e0bSTom Erickson nvlist_t **errlist) 219592241e0bSTom Erickson { 219692241e0bSTom Erickson nvpair_t *pair; 219792241e0bSTom Erickson nvpair_t *propval; 219802e383d1STom Erickson int rv = 0; 219992241e0bSTom Erickson uint64_t intval; 220092241e0bSTom Erickson char *strval; 220192241e0bSTom Erickson nvlist_t *genericnvl; 220292241e0bSTom Erickson nvlist_t *errors; 220392241e0bSTom Erickson nvlist_t *retrynvl; 2204e9dbad6fSeschrock 220592241e0bSTom Erickson VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 220692241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 220792241e0bSTom Erickson VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2208a9799022Sck 220992241e0bSTom Erickson retry: 221092241e0bSTom Erickson pair = NULL; 221192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 221292241e0bSTom Erickson const char *propname = nvpair_name(pair); 221392241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2214cfa69fd2STom Erickson int err = 0; 2215e9dbad6fSeschrock 221692241e0bSTom Erickson /* decode the property value */ 221792241e0bSTom Erickson propval = pair; 221892241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 221992241e0bSTom Erickson nvlist_t *attrs; 222092241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2221eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2222eeb85002STim Haley &propval) != 0) 2223eeb85002STim Haley err = EINVAL; 222414843421SMatthew Ahrens } 2225e9dbad6fSeschrock 222692241e0bSTom Erickson /* Validate value type */ 2227eeb85002STim Haley if (err == 0 && prop == ZPROP_INVAL) { 222892241e0bSTom Erickson if (zfs_prop_user(propname)) { 222992241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 223092241e0bSTom Erickson err = EINVAL; 223192241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 223292241e0bSTom Erickson if (nvpair_type(propval) != 223392241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 223492241e0bSTom Erickson err = EINVAL; 22354201a95eSRic Aleshire } 2236eeb85002STim Haley } else if (err == 0) { 223792241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 223892241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 223992241e0bSTom Erickson err = EINVAL; 224092241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2241a2eea2e1Sahrens const char *unused; 2242a2eea2e1Sahrens 224392241e0bSTom Erickson VERIFY(nvpair_value_uint64(propval, 224492241e0bSTom Erickson &intval) == 0); 2245e9dbad6fSeschrock 2246e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 224791ebeef5Sahrens case PROP_TYPE_NUMBER: 2248e9dbad6fSeschrock break; 224991ebeef5Sahrens case PROP_TYPE_STRING: 225092241e0bSTom Erickson err = EINVAL; 225192241e0bSTom Erickson break; 225291ebeef5Sahrens case PROP_TYPE_INDEX: 2253acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 225492241e0bSTom Erickson intval, &unused) != 0) 225592241e0bSTom Erickson err = EINVAL; 2256e9dbad6fSeschrock break; 2257e9dbad6fSeschrock default: 2258e7437265Sahrens cmn_err(CE_PANIC, 2259e7437265Sahrens "unknown property type"); 2260e9dbad6fSeschrock } 2261e9dbad6fSeschrock } else { 226292241e0bSTom Erickson err = EINVAL; 2263e9dbad6fSeschrock } 2264e9dbad6fSeschrock } 226592241e0bSTom Erickson 226692241e0bSTom Erickson /* Validate permissions */ 226792241e0bSTom Erickson if (err == 0) 226892241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 226992241e0bSTom Erickson 227092241e0bSTom Erickson if (err == 0) { 227192241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 227292241e0bSTom Erickson if (err == -1) { 227392241e0bSTom Erickson /* 227492241e0bSTom Erickson * For better performance we build up a list of 227592241e0bSTom Erickson * properties to set in a single transaction. 227692241e0bSTom Erickson */ 227792241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 227892241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 227992241e0bSTom Erickson /* 228092241e0bSTom Erickson * This may be a spurious error caused by 228192241e0bSTom Erickson * receiving quota and reservation out of order. 228292241e0bSTom Erickson * Try again in a second pass. 228392241e0bSTom Erickson */ 228492241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 228592241e0bSTom Erickson } 228692241e0bSTom Erickson } 228792241e0bSTom Erickson 228892241e0bSTom Erickson if (err != 0) 228992241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, propname, err) == 0); 2290e9dbad6fSeschrock } 2291e9dbad6fSeschrock 229292241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 229392241e0bSTom Erickson nvl = retrynvl; 229492241e0bSTom Erickson goto retry; 229592241e0bSTom Erickson } 229692241e0bSTom Erickson 229792241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 229892241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 229992241e0bSTom Erickson /* 230092241e0bSTom Erickson * If this fails, we still want to set as many properties as we 230192241e0bSTom Erickson * can, so try setting them individually. 230292241e0bSTom Erickson */ 230392241e0bSTom Erickson pair = NULL; 230492241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 230592241e0bSTom Erickson const char *propname = nvpair_name(pair); 2306cfa69fd2STom Erickson int err = 0; 230792241e0bSTom Erickson 230892241e0bSTom Erickson propval = pair; 230992241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 231092241e0bSTom Erickson nvlist_t *attrs; 231192241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 231292241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 231392241e0bSTom Erickson &propval) == 0); 231492241e0bSTom Erickson } 231592241e0bSTom Erickson 231692241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 231792241e0bSTom Erickson VERIFY(nvpair_value_string(propval, 231892241e0bSTom Erickson &strval) == 0); 231992241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 1, 232092241e0bSTom Erickson strlen(strval) + 1, strval); 232192241e0bSTom Erickson } else { 232292241e0bSTom Erickson VERIFY(nvpair_value_uint64(propval, 232392241e0bSTom Erickson &intval) == 0); 232492241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 8, 232592241e0bSTom Erickson 1, &intval); 232692241e0bSTom Erickson } 232792241e0bSTom Erickson 232892241e0bSTom Erickson if (err != 0) { 232992241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, propname, 233092241e0bSTom Erickson err) == 0); 233192241e0bSTom Erickson } 233292241e0bSTom Erickson } 23335c0b6a79SRich Morris } 23345c0b6a79SRich Morris nvlist_free(genericnvl); 233592241e0bSTom Erickson nvlist_free(retrynvl); 233692241e0bSTom Erickson 233792241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 233892241e0bSTom Erickson nvlist_free(errors); 233992241e0bSTom Erickson errors = NULL; 234092241e0bSTom Erickson } else { 234192241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 234292241e0bSTom Erickson } 234392241e0bSTom Erickson 234492241e0bSTom Erickson if (errlist == NULL) 234592241e0bSTom Erickson nvlist_free(errors); 234692241e0bSTom Erickson else 234792241e0bSTom Erickson *errlist = errors; 234892241e0bSTom Erickson 234992241e0bSTom Erickson return (rv); 2350fa9e4066Sahrens } 2351fa9e4066Sahrens 2352ea2f5b9eSMatthew Ahrens /* 2353ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2354ea2f5b9eSMatthew Ahrens */ 2355ea2f5b9eSMatthew Ahrens static int 2356ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl) 2357ea2f5b9eSMatthew Ahrens { 235892241e0bSTom Erickson nvpair_t *pair = NULL; 2359ea2f5b9eSMatthew Ahrens int error = 0; 2360ea2f5b9eSMatthew Ahrens 236192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 236292241e0bSTom Erickson const char *propname = nvpair_name(pair); 2363ea2f5b9eSMatthew Ahrens char *valstr; 2364ea2f5b9eSMatthew Ahrens 2365ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 236692241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2367ea2f5b9eSMatthew Ahrens return (EINVAL); 2368ea2f5b9eSMatthew Ahrens 2369ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2370ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2371ea2f5b9eSMatthew Ahrens return (error); 2372ea2f5b9eSMatthew Ahrens 2373ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2374ea2f5b9eSMatthew Ahrens return (ENAMETOOLONG); 2375ea2f5b9eSMatthew Ahrens 237692241e0bSTom Erickson VERIFY(nvpair_value_string(pair, &valstr) == 0); 2377ea2f5b9eSMatthew Ahrens if (strlen(valstr) >= ZAP_MAXVALUELEN) 2378ea2f5b9eSMatthew Ahrens return (E2BIG); 2379ea2f5b9eSMatthew Ahrens } 2380ea2f5b9eSMatthew Ahrens return (0); 2381ea2f5b9eSMatthew Ahrens } 2382ea2f5b9eSMatthew Ahrens 238392241e0bSTom Erickson static void 238492241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 238592241e0bSTom Erickson { 238692241e0bSTom Erickson nvpair_t *pair; 238792241e0bSTom Erickson 238892241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 238992241e0bSTom Erickson 239092241e0bSTom Erickson pair = NULL; 239192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 239292241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 239392241e0bSTom Erickson continue; 239492241e0bSTom Erickson 239592241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 239692241e0bSTom Erickson } 239792241e0bSTom Erickson } 239892241e0bSTom Erickson 239992241e0bSTom Erickson static int 240092241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props, 240192241e0bSTom Erickson nvlist_t *skipped) 240292241e0bSTom Erickson { 240392241e0bSTom Erickson int err = 0; 240492241e0bSTom Erickson nvlist_t *cleared_props = NULL; 240592241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 240692241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 240792241e0bSTom Erickson /* 240892241e0bSTom Erickson * Acts on local properties until the dataset has received 240992241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 241092241e0bSTom Erickson */ 241192241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 241292241e0bSTom Erickson (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0)); 241392241e0bSTom Erickson err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL); 241492241e0bSTom Erickson } 241592241e0bSTom Erickson nvlist_free(cleared_props); 241692241e0bSTom Erickson return (err); 241792241e0bSTom Erickson } 241892241e0bSTom Erickson 24193cb34c60Sahrens /* 24203cb34c60Sahrens * inputs: 24213cb34c60Sahrens * zc_name name of filesystem 24225c0b6a79SRich Morris * zc_value name of property to set 24233cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 242492241e0bSTom Erickson * zc_cookie received properties flag 24253cb34c60Sahrens * 242692241e0bSTom Erickson * outputs: 242792241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 24283cb34c60Sahrens */ 2429fa9e4066Sahrens static int 2430e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2431fa9e4066Sahrens { 2432e9dbad6fSeschrock nvlist_t *nvl; 243392241e0bSTom Erickson boolean_t received = zc->zc_cookie; 243492241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 243592241e0bSTom Erickson ZPROP_SRC_LOCAL); 243692241e0bSTom Erickson nvlist_t *errors = NULL; 2437e9dbad6fSeschrock int error; 2438e9dbad6fSeschrock 2439990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2440478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2441e9dbad6fSeschrock return (error); 2442e9dbad6fSeschrock 244392241e0bSTom Erickson if (received) { 2444bb0ade09Sahrens nvlist_t *origprops; 2445bb0ade09Sahrens objset_t *os; 2446bb0ade09Sahrens 2447503ad85cSMatthew Ahrens if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) { 244892241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 244992241e0bSTom Erickson (void) clear_received_props(os, 245092241e0bSTom Erickson zc->zc_name, origprops, nvl); 2451bb0ade09Sahrens nvlist_free(origprops); 2452bb0ade09Sahrens } 245392241e0bSTom Erickson 245492241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 2455503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2456bb0ade09Sahrens } 2457bb0ade09Sahrens } 2458bb0ade09Sahrens 245992241e0bSTom Erickson error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors); 246092241e0bSTom Erickson 246192241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 246292241e0bSTom Erickson (void) put_nvlist(zc, errors); 246392241e0bSTom Erickson } 2464ecd6cf80Smarks 246592241e0bSTom Erickson nvlist_free(errors); 2466e9dbad6fSeschrock nvlist_free(nvl); 2467e9dbad6fSeschrock return (error); 2468fa9e4066Sahrens } 2469fa9e4066Sahrens 24703cb34c60Sahrens /* 24713cb34c60Sahrens * inputs: 24723cb34c60Sahrens * zc_name name of filesystem 24733cb34c60Sahrens * zc_value name of property to inherit 247492241e0bSTom Erickson * zc_cookie revert to received value if TRUE 24753cb34c60Sahrens * 24763cb34c60Sahrens * outputs: none 24773cb34c60Sahrens */ 2478e45ce728Sahrens static int 2479e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2480e45ce728Sahrens { 248192241e0bSTom Erickson const char *propname = zc->zc_value; 248292241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 248392241e0bSTom Erickson boolean_t received = zc->zc_cookie; 248492241e0bSTom Erickson zprop_source_t source = (received 248592241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 248692241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 248792241e0bSTom Erickson 248892241e0bSTom Erickson if (received) { 248992241e0bSTom Erickson nvlist_t *dummy; 249092241e0bSTom Erickson nvpair_t *pair; 249192241e0bSTom Erickson zprop_type_t type; 249292241e0bSTom Erickson int err; 249392241e0bSTom Erickson 249492241e0bSTom Erickson /* 249592241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 249692241e0bSTom Erickson * nvpair with type info. 249792241e0bSTom Erickson */ 249892241e0bSTom Erickson if (prop == ZPROP_INVAL) { 249992241e0bSTom Erickson if (!zfs_prop_user(propname)) 250092241e0bSTom Erickson return (EINVAL); 250192241e0bSTom Erickson 250292241e0bSTom Erickson type = PROP_TYPE_STRING; 2503a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2504a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2505a79992aaSTom Erickson return (EINVAL); 250692241e0bSTom Erickson } else { 250792241e0bSTom Erickson type = zfs_prop_get_type(prop); 250892241e0bSTom Erickson } 250992241e0bSTom Erickson 251092241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 251192241e0bSTom Erickson 251292241e0bSTom Erickson switch (type) { 251392241e0bSTom Erickson case PROP_TYPE_STRING: 251492241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 251592241e0bSTom Erickson break; 251692241e0bSTom Erickson case PROP_TYPE_NUMBER: 251792241e0bSTom Erickson case PROP_TYPE_INDEX: 251892241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 251992241e0bSTom Erickson break; 252092241e0bSTom Erickson default: 252192241e0bSTom Erickson nvlist_free(dummy); 252292241e0bSTom Erickson return (EINVAL); 252392241e0bSTom Erickson } 252492241e0bSTom Erickson 252592241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 252692241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 252792241e0bSTom Erickson nvlist_free(dummy); 252892241e0bSTom Erickson if (err != -1) 252992241e0bSTom Erickson return (err); /* special property already handled */ 253092241e0bSTom Erickson } else { 253192241e0bSTom Erickson /* 253292241e0bSTom Erickson * Only check this in the non-received case. We want to allow 253392241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 253492241e0bSTom Erickson * and reservation to the received or default values even though 253592241e0bSTom Erickson * they are not considered inheritable. 253692241e0bSTom Erickson */ 253792241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 253892241e0bSTom Erickson return (EINVAL); 253992241e0bSTom Erickson } 254092241e0bSTom Erickson 2541e45ce728Sahrens /* the property name has been validated by zfs_secpolicy_inherit() */ 254292241e0bSTom Erickson return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL)); 2543e45ce728Sahrens } 2544e45ce728Sahrens 2545b1b8ab34Slling static int 254611a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2547b1b8ab34Slling { 2548990b4856Slling nvlist_t *props; 2549b1b8ab34Slling spa_t *spa; 2550990b4856Slling int error; 255192241e0bSTom Erickson nvpair_t *pair; 2552b1b8ab34Slling 255392241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 255492241e0bSTom Erickson zc->zc_iflags, &props)) 2555b1b8ab34Slling return (error); 2556b1b8ab34Slling 2557379c004dSEric Schrock /* 2558379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2559379c004dSEric Schrock * to handle the faulted case. 2560379c004dSEric Schrock */ 256192241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 256292241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2563379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 256492241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2565379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2566379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2567379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2568379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2569379c004dSEric Schrock } 2570379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2571b693757aSEric Schrock if (spa != NULL) { 2572b693757aSEric Schrock nvlist_free(props); 2573379c004dSEric Schrock return (0); 2574b693757aSEric Schrock } 2575379c004dSEric Schrock } 2576379c004dSEric Schrock 2577b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2578990b4856Slling nvlist_free(props); 2579b1b8ab34Slling return (error); 2580b1b8ab34Slling } 2581b1b8ab34Slling 2582990b4856Slling error = spa_prop_set(spa, props); 2583b1b8ab34Slling 2584990b4856Slling nvlist_free(props); 2585b1b8ab34Slling spa_close(spa, FTAG); 2586b1b8ab34Slling 2587b1b8ab34Slling return (error); 2588b1b8ab34Slling } 2589b1b8ab34Slling 2590b1b8ab34Slling static int 259111a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2592b1b8ab34Slling { 2593b1b8ab34Slling spa_t *spa; 2594b1b8ab34Slling int error; 2595b1b8ab34Slling nvlist_t *nvp = NULL; 2596b1b8ab34Slling 2597379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2598379c004dSEric Schrock /* 2599379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2600379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2601379c004dSEric Schrock * anyway. 2602379c004dSEric Schrock */ 2603379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2604379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2605379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2606379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2607379c004dSEric Schrock } else { 2608379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2609379c004dSEric Schrock spa_close(spa, FTAG); 2610379c004dSEric Schrock } 2611b1b8ab34Slling 2612b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2613b1b8ab34Slling error = put_nvlist(zc, nvp); 2614b1b8ab34Slling else 2615b1b8ab34Slling error = EFAULT; 2616b1b8ab34Slling 2617379c004dSEric Schrock nvlist_free(nvp); 2618b1b8ab34Slling return (error); 2619b1b8ab34Slling } 2620b1b8ab34Slling 26213cb34c60Sahrens /* 26223cb34c60Sahrens * inputs: 26233cb34c60Sahrens * zc_name name of filesystem 26243cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 26253cb34c60Sahrens * zc_perm_action allow/unallow flag 26263cb34c60Sahrens * 26273cb34c60Sahrens * outputs: none 26283cb34c60Sahrens */ 2629ecd6cf80Smarks static int 2630ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2631ecd6cf80Smarks { 2632ecd6cf80Smarks int error; 2633ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2634ecd6cf80Smarks 2635990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2636478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2637ecd6cf80Smarks return (error); 2638ecd6cf80Smarks 2639ecd6cf80Smarks /* 2640ecd6cf80Smarks * Verify nvlist is constructed correctly 2641ecd6cf80Smarks */ 2642ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2643ecd6cf80Smarks nvlist_free(fsaclnv); 2644ecd6cf80Smarks return (EINVAL); 2645ecd6cf80Smarks } 2646ecd6cf80Smarks 2647ecd6cf80Smarks /* 2648ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2649ecd6cf80Smarks * that user is allowed to hand out each permission in 2650ecd6cf80Smarks * the nvlist(s) 2651ecd6cf80Smarks */ 2652ecd6cf80Smarks 265391ebeef5Sahrens error = secpolicy_zfs(CRED()); 2654ecd6cf80Smarks if (error) { 265591ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 265691ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 265791ebeef5Sahrens fsaclnv, CRED()); 265891ebeef5Sahrens } else { 265991ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 266091ebeef5Sahrens fsaclnv, CRED()); 266191ebeef5Sahrens } 2662ecd6cf80Smarks } 2663ecd6cf80Smarks 2664ecd6cf80Smarks if (error == 0) 2665ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2666ecd6cf80Smarks 2667ecd6cf80Smarks nvlist_free(fsaclnv); 2668ecd6cf80Smarks return (error); 2669ecd6cf80Smarks } 2670ecd6cf80Smarks 26713cb34c60Sahrens /* 26723cb34c60Sahrens * inputs: 26733cb34c60Sahrens * zc_name name of filesystem 26743cb34c60Sahrens * 26753cb34c60Sahrens * outputs: 26763cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 26773cb34c60Sahrens */ 2678ecd6cf80Smarks static int 2679ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2680ecd6cf80Smarks { 2681ecd6cf80Smarks nvlist_t *nvp; 2682ecd6cf80Smarks int error; 2683ecd6cf80Smarks 2684ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2685ecd6cf80Smarks error = put_nvlist(zc, nvp); 2686ecd6cf80Smarks nvlist_free(nvp); 2687ecd6cf80Smarks } 2688ecd6cf80Smarks 2689ecd6cf80Smarks return (error); 2690ecd6cf80Smarks } 2691ecd6cf80Smarks 2692fa9e4066Sahrens /* 2693fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2694fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2695fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2696fa9e4066Sahrens */ 2697fa9e4066Sahrens static vfs_t * 2698fa9e4066Sahrens zfs_get_vfs(const char *resource) 2699fa9e4066Sahrens { 2700fa9e4066Sahrens struct vfs *vfsp; 2701fa9e4066Sahrens struct vfs *vfs_found = NULL; 2702fa9e4066Sahrens 2703fa9e4066Sahrens vfs_list_read_lock(); 2704fa9e4066Sahrens vfsp = rootvfs; 2705fa9e4066Sahrens do { 2706fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2707fa9e4066Sahrens VFS_HOLD(vfsp); 2708fa9e4066Sahrens vfs_found = vfsp; 2709fa9e4066Sahrens break; 2710fa9e4066Sahrens } 2711fa9e4066Sahrens vfsp = vfsp->vfs_next; 2712fa9e4066Sahrens } while (vfsp != rootvfs); 2713fa9e4066Sahrens vfs_list_unlock(); 2714fa9e4066Sahrens return (vfs_found); 2715fa9e4066Sahrens } 2716fa9e4066Sahrens 2717ecd6cf80Smarks /* ARGSUSED */ 2718fa9e4066Sahrens static void 2719ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2720fa9e4066Sahrens { 2721da6c28aaSamw zfs_creat_t *zct = arg; 2722da6c28aaSamw 2723de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2724da6c28aaSamw } 2725da6c28aaSamw 2726de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2727da6c28aaSamw 2728da6c28aaSamw /* 2729de8267e0Stimh * inputs: 27300a48a24eStimh * createprops list of properties requested by creator 27310a48a24eStimh * default_zplver zpl version to use if unspecified in createprops 27320a48a24eStimh * fuids_ok fuids allowed in this version of the spa? 27330a48a24eStimh * os parent objset pointer (NULL if root fs) 2734de8267e0Stimh * 2735de8267e0Stimh * outputs: 2736de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 27370a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 2738da6c28aaSamw * 2739de8267e0Stimh * Determine the settings for utf8only, normalization and 2740de8267e0Stimh * casesensitivity. Specific values may have been requested by the 2741de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 2742de8267e0Stimh * the file system is of too early a vintage, a creator can not 2743de8267e0Stimh * request settings for these properties, even if the requested 2744de8267e0Stimh * setting is the default value. We don't actually want to create dsl 2745de8267e0Stimh * properties for these, so remove them from the source nvlist after 2746de8267e0Stimh * processing. 2747da6c28aaSamw */ 2748da6c28aaSamw static int 274914843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 27500a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 27510a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 2752da6c28aaSamw { 2753de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 2754de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 2755de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 2756da6c28aaSamw 2757de8267e0Stimh ASSERT(zplprops != NULL); 2758da6c28aaSamw 2759de8267e0Stimh /* 2760de8267e0Stimh * Pull out creator prop choices, if any. 2761de8267e0Stimh */ 2762de8267e0Stimh if (createprops) { 27630a48a24eStimh (void) nvlist_lookup_uint64(createprops, 27640a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 2765de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2766de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 2767de8267e0Stimh (void) nvlist_remove_all(createprops, 2768de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 2769de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2770de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 2771de8267e0Stimh (void) nvlist_remove_all(createprops, 2772de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 2773de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2774de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 2775de8267e0Stimh (void) nvlist_remove_all(createprops, 2776de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 2777de8267e0Stimh } 2778da6c28aaSamw 2779c2a93d44Stimh /* 27800a48a24eStimh * If the zpl version requested is whacky or the file system 27810a48a24eStimh * or pool is version is too "young" to support normalization 27820a48a24eStimh * and the creator tried to set a value for one of the props, 27830a48a24eStimh * error out. 2784c2a93d44Stimh */ 27850a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 27860a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 27870a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 27880a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 2789de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 27900a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 2791de8267e0Stimh return (ENOTSUP); 2792c2a93d44Stimh 2793de8267e0Stimh /* 2794de8267e0Stimh * Put the version in the zplprops 2795de8267e0Stimh */ 2796de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2797de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 2798da6c28aaSamw 2799de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 2800de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 2801de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2802de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 2803da6c28aaSamw 2804c2a93d44Stimh /* 2805de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 2806c2a93d44Stimh */ 2807de8267e0Stimh if (norm) 2808de8267e0Stimh u8 = 1; 2809de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 2810de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 2811de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2812de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 2813de8267e0Stimh 2814de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 2815de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 2816de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2817de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 2818c2a93d44Stimh 2819ab04eb8eStimh if (is_ci) 2820ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 2821ab04eb8eStimh 2822da6c28aaSamw return (0); 2823fa9e4066Sahrens } 2824fa9e4066Sahrens 28250a48a24eStimh static int 28260a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 28270a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 28280a48a24eStimh { 28290a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 28300a48a24eStimh uint64_t zplver = ZPL_VERSION; 28310a48a24eStimh objset_t *os = NULL; 28320a48a24eStimh char parentname[MAXNAMELEN]; 28330a48a24eStimh char *cp; 28340a586ceaSMark Shellenbaum spa_t *spa; 28350a586ceaSMark Shellenbaum uint64_t spa_vers; 28360a48a24eStimh int error; 28370a48a24eStimh 28380a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 28390a48a24eStimh cp = strrchr(parentname, '/'); 28400a48a24eStimh ASSERT(cp != NULL); 28410a48a24eStimh cp[0] = '\0'; 28420a48a24eStimh 28430a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 28440a586ceaSMark Shellenbaum return (error); 28450a586ceaSMark Shellenbaum 28460a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 28470a586ceaSMark Shellenbaum spa_close(spa, FTAG); 28480a586ceaSMark Shellenbaum 28490a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 28500a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 28510a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 28520a48a24eStimh 28530a48a24eStimh /* 28540a48a24eStimh * Open parent object set so we can inherit zplprop values. 28550a48a24eStimh */ 2856503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 28570a48a24eStimh return (error); 28580a48a24eStimh 28590a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 28600a48a24eStimh zplprops, is_ci); 2861503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 28620a48a24eStimh return (error); 28630a48a24eStimh } 28640a48a24eStimh 28650a48a24eStimh static int 28660a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 28670a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 28680a48a24eStimh { 28690a586ceaSMark Shellenbaum boolean_t fuids_ok; 28700a586ceaSMark Shellenbaum boolean_t sa_ok; 28710a48a24eStimh uint64_t zplver = ZPL_VERSION; 28720a48a24eStimh int error; 28730a48a24eStimh 28740a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 28750a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 28760a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 28770a48a24eStimh 28780a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 28790a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 28800a48a24eStimh return (error); 28810a48a24eStimh } 28820a48a24eStimh 28833cb34c60Sahrens /* 28843cb34c60Sahrens * inputs: 28853cb34c60Sahrens * zc_objset_type type of objset to create (fs vs zvol) 28863cb34c60Sahrens * zc_name name of new objset 28873cb34c60Sahrens * zc_value name of snapshot to clone from (may be empty) 28883cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 28893cb34c60Sahrens * 2890de8267e0Stimh * outputs: none 28913cb34c60Sahrens */ 2892fa9e4066Sahrens static int 2893fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc) 2894fa9e4066Sahrens { 2895fa9e4066Sahrens objset_t *clone; 2896fa9e4066Sahrens int error = 0; 2897da6c28aaSamw zfs_creat_t zct; 2898ecd6cf80Smarks nvlist_t *nvprops = NULL; 2899ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 2900fa9e4066Sahrens dmu_objset_type_t type = zc->zc_objset_type; 2901fa9e4066Sahrens 2902fa9e4066Sahrens switch (type) { 2903fa9e4066Sahrens 2904fa9e4066Sahrens case DMU_OST_ZFS: 2905fa9e4066Sahrens cbfunc = zfs_create_cb; 2906fa9e4066Sahrens break; 2907fa9e4066Sahrens 2908fa9e4066Sahrens case DMU_OST_ZVOL: 2909fa9e4066Sahrens cbfunc = zvol_create_cb; 2910fa9e4066Sahrens break; 2911fa9e4066Sahrens 2912fa9e4066Sahrens default: 29131d452cf5Sahrens cbfunc = NULL; 2914e7cbe64fSgw break; 2915fa9e4066Sahrens } 2916f18faf3fSek if (strchr(zc->zc_name, '@') || 2917f18faf3fSek strchr(zc->zc_name, '%')) 29181d452cf5Sahrens return (EINVAL); 2919fa9e4066Sahrens 2920e9dbad6fSeschrock if (zc->zc_nvlist_src != NULL && 2921990b4856Slling (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2922478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2923e9dbad6fSeschrock return (error); 2924e9dbad6fSeschrock 2925de8267e0Stimh zct.zct_zplprops = NULL; 2926da6c28aaSamw zct.zct_props = nvprops; 2927da6c28aaSamw 2928e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 2929fa9e4066Sahrens /* 2930fa9e4066Sahrens * We're creating a clone of an existing snapshot. 2931fa9e4066Sahrens */ 2932e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 2933e9dbad6fSeschrock if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) { 2934ecd6cf80Smarks nvlist_free(nvprops); 2935fa9e4066Sahrens return (EINVAL); 2936e9dbad6fSeschrock } 2937fa9e4066Sahrens 2938503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_value, FTAG, &clone); 2939e9dbad6fSeschrock if (error) { 2940ecd6cf80Smarks nvlist_free(nvprops); 2941fa9e4066Sahrens return (error); 2942e9dbad6fSeschrock } 2943ab04eb8eStimh 2944ae46e4c7SMatthew Ahrens error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0); 2945503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 2946da6c28aaSamw if (error) { 2947da6c28aaSamw nvlist_free(nvprops); 2948da6c28aaSamw return (error); 2949da6c28aaSamw } 2950fa9e4066Sahrens } else { 2951ab04eb8eStimh boolean_t is_insensitive = B_FALSE; 2952ab04eb8eStimh 2953e9dbad6fSeschrock if (cbfunc == NULL) { 2954ecd6cf80Smarks nvlist_free(nvprops); 29551d452cf5Sahrens return (EINVAL); 2956e9dbad6fSeschrock } 29575c5460e9Seschrock 2958e9dbad6fSeschrock if (type == DMU_OST_ZVOL) { 2959e9dbad6fSeschrock uint64_t volsize, volblocksize; 2960e9dbad6fSeschrock 2961ecd6cf80Smarks if (nvprops == NULL || 2962ecd6cf80Smarks nvlist_lookup_uint64(nvprops, 2963e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 2964e9dbad6fSeschrock &volsize) != 0) { 2965ecd6cf80Smarks nvlist_free(nvprops); 2966e9dbad6fSeschrock return (EINVAL); 2967e9dbad6fSeschrock } 2968e9dbad6fSeschrock 2969ecd6cf80Smarks if ((error = nvlist_lookup_uint64(nvprops, 2970e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2971e9dbad6fSeschrock &volblocksize)) != 0 && error != ENOENT) { 2972ecd6cf80Smarks nvlist_free(nvprops); 2973e9dbad6fSeschrock return (EINVAL); 2974e9dbad6fSeschrock } 2975e9dbad6fSeschrock 2976e9dbad6fSeschrock if (error != 0) 2977e9dbad6fSeschrock volblocksize = zfs_prop_default_numeric( 2978e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2979e9dbad6fSeschrock 2980e9dbad6fSeschrock if ((error = zvol_check_volblocksize( 2981e9dbad6fSeschrock volblocksize)) != 0 || 2982e9dbad6fSeschrock (error = zvol_check_volsize(volsize, 2983e9dbad6fSeschrock volblocksize)) != 0) { 2984ecd6cf80Smarks nvlist_free(nvprops); 29855c5460e9Seschrock return (error); 2986e9dbad6fSeschrock } 2987e7437265Sahrens } else if (type == DMU_OST_ZFS) { 2988da6c28aaSamw int error; 2989da6c28aaSamw 2990da6c28aaSamw /* 2991da6c28aaSamw * We have to have normalization and 2992da6c28aaSamw * case-folding flags correct when we do the 2993da6c28aaSamw * file system creation, so go figure them out 2994de8267e0Stimh * now. 2995da6c28aaSamw */ 2996de8267e0Stimh VERIFY(nvlist_alloc(&zct.zct_zplprops, 2997de8267e0Stimh NV_UNIQUE_NAME, KM_SLEEP) == 0); 2998de8267e0Stimh error = zfs_fill_zplprops(zc->zc_name, nvprops, 29990a48a24eStimh zct.zct_zplprops, &is_insensitive); 3000da6c28aaSamw if (error != 0) { 3001da6c28aaSamw nvlist_free(nvprops); 3002de8267e0Stimh nvlist_free(zct.zct_zplprops); 3003da6c28aaSamw return (error); 3004da6c28aaSamw } 3005da6c28aaSamw } 3006ae46e4c7SMatthew Ahrens error = dmu_objset_create(zc->zc_name, type, 3007ab04eb8eStimh is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 3008de8267e0Stimh nvlist_free(zct.zct_zplprops); 3009fa9e4066Sahrens } 3010e9dbad6fSeschrock 3011e9dbad6fSeschrock /* 3012e9dbad6fSeschrock * It would be nice to do this atomically. 3013e9dbad6fSeschrock */ 3014e9dbad6fSeschrock if (error == 0) { 301592241e0bSTom Erickson error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL, 301692241e0bSTom Erickson nvprops, NULL); 301792241e0bSTom Erickson if (error != 0) 3018842727c2SChris Kirby (void) dmu_objset_destroy(zc->zc_name, B_FALSE); 3019e9dbad6fSeschrock } 3020ecd6cf80Smarks nvlist_free(nvprops); 3021fa9e4066Sahrens return (error); 3022fa9e4066Sahrens } 3023fa9e4066Sahrens 30243cb34c60Sahrens /* 30253cb34c60Sahrens * inputs: 30263cb34c60Sahrens * zc_name name of filesystem 30273cb34c60Sahrens * zc_value short name of snapshot 30283cb34c60Sahrens * zc_cookie recursive flag 302914843421SMatthew Ahrens * zc_nvlist_src[_size] property list 30303cb34c60Sahrens * 3031681d9761SEric Taylor * outputs: 3032681d9761SEric Taylor * zc_value short snapname (i.e. part after the '@') 30333cb34c60Sahrens */ 3034fa9e4066Sahrens static int 30351d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc) 3036fa9e4066Sahrens { 3037bb0ade09Sahrens nvlist_t *nvprops = NULL; 3038bb0ade09Sahrens int error; 3039bb0ade09Sahrens boolean_t recursive = zc->zc_cookie; 3040bb0ade09Sahrens 3041e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 30421d452cf5Sahrens return (EINVAL); 3043bb0ade09Sahrens 3044bb0ade09Sahrens if (zc->zc_nvlist_src != NULL && 3045bb0ade09Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 3046478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 3047bb0ade09Sahrens return (error); 3048bb0ade09Sahrens 3049ea2f5b9eSMatthew Ahrens error = zfs_check_userprops(zc->zc_name, nvprops); 3050ea2f5b9eSMatthew Ahrens if (error) 3051ea2f5b9eSMatthew Ahrens goto out; 3052bb0ade09Sahrens 305392241e0bSTom Erickson if (!nvlist_empty(nvprops) && 3054ea2f5b9eSMatthew Ahrens zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) { 3055ea2f5b9eSMatthew Ahrens error = ENOTSUP; 3056ea2f5b9eSMatthew Ahrens goto out; 3057bb0ade09Sahrens } 3058ea2f5b9eSMatthew Ahrens 305999d5e173STim Haley error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, NULL, 306099d5e173STim Haley nvprops, recursive, B_FALSE, -1); 3061ea2f5b9eSMatthew Ahrens 3062ea2f5b9eSMatthew Ahrens out: 3063bb0ade09Sahrens nvlist_free(nvprops); 3064bb0ade09Sahrens return (error); 30651d452cf5Sahrens } 3066fa9e4066Sahrens 3067cdf5b4caSmmusante int 3068fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg) 30691d452cf5Sahrens { 30700b69c2f0Sahrens vfs_t *vfsp = NULL; 30711d452cf5Sahrens 3072745cd3c5Smaybee if (arg) { 3073745cd3c5Smaybee char *snapname = arg; 3074fd136879SMatthew Ahrens char *fullname = kmem_asprintf("%s@%s", name, snapname); 3075fd136879SMatthew Ahrens vfsp = zfs_get_vfs(fullname); 3076fd136879SMatthew Ahrens strfree(fullname); 30770b69c2f0Sahrens } else if (strchr(name, '@')) { 30781d452cf5Sahrens vfsp = zfs_get_vfs(name); 30791d452cf5Sahrens } 30801d452cf5Sahrens 30811d452cf5Sahrens if (vfsp) { 3082fa9e4066Sahrens /* 30831d452cf5Sahrens * Always force the unmount for snapshots. 3084fa9e4066Sahrens */ 30851d452cf5Sahrens int flag = MS_FORCE; 30861d452cf5Sahrens int err; 30871d452cf5Sahrens 30881d452cf5Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 3089fa9e4066Sahrens VFS_RELE(vfsp); 30901d452cf5Sahrens return (err); 3091fa9e4066Sahrens } 30921d452cf5Sahrens VFS_RELE(vfsp); 30931d452cf5Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0) 30941d452cf5Sahrens return (err); 30951d452cf5Sahrens } 30961d452cf5Sahrens return (0); 30971d452cf5Sahrens } 30981d452cf5Sahrens 30993cb34c60Sahrens /* 31003cb34c60Sahrens * inputs: 3101842727c2SChris Kirby * zc_name name of filesystem 3102842727c2SChris Kirby * zc_value short name of snapshot 3103842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 31043cb34c60Sahrens * 31053cb34c60Sahrens * outputs: none 31063cb34c60Sahrens */ 31071d452cf5Sahrens static int 31081d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc) 31091d452cf5Sahrens { 31101d452cf5Sahrens int err; 31111d452cf5Sahrens 3112e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 31131d452cf5Sahrens return (EINVAL); 31141d452cf5Sahrens err = dmu_objset_find(zc->zc_name, 3115e9dbad6fSeschrock zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); 31161d452cf5Sahrens if (err) 31171d452cf5Sahrens return (err); 3118842727c2SChris Kirby return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value, 3119842727c2SChris Kirby zc->zc_defer_destroy)); 31201d452cf5Sahrens } 31211d452cf5Sahrens 31223cb34c60Sahrens /* 31233cb34c60Sahrens * inputs: 31243cb34c60Sahrens * zc_name name of dataset to destroy 31253cb34c60Sahrens * zc_objset_type type of objset 3126842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 31273cb34c60Sahrens * 31283cb34c60Sahrens * outputs: none 31293cb34c60Sahrens */ 31301d452cf5Sahrens static int 31311d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 31321d452cf5Sahrens { 3133681d9761SEric Taylor int err; 31341d452cf5Sahrens if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) { 3135681d9761SEric Taylor err = zfs_unmount_snap(zc->zc_name, NULL); 31361d452cf5Sahrens if (err) 31371d452cf5Sahrens return (err); 3138fa9e4066Sahrens } 3139fa9e4066Sahrens 3140681d9761SEric Taylor err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy); 3141681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 31425c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 3143681d9761SEric Taylor return (err); 3144fa9e4066Sahrens } 3145fa9e4066Sahrens 31463cb34c60Sahrens /* 31473cb34c60Sahrens * inputs: 31484ccbb6e7Sahrens * zc_name name of dataset to rollback (to most recent snapshot) 31493cb34c60Sahrens * 31503cb34c60Sahrens * outputs: none 31513cb34c60Sahrens */ 3152fa9e4066Sahrens static int 3153fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc) 3154fa9e4066Sahrens { 3155ae46e4c7SMatthew Ahrens dsl_dataset_t *ds, *clone; 31564ccbb6e7Sahrens int error; 3157ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 3158ae46e4c7SMatthew Ahrens char *clone_name; 3159ae46e4c7SMatthew Ahrens 3160ae46e4c7SMatthew Ahrens error = dsl_dataset_hold(zc->zc_name, FTAG, &ds); 3161ae46e4c7SMatthew Ahrens if (error) 3162ae46e4c7SMatthew Ahrens return (error); 3163ae46e4c7SMatthew Ahrens 3164ae46e4c7SMatthew Ahrens /* must not be a snapshot */ 3165ae46e4c7SMatthew Ahrens if (dsl_dataset_is_snapshot(ds)) { 3166ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 3167ae46e4c7SMatthew Ahrens return (EINVAL); 3168ae46e4c7SMatthew Ahrens } 3169ae46e4c7SMatthew Ahrens 3170ae46e4c7SMatthew Ahrens /* must have a most recent snapshot */ 3171ae46e4c7SMatthew Ahrens if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) { 3172ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 3173ae46e4c7SMatthew Ahrens return (EINVAL); 3174ae46e4c7SMatthew Ahrens } 31754ccbb6e7Sahrens 31764ccbb6e7Sahrens /* 3177ae46e4c7SMatthew Ahrens * Create clone of most recent snapshot. 31784ccbb6e7Sahrens */ 3179ae46e4c7SMatthew Ahrens clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name); 3180ae46e4c7SMatthew Ahrens error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT); 31814ccbb6e7Sahrens if (error) 3182ae46e4c7SMatthew Ahrens goto out; 31834ccbb6e7Sahrens 3184503ad85cSMatthew Ahrens error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone); 3185ae46e4c7SMatthew Ahrens if (error) 3186ae46e4c7SMatthew Ahrens goto out; 3187ae46e4c7SMatthew Ahrens 3188ae46e4c7SMatthew Ahrens /* 3189ae46e4c7SMatthew Ahrens * Do clone swap. 3190ae46e4c7SMatthew Ahrens */ 319114843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 3192503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 319347f263f4Sek if (error == 0) { 319447f263f4Sek int resume_err; 31954ccbb6e7Sahrens 3196ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 3197ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, 3198ae46e4c7SMatthew Ahrens B_TRUE); 3199ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 3200ae46e4c7SMatthew Ahrens ds = NULL; 3201ae46e4c7SMatthew Ahrens } else { 3202ae46e4c7SMatthew Ahrens error = EBUSY; 3203ae46e4c7SMatthew Ahrens } 3204503ad85cSMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, zc->zc_name); 320547f263f4Sek error = error ? error : resume_err; 320647f263f4Sek } 32074ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 32084ccbb6e7Sahrens } else { 3209ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 3210ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, B_TRUE); 3211ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 3212ae46e4c7SMatthew Ahrens ds = NULL; 3213ae46e4c7SMatthew Ahrens } else { 3214ae46e4c7SMatthew Ahrens error = EBUSY; 3215ae46e4c7SMatthew Ahrens } 32164ccbb6e7Sahrens } 32174ccbb6e7Sahrens 3218ae46e4c7SMatthew Ahrens /* 3219ae46e4c7SMatthew Ahrens * Destroy clone (which also closes it). 3220ae46e4c7SMatthew Ahrens */ 3221ae46e4c7SMatthew Ahrens (void) dsl_dataset_destroy(clone, FTAG, B_FALSE); 3222ae46e4c7SMatthew Ahrens 3223ae46e4c7SMatthew Ahrens out: 3224ae46e4c7SMatthew Ahrens strfree(clone_name); 3225ae46e4c7SMatthew Ahrens if (ds) 3226ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 32274ccbb6e7Sahrens return (error); 3228fa9e4066Sahrens } 3229fa9e4066Sahrens 32303cb34c60Sahrens /* 32313cb34c60Sahrens * inputs: 32323cb34c60Sahrens * zc_name old name of dataset 32333cb34c60Sahrens * zc_value new name of dataset 32343cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 32353cb34c60Sahrens * 32363cb34c60Sahrens * outputs: none 32373cb34c60Sahrens */ 3238fa9e4066Sahrens static int 3239fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3240fa9e4066Sahrens { 32417f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 3242cdf5b4caSmmusante 3243e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3244f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3245f18faf3fSek strchr(zc->zc_value, '%')) 3246fa9e4066Sahrens return (EINVAL); 3247fa9e4066Sahrens 3248cdf5b4caSmmusante /* 3249cdf5b4caSmmusante * Unmount snapshot unless we're doing a recursive rename, 3250cdf5b4caSmmusante * in which case the dataset code figures out which snapshots 3251cdf5b4caSmmusante * to unmount. 3252cdf5b4caSmmusante */ 3253cdf5b4caSmmusante if (!recursive && strchr(zc->zc_name, '@') != NULL && 3254fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 32551d452cf5Sahrens int err = zfs_unmount_snap(zc->zc_name, NULL); 32561d452cf5Sahrens if (err) 32571d452cf5Sahrens return (err); 3258fa9e4066Sahrens } 3259681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL) 3260681d9761SEric Taylor (void) zvol_remove_minor(zc->zc_name); 3261cdf5b4caSmmusante return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); 3262fa9e4066Sahrens } 3263fa9e4066Sahrens 326492241e0bSTom Erickson static int 326592241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 326692241e0bSTom Erickson { 326792241e0bSTom Erickson const char *propname = nvpair_name(pair); 326892241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 326992241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 327092241e0bSTom Erickson uint64_t intval; 327192241e0bSTom Erickson int err; 327292241e0bSTom Erickson 327392241e0bSTom Erickson if (prop == ZPROP_INVAL) { 327492241e0bSTom Erickson if (zfs_prop_user(propname)) { 327592241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 327692241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 327792241e0bSTom Erickson return (err); 327892241e0bSTom Erickson return (0); 327992241e0bSTom Erickson } 328092241e0bSTom Erickson 328192241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 328292241e0bSTom Erickson const char *perm = NULL; 328392241e0bSTom Erickson const char *uq_prefix = 328492241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 328592241e0bSTom Erickson const char *gq_prefix = 328692241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 328792241e0bSTom Erickson 328892241e0bSTom Erickson if (strncmp(propname, uq_prefix, 328992241e0bSTom Erickson strlen(uq_prefix)) == 0) { 329092241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 329192241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 329292241e0bSTom Erickson strlen(gq_prefix)) == 0) { 329392241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 329492241e0bSTom Erickson } else { 329592241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 329692241e0bSTom Erickson return (EINVAL); 329792241e0bSTom Erickson } 329892241e0bSTom Erickson 329992241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 330092241e0bSTom Erickson return (err); 330192241e0bSTom Erickson return (0); 330292241e0bSTom Erickson } 330392241e0bSTom Erickson 330492241e0bSTom Erickson return (EINVAL); 330592241e0bSTom Erickson } 330692241e0bSTom Erickson 330792241e0bSTom Erickson if (issnap) 330892241e0bSTom Erickson return (EINVAL); 330992241e0bSTom Erickson 331092241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 331192241e0bSTom Erickson /* 331292241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 331392241e0bSTom Erickson * format. 331492241e0bSTom Erickson */ 331592241e0bSTom Erickson nvlist_t *attrs; 331692241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 331792241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 331892241e0bSTom Erickson &pair) == 0); 331992241e0bSTom Erickson } 332092241e0bSTom Erickson 332192241e0bSTom Erickson /* 332292241e0bSTom Erickson * Check that this value is valid for this pool version 332392241e0bSTom Erickson */ 332492241e0bSTom Erickson switch (prop) { 332592241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 332692241e0bSTom Erickson /* 332792241e0bSTom Erickson * If the user specified gzip compression, make sure 332892241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 332992241e0bSTom Erickson * we'll catch them later. 333092241e0bSTom Erickson */ 333192241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 333292241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 333392241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 333492241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 333592241e0bSTom Erickson zfs_earlier_version(dsname, 333692241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 333792241e0bSTom Erickson return (ENOTSUP); 333892241e0bSTom Erickson } 333992241e0bSTom Erickson 334092241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 334192241e0bSTom Erickson zfs_earlier_version(dsname, 334292241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 334392241e0bSTom Erickson return (ENOTSUP); 334492241e0bSTom Erickson 334592241e0bSTom Erickson /* 334692241e0bSTom Erickson * If this is a bootable dataset then 334792241e0bSTom Erickson * verify that the compression algorithm 334892241e0bSTom Erickson * is supported for booting. We must return 334992241e0bSTom Erickson * something other than ENOTSUP since it 335092241e0bSTom Erickson * implies a downrev pool version. 335192241e0bSTom Erickson */ 335292241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 335392241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 335492241e0bSTom Erickson return (ERANGE); 335592241e0bSTom Erickson } 335692241e0bSTom Erickson } 335792241e0bSTom Erickson break; 335892241e0bSTom Erickson 335992241e0bSTom Erickson case ZFS_PROP_COPIES: 336092241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 336192241e0bSTom Erickson return (ENOTSUP); 336292241e0bSTom Erickson break; 336392241e0bSTom Erickson 336492241e0bSTom Erickson case ZFS_PROP_DEDUP: 336592241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 336692241e0bSTom Erickson return (ENOTSUP); 336792241e0bSTom Erickson break; 336892241e0bSTom Erickson 336992241e0bSTom Erickson case ZFS_PROP_SHARESMB: 337092241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 337192241e0bSTom Erickson return (ENOTSUP); 337292241e0bSTom Erickson break; 337392241e0bSTom Erickson 337492241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 337592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 337692241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 337792241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 337892241e0bSTom Erickson zfs_earlier_version(dsname, 337992241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 338092241e0bSTom Erickson return (ENOTSUP); 338192241e0bSTom Erickson } 338292241e0bSTom Erickson break; 338392241e0bSTom Erickson } 338492241e0bSTom Erickson 338592241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 338692241e0bSTom Erickson } 338792241e0bSTom Erickson 338892241e0bSTom Erickson /* 338992241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 339092241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 339192241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 339292241e0bSTom Erickson * 339392241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 339492241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 339592241e0bSTom Erickson * of all the properties that failed a permission check along with the 339692241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 339792241e0bSTom Erickson * returned errlist. 339892241e0bSTom Erickson * 339992241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 340092241e0bSTom Erickson * pointed at by errlist is NULL. 340192241e0bSTom Erickson */ 340292241e0bSTom Erickson static int 340392241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 3404745cd3c5Smaybee { 3405745cd3c5Smaybee zfs_cmd_t *zc; 340692241e0bSTom Erickson nvpair_t *pair, *next_pair; 340792241e0bSTom Erickson nvlist_t *errors; 340892241e0bSTom Erickson int err, rv = 0; 3409745cd3c5Smaybee 3410745cd3c5Smaybee if (props == NULL) 341192241e0bSTom Erickson return (0); 341292241e0bSTom Erickson 341392241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 341492241e0bSTom Erickson 3415745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 3416745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 341792241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 341892241e0bSTom Erickson while (pair != NULL) { 341992241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 342092241e0bSTom Erickson 342192241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 342292241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 342392241e0bSTom Erickson (err = zfs_secpolicy_inherit(zc, CRED())) != 0) { 342492241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 342592241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 342692241e0bSTom Erickson zc->zc_value, err) == 0); 342792241e0bSTom Erickson } 342892241e0bSTom Erickson pair = next_pair; 3429745cd3c5Smaybee } 3430745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 343192241e0bSTom Erickson 343292241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 343392241e0bSTom Erickson nvlist_free(errors); 343492241e0bSTom Erickson errors = NULL; 343592241e0bSTom Erickson } else { 343692241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 343792241e0bSTom Erickson } 343892241e0bSTom Erickson 343992241e0bSTom Erickson if (errlist == NULL) 344092241e0bSTom Erickson nvlist_free(errors); 344192241e0bSTom Erickson else 344292241e0bSTom Erickson *errlist = errors; 344392241e0bSTom Erickson 344492241e0bSTom Erickson return (rv); 344592241e0bSTom Erickson } 344692241e0bSTom Erickson 344792241e0bSTom Erickson static boolean_t 344892241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 344992241e0bSTom Erickson { 345092241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 345192241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 345292241e0bSTom Erickson nvlist_t *attrs; 345392241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 345492241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 345592241e0bSTom Erickson &p1) == 0); 345692241e0bSTom Erickson } 345792241e0bSTom Erickson 345892241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 345992241e0bSTom Erickson nvlist_t *attrs; 346092241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 346192241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 346292241e0bSTom Erickson &p2) == 0); 346392241e0bSTom Erickson } 346492241e0bSTom Erickson 346592241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 346692241e0bSTom Erickson return (B_FALSE); 346792241e0bSTom Erickson 346892241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 346992241e0bSTom Erickson char *valstr1, *valstr2; 347092241e0bSTom Erickson 347192241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 347292241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 347392241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 347492241e0bSTom Erickson } else { 347592241e0bSTom Erickson uint64_t intval1, intval2; 347692241e0bSTom Erickson 347792241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 347892241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 347992241e0bSTom Erickson return (intval1 == intval2); 348092241e0bSTom Erickson } 3481745cd3c5Smaybee } 3482745cd3c5Smaybee 348392241e0bSTom Erickson /* 348492241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 348592241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 348692241e0bSTom Erickson * do not need to clear or restore properties that won't change. 348792241e0bSTom Erickson */ 348892241e0bSTom Erickson static void 348992241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 349092241e0bSTom Erickson { 349192241e0bSTom Erickson nvpair_t *pair, *next_pair; 349292241e0bSTom Erickson 349392241e0bSTom Erickson if (origprops == NULL) 349492241e0bSTom Erickson return; /* all props need to be received */ 349592241e0bSTom Erickson 349692241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 349792241e0bSTom Erickson while (pair != NULL) { 349892241e0bSTom Erickson const char *propname = nvpair_name(pair); 349992241e0bSTom Erickson nvpair_t *match; 350092241e0bSTom Erickson 350192241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 350292241e0bSTom Erickson 350392241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 350492241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 350592241e0bSTom Erickson goto next; /* need to set received value */ 350692241e0bSTom Erickson 350792241e0bSTom Erickson /* don't clear the existing received value */ 350892241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 350992241e0bSTom Erickson /* don't bother receiving the property */ 351092241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 351192241e0bSTom Erickson next: 351292241e0bSTom Erickson pair = next_pair; 351392241e0bSTom Erickson } 351492241e0bSTom Erickson } 351592241e0bSTom Erickson 351692241e0bSTom Erickson #ifdef DEBUG 351792241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 351892241e0bSTom Erickson #endif 351992241e0bSTom Erickson 35203cb34c60Sahrens /* 35213cb34c60Sahrens * inputs: 35223cb34c60Sahrens * zc_name name of containing filesystem 35233cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 35243cb34c60Sahrens * zc_value name of snapshot to create 35253cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 35263cb34c60Sahrens * zc_cookie file descriptor to recv from 35273cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 35283cb34c60Sahrens * zc_guid force flag 3529c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor 3530c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping (or zero on first call) 35313cb34c60Sahrens * 35323cb34c60Sahrens * outputs: 35333cb34c60Sahrens * zc_cookie number of bytes read 353492241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 353592241e0bSTom Erickson * zc_obj zprop_errflags_t 3536c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping 35373cb34c60Sahrens */ 3538fa9e4066Sahrens static int 35393cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 3540fa9e4066Sahrens { 3541fa9e4066Sahrens file_t *fp; 3542f18faf3fSek objset_t *os; 35433cb34c60Sahrens dmu_recv_cookie_t drc; 3544f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 354592241e0bSTom Erickson int fd; 354692241e0bSTom Erickson int error = 0; 354792241e0bSTom Erickson int props_error = 0; 354892241e0bSTom Erickson nvlist_t *errors; 35493cb34c60Sahrens offset_t off; 355092241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 355192241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 35523cb34c60Sahrens objset_t *origin = NULL; 35533cb34c60Sahrens char *tosnap; 35543cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 355592241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 3556fa9e4066Sahrens 35573ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3558f18faf3fSek strchr(zc->zc_value, '@') == NULL || 3559f18faf3fSek strchr(zc->zc_value, '%')) 35603ccfa83cSahrens return (EINVAL); 35613ccfa83cSahrens 35623cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 35633cb34c60Sahrens tosnap = strchr(tofs, '@'); 356492241e0bSTom Erickson *tosnap++ = '\0'; 35653cb34c60Sahrens 35663cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 35673cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 3568478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 35693cb34c60Sahrens return (error); 35703cb34c60Sahrens 3571fa9e4066Sahrens fd = zc->zc_cookie; 3572fa9e4066Sahrens fp = getf(fd); 35733cb34c60Sahrens if (fp == NULL) { 35743cb34c60Sahrens nvlist_free(props); 3575fa9e4066Sahrens return (EBADF); 35763cb34c60Sahrens } 3577f18faf3fSek 357892241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 357992241e0bSTom Erickson 3580503ad85cSMatthew Ahrens if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) { 358192241e0bSTom Erickson if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) && 358292241e0bSTom Erickson !dsl_prop_get_hasrecvd(os)) { 358392241e0bSTom Erickson first_recvd_props = B_TRUE; 358492241e0bSTom Erickson } 358592241e0bSTom Erickson 3586745cd3c5Smaybee /* 358792241e0bSTom Erickson * If new received properties are supplied, they are to 358892241e0bSTom Erickson * completely replace the existing received properties, so stash 358992241e0bSTom Erickson * away the existing ones. 3590745cd3c5Smaybee */ 359192241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 359292241e0bSTom Erickson nvlist_t *errlist = NULL; 359392241e0bSTom Erickson /* 359492241e0bSTom Erickson * Don't bother writing a property if its value won't 359592241e0bSTom Erickson * change (and avoid the unnecessary security checks). 359692241e0bSTom Erickson * 359792241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 359892241e0bSTom Erickson * special case where we blow away all local properties 359992241e0bSTom Erickson * regardless. 360092241e0bSTom Erickson */ 360192241e0bSTom Erickson if (!first_recvd_props) 360292241e0bSTom Erickson props_reduce(props, origprops); 360392241e0bSTom Erickson if (zfs_check_clearable(tofs, origprops, 360492241e0bSTom Erickson &errlist) != 0) 360592241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 360692241e0bSTom Erickson nvlist_free(errlist); 360792241e0bSTom Erickson } 3608745cd3c5Smaybee 3609503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3610f18faf3fSek } 3611f18faf3fSek 36123cb34c60Sahrens if (zc->zc_string[0]) { 3613503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_string, FTAG, &origin); 3614745cd3c5Smaybee if (error) 3615745cd3c5Smaybee goto out; 36163cb34c60Sahrens } 36173cb34c60Sahrens 36189e69d7d0SLori Alt error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds, 36199e69d7d0SLori Alt &zc->zc_begin_record, force, origin, &drc); 36203cb34c60Sahrens if (origin) 3621503ad85cSMatthew Ahrens dmu_objset_rele(origin, FTAG); 3622745cd3c5Smaybee if (error) 3623745cd3c5Smaybee goto out; 3624f18faf3fSek 3625f18faf3fSek /* 362692241e0bSTom Erickson * Set properties before we receive the stream so that they are applied 362792241e0bSTom Erickson * to the new data. Note that we must call dmu_recv_stream() if 362892241e0bSTom Erickson * dmu_recv_begin() succeeds. 3629f18faf3fSek */ 36303cb34c60Sahrens if (props) { 363192241e0bSTom Erickson nvlist_t *errlist; 363292241e0bSTom Erickson 363392241e0bSTom Erickson if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) { 363492241e0bSTom Erickson if (drc.drc_newfs) { 363592241e0bSTom Erickson if (spa_version(os->os_spa) >= 363692241e0bSTom Erickson SPA_VERSION_RECVD_PROPS) 363792241e0bSTom Erickson first_recvd_props = B_TRUE; 363892241e0bSTom Erickson } else if (origprops != NULL) { 363992241e0bSTom Erickson if (clear_received_props(os, tofs, origprops, 364092241e0bSTom Erickson first_recvd_props ? NULL : props) != 0) 364192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 364292241e0bSTom Erickson } else { 364392241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 364492241e0bSTom Erickson } 364592241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 364692241e0bSTom Erickson } else if (!drc.drc_newfs) { 364792241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 364892241e0bSTom Erickson } 364992241e0bSTom Erickson 365092241e0bSTom Erickson (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 365192241e0bSTom Erickson props, &errlist); 365292241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 365392241e0bSTom Erickson nvlist_free(errlist); 365492241e0bSTom Erickson } 365592241e0bSTom Erickson 365692241e0bSTom Erickson if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) { 3657745cd3c5Smaybee /* 365892241e0bSTom Erickson * Caller made zc->zc_nvlist_dst less than the minimum expected 365992241e0bSTom Erickson * size or supplied an invalid address. 3660745cd3c5Smaybee */ 366192241e0bSTom Erickson props_error = EINVAL; 36623cb34c60Sahrens } 36633cb34c60Sahrens 36643cb34c60Sahrens off = fp->f_offset; 3665c99e4bdcSChris Kirby error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, 3666c99e4bdcSChris Kirby &zc->zc_action_handle); 3667a2eea2e1Sahrens 3668f4b94bdeSMatthew Ahrens if (error == 0) { 3669f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 3670745cd3c5Smaybee 3671f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 3672f4b94bdeSMatthew Ahrens /* online recv */ 3673f4b94bdeSMatthew Ahrens int end_err; 3674745cd3c5Smaybee 3675503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3676f4b94bdeSMatthew Ahrens /* 3677f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 3678f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 3679f4b94bdeSMatthew Ahrens */ 3680f4b94bdeSMatthew Ahrens end_err = dmu_recv_end(&drc); 36815c703fceSGeorge Wilson if (error == 0) 36825c703fceSGeorge Wilson error = zfs_resume_fs(zfsvfs, tofs); 3683f4b94bdeSMatthew Ahrens error = error ? error : end_err; 3684f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 3685745cd3c5Smaybee } else { 3686f4b94bdeSMatthew Ahrens error = dmu_recv_end(&drc); 36873cb34c60Sahrens } 368847f263f4Sek } 36893cb34c60Sahrens 36903cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 36913cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 36923cb34c60Sahrens fp->f_offset = off; 3693a2eea2e1Sahrens 369492241e0bSTom Erickson #ifdef DEBUG 369592241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 369692241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 369792241e0bSTom Erickson error = 1; 369892241e0bSTom Erickson } 369992241e0bSTom Erickson #endif 3700745cd3c5Smaybee /* 3701745cd3c5Smaybee * On error, restore the original props. 3702745cd3c5Smaybee */ 3703745cd3c5Smaybee if (error && props) { 370492241e0bSTom Erickson if (dmu_objset_hold(tofs, FTAG, &os) == 0) { 370592241e0bSTom Erickson if (clear_received_props(os, tofs, props, NULL) != 0) { 370692241e0bSTom Erickson /* 370792241e0bSTom Erickson * We failed to clear the received properties. 370892241e0bSTom Erickson * Since we may have left a $recvd value on the 370992241e0bSTom Erickson * system, we can't clear the $hasrecvd flag. 371092241e0bSTom Erickson */ 371192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 371292241e0bSTom Erickson } else if (first_recvd_props) { 371392241e0bSTom Erickson dsl_prop_unset_hasrecvd(os); 371492241e0bSTom Erickson } 371592241e0bSTom Erickson dmu_objset_rele(os, FTAG); 371692241e0bSTom Erickson } else if (!drc.drc_newfs) { 371792241e0bSTom Erickson /* We failed to clear the received properties. */ 371892241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 371992241e0bSTom Erickson } 372092241e0bSTom Erickson 372192241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 372292241e0bSTom Erickson /* We failed to stash the original properties. */ 372392241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 372492241e0bSTom Erickson } 372592241e0bSTom Erickson 372692241e0bSTom Erickson /* 372792241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 372892241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 372992241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 373092241e0bSTom Erickson * first new-style receive. 373192241e0bSTom Erickson */ 373292241e0bSTom Erickson if (origprops != NULL && 373392241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 373492241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 373592241e0bSTom Erickson origprops, NULL) != 0) { 373692241e0bSTom Erickson /* 373792241e0bSTom Erickson * We stashed the original properties but failed to 373892241e0bSTom Erickson * restore them. 373992241e0bSTom Erickson */ 374092241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 374192241e0bSTom Erickson } 3742745cd3c5Smaybee } 3743745cd3c5Smaybee out: 3744745cd3c5Smaybee nvlist_free(props); 3745745cd3c5Smaybee nvlist_free(origprops); 374692241e0bSTom Erickson nvlist_free(errors); 3747fa9e4066Sahrens releasef(fd); 374892241e0bSTom Erickson 374992241e0bSTom Erickson if (error == 0) 375092241e0bSTom Erickson error = props_error; 375192241e0bSTom Erickson 3752fa9e4066Sahrens return (error); 3753fa9e4066Sahrens } 3754fa9e4066Sahrens 37553cb34c60Sahrens /* 37563cb34c60Sahrens * inputs: 37573cb34c60Sahrens * zc_name name of snapshot to send 37583cb34c60Sahrens * zc_cookie file descriptor to send stream to 3759a7f53a56SChris Kirby * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) 3760a7f53a56SChris Kirby * zc_sendobj objsetid of snapshot to send 3761a7f53a56SChris Kirby * zc_fromobj objsetid of incremental fromsnap (may be zero) 37623cb34c60Sahrens * 37633cb34c60Sahrens * outputs: none 37643cb34c60Sahrens */ 3765fa9e4066Sahrens static int 37663cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 3767fa9e4066Sahrens { 3768fa9e4066Sahrens objset_t *fromsnap = NULL; 3769fa9e4066Sahrens objset_t *tosnap; 3770fa9e4066Sahrens file_t *fp; 3771fa9e4066Sahrens int error; 37723cb34c60Sahrens offset_t off; 3773a7f53a56SChris Kirby dsl_dataset_t *ds; 3774a7f53a56SChris Kirby dsl_dataset_t *dsfrom = NULL; 3775a7f53a56SChris Kirby spa_t *spa; 3776a7f53a56SChris Kirby dsl_pool_t *dp; 3777fa9e4066Sahrens 3778a7f53a56SChris Kirby error = spa_open(zc->zc_name, &spa, FTAG); 3779fa9e4066Sahrens if (error) 3780fa9e4066Sahrens return (error); 3781fa9e4066Sahrens 3782a7f53a56SChris Kirby dp = spa_get_dsl(spa); 3783a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 3784a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 3785a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 3786a7f53a56SChris Kirby if (error) { 3787a7f53a56SChris Kirby spa_close(spa, FTAG); 3788a7f53a56SChris Kirby return (error); 3789a7f53a56SChris Kirby } 3790a7f53a56SChris Kirby 3791a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &tosnap); 3792a7f53a56SChris Kirby if (error) { 3793a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 3794a7f53a56SChris Kirby spa_close(spa, FTAG); 3795a7f53a56SChris Kirby return (error); 3796a7f53a56SChris Kirby } 3797a7f53a56SChris Kirby 3798a7f53a56SChris Kirby if (zc->zc_fromobj != 0) { 3799a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 3800a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &dsfrom); 3801a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 3802a7f53a56SChris Kirby spa_close(spa, FTAG); 3803a7f53a56SChris Kirby if (error) { 3804a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 3805a7f53a56SChris Kirby return (error); 3806a7f53a56SChris Kirby } 3807a7f53a56SChris Kirby error = dmu_objset_from_ds(dsfrom, &fromsnap); 3808fa9e4066Sahrens if (error) { 3809a7f53a56SChris Kirby dsl_dataset_rele(dsfrom, FTAG); 3810a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 3811fa9e4066Sahrens return (error); 3812fa9e4066Sahrens } 3813a7f53a56SChris Kirby } else { 3814a7f53a56SChris Kirby spa_close(spa, FTAG); 3815fa9e4066Sahrens } 3816fa9e4066Sahrens 3817fa9e4066Sahrens fp = getf(zc->zc_cookie); 3818fa9e4066Sahrens if (fp == NULL) { 3819a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 3820a7f53a56SChris Kirby if (dsfrom) 3821a7f53a56SChris Kirby dsl_dataset_rele(dsfrom, FTAG); 3822fa9e4066Sahrens return (EBADF); 3823fa9e4066Sahrens } 3824fa9e4066Sahrens 38253cb34c60Sahrens off = fp->f_offset; 38263cb34c60Sahrens error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off); 3827fa9e4066Sahrens 38283cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 38293cb34c60Sahrens fp->f_offset = off; 3830fa9e4066Sahrens releasef(zc->zc_cookie); 3831a7f53a56SChris Kirby if (dsfrom) 3832a7f53a56SChris Kirby dsl_dataset_rele(dsfrom, FTAG); 3833a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 3834fa9e4066Sahrens return (error); 3835fa9e4066Sahrens } 3836fa9e4066Sahrens 3837ea8dc4b6Seschrock static int 3838ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 3839ea8dc4b6Seschrock { 3840ea8dc4b6Seschrock int id, error; 3841ea8dc4b6Seschrock 3842ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 3843ea8dc4b6Seschrock &zc->zc_inject_record); 3844ea8dc4b6Seschrock 3845ea8dc4b6Seschrock if (error == 0) 3846ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 3847ea8dc4b6Seschrock 3848ea8dc4b6Seschrock return (error); 3849ea8dc4b6Seschrock } 3850ea8dc4b6Seschrock 3851ea8dc4b6Seschrock static int 3852ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 3853ea8dc4b6Seschrock { 3854ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 3855ea8dc4b6Seschrock } 3856ea8dc4b6Seschrock 3857ea8dc4b6Seschrock static int 3858ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 3859ea8dc4b6Seschrock { 3860ea8dc4b6Seschrock int id = (int)zc->zc_guid; 3861ea8dc4b6Seschrock int error; 3862ea8dc4b6Seschrock 3863ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 3864ea8dc4b6Seschrock &zc->zc_inject_record); 3865ea8dc4b6Seschrock 3866ea8dc4b6Seschrock zc->zc_guid = id; 3867ea8dc4b6Seschrock 3868ea8dc4b6Seschrock return (error); 3869ea8dc4b6Seschrock } 3870ea8dc4b6Seschrock 3871ea8dc4b6Seschrock static int 3872ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 3873ea8dc4b6Seschrock { 3874ea8dc4b6Seschrock spa_t *spa; 3875ea8dc4b6Seschrock int error; 3876e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 3877ea8dc4b6Seschrock 3878ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 3879ea8dc4b6Seschrock return (error); 3880ea8dc4b6Seschrock 3881e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 3882ea8dc4b6Seschrock &count); 3883ea8dc4b6Seschrock if (error == 0) 3884e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 3885ea8dc4b6Seschrock else 3886e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 3887ea8dc4b6Seschrock 3888ea8dc4b6Seschrock spa_close(spa, FTAG); 3889ea8dc4b6Seschrock 3890ea8dc4b6Seschrock return (error); 3891ea8dc4b6Seschrock } 3892ea8dc4b6Seschrock 3893ea8dc4b6Seschrock static int 3894ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 3895ea8dc4b6Seschrock { 3896ea8dc4b6Seschrock spa_t *spa; 3897ea8dc4b6Seschrock vdev_t *vd; 3898bb8b5132Sek int error; 3899ea8dc4b6Seschrock 3900b87f3af3Sperrin /* 3901b87f3af3Sperrin * On zpool clear we also fix up missing slogs 3902b87f3af3Sperrin */ 3903b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 3904b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 3905b87f3af3Sperrin if (spa == NULL) { 3906b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3907b87f3af3Sperrin return (EIO); 3908b87f3af3Sperrin } 3909b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 3910b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 3911b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 3912b87f3af3Sperrin } 3913468c413aSTim Haley spa->spa_last_open_failed = 0; 3914b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3915b87f3af3Sperrin 3916c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 3917468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 3918468c413aSTim Haley } else { 3919468c413aSTim Haley nvlist_t *policy; 3920468c413aSTim Haley nvlist_t *config = NULL; 3921468c413aSTim Haley 3922468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 3923468c413aSTim Haley return (EINVAL); 3924468c413aSTim Haley 3925468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 3926468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 3927468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 3928468c413aSTim Haley policy, &config); 3929468c413aSTim Haley if (config != NULL) { 39304b964adaSGeorge Wilson int err; 39314b964adaSGeorge Wilson 39324b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 39334b964adaSGeorge Wilson error = err; 3934468c413aSTim Haley nvlist_free(config); 3935468c413aSTim Haley } 3936468c413aSTim Haley nvlist_free(policy); 3937468c413aSTim Haley } 3938468c413aSTim Haley } 3939468c413aSTim Haley 3940468c413aSTim Haley if (error) 3941ea8dc4b6Seschrock return (error); 3942ea8dc4b6Seschrock 39438f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 3944ea8dc4b6Seschrock 3945e9dbad6fSeschrock if (zc->zc_guid == 0) { 3946ea8dc4b6Seschrock vd = NULL; 3947c5904d13Seschrock } else { 3948c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 3949fa94a07fSbrendan if (vd == NULL) { 3950e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 3951fa94a07fSbrendan spa_close(spa, FTAG); 3952fa94a07fSbrendan return (ENODEV); 3953fa94a07fSbrendan } 3954ea8dc4b6Seschrock } 3955ea8dc4b6Seschrock 3956e14bb325SJeff Bonwick vdev_clear(spa, vd); 3957e14bb325SJeff Bonwick 3958e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 3959ea8dc4b6Seschrock 3960e14bb325SJeff Bonwick /* 3961e14bb325SJeff Bonwick * Resume any suspended I/Os. 3962e14bb325SJeff Bonwick */ 396354d692b7SGeorge Wilson if (zio_resume(spa) != 0) 396454d692b7SGeorge Wilson error = EIO; 3965ea8dc4b6Seschrock 3966ea8dc4b6Seschrock spa_close(spa, FTAG); 3967ea8dc4b6Seschrock 396854d692b7SGeorge Wilson return (error); 3969ea8dc4b6Seschrock } 3970ea8dc4b6Seschrock 39713cb34c60Sahrens /* 39723cb34c60Sahrens * inputs: 39733cb34c60Sahrens * zc_name name of filesystem 39743cb34c60Sahrens * zc_value name of origin snapshot 39753cb34c60Sahrens * 3976681d9761SEric Taylor * outputs: 3977681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 39783cb34c60Sahrens */ 397999653d4eSeschrock static int 398099653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 398199653d4eSeschrock { 39820b69c2f0Sahrens char *cp; 39830b69c2f0Sahrens 39840b69c2f0Sahrens /* 39850b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 39860b69c2f0Sahrens * it's easier. 39870b69c2f0Sahrens */ 3988e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 39890b69c2f0Sahrens if (cp) 39900b69c2f0Sahrens *cp = '\0'; 3991e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 39920b69c2f0Sahrens zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); 3993681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 399499653d4eSeschrock } 399599653d4eSeschrock 399614843421SMatthew Ahrens /* 399714843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 399814843421SMatthew Ahrens * 399914843421SMatthew Ahrens * inputs: 400014843421SMatthew Ahrens * zc_name name of filesystem 400114843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 400214843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 400314843421SMatthew Ahrens * zc_guid RID/UID/GID 400414843421SMatthew Ahrens * 400514843421SMatthew Ahrens * outputs: 400614843421SMatthew Ahrens * zc_cookie property value 400714843421SMatthew Ahrens */ 400814843421SMatthew Ahrens static int 400914843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 401014843421SMatthew Ahrens { 401114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 401214843421SMatthew Ahrens int error; 401314843421SMatthew Ahrens 401414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 401514843421SMatthew Ahrens return (EINVAL); 401614843421SMatthew Ahrens 40171412a1a2SMark Shellenbaum error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 401814843421SMatthew Ahrens if (error) 401914843421SMatthew Ahrens return (error); 402014843421SMatthew Ahrens 402114843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 402214843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 402314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 402414843421SMatthew Ahrens 402514843421SMatthew Ahrens return (error); 402614843421SMatthew Ahrens } 402714843421SMatthew Ahrens 402814843421SMatthew Ahrens /* 402914843421SMatthew Ahrens * inputs: 403014843421SMatthew Ahrens * zc_name name of filesystem 403114843421SMatthew Ahrens * zc_cookie zap cursor 403214843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 403314843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 403414843421SMatthew Ahrens * 403514843421SMatthew Ahrens * outputs: 403614843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 403714843421SMatthew Ahrens * zc_cookie zap cursor 403814843421SMatthew Ahrens */ 403914843421SMatthew Ahrens static int 404014843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 404114843421SMatthew Ahrens { 404214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 4043eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 404414843421SMatthew Ahrens 4045eeb85002STim Haley if (bufsize <= 0) 4046eeb85002STim Haley return (ENOMEM); 4047eeb85002STim Haley 40481412a1a2SMark Shellenbaum int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 404914843421SMatthew Ahrens if (error) 405014843421SMatthew Ahrens return (error); 405114843421SMatthew Ahrens 405214843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 405314843421SMatthew Ahrens 405414843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 405514843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 405614843421SMatthew Ahrens 405714843421SMatthew Ahrens if (error == 0) { 405814843421SMatthew Ahrens error = xcopyout(buf, 405914843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 406014843421SMatthew Ahrens zc->zc_nvlist_dst_size); 406114843421SMatthew Ahrens } 406214843421SMatthew Ahrens kmem_free(buf, bufsize); 406314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 406414843421SMatthew Ahrens 406514843421SMatthew Ahrens return (error); 406614843421SMatthew Ahrens } 406714843421SMatthew Ahrens 406814843421SMatthew Ahrens /* 406914843421SMatthew Ahrens * inputs: 407014843421SMatthew Ahrens * zc_name name of filesystem 407114843421SMatthew Ahrens * 407214843421SMatthew Ahrens * outputs: 407314843421SMatthew Ahrens * none 407414843421SMatthew Ahrens */ 407514843421SMatthew Ahrens static int 407614843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 407714843421SMatthew Ahrens { 407814843421SMatthew Ahrens objset_t *os; 40791195e687SMark J Musante int error = 0; 408014843421SMatthew Ahrens zfsvfs_t *zfsvfs; 408114843421SMatthew Ahrens 408214843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 4083503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 408414843421SMatthew Ahrens /* 408514843421SMatthew Ahrens * If userused is not enabled, it may be because the 408614843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 408714843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 408814843421SMatthew Ahrens */ 4089503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 4090503ad85cSMatthew Ahrens if (error == 0) 4091503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 409214843421SMatthew Ahrens } 409314843421SMatthew Ahrens if (error == 0) 409414843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 409514843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 409614843421SMatthew Ahrens } else { 4097503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 4098503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 409914843421SMatthew Ahrens if (error) 410014843421SMatthew Ahrens return (error); 410114843421SMatthew Ahrens 410214843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 4103503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 410414843421SMatthew Ahrens } 410514843421SMatthew Ahrens 410614843421SMatthew Ahrens return (error); 410714843421SMatthew Ahrens } 410814843421SMatthew Ahrens 4109ecd6cf80Smarks /* 4110ecd6cf80Smarks * We don't want to have a hard dependency 4111ecd6cf80Smarks * against some special symbols in sharefs 4112da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 4113ecd6cf80Smarks * the first file system is shared. 4114da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 4115ecd6cf80Smarks */ 4116da6c28aaSamw int (*znfsexport_fs)(void *arg); 4117ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 4118da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 4119da6c28aaSamw 4120da6c28aaSamw int zfs_nfsshare_inited; 4121da6c28aaSamw int zfs_smbshare_inited; 4122ecd6cf80Smarks 4123ecd6cf80Smarks ddi_modhandle_t nfs_mod; 4124ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 4125da6c28aaSamw ddi_modhandle_t smbsrv_mod; 4126ecd6cf80Smarks kmutex_t zfs_share_lock; 4127ecd6cf80Smarks 4128da6c28aaSamw static int 4129da6c28aaSamw zfs_init_sharefs() 4130da6c28aaSamw { 4131da6c28aaSamw int error; 4132da6c28aaSamw 4133da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 4134da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 4135da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 4136da6c28aaSamw ddi_modopen("fs/sharefs", 4137da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4138da6c28aaSamw return (ENOSYS); 4139da6c28aaSamw } 4140da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 4141da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 4142da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 4143da6c28aaSamw return (ENOSYS); 4144da6c28aaSamw } 4145da6c28aaSamw return (0); 4146da6c28aaSamw } 4147da6c28aaSamw 4148ecd6cf80Smarks static int 4149ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 4150ecd6cf80Smarks { 4151ecd6cf80Smarks int error; 4152ecd6cf80Smarks int opcode; 4153ecd6cf80Smarks 4154da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4155da6c28aaSamw case ZFS_SHARE_NFS: 4156da6c28aaSamw case ZFS_UNSHARE_NFS: 4157da6c28aaSamw if (zfs_nfsshare_inited == 0) { 4158da6c28aaSamw mutex_enter(&zfs_share_lock); 4159da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 4160da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4161da6c28aaSamw mutex_exit(&zfs_share_lock); 4162da6c28aaSamw return (ENOSYS); 4163da6c28aaSamw } 4164da6c28aaSamw if (znfsexport_fs == NULL && 4165da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 4166da6c28aaSamw ddi_modsym(nfs_mod, 4167da6c28aaSamw "nfs_export", &error)) == NULL)) { 4168da6c28aaSamw mutex_exit(&zfs_share_lock); 4169da6c28aaSamw return (ENOSYS); 4170da6c28aaSamw } 4171da6c28aaSamw error = zfs_init_sharefs(); 4172da6c28aaSamw if (error) { 4173da6c28aaSamw mutex_exit(&zfs_share_lock); 4174da6c28aaSamw return (ENOSYS); 4175da6c28aaSamw } 4176da6c28aaSamw zfs_nfsshare_inited = 1; 4177ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4178ecd6cf80Smarks } 4179da6c28aaSamw break; 4180da6c28aaSamw case ZFS_SHARE_SMB: 4181da6c28aaSamw case ZFS_UNSHARE_SMB: 4182da6c28aaSamw if (zfs_smbshare_inited == 0) { 4183da6c28aaSamw mutex_enter(&zfs_share_lock); 4184da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 4185da6c28aaSamw ddi_modopen("drv/smbsrv", 4186da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4187da6c28aaSamw mutex_exit(&zfs_share_lock); 4188da6c28aaSamw return (ENOSYS); 4189da6c28aaSamw } 4190da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 4191da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 4192faa1795aSjb "smb_server_share", &error)) == NULL)) { 4193da6c28aaSamw mutex_exit(&zfs_share_lock); 4194da6c28aaSamw return (ENOSYS); 4195da6c28aaSamw } 4196da6c28aaSamw error = zfs_init_sharefs(); 4197da6c28aaSamw if (error) { 4198da6c28aaSamw mutex_exit(&zfs_share_lock); 4199da6c28aaSamw return (ENOSYS); 4200da6c28aaSamw } 4201da6c28aaSamw zfs_smbshare_inited = 1; 4202ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4203ecd6cf80Smarks } 4204da6c28aaSamw break; 4205da6c28aaSamw default: 4206da6c28aaSamw return (EINVAL); 4207da6c28aaSamw } 4208ecd6cf80Smarks 4209da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4210da6c28aaSamw case ZFS_SHARE_NFS: 4211da6c28aaSamw case ZFS_UNSHARE_NFS: 4212da6c28aaSamw if (error = 4213da6c28aaSamw znfsexport_fs((void *) 4214da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 4215da6c28aaSamw return (error); 4216da6c28aaSamw break; 4217da6c28aaSamw case ZFS_SHARE_SMB: 4218da6c28aaSamw case ZFS_UNSHARE_SMB: 4219da6c28aaSamw if (error = zsmbexport_fs((void *) 4220da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 4221da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 4222743a77edSAlan Wright B_TRUE: B_FALSE)) { 4223da6c28aaSamw return (error); 4224ecd6cf80Smarks } 4225da6c28aaSamw break; 4226ecd6cf80Smarks } 4227ecd6cf80Smarks 4228da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 4229da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 4230ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 4231ecd6cf80Smarks 4232da6c28aaSamw /* 4233da6c28aaSamw * Add or remove share from sharetab 4234da6c28aaSamw */ 4235ecd6cf80Smarks error = zshare_fs(opcode, 4236ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 4237ecd6cf80Smarks zc->zc_share.z_sharemax); 4238ecd6cf80Smarks 4239ecd6cf80Smarks return (error); 4240ecd6cf80Smarks 4241ecd6cf80Smarks } 4242ecd6cf80Smarks 4243743a77edSAlan Wright ace_t full_access[] = { 4244743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 4245743a77edSAlan Wright }; 4246743a77edSAlan Wright 424799d5e173STim Haley /* 424899d5e173STim Haley * inputs: 424999d5e173STim Haley * zc_name name of containing filesystem 425099d5e173STim Haley * zc_obj object # beyond which we want next in-use object # 425199d5e173STim Haley * 425299d5e173STim Haley * outputs: 425399d5e173STim Haley * zc_obj next in-use object # 425499d5e173STim Haley */ 425599d5e173STim Haley static int 425699d5e173STim Haley zfs_ioc_next_obj(zfs_cmd_t *zc) 425799d5e173STim Haley { 425899d5e173STim Haley objset_t *os = NULL; 425999d5e173STim Haley int error; 426099d5e173STim Haley 426199d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &os); 426299d5e173STim Haley if (error) 426399d5e173STim Haley return (error); 426499d5e173STim Haley 426599d5e173STim Haley error = dmu_object_next(os, &zc->zc_obj, B_FALSE, 426699d5e173STim Haley os->os_dsl_dataset->ds_phys->ds_prev_snap_txg); 426799d5e173STim Haley 426899d5e173STim Haley dmu_objset_rele(os, FTAG); 426999d5e173STim Haley return (error); 427099d5e173STim Haley } 427199d5e173STim Haley 427299d5e173STim Haley /* 427399d5e173STim Haley * inputs: 427499d5e173STim Haley * zc_name name of filesystem 427599d5e173STim Haley * zc_value prefix name for snapshot 427699d5e173STim Haley * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 427799d5e173STim Haley * 427899d5e173STim Haley * outputs: 427999d5e173STim Haley */ 428099d5e173STim Haley static int 428199d5e173STim Haley zfs_ioc_tmp_snapshot(zfs_cmd_t *zc) 428299d5e173STim Haley { 428399d5e173STim Haley char *snap_name; 428499d5e173STim Haley int error; 428599d5e173STim Haley 428699d5e173STim Haley snap_name = kmem_asprintf("%s-%016llx", zc->zc_value, 428799d5e173STim Haley (u_longlong_t)ddi_get_lbolt64()); 428899d5e173STim Haley 428999d5e173STim Haley if (strlen(snap_name) >= MAXNAMELEN) { 429099d5e173STim Haley strfree(snap_name); 429199d5e173STim Haley return (E2BIG); 429299d5e173STim Haley } 429399d5e173STim Haley 429499d5e173STim Haley error = dmu_objset_snapshot(zc->zc_name, snap_name, snap_name, 429599d5e173STim Haley NULL, B_FALSE, B_TRUE, zc->zc_cleanup_fd); 429699d5e173STim Haley if (error != 0) { 429799d5e173STim Haley strfree(snap_name); 429899d5e173STim Haley return (error); 429999d5e173STim Haley } 430099d5e173STim Haley 430199d5e173STim Haley (void) strcpy(zc->zc_value, snap_name); 430299d5e173STim Haley strfree(snap_name); 430399d5e173STim Haley return (0); 430499d5e173STim Haley } 430599d5e173STim Haley 430699d5e173STim Haley /* 430799d5e173STim Haley * inputs: 430899d5e173STim Haley * zc_name name of "to" snapshot 430999d5e173STim Haley * zc_value name of "from" snapshot 431099d5e173STim Haley * zc_cookie file descriptor to write diff data on 431199d5e173STim Haley * 431299d5e173STim Haley * outputs: 431399d5e173STim Haley * dmu_diff_record_t's to the file descriptor 431499d5e173STim Haley */ 431599d5e173STim Haley static int 431699d5e173STim Haley zfs_ioc_diff(zfs_cmd_t *zc) 431799d5e173STim Haley { 431899d5e173STim Haley objset_t *fromsnap; 431999d5e173STim Haley objset_t *tosnap; 432099d5e173STim Haley file_t *fp; 432199d5e173STim Haley offset_t off; 432299d5e173STim Haley int error; 432399d5e173STim Haley 432499d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap); 432599d5e173STim Haley if (error) 432699d5e173STim Haley return (error); 432799d5e173STim Haley 432899d5e173STim Haley error = dmu_objset_hold(zc->zc_value, FTAG, &fromsnap); 432999d5e173STim Haley if (error) { 433099d5e173STim Haley dmu_objset_rele(tosnap, FTAG); 433199d5e173STim Haley return (error); 433299d5e173STim Haley } 433399d5e173STim Haley 433499d5e173STim Haley fp = getf(zc->zc_cookie); 433599d5e173STim Haley if (fp == NULL) { 433699d5e173STim Haley dmu_objset_rele(fromsnap, FTAG); 433799d5e173STim Haley dmu_objset_rele(tosnap, FTAG); 433899d5e173STim Haley return (EBADF); 433999d5e173STim Haley } 434099d5e173STim Haley 434199d5e173STim Haley off = fp->f_offset; 434299d5e173STim Haley 434399d5e173STim Haley error = dmu_diff(tosnap, fromsnap, fp->f_vnode, &off); 434499d5e173STim Haley 434599d5e173STim Haley if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 434699d5e173STim Haley fp->f_offset = off; 434799d5e173STim Haley releasef(zc->zc_cookie); 434899d5e173STim Haley 434999d5e173STim Haley dmu_objset_rele(fromsnap, FTAG); 435099d5e173STim Haley dmu_objset_rele(tosnap, FTAG); 435199d5e173STim Haley return (error); 435299d5e173STim Haley } 435399d5e173STim Haley 4354743a77edSAlan Wright /* 4355743a77edSAlan Wright * Remove all ACL files in shares dir 4356743a77edSAlan Wright */ 4357743a77edSAlan Wright static int 4358743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 4359743a77edSAlan Wright { 4360743a77edSAlan Wright zap_cursor_t zc; 4361743a77edSAlan Wright zap_attribute_t zap; 4362743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 4363743a77edSAlan Wright int error; 4364743a77edSAlan Wright 4365743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 4366743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 4367743a77edSAlan Wright zap_cursor_advance(&zc)) { 4368743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 4369743a77edSAlan Wright NULL, 0)) != 0) 4370743a77edSAlan Wright break; 4371743a77edSAlan Wright } 4372743a77edSAlan Wright zap_cursor_fini(&zc); 4373743a77edSAlan Wright return (error); 4374743a77edSAlan Wright } 4375743a77edSAlan Wright 4376743a77edSAlan Wright static int 4377743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 4378743a77edSAlan Wright { 4379743a77edSAlan Wright vnode_t *vp; 4380743a77edSAlan Wright znode_t *dzp; 4381743a77edSAlan Wright vnode_t *resourcevp = NULL; 4382743a77edSAlan Wright znode_t *sharedir; 4383743a77edSAlan Wright zfsvfs_t *zfsvfs; 4384743a77edSAlan Wright nvlist_t *nvlist; 4385743a77edSAlan Wright char *src, *target; 4386743a77edSAlan Wright vattr_t vattr; 4387743a77edSAlan Wright vsecattr_t vsec; 4388743a77edSAlan Wright int error = 0; 4389743a77edSAlan Wright 4390743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 4391743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 4392743a77edSAlan Wright return (error); 4393743a77edSAlan Wright 4394743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 4395743a77edSAlan Wright 4396743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 4397743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 4398743a77edSAlan Wright zc->zc_name) != 0)) { 4399743a77edSAlan Wright VN_RELE(vp); 4400743a77edSAlan Wright return (EINVAL); 4401743a77edSAlan Wright } 4402743a77edSAlan Wright 4403743a77edSAlan Wright dzp = VTOZ(vp); 4404743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 4405743a77edSAlan Wright ZFS_ENTER(zfsvfs); 4406743a77edSAlan Wright 44079e1320c0SMark Shellenbaum /* 44089e1320c0SMark Shellenbaum * Create share dir if its missing. 44099e1320c0SMark Shellenbaum */ 44109e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 44119e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 44129e1320c0SMark Shellenbaum dmu_tx_t *tx; 44139e1320c0SMark Shellenbaum 44149e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 44159e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 44169e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 44179e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 44189e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 44199e1320c0SMark Shellenbaum if (error) { 44209e1320c0SMark Shellenbaum dmu_tx_abort(tx); 44219e1320c0SMark Shellenbaum } else { 44229e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 44239e1320c0SMark Shellenbaum dmu_tx_commit(tx); 44249e1320c0SMark Shellenbaum } 44259e1320c0SMark Shellenbaum if (error) { 44269e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 44279e1320c0SMark Shellenbaum VN_RELE(vp); 44289e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 44299e1320c0SMark Shellenbaum return (error); 44309e1320c0SMark Shellenbaum } 44319e1320c0SMark Shellenbaum } 44329e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 44339e1320c0SMark Shellenbaum 44349e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 4435743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 44369e1320c0SMark Shellenbaum VN_RELE(vp); 4437743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4438743a77edSAlan Wright return (error); 4439743a77edSAlan Wright } 4440743a77edSAlan Wright 4441743a77edSAlan Wright switch (zc->zc_cookie) { 4442743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 4443743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 4444743a77edSAlan Wright vattr.va_type = VREG; 4445743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 4446743a77edSAlan Wright vattr.va_uid = 0; 4447743a77edSAlan Wright vattr.va_gid = 0; 4448743a77edSAlan Wright 4449743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 4450743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 4451743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 4452743a77edSAlan Wright vsec.vsa_aclcnt = 1; 4453743a77edSAlan Wright 4454743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 4455743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 4456743a77edSAlan Wright if (resourcevp) 4457743a77edSAlan Wright VN_RELE(resourcevp); 4458743a77edSAlan Wright break; 4459743a77edSAlan Wright 4460743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 4461743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 4462743a77edSAlan Wright NULL, 0); 4463743a77edSAlan Wright break; 4464743a77edSAlan Wright 4465743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 4466743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 4467478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 4468743a77edSAlan Wright VN_RELE(vp); 4469743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4470743a77edSAlan Wright return (error); 4471743a77edSAlan Wright } 4472743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 4473743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 4474743a77edSAlan Wright &target)) { 4475743a77edSAlan Wright VN_RELE(vp); 447689459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 4477743a77edSAlan Wright ZFS_EXIT(zfsvfs); 44781195e687SMark J Musante nvlist_free(nvlist); 4479743a77edSAlan Wright return (error); 4480743a77edSAlan Wright } 4481743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 4482743a77edSAlan Wright kcred, NULL, 0); 4483743a77edSAlan Wright nvlist_free(nvlist); 4484743a77edSAlan Wright break; 4485743a77edSAlan Wright 4486743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 4487743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 4488743a77edSAlan Wright break; 4489743a77edSAlan Wright 4490743a77edSAlan Wright default: 4491743a77edSAlan Wright error = EINVAL; 4492743a77edSAlan Wright break; 4493743a77edSAlan Wright } 4494743a77edSAlan Wright 4495743a77edSAlan Wright VN_RELE(vp); 4496743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 4497743a77edSAlan Wright 4498743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4499743a77edSAlan Wright 4500743a77edSAlan Wright return (error); 4501743a77edSAlan Wright } 4502743a77edSAlan Wright 4503842727c2SChris Kirby /* 4504842727c2SChris Kirby * inputs: 4505c99e4bdcSChris Kirby * zc_name name of filesystem 4506c99e4bdcSChris Kirby * zc_value short name of snap 4507c99e4bdcSChris Kirby * zc_string user-supplied tag for this hold 4508c99e4bdcSChris Kirby * zc_cookie recursive flag 4509c99e4bdcSChris Kirby * zc_temphold set if hold is temporary 4510c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 4511a7f53a56SChris Kirby * zc_sendobj if non-zero, the objid for zc_name@zc_value 4512a7f53a56SChris Kirby * zc_createtxg if zc_sendobj is non-zero, snap must have zc_createtxg 4513842727c2SChris Kirby * 4514842727c2SChris Kirby * outputs: none 4515842727c2SChris Kirby */ 4516842727c2SChris Kirby static int 4517842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc) 4518842727c2SChris Kirby { 4519842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4520a7f53a56SChris Kirby spa_t *spa; 4521a7f53a56SChris Kirby dsl_pool_t *dp; 4522a7f53a56SChris Kirby dsl_dataset_t *ds; 4523a7f53a56SChris Kirby int error; 4524a7f53a56SChris Kirby minor_t minor = 0; 4525842727c2SChris Kirby 4526842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4527842727c2SChris Kirby return (EINVAL); 4528842727c2SChris Kirby 4529a7f53a56SChris Kirby if (zc->zc_sendobj == 0) { 4530a7f53a56SChris Kirby return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value, 4531a7f53a56SChris Kirby zc->zc_string, recursive, zc->zc_temphold, 4532a7f53a56SChris Kirby zc->zc_cleanup_fd)); 4533a7f53a56SChris Kirby } 4534a7f53a56SChris Kirby 4535a7f53a56SChris Kirby if (recursive) 4536a7f53a56SChris Kirby return (EINVAL); 4537a7f53a56SChris Kirby 4538a7f53a56SChris Kirby error = spa_open(zc->zc_name, &spa, FTAG); 4539a7f53a56SChris Kirby if (error) 4540a7f53a56SChris Kirby return (error); 4541a7f53a56SChris Kirby 4542a7f53a56SChris Kirby dp = spa_get_dsl(spa); 4543a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 4544a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 4545a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 4546a7f53a56SChris Kirby spa_close(spa, FTAG); 4547a7f53a56SChris Kirby if (error) 4548a7f53a56SChris Kirby return (error); 4549a7f53a56SChris Kirby 4550a7f53a56SChris Kirby /* 4551a7f53a56SChris Kirby * Until we have a hold on this snapshot, it's possible that 4552a7f53a56SChris Kirby * zc_sendobj could've been destroyed and reused as part 4553a7f53a56SChris Kirby * of a later txg. Make sure we're looking at the right object. 4554a7f53a56SChris Kirby */ 4555a7f53a56SChris Kirby if (zc->zc_createtxg != ds->ds_phys->ds_creation_txg) { 4556a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4557a7f53a56SChris Kirby return (ENOENT); 4558a7f53a56SChris Kirby } 4559a7f53a56SChris Kirby 4560a7f53a56SChris Kirby if (zc->zc_cleanup_fd != -1 && zc->zc_temphold) { 4561a7f53a56SChris Kirby error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor); 4562a7f53a56SChris Kirby if (error) { 4563a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4564a7f53a56SChris Kirby return (error); 4565a7f53a56SChris Kirby } 4566a7f53a56SChris Kirby } 4567a7f53a56SChris Kirby 4568a7f53a56SChris Kirby error = dsl_dataset_user_hold_for_send(ds, zc->zc_string, 4569a7f53a56SChris Kirby zc->zc_temphold); 4570a7f53a56SChris Kirby if (minor != 0) { 4571a7f53a56SChris Kirby if (error == 0) { 4572a7f53a56SChris Kirby dsl_register_onexit_hold_cleanup(ds, zc->zc_string, 4573a7f53a56SChris Kirby minor); 4574a7f53a56SChris Kirby } 4575a7f53a56SChris Kirby zfs_onexit_fd_rele(zc->zc_cleanup_fd); 4576a7f53a56SChris Kirby } 4577a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4578a7f53a56SChris Kirby 4579a7f53a56SChris Kirby return (error); 4580842727c2SChris Kirby } 4581842727c2SChris Kirby 4582842727c2SChris Kirby /* 4583842727c2SChris Kirby * inputs: 4584c99e4bdcSChris Kirby * zc_name name of dataset from which we're releasing a user hold 4585842727c2SChris Kirby * zc_value short name of snap 4586c99e4bdcSChris Kirby * zc_string user-supplied tag for this hold 4587842727c2SChris Kirby * zc_cookie recursive flag 4588842727c2SChris Kirby * 4589c99e4bdcSChris Kirby * outputs: none 4590842727c2SChris Kirby */ 4591842727c2SChris Kirby static int 4592842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc) 4593842727c2SChris Kirby { 4594842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4595842727c2SChris Kirby 4596842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4597842727c2SChris Kirby return (EINVAL); 4598842727c2SChris Kirby 4599842727c2SChris Kirby return (dsl_dataset_user_release(zc->zc_name, zc->zc_value, 4600842727c2SChris Kirby zc->zc_string, recursive)); 4601842727c2SChris Kirby } 4602842727c2SChris Kirby 4603842727c2SChris Kirby /* 4604842727c2SChris Kirby * inputs: 4605842727c2SChris Kirby * zc_name name of filesystem 4606842727c2SChris Kirby * 4607842727c2SChris Kirby * outputs: 4608842727c2SChris Kirby * zc_nvlist_src{_size} nvlist of snapshot holds 4609842727c2SChris Kirby */ 4610842727c2SChris Kirby static int 4611842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc) 4612842727c2SChris Kirby { 4613842727c2SChris Kirby nvlist_t *nvp; 4614842727c2SChris Kirby int error; 4615842727c2SChris Kirby 4616842727c2SChris Kirby if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) { 4617842727c2SChris Kirby error = put_nvlist(zc, nvp); 4618842727c2SChris Kirby nvlist_free(nvp); 4619842727c2SChris Kirby } 4620842727c2SChris Kirby 4621842727c2SChris Kirby return (error); 4622842727c2SChris Kirby } 4623842727c2SChris Kirby 4624ecd6cf80Smarks /* 46252a6b87f0Sek * pool create, destroy, and export don't log the history as part of 46262a6b87f0Sek * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export 46272a6b87f0Sek * do the logging of those commands. 4628ecd6cf80Smarks */ 4629fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = { 463054d692b7SGeorge Wilson { zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE, 4631*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 463254d692b7SGeorge Wilson { zfs_ioc_pool_destroy, zfs_secpolicy_config, POOL_NAME, B_FALSE, 4633*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 463454d692b7SGeorge Wilson { zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4635*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 463654d692b7SGeorge Wilson { zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE, 4637*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 463854d692b7SGeorge Wilson { zfs_ioc_pool_configs, zfs_secpolicy_none, NO_NAME, B_FALSE, 4639*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 464054d692b7SGeorge Wilson { zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE, 4641*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 464254d692b7SGeorge Wilson { zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE, 4643*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 46443f9d6ad7SLin Ling { zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4645*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 464654d692b7SGeorge Wilson { zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE, 4647*f9af39baSGeorge Wilson POOL_CHECK_READONLY }, 464854d692b7SGeorge Wilson { zfs_ioc_pool_upgrade, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4649*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 465054d692b7SGeorge Wilson { zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE, 4651*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 465254d692b7SGeorge Wilson { zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4653*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 465454d692b7SGeorge Wilson { zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4655*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 465654d692b7SGeorge Wilson { zfs_ioc_vdev_set_state, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4657*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 465854d692b7SGeorge Wilson { zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4659*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 466054d692b7SGeorge Wilson { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4661*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 466254d692b7SGeorge Wilson { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE, 4663*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 46646809eb4eSEric Schrock { zfs_ioc_vdev_setfru, zfs_secpolicy_config, POOL_NAME, B_FALSE, 4665*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 466654d692b7SGeorge Wilson { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4667*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED }, 466854d692b7SGeorge Wilson { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4669*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 467054d692b7SGeorge Wilson { zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4671*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED }, 467254d692b7SGeorge Wilson { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4673*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED }, 4674*f9af39baSGeorge Wilson { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, 4675*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4676*f9af39baSGeorge Wilson { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, 4677*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 467854d692b7SGeorge Wilson { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, 4679*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 468054d692b7SGeorge Wilson { zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE, 4681*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4682*f9af39baSGeorge Wilson { zfs_ioc_rename, zfs_secpolicy_rename, DATASET_NAME, B_TRUE, 4683*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4684*f9af39baSGeorge Wilson { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, 4685*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4686*f9af39baSGeorge Wilson { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, 4687*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 468854d692b7SGeorge Wilson { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 4689*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 469054d692b7SGeorge Wilson { zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 4691*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 469254d692b7SGeorge Wilson { zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE, 4693*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 469454d692b7SGeorge Wilson { zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE, 4695*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 4696*f9af39baSGeorge Wilson { zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4697*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 469854d692b7SGeorge Wilson { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE, 4699*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4700cbf6f6aaSWilliam Gorrell { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME, 4701*f9af39baSGeorge Wilson B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 470254d692b7SGeorge Wilson { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE, 4703*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 470499d5e173STim Haley { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_diff, POOL_NAME, B_FALSE, 4705*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 470699d5e173STim Haley { zfs_ioc_obj_to_path, zfs_secpolicy_diff, DATASET_NAME, B_FALSE, 4707*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED }, 470854d692b7SGeorge Wilson { zfs_ioc_pool_set_props, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4709*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 471054d692b7SGeorge Wilson { zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE, 4711*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 471254d692b7SGeorge Wilson { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE, 4713*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 471454d692b7SGeorge Wilson { zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4715*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 4716*f9af39baSGeorge Wilson { zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, 4717*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 471854d692b7SGeorge Wilson { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE, 4719*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 472054d692b7SGeorge Wilson { zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE, 4721*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 4722*f9af39baSGeorge Wilson { zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, DATASET_NAME, 4723*f9af39baSGeorge Wilson B_FALSE, POOL_CHECK_NONE }, 4724*f9af39baSGeorge Wilson { zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, DATASET_NAME, 4725*f9af39baSGeorge Wilson B_FALSE, POOL_CHECK_NONE }, 472614843421SMatthew Ahrens { zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 4727*f9af39baSGeorge Wilson DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4728*f9af39baSGeorge Wilson { zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, 4729*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4730842727c2SChris Kirby { zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE, 4731*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 4732842727c2SChris Kirby { zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4733*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED }, 473492241e0bSTom Erickson { zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4735*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 47361195e687SMark J Musante { zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE, 4737*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 473899d5e173STim Haley { zfs_ioc_next_obj, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4739*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 4740*f9af39baSGeorge Wilson { zfs_ioc_diff, zfs_secpolicy_diff, DATASET_NAME, B_FALSE, 4741*f9af39baSGeorge Wilson POOL_CHECK_NONE }, 474299d5e173STim Haley { zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, DATASET_NAME, 4743*f9af39baSGeorge Wilson B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY }, 474499d5e173STim Haley { zfs_ioc_obj_to_stats, zfs_secpolicy_diff, DATASET_NAME, B_FALSE, 4745*f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED } 4746fa9e4066Sahrens }; 4747fa9e4066Sahrens 474854d692b7SGeorge Wilson int 4749*f9af39baSGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type, 4750*f9af39baSGeorge Wilson zfs_ioc_poolcheck_t check) 475154d692b7SGeorge Wilson { 475254d692b7SGeorge Wilson spa_t *spa; 475354d692b7SGeorge Wilson int error; 475454d692b7SGeorge Wilson 475554d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 475654d692b7SGeorge Wilson 4757*f9af39baSGeorge Wilson if (check & POOL_CHECK_NONE) 4758*f9af39baSGeorge Wilson return (0); 4759*f9af39baSGeorge Wilson 476014843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 476154d692b7SGeorge Wilson if (error == 0) { 4762*f9af39baSGeorge Wilson if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa)) 476354d692b7SGeorge Wilson error = EAGAIN; 4764*f9af39baSGeorge Wilson else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa)) 4765*f9af39baSGeorge Wilson error = EROFS; 476654d692b7SGeorge Wilson spa_close(spa, FTAG); 476754d692b7SGeorge Wilson } 476854d692b7SGeorge Wilson return (error); 476954d692b7SGeorge Wilson } 477054d692b7SGeorge Wilson 4771c99e4bdcSChris Kirby /* 4772c99e4bdcSChris Kirby * Find a free minor number. 4773c99e4bdcSChris Kirby */ 4774c99e4bdcSChris Kirby minor_t 4775c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 4776c99e4bdcSChris Kirby { 4777c99e4bdcSChris Kirby static minor_t last_minor; 4778c99e4bdcSChris Kirby minor_t m; 4779c99e4bdcSChris Kirby 4780c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 4781c99e4bdcSChris Kirby 4782c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 4783c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 4784c99e4bdcSChris Kirby m = 1; 4785c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 4786c99e4bdcSChris Kirby last_minor = m; 4787c99e4bdcSChris Kirby return (m); 4788c99e4bdcSChris Kirby } 4789c99e4bdcSChris Kirby } 4790c99e4bdcSChris Kirby 4791c99e4bdcSChris Kirby return (0); 4792c99e4bdcSChris Kirby } 4793c99e4bdcSChris Kirby 4794c99e4bdcSChris Kirby static int 4795c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 4796c99e4bdcSChris Kirby { 4797c99e4bdcSChris Kirby minor_t minor; 4798c99e4bdcSChris Kirby zfs_soft_state_t *zs; 4799c99e4bdcSChris Kirby 4800c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 4801c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 4802c99e4bdcSChris Kirby 4803c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 4804c99e4bdcSChris Kirby if (minor == 0) 4805c99e4bdcSChris Kirby return (ENXIO); 4806c99e4bdcSChris Kirby 4807c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 4808c99e4bdcSChris Kirby return (EAGAIN); 4809c99e4bdcSChris Kirby 4810c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 4811c99e4bdcSChris Kirby 4812c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 4813c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 4814c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 4815c99e4bdcSChris Kirby 4816c99e4bdcSChris Kirby return (0); 4817c99e4bdcSChris Kirby } 4818c99e4bdcSChris Kirby 4819c99e4bdcSChris Kirby static void 4820c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 4821c99e4bdcSChris Kirby { 4822c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 4823c99e4bdcSChris Kirby 4824c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 4825c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 4826c99e4bdcSChris Kirby } 4827c99e4bdcSChris Kirby 4828c99e4bdcSChris Kirby void * 4829c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 4830c99e4bdcSChris Kirby { 4831c99e4bdcSChris Kirby zfs_soft_state_t *zp; 4832c99e4bdcSChris Kirby 4833c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 4834c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 4835c99e4bdcSChris Kirby return (NULL); 4836c99e4bdcSChris Kirby 4837c99e4bdcSChris Kirby return (zp->zss_data); 4838c99e4bdcSChris Kirby } 4839c99e4bdcSChris Kirby 4840c99e4bdcSChris Kirby static int 4841c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 4842c99e4bdcSChris Kirby { 4843c99e4bdcSChris Kirby int error = 0; 4844c99e4bdcSChris Kirby 4845c99e4bdcSChris Kirby if (getminor(*devp) != 0) 4846c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 4847c99e4bdcSChris Kirby 4848c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 4849c99e4bdcSChris Kirby if (flag & FEXCL) { 4850c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 4851c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 4852c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 4853c99e4bdcSChris Kirby } 4854c99e4bdcSChris Kirby 4855c99e4bdcSChris Kirby return (error); 4856c99e4bdcSChris Kirby } 4857c99e4bdcSChris Kirby 4858c99e4bdcSChris Kirby static int 4859c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 4860c99e4bdcSChris Kirby { 4861c99e4bdcSChris Kirby zfs_onexit_t *zo; 4862c99e4bdcSChris Kirby minor_t minor = getminor(dev); 4863c99e4bdcSChris Kirby 4864c99e4bdcSChris Kirby if (minor == 0) 4865c99e4bdcSChris Kirby return (0); 4866c99e4bdcSChris Kirby 4867c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 4868c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 4869c99e4bdcSChris Kirby if (zo == NULL) { 4870c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 4871c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 4872c99e4bdcSChris Kirby } 4873c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 4874c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 4875c99e4bdcSChris Kirby 4876c99e4bdcSChris Kirby return (0); 4877c99e4bdcSChris Kirby } 4878c99e4bdcSChris Kirby 4879fa9e4066Sahrens static int 4880fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 4881fa9e4066Sahrens { 4882fa9e4066Sahrens zfs_cmd_t *zc; 4883fa9e4066Sahrens uint_t vec; 48841d452cf5Sahrens int error, rc; 4885c99e4bdcSChris Kirby minor_t minor = getminor(dev); 4886fa9e4066Sahrens 4887c99e4bdcSChris Kirby if (minor != 0 && 4888c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 4889fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 4890fa9e4066Sahrens 4891fa9e4066Sahrens vec = cmd - ZFS_IOC; 489291ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 4893fa9e4066Sahrens 4894fa9e4066Sahrens if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 4895fa9e4066Sahrens return (EINVAL); 4896fa9e4066Sahrens 4897fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 4898fa9e4066Sahrens 4899478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 49006e27f868SSam Falkner if (error != 0) 49016e27f868SSam Falkner error = EFAULT; 4902fa9e4066Sahrens 4903681d9761SEric Taylor if ((error == 0) && !(flag & FKIOCTL)) 4904ecd6cf80Smarks error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr); 4905fa9e4066Sahrens 4906fa9e4066Sahrens /* 4907fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 4908fa9e4066Sahrens * the lower layers. 4909fa9e4066Sahrens */ 4910fa9e4066Sahrens if (error == 0) { 4911fa9e4066Sahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 4912478ed9adSEric Taylor zc->zc_iflags = flag & FKIOCTL; 4913fa9e4066Sahrens switch (zfs_ioc_vec[vec].zvec_namecheck) { 4914e7437265Sahrens case POOL_NAME: 4915fa9e4066Sahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 4916fa9e4066Sahrens error = EINVAL; 4917*f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 4918*f9af39baSGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck, 4919*f9af39baSGeorge Wilson zfs_ioc_vec[vec].zvec_pool_check); 4920fa9e4066Sahrens break; 4921fa9e4066Sahrens 4922e7437265Sahrens case DATASET_NAME: 4923fa9e4066Sahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 4924fa9e4066Sahrens error = EINVAL; 4925*f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 4926*f9af39baSGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck, 4927*f9af39baSGeorge Wilson zfs_ioc_vec[vec].zvec_pool_check); 4928fa9e4066Sahrens break; 49295ad82045Snd 4930e7437265Sahrens case NO_NAME: 49315ad82045Snd break; 4932fa9e4066Sahrens } 4933fa9e4066Sahrens } 4934fa9e4066Sahrens 4935fa9e4066Sahrens if (error == 0) 4936fa9e4066Sahrens error = zfs_ioc_vec[vec].zvec_func(zc); 4937fa9e4066Sahrens 4938478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 4939ecd6cf80Smarks if (error == 0) { 49406e27f868SSam Falkner if (rc != 0) 49416e27f868SSam Falkner error = EFAULT; 494214843421SMatthew Ahrens if (zfs_ioc_vec[vec].zvec_his_log) 4943ecd6cf80Smarks zfs_log_history(zc); 4944ecd6cf80Smarks } 4945fa9e4066Sahrens 4946fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 4947fa9e4066Sahrens return (error); 4948fa9e4066Sahrens } 4949fa9e4066Sahrens 4950fa9e4066Sahrens static int 4951fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4952fa9e4066Sahrens { 4953fa9e4066Sahrens if (cmd != DDI_ATTACH) 4954fa9e4066Sahrens return (DDI_FAILURE); 4955fa9e4066Sahrens 4956fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 4957fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 4958fa9e4066Sahrens return (DDI_FAILURE); 4959fa9e4066Sahrens 4960fa9e4066Sahrens zfs_dip = dip; 4961fa9e4066Sahrens 4962fa9e4066Sahrens ddi_report_dev(dip); 4963fa9e4066Sahrens 4964fa9e4066Sahrens return (DDI_SUCCESS); 4965fa9e4066Sahrens } 4966fa9e4066Sahrens 4967fa9e4066Sahrens static int 4968fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 4969fa9e4066Sahrens { 4970fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 4971fa9e4066Sahrens return (DDI_FAILURE); 4972fa9e4066Sahrens 4973fa9e4066Sahrens if (cmd != DDI_DETACH) 4974fa9e4066Sahrens return (DDI_FAILURE); 4975fa9e4066Sahrens 4976fa9e4066Sahrens zfs_dip = NULL; 4977fa9e4066Sahrens 4978fa9e4066Sahrens ddi_prop_remove_all(dip); 4979fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 4980fa9e4066Sahrens 4981fa9e4066Sahrens return (DDI_SUCCESS); 4982fa9e4066Sahrens } 4983fa9e4066Sahrens 4984fa9e4066Sahrens /*ARGSUSED*/ 4985fa9e4066Sahrens static int 4986fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 4987fa9e4066Sahrens { 4988fa9e4066Sahrens switch (infocmd) { 4989fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 4990fa9e4066Sahrens *result = zfs_dip; 4991fa9e4066Sahrens return (DDI_SUCCESS); 4992fa9e4066Sahrens 4993fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 4994a0965f35Sbonwick *result = (void *)0; 4995fa9e4066Sahrens return (DDI_SUCCESS); 4996fa9e4066Sahrens } 4997fa9e4066Sahrens 4998fa9e4066Sahrens return (DDI_FAILURE); 4999fa9e4066Sahrens } 5000fa9e4066Sahrens 5001fa9e4066Sahrens /* 5002fa9e4066Sahrens * OK, so this is a little weird. 5003fa9e4066Sahrens * 5004fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 5005fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 5006fa9e4066Sahrens * 5007fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 5008fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 5009fa9e4066Sahrens */ 5010fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 5011c99e4bdcSChris Kirby zfsdev_open, /* open */ 5012c99e4bdcSChris Kirby zfsdev_close, /* close */ 5013fa9e4066Sahrens zvol_strategy, /* strategy */ 5014fa9e4066Sahrens nodev, /* print */ 5015e7cbe64fSgw zvol_dump, /* dump */ 5016fa9e4066Sahrens zvol_read, /* read */ 5017fa9e4066Sahrens zvol_write, /* write */ 5018fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 5019fa9e4066Sahrens nodev, /* devmap */ 5020fa9e4066Sahrens nodev, /* mmap */ 5021fa9e4066Sahrens nodev, /* segmap */ 5022fa9e4066Sahrens nochpoll, /* poll */ 5023fa9e4066Sahrens ddi_prop_op, /* prop_op */ 5024fa9e4066Sahrens NULL, /* streamtab */ 5025fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 5026fa9e4066Sahrens CB_REV, /* version */ 5027feb08c6bSbillm nodev, /* async read */ 5028feb08c6bSbillm nodev, /* async write */ 5029fa9e4066Sahrens }; 5030fa9e4066Sahrens 5031fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 5032fa9e4066Sahrens DEVO_REV, /* version */ 5033fa9e4066Sahrens 0, /* refcnt */ 5034fa9e4066Sahrens zfs_info, /* info */ 5035fa9e4066Sahrens nulldev, /* identify */ 5036fa9e4066Sahrens nulldev, /* probe */ 5037fa9e4066Sahrens zfs_attach, /* attach */ 5038fa9e4066Sahrens zfs_detach, /* detach */ 5039fa9e4066Sahrens nodev, /* reset */ 5040fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 504119397407SSherry Moore NULL, /* no bus operations */ 504219397407SSherry Moore NULL, /* power */ 504319397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 5044fa9e4066Sahrens }; 5045fa9e4066Sahrens 5046fa9e4066Sahrens static struct modldrv zfs_modldrv = { 504719397407SSherry Moore &mod_driverops, 504819397407SSherry Moore "ZFS storage pool", 504919397407SSherry Moore &zfs_dev_ops 5050fa9e4066Sahrens }; 5051fa9e4066Sahrens 5052fa9e4066Sahrens static struct modlinkage modlinkage = { 5053fa9e4066Sahrens MODREV_1, 5054fa9e4066Sahrens (void *)&zfs_modlfs, 5055fa9e4066Sahrens (void *)&zfs_modldrv, 5056fa9e4066Sahrens NULL 5057fa9e4066Sahrens }; 5058fa9e4066Sahrens 5059ec533521Sfr 5060ec533521Sfr uint_t zfs_fsyncer_key; 5061f18faf3fSek extern uint_t rrw_tsd_key; 5062ec533521Sfr 5063fa9e4066Sahrens int 5064fa9e4066Sahrens _init(void) 5065fa9e4066Sahrens { 5066fa9e4066Sahrens int error; 5067fa9e4066Sahrens 5068a0965f35Sbonwick spa_init(FREAD | FWRITE); 5069a0965f35Sbonwick zfs_init(); 5070a0965f35Sbonwick zvol_init(); 5071a0965f35Sbonwick 5072a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 5073a0965f35Sbonwick zvol_fini(); 5074a0965f35Sbonwick zfs_fini(); 5075a0965f35Sbonwick spa_fini(); 5076fa9e4066Sahrens return (error); 5077a0965f35Sbonwick } 5078fa9e4066Sahrens 5079ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 5080f18faf3fSek tsd_create(&rrw_tsd_key, NULL); 5081ec533521Sfr 5082fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 5083fa9e4066Sahrens ASSERT(error == 0); 5084ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 5085fa9e4066Sahrens 5086fa9e4066Sahrens return (0); 5087fa9e4066Sahrens } 5088fa9e4066Sahrens 5089fa9e4066Sahrens int 5090fa9e4066Sahrens _fini(void) 5091fa9e4066Sahrens { 5092fa9e4066Sahrens int error; 5093fa9e4066Sahrens 5094ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 5095fa9e4066Sahrens return (EBUSY); 5096fa9e4066Sahrens 5097fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 5098fa9e4066Sahrens return (error); 5099fa9e4066Sahrens 5100fa9e4066Sahrens zvol_fini(); 5101fa9e4066Sahrens zfs_fini(); 5102fa9e4066Sahrens spa_fini(); 5103da6c28aaSamw if (zfs_nfsshare_inited) 5104ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 5105da6c28aaSamw if (zfs_smbshare_inited) 5106da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 5107da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 5108ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 5109fa9e4066Sahrens 5110ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 5111fa9e4066Sahrens ldi_ident_release(zfs_li); 5112fa9e4066Sahrens zfs_li = NULL; 5113ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 5114fa9e4066Sahrens 5115fa9e4066Sahrens return (error); 5116fa9e4066Sahrens } 5117fa9e4066Sahrens 5118fa9e4066Sahrens int 5119fa9e4066Sahrens _info(struct modinfo *modinfop) 5120fa9e4066Sahrens { 5121fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 5122fa9e4066Sahrens } 5123