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 /* 22379c004dSEric Schrock * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23fa9e4066Sahrens * Use is subject to license terms. 24fa9e4066Sahrens */ 25fa9e4066Sahrens 26fa9e4066Sahrens #include <sys/types.h> 27fa9e4066Sahrens #include <sys/param.h> 28fa9e4066Sahrens #include <sys/errno.h> 29fa9e4066Sahrens #include <sys/uio.h> 30fa9e4066Sahrens #include <sys/buf.h> 31fa9e4066Sahrens #include <sys/modctl.h> 32fa9e4066Sahrens #include <sys/open.h> 33fa9e4066Sahrens #include <sys/file.h> 34fa9e4066Sahrens #include <sys/kmem.h> 35fa9e4066Sahrens #include <sys/conf.h> 36fa9e4066Sahrens #include <sys/cmn_err.h> 37fa9e4066Sahrens #include <sys/stat.h> 38fa9e4066Sahrens #include <sys/zfs_ioctl.h> 394201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 40da6c28aaSamw #include <sys/zfs_znode.h> 41fa9e4066Sahrens #include <sys/zap.h> 42fa9e4066Sahrens #include <sys/spa.h> 43b1b8ab34Slling #include <sys/spa_impl.h> 44fa9e4066Sahrens #include <sys/vdev.h> 454201a95eSRic Aleshire #include <sys/priv_impl.h> 46fa9e4066Sahrens #include <sys/dmu.h> 47fa9e4066Sahrens #include <sys/dsl_dir.h> 48fa9e4066Sahrens #include <sys/dsl_dataset.h> 49fa9e4066Sahrens #include <sys/dsl_prop.h> 50ecd6cf80Smarks #include <sys/dsl_deleg.h> 51ecd6cf80Smarks #include <sys/dmu_objset.h> 52fa9e4066Sahrens #include <sys/ddi.h> 53fa9e4066Sahrens #include <sys/sunddi.h> 54fa9e4066Sahrens #include <sys/sunldi.h> 55fa9e4066Sahrens #include <sys/policy.h> 56fa9e4066Sahrens #include <sys/zone.h> 57fa9e4066Sahrens #include <sys/nvpair.h> 58fa9e4066Sahrens #include <sys/pathname.h> 59fa9e4066Sahrens #include <sys/mount.h> 60fa9e4066Sahrens #include <sys/sdt.h> 61fa9e4066Sahrens #include <sys/fs/zfs.h> 62fa9e4066Sahrens #include <sys/zfs_ctldir.h> 63da6c28aaSamw #include <sys/zfs_dir.h> 64a2eea2e1Sahrens #include <sys/zvol.h> 65ecd6cf80Smarks #include <sharefs/share.h> 66f18faf3fSek #include <sys/dmu_objset.h> 67fa9e4066Sahrens 68fa9e4066Sahrens #include "zfs_namecheck.h" 69e9dbad6fSeschrock #include "zfs_prop.h" 70ecd6cf80Smarks #include "zfs_deleg.h" 71fa9e4066Sahrens 72fa9e4066Sahrens extern struct modlfs zfs_modlfs; 73fa9e4066Sahrens 74fa9e4066Sahrens extern void zfs_init(void); 75fa9e4066Sahrens extern void zfs_fini(void); 76fa9e4066Sahrens 77fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 78fa9e4066Sahrens dev_info_t *zfs_dip; 79fa9e4066Sahrens 80fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *); 81ecd6cf80Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *); 82fa9e4066Sahrens 8354d692b7SGeorge Wilson typedef enum { 8454d692b7SGeorge Wilson NO_NAME, 8554d692b7SGeorge Wilson POOL_NAME, 8654d692b7SGeorge Wilson DATASET_NAME 8754d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 8854d692b7SGeorge Wilson 89fa9e4066Sahrens typedef struct zfs_ioc_vec { 90fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 91fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 9254d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 93ecd6cf80Smarks boolean_t zvec_his_log; 9454d692b7SGeorge Wilson boolean_t zvec_pool_check; 95fa9e4066Sahrens } zfs_ioc_vec_t; 96fa9e4066Sahrens 9714843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 9814843421SMatthew Ahrens static const char *userquota_perms[] = { 9914843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 10014843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 10114843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 10214843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 10314843421SMatthew Ahrens }; 10414843421SMatthew Ahrens 10514843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 106*92241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 107*92241e0bSTom Erickson cred_t *cr); 108*92241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 109*92241e0bSTom Erickson nvlist_t **errors); 1100a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 1110a48a24eStimh boolean_t *); 112*92241e0bSTom Erickson int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **); 1130a48a24eStimh 114fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 115fa9e4066Sahrens void 116fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 117fa9e4066Sahrens { 118fa9e4066Sahrens const char *newfile; 119fa9e4066Sahrens char buf[256]; 120fa9e4066Sahrens va_list adx; 121fa9e4066Sahrens 122fa9e4066Sahrens /* 123fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 124fa9e4066Sahrens */ 125fa9e4066Sahrens newfile = strrchr(file, '/'); 126fa9e4066Sahrens if (newfile != NULL) { 127fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 128fa9e4066Sahrens } else { 129fa9e4066Sahrens newfile = file; 130fa9e4066Sahrens } 131fa9e4066Sahrens 132fa9e4066Sahrens va_start(adx, fmt); 133fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 134fa9e4066Sahrens va_end(adx); 135fa9e4066Sahrens 136fa9e4066Sahrens /* 137fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 138fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 139fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 140fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 141fa9e4066Sahrens * arg0 = file name 142fa9e4066Sahrens * arg1 = function name 143fa9e4066Sahrens * arg2 = line number 144fa9e4066Sahrens * arg3 = message 145fa9e4066Sahrens */ 146fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 147fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 148fa9e4066Sahrens } 149fa9e4066Sahrens 150ecd6cf80Smarks static void 151228975ccSek history_str_free(char *buf) 152228975ccSek { 153228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 154228975ccSek } 155228975ccSek 156228975ccSek static char * 157228975ccSek history_str_get(zfs_cmd_t *zc) 158ecd6cf80Smarks { 15940feaa91Sahrens char *buf; 160ecd6cf80Smarks 161ecd6cf80Smarks if (zc->zc_history == NULL) 162228975ccSek return (NULL); 163e7437265Sahrens 164ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 165ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 166ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 167228975ccSek history_str_free(buf); 168228975ccSek return (NULL); 169ecd6cf80Smarks } 170ecd6cf80Smarks 171ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 172ecd6cf80Smarks 173228975ccSek return (buf); 174228975ccSek } 175ecd6cf80Smarks 17615e6edf1Sgw /* 17715e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 17815e6edf1Sgw */ 17915e6edf1Sgw static boolean_t 18015e6edf1Sgw zfs_is_bootfs(const char *name) 18115e6edf1Sgw { 182503ad85cSMatthew Ahrens objset_t *os; 18315e6edf1Sgw 184503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 185503ad85cSMatthew Ahrens boolean_t ret; 186b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 187503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 188503ad85cSMatthew Ahrens return (ret); 18915e6edf1Sgw } 190503ad85cSMatthew Ahrens return (B_FALSE); 19115e6edf1Sgw } 19215e6edf1Sgw 193c2a93d44Stimh /* 1940a48a24eStimh * zfs_earlier_version 195c2a93d44Stimh * 196c2a93d44Stimh * Return non-zero if the spa version is less than requested version. 197c2a93d44Stimh */ 198da6c28aaSamw static int 1990a48a24eStimh zfs_earlier_version(const char *name, int version) 200da6c28aaSamw { 201da6c28aaSamw spa_t *spa; 202da6c28aaSamw 203da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 204da6c28aaSamw if (spa_version(spa) < version) { 205da6c28aaSamw spa_close(spa, FTAG); 206da6c28aaSamw return (1); 207da6c28aaSamw } 208da6c28aaSamw spa_close(spa, FTAG); 209da6c28aaSamw } 210da6c28aaSamw return (0); 211da6c28aaSamw } 212da6c28aaSamw 2139e6eda55Smarks /* 214745cd3c5Smaybee * zpl_earlier_version 2159e6eda55Smarks * 216745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 2179e6eda55Smarks */ 218745cd3c5Smaybee static boolean_t 219745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 2209e6eda55Smarks { 2219e6eda55Smarks objset_t *os; 222745cd3c5Smaybee boolean_t rc = B_TRUE; 2239e6eda55Smarks 224503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 225745cd3c5Smaybee uint64_t zplversion; 2269e6eda55Smarks 227503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 228503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 229503ad85cSMatthew Ahrens return (B_TRUE); 230503ad85cSMatthew Ahrens } 231503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 232745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 233745cd3c5Smaybee rc = zplversion < version; 234503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2359e6eda55Smarks } 2369e6eda55Smarks return (rc); 2379e6eda55Smarks } 2389e6eda55Smarks 239228975ccSek static void 240228975ccSek zfs_log_history(zfs_cmd_t *zc) 241228975ccSek { 242228975ccSek spa_t *spa; 243228975ccSek char *buf; 244ecd6cf80Smarks 245228975ccSek if ((buf = history_str_get(zc)) == NULL) 246228975ccSek return; 247228975ccSek 248228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 249228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 250228975ccSek (void) spa_history_log(spa, buf, LOG_CMD_NORMAL); 251228975ccSek spa_close(spa, FTAG); 252228975ccSek } 253228975ccSek history_str_free(buf); 254ecd6cf80Smarks } 255ecd6cf80Smarks 256fa9e4066Sahrens /* 257fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 258fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 259fa9e4066Sahrens */ 260fa9e4066Sahrens /* ARGSUSED */ 261fa9e4066Sahrens static int 262ecd6cf80Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr) 263fa9e4066Sahrens { 264fa9e4066Sahrens return (0); 265fa9e4066Sahrens } 266fa9e4066Sahrens 267fa9e4066Sahrens /* 268fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 269fa9e4066Sahrens * no privileges, but must be visible in the local zone. 270fa9e4066Sahrens */ 271fa9e4066Sahrens /* ARGSUSED */ 272fa9e4066Sahrens static int 273ecd6cf80Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr) 274fa9e4066Sahrens { 275fa9e4066Sahrens if (INGLOBALZONE(curproc) || 276ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 277fa9e4066Sahrens return (0); 278fa9e4066Sahrens 279fa9e4066Sahrens return (ENOENT); 280fa9e4066Sahrens } 281fa9e4066Sahrens 282fa9e4066Sahrens static int 283fa9e4066Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr) 284fa9e4066Sahrens { 285fa9e4066Sahrens uint64_t zoned; 286fa9e4066Sahrens int writable = 1; 287fa9e4066Sahrens 288fa9e4066Sahrens /* 289fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 290fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 291fa9e4066Sahrens */ 292fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 293fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 294fa9e4066Sahrens return (ENOENT); 295fa9e4066Sahrens 296fa9e4066Sahrens if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 297fa9e4066Sahrens return (ENOENT); 298fa9e4066Sahrens 299fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 300fa9e4066Sahrens /* 301fa9e4066Sahrens * If the fs is zoned, only root can access it from the 302fa9e4066Sahrens * global zone. 303fa9e4066Sahrens */ 304fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 305fa9e4066Sahrens return (EPERM); 306fa9e4066Sahrens } else { 307fa9e4066Sahrens /* 308fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 309fa9e4066Sahrens */ 310fa9e4066Sahrens if (!zoned) 311fa9e4066Sahrens return (EPERM); 312fa9e4066Sahrens 313fa9e4066Sahrens /* must be writable by this zone */ 314fa9e4066Sahrens if (!writable) 315fa9e4066Sahrens return (EPERM); 316fa9e4066Sahrens } 317fa9e4066Sahrens return (0); 318fa9e4066Sahrens } 319fa9e4066Sahrens 320fa9e4066Sahrens int 321ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 322fa9e4066Sahrens { 323fa9e4066Sahrens int error; 324fa9e4066Sahrens 325ecd6cf80Smarks error = zfs_dozonecheck(name, cr); 326ecd6cf80Smarks if (error == 0) { 327ecd6cf80Smarks error = secpolicy_zfs(cr); 328db870a07Sahrens if (error) 329ecd6cf80Smarks error = dsl_deleg_access(name, perm, cr); 330ecd6cf80Smarks } 331ecd6cf80Smarks return (error); 332ecd6cf80Smarks } 333ecd6cf80Smarks 3344201a95eSRic Aleshire /* 3354201a95eSRic Aleshire * Policy for setting the security label property. 3364201a95eSRic Aleshire * 3374201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 3384201a95eSRic Aleshire */ 3394201a95eSRic Aleshire static int 340*92241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 3414201a95eSRic Aleshire { 3424201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 3434201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 3444201a95eSRic Aleshire boolean_t new_default = FALSE; 3454201a95eSRic Aleshire uint64_t zoned; 3464201a95eSRic Aleshire int needed_priv = -1; 3474201a95eSRic Aleshire int error; 3484201a95eSRic Aleshire 3494201a95eSRic Aleshire /* First get the existing dataset label. */ 3504201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 3514201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 3524201a95eSRic Aleshire if (error) 3534201a95eSRic Aleshire return (EPERM); 3544201a95eSRic Aleshire 3554201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 3564201a95eSRic Aleshire new_default = TRUE; 3574201a95eSRic Aleshire 3584201a95eSRic Aleshire /* The label must be translatable */ 3594201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 3604201a95eSRic Aleshire return (EINVAL); 3614201a95eSRic Aleshire 3624201a95eSRic Aleshire /* 3634201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 3644201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 3654201a95eSRic Aleshire * are needed. 3664201a95eSRic Aleshire */ 3674201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 3684201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 3694201a95eSRic Aleshire return (EPERM); 3704201a95eSRic Aleshire return (0); 3714201a95eSRic Aleshire } 3724201a95eSRic Aleshire 3734201a95eSRic Aleshire /* 3744201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 3754201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 3764201a95eSRic Aleshire * global zone. 3774201a95eSRic Aleshire */ 3784201a95eSRic Aleshire if (dsl_prop_get_integer(name, 3794201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 3804201a95eSRic Aleshire return (EPERM); 3814201a95eSRic Aleshire if (!zoned) { 3824201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 3834201a95eSRic Aleshire return (EPERM); 3844201a95eSRic Aleshire } 3854201a95eSRic Aleshire 3864201a95eSRic Aleshire /* 3874201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 3884201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 3894201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 3904201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 3914201a95eSRic Aleshire */ 3924201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 393*92241e0bSTom Erickson objset_t *os; 394*92241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 395*92241e0bSTom Erickson 3964201a95eSRic Aleshire /* 3974201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 3984201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 3994201a95eSRic Aleshire */ 4004201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 401*92241e0bSTom Erickson setsl_tag, &os); 4024201a95eSRic Aleshire if (error) 4034201a95eSRic Aleshire return (EPERM); 4044201a95eSRic Aleshire 405*92241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 406*92241e0bSTom Erickson 4074201a95eSRic Aleshire if (new_default) { 4084201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 4094201a95eSRic Aleshire goto out_check; 4104201a95eSRic Aleshire } 4114201a95eSRic Aleshire 4124201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 4134201a95eSRic Aleshire return (EPERM); 4144201a95eSRic Aleshire 4154201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 4164201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 4174201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 4184201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 4194201a95eSRic Aleshire } else { 4204201a95eSRic Aleshire /* dataset currently has a default label */ 4214201a95eSRic Aleshire if (!new_default) 4224201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 4234201a95eSRic Aleshire } 4244201a95eSRic Aleshire 4254201a95eSRic Aleshire out_check: 4264201a95eSRic Aleshire if (needed_priv != -1) 4274201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 4284201a95eSRic Aleshire return (0); 4294201a95eSRic Aleshire } 4304201a95eSRic Aleshire 431ecd6cf80Smarks static int 432*92241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 433*92241e0bSTom Erickson cred_t *cr) 434ecd6cf80Smarks { 435*92241e0bSTom Erickson char *strval; 436*92241e0bSTom Erickson 437ecd6cf80Smarks /* 438ecd6cf80Smarks * Check permissions for special properties. 439ecd6cf80Smarks */ 440ecd6cf80Smarks switch (prop) { 441ecd6cf80Smarks case ZFS_PROP_ZONED: 442ecd6cf80Smarks /* 443ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 444ecd6cf80Smarks */ 445ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 446ecd6cf80Smarks return (EPERM); 447ecd6cf80Smarks break; 448ecd6cf80Smarks 449ecd6cf80Smarks case ZFS_PROP_QUOTA: 450ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 451ecd6cf80Smarks uint64_t zoned; 452ecd6cf80Smarks char setpoint[MAXNAMELEN]; 453ecd6cf80Smarks /* 454ecd6cf80Smarks * Unprivileged users are allowed to modify the 455ecd6cf80Smarks * quota on things *under* (ie. contained by) 456ecd6cf80Smarks * the thing they own. 457ecd6cf80Smarks */ 458*92241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 459ecd6cf80Smarks setpoint)) 460ecd6cf80Smarks return (EPERM); 461*92241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 462ecd6cf80Smarks return (EPERM); 463ecd6cf80Smarks } 464db870a07Sahrens break; 4654201a95eSRic Aleshire 4664201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 4674201a95eSRic Aleshire if (!is_system_labeled()) 4684201a95eSRic Aleshire return (EPERM); 469*92241e0bSTom Erickson 470*92241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 471*92241e0bSTom Erickson int err; 472*92241e0bSTom Erickson 473*92241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 474*92241e0bSTom Erickson if (err != 0) 475*92241e0bSTom Erickson return (err); 476*92241e0bSTom Erickson } 4774201a95eSRic Aleshire break; 478ecd6cf80Smarks } 479ecd6cf80Smarks 480*92241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 481ecd6cf80Smarks } 482ecd6cf80Smarks 483ecd6cf80Smarks int 484ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr) 485ecd6cf80Smarks { 486ecd6cf80Smarks int error; 487ecd6cf80Smarks 488ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 489ecd6cf80Smarks if (error) 490fa9e4066Sahrens return (error); 491fa9e4066Sahrens 492ecd6cf80Smarks /* 493ecd6cf80Smarks * permission to set permissions will be evaluated later in 494ecd6cf80Smarks * dsl_deleg_can_allow() 495ecd6cf80Smarks */ 496ecd6cf80Smarks return (0); 497ecd6cf80Smarks } 498ecd6cf80Smarks 499ecd6cf80Smarks int 500ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr) 501ecd6cf80Smarks { 502681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 503681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 504ecd6cf80Smarks } 505ecd6cf80Smarks 506ecd6cf80Smarks int 507ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr) 508ecd6cf80Smarks { 509ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 510ecd6cf80Smarks ZFS_DELEG_PERM_SEND, cr)); 511ecd6cf80Smarks } 512ecd6cf80Smarks 513743a77edSAlan Wright static int 514743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr) 515743a77edSAlan Wright { 516743a77edSAlan Wright vnode_t *vp; 517743a77edSAlan Wright int error; 518743a77edSAlan Wright 519743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 520743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 521743a77edSAlan Wright return (error); 522743a77edSAlan Wright 523743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 524743a77edSAlan Wright 525743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 526743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 527743a77edSAlan Wright zc->zc_name) != 0)) { 528743a77edSAlan Wright VN_RELE(vp); 529743a77edSAlan Wright return (EPERM); 530743a77edSAlan Wright } 531743a77edSAlan Wright 532743a77edSAlan Wright VN_RELE(vp); 533743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 534743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 535743a77edSAlan Wright } 536743a77edSAlan Wright 537ecd6cf80Smarks int 538ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr) 539ecd6cf80Smarks { 540ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 541ecd6cf80Smarks return (EPERM); 542ecd6cf80Smarks 5433cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 544ecd6cf80Smarks return (0); 545ecd6cf80Smarks } else { 546743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 547743a77edSAlan Wright } 548743a77edSAlan Wright } 549ecd6cf80Smarks 550743a77edSAlan Wright int 551743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr) 552743a77edSAlan Wright { 553743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 554743a77edSAlan Wright return (EPERM); 555ecd6cf80Smarks 556743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 557743a77edSAlan Wright return (0); 558743a77edSAlan Wright } else { 559743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 560ecd6cf80Smarks } 561fa9e4066Sahrens } 562fa9e4066Sahrens 563fa9e4066Sahrens static int 564ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 565fa9e4066Sahrens { 566fa9e4066Sahrens char *cp; 567fa9e4066Sahrens 568fa9e4066Sahrens /* 569fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 570fa9e4066Sahrens */ 571ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 572ecd6cf80Smarks cp = strrchr(parent, '@'); 573fa9e4066Sahrens if (cp != NULL) { 574fa9e4066Sahrens cp[0] = '\0'; 575fa9e4066Sahrens } else { 576ecd6cf80Smarks cp = strrchr(parent, '/'); 577fa9e4066Sahrens if (cp == NULL) 578fa9e4066Sahrens return (ENOENT); 579fa9e4066Sahrens cp[0] = '\0'; 580ecd6cf80Smarks } 581ecd6cf80Smarks 582ecd6cf80Smarks return (0); 583ecd6cf80Smarks } 584ecd6cf80Smarks 585ecd6cf80Smarks int 586ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 587ecd6cf80Smarks { 588ecd6cf80Smarks int error; 589ecd6cf80Smarks 590ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 591ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 592ecd6cf80Smarks return (error); 593ecd6cf80Smarks 594ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 595ecd6cf80Smarks } 596ecd6cf80Smarks 597ecd6cf80Smarks static int 598ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr) 599ecd6cf80Smarks { 600ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 601ecd6cf80Smarks } 602ecd6cf80Smarks 603ecd6cf80Smarks /* 604ecd6cf80Smarks * Must have sys_config privilege to check the iscsi permission 605ecd6cf80Smarks */ 606ecd6cf80Smarks /* ARGSUSED */ 607ecd6cf80Smarks static int 608ecd6cf80Smarks zfs_secpolicy_iscsi(zfs_cmd_t *zc, cred_t *cr) 609ecd6cf80Smarks { 610ecd6cf80Smarks return (secpolicy_zfs(cr)); 611ecd6cf80Smarks } 612ecd6cf80Smarks 613ecd6cf80Smarks int 614ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 615ecd6cf80Smarks { 616*92241e0bSTom Erickson char parentname[MAXNAMELEN]; 617ecd6cf80Smarks int error; 618ecd6cf80Smarks 619ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 620ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 621ecd6cf80Smarks return (error); 622ecd6cf80Smarks 623ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 624ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 625ecd6cf80Smarks return (error); 626ecd6cf80Smarks 627ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 628ecd6cf80Smarks sizeof (parentname))) != 0) 629ecd6cf80Smarks return (error); 630ecd6cf80Smarks 631ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 632ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 633ecd6cf80Smarks return (error); 634ecd6cf80Smarks 635ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 636ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 637ecd6cf80Smarks return (error); 638ecd6cf80Smarks 639ecd6cf80Smarks return (error); 640ecd6cf80Smarks } 641ecd6cf80Smarks 642ecd6cf80Smarks static int 643ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr) 644ecd6cf80Smarks { 645ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 646ecd6cf80Smarks } 647ecd6cf80Smarks 648ecd6cf80Smarks static int 649ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr) 650ecd6cf80Smarks { 651*92241e0bSTom Erickson char parentname[MAXNAMELEN]; 652ecd6cf80Smarks objset_t *clone; 653ecd6cf80Smarks int error; 654ecd6cf80Smarks 655ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 656ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 657ecd6cf80Smarks if (error) 658ecd6cf80Smarks return (error); 659ecd6cf80Smarks 660503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &clone); 661ecd6cf80Smarks 662ecd6cf80Smarks if (error == 0) { 663ecd6cf80Smarks dsl_dataset_t *pclone = NULL; 664ecd6cf80Smarks dsl_dir_t *dd; 665503ad85cSMatthew Ahrens dd = clone->os_dsl_dataset->ds_dir; 666ecd6cf80Smarks 667ecd6cf80Smarks rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 668745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 669745cd3c5Smaybee dd->dd_phys->dd_origin_obj, FTAG, &pclone); 670ecd6cf80Smarks rw_exit(&dd->dd_pool->dp_config_rwlock); 671ecd6cf80Smarks if (error) { 672503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 673ecd6cf80Smarks return (error); 674ecd6cf80Smarks } 675ecd6cf80Smarks 676ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 677ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 678ecd6cf80Smarks 679ecd6cf80Smarks dsl_dataset_name(pclone, parentname); 680503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 681745cd3c5Smaybee dsl_dataset_rele(pclone, FTAG); 682ecd6cf80Smarks if (error == 0) 683ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 684ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 685ecd6cf80Smarks } 686ecd6cf80Smarks return (error); 687ecd6cf80Smarks } 688ecd6cf80Smarks 689ecd6cf80Smarks static int 690ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr) 691ecd6cf80Smarks { 692ecd6cf80Smarks int error; 693ecd6cf80Smarks 694ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 695ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 696ecd6cf80Smarks return (error); 697ecd6cf80Smarks 698ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 699ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 700ecd6cf80Smarks return (error); 701ecd6cf80Smarks 702ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 703ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 704ecd6cf80Smarks } 705ecd6cf80Smarks 706ecd6cf80Smarks int 707ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 708ecd6cf80Smarks { 709681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 710681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 711ecd6cf80Smarks } 712ecd6cf80Smarks 713ecd6cf80Smarks static int 714ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr) 715ecd6cf80Smarks { 716ecd6cf80Smarks 717ecd6cf80Smarks return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr)); 718ecd6cf80Smarks } 719ecd6cf80Smarks 720ecd6cf80Smarks static int 721ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr) 722ecd6cf80Smarks { 723*92241e0bSTom Erickson char parentname[MAXNAMELEN]; 724*92241e0bSTom Erickson int error; 725ecd6cf80Smarks 726ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 727ecd6cf80Smarks sizeof (parentname))) != 0) 728ecd6cf80Smarks return (error); 729fa9e4066Sahrens 730ecd6cf80Smarks if (zc->zc_value[0] != '\0') { 731ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_value, 732ecd6cf80Smarks ZFS_DELEG_PERM_CLONE, cr)) != 0) 733ecd6cf80Smarks return (error); 734fa9e4066Sahrens } 735fa9e4066Sahrens 736ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 737ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 738ecd6cf80Smarks return (error); 739ecd6cf80Smarks 740ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 741ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 742ecd6cf80Smarks 743ecd6cf80Smarks return (error); 744ecd6cf80Smarks } 745ecd6cf80Smarks 746ecd6cf80Smarks static int 747ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr) 748ecd6cf80Smarks { 749ecd6cf80Smarks int error; 750ecd6cf80Smarks 751ecd6cf80Smarks error = secpolicy_fs_unmount(cr, NULL); 752ecd6cf80Smarks if (error) { 753ecd6cf80Smarks error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr); 754ecd6cf80Smarks } 755ecd6cf80Smarks return (error); 756fa9e4066Sahrens } 757fa9e4066Sahrens 758fa9e4066Sahrens /* 759fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 760fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 761fa9e4066Sahrens */ 762fa9e4066Sahrens /* ARGSUSED */ 763fa9e4066Sahrens static int 764ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr) 765fa9e4066Sahrens { 766fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 767fa9e4066Sahrens return (EPERM); 768fa9e4066Sahrens 769fa9e4066Sahrens return (0); 770fa9e4066Sahrens } 771fa9e4066Sahrens 772ea8dc4b6Seschrock /* 773ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 774ea8dc4b6Seschrock */ 775ea8dc4b6Seschrock /* ARGSUSED */ 776ea8dc4b6Seschrock static int 777ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr) 778ea8dc4b6Seschrock { 779ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 780ea8dc4b6Seschrock } 781ea8dc4b6Seschrock 782e45ce728Sahrens static int 783e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr) 784e45ce728Sahrens { 785e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 786e45ce728Sahrens 787990b4856Slling if (prop == ZPROP_INVAL) { 788e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 789e45ce728Sahrens return (EINVAL); 790e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 791e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 792e45ce728Sahrens } else { 793*92241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 794*92241e0bSTom Erickson NULL, cr)); 795e45ce728Sahrens } 796e45ce728Sahrens } 797e45ce728Sahrens 79814843421SMatthew Ahrens static int 79914843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr) 80014843421SMatthew Ahrens { 80114843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 80214843421SMatthew Ahrens if (err) 80314843421SMatthew Ahrens return (err); 80414843421SMatthew Ahrens 80514843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 80614843421SMatthew Ahrens return (EINVAL); 80714843421SMatthew Ahrens 80814843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 80914843421SMatthew Ahrens /* 81014843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 81114843421SMatthew Ahrens * themself, allow it. 81214843421SMatthew Ahrens */ 81314843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 81414843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 81514843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 81614843421SMatthew Ahrens return (0); 81714843421SMatthew Ahrens } else { 81814843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 81914843421SMatthew Ahrens return (0); 82014843421SMatthew Ahrens } 82114843421SMatthew Ahrens } 82214843421SMatthew Ahrens 82314843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 82414843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 82514843421SMatthew Ahrens } 82614843421SMatthew Ahrens 82714843421SMatthew Ahrens static int 82814843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr) 82914843421SMatthew Ahrens { 83014843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 83114843421SMatthew Ahrens if (err) 83214843421SMatthew Ahrens return (err); 83314843421SMatthew Ahrens 83414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 83514843421SMatthew Ahrens return (EINVAL); 83614843421SMatthew Ahrens 83714843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 83814843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 83914843421SMatthew Ahrens } 84014843421SMatthew Ahrens 84114843421SMatthew Ahrens static int 84214843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr) 84314843421SMatthew Ahrens { 844*92241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 845*92241e0bSTom Erickson NULL, cr)); 84614843421SMatthew Ahrens } 84714843421SMatthew Ahrens 848842727c2SChris Kirby static int 849842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr) 850842727c2SChris Kirby { 851842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 852842727c2SChris Kirby ZFS_DELEG_PERM_HOLD, cr)); 853842727c2SChris Kirby } 854842727c2SChris Kirby 855842727c2SChris Kirby static int 856842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr) 857842727c2SChris Kirby { 858842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 859842727c2SChris Kirby ZFS_DELEG_PERM_RELEASE, cr)); 860842727c2SChris Kirby } 861842727c2SChris Kirby 862fa9e4066Sahrens /* 863fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 864fa9e4066Sahrens */ 865fa9e4066Sahrens static int 866478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 867fa9e4066Sahrens { 868fa9e4066Sahrens char *packed; 869fa9e4066Sahrens int error; 870990b4856Slling nvlist_t *list = NULL; 871fa9e4066Sahrens 872fa9e4066Sahrens /* 873e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 874fa9e4066Sahrens */ 875990b4856Slling if (size == 0) 876fa9e4066Sahrens return (EINVAL); 877fa9e4066Sahrens 878fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 879fa9e4066Sahrens 880478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 881478ed9adSEric Taylor iflag)) != 0) { 882fa9e4066Sahrens kmem_free(packed, size); 883fa9e4066Sahrens return (error); 884fa9e4066Sahrens } 885fa9e4066Sahrens 886990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 887fa9e4066Sahrens kmem_free(packed, size); 888fa9e4066Sahrens return (error); 889fa9e4066Sahrens } 890fa9e4066Sahrens 891fa9e4066Sahrens kmem_free(packed, size); 892fa9e4066Sahrens 893990b4856Slling *nvp = list; 894fa9e4066Sahrens return (0); 895fa9e4066Sahrens } 896fa9e4066Sahrens 897*92241e0bSTom Erickson static int 898*92241e0bSTom Erickson fit_error_list(zfs_cmd_t *zc, nvlist_t **errors) 899*92241e0bSTom Erickson { 900*92241e0bSTom Erickson size_t size; 901*92241e0bSTom Erickson 902*92241e0bSTom Erickson VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0); 903*92241e0bSTom Erickson 904*92241e0bSTom Erickson if (size > zc->zc_nvlist_dst_size) { 905*92241e0bSTom Erickson nvpair_t *more_errors; 906*92241e0bSTom Erickson int n = 0; 907*92241e0bSTom Erickson 908*92241e0bSTom Erickson if (zc->zc_nvlist_dst_size < 1024) 909*92241e0bSTom Erickson return (ENOMEM); 910*92241e0bSTom Erickson 911*92241e0bSTom Erickson VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0); 912*92241e0bSTom Erickson more_errors = nvlist_prev_nvpair(*errors, NULL); 913*92241e0bSTom Erickson 914*92241e0bSTom Erickson do { 915*92241e0bSTom Erickson nvpair_t *pair = nvlist_prev_nvpair(*errors, 916*92241e0bSTom Erickson more_errors); 917*92241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(*errors, pair) == 0); 918*92241e0bSTom Erickson n++; 919*92241e0bSTom Erickson VERIFY(nvlist_size(*errors, &size, 920*92241e0bSTom Erickson NV_ENCODE_NATIVE) == 0); 921*92241e0bSTom Erickson } while (size > zc->zc_nvlist_dst_size); 922*92241e0bSTom Erickson 923*92241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0); 924*92241e0bSTom Erickson VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0); 925*92241e0bSTom Erickson ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0); 926*92241e0bSTom Erickson ASSERT(size <= zc->zc_nvlist_dst_size); 927*92241e0bSTom Erickson } 928*92241e0bSTom Erickson 929*92241e0bSTom Erickson return (0); 930*92241e0bSTom Erickson } 931*92241e0bSTom Erickson 932e9dbad6fSeschrock static int 933e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 934e9dbad6fSeschrock { 935e9dbad6fSeschrock char *packed = NULL; 936e9dbad6fSeschrock size_t size; 937e9dbad6fSeschrock int error; 938e9dbad6fSeschrock 939e9dbad6fSeschrock VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); 940e9dbad6fSeschrock 941e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 942e9dbad6fSeschrock error = ENOMEM; 943e9dbad6fSeschrock } else { 944da165920Smarks packed = kmem_alloc(size, KM_SLEEP); 945e9dbad6fSeschrock VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, 946e9dbad6fSeschrock KM_SLEEP) == 0); 947478ed9adSEric Taylor error = ddi_copyout(packed, 948478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags); 949e9dbad6fSeschrock kmem_free(packed, size); 950e9dbad6fSeschrock } 951e9dbad6fSeschrock 952e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 953e9dbad6fSeschrock return (error); 954e9dbad6fSeschrock } 955e9dbad6fSeschrock 95614843421SMatthew Ahrens static int 95714843421SMatthew Ahrens getzfsvfs(const char *dsname, zfsvfs_t **zvp) 95814843421SMatthew Ahrens { 95914843421SMatthew Ahrens objset_t *os; 96014843421SMatthew Ahrens int error; 96114843421SMatthew Ahrens 962503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 96314843421SMatthew Ahrens if (error) 96414843421SMatthew Ahrens return (error); 965503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 966503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 967503ad85cSMatthew Ahrens return (EINVAL); 968503ad85cSMatthew Ahrens } 96914843421SMatthew Ahrens 970503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 97114843421SMatthew Ahrens *zvp = dmu_objset_get_user(os); 97214843421SMatthew Ahrens if (*zvp) { 97314843421SMatthew Ahrens VFS_HOLD((*zvp)->z_vfs); 97414843421SMatthew Ahrens } else { 97514843421SMatthew Ahrens error = ESRCH; 97614843421SMatthew Ahrens } 977503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 978503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 97914843421SMatthew Ahrens return (error); 98014843421SMatthew Ahrens } 98114843421SMatthew Ahrens 98214843421SMatthew Ahrens /* 98314843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 98414843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 98514843421SMatthew Ahrens */ 98614843421SMatthew Ahrens static int 987503ad85cSMatthew Ahrens zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zvp) 98814843421SMatthew Ahrens { 98914843421SMatthew Ahrens int error = 0; 99014843421SMatthew Ahrens 99114843421SMatthew Ahrens if (getzfsvfs(name, zvp) != 0) 992503ad85cSMatthew Ahrens error = zfsvfs_create(name, zvp); 99314843421SMatthew Ahrens if (error == 0) { 99414843421SMatthew Ahrens rrw_enter(&(*zvp)->z_teardown_lock, RW_READER, tag); 99514843421SMatthew Ahrens if ((*zvp)->z_unmounted) { 99614843421SMatthew Ahrens /* 99714843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 99814843421SMatthew Ahrens * thread should be just about to disassociate the 99914843421SMatthew Ahrens * objset from the zfsvfs. 100014843421SMatthew Ahrens */ 100114843421SMatthew Ahrens rrw_exit(&(*zvp)->z_teardown_lock, tag); 100214843421SMatthew Ahrens return (EBUSY); 100314843421SMatthew Ahrens } 100414843421SMatthew Ahrens } 100514843421SMatthew Ahrens return (error); 100614843421SMatthew Ahrens } 100714843421SMatthew Ahrens 100814843421SMatthew Ahrens static void 100914843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 101014843421SMatthew Ahrens { 101114843421SMatthew Ahrens rrw_exit(&zfsvfs->z_teardown_lock, tag); 101214843421SMatthew Ahrens 101314843421SMatthew Ahrens if (zfsvfs->z_vfs) { 101414843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 101514843421SMatthew Ahrens } else { 1016503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 101714843421SMatthew Ahrens zfsvfs_free(zfsvfs); 101814843421SMatthew Ahrens } 101914843421SMatthew Ahrens } 102014843421SMatthew Ahrens 1021fa9e4066Sahrens static int 1022fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1023fa9e4066Sahrens { 1024fa9e4066Sahrens int error; 1025990b4856Slling nvlist_t *config, *props = NULL; 10260a48a24eStimh nvlist_t *rootprops = NULL; 10270a48a24eStimh nvlist_t *zplprops = NULL; 1028228975ccSek char *buf; 1029fa9e4066Sahrens 1030990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1031478ed9adSEric Taylor zc->zc_iflags, &config)) 1032fa9e4066Sahrens return (error); 10332a6b87f0Sek 1034990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1035478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1036478ed9adSEric Taylor zc->zc_iflags, &props))) { 1037990b4856Slling nvlist_free(config); 1038990b4856Slling return (error); 1039990b4856Slling } 1040990b4856Slling 10410a48a24eStimh if (props) { 10420a48a24eStimh nvlist_t *nvl = NULL; 10430a48a24eStimh uint64_t version = SPA_VERSION; 10440a48a24eStimh 10450a48a24eStimh (void) nvlist_lookup_uint64(props, 10460a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 10470a48a24eStimh if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) { 10480a48a24eStimh error = EINVAL; 10490a48a24eStimh goto pool_props_bad; 10500a48a24eStimh } 10510a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 10520a48a24eStimh if (nvl) { 10530a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 10540a48a24eStimh if (error != 0) { 10550a48a24eStimh nvlist_free(config); 10560a48a24eStimh nvlist_free(props); 10570a48a24eStimh return (error); 10580a48a24eStimh } 10590a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 10600a48a24eStimh } 10610a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 10620a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 10630a48a24eStimh zplprops, NULL); 10640a48a24eStimh if (error) 10650a48a24eStimh goto pool_props_bad; 10660a48a24eStimh } 10670a48a24eStimh 10682a6b87f0Sek buf = history_str_get(zc); 1069fa9e4066Sahrens 10700a48a24eStimh error = spa_create(zc->zc_name, config, props, buf, zplprops); 10710a48a24eStimh 10720a48a24eStimh /* 10730a48a24eStimh * Set the remaining root properties 10740a48a24eStimh */ 1075*92241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 1076*92241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 10770a48a24eStimh (void) spa_destroy(zc->zc_name); 1078fa9e4066Sahrens 10792a6b87f0Sek if (buf != NULL) 10802a6b87f0Sek history_str_free(buf); 1081990b4856Slling 10820a48a24eStimh pool_props_bad: 10830a48a24eStimh nvlist_free(rootprops); 10840a48a24eStimh nvlist_free(zplprops); 1085fa9e4066Sahrens nvlist_free(config); 10860a48a24eStimh nvlist_free(props); 1087990b4856Slling 1088fa9e4066Sahrens return (error); 1089fa9e4066Sahrens } 1090fa9e4066Sahrens 1091fa9e4066Sahrens static int 1092fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1093fa9e4066Sahrens { 1094ecd6cf80Smarks int error; 1095ecd6cf80Smarks zfs_log_history(zc); 1096ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1097681d9761SEric Taylor if (error == 0) 1098681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1099ecd6cf80Smarks return (error); 1100fa9e4066Sahrens } 1101fa9e4066Sahrens 1102fa9e4066Sahrens static int 1103fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1104fa9e4066Sahrens { 1105990b4856Slling nvlist_t *config, *props = NULL; 1106fa9e4066Sahrens uint64_t guid; 1107468c413aSTim Haley int error; 1108fa9e4066Sahrens 1109990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1110478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1111990b4856Slling return (error); 1112990b4856Slling 1113990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1114478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1115478ed9adSEric Taylor zc->zc_iflags, &props))) { 1116990b4856Slling nvlist_free(config); 1117fa9e4066Sahrens return (error); 1118990b4856Slling } 1119fa9e4066Sahrens 1120fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1121ea8dc4b6Seschrock guid != zc->zc_guid) 1122fa9e4066Sahrens error = EINVAL; 1123c5904d13Seschrock else if (zc->zc_cookie) 1124468c413aSTim Haley error = spa_import_verbatim(zc->zc_name, config, props); 1125fa9e4066Sahrens else 1126990b4856Slling error = spa_import(zc->zc_name, config, props); 1127fa9e4066Sahrens 1128468c413aSTim Haley if (zc->zc_nvlist_dst != 0) 1129468c413aSTim Haley (void) put_nvlist(zc, config); 1130468c413aSTim Haley 1131fa9e4066Sahrens nvlist_free(config); 1132fa9e4066Sahrens 1133990b4856Slling if (props) 1134990b4856Slling nvlist_free(props); 1135990b4856Slling 1136fa9e4066Sahrens return (error); 1137fa9e4066Sahrens } 1138fa9e4066Sahrens 1139fa9e4066Sahrens static int 1140fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1141fa9e4066Sahrens { 1142ecd6cf80Smarks int error; 114389a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1144394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 114589a89ebfSlling 1146ecd6cf80Smarks zfs_log_history(zc); 1147394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1148681d9761SEric Taylor if (error == 0) 1149681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1150ecd6cf80Smarks return (error); 1151fa9e4066Sahrens } 1152fa9e4066Sahrens 1153fa9e4066Sahrens static int 1154fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1155fa9e4066Sahrens { 1156fa9e4066Sahrens nvlist_t *configs; 1157fa9e4066Sahrens int error; 1158fa9e4066Sahrens 1159fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1160fa9e4066Sahrens return (EEXIST); 1161fa9e4066Sahrens 1162e9dbad6fSeschrock error = put_nvlist(zc, configs); 1163fa9e4066Sahrens 1164fa9e4066Sahrens nvlist_free(configs); 1165fa9e4066Sahrens 1166fa9e4066Sahrens return (error); 1167fa9e4066Sahrens } 1168fa9e4066Sahrens 1169fa9e4066Sahrens static int 1170fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1171fa9e4066Sahrens { 1172fa9e4066Sahrens nvlist_t *config; 1173fa9e4066Sahrens int error; 1174ea8dc4b6Seschrock int ret = 0; 1175fa9e4066Sahrens 1176e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1177e9dbad6fSeschrock sizeof (zc->zc_value)); 1178fa9e4066Sahrens 1179fa9e4066Sahrens if (config != NULL) { 1180e9dbad6fSeschrock ret = put_nvlist(zc, config); 1181fa9e4066Sahrens nvlist_free(config); 1182ea8dc4b6Seschrock 1183ea8dc4b6Seschrock /* 1184ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1185ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1186ea8dc4b6Seschrock * in 'zc_cookie'. 1187ea8dc4b6Seschrock */ 1188ea8dc4b6Seschrock zc->zc_cookie = error; 1189fa9e4066Sahrens } else { 1190ea8dc4b6Seschrock ret = error; 1191fa9e4066Sahrens } 1192fa9e4066Sahrens 1193ea8dc4b6Seschrock return (ret); 1194fa9e4066Sahrens } 1195fa9e4066Sahrens 1196fa9e4066Sahrens /* 1197fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1198fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1199fa9e4066Sahrens */ 1200fa9e4066Sahrens static int 1201fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1202fa9e4066Sahrens { 1203fa9e4066Sahrens nvlist_t *tryconfig, *config; 1204fa9e4066Sahrens int error; 1205fa9e4066Sahrens 1206990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1207478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1208fa9e4066Sahrens return (error); 1209fa9e4066Sahrens 1210fa9e4066Sahrens config = spa_tryimport(tryconfig); 1211fa9e4066Sahrens 1212fa9e4066Sahrens nvlist_free(tryconfig); 1213fa9e4066Sahrens 1214fa9e4066Sahrens if (config == NULL) 1215fa9e4066Sahrens return (EINVAL); 1216fa9e4066Sahrens 1217e9dbad6fSeschrock error = put_nvlist(zc, config); 1218fa9e4066Sahrens nvlist_free(config); 1219fa9e4066Sahrens 1220fa9e4066Sahrens return (error); 1221fa9e4066Sahrens } 1222fa9e4066Sahrens 1223fa9e4066Sahrens static int 1224fa9e4066Sahrens zfs_ioc_pool_scrub(zfs_cmd_t *zc) 1225fa9e4066Sahrens { 1226fa9e4066Sahrens spa_t *spa; 1227fa9e4066Sahrens int error; 1228fa9e4066Sahrens 122906eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 123006eeb2adSek return (error); 123106eeb2adSek 1232088f3894Sahrens error = spa_scrub(spa, zc->zc_cookie); 123306eeb2adSek 123406eeb2adSek spa_close(spa, FTAG); 123506eeb2adSek 1236fa9e4066Sahrens return (error); 1237fa9e4066Sahrens } 1238fa9e4066Sahrens 1239fa9e4066Sahrens static int 1240fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1241fa9e4066Sahrens { 1242fa9e4066Sahrens spa_t *spa; 1243fa9e4066Sahrens int error; 1244fa9e4066Sahrens 1245fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1246fa9e4066Sahrens if (error == 0) { 1247fa9e4066Sahrens spa_freeze(spa); 1248fa9e4066Sahrens spa_close(spa, FTAG); 1249fa9e4066Sahrens } 1250fa9e4066Sahrens return (error); 1251fa9e4066Sahrens } 1252fa9e4066Sahrens 1253eaca9bbdSeschrock static int 1254eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1255eaca9bbdSeschrock { 1256eaca9bbdSeschrock spa_t *spa; 1257eaca9bbdSeschrock int error; 1258eaca9bbdSeschrock 125906eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 126006eeb2adSek return (error); 126106eeb2adSek 1262558d2d50Slling if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) { 1263558d2d50Slling spa_close(spa, FTAG); 1264558d2d50Slling return (EINVAL); 1265558d2d50Slling } 1266558d2d50Slling 1267990b4856Slling spa_upgrade(spa, zc->zc_cookie); 126806eeb2adSek spa_close(spa, FTAG); 126906eeb2adSek 127006eeb2adSek return (error); 127106eeb2adSek } 127206eeb2adSek 127306eeb2adSek static int 127406eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 127506eeb2adSek { 127606eeb2adSek spa_t *spa; 127706eeb2adSek char *hist_buf; 127806eeb2adSek uint64_t size; 127906eeb2adSek int error; 128006eeb2adSek 128106eeb2adSek if ((size = zc->zc_history_len) == 0) 128206eeb2adSek return (EINVAL); 128306eeb2adSek 128406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 128506eeb2adSek return (error); 128606eeb2adSek 1287e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1288d7306b64Sek spa_close(spa, FTAG); 1289d7306b64Sek return (ENOTSUP); 1290d7306b64Sek } 1291d7306b64Sek 129206eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 129306eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 129406eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1295478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1296478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1297478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 129806eeb2adSek } 129906eeb2adSek 130006eeb2adSek spa_close(spa, FTAG); 130106eeb2adSek kmem_free(hist_buf, size); 130206eeb2adSek return (error); 130306eeb2adSek } 130406eeb2adSek 130555434c77Sek static int 130655434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 130755434c77Sek { 130855434c77Sek int error; 130955434c77Sek 1310b1b8ab34Slling if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)) 131155434c77Sek return (error); 131255434c77Sek 131355434c77Sek return (0); 131455434c77Sek } 131555434c77Sek 1316503ad85cSMatthew Ahrens /* 1317503ad85cSMatthew Ahrens * inputs: 1318503ad85cSMatthew Ahrens * zc_name name of filesystem 1319503ad85cSMatthew Ahrens * zc_obj object to find 1320503ad85cSMatthew Ahrens * 1321503ad85cSMatthew Ahrens * outputs: 1322503ad85cSMatthew Ahrens * zc_value name of object 1323503ad85cSMatthew Ahrens */ 132455434c77Sek static int 132555434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 132655434c77Sek { 1327503ad85cSMatthew Ahrens objset_t *os; 132855434c77Sek int error; 132955434c77Sek 1330503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1331503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 133255434c77Sek return (error); 1333503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1334503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1335503ad85cSMatthew Ahrens return (EINVAL); 1336503ad85cSMatthew Ahrens } 1337503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 133855434c77Sek sizeof (zc->zc_value)); 1339503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 134055434c77Sek 134155434c77Sek return (error); 134255434c77Sek } 134355434c77Sek 1344fa9e4066Sahrens static int 1345fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1346fa9e4066Sahrens { 1347fa9e4066Sahrens spa_t *spa; 1348fa9e4066Sahrens int error; 1349e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1350e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1351fa9e4066Sahrens 1352fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1353fa9e4066Sahrens if (error != 0) 1354fa9e4066Sahrens return (error); 1355fa9e4066Sahrens 1356fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1357478ed9adSEric Taylor zc->zc_iflags, &config); 1358fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1359fa94a07fSbrendan &l2cache, &nl2cache); 1360fa94a07fSbrendan 1361e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1362e7cbe64fSgw &spares, &nspares); 1363e7cbe64fSgw 1364b1b8ab34Slling /* 1365b1b8ab34Slling * A root pool with concatenated devices is not supported. 1366e7cbe64fSgw * Thus, can not add a device to a root pool. 1367e7cbe64fSgw * 1368e7cbe64fSgw * Intent log device can not be added to a rootpool because 1369e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1370e7cbe64fSgw * can not be accessed during the mountroot time. 1371e7cbe64fSgw * 1372e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1373b1b8ab34Slling */ 1374b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 1375b1b8ab34Slling spa_close(spa, FTAG); 1376b1b8ab34Slling return (EDOM); 1377b1b8ab34Slling } 1378b1b8ab34Slling 1379fa94a07fSbrendan if (error == 0) { 1380fa9e4066Sahrens error = spa_vdev_add(spa, config); 1381fa9e4066Sahrens nvlist_free(config); 1382fa9e4066Sahrens } 1383fa9e4066Sahrens spa_close(spa, FTAG); 1384fa9e4066Sahrens return (error); 1385fa9e4066Sahrens } 1386fa9e4066Sahrens 1387fa9e4066Sahrens static int 1388fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1389fa9e4066Sahrens { 139099653d4eSeschrock spa_t *spa; 139199653d4eSeschrock int error; 139299653d4eSeschrock 139399653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 139499653d4eSeschrock if (error != 0) 139599653d4eSeschrock return (error); 139699653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 139799653d4eSeschrock spa_close(spa, FTAG); 139899653d4eSeschrock return (error); 1399fa9e4066Sahrens } 1400fa9e4066Sahrens 1401fa9e4066Sahrens static int 14023d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1403fa9e4066Sahrens { 1404fa9e4066Sahrens spa_t *spa; 1405fa9e4066Sahrens int error; 14063d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1407fa9e4066Sahrens 140806eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1409fa9e4066Sahrens return (error); 14103d7072f8Seschrock switch (zc->zc_cookie) { 14113d7072f8Seschrock case VDEV_STATE_ONLINE: 14123d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 14133d7072f8Seschrock break; 1414fa9e4066Sahrens 14153d7072f8Seschrock case VDEV_STATE_OFFLINE: 14163d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 14173d7072f8Seschrock break; 1418fa9e4066Sahrens 14193d7072f8Seschrock case VDEV_STATE_FAULTED: 1420069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1421069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1422069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1423069f55e2SEric Schrock 1424069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 14253d7072f8Seschrock break; 14263d7072f8Seschrock 14273d7072f8Seschrock case VDEV_STATE_DEGRADED: 1428069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1429069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1430069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1431069f55e2SEric Schrock 1432069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 14333d7072f8Seschrock break; 14343d7072f8Seschrock 14353d7072f8Seschrock default: 14363d7072f8Seschrock error = EINVAL; 14373d7072f8Seschrock } 14383d7072f8Seschrock zc->zc_cookie = newstate; 1439fa9e4066Sahrens spa_close(spa, FTAG); 1440fa9e4066Sahrens return (error); 1441fa9e4066Sahrens } 1442fa9e4066Sahrens 1443fa9e4066Sahrens static int 1444fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1445fa9e4066Sahrens { 1446fa9e4066Sahrens spa_t *spa; 1447fa9e4066Sahrens int replacing = zc->zc_cookie; 1448fa9e4066Sahrens nvlist_t *config; 1449fa9e4066Sahrens int error; 1450fa9e4066Sahrens 145106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1452fa9e4066Sahrens return (error); 1453fa9e4066Sahrens 1454990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1455478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1456ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1457fa9e4066Sahrens nvlist_free(config); 1458fa9e4066Sahrens } 1459fa9e4066Sahrens 1460fa9e4066Sahrens spa_close(spa, FTAG); 1461fa9e4066Sahrens return (error); 1462fa9e4066Sahrens } 1463fa9e4066Sahrens 1464fa9e4066Sahrens static int 1465fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1466fa9e4066Sahrens { 1467fa9e4066Sahrens spa_t *spa; 1468fa9e4066Sahrens int error; 1469fa9e4066Sahrens 147006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1471fa9e4066Sahrens return (error); 1472fa9e4066Sahrens 14738ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1474fa9e4066Sahrens 1475fa9e4066Sahrens spa_close(spa, FTAG); 1476fa9e4066Sahrens return (error); 1477fa9e4066Sahrens } 1478fa9e4066Sahrens 1479c67d9675Seschrock static int 1480c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 1481c67d9675Seschrock { 1482c67d9675Seschrock spa_t *spa; 1483e9dbad6fSeschrock char *path = zc->zc_value; 1484ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 1485c67d9675Seschrock int error; 1486c67d9675Seschrock 1487c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 1488c67d9675Seschrock if (error != 0) 1489c67d9675Seschrock return (error); 1490c67d9675Seschrock 1491c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 1492c67d9675Seschrock spa_close(spa, FTAG); 1493c67d9675Seschrock return (error); 1494c67d9675Seschrock } 1495c67d9675Seschrock 14966809eb4eSEric Schrock static int 14976809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 14986809eb4eSEric Schrock { 14996809eb4eSEric Schrock spa_t *spa; 15006809eb4eSEric Schrock char *fru = zc->zc_value; 15016809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 15026809eb4eSEric Schrock int error; 15036809eb4eSEric Schrock 15046809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 15056809eb4eSEric Schrock if (error != 0) 15066809eb4eSEric Schrock return (error); 15076809eb4eSEric Schrock 15086809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 15096809eb4eSEric Schrock spa_close(spa, FTAG); 15106809eb4eSEric Schrock return (error); 15116809eb4eSEric Schrock } 15126809eb4eSEric Schrock 15133cb34c60Sahrens /* 15143cb34c60Sahrens * inputs: 15153cb34c60Sahrens * zc_name name of filesystem 15163cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 15173cb34c60Sahrens * 15183cb34c60Sahrens * outputs: 15193cb34c60Sahrens * zc_objset_stats stats 15203cb34c60Sahrens * zc_nvlist_dst property nvlist 15213cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 15223cb34c60Sahrens */ 1523fa9e4066Sahrens static int 1524fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc) 1525fa9e4066Sahrens { 1526fa9e4066Sahrens objset_t *os = NULL; 1527fa9e4066Sahrens int error; 15287f7322feSeschrock nvlist_t *nv; 1529fa9e4066Sahrens 1530503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1531fa9e4066Sahrens return (error); 1532fa9e4066Sahrens 1533a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1534fa9e4066Sahrens 15355ad82045Snd if (zc->zc_nvlist_dst != 0 && 1536*92241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 1537a2eea2e1Sahrens dmu_objset_stats(os, nv); 1538432f72fdSahrens /* 1539bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 1540432f72fdSahrens * which we aren't supposed to do with a 1541745cd3c5Smaybee * DS_MODE_USER hold, because it could be 1542432f72fdSahrens * inconsistent. So this is a bit of a workaround... 1543503ad85cSMatthew Ahrens * XXX reading with out owning 1544432f72fdSahrens */ 1545e7437265Sahrens if (!zc->zc_objset_stats.dds_inconsistent) { 1546e7437265Sahrens if (dmu_objset_type(os) == DMU_OST_ZVOL) 1547e7437265Sahrens VERIFY(zvol_get_stats(os, nv) == 0); 1548e7437265Sahrens } 1549e9dbad6fSeschrock error = put_nvlist(zc, nv); 15507f7322feSeschrock nvlist_free(nv); 15517f7322feSeschrock } 1552fa9e4066Sahrens 1553503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1554fa9e4066Sahrens return (error); 1555fa9e4066Sahrens } 1556fa9e4066Sahrens 1557*92241e0bSTom Erickson /* 1558*92241e0bSTom Erickson * inputs: 1559*92241e0bSTom Erickson * zc_name name of filesystem 1560*92241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 1561*92241e0bSTom Erickson * 1562*92241e0bSTom Erickson * outputs: 1563*92241e0bSTom Erickson * zc_nvlist_dst received property nvlist 1564*92241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 1565*92241e0bSTom Erickson * 1566*92241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 1567*92241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 1568*92241e0bSTom Erickson * local property values. 1569*92241e0bSTom Erickson */ 1570*92241e0bSTom Erickson static int 1571*92241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 1572*92241e0bSTom Erickson { 1573*92241e0bSTom Erickson objset_t *os = NULL; 1574*92241e0bSTom Erickson int error; 1575*92241e0bSTom Erickson nvlist_t *nv; 1576*92241e0bSTom Erickson 1577*92241e0bSTom Erickson if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1578*92241e0bSTom Erickson return (error); 1579*92241e0bSTom Erickson 1580*92241e0bSTom Erickson /* 1581*92241e0bSTom Erickson * Without this check, we would return local property values if the 1582*92241e0bSTom Erickson * caller has not already received properties on or after 1583*92241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 1584*92241e0bSTom Erickson */ 1585*92241e0bSTom Erickson if (!dsl_prop_get_hasrecvd(os)) { 1586*92241e0bSTom Erickson dmu_objset_rele(os, FTAG); 1587*92241e0bSTom Erickson return (ENOTSUP); 1588*92241e0bSTom Erickson } 1589*92241e0bSTom Erickson 1590*92241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 1591*92241e0bSTom Erickson (error = dsl_prop_get_received(os, &nv)) == 0) { 1592*92241e0bSTom Erickson error = put_nvlist(zc, nv); 1593*92241e0bSTom Erickson nvlist_free(nv); 1594*92241e0bSTom Erickson } 1595*92241e0bSTom Erickson 1596*92241e0bSTom Erickson dmu_objset_rele(os, FTAG); 1597*92241e0bSTom Erickson return (error); 1598*92241e0bSTom Erickson } 1599*92241e0bSTom Erickson 1600de8267e0Stimh static int 1601de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 1602de8267e0Stimh { 1603de8267e0Stimh uint64_t value; 1604de8267e0Stimh int error; 1605de8267e0Stimh 1606de8267e0Stimh /* 1607de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 1608de8267e0Stimh * the default value (if there is one). 1609de8267e0Stimh */ 1610de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 1611de8267e0Stimh return (error); 1612de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 1613de8267e0Stimh return (0); 1614de8267e0Stimh } 1615de8267e0Stimh 16163cb34c60Sahrens /* 16173cb34c60Sahrens * inputs: 16183cb34c60Sahrens * zc_name name of filesystem 1619de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 16203cb34c60Sahrens * 16213cb34c60Sahrens * outputs: 1622de8267e0Stimh * zc_nvlist_dst zpl property nvlist 1623de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 16243cb34c60Sahrens */ 1625bd00f61bSrm static int 1626de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 1627bd00f61bSrm { 1628de8267e0Stimh objset_t *os; 1629de8267e0Stimh int err; 1630bd00f61bSrm 1631503ad85cSMatthew Ahrens /* XXX reading without owning */ 1632503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1633de8267e0Stimh return (err); 1634bd00f61bSrm 1635bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1636bd00f61bSrm 1637bd00f61bSrm /* 1638de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 1639745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 1640745cd3c5Smaybee * hold, because it could be inconsistent. 1641bd00f61bSrm */ 1642de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 1643de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 1644de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 1645de8267e0Stimh nvlist_t *nv; 1646de8267e0Stimh 1647de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1648de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 1649de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 1650de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 1651de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 1652de8267e0Stimh err = put_nvlist(zc, nv); 1653de8267e0Stimh nvlist_free(nv); 1654de8267e0Stimh } else { 1655de8267e0Stimh err = ENOENT; 1656de8267e0Stimh } 1657503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1658de8267e0Stimh return (err); 1659bd00f61bSrm } 1660bd00f61bSrm 166114843421SMatthew Ahrens static boolean_t 166214843421SMatthew Ahrens dataset_name_hidden(const char *name) 166314843421SMatthew Ahrens { 166414843421SMatthew Ahrens /* 166514843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 166614843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 166714843421SMatthew Ahrens * temporary datasets (which have a % in their name). 166814843421SMatthew Ahrens */ 166914843421SMatthew Ahrens if (strchr(name, '$') != NULL) 167014843421SMatthew Ahrens return (B_TRUE); 167114843421SMatthew Ahrens if (strchr(name, '%') != NULL) 167214843421SMatthew Ahrens return (B_TRUE); 167314843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 167414843421SMatthew Ahrens return (B_TRUE); 167514843421SMatthew Ahrens return (B_FALSE); 167614843421SMatthew Ahrens } 167714843421SMatthew Ahrens 1678de8267e0Stimh /* 1679de8267e0Stimh * inputs: 1680de8267e0Stimh * zc_name name of filesystem 1681de8267e0Stimh * zc_cookie zap cursor 1682de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 1683de8267e0Stimh * 1684de8267e0Stimh * outputs: 1685de8267e0Stimh * zc_name name of next filesystem 168614843421SMatthew Ahrens * zc_cookie zap cursor 1687de8267e0Stimh * zc_objset_stats stats 1688de8267e0Stimh * zc_nvlist_dst property nvlist 1689de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 1690de8267e0Stimh */ 1691fa9e4066Sahrens static int 1692fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 1693fa9e4066Sahrens { 169487e5029aSahrens objset_t *os; 1695fa9e4066Sahrens int error; 1696fa9e4066Sahrens char *p; 1697fa9e4066Sahrens 1698503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 169987e5029aSahrens if (error == ENOENT) 170087e5029aSahrens error = ESRCH; 170187e5029aSahrens return (error); 1702fa9e4066Sahrens } 1703fa9e4066Sahrens 1704fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 1705fa9e4066Sahrens if (p == NULL || p[1] != '\0') 1706fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 1707fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 1708fa9e4066Sahrens 17095c0b6a79SRich Morris /* 17105c0b6a79SRich Morris * Pre-fetch the datasets. dmu_objset_prefetch() always returns 0 17115c0b6a79SRich Morris * but is not declared void because its called by dmu_objset_find(). 17125c0b6a79SRich Morris */ 17137f73c863SRich Morris if (zc->zc_cookie == 0) { 17147f73c863SRich Morris uint64_t cookie = 0; 17157f73c863SRich Morris int len = sizeof (zc->zc_name) - (p - zc->zc_name); 17167f73c863SRich Morris 17177f73c863SRich Morris while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) 17185c0b6a79SRich Morris (void) dmu_objset_prefetch(p, NULL); 17197f73c863SRich Morris } 17207f73c863SRich Morris 1721fa9e4066Sahrens do { 172287e5029aSahrens error = dmu_dir_list_next(os, 172387e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 172487e5029aSahrens NULL, &zc->zc_cookie); 1725fa9e4066Sahrens if (error == ENOENT) 1726fa9e4066Sahrens error = ESRCH; 1727681d9761SEric Taylor } while (error == 0 && dataset_name_hidden(zc->zc_name) && 1728681d9761SEric Taylor !(zc->zc_iflags & FKIOCTL)); 1729503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1730fa9e4066Sahrens 1731681d9761SEric Taylor /* 1732681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 1733681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 1734681d9761SEric Taylor */ 1735681d9761SEric Taylor if (error == 0 && strchr(zc->zc_name, '$') == NULL) 173687e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1737fa9e4066Sahrens return (error); 1738fa9e4066Sahrens } 1739fa9e4066Sahrens 17403cb34c60Sahrens /* 17413cb34c60Sahrens * inputs: 17423cb34c60Sahrens * zc_name name of filesystem 17433cb34c60Sahrens * zc_cookie zap cursor 17443cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 17453cb34c60Sahrens * 17463cb34c60Sahrens * outputs: 17473cb34c60Sahrens * zc_name name of next snapshot 17483cb34c60Sahrens * zc_objset_stats stats 17493cb34c60Sahrens * zc_nvlist_dst property nvlist 17503cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 17513cb34c60Sahrens */ 1752fa9e4066Sahrens static int 1753fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 1754fa9e4066Sahrens { 175587e5029aSahrens objset_t *os; 1756fa9e4066Sahrens int error; 1757fa9e4066Sahrens 17587cbf8b43SRich Morris if (zc->zc_cookie == 0) 17597cbf8b43SRich Morris (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, 17607cbf8b43SRich Morris NULL, DS_FIND_SNAPSHOTS); 17617cbf8b43SRich Morris 1762503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 1763745cd3c5Smaybee if (error) 1764745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 1765fa9e4066Sahrens 1766b81d61a6Slling /* 1767b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 1768b81d61a6Slling * so exit immediately. 1769b81d61a6Slling */ 1770b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 1771503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1772b81d61a6Slling return (ESRCH); 1773fa9e4066Sahrens } 1774fa9e4066Sahrens 177587e5029aSahrens error = dmu_snapshot_list_next(os, 177687e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 1777b38f0970Sck zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL); 1778503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 177987e5029aSahrens if (error == 0) 178087e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1781745cd3c5Smaybee else if (error == ENOENT) 1782745cd3c5Smaybee error = ESRCH; 1783fa9e4066Sahrens 17843cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 1785745cd3c5Smaybee if (error) 17863cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 1787fa9e4066Sahrens return (error); 1788fa9e4066Sahrens } 1789fa9e4066Sahrens 1790*92241e0bSTom Erickson static int 1791*92241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 1792fa9e4066Sahrens { 1793*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 1794*92241e0bSTom Erickson uint64_t *valary; 1795*92241e0bSTom Erickson unsigned int vallen; 1796*92241e0bSTom Erickson const char *domain; 1797*92241e0bSTom Erickson zfs_userquota_prop_t type; 1798*92241e0bSTom Erickson uint64_t rid; 1799*92241e0bSTom Erickson uint64_t quota; 1800*92241e0bSTom Erickson zfsvfs_t *zfsvfs; 1801*92241e0bSTom Erickson int err; 1802*92241e0bSTom Erickson 1803*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 1804*92241e0bSTom Erickson nvlist_t *attrs; 1805*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 1806*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 1807*92241e0bSTom Erickson &pair) == 0); 1808*92241e0bSTom Erickson } 1809e9dbad6fSeschrock 1810*92241e0bSTom Erickson VERIFY(nvpair_value_uint64_array(pair, &valary, &vallen) == 0); 1811*92241e0bSTom Erickson VERIFY(vallen == 3); 1812*92241e0bSTom Erickson type = valary[0]; 1813*92241e0bSTom Erickson rid = valary[1]; 1814*92241e0bSTom Erickson quota = valary[2]; 1815ecd6cf80Smarks /* 1816*92241e0bSTom Erickson * The propname is encoded as 1817*92241e0bSTom Erickson * userquota@<rid>-<domain>. 1818ecd6cf80Smarks */ 1819*92241e0bSTom Erickson domain = strchr(propname, '-') + 1; 1820e9dbad6fSeschrock 1821*92241e0bSTom Erickson err = zfsvfs_hold(dsname, FTAG, &zfsvfs); 1822*92241e0bSTom Erickson if (err == 0) { 1823*92241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 1824*92241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 1825*92241e0bSTom Erickson } 1826e9dbad6fSeschrock 1827*92241e0bSTom Erickson return (err); 1828*92241e0bSTom Erickson } 182914843421SMatthew Ahrens 1830*92241e0bSTom Erickson /* 1831*92241e0bSTom Erickson * If the named property is one that has a special function to set its value, 1832*92241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 1833*92241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 1834*92241e0bSTom Erickson * 1835*92241e0bSTom Erickson * XXX: It would be better for callers of the properety interface if we handled 1836*92241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 1837*92241e0bSTom Erickson */ 1838*92241e0bSTom Erickson static int 1839*92241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 1840*92241e0bSTom Erickson nvpair_t *pair) 1841*92241e0bSTom Erickson { 1842*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 1843*92241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 1844*92241e0bSTom Erickson uint64_t intval; 1845*92241e0bSTom Erickson int err; 1846fa9e4066Sahrens 1847*92241e0bSTom Erickson if (prop == ZPROP_INVAL) { 1848*92241e0bSTom Erickson if (zfs_prop_userquota(propname)) 1849*92241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 1850*92241e0bSTom Erickson return (-1); 1851*92241e0bSTom Erickson } 185214843421SMatthew Ahrens 1853*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 1854*92241e0bSTom Erickson nvlist_t *attrs; 1855*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 1856*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 1857*92241e0bSTom Erickson &pair) == 0); 1858*92241e0bSTom Erickson } 1859db870a07Sahrens 1860*92241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 1861*92241e0bSTom Erickson return (-1); 1862b24ab676SJeff Bonwick 1863*92241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 186440feaa91Sahrens 1865*92241e0bSTom Erickson switch (prop) { 1866*92241e0bSTom Erickson case ZFS_PROP_QUOTA: 1867*92241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 1868*92241e0bSTom Erickson break; 1869*92241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 1870*92241e0bSTom Erickson err = dsl_dataset_set_quota(dsname, source, intval); 1871*92241e0bSTom Erickson break; 1872*92241e0bSTom Erickson case ZFS_PROP_RESERVATION: 1873*92241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 1874*92241e0bSTom Erickson break; 1875*92241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 1876*92241e0bSTom Erickson err = dsl_dataset_set_reservation(dsname, source, intval); 1877*92241e0bSTom Erickson break; 1878*92241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 1879*92241e0bSTom Erickson err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip), 1880*92241e0bSTom Erickson intval); 1881*92241e0bSTom Erickson break; 1882*92241e0bSTom Erickson case ZFS_PROP_VERSION: 1883*92241e0bSTom Erickson { 1884*92241e0bSTom Erickson zfsvfs_t *zfsvfs; 18859e6eda55Smarks 1886*92241e0bSTom Erickson if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs)) != 0) 1887b24ab676SJeff Bonwick break; 1888b24ab676SJeff Bonwick 1889*92241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 1890*92241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 1891d0f3f37eSMark Shellenbaum 1892*92241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 1893*92241e0bSTom Erickson zfs_cmd_t zc = { 0 }; 1894*92241e0bSTom Erickson (void) strcpy(zc.zc_name, dsname); 1895*92241e0bSTom Erickson (void) zfs_ioc_userspace_upgrade(&zc); 189640feaa91Sahrens } 1897*92241e0bSTom Erickson break; 1898ecd6cf80Smarks } 1899ecd6cf80Smarks 1900*92241e0bSTom Erickson default: 1901*92241e0bSTom Erickson err = -1; 1902*92241e0bSTom Erickson } 1903e9dbad6fSeschrock 1904*92241e0bSTom Erickson return (err); 1905*92241e0bSTom Erickson } 1906a9799022Sck 1907*92241e0bSTom Erickson /* 1908*92241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 1909*92241e0bSTom Erickson * it continues to set as many as it can and returns the first error 1910*92241e0bSTom Erickson * encountered. If the caller provides a non-NULL errlist, it also gives the 1911*92241e0bSTom Erickson * complete list of names of all the properties it failed to set along with the 1912*92241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 1913*92241e0bSTom Erickson * returned errlist. 1914*92241e0bSTom Erickson * 1915*92241e0bSTom Erickson * If every property is set successfully, zero is returned and the list pointed 1916*92241e0bSTom Erickson * at by errlist is NULL. 1917*92241e0bSTom Erickson */ 1918*92241e0bSTom Erickson int 1919*92241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 1920*92241e0bSTom Erickson nvlist_t **errlist) 1921*92241e0bSTom Erickson { 1922*92241e0bSTom Erickson nvpair_t *pair; 1923*92241e0bSTom Erickson nvpair_t *propval; 1924*92241e0bSTom Erickson int err, rv = 0; 1925*92241e0bSTom Erickson uint64_t intval; 1926*92241e0bSTom Erickson char *strval; 1927*92241e0bSTom Erickson nvlist_t *genericnvl; 1928*92241e0bSTom Erickson nvlist_t *errors; 1929*92241e0bSTom Erickson nvlist_t *retrynvl; 1930e9dbad6fSeschrock 1931*92241e0bSTom Erickson VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1932*92241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1933*92241e0bSTom Erickson VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1934a9799022Sck 1935*92241e0bSTom Erickson retry: 1936*92241e0bSTom Erickson pair = NULL; 1937*92241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 1938*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 1939*92241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 1940e9dbad6fSeschrock 1941*92241e0bSTom Erickson /* decode the property value */ 1942*92241e0bSTom Erickson propval = pair; 1943*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 1944*92241e0bSTom Erickson nvlist_t *attrs; 1945*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 1946*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 1947*92241e0bSTom Erickson &propval) == 0); 194814843421SMatthew Ahrens } 1949e9dbad6fSeschrock 1950*92241e0bSTom Erickson /* Validate value type */ 1951*92241e0bSTom Erickson if (prop == ZPROP_INVAL) { 1952*92241e0bSTom Erickson if (zfs_prop_user(propname)) { 1953*92241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 1954*92241e0bSTom Erickson err = EINVAL; 1955*92241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 1956*92241e0bSTom Erickson if (nvpair_type(propval) != 1957*92241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 1958*92241e0bSTom Erickson err = EINVAL; 19594201a95eSRic Aleshire } 1960*92241e0bSTom Erickson } else { 1961*92241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 1962*92241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 1963*92241e0bSTom Erickson err = EINVAL; 1964*92241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 1965a2eea2e1Sahrens const char *unused; 1966a2eea2e1Sahrens 1967*92241e0bSTom Erickson VERIFY(nvpair_value_uint64(propval, 1968*92241e0bSTom Erickson &intval) == 0); 1969e9dbad6fSeschrock 1970e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 197191ebeef5Sahrens case PROP_TYPE_NUMBER: 1972e9dbad6fSeschrock break; 197391ebeef5Sahrens case PROP_TYPE_STRING: 1974*92241e0bSTom Erickson err = EINVAL; 1975*92241e0bSTom Erickson break; 197691ebeef5Sahrens case PROP_TYPE_INDEX: 1977acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 1978*92241e0bSTom Erickson intval, &unused) != 0) 1979*92241e0bSTom Erickson err = EINVAL; 1980e9dbad6fSeschrock break; 1981e9dbad6fSeschrock default: 1982e7437265Sahrens cmn_err(CE_PANIC, 1983e7437265Sahrens "unknown property type"); 1984e9dbad6fSeschrock } 1985e9dbad6fSeschrock } else { 1986*92241e0bSTom Erickson err = EINVAL; 1987e9dbad6fSeschrock } 1988e9dbad6fSeschrock } 1989*92241e0bSTom Erickson 1990*92241e0bSTom Erickson /* Validate permissions */ 1991*92241e0bSTom Erickson if (err == 0) 1992*92241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 1993*92241e0bSTom Erickson 1994*92241e0bSTom Erickson if (err == 0) { 1995*92241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 1996*92241e0bSTom Erickson if (err == -1) { 1997*92241e0bSTom Erickson /* 1998*92241e0bSTom Erickson * For better performance we build up a list of 1999*92241e0bSTom Erickson * properties to set in a single transaction. 2000*92241e0bSTom Erickson */ 2001*92241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 2002*92241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 2003*92241e0bSTom Erickson /* 2004*92241e0bSTom Erickson * This may be a spurious error caused by 2005*92241e0bSTom Erickson * receiving quota and reservation out of order. 2006*92241e0bSTom Erickson * Try again in a second pass. 2007*92241e0bSTom Erickson */ 2008*92241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 2009*92241e0bSTom Erickson } 2010*92241e0bSTom Erickson } 2011*92241e0bSTom Erickson 2012*92241e0bSTom Erickson if (err != 0) 2013*92241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, propname, err) == 0); 2014e9dbad6fSeschrock } 2015e9dbad6fSeschrock 2016*92241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 2017*92241e0bSTom Erickson nvl = retrynvl; 2018*92241e0bSTom Erickson goto retry; 2019*92241e0bSTom Erickson } 2020*92241e0bSTom Erickson 2021*92241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 2022*92241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 2023*92241e0bSTom Erickson /* 2024*92241e0bSTom Erickson * If this fails, we still want to set as many properties as we 2025*92241e0bSTom Erickson * can, so try setting them individually. 2026*92241e0bSTom Erickson */ 2027*92241e0bSTom Erickson pair = NULL; 2028*92241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 2029*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 2030*92241e0bSTom Erickson 2031*92241e0bSTom Erickson propval = pair; 2032*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 2033*92241e0bSTom Erickson nvlist_t *attrs; 2034*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2035*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2036*92241e0bSTom Erickson &propval) == 0); 2037*92241e0bSTom Erickson } 2038*92241e0bSTom Erickson 2039*92241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 2040*92241e0bSTom Erickson VERIFY(nvpair_value_string(propval, 2041*92241e0bSTom Erickson &strval) == 0); 2042*92241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 1, 2043*92241e0bSTom Erickson strlen(strval) + 1, strval); 2044*92241e0bSTom Erickson } else { 2045*92241e0bSTom Erickson VERIFY(nvpair_value_uint64(propval, 2046*92241e0bSTom Erickson &intval) == 0); 2047*92241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 8, 2048*92241e0bSTom Erickson 1, &intval); 2049*92241e0bSTom Erickson } 2050*92241e0bSTom Erickson 2051*92241e0bSTom Erickson if (err != 0) { 2052*92241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, propname, 2053*92241e0bSTom Erickson err) == 0); 2054*92241e0bSTom Erickson } 2055*92241e0bSTom Erickson } 20565c0b6a79SRich Morris } 20575c0b6a79SRich Morris nvlist_free(genericnvl); 2058*92241e0bSTom Erickson nvlist_free(retrynvl); 2059*92241e0bSTom Erickson 2060*92241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 2061*92241e0bSTom Erickson nvlist_free(errors); 2062*92241e0bSTom Erickson errors = NULL; 2063*92241e0bSTom Erickson } else { 2064*92241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 2065*92241e0bSTom Erickson } 2066*92241e0bSTom Erickson 2067*92241e0bSTom Erickson if (errlist == NULL) 2068*92241e0bSTom Erickson nvlist_free(errors); 2069*92241e0bSTom Erickson else 2070*92241e0bSTom Erickson *errlist = errors; 2071*92241e0bSTom Erickson 2072*92241e0bSTom Erickson return (rv); 2073fa9e4066Sahrens } 2074fa9e4066Sahrens 2075ea2f5b9eSMatthew Ahrens /* 2076ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2077ea2f5b9eSMatthew Ahrens */ 2078ea2f5b9eSMatthew Ahrens static int 2079ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl) 2080ea2f5b9eSMatthew Ahrens { 2081*92241e0bSTom Erickson nvpair_t *pair = NULL; 2082ea2f5b9eSMatthew Ahrens int error = 0; 2083ea2f5b9eSMatthew Ahrens 2084*92241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 2085*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 2086ea2f5b9eSMatthew Ahrens char *valstr; 2087ea2f5b9eSMatthew Ahrens 2088ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 2089*92241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2090ea2f5b9eSMatthew Ahrens return (EINVAL); 2091ea2f5b9eSMatthew Ahrens 2092ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2093ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2094ea2f5b9eSMatthew Ahrens return (error); 2095ea2f5b9eSMatthew Ahrens 2096ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2097ea2f5b9eSMatthew Ahrens return (ENAMETOOLONG); 2098ea2f5b9eSMatthew Ahrens 2099*92241e0bSTom Erickson VERIFY(nvpair_value_string(pair, &valstr) == 0); 2100ea2f5b9eSMatthew Ahrens if (strlen(valstr) >= ZAP_MAXVALUELEN) 2101ea2f5b9eSMatthew Ahrens return (E2BIG); 2102ea2f5b9eSMatthew Ahrens } 2103ea2f5b9eSMatthew Ahrens return (0); 2104ea2f5b9eSMatthew Ahrens } 2105ea2f5b9eSMatthew Ahrens 2106*92241e0bSTom Erickson static void 2107*92241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 2108*92241e0bSTom Erickson { 2109*92241e0bSTom Erickson nvpair_t *pair; 2110*92241e0bSTom Erickson 2111*92241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2112*92241e0bSTom Erickson 2113*92241e0bSTom Erickson pair = NULL; 2114*92241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 2115*92241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 2116*92241e0bSTom Erickson continue; 2117*92241e0bSTom Erickson 2118*92241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 2119*92241e0bSTom Erickson } 2120*92241e0bSTom Erickson } 2121*92241e0bSTom Erickson 2122*92241e0bSTom Erickson static int 2123*92241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props, 2124*92241e0bSTom Erickson nvlist_t *skipped) 2125*92241e0bSTom Erickson { 2126*92241e0bSTom Erickson int err = 0; 2127*92241e0bSTom Erickson nvlist_t *cleared_props = NULL; 2128*92241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 2129*92241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 2130*92241e0bSTom Erickson /* 2131*92241e0bSTom Erickson * Acts on local properties until the dataset has received 2132*92241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 2133*92241e0bSTom Erickson */ 2134*92241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 2135*92241e0bSTom Erickson (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0)); 2136*92241e0bSTom Erickson err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL); 2137*92241e0bSTom Erickson } 2138*92241e0bSTom Erickson nvlist_free(cleared_props); 2139*92241e0bSTom Erickson return (err); 2140*92241e0bSTom Erickson } 2141*92241e0bSTom Erickson 21423cb34c60Sahrens /* 21433cb34c60Sahrens * inputs: 21443cb34c60Sahrens * zc_name name of filesystem 21455c0b6a79SRich Morris * zc_value name of property to set 21463cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 2147*92241e0bSTom Erickson * zc_cookie received properties flag 21483cb34c60Sahrens * 2149*92241e0bSTom Erickson * outputs: 2150*92241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 21513cb34c60Sahrens */ 2152fa9e4066Sahrens static int 2153e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2154fa9e4066Sahrens { 2155e9dbad6fSeschrock nvlist_t *nvl; 2156*92241e0bSTom Erickson boolean_t received = zc->zc_cookie; 2157*92241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 2158*92241e0bSTom Erickson ZPROP_SRC_LOCAL); 2159*92241e0bSTom Erickson nvlist_t *errors = NULL; 2160e9dbad6fSeschrock int error; 2161e9dbad6fSeschrock 2162990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2163478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2164e9dbad6fSeschrock return (error); 2165e9dbad6fSeschrock 2166*92241e0bSTom Erickson if (received) { 2167bb0ade09Sahrens nvlist_t *origprops; 2168bb0ade09Sahrens objset_t *os; 2169bb0ade09Sahrens 2170503ad85cSMatthew Ahrens if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) { 2171*92241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 2172*92241e0bSTom Erickson (void) clear_received_props(os, 2173*92241e0bSTom Erickson zc->zc_name, origprops, nvl); 2174bb0ade09Sahrens nvlist_free(origprops); 2175bb0ade09Sahrens } 2176*92241e0bSTom Erickson 2177*92241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 2178503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2179bb0ade09Sahrens } 2180bb0ade09Sahrens } 2181bb0ade09Sahrens 2182*92241e0bSTom Erickson error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors); 2183*92241e0bSTom Erickson 2184*92241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 2185*92241e0bSTom Erickson (void) put_nvlist(zc, errors); 2186*92241e0bSTom Erickson } 2187ecd6cf80Smarks 2188*92241e0bSTom Erickson nvlist_free(errors); 2189e9dbad6fSeschrock nvlist_free(nvl); 2190e9dbad6fSeschrock return (error); 2191fa9e4066Sahrens } 2192fa9e4066Sahrens 21933cb34c60Sahrens /* 21943cb34c60Sahrens * inputs: 21953cb34c60Sahrens * zc_name name of filesystem 21963cb34c60Sahrens * zc_value name of property to inherit 2197*92241e0bSTom Erickson * zc_cookie revert to received value if TRUE 21983cb34c60Sahrens * 21993cb34c60Sahrens * outputs: none 22003cb34c60Sahrens */ 2201e45ce728Sahrens static int 2202e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2203e45ce728Sahrens { 2204*92241e0bSTom Erickson const char *propname = zc->zc_value; 2205*92241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2206*92241e0bSTom Erickson boolean_t received = zc->zc_cookie; 2207*92241e0bSTom Erickson zprop_source_t source = (received 2208*92241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 2209*92241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 2210*92241e0bSTom Erickson 2211*92241e0bSTom Erickson if (received) { 2212*92241e0bSTom Erickson nvlist_t *dummy; 2213*92241e0bSTom Erickson nvpair_t *pair; 2214*92241e0bSTom Erickson zprop_type_t type; 2215*92241e0bSTom Erickson int err; 2216*92241e0bSTom Erickson 2217*92241e0bSTom Erickson /* 2218*92241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 2219*92241e0bSTom Erickson * nvpair with type info. 2220*92241e0bSTom Erickson */ 2221*92241e0bSTom Erickson if (prop == ZPROP_INVAL) { 2222*92241e0bSTom Erickson if (!zfs_prop_user(propname)) 2223*92241e0bSTom Erickson return (EINVAL); 2224*92241e0bSTom Erickson 2225*92241e0bSTom Erickson type = PROP_TYPE_STRING; 2226*92241e0bSTom Erickson } else { 2227*92241e0bSTom Erickson type = zfs_prop_get_type(prop); 2228*92241e0bSTom Erickson } 2229*92241e0bSTom Erickson 2230*92241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2231*92241e0bSTom Erickson 2232*92241e0bSTom Erickson switch (type) { 2233*92241e0bSTom Erickson case PROP_TYPE_STRING: 2234*92241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 2235*92241e0bSTom Erickson break; 2236*92241e0bSTom Erickson case PROP_TYPE_NUMBER: 2237*92241e0bSTom Erickson case PROP_TYPE_INDEX: 2238*92241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 2239*92241e0bSTom Erickson break; 2240*92241e0bSTom Erickson default: 2241*92241e0bSTom Erickson nvlist_free(dummy); 2242*92241e0bSTom Erickson return (EINVAL); 2243*92241e0bSTom Erickson } 2244*92241e0bSTom Erickson 2245*92241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 2246*92241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 2247*92241e0bSTom Erickson nvlist_free(dummy); 2248*92241e0bSTom Erickson if (err != -1) 2249*92241e0bSTom Erickson return (err); /* special property already handled */ 2250*92241e0bSTom Erickson } else { 2251*92241e0bSTom Erickson /* 2252*92241e0bSTom Erickson * Only check this in the non-received case. We want to allow 2253*92241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 2254*92241e0bSTom Erickson * and reservation to the received or default values even though 2255*92241e0bSTom Erickson * they are not considered inheritable. 2256*92241e0bSTom Erickson */ 2257*92241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 2258*92241e0bSTom Erickson return (EINVAL); 2259*92241e0bSTom Erickson } 2260*92241e0bSTom Erickson 2261e45ce728Sahrens /* the property name has been validated by zfs_secpolicy_inherit() */ 2262*92241e0bSTom Erickson return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL)); 2263e45ce728Sahrens } 2264e45ce728Sahrens 2265b1b8ab34Slling static int 226611a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2267b1b8ab34Slling { 2268990b4856Slling nvlist_t *props; 2269b1b8ab34Slling spa_t *spa; 2270990b4856Slling int error; 2271*92241e0bSTom Erickson nvpair_t *pair; 2272b1b8ab34Slling 2273*92241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2274*92241e0bSTom Erickson zc->zc_iflags, &props)) 2275b1b8ab34Slling return (error); 2276b1b8ab34Slling 2277379c004dSEric Schrock /* 2278379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2279379c004dSEric Schrock * to handle the faulted case. 2280379c004dSEric Schrock */ 2281*92241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 2282*92241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2283379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 2284*92241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2285379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2286379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2287379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2288379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2289379c004dSEric Schrock } 2290379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2291b693757aSEric Schrock if (spa != NULL) { 2292b693757aSEric Schrock nvlist_free(props); 2293379c004dSEric Schrock return (0); 2294b693757aSEric Schrock } 2295379c004dSEric Schrock } 2296379c004dSEric Schrock 2297b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2298990b4856Slling nvlist_free(props); 2299b1b8ab34Slling return (error); 2300b1b8ab34Slling } 2301b1b8ab34Slling 2302990b4856Slling error = spa_prop_set(spa, props); 2303b1b8ab34Slling 2304990b4856Slling nvlist_free(props); 2305b1b8ab34Slling spa_close(spa, FTAG); 2306b1b8ab34Slling 2307b1b8ab34Slling return (error); 2308b1b8ab34Slling } 2309b1b8ab34Slling 2310b1b8ab34Slling static int 231111a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2312b1b8ab34Slling { 2313b1b8ab34Slling spa_t *spa; 2314b1b8ab34Slling int error; 2315b1b8ab34Slling nvlist_t *nvp = NULL; 2316b1b8ab34Slling 2317379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2318379c004dSEric Schrock /* 2319379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2320379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2321379c004dSEric Schrock * anyway. 2322379c004dSEric Schrock */ 2323379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2324379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2325379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2326379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2327379c004dSEric Schrock } else { 2328379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2329379c004dSEric Schrock spa_close(spa, FTAG); 2330379c004dSEric Schrock } 2331b1b8ab34Slling 2332b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2333b1b8ab34Slling error = put_nvlist(zc, nvp); 2334b1b8ab34Slling else 2335b1b8ab34Slling error = EFAULT; 2336b1b8ab34Slling 2337379c004dSEric Schrock nvlist_free(nvp); 2338b1b8ab34Slling return (error); 2339b1b8ab34Slling } 2340b1b8ab34Slling 2341ecd6cf80Smarks static int 2342ecd6cf80Smarks zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc) 2343ecd6cf80Smarks { 2344ecd6cf80Smarks nvlist_t *nvp; 2345ecd6cf80Smarks int error; 2346ecd6cf80Smarks uint32_t uid; 2347ecd6cf80Smarks uint32_t gid; 2348ecd6cf80Smarks uint32_t *groups; 2349ecd6cf80Smarks uint_t group_cnt; 2350ecd6cf80Smarks cred_t *usercred; 2351ecd6cf80Smarks 2352990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2353478ed9adSEric Taylor zc->zc_iflags, &nvp)) != 0) { 2354ecd6cf80Smarks return (error); 2355ecd6cf80Smarks } 2356ecd6cf80Smarks 2357ecd6cf80Smarks if ((error = nvlist_lookup_uint32(nvp, 2358ecd6cf80Smarks ZFS_DELEG_PERM_UID, &uid)) != 0) { 2359ecd6cf80Smarks nvlist_free(nvp); 2360ecd6cf80Smarks return (EPERM); 2361ecd6cf80Smarks } 2362ecd6cf80Smarks 2363ecd6cf80Smarks if ((error = nvlist_lookup_uint32(nvp, 2364ecd6cf80Smarks ZFS_DELEG_PERM_GID, &gid)) != 0) { 2365ecd6cf80Smarks nvlist_free(nvp); 2366ecd6cf80Smarks return (EPERM); 2367ecd6cf80Smarks } 2368ecd6cf80Smarks 2369ecd6cf80Smarks if ((error = nvlist_lookup_uint32_array(nvp, ZFS_DELEG_PERM_GROUPS, 2370ecd6cf80Smarks &groups, &group_cnt)) != 0) { 2371ecd6cf80Smarks nvlist_free(nvp); 2372ecd6cf80Smarks return (EPERM); 2373ecd6cf80Smarks } 2374ecd6cf80Smarks usercred = cralloc(); 2375ecd6cf80Smarks if ((crsetugid(usercred, uid, gid) != 0) || 2376ecd6cf80Smarks (crsetgroups(usercred, group_cnt, (gid_t *)groups) != 0)) { 2377ecd6cf80Smarks nvlist_free(nvp); 2378ecd6cf80Smarks crfree(usercred); 2379ecd6cf80Smarks return (EPERM); 2380ecd6cf80Smarks } 2381ecd6cf80Smarks nvlist_free(nvp); 2382ecd6cf80Smarks error = dsl_deleg_access(zc->zc_name, 238391ebeef5Sahrens zfs_prop_to_name(ZFS_PROP_SHAREISCSI), usercred); 2384ecd6cf80Smarks crfree(usercred); 2385ecd6cf80Smarks return (error); 2386ecd6cf80Smarks } 2387ecd6cf80Smarks 23883cb34c60Sahrens /* 23893cb34c60Sahrens * inputs: 23903cb34c60Sahrens * zc_name name of filesystem 23913cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 23923cb34c60Sahrens * zc_perm_action allow/unallow flag 23933cb34c60Sahrens * 23943cb34c60Sahrens * outputs: none 23953cb34c60Sahrens */ 2396ecd6cf80Smarks static int 2397ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2398ecd6cf80Smarks { 2399ecd6cf80Smarks int error; 2400ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2401ecd6cf80Smarks 2402990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2403478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2404ecd6cf80Smarks return (error); 2405ecd6cf80Smarks 2406ecd6cf80Smarks /* 2407ecd6cf80Smarks * Verify nvlist is constructed correctly 2408ecd6cf80Smarks */ 2409ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2410ecd6cf80Smarks nvlist_free(fsaclnv); 2411ecd6cf80Smarks return (EINVAL); 2412ecd6cf80Smarks } 2413ecd6cf80Smarks 2414ecd6cf80Smarks /* 2415ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2416ecd6cf80Smarks * that user is allowed to hand out each permission in 2417ecd6cf80Smarks * the nvlist(s) 2418ecd6cf80Smarks */ 2419ecd6cf80Smarks 242091ebeef5Sahrens error = secpolicy_zfs(CRED()); 2421ecd6cf80Smarks if (error) { 242291ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 242391ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 242491ebeef5Sahrens fsaclnv, CRED()); 242591ebeef5Sahrens } else { 242691ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 242791ebeef5Sahrens fsaclnv, CRED()); 242891ebeef5Sahrens } 2429ecd6cf80Smarks } 2430ecd6cf80Smarks 2431ecd6cf80Smarks if (error == 0) 2432ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2433ecd6cf80Smarks 2434ecd6cf80Smarks nvlist_free(fsaclnv); 2435ecd6cf80Smarks return (error); 2436ecd6cf80Smarks } 2437ecd6cf80Smarks 24383cb34c60Sahrens /* 24393cb34c60Sahrens * inputs: 24403cb34c60Sahrens * zc_name name of filesystem 24413cb34c60Sahrens * 24423cb34c60Sahrens * outputs: 24433cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 24443cb34c60Sahrens */ 2445ecd6cf80Smarks static int 2446ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2447ecd6cf80Smarks { 2448ecd6cf80Smarks nvlist_t *nvp; 2449ecd6cf80Smarks int error; 2450ecd6cf80Smarks 2451ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2452ecd6cf80Smarks error = put_nvlist(zc, nvp); 2453ecd6cf80Smarks nvlist_free(nvp); 2454ecd6cf80Smarks } 2455ecd6cf80Smarks 2456ecd6cf80Smarks return (error); 2457ecd6cf80Smarks } 2458ecd6cf80Smarks 2459fa9e4066Sahrens /* 2460fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2461fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2462fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2463fa9e4066Sahrens */ 2464fa9e4066Sahrens static vfs_t * 2465fa9e4066Sahrens zfs_get_vfs(const char *resource) 2466fa9e4066Sahrens { 2467fa9e4066Sahrens struct vfs *vfsp; 2468fa9e4066Sahrens struct vfs *vfs_found = NULL; 2469fa9e4066Sahrens 2470fa9e4066Sahrens vfs_list_read_lock(); 2471fa9e4066Sahrens vfsp = rootvfs; 2472fa9e4066Sahrens do { 2473fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2474fa9e4066Sahrens VFS_HOLD(vfsp); 2475fa9e4066Sahrens vfs_found = vfsp; 2476fa9e4066Sahrens break; 2477fa9e4066Sahrens } 2478fa9e4066Sahrens vfsp = vfsp->vfs_next; 2479fa9e4066Sahrens } while (vfsp != rootvfs); 2480fa9e4066Sahrens vfs_list_unlock(); 2481fa9e4066Sahrens return (vfs_found); 2482fa9e4066Sahrens } 2483fa9e4066Sahrens 2484ecd6cf80Smarks /* ARGSUSED */ 2485fa9e4066Sahrens static void 2486ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2487fa9e4066Sahrens { 2488da6c28aaSamw zfs_creat_t *zct = arg; 2489da6c28aaSamw 2490de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2491da6c28aaSamw } 2492da6c28aaSamw 2493de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2494da6c28aaSamw 2495da6c28aaSamw /* 2496de8267e0Stimh * inputs: 24970a48a24eStimh * createprops list of properties requested by creator 24980a48a24eStimh * default_zplver zpl version to use if unspecified in createprops 24990a48a24eStimh * fuids_ok fuids allowed in this version of the spa? 25000a48a24eStimh * os parent objset pointer (NULL if root fs) 2501de8267e0Stimh * 2502de8267e0Stimh * outputs: 2503de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 25040a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 2505da6c28aaSamw * 2506de8267e0Stimh * Determine the settings for utf8only, normalization and 2507de8267e0Stimh * casesensitivity. Specific values may have been requested by the 2508de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 2509de8267e0Stimh * the file system is of too early a vintage, a creator can not 2510de8267e0Stimh * request settings for these properties, even if the requested 2511de8267e0Stimh * setting is the default value. We don't actually want to create dsl 2512de8267e0Stimh * properties for these, so remove them from the source nvlist after 2513de8267e0Stimh * processing. 2514da6c28aaSamw */ 2515da6c28aaSamw static int 251614843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 25170a48a24eStimh boolean_t fuids_ok, nvlist_t *createprops, nvlist_t *zplprops, 25180a48a24eStimh boolean_t *is_ci) 2519da6c28aaSamw { 2520de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 2521de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 2522de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 2523da6c28aaSamw 2524de8267e0Stimh ASSERT(zplprops != NULL); 2525da6c28aaSamw 2526de8267e0Stimh /* 2527de8267e0Stimh * Pull out creator prop choices, if any. 2528de8267e0Stimh */ 2529de8267e0Stimh if (createprops) { 25300a48a24eStimh (void) nvlist_lookup_uint64(createprops, 25310a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 2532de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2533de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 2534de8267e0Stimh (void) nvlist_remove_all(createprops, 2535de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 2536de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2537de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 2538de8267e0Stimh (void) nvlist_remove_all(createprops, 2539de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 2540de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2541de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 2542de8267e0Stimh (void) nvlist_remove_all(createprops, 2543de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 2544de8267e0Stimh } 2545da6c28aaSamw 2546c2a93d44Stimh /* 25470a48a24eStimh * If the zpl version requested is whacky or the file system 25480a48a24eStimh * or pool is version is too "young" to support normalization 25490a48a24eStimh * and the creator tried to set a value for one of the props, 25500a48a24eStimh * error out. 2551c2a93d44Stimh */ 25520a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 25530a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 25540a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 2555de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 25560a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 2557de8267e0Stimh return (ENOTSUP); 2558c2a93d44Stimh 2559de8267e0Stimh /* 2560de8267e0Stimh * Put the version in the zplprops 2561de8267e0Stimh */ 2562de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2563de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 2564da6c28aaSamw 2565de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 2566de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 2567de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2568de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 2569da6c28aaSamw 2570c2a93d44Stimh /* 2571de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 2572c2a93d44Stimh */ 2573de8267e0Stimh if (norm) 2574de8267e0Stimh u8 = 1; 2575de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 2576de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 2577de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2578de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 2579de8267e0Stimh 2580de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 2581de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 2582de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2583de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 2584c2a93d44Stimh 2585ab04eb8eStimh if (is_ci) 2586ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 2587ab04eb8eStimh 2588da6c28aaSamw return (0); 2589fa9e4066Sahrens } 2590fa9e4066Sahrens 25910a48a24eStimh static int 25920a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 25930a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 25940a48a24eStimh { 25950a48a24eStimh boolean_t fuids_ok = B_TRUE; 25960a48a24eStimh uint64_t zplver = ZPL_VERSION; 25970a48a24eStimh objset_t *os = NULL; 25980a48a24eStimh char parentname[MAXNAMELEN]; 25990a48a24eStimh char *cp; 26000a48a24eStimh int error; 26010a48a24eStimh 26020a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 26030a48a24eStimh cp = strrchr(parentname, '/'); 26040a48a24eStimh ASSERT(cp != NULL); 26050a48a24eStimh cp[0] = '\0'; 26060a48a24eStimh 260714843421SMatthew Ahrens if (zfs_earlier_version(dataset, SPA_VERSION_USERSPACE)) 260814843421SMatthew Ahrens zplver = ZPL_VERSION_USERSPACE - 1; 26090a48a24eStimh if (zfs_earlier_version(dataset, SPA_VERSION_FUID)) { 26100a48a24eStimh zplver = ZPL_VERSION_FUID - 1; 26110a48a24eStimh fuids_ok = B_FALSE; 26120a48a24eStimh } 26130a48a24eStimh 26140a48a24eStimh /* 26150a48a24eStimh * Open parent object set so we can inherit zplprop values. 26160a48a24eStimh */ 2617503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 26180a48a24eStimh return (error); 26190a48a24eStimh 26200a48a24eStimh error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, createprops, 26210a48a24eStimh zplprops, is_ci); 2622503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 26230a48a24eStimh return (error); 26240a48a24eStimh } 26250a48a24eStimh 26260a48a24eStimh static int 26270a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 26280a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 26290a48a24eStimh { 26300a48a24eStimh boolean_t fuids_ok = B_TRUE; 26310a48a24eStimh uint64_t zplver = ZPL_VERSION; 26320a48a24eStimh int error; 26330a48a24eStimh 26340a48a24eStimh if (spa_vers < SPA_VERSION_FUID) { 26350a48a24eStimh zplver = ZPL_VERSION_FUID - 1; 26360a48a24eStimh fuids_ok = B_FALSE; 26370a48a24eStimh } 26380a48a24eStimh 26390a48a24eStimh error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, createprops, 26400a48a24eStimh zplprops, is_ci); 26410a48a24eStimh return (error); 26420a48a24eStimh } 26430a48a24eStimh 26443cb34c60Sahrens /* 26453cb34c60Sahrens * inputs: 26463cb34c60Sahrens * zc_objset_type type of objset to create (fs vs zvol) 26473cb34c60Sahrens * zc_name name of new objset 26483cb34c60Sahrens * zc_value name of snapshot to clone from (may be empty) 26493cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 26503cb34c60Sahrens * 2651de8267e0Stimh * outputs: none 26523cb34c60Sahrens */ 2653fa9e4066Sahrens static int 2654fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc) 2655fa9e4066Sahrens { 2656fa9e4066Sahrens objset_t *clone; 2657fa9e4066Sahrens int error = 0; 2658da6c28aaSamw zfs_creat_t zct; 2659ecd6cf80Smarks nvlist_t *nvprops = NULL; 2660ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 2661fa9e4066Sahrens dmu_objset_type_t type = zc->zc_objset_type; 2662fa9e4066Sahrens 2663fa9e4066Sahrens switch (type) { 2664fa9e4066Sahrens 2665fa9e4066Sahrens case DMU_OST_ZFS: 2666fa9e4066Sahrens cbfunc = zfs_create_cb; 2667fa9e4066Sahrens break; 2668fa9e4066Sahrens 2669fa9e4066Sahrens case DMU_OST_ZVOL: 2670fa9e4066Sahrens cbfunc = zvol_create_cb; 2671fa9e4066Sahrens break; 2672fa9e4066Sahrens 2673fa9e4066Sahrens default: 26741d452cf5Sahrens cbfunc = NULL; 2675e7cbe64fSgw break; 2676fa9e4066Sahrens } 2677f18faf3fSek if (strchr(zc->zc_name, '@') || 2678f18faf3fSek strchr(zc->zc_name, '%')) 26791d452cf5Sahrens return (EINVAL); 2680fa9e4066Sahrens 2681e9dbad6fSeschrock if (zc->zc_nvlist_src != NULL && 2682990b4856Slling (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2683478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2684e9dbad6fSeschrock return (error); 2685e9dbad6fSeschrock 2686de8267e0Stimh zct.zct_zplprops = NULL; 2687da6c28aaSamw zct.zct_props = nvprops; 2688da6c28aaSamw 2689e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 2690fa9e4066Sahrens /* 2691fa9e4066Sahrens * We're creating a clone of an existing snapshot. 2692fa9e4066Sahrens */ 2693e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 2694e9dbad6fSeschrock if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) { 2695ecd6cf80Smarks nvlist_free(nvprops); 2696fa9e4066Sahrens return (EINVAL); 2697e9dbad6fSeschrock } 2698fa9e4066Sahrens 2699503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_value, FTAG, &clone); 2700e9dbad6fSeschrock if (error) { 2701ecd6cf80Smarks nvlist_free(nvprops); 2702fa9e4066Sahrens return (error); 2703e9dbad6fSeschrock } 2704ab04eb8eStimh 2705ae46e4c7SMatthew Ahrens error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0); 2706503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 2707da6c28aaSamw if (error) { 2708da6c28aaSamw nvlist_free(nvprops); 2709da6c28aaSamw return (error); 2710da6c28aaSamw } 2711fa9e4066Sahrens } else { 2712ab04eb8eStimh boolean_t is_insensitive = B_FALSE; 2713ab04eb8eStimh 2714e9dbad6fSeschrock if (cbfunc == NULL) { 2715ecd6cf80Smarks nvlist_free(nvprops); 27161d452cf5Sahrens return (EINVAL); 2717e9dbad6fSeschrock } 27185c5460e9Seschrock 2719e9dbad6fSeschrock if (type == DMU_OST_ZVOL) { 2720e9dbad6fSeschrock uint64_t volsize, volblocksize; 2721e9dbad6fSeschrock 2722ecd6cf80Smarks if (nvprops == NULL || 2723ecd6cf80Smarks nvlist_lookup_uint64(nvprops, 2724e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 2725e9dbad6fSeschrock &volsize) != 0) { 2726ecd6cf80Smarks nvlist_free(nvprops); 2727e9dbad6fSeschrock return (EINVAL); 2728e9dbad6fSeschrock } 2729e9dbad6fSeschrock 2730ecd6cf80Smarks if ((error = nvlist_lookup_uint64(nvprops, 2731e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2732e9dbad6fSeschrock &volblocksize)) != 0 && error != ENOENT) { 2733ecd6cf80Smarks nvlist_free(nvprops); 2734e9dbad6fSeschrock return (EINVAL); 2735e9dbad6fSeschrock } 2736e9dbad6fSeschrock 2737e9dbad6fSeschrock if (error != 0) 2738e9dbad6fSeschrock volblocksize = zfs_prop_default_numeric( 2739e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2740e9dbad6fSeschrock 2741e9dbad6fSeschrock if ((error = zvol_check_volblocksize( 2742e9dbad6fSeschrock volblocksize)) != 0 || 2743e9dbad6fSeschrock (error = zvol_check_volsize(volsize, 2744e9dbad6fSeschrock volblocksize)) != 0) { 2745ecd6cf80Smarks nvlist_free(nvprops); 27465c5460e9Seschrock return (error); 2747e9dbad6fSeschrock } 2748e7437265Sahrens } else if (type == DMU_OST_ZFS) { 2749da6c28aaSamw int error; 2750da6c28aaSamw 2751da6c28aaSamw /* 2752da6c28aaSamw * We have to have normalization and 2753da6c28aaSamw * case-folding flags correct when we do the 2754da6c28aaSamw * file system creation, so go figure them out 2755de8267e0Stimh * now. 2756da6c28aaSamw */ 2757de8267e0Stimh VERIFY(nvlist_alloc(&zct.zct_zplprops, 2758de8267e0Stimh NV_UNIQUE_NAME, KM_SLEEP) == 0); 2759de8267e0Stimh error = zfs_fill_zplprops(zc->zc_name, nvprops, 27600a48a24eStimh zct.zct_zplprops, &is_insensitive); 2761da6c28aaSamw if (error != 0) { 2762da6c28aaSamw nvlist_free(nvprops); 2763de8267e0Stimh nvlist_free(zct.zct_zplprops); 2764da6c28aaSamw return (error); 2765da6c28aaSamw } 2766da6c28aaSamw } 2767ae46e4c7SMatthew Ahrens error = dmu_objset_create(zc->zc_name, type, 2768ab04eb8eStimh is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 2769de8267e0Stimh nvlist_free(zct.zct_zplprops); 2770fa9e4066Sahrens } 2771e9dbad6fSeschrock 2772e9dbad6fSeschrock /* 2773e9dbad6fSeschrock * It would be nice to do this atomically. 2774e9dbad6fSeschrock */ 2775e9dbad6fSeschrock if (error == 0) { 2776*92241e0bSTom Erickson error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL, 2777*92241e0bSTom Erickson nvprops, NULL); 2778*92241e0bSTom Erickson if (error != 0) 2779842727c2SChris Kirby (void) dmu_objset_destroy(zc->zc_name, B_FALSE); 2780e9dbad6fSeschrock } 2781ecd6cf80Smarks nvlist_free(nvprops); 2782fa9e4066Sahrens return (error); 2783fa9e4066Sahrens } 2784fa9e4066Sahrens 27853cb34c60Sahrens /* 27863cb34c60Sahrens * inputs: 27873cb34c60Sahrens * zc_name name of filesystem 27883cb34c60Sahrens * zc_value short name of snapshot 27893cb34c60Sahrens * zc_cookie recursive flag 279014843421SMatthew Ahrens * zc_nvlist_src[_size] property list 27913cb34c60Sahrens * 2792681d9761SEric Taylor * outputs: 2793681d9761SEric Taylor * zc_value short snapname (i.e. part after the '@') 27943cb34c60Sahrens */ 2795fa9e4066Sahrens static int 27961d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc) 2797fa9e4066Sahrens { 2798bb0ade09Sahrens nvlist_t *nvprops = NULL; 2799bb0ade09Sahrens int error; 2800bb0ade09Sahrens boolean_t recursive = zc->zc_cookie; 2801bb0ade09Sahrens 2802e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 28031d452cf5Sahrens return (EINVAL); 2804bb0ade09Sahrens 2805bb0ade09Sahrens if (zc->zc_nvlist_src != NULL && 2806bb0ade09Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2807478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2808bb0ade09Sahrens return (error); 2809bb0ade09Sahrens 2810ea2f5b9eSMatthew Ahrens error = zfs_check_userprops(zc->zc_name, nvprops); 2811ea2f5b9eSMatthew Ahrens if (error) 2812ea2f5b9eSMatthew Ahrens goto out; 2813bb0ade09Sahrens 2814*92241e0bSTom Erickson if (!nvlist_empty(nvprops) && 2815ea2f5b9eSMatthew Ahrens zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) { 2816ea2f5b9eSMatthew Ahrens error = ENOTSUP; 2817ea2f5b9eSMatthew Ahrens goto out; 2818bb0ade09Sahrens } 2819ea2f5b9eSMatthew Ahrens 2820ea2f5b9eSMatthew Ahrens error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, 2821ea2f5b9eSMatthew Ahrens nvprops, recursive); 2822ea2f5b9eSMatthew Ahrens 2823ea2f5b9eSMatthew Ahrens out: 2824bb0ade09Sahrens nvlist_free(nvprops); 2825bb0ade09Sahrens return (error); 28261d452cf5Sahrens } 2827fa9e4066Sahrens 2828cdf5b4caSmmusante int 28291d452cf5Sahrens zfs_unmount_snap(char *name, void *arg) 28301d452cf5Sahrens { 28310b69c2f0Sahrens vfs_t *vfsp = NULL; 28321d452cf5Sahrens 2833745cd3c5Smaybee if (arg) { 2834745cd3c5Smaybee char *snapname = arg; 2835745cd3c5Smaybee int len = strlen(name) + strlen(snapname) + 2; 2836745cd3c5Smaybee char *buf = kmem_alloc(len, KM_SLEEP); 28371d452cf5Sahrens 2838745cd3c5Smaybee (void) strcpy(buf, name); 2839745cd3c5Smaybee (void) strcat(buf, "@"); 2840745cd3c5Smaybee (void) strcat(buf, snapname); 2841745cd3c5Smaybee vfsp = zfs_get_vfs(buf); 2842745cd3c5Smaybee kmem_free(buf, len); 28430b69c2f0Sahrens } else if (strchr(name, '@')) { 28441d452cf5Sahrens vfsp = zfs_get_vfs(name); 28451d452cf5Sahrens } 28461d452cf5Sahrens 28471d452cf5Sahrens if (vfsp) { 2848fa9e4066Sahrens /* 28491d452cf5Sahrens * Always force the unmount for snapshots. 2850fa9e4066Sahrens */ 28511d452cf5Sahrens int flag = MS_FORCE; 28521d452cf5Sahrens int err; 28531d452cf5Sahrens 28541d452cf5Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 2855fa9e4066Sahrens VFS_RELE(vfsp); 28561d452cf5Sahrens return (err); 2857fa9e4066Sahrens } 28581d452cf5Sahrens VFS_RELE(vfsp); 28591d452cf5Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0) 28601d452cf5Sahrens return (err); 28611d452cf5Sahrens } 28621d452cf5Sahrens return (0); 28631d452cf5Sahrens } 28641d452cf5Sahrens 28653cb34c60Sahrens /* 28663cb34c60Sahrens * inputs: 2867842727c2SChris Kirby * zc_name name of filesystem 2868842727c2SChris Kirby * zc_value short name of snapshot 2869842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 28703cb34c60Sahrens * 28713cb34c60Sahrens * outputs: none 28723cb34c60Sahrens */ 28731d452cf5Sahrens static int 28741d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc) 28751d452cf5Sahrens { 28761d452cf5Sahrens int err; 28771d452cf5Sahrens 2878e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 28791d452cf5Sahrens return (EINVAL); 28801d452cf5Sahrens err = dmu_objset_find(zc->zc_name, 2881e9dbad6fSeschrock zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); 28821d452cf5Sahrens if (err) 28831d452cf5Sahrens return (err); 2884842727c2SChris Kirby return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value, 2885842727c2SChris Kirby zc->zc_defer_destroy)); 28861d452cf5Sahrens } 28871d452cf5Sahrens 28883cb34c60Sahrens /* 28893cb34c60Sahrens * inputs: 28903cb34c60Sahrens * zc_name name of dataset to destroy 28913cb34c60Sahrens * zc_objset_type type of objset 2892842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 28933cb34c60Sahrens * 28943cb34c60Sahrens * outputs: none 28953cb34c60Sahrens */ 28961d452cf5Sahrens static int 28971d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 28981d452cf5Sahrens { 2899681d9761SEric Taylor int err; 29001d452cf5Sahrens if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) { 2901681d9761SEric Taylor err = zfs_unmount_snap(zc->zc_name, NULL); 29021d452cf5Sahrens if (err) 29031d452cf5Sahrens return (err); 2904fa9e4066Sahrens } 2905fa9e4066Sahrens 2906681d9761SEric Taylor err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy); 2907681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 29085c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 2909681d9761SEric Taylor return (err); 2910fa9e4066Sahrens } 2911fa9e4066Sahrens 29123cb34c60Sahrens /* 29133cb34c60Sahrens * inputs: 29144ccbb6e7Sahrens * zc_name name of dataset to rollback (to most recent snapshot) 29153cb34c60Sahrens * 29163cb34c60Sahrens * outputs: none 29173cb34c60Sahrens */ 2918fa9e4066Sahrens static int 2919fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc) 2920fa9e4066Sahrens { 2921ae46e4c7SMatthew Ahrens dsl_dataset_t *ds, *clone; 29224ccbb6e7Sahrens int error; 2923ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 2924ae46e4c7SMatthew Ahrens char *clone_name; 2925ae46e4c7SMatthew Ahrens 2926ae46e4c7SMatthew Ahrens error = dsl_dataset_hold(zc->zc_name, FTAG, &ds); 2927ae46e4c7SMatthew Ahrens if (error) 2928ae46e4c7SMatthew Ahrens return (error); 2929ae46e4c7SMatthew Ahrens 2930ae46e4c7SMatthew Ahrens /* must not be a snapshot */ 2931ae46e4c7SMatthew Ahrens if (dsl_dataset_is_snapshot(ds)) { 2932ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 2933ae46e4c7SMatthew Ahrens return (EINVAL); 2934ae46e4c7SMatthew Ahrens } 2935ae46e4c7SMatthew Ahrens 2936ae46e4c7SMatthew Ahrens /* must have a most recent snapshot */ 2937ae46e4c7SMatthew Ahrens if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) { 2938ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 2939ae46e4c7SMatthew Ahrens return (EINVAL); 2940ae46e4c7SMatthew Ahrens } 29414ccbb6e7Sahrens 29424ccbb6e7Sahrens /* 2943ae46e4c7SMatthew Ahrens * Create clone of most recent snapshot. 29444ccbb6e7Sahrens */ 2945ae46e4c7SMatthew Ahrens clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name); 2946ae46e4c7SMatthew Ahrens error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT); 29474ccbb6e7Sahrens if (error) 2948ae46e4c7SMatthew Ahrens goto out; 29494ccbb6e7Sahrens 2950503ad85cSMatthew Ahrens error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone); 2951ae46e4c7SMatthew Ahrens if (error) 2952ae46e4c7SMatthew Ahrens goto out; 2953ae46e4c7SMatthew Ahrens 2954ae46e4c7SMatthew Ahrens /* 2955ae46e4c7SMatthew Ahrens * Do clone swap. 2956ae46e4c7SMatthew Ahrens */ 295714843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 2958503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 295947f263f4Sek if (error == 0) { 296047f263f4Sek int resume_err; 29614ccbb6e7Sahrens 2962ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 2963ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, 2964ae46e4c7SMatthew Ahrens B_TRUE); 2965ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 2966ae46e4c7SMatthew Ahrens ds = NULL; 2967ae46e4c7SMatthew Ahrens } else { 2968ae46e4c7SMatthew Ahrens error = EBUSY; 2969ae46e4c7SMatthew Ahrens } 2970503ad85cSMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, zc->zc_name); 297147f263f4Sek error = error ? error : resume_err; 297247f263f4Sek } 29734ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 29744ccbb6e7Sahrens } else { 2975ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 2976ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, B_TRUE); 2977ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 2978ae46e4c7SMatthew Ahrens ds = NULL; 2979ae46e4c7SMatthew Ahrens } else { 2980ae46e4c7SMatthew Ahrens error = EBUSY; 2981ae46e4c7SMatthew Ahrens } 29824ccbb6e7Sahrens } 29834ccbb6e7Sahrens 2984ae46e4c7SMatthew Ahrens /* 2985ae46e4c7SMatthew Ahrens * Destroy clone (which also closes it). 2986ae46e4c7SMatthew Ahrens */ 2987ae46e4c7SMatthew Ahrens (void) dsl_dataset_destroy(clone, FTAG, B_FALSE); 2988ae46e4c7SMatthew Ahrens 2989ae46e4c7SMatthew Ahrens out: 2990ae46e4c7SMatthew Ahrens strfree(clone_name); 2991ae46e4c7SMatthew Ahrens if (ds) 2992ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 29934ccbb6e7Sahrens return (error); 2994fa9e4066Sahrens } 2995fa9e4066Sahrens 29963cb34c60Sahrens /* 29973cb34c60Sahrens * inputs: 29983cb34c60Sahrens * zc_name old name of dataset 29993cb34c60Sahrens * zc_value new name of dataset 30003cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 30013cb34c60Sahrens * 30023cb34c60Sahrens * outputs: none 30033cb34c60Sahrens */ 3004fa9e4066Sahrens static int 3005fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3006fa9e4066Sahrens { 30077f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 3008cdf5b4caSmmusante 3009e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3010f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3011f18faf3fSek strchr(zc->zc_value, '%')) 3012fa9e4066Sahrens return (EINVAL); 3013fa9e4066Sahrens 3014cdf5b4caSmmusante /* 3015cdf5b4caSmmusante * Unmount snapshot unless we're doing a recursive rename, 3016cdf5b4caSmmusante * in which case the dataset code figures out which snapshots 3017cdf5b4caSmmusante * to unmount. 3018cdf5b4caSmmusante */ 3019cdf5b4caSmmusante if (!recursive && strchr(zc->zc_name, '@') != NULL && 3020fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 30211d452cf5Sahrens int err = zfs_unmount_snap(zc->zc_name, NULL); 30221d452cf5Sahrens if (err) 30231d452cf5Sahrens return (err); 3024fa9e4066Sahrens } 3025681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL) 3026681d9761SEric Taylor (void) zvol_remove_minor(zc->zc_name); 3027cdf5b4caSmmusante return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); 3028fa9e4066Sahrens } 3029fa9e4066Sahrens 3030*92241e0bSTom Erickson static int 3031*92241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 3032*92241e0bSTom Erickson { 3033*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 3034*92241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 3035*92241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 3036*92241e0bSTom Erickson uint64_t intval; 3037*92241e0bSTom Erickson int err; 3038*92241e0bSTom Erickson 3039*92241e0bSTom Erickson if (prop == ZPROP_INVAL) { 3040*92241e0bSTom Erickson if (zfs_prop_user(propname)) { 3041*92241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 3042*92241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 3043*92241e0bSTom Erickson return (err); 3044*92241e0bSTom Erickson return (0); 3045*92241e0bSTom Erickson } 3046*92241e0bSTom Erickson 3047*92241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 3048*92241e0bSTom Erickson const char *perm = NULL; 3049*92241e0bSTom Erickson const char *uq_prefix = 3050*92241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 3051*92241e0bSTom Erickson const char *gq_prefix = 3052*92241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 3053*92241e0bSTom Erickson 3054*92241e0bSTom Erickson if (strncmp(propname, uq_prefix, 3055*92241e0bSTom Erickson strlen(uq_prefix)) == 0) { 3056*92241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 3057*92241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 3058*92241e0bSTom Erickson strlen(gq_prefix)) == 0) { 3059*92241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 3060*92241e0bSTom Erickson } else { 3061*92241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 3062*92241e0bSTom Erickson return (EINVAL); 3063*92241e0bSTom Erickson } 3064*92241e0bSTom Erickson 3065*92241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 3066*92241e0bSTom Erickson return (err); 3067*92241e0bSTom Erickson return (0); 3068*92241e0bSTom Erickson } 3069*92241e0bSTom Erickson 3070*92241e0bSTom Erickson return (EINVAL); 3071*92241e0bSTom Erickson } 3072*92241e0bSTom Erickson 3073*92241e0bSTom Erickson if (issnap) 3074*92241e0bSTom Erickson return (EINVAL); 3075*92241e0bSTom Erickson 3076*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 3077*92241e0bSTom Erickson /* 3078*92241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 3079*92241e0bSTom Erickson * format. 3080*92241e0bSTom Erickson */ 3081*92241e0bSTom Erickson nvlist_t *attrs; 3082*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 3083*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 3084*92241e0bSTom Erickson &pair) == 0); 3085*92241e0bSTom Erickson } 3086*92241e0bSTom Erickson 3087*92241e0bSTom Erickson /* 3088*92241e0bSTom Erickson * Check that this value is valid for this pool version 3089*92241e0bSTom Erickson */ 3090*92241e0bSTom Erickson switch (prop) { 3091*92241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 3092*92241e0bSTom Erickson /* 3093*92241e0bSTom Erickson * If the user specified gzip compression, make sure 3094*92241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 3095*92241e0bSTom Erickson * we'll catch them later. 3096*92241e0bSTom Erickson */ 3097*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 3098*92241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 3099*92241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 3100*92241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 3101*92241e0bSTom Erickson zfs_earlier_version(dsname, 3102*92241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 3103*92241e0bSTom Erickson return (ENOTSUP); 3104*92241e0bSTom Erickson } 3105*92241e0bSTom Erickson 3106*92241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 3107*92241e0bSTom Erickson zfs_earlier_version(dsname, 3108*92241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 3109*92241e0bSTom Erickson return (ENOTSUP); 3110*92241e0bSTom Erickson 3111*92241e0bSTom Erickson /* 3112*92241e0bSTom Erickson * If this is a bootable dataset then 3113*92241e0bSTom Erickson * verify that the compression algorithm 3114*92241e0bSTom Erickson * is supported for booting. We must return 3115*92241e0bSTom Erickson * something other than ENOTSUP since it 3116*92241e0bSTom Erickson * implies a downrev pool version. 3117*92241e0bSTom Erickson */ 3118*92241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 3119*92241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 3120*92241e0bSTom Erickson return (ERANGE); 3121*92241e0bSTom Erickson } 3122*92241e0bSTom Erickson } 3123*92241e0bSTom Erickson break; 3124*92241e0bSTom Erickson 3125*92241e0bSTom Erickson case ZFS_PROP_COPIES: 3126*92241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 3127*92241e0bSTom Erickson return (ENOTSUP); 3128*92241e0bSTom Erickson break; 3129*92241e0bSTom Erickson 3130*92241e0bSTom Erickson case ZFS_PROP_DEDUP: 3131*92241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 3132*92241e0bSTom Erickson return (ENOTSUP); 3133*92241e0bSTom Erickson break; 3134*92241e0bSTom Erickson 3135*92241e0bSTom Erickson case ZFS_PROP_SHARESMB: 3136*92241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 3137*92241e0bSTom Erickson return (ENOTSUP); 3138*92241e0bSTom Erickson break; 3139*92241e0bSTom Erickson 3140*92241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 3141*92241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 3142*92241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 3143*92241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 3144*92241e0bSTom Erickson zfs_earlier_version(dsname, 3145*92241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 3146*92241e0bSTom Erickson return (ENOTSUP); 3147*92241e0bSTom Erickson } 3148*92241e0bSTom Erickson break; 3149*92241e0bSTom Erickson } 3150*92241e0bSTom Erickson 3151*92241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 3152*92241e0bSTom Erickson } 3153*92241e0bSTom Erickson 3154*92241e0bSTom Erickson /* 3155*92241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 3156*92241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 3157*92241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 3158*92241e0bSTom Erickson * 3159*92241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 3160*92241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 3161*92241e0bSTom Erickson * of all the properties that failed a permission check along with the 3162*92241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 3163*92241e0bSTom Erickson * returned errlist. 3164*92241e0bSTom Erickson * 3165*92241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 3166*92241e0bSTom Erickson * pointed at by errlist is NULL. 3167*92241e0bSTom Erickson */ 3168*92241e0bSTom Erickson static int 3169*92241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 3170745cd3c5Smaybee { 3171745cd3c5Smaybee zfs_cmd_t *zc; 3172*92241e0bSTom Erickson nvpair_t *pair, *next_pair; 3173*92241e0bSTom Erickson nvlist_t *errors; 3174*92241e0bSTom Erickson int err, rv = 0; 3175745cd3c5Smaybee 3176745cd3c5Smaybee if (props == NULL) 3177*92241e0bSTom Erickson return (0); 3178*92241e0bSTom Erickson 3179*92241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 3180*92241e0bSTom Erickson 3181745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 3182745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 3183*92241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 3184*92241e0bSTom Erickson while (pair != NULL) { 3185*92241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 3186*92241e0bSTom Erickson 3187*92241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 3188*92241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 3189*92241e0bSTom Erickson (err = zfs_secpolicy_inherit(zc, CRED())) != 0) { 3190*92241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 3191*92241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 3192*92241e0bSTom Erickson zc->zc_value, err) == 0); 3193*92241e0bSTom Erickson } 3194*92241e0bSTom Erickson pair = next_pair; 3195745cd3c5Smaybee } 3196745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 3197*92241e0bSTom Erickson 3198*92241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 3199*92241e0bSTom Erickson nvlist_free(errors); 3200*92241e0bSTom Erickson errors = NULL; 3201*92241e0bSTom Erickson } else { 3202*92241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 3203*92241e0bSTom Erickson } 3204*92241e0bSTom Erickson 3205*92241e0bSTom Erickson if (errlist == NULL) 3206*92241e0bSTom Erickson nvlist_free(errors); 3207*92241e0bSTom Erickson else 3208*92241e0bSTom Erickson *errlist = errors; 3209*92241e0bSTom Erickson 3210*92241e0bSTom Erickson return (rv); 3211*92241e0bSTom Erickson } 3212*92241e0bSTom Erickson 3213*92241e0bSTom Erickson static boolean_t 3214*92241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 3215*92241e0bSTom Erickson { 3216*92241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 3217*92241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 3218*92241e0bSTom Erickson nvlist_t *attrs; 3219*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 3220*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 3221*92241e0bSTom Erickson &p1) == 0); 3222*92241e0bSTom Erickson } 3223*92241e0bSTom Erickson 3224*92241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 3225*92241e0bSTom Erickson nvlist_t *attrs; 3226*92241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 3227*92241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 3228*92241e0bSTom Erickson &p2) == 0); 3229*92241e0bSTom Erickson } 3230*92241e0bSTom Erickson 3231*92241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 3232*92241e0bSTom Erickson return (B_FALSE); 3233*92241e0bSTom Erickson 3234*92241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 3235*92241e0bSTom Erickson char *valstr1, *valstr2; 3236*92241e0bSTom Erickson 3237*92241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 3238*92241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 3239*92241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 3240*92241e0bSTom Erickson } else { 3241*92241e0bSTom Erickson uint64_t intval1, intval2; 3242*92241e0bSTom Erickson 3243*92241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 3244*92241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 3245*92241e0bSTom Erickson return (intval1 == intval2); 3246*92241e0bSTom Erickson } 3247745cd3c5Smaybee } 3248745cd3c5Smaybee 3249*92241e0bSTom Erickson /* 3250*92241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 3251*92241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 3252*92241e0bSTom Erickson * do not need to clear or restore properties that won't change. 3253*92241e0bSTom Erickson */ 3254*92241e0bSTom Erickson static void 3255*92241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 3256*92241e0bSTom Erickson { 3257*92241e0bSTom Erickson nvpair_t *pair, *next_pair; 3258*92241e0bSTom Erickson 3259*92241e0bSTom Erickson if (origprops == NULL) 3260*92241e0bSTom Erickson return; /* all props need to be received */ 3261*92241e0bSTom Erickson 3262*92241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 3263*92241e0bSTom Erickson while (pair != NULL) { 3264*92241e0bSTom Erickson const char *propname = nvpair_name(pair); 3265*92241e0bSTom Erickson nvpair_t *match; 3266*92241e0bSTom Erickson 3267*92241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 3268*92241e0bSTom Erickson 3269*92241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 3270*92241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 3271*92241e0bSTom Erickson goto next; /* need to set received value */ 3272*92241e0bSTom Erickson 3273*92241e0bSTom Erickson /* don't clear the existing received value */ 3274*92241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 3275*92241e0bSTom Erickson /* don't bother receiving the property */ 3276*92241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 3277*92241e0bSTom Erickson next: 3278*92241e0bSTom Erickson pair = next_pair; 3279*92241e0bSTom Erickson } 3280*92241e0bSTom Erickson } 3281*92241e0bSTom Erickson 3282*92241e0bSTom Erickson #ifdef DEBUG 3283*92241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 3284*92241e0bSTom Erickson #endif 3285*92241e0bSTom Erickson 32863cb34c60Sahrens /* 32873cb34c60Sahrens * inputs: 32883cb34c60Sahrens * zc_name name of containing filesystem 32893cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 32903cb34c60Sahrens * zc_value name of snapshot to create 32913cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 32923cb34c60Sahrens * zc_cookie file descriptor to recv from 32933cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 32943cb34c60Sahrens * zc_guid force flag 32953cb34c60Sahrens * 32963cb34c60Sahrens * outputs: 32973cb34c60Sahrens * zc_cookie number of bytes read 3298*92241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 3299*92241e0bSTom Erickson * zc_obj zprop_errflags_t 33003cb34c60Sahrens */ 3301fa9e4066Sahrens static int 33023cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 3303fa9e4066Sahrens { 3304fa9e4066Sahrens file_t *fp; 3305f18faf3fSek objset_t *os; 33063cb34c60Sahrens dmu_recv_cookie_t drc; 3307f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 3308*92241e0bSTom Erickson int fd; 3309*92241e0bSTom Erickson int error = 0; 3310*92241e0bSTom Erickson int props_error = 0; 3311*92241e0bSTom Erickson nvlist_t *errors; 33123cb34c60Sahrens offset_t off; 3313*92241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 3314*92241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 33153cb34c60Sahrens objset_t *origin = NULL; 33163cb34c60Sahrens char *tosnap; 33173cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 3318*92241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 3319fa9e4066Sahrens 33203ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3321f18faf3fSek strchr(zc->zc_value, '@') == NULL || 3322f18faf3fSek strchr(zc->zc_value, '%')) 33233ccfa83cSahrens return (EINVAL); 33243ccfa83cSahrens 33253cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 33263cb34c60Sahrens tosnap = strchr(tofs, '@'); 3327*92241e0bSTom Erickson *tosnap++ = '\0'; 33283cb34c60Sahrens 33293cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 33303cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 3331478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 33323cb34c60Sahrens return (error); 33333cb34c60Sahrens 3334fa9e4066Sahrens fd = zc->zc_cookie; 3335fa9e4066Sahrens fp = getf(fd); 33363cb34c60Sahrens if (fp == NULL) { 33373cb34c60Sahrens nvlist_free(props); 3338fa9e4066Sahrens return (EBADF); 33393cb34c60Sahrens } 3340f18faf3fSek 3341*92241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 3342*92241e0bSTom Erickson 3343503ad85cSMatthew Ahrens if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) { 3344*92241e0bSTom Erickson if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) && 3345*92241e0bSTom Erickson !dsl_prop_get_hasrecvd(os)) { 3346*92241e0bSTom Erickson first_recvd_props = B_TRUE; 3347*92241e0bSTom Erickson } 3348*92241e0bSTom Erickson 3349745cd3c5Smaybee /* 3350*92241e0bSTom Erickson * If new received properties are supplied, they are to 3351*92241e0bSTom Erickson * completely replace the existing received properties, so stash 3352*92241e0bSTom Erickson * away the existing ones. 3353745cd3c5Smaybee */ 3354*92241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 3355*92241e0bSTom Erickson nvlist_t *errlist = NULL; 3356*92241e0bSTom Erickson /* 3357*92241e0bSTom Erickson * Don't bother writing a property if its value won't 3358*92241e0bSTom Erickson * change (and avoid the unnecessary security checks). 3359*92241e0bSTom Erickson * 3360*92241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 3361*92241e0bSTom Erickson * special case where we blow away all local properties 3362*92241e0bSTom Erickson * regardless. 3363*92241e0bSTom Erickson */ 3364*92241e0bSTom Erickson if (!first_recvd_props) 3365*92241e0bSTom Erickson props_reduce(props, origprops); 3366*92241e0bSTom Erickson if (zfs_check_clearable(tofs, origprops, 3367*92241e0bSTom Erickson &errlist) != 0) 3368*92241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 3369*92241e0bSTom Erickson nvlist_free(errlist); 3370*92241e0bSTom Erickson } 3371745cd3c5Smaybee 3372503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3373f18faf3fSek } 3374f18faf3fSek 33753cb34c60Sahrens if (zc->zc_string[0]) { 3376503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_string, FTAG, &origin); 3377745cd3c5Smaybee if (error) 3378745cd3c5Smaybee goto out; 33793cb34c60Sahrens } 33803cb34c60Sahrens 33819e69d7d0SLori Alt error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds, 33829e69d7d0SLori Alt &zc->zc_begin_record, force, origin, &drc); 33833cb34c60Sahrens if (origin) 3384503ad85cSMatthew Ahrens dmu_objset_rele(origin, FTAG); 3385745cd3c5Smaybee if (error) 3386745cd3c5Smaybee goto out; 3387f18faf3fSek 3388f18faf3fSek /* 3389*92241e0bSTom Erickson * Set properties before we receive the stream so that they are applied 3390*92241e0bSTom Erickson * to the new data. Note that we must call dmu_recv_stream() if 3391*92241e0bSTom Erickson * dmu_recv_begin() succeeds. 3392f18faf3fSek */ 33933cb34c60Sahrens if (props) { 3394*92241e0bSTom Erickson nvlist_t *errlist; 3395*92241e0bSTom Erickson 3396*92241e0bSTom Erickson if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) { 3397*92241e0bSTom Erickson if (drc.drc_newfs) { 3398*92241e0bSTom Erickson if (spa_version(os->os_spa) >= 3399*92241e0bSTom Erickson SPA_VERSION_RECVD_PROPS) 3400*92241e0bSTom Erickson first_recvd_props = B_TRUE; 3401*92241e0bSTom Erickson } else if (origprops != NULL) { 3402*92241e0bSTom Erickson if (clear_received_props(os, tofs, origprops, 3403*92241e0bSTom Erickson first_recvd_props ? NULL : props) != 0) 3404*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 3405*92241e0bSTom Erickson } else { 3406*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 3407*92241e0bSTom Erickson } 3408*92241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 3409*92241e0bSTom Erickson } else if (!drc.drc_newfs) { 3410*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 3411*92241e0bSTom Erickson } 3412*92241e0bSTom Erickson 3413*92241e0bSTom Erickson (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 3414*92241e0bSTom Erickson props, &errlist); 3415*92241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 3416*92241e0bSTom Erickson nvlist_free(errlist); 3417*92241e0bSTom Erickson } 3418*92241e0bSTom Erickson 3419*92241e0bSTom Erickson if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) { 3420745cd3c5Smaybee /* 3421*92241e0bSTom Erickson * Caller made zc->zc_nvlist_dst less than the minimum expected 3422*92241e0bSTom Erickson * size or supplied an invalid address. 3423745cd3c5Smaybee */ 3424*92241e0bSTom Erickson props_error = EINVAL; 34253cb34c60Sahrens } 34263cb34c60Sahrens 34273cb34c60Sahrens off = fp->f_offset; 34283cb34c60Sahrens error = dmu_recv_stream(&drc, fp->f_vnode, &off); 3429a2eea2e1Sahrens 3430f4b94bdeSMatthew Ahrens if (error == 0) { 3431f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 3432745cd3c5Smaybee 3433f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 3434f4b94bdeSMatthew Ahrens /* online recv */ 3435f4b94bdeSMatthew Ahrens int end_err; 3436745cd3c5Smaybee 3437503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3438f4b94bdeSMatthew Ahrens /* 3439f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 3440f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 3441f4b94bdeSMatthew Ahrens */ 3442f4b94bdeSMatthew Ahrens end_err = dmu_recv_end(&drc); 3443f4b94bdeSMatthew Ahrens if (error == 0) { 3444f4b94bdeSMatthew Ahrens int resume_err = 3445503ad85cSMatthew Ahrens zfs_resume_fs(zfsvfs, tofs); 3446f4b94bdeSMatthew Ahrens error = error ? error : resume_err; 3447f4b94bdeSMatthew Ahrens } 3448f4b94bdeSMatthew Ahrens error = error ? error : end_err; 3449f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 3450745cd3c5Smaybee } else { 3451f4b94bdeSMatthew Ahrens error = dmu_recv_end(&drc); 34523cb34c60Sahrens } 345347f263f4Sek } 34543cb34c60Sahrens 34553cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 34563cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 34573cb34c60Sahrens fp->f_offset = off; 3458a2eea2e1Sahrens 3459*92241e0bSTom Erickson #ifdef DEBUG 3460*92241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 3461*92241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 3462*92241e0bSTom Erickson error = 1; 3463*92241e0bSTom Erickson } 3464*92241e0bSTom Erickson #endif 3465745cd3c5Smaybee /* 3466745cd3c5Smaybee * On error, restore the original props. 3467745cd3c5Smaybee */ 3468745cd3c5Smaybee if (error && props) { 3469*92241e0bSTom Erickson if (dmu_objset_hold(tofs, FTAG, &os) == 0) { 3470*92241e0bSTom Erickson if (clear_received_props(os, tofs, props, NULL) != 0) { 3471*92241e0bSTom Erickson /* 3472*92241e0bSTom Erickson * We failed to clear the received properties. 3473*92241e0bSTom Erickson * Since we may have left a $recvd value on the 3474*92241e0bSTom Erickson * system, we can't clear the $hasrecvd flag. 3475*92241e0bSTom Erickson */ 3476*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 3477*92241e0bSTom Erickson } else if (first_recvd_props) { 3478*92241e0bSTom Erickson dsl_prop_unset_hasrecvd(os); 3479*92241e0bSTom Erickson } 3480*92241e0bSTom Erickson dmu_objset_rele(os, FTAG); 3481*92241e0bSTom Erickson } else if (!drc.drc_newfs) { 3482*92241e0bSTom Erickson /* We failed to clear the received properties. */ 3483*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 3484*92241e0bSTom Erickson } 3485*92241e0bSTom Erickson 3486*92241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 3487*92241e0bSTom Erickson /* We failed to stash the original properties. */ 3488*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 3489*92241e0bSTom Erickson } 3490*92241e0bSTom Erickson 3491*92241e0bSTom Erickson /* 3492*92241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 3493*92241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 3494*92241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 3495*92241e0bSTom Erickson * first new-style receive. 3496*92241e0bSTom Erickson */ 3497*92241e0bSTom Erickson if (origprops != NULL && 3498*92241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 3499*92241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 3500*92241e0bSTom Erickson origprops, NULL) != 0) { 3501*92241e0bSTom Erickson /* 3502*92241e0bSTom Erickson * We stashed the original properties but failed to 3503*92241e0bSTom Erickson * restore them. 3504*92241e0bSTom Erickson */ 3505*92241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 3506*92241e0bSTom Erickson } 3507745cd3c5Smaybee } 3508745cd3c5Smaybee out: 3509745cd3c5Smaybee nvlist_free(props); 3510745cd3c5Smaybee nvlist_free(origprops); 3511*92241e0bSTom Erickson nvlist_free(errors); 3512fa9e4066Sahrens releasef(fd); 3513*92241e0bSTom Erickson 3514*92241e0bSTom Erickson if (error == 0) 3515*92241e0bSTom Erickson error = props_error; 3516*92241e0bSTom Erickson 3517fa9e4066Sahrens return (error); 3518fa9e4066Sahrens } 3519fa9e4066Sahrens 35203cb34c60Sahrens /* 35213cb34c60Sahrens * inputs: 35223cb34c60Sahrens * zc_name name of snapshot to send 35233cb34c60Sahrens * zc_value short name of incremental fromsnap (may be empty) 35243cb34c60Sahrens * zc_cookie file descriptor to send stream to 35253cb34c60Sahrens * zc_obj fromorigin flag (mutually exclusive with zc_value) 35263cb34c60Sahrens * 35273cb34c60Sahrens * outputs: none 35283cb34c60Sahrens */ 3529fa9e4066Sahrens static int 35303cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 3531fa9e4066Sahrens { 3532fa9e4066Sahrens objset_t *fromsnap = NULL; 3533fa9e4066Sahrens objset_t *tosnap; 3534fa9e4066Sahrens file_t *fp; 3535fa9e4066Sahrens int error; 35363cb34c60Sahrens offset_t off; 3537fa9e4066Sahrens 3538503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap); 3539fa9e4066Sahrens if (error) 3540fa9e4066Sahrens return (error); 3541fa9e4066Sahrens 3542e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 35436a0f0066SEric Taylor char *buf; 3544a2eea2e1Sahrens char *cp; 3545a2eea2e1Sahrens 35466a0f0066SEric Taylor buf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 35476a0f0066SEric Taylor (void) strncpy(buf, zc->zc_name, MAXPATHLEN); 3548a2eea2e1Sahrens cp = strchr(buf, '@'); 3549a2eea2e1Sahrens if (cp) 3550a2eea2e1Sahrens *(cp+1) = 0; 35516a0f0066SEric Taylor (void) strncat(buf, zc->zc_value, MAXPATHLEN); 3552503ad85cSMatthew Ahrens error = dmu_objset_hold(buf, FTAG, &fromsnap); 35536a0f0066SEric Taylor kmem_free(buf, MAXPATHLEN); 3554fa9e4066Sahrens if (error) { 3555503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3556fa9e4066Sahrens return (error); 3557fa9e4066Sahrens } 3558fa9e4066Sahrens } 3559fa9e4066Sahrens 3560fa9e4066Sahrens fp = getf(zc->zc_cookie); 3561fa9e4066Sahrens if (fp == NULL) { 3562503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3563fa9e4066Sahrens if (fromsnap) 3564503ad85cSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 3565fa9e4066Sahrens return (EBADF); 3566fa9e4066Sahrens } 3567fa9e4066Sahrens 35683cb34c60Sahrens off = fp->f_offset; 35693cb34c60Sahrens error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off); 3570fa9e4066Sahrens 35713cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 35723cb34c60Sahrens fp->f_offset = off; 3573fa9e4066Sahrens releasef(zc->zc_cookie); 3574fa9e4066Sahrens if (fromsnap) 3575503ad85cSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 3576503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3577fa9e4066Sahrens return (error); 3578fa9e4066Sahrens } 3579fa9e4066Sahrens 3580ea8dc4b6Seschrock static int 3581ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 3582ea8dc4b6Seschrock { 3583ea8dc4b6Seschrock int id, error; 3584ea8dc4b6Seschrock 3585ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 3586ea8dc4b6Seschrock &zc->zc_inject_record); 3587ea8dc4b6Seschrock 3588ea8dc4b6Seschrock if (error == 0) 3589ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 3590ea8dc4b6Seschrock 3591ea8dc4b6Seschrock return (error); 3592ea8dc4b6Seschrock } 3593ea8dc4b6Seschrock 3594ea8dc4b6Seschrock static int 3595ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 3596ea8dc4b6Seschrock { 3597ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 3598ea8dc4b6Seschrock } 3599ea8dc4b6Seschrock 3600ea8dc4b6Seschrock static int 3601ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 3602ea8dc4b6Seschrock { 3603ea8dc4b6Seschrock int id = (int)zc->zc_guid; 3604ea8dc4b6Seschrock int error; 3605ea8dc4b6Seschrock 3606ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 3607ea8dc4b6Seschrock &zc->zc_inject_record); 3608ea8dc4b6Seschrock 3609ea8dc4b6Seschrock zc->zc_guid = id; 3610ea8dc4b6Seschrock 3611ea8dc4b6Seschrock return (error); 3612ea8dc4b6Seschrock } 3613ea8dc4b6Seschrock 3614ea8dc4b6Seschrock static int 3615ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 3616ea8dc4b6Seschrock { 3617ea8dc4b6Seschrock spa_t *spa; 3618ea8dc4b6Seschrock int error; 3619e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 3620ea8dc4b6Seschrock 3621ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 3622ea8dc4b6Seschrock return (error); 3623ea8dc4b6Seschrock 3624e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 3625ea8dc4b6Seschrock &count); 3626ea8dc4b6Seschrock if (error == 0) 3627e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 3628ea8dc4b6Seschrock else 3629e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 3630ea8dc4b6Seschrock 3631ea8dc4b6Seschrock spa_close(spa, FTAG); 3632ea8dc4b6Seschrock 3633ea8dc4b6Seschrock return (error); 3634ea8dc4b6Seschrock } 3635ea8dc4b6Seschrock 3636ea8dc4b6Seschrock static int 3637ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 3638ea8dc4b6Seschrock { 3639ea8dc4b6Seschrock spa_t *spa; 3640ea8dc4b6Seschrock vdev_t *vd; 3641bb8b5132Sek int error; 3642ea8dc4b6Seschrock 3643b87f3af3Sperrin /* 3644b87f3af3Sperrin * On zpool clear we also fix up missing slogs 3645b87f3af3Sperrin */ 3646b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 3647b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 3648b87f3af3Sperrin if (spa == NULL) { 3649b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3650b87f3af3Sperrin return (EIO); 3651b87f3af3Sperrin } 3652b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 3653b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 3654b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 3655b87f3af3Sperrin } 3656468c413aSTim Haley spa->spa_last_open_failed = 0; 3657b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3658b87f3af3Sperrin 3659468c413aSTim Haley if (zc->zc_cookie == ZPOOL_NO_REWIND) { 3660468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 3661468c413aSTim Haley } else { 3662468c413aSTim Haley nvlist_t *policy; 3663468c413aSTim Haley nvlist_t *config = NULL; 3664468c413aSTim Haley 3665468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 3666468c413aSTim Haley return (EINVAL); 3667468c413aSTim Haley 3668468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 3669468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 3670468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 3671468c413aSTim Haley policy, &config); 3672468c413aSTim Haley if (config != NULL) { 3673468c413aSTim Haley (void) put_nvlist(zc, config); 3674468c413aSTim Haley nvlist_free(config); 3675468c413aSTim Haley } 3676468c413aSTim Haley nvlist_free(policy); 3677468c413aSTim Haley } 3678468c413aSTim Haley } 3679468c413aSTim Haley 3680468c413aSTim Haley if (error) 3681ea8dc4b6Seschrock return (error); 3682ea8dc4b6Seschrock 36838f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 3684ea8dc4b6Seschrock 3685e9dbad6fSeschrock if (zc->zc_guid == 0) { 3686ea8dc4b6Seschrock vd = NULL; 3687c5904d13Seschrock } else { 3688c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 3689fa94a07fSbrendan if (vd == NULL) { 3690e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 3691fa94a07fSbrendan spa_close(spa, FTAG); 3692fa94a07fSbrendan return (ENODEV); 3693fa94a07fSbrendan } 3694ea8dc4b6Seschrock } 3695ea8dc4b6Seschrock 3696e14bb325SJeff Bonwick vdev_clear(spa, vd); 3697e14bb325SJeff Bonwick 3698e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 3699ea8dc4b6Seschrock 3700e14bb325SJeff Bonwick /* 3701e14bb325SJeff Bonwick * Resume any suspended I/Os. 3702e14bb325SJeff Bonwick */ 370354d692b7SGeorge Wilson if (zio_resume(spa) != 0) 370454d692b7SGeorge Wilson error = EIO; 3705ea8dc4b6Seschrock 3706ea8dc4b6Seschrock spa_close(spa, FTAG); 3707ea8dc4b6Seschrock 370854d692b7SGeorge Wilson return (error); 3709ea8dc4b6Seschrock } 3710ea8dc4b6Seschrock 37113cb34c60Sahrens /* 37123cb34c60Sahrens * inputs: 37133cb34c60Sahrens * zc_name name of filesystem 37143cb34c60Sahrens * zc_value name of origin snapshot 37153cb34c60Sahrens * 3716681d9761SEric Taylor * outputs: 3717681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 37183cb34c60Sahrens */ 371999653d4eSeschrock static int 372099653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 372199653d4eSeschrock { 37220b69c2f0Sahrens char *cp; 37230b69c2f0Sahrens 37240b69c2f0Sahrens /* 37250b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 37260b69c2f0Sahrens * it's easier. 37270b69c2f0Sahrens */ 3728e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 37290b69c2f0Sahrens if (cp) 37300b69c2f0Sahrens *cp = '\0'; 3731e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 37320b69c2f0Sahrens zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); 3733681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 373499653d4eSeschrock } 373599653d4eSeschrock 373614843421SMatthew Ahrens /* 373714843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 373814843421SMatthew Ahrens * 373914843421SMatthew Ahrens * inputs: 374014843421SMatthew Ahrens * zc_name name of filesystem 374114843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 374214843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 374314843421SMatthew Ahrens * zc_guid RID/UID/GID 374414843421SMatthew Ahrens * 374514843421SMatthew Ahrens * outputs: 374614843421SMatthew Ahrens * zc_cookie property value 374714843421SMatthew Ahrens */ 374814843421SMatthew Ahrens static int 374914843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 375014843421SMatthew Ahrens { 375114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 375214843421SMatthew Ahrens int error; 375314843421SMatthew Ahrens 375414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 375514843421SMatthew Ahrens return (EINVAL); 375614843421SMatthew Ahrens 3757503ad85cSMatthew Ahrens error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs); 375814843421SMatthew Ahrens if (error) 375914843421SMatthew Ahrens return (error); 376014843421SMatthew Ahrens 376114843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 376214843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 376314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 376414843421SMatthew Ahrens 376514843421SMatthew Ahrens return (error); 376614843421SMatthew Ahrens } 376714843421SMatthew Ahrens 376814843421SMatthew Ahrens /* 376914843421SMatthew Ahrens * inputs: 377014843421SMatthew Ahrens * zc_name name of filesystem 377114843421SMatthew Ahrens * zc_cookie zap cursor 377214843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 377314843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 377414843421SMatthew Ahrens * 377514843421SMatthew Ahrens * outputs: 377614843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 377714843421SMatthew Ahrens * zc_cookie zap cursor 377814843421SMatthew Ahrens */ 377914843421SMatthew Ahrens static int 378014843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 378114843421SMatthew Ahrens { 378214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 378314843421SMatthew Ahrens int error; 378414843421SMatthew Ahrens 3785503ad85cSMatthew Ahrens error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs); 378614843421SMatthew Ahrens if (error) 378714843421SMatthew Ahrens return (error); 378814843421SMatthew Ahrens 378914843421SMatthew Ahrens int bufsize = zc->zc_nvlist_dst_size; 379014843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 379114843421SMatthew Ahrens 379214843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 379314843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 379414843421SMatthew Ahrens 379514843421SMatthew Ahrens if (error == 0) { 379614843421SMatthew Ahrens error = xcopyout(buf, 379714843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 379814843421SMatthew Ahrens zc->zc_nvlist_dst_size); 379914843421SMatthew Ahrens } 380014843421SMatthew Ahrens kmem_free(buf, bufsize); 380114843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 380214843421SMatthew Ahrens 380314843421SMatthew Ahrens return (error); 380414843421SMatthew Ahrens } 380514843421SMatthew Ahrens 380614843421SMatthew Ahrens /* 380714843421SMatthew Ahrens * inputs: 380814843421SMatthew Ahrens * zc_name name of filesystem 380914843421SMatthew Ahrens * 381014843421SMatthew Ahrens * outputs: 381114843421SMatthew Ahrens * none 381214843421SMatthew Ahrens */ 381314843421SMatthew Ahrens static int 381414843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 381514843421SMatthew Ahrens { 381614843421SMatthew Ahrens objset_t *os; 381714843421SMatthew Ahrens int error; 381814843421SMatthew Ahrens zfsvfs_t *zfsvfs; 381914843421SMatthew Ahrens 382014843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 3821503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 382214843421SMatthew Ahrens /* 382314843421SMatthew Ahrens * If userused is not enabled, it may be because the 382414843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 382514843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 382614843421SMatthew Ahrens */ 3827503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3828503ad85cSMatthew Ahrens if (error == 0) 3829503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 383014843421SMatthew Ahrens } 383114843421SMatthew Ahrens if (error == 0) 383214843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 383314843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 383414843421SMatthew Ahrens } else { 3835503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 3836503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 383714843421SMatthew Ahrens if (error) 383814843421SMatthew Ahrens return (error); 383914843421SMatthew Ahrens 384014843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 3841503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 384214843421SMatthew Ahrens } 384314843421SMatthew Ahrens 384414843421SMatthew Ahrens return (error); 384514843421SMatthew Ahrens } 384614843421SMatthew Ahrens 3847ecd6cf80Smarks /* 3848ecd6cf80Smarks * We don't want to have a hard dependency 3849ecd6cf80Smarks * against some special symbols in sharefs 3850da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 3851ecd6cf80Smarks * the first file system is shared. 3852da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 3853ecd6cf80Smarks */ 3854da6c28aaSamw int (*znfsexport_fs)(void *arg); 3855ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 3856da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 3857da6c28aaSamw 3858da6c28aaSamw int zfs_nfsshare_inited; 3859da6c28aaSamw int zfs_smbshare_inited; 3860ecd6cf80Smarks 3861ecd6cf80Smarks ddi_modhandle_t nfs_mod; 3862ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 3863da6c28aaSamw ddi_modhandle_t smbsrv_mod; 3864ecd6cf80Smarks kmutex_t zfs_share_lock; 3865ecd6cf80Smarks 3866da6c28aaSamw static int 3867da6c28aaSamw zfs_init_sharefs() 3868da6c28aaSamw { 3869da6c28aaSamw int error; 3870da6c28aaSamw 3871da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 3872da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 3873da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 3874da6c28aaSamw ddi_modopen("fs/sharefs", 3875da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3876da6c28aaSamw return (ENOSYS); 3877da6c28aaSamw } 3878da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 3879da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 3880da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 3881da6c28aaSamw return (ENOSYS); 3882da6c28aaSamw } 3883da6c28aaSamw return (0); 3884da6c28aaSamw } 3885da6c28aaSamw 3886ecd6cf80Smarks static int 3887ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 3888ecd6cf80Smarks { 3889ecd6cf80Smarks int error; 3890ecd6cf80Smarks int opcode; 3891ecd6cf80Smarks 3892da6c28aaSamw switch (zc->zc_share.z_sharetype) { 3893da6c28aaSamw case ZFS_SHARE_NFS: 3894da6c28aaSamw case ZFS_UNSHARE_NFS: 3895da6c28aaSamw if (zfs_nfsshare_inited == 0) { 3896da6c28aaSamw mutex_enter(&zfs_share_lock); 3897da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 3898da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3899da6c28aaSamw mutex_exit(&zfs_share_lock); 3900da6c28aaSamw return (ENOSYS); 3901da6c28aaSamw } 3902da6c28aaSamw if (znfsexport_fs == NULL && 3903da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 3904da6c28aaSamw ddi_modsym(nfs_mod, 3905da6c28aaSamw "nfs_export", &error)) == NULL)) { 3906da6c28aaSamw mutex_exit(&zfs_share_lock); 3907da6c28aaSamw return (ENOSYS); 3908da6c28aaSamw } 3909da6c28aaSamw error = zfs_init_sharefs(); 3910da6c28aaSamw if (error) { 3911da6c28aaSamw mutex_exit(&zfs_share_lock); 3912da6c28aaSamw return (ENOSYS); 3913da6c28aaSamw } 3914da6c28aaSamw zfs_nfsshare_inited = 1; 3915ecd6cf80Smarks mutex_exit(&zfs_share_lock); 3916ecd6cf80Smarks } 3917da6c28aaSamw break; 3918da6c28aaSamw case ZFS_SHARE_SMB: 3919da6c28aaSamw case ZFS_UNSHARE_SMB: 3920da6c28aaSamw if (zfs_smbshare_inited == 0) { 3921da6c28aaSamw mutex_enter(&zfs_share_lock); 3922da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 3923da6c28aaSamw ddi_modopen("drv/smbsrv", 3924da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3925da6c28aaSamw mutex_exit(&zfs_share_lock); 3926da6c28aaSamw return (ENOSYS); 3927da6c28aaSamw } 3928da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 3929da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 3930faa1795aSjb "smb_server_share", &error)) == NULL)) { 3931da6c28aaSamw mutex_exit(&zfs_share_lock); 3932da6c28aaSamw return (ENOSYS); 3933da6c28aaSamw } 3934da6c28aaSamw error = zfs_init_sharefs(); 3935da6c28aaSamw if (error) { 3936da6c28aaSamw mutex_exit(&zfs_share_lock); 3937da6c28aaSamw return (ENOSYS); 3938da6c28aaSamw } 3939da6c28aaSamw zfs_smbshare_inited = 1; 3940ecd6cf80Smarks mutex_exit(&zfs_share_lock); 3941ecd6cf80Smarks } 3942da6c28aaSamw break; 3943da6c28aaSamw default: 3944da6c28aaSamw return (EINVAL); 3945da6c28aaSamw } 3946ecd6cf80Smarks 3947da6c28aaSamw switch (zc->zc_share.z_sharetype) { 3948da6c28aaSamw case ZFS_SHARE_NFS: 3949da6c28aaSamw case ZFS_UNSHARE_NFS: 3950da6c28aaSamw if (error = 3951da6c28aaSamw znfsexport_fs((void *) 3952da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 3953da6c28aaSamw return (error); 3954da6c28aaSamw break; 3955da6c28aaSamw case ZFS_SHARE_SMB: 3956da6c28aaSamw case ZFS_UNSHARE_SMB: 3957da6c28aaSamw if (error = zsmbexport_fs((void *) 3958da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 3959da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 3960743a77edSAlan Wright B_TRUE: B_FALSE)) { 3961da6c28aaSamw return (error); 3962ecd6cf80Smarks } 3963da6c28aaSamw break; 3964ecd6cf80Smarks } 3965ecd6cf80Smarks 3966da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 3967da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 3968ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 3969ecd6cf80Smarks 3970da6c28aaSamw /* 3971da6c28aaSamw * Add or remove share from sharetab 3972da6c28aaSamw */ 3973ecd6cf80Smarks error = zshare_fs(opcode, 3974ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 3975ecd6cf80Smarks zc->zc_share.z_sharemax); 3976ecd6cf80Smarks 3977ecd6cf80Smarks return (error); 3978ecd6cf80Smarks 3979ecd6cf80Smarks } 3980ecd6cf80Smarks 3981743a77edSAlan Wright ace_t full_access[] = { 3982743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 3983743a77edSAlan Wright }; 3984743a77edSAlan Wright 3985743a77edSAlan Wright /* 3986743a77edSAlan Wright * Remove all ACL files in shares dir 3987743a77edSAlan Wright */ 3988743a77edSAlan Wright static int 3989743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 3990743a77edSAlan Wright { 3991743a77edSAlan Wright zap_cursor_t zc; 3992743a77edSAlan Wright zap_attribute_t zap; 3993743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 3994743a77edSAlan Wright int error; 3995743a77edSAlan Wright 3996743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 3997743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 3998743a77edSAlan Wright zap_cursor_advance(&zc)) { 3999743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 4000743a77edSAlan Wright NULL, 0)) != 0) 4001743a77edSAlan Wright break; 4002743a77edSAlan Wright } 4003743a77edSAlan Wright zap_cursor_fini(&zc); 4004743a77edSAlan Wright return (error); 4005743a77edSAlan Wright } 4006743a77edSAlan Wright 4007743a77edSAlan Wright static int 4008743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 4009743a77edSAlan Wright { 4010743a77edSAlan Wright vnode_t *vp; 4011743a77edSAlan Wright znode_t *dzp; 4012743a77edSAlan Wright vnode_t *resourcevp = NULL; 4013743a77edSAlan Wright znode_t *sharedir; 4014743a77edSAlan Wright zfsvfs_t *zfsvfs; 4015743a77edSAlan Wright nvlist_t *nvlist; 4016743a77edSAlan Wright char *src, *target; 4017743a77edSAlan Wright vattr_t vattr; 4018743a77edSAlan Wright vsecattr_t vsec; 4019743a77edSAlan Wright int error = 0; 4020743a77edSAlan Wright 4021743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 4022743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 4023743a77edSAlan Wright return (error); 4024743a77edSAlan Wright 4025743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 4026743a77edSAlan Wright 4027743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 4028743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 4029743a77edSAlan Wright zc->zc_name) != 0)) { 4030743a77edSAlan Wright VN_RELE(vp); 4031743a77edSAlan Wright return (EINVAL); 4032743a77edSAlan Wright } 4033743a77edSAlan Wright 4034743a77edSAlan Wright dzp = VTOZ(vp); 4035743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 4036743a77edSAlan Wright ZFS_ENTER(zfsvfs); 4037743a77edSAlan Wright 40389e1320c0SMark Shellenbaum /* 40399e1320c0SMark Shellenbaum * Create share dir if its missing. 40409e1320c0SMark Shellenbaum */ 40419e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 40429e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 40439e1320c0SMark Shellenbaum dmu_tx_t *tx; 40449e1320c0SMark Shellenbaum 40459e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 40469e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 40479e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 40489e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 40499e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 40509e1320c0SMark Shellenbaum if (error) { 40519e1320c0SMark Shellenbaum dmu_tx_abort(tx); 40529e1320c0SMark Shellenbaum } else { 40539e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 40549e1320c0SMark Shellenbaum dmu_tx_commit(tx); 40559e1320c0SMark Shellenbaum } 40569e1320c0SMark Shellenbaum if (error) { 40579e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 40589e1320c0SMark Shellenbaum VN_RELE(vp); 40599e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 40609e1320c0SMark Shellenbaum return (error); 40619e1320c0SMark Shellenbaum } 40629e1320c0SMark Shellenbaum } 40639e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 40649e1320c0SMark Shellenbaum 40659e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 4066743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 40679e1320c0SMark Shellenbaum VN_RELE(vp); 4068743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4069743a77edSAlan Wright return (error); 4070743a77edSAlan Wright } 4071743a77edSAlan Wright 4072743a77edSAlan Wright switch (zc->zc_cookie) { 4073743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 4074743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 4075743a77edSAlan Wright vattr.va_type = VREG; 4076743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 4077743a77edSAlan Wright vattr.va_uid = 0; 4078743a77edSAlan Wright vattr.va_gid = 0; 4079743a77edSAlan Wright 4080743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 4081743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 4082743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 4083743a77edSAlan Wright vsec.vsa_aclcnt = 1; 4084743a77edSAlan Wright 4085743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 4086743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 4087743a77edSAlan Wright if (resourcevp) 4088743a77edSAlan Wright VN_RELE(resourcevp); 4089743a77edSAlan Wright break; 4090743a77edSAlan Wright 4091743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 4092743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 4093743a77edSAlan Wright NULL, 0); 4094743a77edSAlan Wright break; 4095743a77edSAlan Wright 4096743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 4097743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 4098478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 4099743a77edSAlan Wright VN_RELE(vp); 4100743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4101743a77edSAlan Wright return (error); 4102743a77edSAlan Wright } 4103743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 4104743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 4105743a77edSAlan Wright &target)) { 4106743a77edSAlan Wright VN_RELE(vp); 410789459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 4108743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4109743a77edSAlan Wright return (error); 4110743a77edSAlan Wright } 4111743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 4112743a77edSAlan Wright kcred, NULL, 0); 4113743a77edSAlan Wright nvlist_free(nvlist); 4114743a77edSAlan Wright break; 4115743a77edSAlan Wright 4116743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 4117743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 4118743a77edSAlan Wright break; 4119743a77edSAlan Wright 4120743a77edSAlan Wright default: 4121743a77edSAlan Wright error = EINVAL; 4122743a77edSAlan Wright break; 4123743a77edSAlan Wright } 4124743a77edSAlan Wright 4125743a77edSAlan Wright VN_RELE(vp); 4126743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 4127743a77edSAlan Wright 4128743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4129743a77edSAlan Wright 4130743a77edSAlan Wright return (error); 4131743a77edSAlan Wright } 4132743a77edSAlan Wright 4133842727c2SChris Kirby /* 4134842727c2SChris Kirby * inputs: 4135842727c2SChris Kirby * zc_name name of filesystem 4136842727c2SChris Kirby * zc_value short name of snap 4137842727c2SChris Kirby * zc_string user-supplied tag for this reference 4138842727c2SChris Kirby * zc_cookie recursive flag 4139ca45db41SChris Kirby * zc_temphold set if hold is temporary 4140842727c2SChris Kirby * 4141842727c2SChris Kirby * outputs: none 4142842727c2SChris Kirby */ 4143842727c2SChris Kirby static int 4144842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc) 4145842727c2SChris Kirby { 4146842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4147842727c2SChris Kirby 4148842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4149842727c2SChris Kirby return (EINVAL); 4150842727c2SChris Kirby 4151842727c2SChris Kirby return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value, 4152ca45db41SChris Kirby zc->zc_string, recursive, zc->zc_temphold)); 4153842727c2SChris Kirby } 4154842727c2SChris Kirby 4155842727c2SChris Kirby /* 4156842727c2SChris Kirby * inputs: 4157842727c2SChris Kirby * zc_name name of dataset from which we're releasing a user reference 4158842727c2SChris Kirby * zc_value short name of snap 4159842727c2SChris Kirby * zc_string user-supplied tag for this reference 4160842727c2SChris Kirby * zc_cookie recursive flag 4161842727c2SChris Kirby * 4162842727c2SChris Kirby * outputs: none 4163842727c2SChris Kirby */ 4164842727c2SChris Kirby static int 4165842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc) 4166842727c2SChris Kirby { 4167842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4168842727c2SChris Kirby 4169842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4170842727c2SChris Kirby return (EINVAL); 4171842727c2SChris Kirby 4172842727c2SChris Kirby return (dsl_dataset_user_release(zc->zc_name, zc->zc_value, 4173842727c2SChris Kirby zc->zc_string, recursive)); 4174842727c2SChris Kirby } 4175842727c2SChris Kirby 4176842727c2SChris Kirby /* 4177842727c2SChris Kirby * inputs: 4178842727c2SChris Kirby * zc_name name of filesystem 4179842727c2SChris Kirby * 4180842727c2SChris Kirby * outputs: 4181842727c2SChris Kirby * zc_nvlist_src{_size} nvlist of snapshot holds 4182842727c2SChris Kirby */ 4183842727c2SChris Kirby static int 4184842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc) 4185842727c2SChris Kirby { 4186842727c2SChris Kirby nvlist_t *nvp; 4187842727c2SChris Kirby int error; 4188842727c2SChris Kirby 4189842727c2SChris Kirby if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) { 4190842727c2SChris Kirby error = put_nvlist(zc, nvp); 4191842727c2SChris Kirby nvlist_free(nvp); 4192842727c2SChris Kirby } 4193842727c2SChris Kirby 4194842727c2SChris Kirby return (error); 4195842727c2SChris Kirby } 4196842727c2SChris Kirby 4197ecd6cf80Smarks /* 41982a6b87f0Sek * pool create, destroy, and export don't log the history as part of 41992a6b87f0Sek * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export 42002a6b87f0Sek * do the logging of those commands. 4201ecd6cf80Smarks */ 4202fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = { 420354d692b7SGeorge Wilson { zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE, 420454d692b7SGeorge Wilson B_FALSE }, 420554d692b7SGeorge Wilson { zfs_ioc_pool_destroy, zfs_secpolicy_config, POOL_NAME, B_FALSE, 420654d692b7SGeorge Wilson B_FALSE }, 420754d692b7SGeorge Wilson { zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE, 420854d692b7SGeorge Wilson B_FALSE }, 420954d692b7SGeorge Wilson { zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE, 421054d692b7SGeorge Wilson B_FALSE }, 421154d692b7SGeorge Wilson { zfs_ioc_pool_configs, zfs_secpolicy_none, NO_NAME, B_FALSE, 421254d692b7SGeorge Wilson B_FALSE }, 421354d692b7SGeorge Wilson { zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE, 421454d692b7SGeorge Wilson B_FALSE }, 421554d692b7SGeorge Wilson { zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE, 421654d692b7SGeorge Wilson B_FALSE }, 421754d692b7SGeorge Wilson { zfs_ioc_pool_scrub, zfs_secpolicy_config, POOL_NAME, B_TRUE, 421854d692b7SGeorge Wilson B_TRUE }, 421954d692b7SGeorge Wilson { zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE, 422054d692b7SGeorge Wilson B_FALSE }, 422154d692b7SGeorge Wilson { zfs_ioc_pool_upgrade, zfs_secpolicy_config, POOL_NAME, B_TRUE, 422254d692b7SGeorge Wilson B_TRUE }, 422354d692b7SGeorge Wilson { zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE, 422454d692b7SGeorge Wilson B_FALSE }, 422554d692b7SGeorge Wilson { zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE, 422654d692b7SGeorge Wilson B_TRUE }, 422754d692b7SGeorge Wilson { zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE, 422854d692b7SGeorge Wilson B_TRUE }, 422954d692b7SGeorge Wilson { zfs_ioc_vdev_set_state, zfs_secpolicy_config, POOL_NAME, B_TRUE, 423054d692b7SGeorge Wilson B_FALSE }, 423154d692b7SGeorge Wilson { zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 423254d692b7SGeorge Wilson B_TRUE }, 423354d692b7SGeorge Wilson { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 423454d692b7SGeorge Wilson B_TRUE }, 423554d692b7SGeorge Wilson { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE, 423654d692b7SGeorge Wilson B_TRUE }, 42376809eb4eSEric Schrock { zfs_ioc_vdev_setfru, zfs_secpolicy_config, POOL_NAME, B_FALSE, 42386809eb4eSEric Schrock B_TRUE }, 423954d692b7SGeorge Wilson { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 424054d692b7SGeorge Wilson B_FALSE }, 424154d692b7SGeorge Wilson { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 424254d692b7SGeorge Wilson B_FALSE }, 424354d692b7SGeorge Wilson { zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 424454d692b7SGeorge Wilson B_FALSE }, 424554d692b7SGeorge Wilson { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 424654d692b7SGeorge Wilson B_FALSE }, 424754d692b7SGeorge Wilson { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE }, 424854d692b7SGeorge Wilson { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE }, 424954d692b7SGeorge Wilson { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, 425054d692b7SGeorge Wilson B_TRUE}, 425154d692b7SGeorge Wilson { zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE, 425254d692b7SGeorge Wilson B_TRUE }, 425354d692b7SGeorge Wilson { zfs_ioc_rename, zfs_secpolicy_rename, DATASET_NAME, B_TRUE, B_TRUE }, 425454d692b7SGeorge Wilson { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE }, 425554d692b7SGeorge Wilson { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE }, 425654d692b7SGeorge Wilson { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 425754d692b7SGeorge Wilson B_FALSE }, 425854d692b7SGeorge Wilson { zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 425954d692b7SGeorge Wilson B_FALSE }, 426054d692b7SGeorge Wilson { zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE, 426154d692b7SGeorge Wilson B_FALSE }, 426254d692b7SGeorge Wilson { zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE, 426354d692b7SGeorge Wilson B_FALSE }, 426454d692b7SGeorge Wilson { zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE }, 426554d692b7SGeorge Wilson { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE, 426654d692b7SGeorge Wilson B_TRUE }, 426754d692b7SGeorge Wilson { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, 426854d692b7SGeorge Wilson B_TRUE }, 426954d692b7SGeorge Wilson { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE, 427054d692b7SGeorge Wilson B_TRUE }, 427154d692b7SGeorge Wilson { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE, 427254d692b7SGeorge Wilson B_FALSE }, 42736e8a0f56SGeorge Wilson { zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE, 42746e8a0f56SGeorge Wilson B_TRUE }, 427554d692b7SGeorge Wilson { zfs_ioc_pool_set_props, zfs_secpolicy_config, POOL_NAME, B_TRUE, 427654d692b7SGeorge Wilson B_TRUE }, 427754d692b7SGeorge Wilson { zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE, 427854d692b7SGeorge Wilson B_FALSE }, 427954d692b7SGeorge Wilson { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE, 428054d692b7SGeorge Wilson B_TRUE }, 428154d692b7SGeorge Wilson { zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 428254d692b7SGeorge Wilson B_FALSE }, 428354d692b7SGeorge Wilson { zfs_ioc_iscsi_perm_check, zfs_secpolicy_iscsi, DATASET_NAME, B_FALSE, 428454d692b7SGeorge Wilson B_FALSE }, 428554d692b7SGeorge Wilson { zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE }, 428654d692b7SGeorge Wilson { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE, 428754d692b7SGeorge Wilson B_TRUE }, 428854d692b7SGeorge Wilson { zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE, 428914843421SMatthew Ahrens B_FALSE }, 429014843421SMatthew Ahrens { zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, 429114843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_FALSE }, 429214843421SMatthew Ahrens { zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, 429314843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_FALSE }, 429414843421SMatthew Ahrens { zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 429514843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_TRUE }, 4296842727c2SChris Kirby { zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE }, 4297842727c2SChris Kirby { zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE, 4298842727c2SChris Kirby B_TRUE }, 4299842727c2SChris Kirby { zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4300*92241e0bSTom Erickson B_TRUE }, 4301*92241e0bSTom Erickson { zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4302*92241e0bSTom Erickson B_FALSE } 4303fa9e4066Sahrens }; 4304fa9e4066Sahrens 430554d692b7SGeorge Wilson int 430654d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type) 430754d692b7SGeorge Wilson { 430854d692b7SGeorge Wilson spa_t *spa; 430954d692b7SGeorge Wilson int error; 431054d692b7SGeorge Wilson 431154d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 431254d692b7SGeorge Wilson 431314843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 431454d692b7SGeorge Wilson if (error == 0) { 431554d692b7SGeorge Wilson if (spa_suspended(spa)) 431654d692b7SGeorge Wilson error = EAGAIN; 431754d692b7SGeorge Wilson spa_close(spa, FTAG); 431854d692b7SGeorge Wilson } 431954d692b7SGeorge Wilson return (error); 432054d692b7SGeorge Wilson } 432154d692b7SGeorge Wilson 4322fa9e4066Sahrens static int 4323fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 4324fa9e4066Sahrens { 4325fa9e4066Sahrens zfs_cmd_t *zc; 4326fa9e4066Sahrens uint_t vec; 43271d452cf5Sahrens int error, rc; 4328fa9e4066Sahrens 4329fa9e4066Sahrens if (getminor(dev) != 0) 4330fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 4331fa9e4066Sahrens 4332fa9e4066Sahrens vec = cmd - ZFS_IOC; 433391ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 4334fa9e4066Sahrens 4335fa9e4066Sahrens if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 4336fa9e4066Sahrens return (EINVAL); 4337fa9e4066Sahrens 4338fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 4339fa9e4066Sahrens 4340478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 4341fa9e4066Sahrens 4342681d9761SEric Taylor if ((error == 0) && !(flag & FKIOCTL)) 4343ecd6cf80Smarks error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr); 4344fa9e4066Sahrens 4345fa9e4066Sahrens /* 4346fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 4347fa9e4066Sahrens * the lower layers. 4348fa9e4066Sahrens */ 4349fa9e4066Sahrens if (error == 0) { 4350fa9e4066Sahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 4351478ed9adSEric Taylor zc->zc_iflags = flag & FKIOCTL; 4352fa9e4066Sahrens switch (zfs_ioc_vec[vec].zvec_namecheck) { 4353e7437265Sahrens case POOL_NAME: 4354fa9e4066Sahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 4355fa9e4066Sahrens error = EINVAL; 435654d692b7SGeorge Wilson if (zfs_ioc_vec[vec].zvec_pool_check) 435754d692b7SGeorge Wilson error = pool_status_check(zc->zc_name, 435854d692b7SGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck); 4359fa9e4066Sahrens break; 4360fa9e4066Sahrens 4361e7437265Sahrens case DATASET_NAME: 4362fa9e4066Sahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 4363fa9e4066Sahrens error = EINVAL; 436454d692b7SGeorge Wilson if (zfs_ioc_vec[vec].zvec_pool_check) 436554d692b7SGeorge Wilson error = pool_status_check(zc->zc_name, 436654d692b7SGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck); 4367fa9e4066Sahrens break; 43685ad82045Snd 4369e7437265Sahrens case NO_NAME: 43705ad82045Snd break; 4371fa9e4066Sahrens } 4372fa9e4066Sahrens } 4373fa9e4066Sahrens 4374fa9e4066Sahrens if (error == 0) 4375fa9e4066Sahrens error = zfs_ioc_vec[vec].zvec_func(zc); 4376fa9e4066Sahrens 4377478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 4378ecd6cf80Smarks if (error == 0) { 43791d452cf5Sahrens error = rc; 438014843421SMatthew Ahrens if (zfs_ioc_vec[vec].zvec_his_log) 4381ecd6cf80Smarks zfs_log_history(zc); 4382ecd6cf80Smarks } 4383fa9e4066Sahrens 4384fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 4385fa9e4066Sahrens return (error); 4386fa9e4066Sahrens } 4387fa9e4066Sahrens 4388fa9e4066Sahrens static int 4389fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4390fa9e4066Sahrens { 4391fa9e4066Sahrens if (cmd != DDI_ATTACH) 4392fa9e4066Sahrens return (DDI_FAILURE); 4393fa9e4066Sahrens 4394fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 4395fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 4396fa9e4066Sahrens return (DDI_FAILURE); 4397fa9e4066Sahrens 4398fa9e4066Sahrens zfs_dip = dip; 4399fa9e4066Sahrens 4400fa9e4066Sahrens ddi_report_dev(dip); 4401fa9e4066Sahrens 4402fa9e4066Sahrens return (DDI_SUCCESS); 4403fa9e4066Sahrens } 4404fa9e4066Sahrens 4405fa9e4066Sahrens static int 4406fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 4407fa9e4066Sahrens { 4408fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 4409fa9e4066Sahrens return (DDI_FAILURE); 4410fa9e4066Sahrens 4411fa9e4066Sahrens if (cmd != DDI_DETACH) 4412fa9e4066Sahrens return (DDI_FAILURE); 4413fa9e4066Sahrens 4414fa9e4066Sahrens zfs_dip = NULL; 4415fa9e4066Sahrens 4416fa9e4066Sahrens ddi_prop_remove_all(dip); 4417fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 4418fa9e4066Sahrens 4419fa9e4066Sahrens return (DDI_SUCCESS); 4420fa9e4066Sahrens } 4421fa9e4066Sahrens 4422fa9e4066Sahrens /*ARGSUSED*/ 4423fa9e4066Sahrens static int 4424fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 4425fa9e4066Sahrens { 4426fa9e4066Sahrens switch (infocmd) { 4427fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 4428fa9e4066Sahrens *result = zfs_dip; 4429fa9e4066Sahrens return (DDI_SUCCESS); 4430fa9e4066Sahrens 4431fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 4432a0965f35Sbonwick *result = (void *)0; 4433fa9e4066Sahrens return (DDI_SUCCESS); 4434fa9e4066Sahrens } 4435fa9e4066Sahrens 4436fa9e4066Sahrens return (DDI_FAILURE); 4437fa9e4066Sahrens } 4438fa9e4066Sahrens 4439fa9e4066Sahrens /* 4440fa9e4066Sahrens * OK, so this is a little weird. 4441fa9e4066Sahrens * 4442fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 4443fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 4444fa9e4066Sahrens * 4445fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 4446fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 4447fa9e4066Sahrens */ 4448fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 4449fa9e4066Sahrens zvol_open, /* open */ 4450fa9e4066Sahrens zvol_close, /* close */ 4451fa9e4066Sahrens zvol_strategy, /* strategy */ 4452fa9e4066Sahrens nodev, /* print */ 4453e7cbe64fSgw zvol_dump, /* dump */ 4454fa9e4066Sahrens zvol_read, /* read */ 4455fa9e4066Sahrens zvol_write, /* write */ 4456fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 4457fa9e4066Sahrens nodev, /* devmap */ 4458fa9e4066Sahrens nodev, /* mmap */ 4459fa9e4066Sahrens nodev, /* segmap */ 4460fa9e4066Sahrens nochpoll, /* poll */ 4461fa9e4066Sahrens ddi_prop_op, /* prop_op */ 4462fa9e4066Sahrens NULL, /* streamtab */ 4463fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 4464fa9e4066Sahrens CB_REV, /* version */ 4465feb08c6bSbillm nodev, /* async read */ 4466feb08c6bSbillm nodev, /* async write */ 4467fa9e4066Sahrens }; 4468fa9e4066Sahrens 4469fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 4470fa9e4066Sahrens DEVO_REV, /* version */ 4471fa9e4066Sahrens 0, /* refcnt */ 4472fa9e4066Sahrens zfs_info, /* info */ 4473fa9e4066Sahrens nulldev, /* identify */ 4474fa9e4066Sahrens nulldev, /* probe */ 4475fa9e4066Sahrens zfs_attach, /* attach */ 4476fa9e4066Sahrens zfs_detach, /* detach */ 4477fa9e4066Sahrens nodev, /* reset */ 4478fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 447919397407SSherry Moore NULL, /* no bus operations */ 448019397407SSherry Moore NULL, /* power */ 448119397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 4482fa9e4066Sahrens }; 4483fa9e4066Sahrens 4484fa9e4066Sahrens static struct modldrv zfs_modldrv = { 448519397407SSherry Moore &mod_driverops, 448619397407SSherry Moore "ZFS storage pool", 448719397407SSherry Moore &zfs_dev_ops 4488fa9e4066Sahrens }; 4489fa9e4066Sahrens 4490fa9e4066Sahrens static struct modlinkage modlinkage = { 4491fa9e4066Sahrens MODREV_1, 4492fa9e4066Sahrens (void *)&zfs_modlfs, 4493fa9e4066Sahrens (void *)&zfs_modldrv, 4494fa9e4066Sahrens NULL 4495fa9e4066Sahrens }; 4496fa9e4066Sahrens 4497ec533521Sfr 4498ec533521Sfr uint_t zfs_fsyncer_key; 4499f18faf3fSek extern uint_t rrw_tsd_key; 4500ec533521Sfr 4501fa9e4066Sahrens int 4502fa9e4066Sahrens _init(void) 4503fa9e4066Sahrens { 4504fa9e4066Sahrens int error; 4505fa9e4066Sahrens 4506a0965f35Sbonwick spa_init(FREAD | FWRITE); 4507a0965f35Sbonwick zfs_init(); 4508a0965f35Sbonwick zvol_init(); 4509a0965f35Sbonwick 4510a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 4511a0965f35Sbonwick zvol_fini(); 4512a0965f35Sbonwick zfs_fini(); 4513a0965f35Sbonwick spa_fini(); 4514fa9e4066Sahrens return (error); 4515a0965f35Sbonwick } 4516fa9e4066Sahrens 4517ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 4518f18faf3fSek tsd_create(&rrw_tsd_key, NULL); 4519ec533521Sfr 4520fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 4521fa9e4066Sahrens ASSERT(error == 0); 4522ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 4523fa9e4066Sahrens 4524fa9e4066Sahrens return (0); 4525fa9e4066Sahrens } 4526fa9e4066Sahrens 4527fa9e4066Sahrens int 4528fa9e4066Sahrens _fini(void) 4529fa9e4066Sahrens { 4530fa9e4066Sahrens int error; 4531fa9e4066Sahrens 4532ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 4533fa9e4066Sahrens return (EBUSY); 4534fa9e4066Sahrens 4535fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 4536fa9e4066Sahrens return (error); 4537fa9e4066Sahrens 4538fa9e4066Sahrens zvol_fini(); 4539fa9e4066Sahrens zfs_fini(); 4540fa9e4066Sahrens spa_fini(); 4541da6c28aaSamw if (zfs_nfsshare_inited) 4542ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 4543da6c28aaSamw if (zfs_smbshare_inited) 4544da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 4545da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 4546ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 4547fa9e4066Sahrens 4548ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 4549fa9e4066Sahrens ldi_ident_release(zfs_li); 4550fa9e4066Sahrens zfs_li = NULL; 4551ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 4552fa9e4066Sahrens 4553fa9e4066Sahrens return (error); 4554fa9e4066Sahrens } 4555fa9e4066Sahrens 4556fa9e4066Sahrens int 4557fa9e4066Sahrens _info(struct modinfo *modinfop) 4558fa9e4066Sahrens { 4559fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 4560fa9e4066Sahrens } 4561