1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5441d80aaSlling * Common Development and Distribution License (the "License"). 6441d80aaSlling * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21fa9e4066Sahrens /* 223f9d6ad7SLin Ling * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23fa9e4066Sahrens */ 24fa9e4066Sahrens 25fa9e4066Sahrens #include <sys/types.h> 26fa9e4066Sahrens #include <sys/param.h> 27fa9e4066Sahrens #include <sys/errno.h> 28fa9e4066Sahrens #include <sys/uio.h> 29fa9e4066Sahrens #include <sys/buf.h> 30fa9e4066Sahrens #include <sys/modctl.h> 31fa9e4066Sahrens #include <sys/open.h> 32fa9e4066Sahrens #include <sys/file.h> 33fa9e4066Sahrens #include <sys/kmem.h> 34fa9e4066Sahrens #include <sys/conf.h> 35fa9e4066Sahrens #include <sys/cmn_err.h> 36fa9e4066Sahrens #include <sys/stat.h> 37fa9e4066Sahrens #include <sys/zfs_ioctl.h> 384201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 39da6c28aaSamw #include <sys/zfs_znode.h> 40fa9e4066Sahrens #include <sys/zap.h> 41fa9e4066Sahrens #include <sys/spa.h> 42b1b8ab34Slling #include <sys/spa_impl.h> 43fa9e4066Sahrens #include <sys/vdev.h> 444201a95eSRic Aleshire #include <sys/priv_impl.h> 45fa9e4066Sahrens #include <sys/dmu.h> 46fa9e4066Sahrens #include <sys/dsl_dir.h> 47fa9e4066Sahrens #include <sys/dsl_dataset.h> 48fa9e4066Sahrens #include <sys/dsl_prop.h> 49ecd6cf80Smarks #include <sys/dsl_deleg.h> 50ecd6cf80Smarks #include <sys/dmu_objset.h> 51fa9e4066Sahrens #include <sys/ddi.h> 52fa9e4066Sahrens #include <sys/sunddi.h> 53fa9e4066Sahrens #include <sys/sunldi.h> 54fa9e4066Sahrens #include <sys/policy.h> 55fa9e4066Sahrens #include <sys/zone.h> 56fa9e4066Sahrens #include <sys/nvpair.h> 57fa9e4066Sahrens #include <sys/pathname.h> 58fa9e4066Sahrens #include <sys/mount.h> 59fa9e4066Sahrens #include <sys/sdt.h> 60fa9e4066Sahrens #include <sys/fs/zfs.h> 61fa9e4066Sahrens #include <sys/zfs_ctldir.h> 62da6c28aaSamw #include <sys/zfs_dir.h> 63*c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 64a2eea2e1Sahrens #include <sys/zvol.h> 653f9d6ad7SLin Ling #include <sys/dsl_scan.h> 66ecd6cf80Smarks #include <sharefs/share.h> 67f18faf3fSek #include <sys/dmu_objset.h> 68fa9e4066Sahrens 69fa9e4066Sahrens #include "zfs_namecheck.h" 70e9dbad6fSeschrock #include "zfs_prop.h" 71ecd6cf80Smarks #include "zfs_deleg.h" 720a586ceaSMark Shellenbaum #include "zfs_comutil.h" 73fa9e4066Sahrens 74fa9e4066Sahrens extern struct modlfs zfs_modlfs; 75fa9e4066Sahrens 76fa9e4066Sahrens extern void zfs_init(void); 77fa9e4066Sahrens extern void zfs_fini(void); 78fa9e4066Sahrens 79fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 80fa9e4066Sahrens dev_info_t *zfs_dip; 81fa9e4066Sahrens 82fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *); 83ecd6cf80Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *); 84fa9e4066Sahrens 8554d692b7SGeorge Wilson typedef enum { 8654d692b7SGeorge Wilson NO_NAME, 8754d692b7SGeorge Wilson POOL_NAME, 8854d692b7SGeorge Wilson DATASET_NAME 8954d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 9054d692b7SGeorge Wilson 91fa9e4066Sahrens typedef struct zfs_ioc_vec { 92fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 93fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 9454d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 95ecd6cf80Smarks boolean_t zvec_his_log; 9654d692b7SGeorge Wilson boolean_t zvec_pool_check; 97fa9e4066Sahrens } zfs_ioc_vec_t; 98fa9e4066Sahrens 9914843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 10014843421SMatthew Ahrens static const char *userquota_perms[] = { 10114843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 10214843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 10314843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 10414843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 10514843421SMatthew Ahrens }; 10614843421SMatthew Ahrens 10714843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 10892241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 10992241e0bSTom Erickson cred_t *cr); 11092241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 11192241e0bSTom Erickson nvlist_t **errors); 1120a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 1130a48a24eStimh boolean_t *); 11492241e0bSTom Erickson int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **); 1150a48a24eStimh 116fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 117fa9e4066Sahrens void 118fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 119fa9e4066Sahrens { 120fa9e4066Sahrens const char *newfile; 1213f9d6ad7SLin Ling char buf[512]; 122fa9e4066Sahrens va_list adx; 123fa9e4066Sahrens 124fa9e4066Sahrens /* 125fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 126fa9e4066Sahrens */ 127fa9e4066Sahrens newfile = strrchr(file, '/'); 128fa9e4066Sahrens if (newfile != NULL) { 129fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 130fa9e4066Sahrens } else { 131fa9e4066Sahrens newfile = file; 132fa9e4066Sahrens } 133fa9e4066Sahrens 134fa9e4066Sahrens va_start(adx, fmt); 135fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 136fa9e4066Sahrens va_end(adx); 137fa9e4066Sahrens 138fa9e4066Sahrens /* 139fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 140fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 141fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 142fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 143fa9e4066Sahrens * arg0 = file name 144fa9e4066Sahrens * arg1 = function name 145fa9e4066Sahrens * arg2 = line number 146fa9e4066Sahrens * arg3 = message 147fa9e4066Sahrens */ 148fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 149fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 150fa9e4066Sahrens } 151fa9e4066Sahrens 152ecd6cf80Smarks static void 153228975ccSek history_str_free(char *buf) 154228975ccSek { 155228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 156228975ccSek } 157228975ccSek 158228975ccSek static char * 159228975ccSek history_str_get(zfs_cmd_t *zc) 160ecd6cf80Smarks { 16140feaa91Sahrens char *buf; 162ecd6cf80Smarks 163ecd6cf80Smarks if (zc->zc_history == NULL) 164228975ccSek return (NULL); 165e7437265Sahrens 166ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 167ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 168ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 169228975ccSek history_str_free(buf); 170228975ccSek return (NULL); 171ecd6cf80Smarks } 172ecd6cf80Smarks 173ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 174ecd6cf80Smarks 175228975ccSek return (buf); 176228975ccSek } 177ecd6cf80Smarks 17815e6edf1Sgw /* 17915e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 18015e6edf1Sgw */ 18115e6edf1Sgw static boolean_t 18215e6edf1Sgw zfs_is_bootfs(const char *name) 18315e6edf1Sgw { 184503ad85cSMatthew Ahrens objset_t *os; 18515e6edf1Sgw 186503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 187503ad85cSMatthew Ahrens boolean_t ret; 188b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 189503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 190503ad85cSMatthew Ahrens return (ret); 19115e6edf1Sgw } 192503ad85cSMatthew Ahrens return (B_FALSE); 19315e6edf1Sgw } 19415e6edf1Sgw 195c2a93d44Stimh /* 1960a48a24eStimh * zfs_earlier_version 197c2a93d44Stimh * 198c2a93d44Stimh * Return non-zero if the spa version is less than requested version. 199c2a93d44Stimh */ 200da6c28aaSamw static int 2010a48a24eStimh zfs_earlier_version(const char *name, int version) 202da6c28aaSamw { 203da6c28aaSamw spa_t *spa; 204da6c28aaSamw 205da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 206da6c28aaSamw if (spa_version(spa) < version) { 207da6c28aaSamw spa_close(spa, FTAG); 208da6c28aaSamw return (1); 209da6c28aaSamw } 210da6c28aaSamw spa_close(spa, FTAG); 211da6c28aaSamw } 212da6c28aaSamw return (0); 213da6c28aaSamw } 214da6c28aaSamw 2159e6eda55Smarks /* 216745cd3c5Smaybee * zpl_earlier_version 2179e6eda55Smarks * 218745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 2199e6eda55Smarks */ 220745cd3c5Smaybee static boolean_t 221745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 2229e6eda55Smarks { 2239e6eda55Smarks objset_t *os; 224745cd3c5Smaybee boolean_t rc = B_TRUE; 2259e6eda55Smarks 226503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 227745cd3c5Smaybee uint64_t zplversion; 2289e6eda55Smarks 229503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 230503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 231503ad85cSMatthew Ahrens return (B_TRUE); 232503ad85cSMatthew Ahrens } 233503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 234745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 235745cd3c5Smaybee rc = zplversion < version; 236503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2379e6eda55Smarks } 2389e6eda55Smarks return (rc); 2399e6eda55Smarks } 2409e6eda55Smarks 241228975ccSek static void 242228975ccSek zfs_log_history(zfs_cmd_t *zc) 243228975ccSek { 244228975ccSek spa_t *spa; 245228975ccSek char *buf; 246ecd6cf80Smarks 247228975ccSek if ((buf = history_str_get(zc)) == NULL) 248228975ccSek return; 249228975ccSek 250228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 251228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 252228975ccSek (void) spa_history_log(spa, buf, LOG_CMD_NORMAL); 253228975ccSek spa_close(spa, FTAG); 254228975ccSek } 255228975ccSek history_str_free(buf); 256ecd6cf80Smarks } 257ecd6cf80Smarks 258fa9e4066Sahrens /* 259fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 260fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 261fa9e4066Sahrens */ 262fa9e4066Sahrens /* ARGSUSED */ 263fa9e4066Sahrens static int 264ecd6cf80Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr) 265fa9e4066Sahrens { 266fa9e4066Sahrens return (0); 267fa9e4066Sahrens } 268fa9e4066Sahrens 269fa9e4066Sahrens /* 270fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 271fa9e4066Sahrens * no privileges, but must be visible in the local zone. 272fa9e4066Sahrens */ 273fa9e4066Sahrens /* ARGSUSED */ 274fa9e4066Sahrens static int 275ecd6cf80Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr) 276fa9e4066Sahrens { 277fa9e4066Sahrens if (INGLOBALZONE(curproc) || 278ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 279fa9e4066Sahrens return (0); 280fa9e4066Sahrens 281fa9e4066Sahrens return (ENOENT); 282fa9e4066Sahrens } 283fa9e4066Sahrens 284fa9e4066Sahrens static int 285fa9e4066Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr) 286fa9e4066Sahrens { 287fa9e4066Sahrens uint64_t zoned; 288fa9e4066Sahrens int writable = 1; 289fa9e4066Sahrens 290fa9e4066Sahrens /* 291fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 292fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 293fa9e4066Sahrens */ 294fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 295fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 296fa9e4066Sahrens return (ENOENT); 297fa9e4066Sahrens 298fa9e4066Sahrens if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 299fa9e4066Sahrens return (ENOENT); 300fa9e4066Sahrens 301fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 302fa9e4066Sahrens /* 303fa9e4066Sahrens * If the fs is zoned, only root can access it from the 304fa9e4066Sahrens * global zone. 305fa9e4066Sahrens */ 306fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 307fa9e4066Sahrens return (EPERM); 308fa9e4066Sahrens } else { 309fa9e4066Sahrens /* 310fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 311fa9e4066Sahrens */ 312fa9e4066Sahrens if (!zoned) 313fa9e4066Sahrens return (EPERM); 314fa9e4066Sahrens 315fa9e4066Sahrens /* must be writable by this zone */ 316fa9e4066Sahrens if (!writable) 317fa9e4066Sahrens return (EPERM); 318fa9e4066Sahrens } 319fa9e4066Sahrens return (0); 320fa9e4066Sahrens } 321fa9e4066Sahrens 322fa9e4066Sahrens int 323ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 324fa9e4066Sahrens { 325fa9e4066Sahrens int error; 326fa9e4066Sahrens 327ecd6cf80Smarks error = zfs_dozonecheck(name, cr); 328ecd6cf80Smarks if (error == 0) { 329ecd6cf80Smarks error = secpolicy_zfs(cr); 330db870a07Sahrens if (error) 331ecd6cf80Smarks error = dsl_deleg_access(name, perm, cr); 332ecd6cf80Smarks } 333ecd6cf80Smarks return (error); 334ecd6cf80Smarks } 335ecd6cf80Smarks 3364201a95eSRic Aleshire /* 3374201a95eSRic Aleshire * Policy for setting the security label property. 3384201a95eSRic Aleshire * 3394201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 3404201a95eSRic Aleshire */ 3414201a95eSRic Aleshire static int 34292241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 3434201a95eSRic Aleshire { 3444201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 3454201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 3464201a95eSRic Aleshire boolean_t new_default = FALSE; 3474201a95eSRic Aleshire uint64_t zoned; 3484201a95eSRic Aleshire int needed_priv = -1; 3494201a95eSRic Aleshire int error; 3504201a95eSRic Aleshire 3514201a95eSRic Aleshire /* First get the existing dataset label. */ 3524201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 3534201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 3544201a95eSRic Aleshire if (error) 3554201a95eSRic Aleshire return (EPERM); 3564201a95eSRic Aleshire 3574201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 3584201a95eSRic Aleshire new_default = TRUE; 3594201a95eSRic Aleshire 3604201a95eSRic Aleshire /* The label must be translatable */ 3614201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 3624201a95eSRic Aleshire return (EINVAL); 3634201a95eSRic Aleshire 3644201a95eSRic Aleshire /* 3654201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 3664201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 3674201a95eSRic Aleshire * are needed. 3684201a95eSRic Aleshire */ 3694201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 3704201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 3714201a95eSRic Aleshire return (EPERM); 3724201a95eSRic Aleshire return (0); 3734201a95eSRic Aleshire } 3744201a95eSRic Aleshire 3754201a95eSRic Aleshire /* 3764201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 3774201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 3784201a95eSRic Aleshire * global zone. 3794201a95eSRic Aleshire */ 3804201a95eSRic Aleshire if (dsl_prop_get_integer(name, 3814201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 3824201a95eSRic Aleshire return (EPERM); 3834201a95eSRic Aleshire if (!zoned) { 3844201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 3854201a95eSRic Aleshire return (EPERM); 3864201a95eSRic Aleshire } 3874201a95eSRic Aleshire 3884201a95eSRic Aleshire /* 3894201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 3904201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 3914201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 3924201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 3934201a95eSRic Aleshire */ 3944201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 39592241e0bSTom Erickson objset_t *os; 39692241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 39792241e0bSTom Erickson 3984201a95eSRic Aleshire /* 3994201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 4004201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 4014201a95eSRic Aleshire */ 4024201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 40392241e0bSTom Erickson setsl_tag, &os); 4044201a95eSRic Aleshire if (error) 4054201a95eSRic Aleshire return (EPERM); 4064201a95eSRic Aleshire 40792241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 40892241e0bSTom Erickson 4094201a95eSRic Aleshire if (new_default) { 4104201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 4114201a95eSRic Aleshire goto out_check; 4124201a95eSRic Aleshire } 4134201a95eSRic Aleshire 4144201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 4154201a95eSRic Aleshire return (EPERM); 4164201a95eSRic Aleshire 4174201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 4184201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 4194201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 4204201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 4214201a95eSRic Aleshire } else { 4224201a95eSRic Aleshire /* dataset currently has a default label */ 4234201a95eSRic Aleshire if (!new_default) 4244201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 4254201a95eSRic Aleshire } 4264201a95eSRic Aleshire 4274201a95eSRic Aleshire out_check: 4284201a95eSRic Aleshire if (needed_priv != -1) 4294201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 4304201a95eSRic Aleshire return (0); 4314201a95eSRic Aleshire } 4324201a95eSRic Aleshire 433ecd6cf80Smarks static int 43492241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 43592241e0bSTom Erickson cred_t *cr) 436ecd6cf80Smarks { 43792241e0bSTom Erickson char *strval; 43892241e0bSTom Erickson 439ecd6cf80Smarks /* 440ecd6cf80Smarks * Check permissions for special properties. 441ecd6cf80Smarks */ 442ecd6cf80Smarks switch (prop) { 443ecd6cf80Smarks case ZFS_PROP_ZONED: 444ecd6cf80Smarks /* 445ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 446ecd6cf80Smarks */ 447ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 448ecd6cf80Smarks return (EPERM); 449ecd6cf80Smarks break; 450ecd6cf80Smarks 451ecd6cf80Smarks case ZFS_PROP_QUOTA: 452ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 453ecd6cf80Smarks uint64_t zoned; 454ecd6cf80Smarks char setpoint[MAXNAMELEN]; 455ecd6cf80Smarks /* 456ecd6cf80Smarks * Unprivileged users are allowed to modify the 457ecd6cf80Smarks * quota on things *under* (ie. contained by) 458ecd6cf80Smarks * the thing they own. 459ecd6cf80Smarks */ 46092241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 461ecd6cf80Smarks setpoint)) 462ecd6cf80Smarks return (EPERM); 46392241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 464ecd6cf80Smarks return (EPERM); 465ecd6cf80Smarks } 466db870a07Sahrens break; 4674201a95eSRic Aleshire 4684201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 4694201a95eSRic Aleshire if (!is_system_labeled()) 4704201a95eSRic Aleshire return (EPERM); 47192241e0bSTom Erickson 47292241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 47392241e0bSTom Erickson int err; 47492241e0bSTom Erickson 47592241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 47692241e0bSTom Erickson if (err != 0) 47792241e0bSTom Erickson return (err); 47892241e0bSTom Erickson } 4794201a95eSRic Aleshire break; 480ecd6cf80Smarks } 481ecd6cf80Smarks 48292241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 483ecd6cf80Smarks } 484ecd6cf80Smarks 485ecd6cf80Smarks int 486ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr) 487ecd6cf80Smarks { 488ecd6cf80Smarks int error; 489ecd6cf80Smarks 490ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 491ecd6cf80Smarks if (error) 492fa9e4066Sahrens return (error); 493fa9e4066Sahrens 494ecd6cf80Smarks /* 495ecd6cf80Smarks * permission to set permissions will be evaluated later in 496ecd6cf80Smarks * dsl_deleg_can_allow() 497ecd6cf80Smarks */ 498ecd6cf80Smarks return (0); 499ecd6cf80Smarks } 500ecd6cf80Smarks 501ecd6cf80Smarks int 502ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr) 503ecd6cf80Smarks { 504681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 505681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 506ecd6cf80Smarks } 507ecd6cf80Smarks 508ecd6cf80Smarks int 509ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr) 510ecd6cf80Smarks { 511ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 512ecd6cf80Smarks ZFS_DELEG_PERM_SEND, cr)); 513ecd6cf80Smarks } 514ecd6cf80Smarks 515743a77edSAlan Wright static int 516743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr) 517743a77edSAlan Wright { 518743a77edSAlan Wright vnode_t *vp; 519743a77edSAlan Wright int error; 520743a77edSAlan Wright 521743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 522743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 523743a77edSAlan Wright return (error); 524743a77edSAlan Wright 525743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 526743a77edSAlan Wright 527743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 528743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 529743a77edSAlan Wright zc->zc_name) != 0)) { 530743a77edSAlan Wright VN_RELE(vp); 531743a77edSAlan Wright return (EPERM); 532743a77edSAlan Wright } 533743a77edSAlan Wright 534743a77edSAlan Wright VN_RELE(vp); 535743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 536743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 537743a77edSAlan Wright } 538743a77edSAlan Wright 539ecd6cf80Smarks int 540ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr) 541ecd6cf80Smarks { 542ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 543ecd6cf80Smarks return (EPERM); 544ecd6cf80Smarks 5453cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 546ecd6cf80Smarks return (0); 547ecd6cf80Smarks } else { 548743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 549743a77edSAlan Wright } 550743a77edSAlan Wright } 551ecd6cf80Smarks 552743a77edSAlan Wright int 553743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr) 554743a77edSAlan Wright { 555743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 556743a77edSAlan Wright return (EPERM); 557ecd6cf80Smarks 558743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 559743a77edSAlan Wright return (0); 560743a77edSAlan Wright } else { 561743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 562ecd6cf80Smarks } 563fa9e4066Sahrens } 564fa9e4066Sahrens 565fa9e4066Sahrens static int 566ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 567fa9e4066Sahrens { 568fa9e4066Sahrens char *cp; 569fa9e4066Sahrens 570fa9e4066Sahrens /* 571fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 572fa9e4066Sahrens */ 573ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 574ecd6cf80Smarks cp = strrchr(parent, '@'); 575fa9e4066Sahrens if (cp != NULL) { 576fa9e4066Sahrens cp[0] = '\0'; 577fa9e4066Sahrens } else { 578ecd6cf80Smarks cp = strrchr(parent, '/'); 579fa9e4066Sahrens if (cp == NULL) 580fa9e4066Sahrens return (ENOENT); 581fa9e4066Sahrens cp[0] = '\0'; 582ecd6cf80Smarks } 583ecd6cf80Smarks 584ecd6cf80Smarks return (0); 585ecd6cf80Smarks } 586ecd6cf80Smarks 587ecd6cf80Smarks int 588ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 589ecd6cf80Smarks { 590ecd6cf80Smarks int error; 591ecd6cf80Smarks 592ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 593ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 594ecd6cf80Smarks return (error); 595ecd6cf80Smarks 596ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 597ecd6cf80Smarks } 598ecd6cf80Smarks 599ecd6cf80Smarks static int 600ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr) 601ecd6cf80Smarks { 602ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 603ecd6cf80Smarks } 604ecd6cf80Smarks 605cbf6f6aaSWilliam Gorrell /* 606cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 607cbf6f6aaSWilliam Gorrell * descendent mount and destroy permissions. 608cbf6f6aaSWilliam Gorrell * Reassemble the full filesystem@snap name so dsl_deleg_access() 609cbf6f6aaSWilliam Gorrell * can do the correct permission check. 610cbf6f6aaSWilliam Gorrell * 611cbf6f6aaSWilliam Gorrell * Since this routine is used when doing a recursive destroy of snapshots 612cbf6f6aaSWilliam Gorrell * and destroying snapshots requires descendent permissions, a successfull 613cbf6f6aaSWilliam Gorrell * check of the top level snapshot applies to snapshots of all descendent 614cbf6f6aaSWilliam Gorrell * datasets as well. 615cbf6f6aaSWilliam Gorrell */ 616cbf6f6aaSWilliam Gorrell static int 617cbf6f6aaSWilliam Gorrell zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr) 618cbf6f6aaSWilliam Gorrell { 619cbf6f6aaSWilliam Gorrell int error; 620cbf6f6aaSWilliam Gorrell char *dsname; 621cbf6f6aaSWilliam Gorrell 622cbf6f6aaSWilliam Gorrell dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value); 623cbf6f6aaSWilliam Gorrell 624cbf6f6aaSWilliam Gorrell error = zfs_secpolicy_destroy_perms(dsname, cr); 625cbf6f6aaSWilliam Gorrell 626cbf6f6aaSWilliam Gorrell strfree(dsname); 627cbf6f6aaSWilliam Gorrell return (error); 628cbf6f6aaSWilliam Gorrell } 629cbf6f6aaSWilliam Gorrell 630ecd6cf80Smarks int 631ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 632ecd6cf80Smarks { 63392241e0bSTom Erickson char parentname[MAXNAMELEN]; 634ecd6cf80Smarks int error; 635ecd6cf80Smarks 636ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 637ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 638ecd6cf80Smarks return (error); 639ecd6cf80Smarks 640ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 641ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 642ecd6cf80Smarks return (error); 643ecd6cf80Smarks 644ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 645ecd6cf80Smarks sizeof (parentname))) != 0) 646ecd6cf80Smarks return (error); 647ecd6cf80Smarks 648ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 649ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 650ecd6cf80Smarks return (error); 651ecd6cf80Smarks 652ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 653ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 654ecd6cf80Smarks return (error); 655ecd6cf80Smarks 656ecd6cf80Smarks return (error); 657ecd6cf80Smarks } 658ecd6cf80Smarks 659ecd6cf80Smarks static int 660ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr) 661ecd6cf80Smarks { 662ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 663ecd6cf80Smarks } 664ecd6cf80Smarks 665ecd6cf80Smarks static int 666ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr) 667ecd6cf80Smarks { 66892241e0bSTom Erickson char parentname[MAXNAMELEN]; 669ecd6cf80Smarks objset_t *clone; 670ecd6cf80Smarks int error; 671ecd6cf80Smarks 672ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 673ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 674ecd6cf80Smarks if (error) 675ecd6cf80Smarks return (error); 676ecd6cf80Smarks 677503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &clone); 678ecd6cf80Smarks 679ecd6cf80Smarks if (error == 0) { 680ecd6cf80Smarks dsl_dataset_t *pclone = NULL; 681ecd6cf80Smarks dsl_dir_t *dd; 682503ad85cSMatthew Ahrens dd = clone->os_dsl_dataset->ds_dir; 683ecd6cf80Smarks 684ecd6cf80Smarks rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 685745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 686745cd3c5Smaybee dd->dd_phys->dd_origin_obj, FTAG, &pclone); 687ecd6cf80Smarks rw_exit(&dd->dd_pool->dp_config_rwlock); 688ecd6cf80Smarks if (error) { 689503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 690ecd6cf80Smarks return (error); 691ecd6cf80Smarks } 692ecd6cf80Smarks 693ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 694ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 695ecd6cf80Smarks 696ecd6cf80Smarks dsl_dataset_name(pclone, parentname); 697503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 698745cd3c5Smaybee dsl_dataset_rele(pclone, FTAG); 699ecd6cf80Smarks if (error == 0) 700ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 701ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 702ecd6cf80Smarks } 703ecd6cf80Smarks return (error); 704ecd6cf80Smarks } 705ecd6cf80Smarks 706ecd6cf80Smarks static int 707ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr) 708ecd6cf80Smarks { 709ecd6cf80Smarks int error; 710ecd6cf80Smarks 711ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 712ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 713ecd6cf80Smarks return (error); 714ecd6cf80Smarks 715ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 716ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 717ecd6cf80Smarks return (error); 718ecd6cf80Smarks 719ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 720ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 721ecd6cf80Smarks } 722ecd6cf80Smarks 723ecd6cf80Smarks int 724ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 725ecd6cf80Smarks { 726681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 727681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 728ecd6cf80Smarks } 729ecd6cf80Smarks 730ecd6cf80Smarks static int 731ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr) 732ecd6cf80Smarks { 733ecd6cf80Smarks 734ecd6cf80Smarks return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr)); 735ecd6cf80Smarks } 736ecd6cf80Smarks 737ecd6cf80Smarks static int 738ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr) 739ecd6cf80Smarks { 74092241e0bSTom Erickson char parentname[MAXNAMELEN]; 74192241e0bSTom Erickson int error; 742ecd6cf80Smarks 743ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 744ecd6cf80Smarks sizeof (parentname))) != 0) 745ecd6cf80Smarks return (error); 746fa9e4066Sahrens 747ecd6cf80Smarks if (zc->zc_value[0] != '\0') { 748ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_value, 749ecd6cf80Smarks ZFS_DELEG_PERM_CLONE, cr)) != 0) 750ecd6cf80Smarks return (error); 751fa9e4066Sahrens } 752fa9e4066Sahrens 753ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 754ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 755ecd6cf80Smarks return (error); 756ecd6cf80Smarks 757ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 758ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 759ecd6cf80Smarks 760ecd6cf80Smarks return (error); 761ecd6cf80Smarks } 762ecd6cf80Smarks 763ecd6cf80Smarks static int 764ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr) 765ecd6cf80Smarks { 766ecd6cf80Smarks int error; 767ecd6cf80Smarks 768ecd6cf80Smarks error = secpolicy_fs_unmount(cr, NULL); 769ecd6cf80Smarks if (error) { 770ecd6cf80Smarks error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr); 771ecd6cf80Smarks } 772ecd6cf80Smarks return (error); 773fa9e4066Sahrens } 774fa9e4066Sahrens 775fa9e4066Sahrens /* 776fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 777fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 778fa9e4066Sahrens */ 779fa9e4066Sahrens /* ARGSUSED */ 780fa9e4066Sahrens static int 781ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr) 782fa9e4066Sahrens { 783fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 784fa9e4066Sahrens return (EPERM); 785fa9e4066Sahrens 786fa9e4066Sahrens return (0); 787fa9e4066Sahrens } 788fa9e4066Sahrens 789ea8dc4b6Seschrock /* 790ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 791ea8dc4b6Seschrock */ 792ea8dc4b6Seschrock /* ARGSUSED */ 793ea8dc4b6Seschrock static int 794ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr) 795ea8dc4b6Seschrock { 796ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 797ea8dc4b6Seschrock } 798ea8dc4b6Seschrock 799e45ce728Sahrens static int 800e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr) 801e45ce728Sahrens { 802e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 803e45ce728Sahrens 804990b4856Slling if (prop == ZPROP_INVAL) { 805e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 806e45ce728Sahrens return (EINVAL); 807e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 808e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 809e45ce728Sahrens } else { 81092241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 81192241e0bSTom Erickson NULL, cr)); 812e45ce728Sahrens } 813e45ce728Sahrens } 814e45ce728Sahrens 81514843421SMatthew Ahrens static int 81614843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr) 81714843421SMatthew Ahrens { 81814843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 81914843421SMatthew Ahrens if (err) 82014843421SMatthew Ahrens return (err); 82114843421SMatthew Ahrens 82214843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 82314843421SMatthew Ahrens return (EINVAL); 82414843421SMatthew Ahrens 82514843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 82614843421SMatthew Ahrens /* 82714843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 82814843421SMatthew Ahrens * themself, allow it. 82914843421SMatthew Ahrens */ 83014843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 83114843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 83214843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 83314843421SMatthew Ahrens return (0); 83414843421SMatthew Ahrens } else { 83514843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 83614843421SMatthew Ahrens return (0); 83714843421SMatthew Ahrens } 83814843421SMatthew Ahrens } 83914843421SMatthew Ahrens 84014843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 84114843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 84214843421SMatthew Ahrens } 84314843421SMatthew Ahrens 84414843421SMatthew Ahrens static int 84514843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr) 84614843421SMatthew Ahrens { 84714843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 84814843421SMatthew Ahrens if (err) 84914843421SMatthew Ahrens return (err); 85014843421SMatthew Ahrens 85114843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 85214843421SMatthew Ahrens return (EINVAL); 85314843421SMatthew Ahrens 85414843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 85514843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 85614843421SMatthew Ahrens } 85714843421SMatthew Ahrens 85814843421SMatthew Ahrens static int 85914843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr) 86014843421SMatthew Ahrens { 86192241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 86292241e0bSTom Erickson NULL, cr)); 86314843421SMatthew Ahrens } 86414843421SMatthew Ahrens 865842727c2SChris Kirby static int 866842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr) 867842727c2SChris Kirby { 868842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 869842727c2SChris Kirby ZFS_DELEG_PERM_HOLD, cr)); 870842727c2SChris Kirby } 871842727c2SChris Kirby 872842727c2SChris Kirby static int 873842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr) 874842727c2SChris Kirby { 875842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 876842727c2SChris Kirby ZFS_DELEG_PERM_RELEASE, cr)); 877842727c2SChris Kirby } 878842727c2SChris Kirby 879fa9e4066Sahrens /* 880fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 881fa9e4066Sahrens */ 882fa9e4066Sahrens static int 883478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 884fa9e4066Sahrens { 885fa9e4066Sahrens char *packed; 886fa9e4066Sahrens int error; 887990b4856Slling nvlist_t *list = NULL; 888fa9e4066Sahrens 889fa9e4066Sahrens /* 890e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 891fa9e4066Sahrens */ 892990b4856Slling if (size == 0) 893fa9e4066Sahrens return (EINVAL); 894fa9e4066Sahrens 895fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 896fa9e4066Sahrens 897478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 898478ed9adSEric Taylor iflag)) != 0) { 899fa9e4066Sahrens kmem_free(packed, size); 900fa9e4066Sahrens return (error); 901fa9e4066Sahrens } 902fa9e4066Sahrens 903990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 904fa9e4066Sahrens kmem_free(packed, size); 905fa9e4066Sahrens return (error); 906fa9e4066Sahrens } 907fa9e4066Sahrens 908fa9e4066Sahrens kmem_free(packed, size); 909fa9e4066Sahrens 910990b4856Slling *nvp = list; 911fa9e4066Sahrens return (0); 912fa9e4066Sahrens } 913fa9e4066Sahrens 91492241e0bSTom Erickson static int 91592241e0bSTom Erickson fit_error_list(zfs_cmd_t *zc, nvlist_t **errors) 91692241e0bSTom Erickson { 91792241e0bSTom Erickson size_t size; 91892241e0bSTom Erickson 91992241e0bSTom Erickson VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0); 92092241e0bSTom Erickson 92192241e0bSTom Erickson if (size > zc->zc_nvlist_dst_size) { 92292241e0bSTom Erickson nvpair_t *more_errors; 92392241e0bSTom Erickson int n = 0; 92492241e0bSTom Erickson 92592241e0bSTom Erickson if (zc->zc_nvlist_dst_size < 1024) 92692241e0bSTom Erickson return (ENOMEM); 92792241e0bSTom Erickson 92892241e0bSTom Erickson VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0); 92992241e0bSTom Erickson more_errors = nvlist_prev_nvpair(*errors, NULL); 93092241e0bSTom Erickson 93192241e0bSTom Erickson do { 93292241e0bSTom Erickson nvpair_t *pair = nvlist_prev_nvpair(*errors, 93392241e0bSTom Erickson more_errors); 93492241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(*errors, pair) == 0); 93592241e0bSTom Erickson n++; 93692241e0bSTom Erickson VERIFY(nvlist_size(*errors, &size, 93792241e0bSTom Erickson NV_ENCODE_NATIVE) == 0); 93892241e0bSTom Erickson } while (size > zc->zc_nvlist_dst_size); 93992241e0bSTom Erickson 94092241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0); 94192241e0bSTom Erickson VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0); 94292241e0bSTom Erickson ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0); 94392241e0bSTom Erickson ASSERT(size <= zc->zc_nvlist_dst_size); 94492241e0bSTom Erickson } 94592241e0bSTom Erickson 94692241e0bSTom Erickson return (0); 94792241e0bSTom Erickson } 94892241e0bSTom Erickson 949e9dbad6fSeschrock static int 950e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 951e9dbad6fSeschrock { 952e9dbad6fSeschrock char *packed = NULL; 9536e27f868SSam Falkner int error = 0; 954e9dbad6fSeschrock size_t size; 955e9dbad6fSeschrock 956e9dbad6fSeschrock VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); 957e9dbad6fSeschrock 958e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 959e9dbad6fSeschrock error = ENOMEM; 960e9dbad6fSeschrock } else { 961da165920Smarks packed = kmem_alloc(size, KM_SLEEP); 962e9dbad6fSeschrock VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, 963e9dbad6fSeschrock KM_SLEEP) == 0); 9646e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 9656e27f868SSam Falkner size, zc->zc_iflags) != 0) 9666e27f868SSam Falkner error = EFAULT; 967e9dbad6fSeschrock kmem_free(packed, size); 968e9dbad6fSeschrock } 969e9dbad6fSeschrock 970e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 971e9dbad6fSeschrock return (error); 972e9dbad6fSeschrock } 973e9dbad6fSeschrock 97414843421SMatthew Ahrens static int 975af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 97614843421SMatthew Ahrens { 97714843421SMatthew Ahrens objset_t *os; 97814843421SMatthew Ahrens int error; 97914843421SMatthew Ahrens 980503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 98114843421SMatthew Ahrens if (error) 98214843421SMatthew Ahrens return (error); 983503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 984503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 985503ad85cSMatthew Ahrens return (EINVAL); 986503ad85cSMatthew Ahrens } 98714843421SMatthew Ahrens 988503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 989af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 990af4c679fSSean McEnroe if (*zfvp) { 991af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 99214843421SMatthew Ahrens } else { 99314843421SMatthew Ahrens error = ESRCH; 99414843421SMatthew Ahrens } 995503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 996503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 99714843421SMatthew Ahrens return (error); 99814843421SMatthew Ahrens } 99914843421SMatthew Ahrens 100014843421SMatthew Ahrens /* 100114843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 100214843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 100314843421SMatthew Ahrens */ 100414843421SMatthew Ahrens static int 1005af4c679fSSean McEnroe zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp) 100614843421SMatthew Ahrens { 100714843421SMatthew Ahrens int error = 0; 100814843421SMatthew Ahrens 1009af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1010af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 101114843421SMatthew Ahrens if (error == 0) { 1012af4c679fSSean McEnroe rrw_enter(&(*zfvp)->z_teardown_lock, RW_READER, tag); 1013af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 101414843421SMatthew Ahrens /* 101514843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 101614843421SMatthew Ahrens * thread should be just about to disassociate the 101714843421SMatthew Ahrens * objset from the zfsvfs. 101814843421SMatthew Ahrens */ 1019af4c679fSSean McEnroe rrw_exit(&(*zfvp)->z_teardown_lock, tag); 102014843421SMatthew Ahrens return (EBUSY); 102114843421SMatthew Ahrens } 102214843421SMatthew Ahrens } 102314843421SMatthew Ahrens return (error); 102414843421SMatthew Ahrens } 102514843421SMatthew Ahrens 102614843421SMatthew Ahrens static void 102714843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 102814843421SMatthew Ahrens { 102914843421SMatthew Ahrens rrw_exit(&zfsvfs->z_teardown_lock, tag); 103014843421SMatthew Ahrens 103114843421SMatthew Ahrens if (zfsvfs->z_vfs) { 103214843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 103314843421SMatthew Ahrens } else { 1034503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 103514843421SMatthew Ahrens zfsvfs_free(zfsvfs); 103614843421SMatthew Ahrens } 103714843421SMatthew Ahrens } 103814843421SMatthew Ahrens 1039fa9e4066Sahrens static int 1040fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1041fa9e4066Sahrens { 1042fa9e4066Sahrens int error; 1043990b4856Slling nvlist_t *config, *props = NULL; 10440a48a24eStimh nvlist_t *rootprops = NULL; 10450a48a24eStimh nvlist_t *zplprops = NULL; 1046228975ccSek char *buf; 1047fa9e4066Sahrens 1048990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1049478ed9adSEric Taylor zc->zc_iflags, &config)) 1050fa9e4066Sahrens return (error); 10512a6b87f0Sek 1052990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1053478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1054478ed9adSEric Taylor zc->zc_iflags, &props))) { 1055990b4856Slling nvlist_free(config); 1056990b4856Slling return (error); 1057990b4856Slling } 1058990b4856Slling 10590a48a24eStimh if (props) { 10600a48a24eStimh nvlist_t *nvl = NULL; 10610a48a24eStimh uint64_t version = SPA_VERSION; 10620a48a24eStimh 10630a48a24eStimh (void) nvlist_lookup_uint64(props, 10640a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 10650a48a24eStimh if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) { 10660a48a24eStimh error = EINVAL; 10670a48a24eStimh goto pool_props_bad; 10680a48a24eStimh } 10690a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 10700a48a24eStimh if (nvl) { 10710a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 10720a48a24eStimh if (error != 0) { 10730a48a24eStimh nvlist_free(config); 10740a48a24eStimh nvlist_free(props); 10750a48a24eStimh return (error); 10760a48a24eStimh } 10770a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 10780a48a24eStimh } 10790a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 10800a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 10810a48a24eStimh zplprops, NULL); 10820a48a24eStimh if (error) 10830a48a24eStimh goto pool_props_bad; 10840a48a24eStimh } 10850a48a24eStimh 10862a6b87f0Sek buf = history_str_get(zc); 1087fa9e4066Sahrens 10880a48a24eStimh error = spa_create(zc->zc_name, config, props, buf, zplprops); 10890a48a24eStimh 10900a48a24eStimh /* 10910a48a24eStimh * Set the remaining root properties 10920a48a24eStimh */ 109392241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 109492241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 10950a48a24eStimh (void) spa_destroy(zc->zc_name); 1096fa9e4066Sahrens 10972a6b87f0Sek if (buf != NULL) 10982a6b87f0Sek history_str_free(buf); 1099990b4856Slling 11000a48a24eStimh pool_props_bad: 11010a48a24eStimh nvlist_free(rootprops); 11020a48a24eStimh nvlist_free(zplprops); 1103fa9e4066Sahrens nvlist_free(config); 11040a48a24eStimh nvlist_free(props); 1105990b4856Slling 1106fa9e4066Sahrens return (error); 1107fa9e4066Sahrens } 1108fa9e4066Sahrens 1109fa9e4066Sahrens static int 1110fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1111fa9e4066Sahrens { 1112ecd6cf80Smarks int error; 1113ecd6cf80Smarks zfs_log_history(zc); 1114ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1115681d9761SEric Taylor if (error == 0) 1116681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1117ecd6cf80Smarks return (error); 1118fa9e4066Sahrens } 1119fa9e4066Sahrens 1120fa9e4066Sahrens static int 1121fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1122fa9e4066Sahrens { 1123990b4856Slling nvlist_t *config, *props = NULL; 1124fa9e4066Sahrens uint64_t guid; 1125468c413aSTim Haley int error; 1126fa9e4066Sahrens 1127990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1128478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1129990b4856Slling return (error); 1130990b4856Slling 1131990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1132478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1133478ed9adSEric Taylor zc->zc_iflags, &props))) { 1134990b4856Slling nvlist_free(config); 1135fa9e4066Sahrens return (error); 1136990b4856Slling } 1137fa9e4066Sahrens 1138fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1139ea8dc4b6Seschrock guid != zc->zc_guid) 1140fa9e4066Sahrens error = EINVAL; 1141c5904d13Seschrock else if (zc->zc_cookie) 1142468c413aSTim Haley error = spa_import_verbatim(zc->zc_name, config, props); 1143fa9e4066Sahrens else 1144990b4856Slling error = spa_import(zc->zc_name, config, props); 1145fa9e4066Sahrens 1146468c413aSTim Haley if (zc->zc_nvlist_dst != 0) 1147468c413aSTim Haley (void) put_nvlist(zc, config); 1148468c413aSTim Haley 1149fa9e4066Sahrens nvlist_free(config); 1150fa9e4066Sahrens 1151990b4856Slling if (props) 1152990b4856Slling nvlist_free(props); 1153990b4856Slling 1154fa9e4066Sahrens return (error); 1155fa9e4066Sahrens } 1156fa9e4066Sahrens 1157fa9e4066Sahrens static int 1158fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1159fa9e4066Sahrens { 1160ecd6cf80Smarks int error; 116189a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1162394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 116389a89ebfSlling 1164ecd6cf80Smarks zfs_log_history(zc); 1165394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1166681d9761SEric Taylor if (error == 0) 1167681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1168ecd6cf80Smarks return (error); 1169fa9e4066Sahrens } 1170fa9e4066Sahrens 1171fa9e4066Sahrens static int 1172fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1173fa9e4066Sahrens { 1174fa9e4066Sahrens nvlist_t *configs; 1175fa9e4066Sahrens int error; 1176fa9e4066Sahrens 1177fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1178fa9e4066Sahrens return (EEXIST); 1179fa9e4066Sahrens 1180e9dbad6fSeschrock error = put_nvlist(zc, configs); 1181fa9e4066Sahrens 1182fa9e4066Sahrens nvlist_free(configs); 1183fa9e4066Sahrens 1184fa9e4066Sahrens return (error); 1185fa9e4066Sahrens } 1186fa9e4066Sahrens 1187fa9e4066Sahrens static int 1188fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1189fa9e4066Sahrens { 1190fa9e4066Sahrens nvlist_t *config; 1191fa9e4066Sahrens int error; 1192ea8dc4b6Seschrock int ret = 0; 1193fa9e4066Sahrens 1194e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1195e9dbad6fSeschrock sizeof (zc->zc_value)); 1196fa9e4066Sahrens 1197fa9e4066Sahrens if (config != NULL) { 1198e9dbad6fSeschrock ret = put_nvlist(zc, config); 1199fa9e4066Sahrens nvlist_free(config); 1200ea8dc4b6Seschrock 1201ea8dc4b6Seschrock /* 1202ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1203ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1204ea8dc4b6Seschrock * in 'zc_cookie'. 1205ea8dc4b6Seschrock */ 1206ea8dc4b6Seschrock zc->zc_cookie = error; 1207fa9e4066Sahrens } else { 1208ea8dc4b6Seschrock ret = error; 1209fa9e4066Sahrens } 1210fa9e4066Sahrens 1211ea8dc4b6Seschrock return (ret); 1212fa9e4066Sahrens } 1213fa9e4066Sahrens 1214fa9e4066Sahrens /* 1215fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1216fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1217fa9e4066Sahrens */ 1218fa9e4066Sahrens static int 1219fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1220fa9e4066Sahrens { 1221fa9e4066Sahrens nvlist_t *tryconfig, *config; 1222fa9e4066Sahrens int error; 1223fa9e4066Sahrens 1224990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1225478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1226fa9e4066Sahrens return (error); 1227fa9e4066Sahrens 1228fa9e4066Sahrens config = spa_tryimport(tryconfig); 1229fa9e4066Sahrens 1230fa9e4066Sahrens nvlist_free(tryconfig); 1231fa9e4066Sahrens 1232fa9e4066Sahrens if (config == NULL) 1233fa9e4066Sahrens return (EINVAL); 1234fa9e4066Sahrens 1235e9dbad6fSeschrock error = put_nvlist(zc, config); 1236fa9e4066Sahrens nvlist_free(config); 1237fa9e4066Sahrens 1238fa9e4066Sahrens return (error); 1239fa9e4066Sahrens } 1240fa9e4066Sahrens 12413f9d6ad7SLin Ling /* 12423f9d6ad7SLin Ling * inputs: 12433f9d6ad7SLin Ling * zc_name name of the pool 12443f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 12453f9d6ad7SLin Ling */ 1246fa9e4066Sahrens static int 12473f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1248fa9e4066Sahrens { 1249fa9e4066Sahrens spa_t *spa; 1250fa9e4066Sahrens int error; 1251fa9e4066Sahrens 125206eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 125306eeb2adSek return (error); 125406eeb2adSek 12553f9d6ad7SLin Ling if (zc->zc_cookie == POOL_SCAN_NONE) 12563f9d6ad7SLin Ling error = spa_scan_stop(spa); 12573f9d6ad7SLin Ling else 12583f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 125906eeb2adSek 126006eeb2adSek spa_close(spa, FTAG); 126106eeb2adSek 1262fa9e4066Sahrens return (error); 1263fa9e4066Sahrens } 1264fa9e4066Sahrens 1265fa9e4066Sahrens static int 1266fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1267fa9e4066Sahrens { 1268fa9e4066Sahrens spa_t *spa; 1269fa9e4066Sahrens int error; 1270fa9e4066Sahrens 1271fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1272fa9e4066Sahrens if (error == 0) { 1273fa9e4066Sahrens spa_freeze(spa); 1274fa9e4066Sahrens spa_close(spa, FTAG); 1275fa9e4066Sahrens } 1276fa9e4066Sahrens return (error); 1277fa9e4066Sahrens } 1278fa9e4066Sahrens 1279eaca9bbdSeschrock static int 1280eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1281eaca9bbdSeschrock { 1282eaca9bbdSeschrock spa_t *spa; 1283eaca9bbdSeschrock int error; 1284eaca9bbdSeschrock 128506eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 128606eeb2adSek return (error); 128706eeb2adSek 1288558d2d50Slling if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) { 1289558d2d50Slling spa_close(spa, FTAG); 1290558d2d50Slling return (EINVAL); 1291558d2d50Slling } 1292558d2d50Slling 1293990b4856Slling spa_upgrade(spa, zc->zc_cookie); 129406eeb2adSek spa_close(spa, FTAG); 129506eeb2adSek 129606eeb2adSek return (error); 129706eeb2adSek } 129806eeb2adSek 129906eeb2adSek static int 130006eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 130106eeb2adSek { 130206eeb2adSek spa_t *spa; 130306eeb2adSek char *hist_buf; 130406eeb2adSek uint64_t size; 130506eeb2adSek int error; 130606eeb2adSek 130706eeb2adSek if ((size = zc->zc_history_len) == 0) 130806eeb2adSek return (EINVAL); 130906eeb2adSek 131006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 131106eeb2adSek return (error); 131206eeb2adSek 1313e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1314d7306b64Sek spa_close(spa, FTAG); 1315d7306b64Sek return (ENOTSUP); 1316d7306b64Sek } 1317d7306b64Sek 131806eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 131906eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 132006eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1321478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1322478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1323478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 132406eeb2adSek } 132506eeb2adSek 132606eeb2adSek spa_close(spa, FTAG); 132706eeb2adSek kmem_free(hist_buf, size); 132806eeb2adSek return (error); 132906eeb2adSek } 133006eeb2adSek 133155434c77Sek static int 133255434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 133355434c77Sek { 133455434c77Sek int error; 133555434c77Sek 1336b1b8ab34Slling if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)) 133755434c77Sek return (error); 133855434c77Sek 133955434c77Sek return (0); 134055434c77Sek } 134155434c77Sek 1342503ad85cSMatthew Ahrens /* 1343503ad85cSMatthew Ahrens * inputs: 1344503ad85cSMatthew Ahrens * zc_name name of filesystem 1345503ad85cSMatthew Ahrens * zc_obj object to find 1346503ad85cSMatthew Ahrens * 1347503ad85cSMatthew Ahrens * outputs: 1348503ad85cSMatthew Ahrens * zc_value name of object 1349503ad85cSMatthew Ahrens */ 135055434c77Sek static int 135155434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 135255434c77Sek { 1353503ad85cSMatthew Ahrens objset_t *os; 135455434c77Sek int error; 135555434c77Sek 1356503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1357503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 135855434c77Sek return (error); 1359503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1360503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1361503ad85cSMatthew Ahrens return (EINVAL); 1362503ad85cSMatthew Ahrens } 1363503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 136455434c77Sek sizeof (zc->zc_value)); 1365503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 136655434c77Sek 136755434c77Sek return (error); 136855434c77Sek } 136955434c77Sek 1370fa9e4066Sahrens static int 1371fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1372fa9e4066Sahrens { 1373fa9e4066Sahrens spa_t *spa; 1374fa9e4066Sahrens int error; 1375e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1376e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1377fa9e4066Sahrens 1378fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1379fa9e4066Sahrens if (error != 0) 1380fa9e4066Sahrens return (error); 1381fa9e4066Sahrens 1382fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1383478ed9adSEric Taylor zc->zc_iflags, &config); 1384fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1385fa94a07fSbrendan &l2cache, &nl2cache); 1386fa94a07fSbrendan 1387e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1388e7cbe64fSgw &spares, &nspares); 1389e7cbe64fSgw 1390b1b8ab34Slling /* 1391b1b8ab34Slling * A root pool with concatenated devices is not supported. 1392e7cbe64fSgw * Thus, can not add a device to a root pool. 1393e7cbe64fSgw * 1394e7cbe64fSgw * Intent log device can not be added to a rootpool because 1395e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1396e7cbe64fSgw * can not be accessed during the mountroot time. 1397e7cbe64fSgw * 1398e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1399b1b8ab34Slling */ 1400b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 14011195e687SMark J Musante nvlist_free(config); 1402b1b8ab34Slling spa_close(spa, FTAG); 1403b1b8ab34Slling return (EDOM); 1404b1b8ab34Slling } 1405b1b8ab34Slling 1406fa94a07fSbrendan if (error == 0) { 1407fa9e4066Sahrens error = spa_vdev_add(spa, config); 1408fa9e4066Sahrens nvlist_free(config); 1409fa9e4066Sahrens } 1410fa9e4066Sahrens spa_close(spa, FTAG); 1411fa9e4066Sahrens return (error); 1412fa9e4066Sahrens } 1413fa9e4066Sahrens 14143f9d6ad7SLin Ling /* 14153f9d6ad7SLin Ling * inputs: 14163f9d6ad7SLin Ling * zc_name name of the pool 14173f9d6ad7SLin Ling * zc_nvlist_conf nvlist of devices to remove 14183f9d6ad7SLin Ling * zc_cookie to stop the remove? 14193f9d6ad7SLin Ling */ 1420fa9e4066Sahrens static int 1421fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1422fa9e4066Sahrens { 142399653d4eSeschrock spa_t *spa; 142499653d4eSeschrock int error; 142599653d4eSeschrock 142699653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 142799653d4eSeschrock if (error != 0) 142899653d4eSeschrock return (error); 142999653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 143099653d4eSeschrock spa_close(spa, FTAG); 143199653d4eSeschrock return (error); 1432fa9e4066Sahrens } 1433fa9e4066Sahrens 1434fa9e4066Sahrens static int 14353d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1436fa9e4066Sahrens { 1437fa9e4066Sahrens spa_t *spa; 1438fa9e4066Sahrens int error; 14393d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1440fa9e4066Sahrens 144106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1442fa9e4066Sahrens return (error); 14433d7072f8Seschrock switch (zc->zc_cookie) { 14443d7072f8Seschrock case VDEV_STATE_ONLINE: 14453d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 14463d7072f8Seschrock break; 1447fa9e4066Sahrens 14483d7072f8Seschrock case VDEV_STATE_OFFLINE: 14493d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 14503d7072f8Seschrock break; 1451fa9e4066Sahrens 14523d7072f8Seschrock case VDEV_STATE_FAULTED: 1453069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1454069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1455069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1456069f55e2SEric Schrock 1457069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 14583d7072f8Seschrock break; 14593d7072f8Seschrock 14603d7072f8Seschrock case VDEV_STATE_DEGRADED: 1461069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1462069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1463069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1464069f55e2SEric Schrock 1465069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 14663d7072f8Seschrock break; 14673d7072f8Seschrock 14683d7072f8Seschrock default: 14693d7072f8Seschrock error = EINVAL; 14703d7072f8Seschrock } 14713d7072f8Seschrock zc->zc_cookie = newstate; 1472fa9e4066Sahrens spa_close(spa, FTAG); 1473fa9e4066Sahrens return (error); 1474fa9e4066Sahrens } 1475fa9e4066Sahrens 1476fa9e4066Sahrens static int 1477fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1478fa9e4066Sahrens { 1479fa9e4066Sahrens spa_t *spa; 1480fa9e4066Sahrens int replacing = zc->zc_cookie; 1481fa9e4066Sahrens nvlist_t *config; 1482fa9e4066Sahrens int error; 1483fa9e4066Sahrens 148406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1485fa9e4066Sahrens return (error); 1486fa9e4066Sahrens 1487990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1488478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1489ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1490fa9e4066Sahrens nvlist_free(config); 1491fa9e4066Sahrens } 1492fa9e4066Sahrens 1493fa9e4066Sahrens spa_close(spa, FTAG); 1494fa9e4066Sahrens return (error); 1495fa9e4066Sahrens } 1496fa9e4066Sahrens 1497fa9e4066Sahrens static int 1498fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1499fa9e4066Sahrens { 1500fa9e4066Sahrens spa_t *spa; 1501fa9e4066Sahrens int error; 1502fa9e4066Sahrens 150306eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1504fa9e4066Sahrens return (error); 1505fa9e4066Sahrens 15068ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1507fa9e4066Sahrens 1508fa9e4066Sahrens spa_close(spa, FTAG); 1509fa9e4066Sahrens return (error); 1510fa9e4066Sahrens } 1511fa9e4066Sahrens 15121195e687SMark J Musante static int 15131195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 15141195e687SMark J Musante { 15151195e687SMark J Musante spa_t *spa; 15161195e687SMark J Musante nvlist_t *config, *props = NULL; 15171195e687SMark J Musante int error; 15181195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 15191195e687SMark J Musante 15201195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 15211195e687SMark J Musante return (error); 15221195e687SMark J Musante 15231195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 15241195e687SMark J Musante zc->zc_iflags, &config)) { 15251195e687SMark J Musante spa_close(spa, FTAG); 15261195e687SMark J Musante return (error); 15271195e687SMark J Musante } 15281195e687SMark J Musante 15291195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 15301195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 15311195e687SMark J Musante zc->zc_iflags, &props))) { 15321195e687SMark J Musante spa_close(spa, FTAG); 15331195e687SMark J Musante nvlist_free(config); 15341195e687SMark J Musante return (error); 15351195e687SMark J Musante } 15361195e687SMark J Musante 15371195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 15381195e687SMark J Musante 15391195e687SMark J Musante spa_close(spa, FTAG); 15401195e687SMark J Musante 15411195e687SMark J Musante nvlist_free(config); 15421195e687SMark J Musante nvlist_free(props); 15431195e687SMark J Musante 15441195e687SMark J Musante return (error); 15451195e687SMark J Musante } 15461195e687SMark J Musante 1547c67d9675Seschrock static int 1548c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 1549c67d9675Seschrock { 1550c67d9675Seschrock spa_t *spa; 1551e9dbad6fSeschrock char *path = zc->zc_value; 1552ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 1553c67d9675Seschrock int error; 1554c67d9675Seschrock 1555c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 1556c67d9675Seschrock if (error != 0) 1557c67d9675Seschrock return (error); 1558c67d9675Seschrock 1559c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 1560c67d9675Seschrock spa_close(spa, FTAG); 1561c67d9675Seschrock return (error); 1562c67d9675Seschrock } 1563c67d9675Seschrock 15646809eb4eSEric Schrock static int 15656809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 15666809eb4eSEric Schrock { 15676809eb4eSEric Schrock spa_t *spa; 15686809eb4eSEric Schrock char *fru = zc->zc_value; 15696809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 15706809eb4eSEric Schrock int error; 15716809eb4eSEric Schrock 15726809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 15736809eb4eSEric Schrock if (error != 0) 15746809eb4eSEric Schrock return (error); 15756809eb4eSEric Schrock 15766809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 15776809eb4eSEric Schrock spa_close(spa, FTAG); 15786809eb4eSEric Schrock return (error); 15796809eb4eSEric Schrock } 15806809eb4eSEric Schrock 15813cb34c60Sahrens /* 15823cb34c60Sahrens * inputs: 15833cb34c60Sahrens * zc_name name of filesystem 15843cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 15853cb34c60Sahrens * 15863cb34c60Sahrens * outputs: 15873cb34c60Sahrens * zc_objset_stats stats 15883cb34c60Sahrens * zc_nvlist_dst property nvlist 15893cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 15903cb34c60Sahrens */ 1591fa9e4066Sahrens static int 1592fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc) 1593fa9e4066Sahrens { 1594fa9e4066Sahrens objset_t *os = NULL; 1595fa9e4066Sahrens int error; 15967f7322feSeschrock nvlist_t *nv; 1597fa9e4066Sahrens 1598503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1599fa9e4066Sahrens return (error); 1600fa9e4066Sahrens 1601a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1602fa9e4066Sahrens 16035ad82045Snd if (zc->zc_nvlist_dst != 0 && 160492241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 1605a2eea2e1Sahrens dmu_objset_stats(os, nv); 1606432f72fdSahrens /* 1607bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 1608432f72fdSahrens * which we aren't supposed to do with a 1609745cd3c5Smaybee * DS_MODE_USER hold, because it could be 1610432f72fdSahrens * inconsistent. So this is a bit of a workaround... 1611503ad85cSMatthew Ahrens * XXX reading with out owning 1612432f72fdSahrens */ 1613e7437265Sahrens if (!zc->zc_objset_stats.dds_inconsistent) { 1614e7437265Sahrens if (dmu_objset_type(os) == DMU_OST_ZVOL) 1615e7437265Sahrens VERIFY(zvol_get_stats(os, nv) == 0); 1616e7437265Sahrens } 1617e9dbad6fSeschrock error = put_nvlist(zc, nv); 16187f7322feSeschrock nvlist_free(nv); 16197f7322feSeschrock } 1620fa9e4066Sahrens 1621503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1622fa9e4066Sahrens return (error); 1623fa9e4066Sahrens } 1624fa9e4066Sahrens 162592241e0bSTom Erickson /* 162692241e0bSTom Erickson * inputs: 162792241e0bSTom Erickson * zc_name name of filesystem 162892241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 162992241e0bSTom Erickson * 163092241e0bSTom Erickson * outputs: 163192241e0bSTom Erickson * zc_nvlist_dst received property nvlist 163292241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 163392241e0bSTom Erickson * 163492241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 163592241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 163692241e0bSTom Erickson * local property values. 163792241e0bSTom Erickson */ 163892241e0bSTom Erickson static int 163992241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 164092241e0bSTom Erickson { 164192241e0bSTom Erickson objset_t *os = NULL; 164292241e0bSTom Erickson int error; 164392241e0bSTom Erickson nvlist_t *nv; 164492241e0bSTom Erickson 164592241e0bSTom Erickson if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 164692241e0bSTom Erickson return (error); 164792241e0bSTom Erickson 164892241e0bSTom Erickson /* 164992241e0bSTom Erickson * Without this check, we would return local property values if the 165092241e0bSTom Erickson * caller has not already received properties on or after 165192241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 165292241e0bSTom Erickson */ 165392241e0bSTom Erickson if (!dsl_prop_get_hasrecvd(os)) { 165492241e0bSTom Erickson dmu_objset_rele(os, FTAG); 165592241e0bSTom Erickson return (ENOTSUP); 165692241e0bSTom Erickson } 165792241e0bSTom Erickson 165892241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 165992241e0bSTom Erickson (error = dsl_prop_get_received(os, &nv)) == 0) { 166092241e0bSTom Erickson error = put_nvlist(zc, nv); 166192241e0bSTom Erickson nvlist_free(nv); 166292241e0bSTom Erickson } 166392241e0bSTom Erickson 166492241e0bSTom Erickson dmu_objset_rele(os, FTAG); 166592241e0bSTom Erickson return (error); 166692241e0bSTom Erickson } 166792241e0bSTom Erickson 1668de8267e0Stimh static int 1669de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 1670de8267e0Stimh { 1671de8267e0Stimh uint64_t value; 1672de8267e0Stimh int error; 1673de8267e0Stimh 1674de8267e0Stimh /* 1675de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 1676de8267e0Stimh * the default value (if there is one). 1677de8267e0Stimh */ 1678de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 1679de8267e0Stimh return (error); 1680de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 1681de8267e0Stimh return (0); 1682de8267e0Stimh } 1683de8267e0Stimh 16843cb34c60Sahrens /* 16853cb34c60Sahrens * inputs: 16863cb34c60Sahrens * zc_name name of filesystem 1687de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 16883cb34c60Sahrens * 16893cb34c60Sahrens * outputs: 1690de8267e0Stimh * zc_nvlist_dst zpl property nvlist 1691de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 16923cb34c60Sahrens */ 1693bd00f61bSrm static int 1694de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 1695bd00f61bSrm { 1696de8267e0Stimh objset_t *os; 1697de8267e0Stimh int err; 1698bd00f61bSrm 1699503ad85cSMatthew Ahrens /* XXX reading without owning */ 1700503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1701de8267e0Stimh return (err); 1702bd00f61bSrm 1703bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1704bd00f61bSrm 1705bd00f61bSrm /* 1706de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 1707745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 1708745cd3c5Smaybee * hold, because it could be inconsistent. 1709bd00f61bSrm */ 1710de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 1711de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 1712de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 1713de8267e0Stimh nvlist_t *nv; 1714de8267e0Stimh 1715de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1716de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 1717de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 1718de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 1719de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 1720de8267e0Stimh err = put_nvlist(zc, nv); 1721de8267e0Stimh nvlist_free(nv); 1722de8267e0Stimh } else { 1723de8267e0Stimh err = ENOENT; 1724de8267e0Stimh } 1725503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1726de8267e0Stimh return (err); 1727bd00f61bSrm } 1728bd00f61bSrm 172914843421SMatthew Ahrens static boolean_t 173014843421SMatthew Ahrens dataset_name_hidden(const char *name) 173114843421SMatthew Ahrens { 173214843421SMatthew Ahrens /* 173314843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 173414843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 173514843421SMatthew Ahrens * temporary datasets (which have a % in their name). 173614843421SMatthew Ahrens */ 173714843421SMatthew Ahrens if (strchr(name, '$') != NULL) 173814843421SMatthew Ahrens return (B_TRUE); 173914843421SMatthew Ahrens if (strchr(name, '%') != NULL) 174014843421SMatthew Ahrens return (B_TRUE); 174114843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 174214843421SMatthew Ahrens return (B_TRUE); 174314843421SMatthew Ahrens return (B_FALSE); 174414843421SMatthew Ahrens } 174514843421SMatthew Ahrens 1746de8267e0Stimh /* 1747de8267e0Stimh * inputs: 1748de8267e0Stimh * zc_name name of filesystem 1749de8267e0Stimh * zc_cookie zap cursor 1750de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 1751de8267e0Stimh * 1752de8267e0Stimh * outputs: 1753de8267e0Stimh * zc_name name of next filesystem 175414843421SMatthew Ahrens * zc_cookie zap cursor 1755de8267e0Stimh * zc_objset_stats stats 1756de8267e0Stimh * zc_nvlist_dst property nvlist 1757de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 1758de8267e0Stimh */ 1759fa9e4066Sahrens static int 1760fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 1761fa9e4066Sahrens { 176287e5029aSahrens objset_t *os; 1763fa9e4066Sahrens int error; 1764fa9e4066Sahrens char *p; 1765620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 1766fa9e4066Sahrens 1767620252bcSChris Kirby top: 1768503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 176987e5029aSahrens if (error == ENOENT) 177087e5029aSahrens error = ESRCH; 177187e5029aSahrens return (error); 1772fa9e4066Sahrens } 1773fa9e4066Sahrens 1774fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 1775fa9e4066Sahrens if (p == NULL || p[1] != '\0') 1776fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 1777fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 1778fa9e4066Sahrens 17795c0b6a79SRich Morris /* 17805c0b6a79SRich Morris * Pre-fetch the datasets. dmu_objset_prefetch() always returns 0 17815c0b6a79SRich Morris * but is not declared void because its called by dmu_objset_find(). 17825c0b6a79SRich Morris */ 17837f73c863SRich Morris if (zc->zc_cookie == 0) { 17847f73c863SRich Morris uint64_t cookie = 0; 17857f73c863SRich Morris int len = sizeof (zc->zc_name) - (p - zc->zc_name); 17867f73c863SRich Morris 17877f73c863SRich Morris while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) 17885c0b6a79SRich Morris (void) dmu_objset_prefetch(p, NULL); 17897f73c863SRich Morris } 17907f73c863SRich Morris 1791fa9e4066Sahrens do { 179287e5029aSahrens error = dmu_dir_list_next(os, 179387e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 179487e5029aSahrens NULL, &zc->zc_cookie); 1795fa9e4066Sahrens if (error == ENOENT) 1796fa9e4066Sahrens error = ESRCH; 1797681d9761SEric Taylor } while (error == 0 && dataset_name_hidden(zc->zc_name) && 1798681d9761SEric Taylor !(zc->zc_iflags & FKIOCTL)); 1799503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1800fa9e4066Sahrens 1801681d9761SEric Taylor /* 1802681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 1803681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 1804681d9761SEric Taylor */ 1805620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 180687e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1807620252bcSChris Kirby if (error == ENOENT) { 1808620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 1809620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 1810620252bcSChris Kirby goto top; 1811620252bcSChris Kirby } 1812620252bcSChris Kirby } 1813fa9e4066Sahrens return (error); 1814fa9e4066Sahrens } 1815fa9e4066Sahrens 18163cb34c60Sahrens /* 18173cb34c60Sahrens * inputs: 18183cb34c60Sahrens * zc_name name of filesystem 18193cb34c60Sahrens * zc_cookie zap cursor 18203cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 18213cb34c60Sahrens * 18223cb34c60Sahrens * outputs: 18233cb34c60Sahrens * zc_name name of next snapshot 18243cb34c60Sahrens * zc_objset_stats stats 18253cb34c60Sahrens * zc_nvlist_dst property nvlist 18263cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 18273cb34c60Sahrens */ 1828fa9e4066Sahrens static int 1829fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 1830fa9e4066Sahrens { 183187e5029aSahrens objset_t *os; 1832fa9e4066Sahrens int error; 1833fa9e4066Sahrens 1834620252bcSChris Kirby top: 18357cbf8b43SRich Morris if (zc->zc_cookie == 0) 18367cbf8b43SRich Morris (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, 18377cbf8b43SRich Morris NULL, DS_FIND_SNAPSHOTS); 18387cbf8b43SRich Morris 1839503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 1840745cd3c5Smaybee if (error) 1841745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 1842fa9e4066Sahrens 1843b81d61a6Slling /* 1844b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 1845b81d61a6Slling * so exit immediately. 1846b81d61a6Slling */ 1847b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 1848503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1849b81d61a6Slling return (ESRCH); 1850fa9e4066Sahrens } 1851fa9e4066Sahrens 185287e5029aSahrens error = dmu_snapshot_list_next(os, 185387e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 1854b38f0970Sck zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL); 1855503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1856620252bcSChris Kirby if (error == 0) { 185787e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1858620252bcSChris Kirby if (error == ENOENT) { 1859620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 1860620252bcSChris Kirby *strchr(zc->zc_name, '@') = '\0'; 1861620252bcSChris Kirby goto top; 1862620252bcSChris Kirby } 1863620252bcSChris Kirby } else if (error == ENOENT) { 1864745cd3c5Smaybee error = ESRCH; 1865620252bcSChris Kirby } 1866fa9e4066Sahrens 18673cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 1868745cd3c5Smaybee if (error) 18693cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 1870fa9e4066Sahrens return (error); 1871fa9e4066Sahrens } 1872fa9e4066Sahrens 187392241e0bSTom Erickson static int 187492241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 1875fa9e4066Sahrens { 187692241e0bSTom Erickson const char *propname = nvpair_name(pair); 187792241e0bSTom Erickson uint64_t *valary; 187892241e0bSTom Erickson unsigned int vallen; 187992241e0bSTom Erickson const char *domain; 1880eeb85002STim Haley char *dash; 188192241e0bSTom Erickson zfs_userquota_prop_t type; 188292241e0bSTom Erickson uint64_t rid; 188392241e0bSTom Erickson uint64_t quota; 188492241e0bSTom Erickson zfsvfs_t *zfsvfs; 188592241e0bSTom Erickson int err; 188692241e0bSTom Erickson 188792241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 188892241e0bSTom Erickson nvlist_t *attrs; 188992241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 1890eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 1891eeb85002STim Haley &pair) != 0) 1892eeb85002STim Haley return (EINVAL); 189392241e0bSTom Erickson } 1894e9dbad6fSeschrock 1895ecd6cf80Smarks /* 1896eeb85002STim Haley * A correctly constructed propname is encoded as 189792241e0bSTom Erickson * userquota@<rid>-<domain>. 1898ecd6cf80Smarks */ 1899eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 1900eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 1901eeb85002STim Haley vallen != 3) 1902eeb85002STim Haley return (EINVAL); 1903eeb85002STim Haley 1904eeb85002STim Haley domain = dash + 1; 1905eeb85002STim Haley type = valary[0]; 1906eeb85002STim Haley rid = valary[1]; 1907eeb85002STim Haley quota = valary[2]; 1908e9dbad6fSeschrock 190992241e0bSTom Erickson err = zfsvfs_hold(dsname, FTAG, &zfsvfs); 191092241e0bSTom Erickson if (err == 0) { 191192241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 191292241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 191392241e0bSTom Erickson } 1914e9dbad6fSeschrock 191592241e0bSTom Erickson return (err); 191692241e0bSTom Erickson } 191714843421SMatthew Ahrens 191892241e0bSTom Erickson /* 191992241e0bSTom Erickson * If the named property is one that has a special function to set its value, 192092241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 192192241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 192292241e0bSTom Erickson * 1923eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 192492241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 192592241e0bSTom Erickson */ 192692241e0bSTom Erickson static int 192792241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 192892241e0bSTom Erickson nvpair_t *pair) 192992241e0bSTom Erickson { 193092241e0bSTom Erickson const char *propname = nvpair_name(pair); 193192241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 193292241e0bSTom Erickson uint64_t intval; 193392241e0bSTom Erickson int err; 1934fa9e4066Sahrens 193592241e0bSTom Erickson if (prop == ZPROP_INVAL) { 193692241e0bSTom Erickson if (zfs_prop_userquota(propname)) 193792241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 193892241e0bSTom Erickson return (-1); 193992241e0bSTom Erickson } 194014843421SMatthew Ahrens 194192241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 194292241e0bSTom Erickson nvlist_t *attrs; 194392241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 194492241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 194592241e0bSTom Erickson &pair) == 0); 194692241e0bSTom Erickson } 1947db870a07Sahrens 194892241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 194992241e0bSTom Erickson return (-1); 1950b24ab676SJeff Bonwick 195192241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 195240feaa91Sahrens 195392241e0bSTom Erickson switch (prop) { 195492241e0bSTom Erickson case ZFS_PROP_QUOTA: 195592241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 195692241e0bSTom Erickson break; 195792241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 195892241e0bSTom Erickson err = dsl_dataset_set_quota(dsname, source, intval); 195992241e0bSTom Erickson break; 196092241e0bSTom Erickson case ZFS_PROP_RESERVATION: 196192241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 196292241e0bSTom Erickson break; 196392241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 196492241e0bSTom Erickson err = dsl_dataset_set_reservation(dsname, source, intval); 196592241e0bSTom Erickson break; 196692241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 196792241e0bSTom Erickson err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip), 196892241e0bSTom Erickson intval); 196992241e0bSTom Erickson break; 197092241e0bSTom Erickson case ZFS_PROP_VERSION: 197192241e0bSTom Erickson { 197292241e0bSTom Erickson zfsvfs_t *zfsvfs; 19739e6eda55Smarks 197492241e0bSTom Erickson if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs)) != 0) 1975b24ab676SJeff Bonwick break; 1976b24ab676SJeff Bonwick 197792241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 197892241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 1979d0f3f37eSMark Shellenbaum 198092241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 1981b16da2e2SGeorge Wilson zfs_cmd_t *zc; 1982b16da2e2SGeorge Wilson 1983b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 1984b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 1985b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 1986b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 198740feaa91Sahrens } 198892241e0bSTom Erickson break; 1989ecd6cf80Smarks } 1990ecd6cf80Smarks 199192241e0bSTom Erickson default: 199292241e0bSTom Erickson err = -1; 199392241e0bSTom Erickson } 1994e9dbad6fSeschrock 199592241e0bSTom Erickson return (err); 199692241e0bSTom Erickson } 1997a9799022Sck 199892241e0bSTom Erickson /* 199992241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 200092241e0bSTom Erickson * it continues to set as many as it can and returns the first error 200192241e0bSTom Erickson * encountered. If the caller provides a non-NULL errlist, it also gives the 200292241e0bSTom Erickson * complete list of names of all the properties it failed to set along with the 200392241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 200492241e0bSTom Erickson * returned errlist. 200592241e0bSTom Erickson * 200692241e0bSTom Erickson * If every property is set successfully, zero is returned and the list pointed 200792241e0bSTom Erickson * at by errlist is NULL. 200892241e0bSTom Erickson */ 200992241e0bSTom Erickson int 201092241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 201192241e0bSTom Erickson nvlist_t **errlist) 201292241e0bSTom Erickson { 201392241e0bSTom Erickson nvpair_t *pair; 201492241e0bSTom Erickson nvpair_t *propval; 201502e383d1STom Erickson int rv = 0; 201692241e0bSTom Erickson uint64_t intval; 201792241e0bSTom Erickson char *strval; 201892241e0bSTom Erickson nvlist_t *genericnvl; 201992241e0bSTom Erickson nvlist_t *errors; 202092241e0bSTom Erickson nvlist_t *retrynvl; 2021e9dbad6fSeschrock 202292241e0bSTom Erickson VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 202392241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 202492241e0bSTom Erickson VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2025a9799022Sck 202692241e0bSTom Erickson retry: 202792241e0bSTom Erickson pair = NULL; 202892241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 202992241e0bSTom Erickson const char *propname = nvpair_name(pair); 203092241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2031cfa69fd2STom Erickson int err = 0; 2032e9dbad6fSeschrock 203392241e0bSTom Erickson /* decode the property value */ 203492241e0bSTom Erickson propval = pair; 203592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 203692241e0bSTom Erickson nvlist_t *attrs; 203792241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2038eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2039eeb85002STim Haley &propval) != 0) 2040eeb85002STim Haley err = EINVAL; 204114843421SMatthew Ahrens } 2042e9dbad6fSeschrock 204392241e0bSTom Erickson /* Validate value type */ 2044eeb85002STim Haley if (err == 0 && prop == ZPROP_INVAL) { 204592241e0bSTom Erickson if (zfs_prop_user(propname)) { 204692241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 204792241e0bSTom Erickson err = EINVAL; 204892241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 204992241e0bSTom Erickson if (nvpair_type(propval) != 205092241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 205192241e0bSTom Erickson err = EINVAL; 20524201a95eSRic Aleshire } 2053eeb85002STim Haley } else if (err == 0) { 205492241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 205592241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 205692241e0bSTom Erickson err = EINVAL; 205792241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2058a2eea2e1Sahrens const char *unused; 2059a2eea2e1Sahrens 206092241e0bSTom Erickson VERIFY(nvpair_value_uint64(propval, 206192241e0bSTom Erickson &intval) == 0); 2062e9dbad6fSeschrock 2063e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 206491ebeef5Sahrens case PROP_TYPE_NUMBER: 2065e9dbad6fSeschrock break; 206691ebeef5Sahrens case PROP_TYPE_STRING: 206792241e0bSTom Erickson err = EINVAL; 206892241e0bSTom Erickson break; 206991ebeef5Sahrens case PROP_TYPE_INDEX: 2070acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 207192241e0bSTom Erickson intval, &unused) != 0) 207292241e0bSTom Erickson err = EINVAL; 2073e9dbad6fSeschrock break; 2074e9dbad6fSeschrock default: 2075e7437265Sahrens cmn_err(CE_PANIC, 2076e7437265Sahrens "unknown property type"); 2077e9dbad6fSeschrock } 2078e9dbad6fSeschrock } else { 207992241e0bSTom Erickson err = EINVAL; 2080e9dbad6fSeschrock } 2081e9dbad6fSeschrock } 208292241e0bSTom Erickson 208392241e0bSTom Erickson /* Validate permissions */ 208492241e0bSTom Erickson if (err == 0) 208592241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 208692241e0bSTom Erickson 208792241e0bSTom Erickson if (err == 0) { 208892241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 208992241e0bSTom Erickson if (err == -1) { 209092241e0bSTom Erickson /* 209192241e0bSTom Erickson * For better performance we build up a list of 209292241e0bSTom Erickson * properties to set in a single transaction. 209392241e0bSTom Erickson */ 209492241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 209592241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 209692241e0bSTom Erickson /* 209792241e0bSTom Erickson * This may be a spurious error caused by 209892241e0bSTom Erickson * receiving quota and reservation out of order. 209992241e0bSTom Erickson * Try again in a second pass. 210092241e0bSTom Erickson */ 210192241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 210292241e0bSTom Erickson } 210392241e0bSTom Erickson } 210492241e0bSTom Erickson 210592241e0bSTom Erickson if (err != 0) 210692241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, propname, err) == 0); 2107e9dbad6fSeschrock } 2108e9dbad6fSeschrock 210992241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 211092241e0bSTom Erickson nvl = retrynvl; 211192241e0bSTom Erickson goto retry; 211292241e0bSTom Erickson } 211392241e0bSTom Erickson 211492241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 211592241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 211692241e0bSTom Erickson /* 211792241e0bSTom Erickson * If this fails, we still want to set as many properties as we 211892241e0bSTom Erickson * can, so try setting them individually. 211992241e0bSTom Erickson */ 212092241e0bSTom Erickson pair = NULL; 212192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 212292241e0bSTom Erickson const char *propname = nvpair_name(pair); 2123cfa69fd2STom Erickson int err = 0; 212492241e0bSTom Erickson 212592241e0bSTom Erickson propval = pair; 212692241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 212792241e0bSTom Erickson nvlist_t *attrs; 212892241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 212992241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 213092241e0bSTom Erickson &propval) == 0); 213192241e0bSTom Erickson } 213292241e0bSTom Erickson 213392241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 213492241e0bSTom Erickson VERIFY(nvpair_value_string(propval, 213592241e0bSTom Erickson &strval) == 0); 213692241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 1, 213792241e0bSTom Erickson strlen(strval) + 1, strval); 213892241e0bSTom Erickson } else { 213992241e0bSTom Erickson VERIFY(nvpair_value_uint64(propval, 214092241e0bSTom Erickson &intval) == 0); 214192241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 8, 214292241e0bSTom Erickson 1, &intval); 214392241e0bSTom Erickson } 214492241e0bSTom Erickson 214592241e0bSTom Erickson if (err != 0) { 214692241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, propname, 214792241e0bSTom Erickson err) == 0); 214892241e0bSTom Erickson } 214992241e0bSTom Erickson } 21505c0b6a79SRich Morris } 21515c0b6a79SRich Morris nvlist_free(genericnvl); 215292241e0bSTom Erickson nvlist_free(retrynvl); 215392241e0bSTom Erickson 215492241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 215592241e0bSTom Erickson nvlist_free(errors); 215692241e0bSTom Erickson errors = NULL; 215792241e0bSTom Erickson } else { 215892241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 215992241e0bSTom Erickson } 216092241e0bSTom Erickson 216192241e0bSTom Erickson if (errlist == NULL) 216292241e0bSTom Erickson nvlist_free(errors); 216392241e0bSTom Erickson else 216492241e0bSTom Erickson *errlist = errors; 216592241e0bSTom Erickson 216692241e0bSTom Erickson return (rv); 2167fa9e4066Sahrens } 2168fa9e4066Sahrens 2169ea2f5b9eSMatthew Ahrens /* 2170ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2171ea2f5b9eSMatthew Ahrens */ 2172ea2f5b9eSMatthew Ahrens static int 2173ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl) 2174ea2f5b9eSMatthew Ahrens { 217592241e0bSTom Erickson nvpair_t *pair = NULL; 2176ea2f5b9eSMatthew Ahrens int error = 0; 2177ea2f5b9eSMatthew Ahrens 217892241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 217992241e0bSTom Erickson const char *propname = nvpair_name(pair); 2180ea2f5b9eSMatthew Ahrens char *valstr; 2181ea2f5b9eSMatthew Ahrens 2182ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 218392241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2184ea2f5b9eSMatthew Ahrens return (EINVAL); 2185ea2f5b9eSMatthew Ahrens 2186ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2187ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2188ea2f5b9eSMatthew Ahrens return (error); 2189ea2f5b9eSMatthew Ahrens 2190ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2191ea2f5b9eSMatthew Ahrens return (ENAMETOOLONG); 2192ea2f5b9eSMatthew Ahrens 219392241e0bSTom Erickson VERIFY(nvpair_value_string(pair, &valstr) == 0); 2194ea2f5b9eSMatthew Ahrens if (strlen(valstr) >= ZAP_MAXVALUELEN) 2195ea2f5b9eSMatthew Ahrens return (E2BIG); 2196ea2f5b9eSMatthew Ahrens } 2197ea2f5b9eSMatthew Ahrens return (0); 2198ea2f5b9eSMatthew Ahrens } 2199ea2f5b9eSMatthew Ahrens 220092241e0bSTom Erickson static void 220192241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 220292241e0bSTom Erickson { 220392241e0bSTom Erickson nvpair_t *pair; 220492241e0bSTom Erickson 220592241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 220692241e0bSTom Erickson 220792241e0bSTom Erickson pair = NULL; 220892241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 220992241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 221092241e0bSTom Erickson continue; 221192241e0bSTom Erickson 221292241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 221392241e0bSTom Erickson } 221492241e0bSTom Erickson } 221592241e0bSTom Erickson 221692241e0bSTom Erickson static int 221792241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props, 221892241e0bSTom Erickson nvlist_t *skipped) 221992241e0bSTom Erickson { 222092241e0bSTom Erickson int err = 0; 222192241e0bSTom Erickson nvlist_t *cleared_props = NULL; 222292241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 222392241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 222492241e0bSTom Erickson /* 222592241e0bSTom Erickson * Acts on local properties until the dataset has received 222692241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 222792241e0bSTom Erickson */ 222892241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 222992241e0bSTom Erickson (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0)); 223092241e0bSTom Erickson err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL); 223192241e0bSTom Erickson } 223292241e0bSTom Erickson nvlist_free(cleared_props); 223392241e0bSTom Erickson return (err); 223492241e0bSTom Erickson } 223592241e0bSTom Erickson 22363cb34c60Sahrens /* 22373cb34c60Sahrens * inputs: 22383cb34c60Sahrens * zc_name name of filesystem 22395c0b6a79SRich Morris * zc_value name of property to set 22403cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 224192241e0bSTom Erickson * zc_cookie received properties flag 22423cb34c60Sahrens * 224392241e0bSTom Erickson * outputs: 224492241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 22453cb34c60Sahrens */ 2246fa9e4066Sahrens static int 2247e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2248fa9e4066Sahrens { 2249e9dbad6fSeschrock nvlist_t *nvl; 225092241e0bSTom Erickson boolean_t received = zc->zc_cookie; 225192241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 225292241e0bSTom Erickson ZPROP_SRC_LOCAL); 225392241e0bSTom Erickson nvlist_t *errors = NULL; 2254e9dbad6fSeschrock int error; 2255e9dbad6fSeschrock 2256990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2257478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2258e9dbad6fSeschrock return (error); 2259e9dbad6fSeschrock 226092241e0bSTom Erickson if (received) { 2261bb0ade09Sahrens nvlist_t *origprops; 2262bb0ade09Sahrens objset_t *os; 2263bb0ade09Sahrens 2264503ad85cSMatthew Ahrens if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) { 226592241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 226692241e0bSTom Erickson (void) clear_received_props(os, 226792241e0bSTom Erickson zc->zc_name, origprops, nvl); 2268bb0ade09Sahrens nvlist_free(origprops); 2269bb0ade09Sahrens } 227092241e0bSTom Erickson 227192241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 2272503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2273bb0ade09Sahrens } 2274bb0ade09Sahrens } 2275bb0ade09Sahrens 227692241e0bSTom Erickson error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors); 227792241e0bSTom Erickson 227892241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 227992241e0bSTom Erickson (void) put_nvlist(zc, errors); 228092241e0bSTom Erickson } 2281ecd6cf80Smarks 228292241e0bSTom Erickson nvlist_free(errors); 2283e9dbad6fSeschrock nvlist_free(nvl); 2284e9dbad6fSeschrock return (error); 2285fa9e4066Sahrens } 2286fa9e4066Sahrens 22873cb34c60Sahrens /* 22883cb34c60Sahrens * inputs: 22893cb34c60Sahrens * zc_name name of filesystem 22903cb34c60Sahrens * zc_value name of property to inherit 229192241e0bSTom Erickson * zc_cookie revert to received value if TRUE 22923cb34c60Sahrens * 22933cb34c60Sahrens * outputs: none 22943cb34c60Sahrens */ 2295e45ce728Sahrens static int 2296e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2297e45ce728Sahrens { 229892241e0bSTom Erickson const char *propname = zc->zc_value; 229992241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 230092241e0bSTom Erickson boolean_t received = zc->zc_cookie; 230192241e0bSTom Erickson zprop_source_t source = (received 230292241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 230392241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 230492241e0bSTom Erickson 230592241e0bSTom Erickson if (received) { 230692241e0bSTom Erickson nvlist_t *dummy; 230792241e0bSTom Erickson nvpair_t *pair; 230892241e0bSTom Erickson zprop_type_t type; 230992241e0bSTom Erickson int err; 231092241e0bSTom Erickson 231192241e0bSTom Erickson /* 231292241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 231392241e0bSTom Erickson * nvpair with type info. 231492241e0bSTom Erickson */ 231592241e0bSTom Erickson if (prop == ZPROP_INVAL) { 231692241e0bSTom Erickson if (!zfs_prop_user(propname)) 231792241e0bSTom Erickson return (EINVAL); 231892241e0bSTom Erickson 231992241e0bSTom Erickson type = PROP_TYPE_STRING; 2320a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2321a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2322a79992aaSTom Erickson return (EINVAL); 232392241e0bSTom Erickson } else { 232492241e0bSTom Erickson type = zfs_prop_get_type(prop); 232592241e0bSTom Erickson } 232692241e0bSTom Erickson 232792241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 232892241e0bSTom Erickson 232992241e0bSTom Erickson switch (type) { 233092241e0bSTom Erickson case PROP_TYPE_STRING: 233192241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 233292241e0bSTom Erickson break; 233392241e0bSTom Erickson case PROP_TYPE_NUMBER: 233492241e0bSTom Erickson case PROP_TYPE_INDEX: 233592241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 233692241e0bSTom Erickson break; 233792241e0bSTom Erickson default: 233892241e0bSTom Erickson nvlist_free(dummy); 233992241e0bSTom Erickson return (EINVAL); 234092241e0bSTom Erickson } 234192241e0bSTom Erickson 234292241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 234392241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 234492241e0bSTom Erickson nvlist_free(dummy); 234592241e0bSTom Erickson if (err != -1) 234692241e0bSTom Erickson return (err); /* special property already handled */ 234792241e0bSTom Erickson } else { 234892241e0bSTom Erickson /* 234992241e0bSTom Erickson * Only check this in the non-received case. We want to allow 235092241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 235192241e0bSTom Erickson * and reservation to the received or default values even though 235292241e0bSTom Erickson * they are not considered inheritable. 235392241e0bSTom Erickson */ 235492241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 235592241e0bSTom Erickson return (EINVAL); 235692241e0bSTom Erickson } 235792241e0bSTom Erickson 2358e45ce728Sahrens /* the property name has been validated by zfs_secpolicy_inherit() */ 235992241e0bSTom Erickson return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL)); 2360e45ce728Sahrens } 2361e45ce728Sahrens 2362b1b8ab34Slling static int 236311a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2364b1b8ab34Slling { 2365990b4856Slling nvlist_t *props; 2366b1b8ab34Slling spa_t *spa; 2367990b4856Slling int error; 236892241e0bSTom Erickson nvpair_t *pair; 2369b1b8ab34Slling 237092241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 237192241e0bSTom Erickson zc->zc_iflags, &props)) 2372b1b8ab34Slling return (error); 2373b1b8ab34Slling 2374379c004dSEric Schrock /* 2375379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2376379c004dSEric Schrock * to handle the faulted case. 2377379c004dSEric Schrock */ 237892241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 237992241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2380379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 238192241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2382379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2383379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2384379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2385379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2386379c004dSEric Schrock } 2387379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2388b693757aSEric Schrock if (spa != NULL) { 2389b693757aSEric Schrock nvlist_free(props); 2390379c004dSEric Schrock return (0); 2391b693757aSEric Schrock } 2392379c004dSEric Schrock } 2393379c004dSEric Schrock 2394b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2395990b4856Slling nvlist_free(props); 2396b1b8ab34Slling return (error); 2397b1b8ab34Slling } 2398b1b8ab34Slling 2399990b4856Slling error = spa_prop_set(spa, props); 2400b1b8ab34Slling 2401990b4856Slling nvlist_free(props); 2402b1b8ab34Slling spa_close(spa, FTAG); 2403b1b8ab34Slling 2404b1b8ab34Slling return (error); 2405b1b8ab34Slling } 2406b1b8ab34Slling 2407b1b8ab34Slling static int 240811a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2409b1b8ab34Slling { 2410b1b8ab34Slling spa_t *spa; 2411b1b8ab34Slling int error; 2412b1b8ab34Slling nvlist_t *nvp = NULL; 2413b1b8ab34Slling 2414379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2415379c004dSEric Schrock /* 2416379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2417379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2418379c004dSEric Schrock * anyway. 2419379c004dSEric Schrock */ 2420379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2421379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2422379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2423379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2424379c004dSEric Schrock } else { 2425379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2426379c004dSEric Schrock spa_close(spa, FTAG); 2427379c004dSEric Schrock } 2428b1b8ab34Slling 2429b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2430b1b8ab34Slling error = put_nvlist(zc, nvp); 2431b1b8ab34Slling else 2432b1b8ab34Slling error = EFAULT; 2433b1b8ab34Slling 2434379c004dSEric Schrock nvlist_free(nvp); 2435b1b8ab34Slling return (error); 2436b1b8ab34Slling } 2437b1b8ab34Slling 24383cb34c60Sahrens /* 24393cb34c60Sahrens * inputs: 24403cb34c60Sahrens * zc_name name of filesystem 24413cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 24423cb34c60Sahrens * zc_perm_action allow/unallow flag 24433cb34c60Sahrens * 24443cb34c60Sahrens * outputs: none 24453cb34c60Sahrens */ 2446ecd6cf80Smarks static int 2447ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2448ecd6cf80Smarks { 2449ecd6cf80Smarks int error; 2450ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2451ecd6cf80Smarks 2452990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2453478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2454ecd6cf80Smarks return (error); 2455ecd6cf80Smarks 2456ecd6cf80Smarks /* 2457ecd6cf80Smarks * Verify nvlist is constructed correctly 2458ecd6cf80Smarks */ 2459ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2460ecd6cf80Smarks nvlist_free(fsaclnv); 2461ecd6cf80Smarks return (EINVAL); 2462ecd6cf80Smarks } 2463ecd6cf80Smarks 2464ecd6cf80Smarks /* 2465ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2466ecd6cf80Smarks * that user is allowed to hand out each permission in 2467ecd6cf80Smarks * the nvlist(s) 2468ecd6cf80Smarks */ 2469ecd6cf80Smarks 247091ebeef5Sahrens error = secpolicy_zfs(CRED()); 2471ecd6cf80Smarks if (error) { 247291ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 247391ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 247491ebeef5Sahrens fsaclnv, CRED()); 247591ebeef5Sahrens } else { 247691ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 247791ebeef5Sahrens fsaclnv, CRED()); 247891ebeef5Sahrens } 2479ecd6cf80Smarks } 2480ecd6cf80Smarks 2481ecd6cf80Smarks if (error == 0) 2482ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2483ecd6cf80Smarks 2484ecd6cf80Smarks nvlist_free(fsaclnv); 2485ecd6cf80Smarks return (error); 2486ecd6cf80Smarks } 2487ecd6cf80Smarks 24883cb34c60Sahrens /* 24893cb34c60Sahrens * inputs: 24903cb34c60Sahrens * zc_name name of filesystem 24913cb34c60Sahrens * 24923cb34c60Sahrens * outputs: 24933cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 24943cb34c60Sahrens */ 2495ecd6cf80Smarks static int 2496ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2497ecd6cf80Smarks { 2498ecd6cf80Smarks nvlist_t *nvp; 2499ecd6cf80Smarks int error; 2500ecd6cf80Smarks 2501ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2502ecd6cf80Smarks error = put_nvlist(zc, nvp); 2503ecd6cf80Smarks nvlist_free(nvp); 2504ecd6cf80Smarks } 2505ecd6cf80Smarks 2506ecd6cf80Smarks return (error); 2507ecd6cf80Smarks } 2508ecd6cf80Smarks 2509fa9e4066Sahrens /* 2510fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2511fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2512fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2513fa9e4066Sahrens */ 2514fa9e4066Sahrens static vfs_t * 2515fa9e4066Sahrens zfs_get_vfs(const char *resource) 2516fa9e4066Sahrens { 2517fa9e4066Sahrens struct vfs *vfsp; 2518fa9e4066Sahrens struct vfs *vfs_found = NULL; 2519fa9e4066Sahrens 2520fa9e4066Sahrens vfs_list_read_lock(); 2521fa9e4066Sahrens vfsp = rootvfs; 2522fa9e4066Sahrens do { 2523fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2524fa9e4066Sahrens VFS_HOLD(vfsp); 2525fa9e4066Sahrens vfs_found = vfsp; 2526fa9e4066Sahrens break; 2527fa9e4066Sahrens } 2528fa9e4066Sahrens vfsp = vfsp->vfs_next; 2529fa9e4066Sahrens } while (vfsp != rootvfs); 2530fa9e4066Sahrens vfs_list_unlock(); 2531fa9e4066Sahrens return (vfs_found); 2532fa9e4066Sahrens } 2533fa9e4066Sahrens 2534ecd6cf80Smarks /* ARGSUSED */ 2535fa9e4066Sahrens static void 2536ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2537fa9e4066Sahrens { 2538da6c28aaSamw zfs_creat_t *zct = arg; 2539da6c28aaSamw 2540de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2541da6c28aaSamw } 2542da6c28aaSamw 2543de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2544da6c28aaSamw 2545da6c28aaSamw /* 2546de8267e0Stimh * inputs: 25470a48a24eStimh * createprops list of properties requested by creator 25480a48a24eStimh * default_zplver zpl version to use if unspecified in createprops 25490a48a24eStimh * fuids_ok fuids allowed in this version of the spa? 25500a48a24eStimh * os parent objset pointer (NULL if root fs) 2551de8267e0Stimh * 2552de8267e0Stimh * outputs: 2553de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 25540a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 2555da6c28aaSamw * 2556de8267e0Stimh * Determine the settings for utf8only, normalization and 2557de8267e0Stimh * casesensitivity. Specific values may have been requested by the 2558de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 2559de8267e0Stimh * the file system is of too early a vintage, a creator can not 2560de8267e0Stimh * request settings for these properties, even if the requested 2561de8267e0Stimh * setting is the default value. We don't actually want to create dsl 2562de8267e0Stimh * properties for these, so remove them from the source nvlist after 2563de8267e0Stimh * processing. 2564da6c28aaSamw */ 2565da6c28aaSamw static int 256614843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 25670a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 25680a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 2569da6c28aaSamw { 2570de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 2571de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 2572de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 2573da6c28aaSamw 2574de8267e0Stimh ASSERT(zplprops != NULL); 2575da6c28aaSamw 2576de8267e0Stimh /* 2577de8267e0Stimh * Pull out creator prop choices, if any. 2578de8267e0Stimh */ 2579de8267e0Stimh if (createprops) { 25800a48a24eStimh (void) nvlist_lookup_uint64(createprops, 25810a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 2582de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2583de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 2584de8267e0Stimh (void) nvlist_remove_all(createprops, 2585de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 2586de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2587de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 2588de8267e0Stimh (void) nvlist_remove_all(createprops, 2589de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 2590de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2591de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 2592de8267e0Stimh (void) nvlist_remove_all(createprops, 2593de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 2594de8267e0Stimh } 2595da6c28aaSamw 2596c2a93d44Stimh /* 25970a48a24eStimh * If the zpl version requested is whacky or the file system 25980a48a24eStimh * or pool is version is too "young" to support normalization 25990a48a24eStimh * and the creator tried to set a value for one of the props, 26000a48a24eStimh * error out. 2601c2a93d44Stimh */ 26020a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 26030a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 26040a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 26050a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 2606de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 26070a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 2608de8267e0Stimh return (ENOTSUP); 2609c2a93d44Stimh 2610de8267e0Stimh /* 2611de8267e0Stimh * Put the version in the zplprops 2612de8267e0Stimh */ 2613de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2614de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 2615da6c28aaSamw 2616de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 2617de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 2618de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2619de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 2620da6c28aaSamw 2621c2a93d44Stimh /* 2622de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 2623c2a93d44Stimh */ 2624de8267e0Stimh if (norm) 2625de8267e0Stimh u8 = 1; 2626de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 2627de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 2628de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2629de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 2630de8267e0Stimh 2631de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 2632de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 2633de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2634de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 2635c2a93d44Stimh 2636ab04eb8eStimh if (is_ci) 2637ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 2638ab04eb8eStimh 2639da6c28aaSamw return (0); 2640fa9e4066Sahrens } 2641fa9e4066Sahrens 26420a48a24eStimh static int 26430a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 26440a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 26450a48a24eStimh { 26460a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 26470a48a24eStimh uint64_t zplver = ZPL_VERSION; 26480a48a24eStimh objset_t *os = NULL; 26490a48a24eStimh char parentname[MAXNAMELEN]; 26500a48a24eStimh char *cp; 26510a586ceaSMark Shellenbaum spa_t *spa; 26520a586ceaSMark Shellenbaum uint64_t spa_vers; 26530a48a24eStimh int error; 26540a48a24eStimh 26550a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 26560a48a24eStimh cp = strrchr(parentname, '/'); 26570a48a24eStimh ASSERT(cp != NULL); 26580a48a24eStimh cp[0] = '\0'; 26590a48a24eStimh 26600a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 26610a586ceaSMark Shellenbaum return (error); 26620a586ceaSMark Shellenbaum 26630a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 26640a586ceaSMark Shellenbaum spa_close(spa, FTAG); 26650a586ceaSMark Shellenbaum 26660a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 26670a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 26680a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 26690a48a24eStimh 26700a48a24eStimh /* 26710a48a24eStimh * Open parent object set so we can inherit zplprop values. 26720a48a24eStimh */ 2673503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 26740a48a24eStimh return (error); 26750a48a24eStimh 26760a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 26770a48a24eStimh zplprops, is_ci); 2678503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 26790a48a24eStimh return (error); 26800a48a24eStimh } 26810a48a24eStimh 26820a48a24eStimh static int 26830a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 26840a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 26850a48a24eStimh { 26860a586ceaSMark Shellenbaum boolean_t fuids_ok; 26870a586ceaSMark Shellenbaum boolean_t sa_ok; 26880a48a24eStimh uint64_t zplver = ZPL_VERSION; 26890a48a24eStimh int error; 26900a48a24eStimh 26910a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 26920a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 26930a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 26940a48a24eStimh 26950a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 26960a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 26970a48a24eStimh return (error); 26980a48a24eStimh } 26990a48a24eStimh 27003cb34c60Sahrens /* 27013cb34c60Sahrens * inputs: 27023cb34c60Sahrens * zc_objset_type type of objset to create (fs vs zvol) 27033cb34c60Sahrens * zc_name name of new objset 27043cb34c60Sahrens * zc_value name of snapshot to clone from (may be empty) 27053cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 27063cb34c60Sahrens * 2707de8267e0Stimh * outputs: none 27083cb34c60Sahrens */ 2709fa9e4066Sahrens static int 2710fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc) 2711fa9e4066Sahrens { 2712fa9e4066Sahrens objset_t *clone; 2713fa9e4066Sahrens int error = 0; 2714da6c28aaSamw zfs_creat_t zct; 2715ecd6cf80Smarks nvlist_t *nvprops = NULL; 2716ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 2717fa9e4066Sahrens dmu_objset_type_t type = zc->zc_objset_type; 2718fa9e4066Sahrens 2719fa9e4066Sahrens switch (type) { 2720fa9e4066Sahrens 2721fa9e4066Sahrens case DMU_OST_ZFS: 2722fa9e4066Sahrens cbfunc = zfs_create_cb; 2723fa9e4066Sahrens break; 2724fa9e4066Sahrens 2725fa9e4066Sahrens case DMU_OST_ZVOL: 2726fa9e4066Sahrens cbfunc = zvol_create_cb; 2727fa9e4066Sahrens break; 2728fa9e4066Sahrens 2729fa9e4066Sahrens default: 27301d452cf5Sahrens cbfunc = NULL; 2731e7cbe64fSgw break; 2732fa9e4066Sahrens } 2733f18faf3fSek if (strchr(zc->zc_name, '@') || 2734f18faf3fSek strchr(zc->zc_name, '%')) 27351d452cf5Sahrens return (EINVAL); 2736fa9e4066Sahrens 2737e9dbad6fSeschrock if (zc->zc_nvlist_src != NULL && 2738990b4856Slling (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2739478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2740e9dbad6fSeschrock return (error); 2741e9dbad6fSeschrock 2742de8267e0Stimh zct.zct_zplprops = NULL; 2743da6c28aaSamw zct.zct_props = nvprops; 2744da6c28aaSamw 2745e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 2746fa9e4066Sahrens /* 2747fa9e4066Sahrens * We're creating a clone of an existing snapshot. 2748fa9e4066Sahrens */ 2749e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 2750e9dbad6fSeschrock if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) { 2751ecd6cf80Smarks nvlist_free(nvprops); 2752fa9e4066Sahrens return (EINVAL); 2753e9dbad6fSeschrock } 2754fa9e4066Sahrens 2755503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_value, FTAG, &clone); 2756e9dbad6fSeschrock if (error) { 2757ecd6cf80Smarks nvlist_free(nvprops); 2758fa9e4066Sahrens return (error); 2759e9dbad6fSeschrock } 2760ab04eb8eStimh 2761ae46e4c7SMatthew Ahrens error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0); 2762503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 2763da6c28aaSamw if (error) { 2764da6c28aaSamw nvlist_free(nvprops); 2765da6c28aaSamw return (error); 2766da6c28aaSamw } 2767fa9e4066Sahrens } else { 2768ab04eb8eStimh boolean_t is_insensitive = B_FALSE; 2769ab04eb8eStimh 2770e9dbad6fSeschrock if (cbfunc == NULL) { 2771ecd6cf80Smarks nvlist_free(nvprops); 27721d452cf5Sahrens return (EINVAL); 2773e9dbad6fSeschrock } 27745c5460e9Seschrock 2775e9dbad6fSeschrock if (type == DMU_OST_ZVOL) { 2776e9dbad6fSeschrock uint64_t volsize, volblocksize; 2777e9dbad6fSeschrock 2778ecd6cf80Smarks if (nvprops == NULL || 2779ecd6cf80Smarks nvlist_lookup_uint64(nvprops, 2780e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 2781e9dbad6fSeschrock &volsize) != 0) { 2782ecd6cf80Smarks nvlist_free(nvprops); 2783e9dbad6fSeschrock return (EINVAL); 2784e9dbad6fSeschrock } 2785e9dbad6fSeschrock 2786ecd6cf80Smarks if ((error = nvlist_lookup_uint64(nvprops, 2787e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2788e9dbad6fSeschrock &volblocksize)) != 0 && error != ENOENT) { 2789ecd6cf80Smarks nvlist_free(nvprops); 2790e9dbad6fSeschrock return (EINVAL); 2791e9dbad6fSeschrock } 2792e9dbad6fSeschrock 2793e9dbad6fSeschrock if (error != 0) 2794e9dbad6fSeschrock volblocksize = zfs_prop_default_numeric( 2795e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2796e9dbad6fSeschrock 2797e9dbad6fSeschrock if ((error = zvol_check_volblocksize( 2798e9dbad6fSeschrock volblocksize)) != 0 || 2799e9dbad6fSeschrock (error = zvol_check_volsize(volsize, 2800e9dbad6fSeschrock volblocksize)) != 0) { 2801ecd6cf80Smarks nvlist_free(nvprops); 28025c5460e9Seschrock return (error); 2803e9dbad6fSeschrock } 2804e7437265Sahrens } else if (type == DMU_OST_ZFS) { 2805da6c28aaSamw int error; 2806da6c28aaSamw 2807da6c28aaSamw /* 2808da6c28aaSamw * We have to have normalization and 2809da6c28aaSamw * case-folding flags correct when we do the 2810da6c28aaSamw * file system creation, so go figure them out 2811de8267e0Stimh * now. 2812da6c28aaSamw */ 2813de8267e0Stimh VERIFY(nvlist_alloc(&zct.zct_zplprops, 2814de8267e0Stimh NV_UNIQUE_NAME, KM_SLEEP) == 0); 2815de8267e0Stimh error = zfs_fill_zplprops(zc->zc_name, nvprops, 28160a48a24eStimh zct.zct_zplprops, &is_insensitive); 2817da6c28aaSamw if (error != 0) { 2818da6c28aaSamw nvlist_free(nvprops); 2819de8267e0Stimh nvlist_free(zct.zct_zplprops); 2820da6c28aaSamw return (error); 2821da6c28aaSamw } 2822da6c28aaSamw } 2823ae46e4c7SMatthew Ahrens error = dmu_objset_create(zc->zc_name, type, 2824ab04eb8eStimh is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 2825de8267e0Stimh nvlist_free(zct.zct_zplprops); 2826fa9e4066Sahrens } 2827e9dbad6fSeschrock 2828e9dbad6fSeschrock /* 2829e9dbad6fSeschrock * It would be nice to do this atomically. 2830e9dbad6fSeschrock */ 2831e9dbad6fSeschrock if (error == 0) { 283292241e0bSTom Erickson error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL, 283392241e0bSTom Erickson nvprops, NULL); 283492241e0bSTom Erickson if (error != 0) 2835842727c2SChris Kirby (void) dmu_objset_destroy(zc->zc_name, B_FALSE); 2836e9dbad6fSeschrock } 2837ecd6cf80Smarks nvlist_free(nvprops); 2838fa9e4066Sahrens return (error); 2839fa9e4066Sahrens } 2840fa9e4066Sahrens 28413cb34c60Sahrens /* 28423cb34c60Sahrens * inputs: 28433cb34c60Sahrens * zc_name name of filesystem 28443cb34c60Sahrens * zc_value short name of snapshot 28453cb34c60Sahrens * zc_cookie recursive flag 284614843421SMatthew Ahrens * zc_nvlist_src[_size] property list 28473cb34c60Sahrens * 2848681d9761SEric Taylor * outputs: 2849681d9761SEric Taylor * zc_value short snapname (i.e. part after the '@') 28503cb34c60Sahrens */ 2851fa9e4066Sahrens static int 28521d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc) 2853fa9e4066Sahrens { 2854bb0ade09Sahrens nvlist_t *nvprops = NULL; 2855bb0ade09Sahrens int error; 2856bb0ade09Sahrens boolean_t recursive = zc->zc_cookie; 2857bb0ade09Sahrens 2858e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 28591d452cf5Sahrens return (EINVAL); 2860bb0ade09Sahrens 2861bb0ade09Sahrens if (zc->zc_nvlist_src != NULL && 2862bb0ade09Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2863478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2864bb0ade09Sahrens return (error); 2865bb0ade09Sahrens 2866ea2f5b9eSMatthew Ahrens error = zfs_check_userprops(zc->zc_name, nvprops); 2867ea2f5b9eSMatthew Ahrens if (error) 2868ea2f5b9eSMatthew Ahrens goto out; 2869bb0ade09Sahrens 287092241e0bSTom Erickson if (!nvlist_empty(nvprops) && 2871ea2f5b9eSMatthew Ahrens zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) { 2872ea2f5b9eSMatthew Ahrens error = ENOTSUP; 2873ea2f5b9eSMatthew Ahrens goto out; 2874bb0ade09Sahrens } 2875ea2f5b9eSMatthew Ahrens 2876ea2f5b9eSMatthew Ahrens error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, 2877ea2f5b9eSMatthew Ahrens nvprops, recursive); 2878ea2f5b9eSMatthew Ahrens 2879ea2f5b9eSMatthew Ahrens out: 2880bb0ade09Sahrens nvlist_free(nvprops); 2881bb0ade09Sahrens return (error); 28821d452cf5Sahrens } 2883fa9e4066Sahrens 2884cdf5b4caSmmusante int 2885fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg) 28861d452cf5Sahrens { 28870b69c2f0Sahrens vfs_t *vfsp = NULL; 28881d452cf5Sahrens 2889745cd3c5Smaybee if (arg) { 2890745cd3c5Smaybee char *snapname = arg; 2891fd136879SMatthew Ahrens char *fullname = kmem_asprintf("%s@%s", name, snapname); 2892fd136879SMatthew Ahrens vfsp = zfs_get_vfs(fullname); 2893fd136879SMatthew Ahrens strfree(fullname); 28940b69c2f0Sahrens } else if (strchr(name, '@')) { 28951d452cf5Sahrens vfsp = zfs_get_vfs(name); 28961d452cf5Sahrens } 28971d452cf5Sahrens 28981d452cf5Sahrens if (vfsp) { 2899fa9e4066Sahrens /* 29001d452cf5Sahrens * Always force the unmount for snapshots. 2901fa9e4066Sahrens */ 29021d452cf5Sahrens int flag = MS_FORCE; 29031d452cf5Sahrens int err; 29041d452cf5Sahrens 29051d452cf5Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 2906fa9e4066Sahrens VFS_RELE(vfsp); 29071d452cf5Sahrens return (err); 2908fa9e4066Sahrens } 29091d452cf5Sahrens VFS_RELE(vfsp); 29101d452cf5Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0) 29111d452cf5Sahrens return (err); 29121d452cf5Sahrens } 29131d452cf5Sahrens return (0); 29141d452cf5Sahrens } 29151d452cf5Sahrens 29163cb34c60Sahrens /* 29173cb34c60Sahrens * inputs: 2918842727c2SChris Kirby * zc_name name of filesystem 2919842727c2SChris Kirby * zc_value short name of snapshot 2920842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 29213cb34c60Sahrens * 29223cb34c60Sahrens * outputs: none 29233cb34c60Sahrens */ 29241d452cf5Sahrens static int 29251d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc) 29261d452cf5Sahrens { 29271d452cf5Sahrens int err; 29281d452cf5Sahrens 2929e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 29301d452cf5Sahrens return (EINVAL); 29311d452cf5Sahrens err = dmu_objset_find(zc->zc_name, 2932e9dbad6fSeschrock zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); 29331d452cf5Sahrens if (err) 29341d452cf5Sahrens return (err); 2935842727c2SChris Kirby return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value, 2936842727c2SChris Kirby zc->zc_defer_destroy)); 29371d452cf5Sahrens } 29381d452cf5Sahrens 29393cb34c60Sahrens /* 29403cb34c60Sahrens * inputs: 29413cb34c60Sahrens * zc_name name of dataset to destroy 29423cb34c60Sahrens * zc_objset_type type of objset 2943842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 29443cb34c60Sahrens * 29453cb34c60Sahrens * outputs: none 29463cb34c60Sahrens */ 29471d452cf5Sahrens static int 29481d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 29491d452cf5Sahrens { 2950681d9761SEric Taylor int err; 29511d452cf5Sahrens if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) { 2952681d9761SEric Taylor err = zfs_unmount_snap(zc->zc_name, NULL); 29531d452cf5Sahrens if (err) 29541d452cf5Sahrens return (err); 2955fa9e4066Sahrens } 2956fa9e4066Sahrens 2957681d9761SEric Taylor err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy); 2958681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 29595c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 2960681d9761SEric Taylor return (err); 2961fa9e4066Sahrens } 2962fa9e4066Sahrens 29633cb34c60Sahrens /* 29643cb34c60Sahrens * inputs: 29654ccbb6e7Sahrens * zc_name name of dataset to rollback (to most recent snapshot) 29663cb34c60Sahrens * 29673cb34c60Sahrens * outputs: none 29683cb34c60Sahrens */ 2969fa9e4066Sahrens static int 2970fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc) 2971fa9e4066Sahrens { 2972ae46e4c7SMatthew Ahrens dsl_dataset_t *ds, *clone; 29734ccbb6e7Sahrens int error; 2974ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 2975ae46e4c7SMatthew Ahrens char *clone_name; 2976ae46e4c7SMatthew Ahrens 2977ae46e4c7SMatthew Ahrens error = dsl_dataset_hold(zc->zc_name, FTAG, &ds); 2978ae46e4c7SMatthew Ahrens if (error) 2979ae46e4c7SMatthew Ahrens return (error); 2980ae46e4c7SMatthew Ahrens 2981ae46e4c7SMatthew Ahrens /* must not be a snapshot */ 2982ae46e4c7SMatthew Ahrens if (dsl_dataset_is_snapshot(ds)) { 2983ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 2984ae46e4c7SMatthew Ahrens return (EINVAL); 2985ae46e4c7SMatthew Ahrens } 2986ae46e4c7SMatthew Ahrens 2987ae46e4c7SMatthew Ahrens /* must have a most recent snapshot */ 2988ae46e4c7SMatthew Ahrens if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) { 2989ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 2990ae46e4c7SMatthew Ahrens return (EINVAL); 2991ae46e4c7SMatthew Ahrens } 29924ccbb6e7Sahrens 29934ccbb6e7Sahrens /* 2994ae46e4c7SMatthew Ahrens * Create clone of most recent snapshot. 29954ccbb6e7Sahrens */ 2996ae46e4c7SMatthew Ahrens clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name); 2997ae46e4c7SMatthew Ahrens error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT); 29984ccbb6e7Sahrens if (error) 2999ae46e4c7SMatthew Ahrens goto out; 30004ccbb6e7Sahrens 3001503ad85cSMatthew Ahrens error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone); 3002ae46e4c7SMatthew Ahrens if (error) 3003ae46e4c7SMatthew Ahrens goto out; 3004ae46e4c7SMatthew Ahrens 3005ae46e4c7SMatthew Ahrens /* 3006ae46e4c7SMatthew Ahrens * Do clone swap. 3007ae46e4c7SMatthew Ahrens */ 300814843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 3009503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 301047f263f4Sek if (error == 0) { 301147f263f4Sek int resume_err; 30124ccbb6e7Sahrens 3013ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 3014ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, 3015ae46e4c7SMatthew Ahrens B_TRUE); 3016ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 3017ae46e4c7SMatthew Ahrens ds = NULL; 3018ae46e4c7SMatthew Ahrens } else { 3019ae46e4c7SMatthew Ahrens error = EBUSY; 3020ae46e4c7SMatthew Ahrens } 3021503ad85cSMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, zc->zc_name); 302247f263f4Sek error = error ? error : resume_err; 302347f263f4Sek } 30244ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 30254ccbb6e7Sahrens } else { 3026ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 3027ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, B_TRUE); 3028ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 3029ae46e4c7SMatthew Ahrens ds = NULL; 3030ae46e4c7SMatthew Ahrens } else { 3031ae46e4c7SMatthew Ahrens error = EBUSY; 3032ae46e4c7SMatthew Ahrens } 30334ccbb6e7Sahrens } 30344ccbb6e7Sahrens 3035ae46e4c7SMatthew Ahrens /* 3036ae46e4c7SMatthew Ahrens * Destroy clone (which also closes it). 3037ae46e4c7SMatthew Ahrens */ 3038ae46e4c7SMatthew Ahrens (void) dsl_dataset_destroy(clone, FTAG, B_FALSE); 3039ae46e4c7SMatthew Ahrens 3040ae46e4c7SMatthew Ahrens out: 3041ae46e4c7SMatthew Ahrens strfree(clone_name); 3042ae46e4c7SMatthew Ahrens if (ds) 3043ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 30444ccbb6e7Sahrens return (error); 3045fa9e4066Sahrens } 3046fa9e4066Sahrens 30473cb34c60Sahrens /* 30483cb34c60Sahrens * inputs: 30493cb34c60Sahrens * zc_name old name of dataset 30503cb34c60Sahrens * zc_value new name of dataset 30513cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 30523cb34c60Sahrens * 30533cb34c60Sahrens * outputs: none 30543cb34c60Sahrens */ 3055fa9e4066Sahrens static int 3056fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3057fa9e4066Sahrens { 30587f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 3059cdf5b4caSmmusante 3060e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3061f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3062f18faf3fSek strchr(zc->zc_value, '%')) 3063fa9e4066Sahrens return (EINVAL); 3064fa9e4066Sahrens 3065cdf5b4caSmmusante /* 3066cdf5b4caSmmusante * Unmount snapshot unless we're doing a recursive rename, 3067cdf5b4caSmmusante * in which case the dataset code figures out which snapshots 3068cdf5b4caSmmusante * to unmount. 3069cdf5b4caSmmusante */ 3070cdf5b4caSmmusante if (!recursive && strchr(zc->zc_name, '@') != NULL && 3071fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 30721d452cf5Sahrens int err = zfs_unmount_snap(zc->zc_name, NULL); 30731d452cf5Sahrens if (err) 30741d452cf5Sahrens return (err); 3075fa9e4066Sahrens } 3076681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL) 3077681d9761SEric Taylor (void) zvol_remove_minor(zc->zc_name); 3078cdf5b4caSmmusante return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); 3079fa9e4066Sahrens } 3080fa9e4066Sahrens 308192241e0bSTom Erickson static int 308292241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 308392241e0bSTom Erickson { 308492241e0bSTom Erickson const char *propname = nvpair_name(pair); 308592241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 308692241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 308792241e0bSTom Erickson uint64_t intval; 308892241e0bSTom Erickson int err; 308992241e0bSTom Erickson 309092241e0bSTom Erickson if (prop == ZPROP_INVAL) { 309192241e0bSTom Erickson if (zfs_prop_user(propname)) { 309292241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 309392241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 309492241e0bSTom Erickson return (err); 309592241e0bSTom Erickson return (0); 309692241e0bSTom Erickson } 309792241e0bSTom Erickson 309892241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 309992241e0bSTom Erickson const char *perm = NULL; 310092241e0bSTom Erickson const char *uq_prefix = 310192241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 310292241e0bSTom Erickson const char *gq_prefix = 310392241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 310492241e0bSTom Erickson 310592241e0bSTom Erickson if (strncmp(propname, uq_prefix, 310692241e0bSTom Erickson strlen(uq_prefix)) == 0) { 310792241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 310892241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 310992241e0bSTom Erickson strlen(gq_prefix)) == 0) { 311092241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 311192241e0bSTom Erickson } else { 311292241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 311392241e0bSTom Erickson return (EINVAL); 311492241e0bSTom Erickson } 311592241e0bSTom Erickson 311692241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 311792241e0bSTom Erickson return (err); 311892241e0bSTom Erickson return (0); 311992241e0bSTom Erickson } 312092241e0bSTom Erickson 312192241e0bSTom Erickson return (EINVAL); 312292241e0bSTom Erickson } 312392241e0bSTom Erickson 312492241e0bSTom Erickson if (issnap) 312592241e0bSTom Erickson return (EINVAL); 312692241e0bSTom Erickson 312792241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 312892241e0bSTom Erickson /* 312992241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 313092241e0bSTom Erickson * format. 313192241e0bSTom Erickson */ 313292241e0bSTom Erickson nvlist_t *attrs; 313392241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 313492241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 313592241e0bSTom Erickson &pair) == 0); 313692241e0bSTom Erickson } 313792241e0bSTom Erickson 313892241e0bSTom Erickson /* 313992241e0bSTom Erickson * Check that this value is valid for this pool version 314092241e0bSTom Erickson */ 314192241e0bSTom Erickson switch (prop) { 314292241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 314392241e0bSTom Erickson /* 314492241e0bSTom Erickson * If the user specified gzip compression, make sure 314592241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 314692241e0bSTom Erickson * we'll catch them later. 314792241e0bSTom Erickson */ 314892241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 314992241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 315092241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 315192241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 315292241e0bSTom Erickson zfs_earlier_version(dsname, 315392241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 315492241e0bSTom Erickson return (ENOTSUP); 315592241e0bSTom Erickson } 315692241e0bSTom Erickson 315792241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 315892241e0bSTom Erickson zfs_earlier_version(dsname, 315992241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 316092241e0bSTom Erickson return (ENOTSUP); 316192241e0bSTom Erickson 316292241e0bSTom Erickson /* 316392241e0bSTom Erickson * If this is a bootable dataset then 316492241e0bSTom Erickson * verify that the compression algorithm 316592241e0bSTom Erickson * is supported for booting. We must return 316692241e0bSTom Erickson * something other than ENOTSUP since it 316792241e0bSTom Erickson * implies a downrev pool version. 316892241e0bSTom Erickson */ 316992241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 317092241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 317192241e0bSTom Erickson return (ERANGE); 317292241e0bSTom Erickson } 317392241e0bSTom Erickson } 317492241e0bSTom Erickson break; 317592241e0bSTom Erickson 317692241e0bSTom Erickson case ZFS_PROP_COPIES: 317792241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 317892241e0bSTom Erickson return (ENOTSUP); 317992241e0bSTom Erickson break; 318092241e0bSTom Erickson 318192241e0bSTom Erickson case ZFS_PROP_DEDUP: 318292241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 318392241e0bSTom Erickson return (ENOTSUP); 318492241e0bSTom Erickson break; 318592241e0bSTom Erickson 318692241e0bSTom Erickson case ZFS_PROP_SHARESMB: 318792241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 318892241e0bSTom Erickson return (ENOTSUP); 318992241e0bSTom Erickson break; 319092241e0bSTom Erickson 319192241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 319292241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 319392241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 319492241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 319592241e0bSTom Erickson zfs_earlier_version(dsname, 319692241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 319792241e0bSTom Erickson return (ENOTSUP); 319892241e0bSTom Erickson } 319992241e0bSTom Erickson break; 320092241e0bSTom Erickson } 320192241e0bSTom Erickson 320292241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 320392241e0bSTom Erickson } 320492241e0bSTom Erickson 320592241e0bSTom Erickson /* 320692241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 320792241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 320892241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 320992241e0bSTom Erickson * 321092241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 321192241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 321292241e0bSTom Erickson * of all the properties that failed a permission check along with the 321392241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 321492241e0bSTom Erickson * returned errlist. 321592241e0bSTom Erickson * 321692241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 321792241e0bSTom Erickson * pointed at by errlist is NULL. 321892241e0bSTom Erickson */ 321992241e0bSTom Erickson static int 322092241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 3221745cd3c5Smaybee { 3222745cd3c5Smaybee zfs_cmd_t *zc; 322392241e0bSTom Erickson nvpair_t *pair, *next_pair; 322492241e0bSTom Erickson nvlist_t *errors; 322592241e0bSTom Erickson int err, rv = 0; 3226745cd3c5Smaybee 3227745cd3c5Smaybee if (props == NULL) 322892241e0bSTom Erickson return (0); 322992241e0bSTom Erickson 323092241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 323192241e0bSTom Erickson 3232745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 3233745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 323492241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 323592241e0bSTom Erickson while (pair != NULL) { 323692241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 323792241e0bSTom Erickson 323892241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 323992241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 324092241e0bSTom Erickson (err = zfs_secpolicy_inherit(zc, CRED())) != 0) { 324192241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 324292241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 324392241e0bSTom Erickson zc->zc_value, err) == 0); 324492241e0bSTom Erickson } 324592241e0bSTom Erickson pair = next_pair; 3246745cd3c5Smaybee } 3247745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 324892241e0bSTom Erickson 324992241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 325092241e0bSTom Erickson nvlist_free(errors); 325192241e0bSTom Erickson errors = NULL; 325292241e0bSTom Erickson } else { 325392241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 325492241e0bSTom Erickson } 325592241e0bSTom Erickson 325692241e0bSTom Erickson if (errlist == NULL) 325792241e0bSTom Erickson nvlist_free(errors); 325892241e0bSTom Erickson else 325992241e0bSTom Erickson *errlist = errors; 326092241e0bSTom Erickson 326192241e0bSTom Erickson return (rv); 326292241e0bSTom Erickson } 326392241e0bSTom Erickson 326492241e0bSTom Erickson static boolean_t 326592241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 326692241e0bSTom Erickson { 326792241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 326892241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 326992241e0bSTom Erickson nvlist_t *attrs; 327092241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 327192241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 327292241e0bSTom Erickson &p1) == 0); 327392241e0bSTom Erickson } 327492241e0bSTom Erickson 327592241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 327692241e0bSTom Erickson nvlist_t *attrs; 327792241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 327892241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 327992241e0bSTom Erickson &p2) == 0); 328092241e0bSTom Erickson } 328192241e0bSTom Erickson 328292241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 328392241e0bSTom Erickson return (B_FALSE); 328492241e0bSTom Erickson 328592241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 328692241e0bSTom Erickson char *valstr1, *valstr2; 328792241e0bSTom Erickson 328892241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 328992241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 329092241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 329192241e0bSTom Erickson } else { 329292241e0bSTom Erickson uint64_t intval1, intval2; 329392241e0bSTom Erickson 329492241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 329592241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 329692241e0bSTom Erickson return (intval1 == intval2); 329792241e0bSTom Erickson } 3298745cd3c5Smaybee } 3299745cd3c5Smaybee 330092241e0bSTom Erickson /* 330192241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 330292241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 330392241e0bSTom Erickson * do not need to clear or restore properties that won't change. 330492241e0bSTom Erickson */ 330592241e0bSTom Erickson static void 330692241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 330792241e0bSTom Erickson { 330892241e0bSTom Erickson nvpair_t *pair, *next_pair; 330992241e0bSTom Erickson 331092241e0bSTom Erickson if (origprops == NULL) 331192241e0bSTom Erickson return; /* all props need to be received */ 331292241e0bSTom Erickson 331392241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 331492241e0bSTom Erickson while (pair != NULL) { 331592241e0bSTom Erickson const char *propname = nvpair_name(pair); 331692241e0bSTom Erickson nvpair_t *match; 331792241e0bSTom Erickson 331892241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 331992241e0bSTom Erickson 332092241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 332192241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 332292241e0bSTom Erickson goto next; /* need to set received value */ 332392241e0bSTom Erickson 332492241e0bSTom Erickson /* don't clear the existing received value */ 332592241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 332692241e0bSTom Erickson /* don't bother receiving the property */ 332792241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 332892241e0bSTom Erickson next: 332992241e0bSTom Erickson pair = next_pair; 333092241e0bSTom Erickson } 333192241e0bSTom Erickson } 333292241e0bSTom Erickson 333392241e0bSTom Erickson #ifdef DEBUG 333492241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 333592241e0bSTom Erickson #endif 333692241e0bSTom Erickson 33373cb34c60Sahrens /* 33383cb34c60Sahrens * inputs: 33393cb34c60Sahrens * zc_name name of containing filesystem 33403cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 33413cb34c60Sahrens * zc_value name of snapshot to create 33423cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 33433cb34c60Sahrens * zc_cookie file descriptor to recv from 33443cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 33453cb34c60Sahrens * zc_guid force flag 3346*c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor 3347*c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping (or zero on first call) 33483cb34c60Sahrens * 33493cb34c60Sahrens * outputs: 33503cb34c60Sahrens * zc_cookie number of bytes read 335192241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 335292241e0bSTom Erickson * zc_obj zprop_errflags_t 3353*c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping 33543cb34c60Sahrens */ 3355fa9e4066Sahrens static int 33563cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 3357fa9e4066Sahrens { 3358fa9e4066Sahrens file_t *fp; 3359f18faf3fSek objset_t *os; 33603cb34c60Sahrens dmu_recv_cookie_t drc; 3361f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 336292241e0bSTom Erickson int fd; 336392241e0bSTom Erickson int error = 0; 336492241e0bSTom Erickson int props_error = 0; 336592241e0bSTom Erickson nvlist_t *errors; 33663cb34c60Sahrens offset_t off; 336792241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 336892241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 33693cb34c60Sahrens objset_t *origin = NULL; 33703cb34c60Sahrens char *tosnap; 33713cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 337292241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 3373fa9e4066Sahrens 33743ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3375f18faf3fSek strchr(zc->zc_value, '@') == NULL || 3376f18faf3fSek strchr(zc->zc_value, '%')) 33773ccfa83cSahrens return (EINVAL); 33783ccfa83cSahrens 33793cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 33803cb34c60Sahrens tosnap = strchr(tofs, '@'); 338192241e0bSTom Erickson *tosnap++ = '\0'; 33823cb34c60Sahrens 33833cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 33843cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 3385478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 33863cb34c60Sahrens return (error); 33873cb34c60Sahrens 3388fa9e4066Sahrens fd = zc->zc_cookie; 3389fa9e4066Sahrens fp = getf(fd); 33903cb34c60Sahrens if (fp == NULL) { 33913cb34c60Sahrens nvlist_free(props); 3392fa9e4066Sahrens return (EBADF); 33933cb34c60Sahrens } 3394f18faf3fSek 339592241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 339692241e0bSTom Erickson 3397503ad85cSMatthew Ahrens if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) { 339892241e0bSTom Erickson if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) && 339992241e0bSTom Erickson !dsl_prop_get_hasrecvd(os)) { 340092241e0bSTom Erickson first_recvd_props = B_TRUE; 340192241e0bSTom Erickson } 340292241e0bSTom Erickson 3403745cd3c5Smaybee /* 340492241e0bSTom Erickson * If new received properties are supplied, they are to 340592241e0bSTom Erickson * completely replace the existing received properties, so stash 340692241e0bSTom Erickson * away the existing ones. 3407745cd3c5Smaybee */ 340892241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 340992241e0bSTom Erickson nvlist_t *errlist = NULL; 341092241e0bSTom Erickson /* 341192241e0bSTom Erickson * Don't bother writing a property if its value won't 341292241e0bSTom Erickson * change (and avoid the unnecessary security checks). 341392241e0bSTom Erickson * 341492241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 341592241e0bSTom Erickson * special case where we blow away all local properties 341692241e0bSTom Erickson * regardless. 341792241e0bSTom Erickson */ 341892241e0bSTom Erickson if (!first_recvd_props) 341992241e0bSTom Erickson props_reduce(props, origprops); 342092241e0bSTom Erickson if (zfs_check_clearable(tofs, origprops, 342192241e0bSTom Erickson &errlist) != 0) 342292241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 342392241e0bSTom Erickson nvlist_free(errlist); 342492241e0bSTom Erickson } 3425745cd3c5Smaybee 3426503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3427f18faf3fSek } 3428f18faf3fSek 34293cb34c60Sahrens if (zc->zc_string[0]) { 3430503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_string, FTAG, &origin); 3431745cd3c5Smaybee if (error) 3432745cd3c5Smaybee goto out; 34333cb34c60Sahrens } 34343cb34c60Sahrens 34359e69d7d0SLori Alt error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds, 34369e69d7d0SLori Alt &zc->zc_begin_record, force, origin, &drc); 34373cb34c60Sahrens if (origin) 3438503ad85cSMatthew Ahrens dmu_objset_rele(origin, FTAG); 3439745cd3c5Smaybee if (error) 3440745cd3c5Smaybee goto out; 3441f18faf3fSek 3442f18faf3fSek /* 344392241e0bSTom Erickson * Set properties before we receive the stream so that they are applied 344492241e0bSTom Erickson * to the new data. Note that we must call dmu_recv_stream() if 344592241e0bSTom Erickson * dmu_recv_begin() succeeds. 3446f18faf3fSek */ 34473cb34c60Sahrens if (props) { 344892241e0bSTom Erickson nvlist_t *errlist; 344992241e0bSTom Erickson 345092241e0bSTom Erickson if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) { 345192241e0bSTom Erickson if (drc.drc_newfs) { 345292241e0bSTom Erickson if (spa_version(os->os_spa) >= 345392241e0bSTom Erickson SPA_VERSION_RECVD_PROPS) 345492241e0bSTom Erickson first_recvd_props = B_TRUE; 345592241e0bSTom Erickson } else if (origprops != NULL) { 345692241e0bSTom Erickson if (clear_received_props(os, tofs, origprops, 345792241e0bSTom Erickson first_recvd_props ? NULL : props) != 0) 345892241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 345992241e0bSTom Erickson } else { 346092241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 346192241e0bSTom Erickson } 346292241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 346392241e0bSTom Erickson } else if (!drc.drc_newfs) { 346492241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 346592241e0bSTom Erickson } 346692241e0bSTom Erickson 346792241e0bSTom Erickson (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 346892241e0bSTom Erickson props, &errlist); 346992241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 347092241e0bSTom Erickson nvlist_free(errlist); 347192241e0bSTom Erickson } 347292241e0bSTom Erickson 347392241e0bSTom Erickson if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) { 3474745cd3c5Smaybee /* 347592241e0bSTom Erickson * Caller made zc->zc_nvlist_dst less than the minimum expected 347692241e0bSTom Erickson * size or supplied an invalid address. 3477745cd3c5Smaybee */ 347892241e0bSTom Erickson props_error = EINVAL; 34793cb34c60Sahrens } 34803cb34c60Sahrens 34813cb34c60Sahrens off = fp->f_offset; 3482*c99e4bdcSChris Kirby error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, 3483*c99e4bdcSChris Kirby &zc->zc_action_handle); 3484a2eea2e1Sahrens 3485f4b94bdeSMatthew Ahrens if (error == 0) { 3486f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 3487745cd3c5Smaybee 3488f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 3489f4b94bdeSMatthew Ahrens /* online recv */ 3490f4b94bdeSMatthew Ahrens int end_err; 3491745cd3c5Smaybee 3492503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3493f4b94bdeSMatthew Ahrens /* 3494f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 3495f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 3496f4b94bdeSMatthew Ahrens */ 3497f4b94bdeSMatthew Ahrens end_err = dmu_recv_end(&drc); 34985c703fceSGeorge Wilson if (error == 0) 34995c703fceSGeorge Wilson error = zfs_resume_fs(zfsvfs, tofs); 3500f4b94bdeSMatthew Ahrens error = error ? error : end_err; 3501f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 3502745cd3c5Smaybee } else { 3503f4b94bdeSMatthew Ahrens error = dmu_recv_end(&drc); 35043cb34c60Sahrens } 350547f263f4Sek } 35063cb34c60Sahrens 35073cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 35083cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 35093cb34c60Sahrens fp->f_offset = off; 3510a2eea2e1Sahrens 351192241e0bSTom Erickson #ifdef DEBUG 351292241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 351392241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 351492241e0bSTom Erickson error = 1; 351592241e0bSTom Erickson } 351692241e0bSTom Erickson #endif 3517745cd3c5Smaybee /* 3518745cd3c5Smaybee * On error, restore the original props. 3519745cd3c5Smaybee */ 3520745cd3c5Smaybee if (error && props) { 352192241e0bSTom Erickson if (dmu_objset_hold(tofs, FTAG, &os) == 0) { 352292241e0bSTom Erickson if (clear_received_props(os, tofs, props, NULL) != 0) { 352392241e0bSTom Erickson /* 352492241e0bSTom Erickson * We failed to clear the received properties. 352592241e0bSTom Erickson * Since we may have left a $recvd value on the 352692241e0bSTom Erickson * system, we can't clear the $hasrecvd flag. 352792241e0bSTom Erickson */ 352892241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 352992241e0bSTom Erickson } else if (first_recvd_props) { 353092241e0bSTom Erickson dsl_prop_unset_hasrecvd(os); 353192241e0bSTom Erickson } 353292241e0bSTom Erickson dmu_objset_rele(os, FTAG); 353392241e0bSTom Erickson } else if (!drc.drc_newfs) { 353492241e0bSTom Erickson /* We failed to clear the received properties. */ 353592241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 353692241e0bSTom Erickson } 353792241e0bSTom Erickson 353892241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 353992241e0bSTom Erickson /* We failed to stash the original properties. */ 354092241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 354192241e0bSTom Erickson } 354292241e0bSTom Erickson 354392241e0bSTom Erickson /* 354492241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 354592241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 354692241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 354792241e0bSTom Erickson * first new-style receive. 354892241e0bSTom Erickson */ 354992241e0bSTom Erickson if (origprops != NULL && 355092241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 355192241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 355292241e0bSTom Erickson origprops, NULL) != 0) { 355392241e0bSTom Erickson /* 355492241e0bSTom Erickson * We stashed the original properties but failed to 355592241e0bSTom Erickson * restore them. 355692241e0bSTom Erickson */ 355792241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 355892241e0bSTom Erickson } 3559745cd3c5Smaybee } 3560745cd3c5Smaybee out: 3561745cd3c5Smaybee nvlist_free(props); 3562745cd3c5Smaybee nvlist_free(origprops); 356392241e0bSTom Erickson nvlist_free(errors); 3564fa9e4066Sahrens releasef(fd); 356592241e0bSTom Erickson 356692241e0bSTom Erickson if (error == 0) 356792241e0bSTom Erickson error = props_error; 356892241e0bSTom Erickson 3569fa9e4066Sahrens return (error); 3570fa9e4066Sahrens } 3571fa9e4066Sahrens 35723cb34c60Sahrens /* 35733cb34c60Sahrens * inputs: 35743cb34c60Sahrens * zc_name name of snapshot to send 35753cb34c60Sahrens * zc_value short name of incremental fromsnap (may be empty) 35763cb34c60Sahrens * zc_cookie file descriptor to send stream to 35773cb34c60Sahrens * zc_obj fromorigin flag (mutually exclusive with zc_value) 35783cb34c60Sahrens * 35793cb34c60Sahrens * outputs: none 35803cb34c60Sahrens */ 3581fa9e4066Sahrens static int 35823cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 3583fa9e4066Sahrens { 3584fa9e4066Sahrens objset_t *fromsnap = NULL; 3585fa9e4066Sahrens objset_t *tosnap; 3586fa9e4066Sahrens file_t *fp; 3587fa9e4066Sahrens int error; 35883cb34c60Sahrens offset_t off; 3589fa9e4066Sahrens 3590503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap); 3591fa9e4066Sahrens if (error) 3592fa9e4066Sahrens return (error); 3593fa9e4066Sahrens 3594e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 35956a0f0066SEric Taylor char *buf; 3596a2eea2e1Sahrens char *cp; 3597a2eea2e1Sahrens 35986a0f0066SEric Taylor buf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 35996a0f0066SEric Taylor (void) strncpy(buf, zc->zc_name, MAXPATHLEN); 3600a2eea2e1Sahrens cp = strchr(buf, '@'); 3601a2eea2e1Sahrens if (cp) 3602a2eea2e1Sahrens *(cp+1) = 0; 36036a0f0066SEric Taylor (void) strncat(buf, zc->zc_value, MAXPATHLEN); 3604503ad85cSMatthew Ahrens error = dmu_objset_hold(buf, FTAG, &fromsnap); 36056a0f0066SEric Taylor kmem_free(buf, MAXPATHLEN); 3606fa9e4066Sahrens if (error) { 3607503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3608fa9e4066Sahrens return (error); 3609fa9e4066Sahrens } 3610fa9e4066Sahrens } 3611fa9e4066Sahrens 3612fa9e4066Sahrens fp = getf(zc->zc_cookie); 3613fa9e4066Sahrens if (fp == NULL) { 3614503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3615fa9e4066Sahrens if (fromsnap) 3616503ad85cSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 3617fa9e4066Sahrens return (EBADF); 3618fa9e4066Sahrens } 3619fa9e4066Sahrens 36203cb34c60Sahrens off = fp->f_offset; 36213cb34c60Sahrens error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off); 3622fa9e4066Sahrens 36233cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 36243cb34c60Sahrens fp->f_offset = off; 3625fa9e4066Sahrens releasef(zc->zc_cookie); 3626fa9e4066Sahrens if (fromsnap) 3627503ad85cSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 3628503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3629fa9e4066Sahrens return (error); 3630fa9e4066Sahrens } 3631fa9e4066Sahrens 3632ea8dc4b6Seschrock static int 3633ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 3634ea8dc4b6Seschrock { 3635ea8dc4b6Seschrock int id, error; 3636ea8dc4b6Seschrock 3637ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 3638ea8dc4b6Seschrock &zc->zc_inject_record); 3639ea8dc4b6Seschrock 3640ea8dc4b6Seschrock if (error == 0) 3641ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 3642ea8dc4b6Seschrock 3643ea8dc4b6Seschrock return (error); 3644ea8dc4b6Seschrock } 3645ea8dc4b6Seschrock 3646ea8dc4b6Seschrock static int 3647ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 3648ea8dc4b6Seschrock { 3649ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 3650ea8dc4b6Seschrock } 3651ea8dc4b6Seschrock 3652ea8dc4b6Seschrock static int 3653ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 3654ea8dc4b6Seschrock { 3655ea8dc4b6Seschrock int id = (int)zc->zc_guid; 3656ea8dc4b6Seschrock int error; 3657ea8dc4b6Seschrock 3658ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 3659ea8dc4b6Seschrock &zc->zc_inject_record); 3660ea8dc4b6Seschrock 3661ea8dc4b6Seschrock zc->zc_guid = id; 3662ea8dc4b6Seschrock 3663ea8dc4b6Seschrock return (error); 3664ea8dc4b6Seschrock } 3665ea8dc4b6Seschrock 3666ea8dc4b6Seschrock static int 3667ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 3668ea8dc4b6Seschrock { 3669ea8dc4b6Seschrock spa_t *spa; 3670ea8dc4b6Seschrock int error; 3671e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 3672ea8dc4b6Seschrock 3673ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 3674ea8dc4b6Seschrock return (error); 3675ea8dc4b6Seschrock 3676e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 3677ea8dc4b6Seschrock &count); 3678ea8dc4b6Seschrock if (error == 0) 3679e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 3680ea8dc4b6Seschrock else 3681e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 3682ea8dc4b6Seschrock 3683ea8dc4b6Seschrock spa_close(spa, FTAG); 3684ea8dc4b6Seschrock 3685ea8dc4b6Seschrock return (error); 3686ea8dc4b6Seschrock } 3687ea8dc4b6Seschrock 3688ea8dc4b6Seschrock static int 3689ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 3690ea8dc4b6Seschrock { 3691ea8dc4b6Seschrock spa_t *spa; 3692ea8dc4b6Seschrock vdev_t *vd; 3693bb8b5132Sek int error; 3694ea8dc4b6Seschrock 3695b87f3af3Sperrin /* 3696b87f3af3Sperrin * On zpool clear we also fix up missing slogs 3697b87f3af3Sperrin */ 3698b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 3699b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 3700b87f3af3Sperrin if (spa == NULL) { 3701b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3702b87f3af3Sperrin return (EIO); 3703b87f3af3Sperrin } 3704b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 3705b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 3706b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 3707b87f3af3Sperrin } 3708468c413aSTim Haley spa->spa_last_open_failed = 0; 3709b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3710b87f3af3Sperrin 3711c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 3712468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 3713468c413aSTim Haley } else { 3714468c413aSTim Haley nvlist_t *policy; 3715468c413aSTim Haley nvlist_t *config = NULL; 3716468c413aSTim Haley 3717468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 3718468c413aSTim Haley return (EINVAL); 3719468c413aSTim Haley 3720468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 3721468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 3722468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 3723468c413aSTim Haley policy, &config); 3724468c413aSTim Haley if (config != NULL) { 3725468c413aSTim Haley (void) put_nvlist(zc, config); 3726468c413aSTim Haley nvlist_free(config); 3727468c413aSTim Haley } 3728468c413aSTim Haley nvlist_free(policy); 3729468c413aSTim Haley } 3730468c413aSTim Haley } 3731468c413aSTim Haley 3732468c413aSTim Haley if (error) 3733ea8dc4b6Seschrock return (error); 3734ea8dc4b6Seschrock 37358f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 3736ea8dc4b6Seschrock 3737e9dbad6fSeschrock if (zc->zc_guid == 0) { 3738ea8dc4b6Seschrock vd = NULL; 3739c5904d13Seschrock } else { 3740c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 3741fa94a07fSbrendan if (vd == NULL) { 3742e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 3743fa94a07fSbrendan spa_close(spa, FTAG); 3744fa94a07fSbrendan return (ENODEV); 3745fa94a07fSbrendan } 3746ea8dc4b6Seschrock } 3747ea8dc4b6Seschrock 3748e14bb325SJeff Bonwick vdev_clear(spa, vd); 3749e14bb325SJeff Bonwick 3750e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 3751ea8dc4b6Seschrock 3752e14bb325SJeff Bonwick /* 3753e14bb325SJeff Bonwick * Resume any suspended I/Os. 3754e14bb325SJeff Bonwick */ 375554d692b7SGeorge Wilson if (zio_resume(spa) != 0) 375654d692b7SGeorge Wilson error = EIO; 3757ea8dc4b6Seschrock 3758ea8dc4b6Seschrock spa_close(spa, FTAG); 3759ea8dc4b6Seschrock 376054d692b7SGeorge Wilson return (error); 3761ea8dc4b6Seschrock } 3762ea8dc4b6Seschrock 37633cb34c60Sahrens /* 37643cb34c60Sahrens * inputs: 37653cb34c60Sahrens * zc_name name of filesystem 37663cb34c60Sahrens * zc_value name of origin snapshot 37673cb34c60Sahrens * 3768681d9761SEric Taylor * outputs: 3769681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 37703cb34c60Sahrens */ 377199653d4eSeschrock static int 377299653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 377399653d4eSeschrock { 37740b69c2f0Sahrens char *cp; 37750b69c2f0Sahrens 37760b69c2f0Sahrens /* 37770b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 37780b69c2f0Sahrens * it's easier. 37790b69c2f0Sahrens */ 3780e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 37810b69c2f0Sahrens if (cp) 37820b69c2f0Sahrens *cp = '\0'; 3783e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 37840b69c2f0Sahrens zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); 3785681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 378699653d4eSeschrock } 378799653d4eSeschrock 378814843421SMatthew Ahrens /* 378914843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 379014843421SMatthew Ahrens * 379114843421SMatthew Ahrens * inputs: 379214843421SMatthew Ahrens * zc_name name of filesystem 379314843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 379414843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 379514843421SMatthew Ahrens * zc_guid RID/UID/GID 379614843421SMatthew Ahrens * 379714843421SMatthew Ahrens * outputs: 379814843421SMatthew Ahrens * zc_cookie property value 379914843421SMatthew Ahrens */ 380014843421SMatthew Ahrens static int 380114843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 380214843421SMatthew Ahrens { 380314843421SMatthew Ahrens zfsvfs_t *zfsvfs; 380414843421SMatthew Ahrens int error; 380514843421SMatthew Ahrens 380614843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 380714843421SMatthew Ahrens return (EINVAL); 380814843421SMatthew Ahrens 3809503ad85cSMatthew Ahrens error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs); 381014843421SMatthew Ahrens if (error) 381114843421SMatthew Ahrens return (error); 381214843421SMatthew Ahrens 381314843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 381414843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 381514843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 381614843421SMatthew Ahrens 381714843421SMatthew Ahrens return (error); 381814843421SMatthew Ahrens } 381914843421SMatthew Ahrens 382014843421SMatthew Ahrens /* 382114843421SMatthew Ahrens * inputs: 382214843421SMatthew Ahrens * zc_name name of filesystem 382314843421SMatthew Ahrens * zc_cookie zap cursor 382414843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 382514843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 382614843421SMatthew Ahrens * 382714843421SMatthew Ahrens * outputs: 382814843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 382914843421SMatthew Ahrens * zc_cookie zap cursor 383014843421SMatthew Ahrens */ 383114843421SMatthew Ahrens static int 383214843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 383314843421SMatthew Ahrens { 383414843421SMatthew Ahrens zfsvfs_t *zfsvfs; 3835eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 383614843421SMatthew Ahrens 3837eeb85002STim Haley if (bufsize <= 0) 3838eeb85002STim Haley return (ENOMEM); 3839eeb85002STim Haley 3840eeb85002STim Haley int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs); 384114843421SMatthew Ahrens if (error) 384214843421SMatthew Ahrens return (error); 384314843421SMatthew Ahrens 384414843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 384514843421SMatthew Ahrens 384614843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 384714843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 384814843421SMatthew Ahrens 384914843421SMatthew Ahrens if (error == 0) { 385014843421SMatthew Ahrens error = xcopyout(buf, 385114843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 385214843421SMatthew Ahrens zc->zc_nvlist_dst_size); 385314843421SMatthew Ahrens } 385414843421SMatthew Ahrens kmem_free(buf, bufsize); 385514843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 385614843421SMatthew Ahrens 385714843421SMatthew Ahrens return (error); 385814843421SMatthew Ahrens } 385914843421SMatthew Ahrens 386014843421SMatthew Ahrens /* 386114843421SMatthew Ahrens * inputs: 386214843421SMatthew Ahrens * zc_name name of filesystem 386314843421SMatthew Ahrens * 386414843421SMatthew Ahrens * outputs: 386514843421SMatthew Ahrens * none 386614843421SMatthew Ahrens */ 386714843421SMatthew Ahrens static int 386814843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 386914843421SMatthew Ahrens { 387014843421SMatthew Ahrens objset_t *os; 38711195e687SMark J Musante int error = 0; 387214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 387314843421SMatthew Ahrens 387414843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 3875503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 387614843421SMatthew Ahrens /* 387714843421SMatthew Ahrens * If userused is not enabled, it may be because the 387814843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 387914843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 388014843421SMatthew Ahrens */ 3881503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3882503ad85cSMatthew Ahrens if (error == 0) 3883503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 388414843421SMatthew Ahrens } 388514843421SMatthew Ahrens if (error == 0) 388614843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 388714843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 388814843421SMatthew Ahrens } else { 3889503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 3890503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 389114843421SMatthew Ahrens if (error) 389214843421SMatthew Ahrens return (error); 389314843421SMatthew Ahrens 389414843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 3895503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 389614843421SMatthew Ahrens } 389714843421SMatthew Ahrens 389814843421SMatthew Ahrens return (error); 389914843421SMatthew Ahrens } 390014843421SMatthew Ahrens 3901ecd6cf80Smarks /* 3902ecd6cf80Smarks * We don't want to have a hard dependency 3903ecd6cf80Smarks * against some special symbols in sharefs 3904da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 3905ecd6cf80Smarks * the first file system is shared. 3906da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 3907ecd6cf80Smarks */ 3908da6c28aaSamw int (*znfsexport_fs)(void *arg); 3909ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 3910da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 3911da6c28aaSamw 3912da6c28aaSamw int zfs_nfsshare_inited; 3913da6c28aaSamw int zfs_smbshare_inited; 3914ecd6cf80Smarks 3915ecd6cf80Smarks ddi_modhandle_t nfs_mod; 3916ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 3917da6c28aaSamw ddi_modhandle_t smbsrv_mod; 3918ecd6cf80Smarks kmutex_t zfs_share_lock; 3919ecd6cf80Smarks 3920da6c28aaSamw static int 3921da6c28aaSamw zfs_init_sharefs() 3922da6c28aaSamw { 3923da6c28aaSamw int error; 3924da6c28aaSamw 3925da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 3926da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 3927da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 3928da6c28aaSamw ddi_modopen("fs/sharefs", 3929da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3930da6c28aaSamw return (ENOSYS); 3931da6c28aaSamw } 3932da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 3933da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 3934da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 3935da6c28aaSamw return (ENOSYS); 3936da6c28aaSamw } 3937da6c28aaSamw return (0); 3938da6c28aaSamw } 3939da6c28aaSamw 3940ecd6cf80Smarks static int 3941ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 3942ecd6cf80Smarks { 3943ecd6cf80Smarks int error; 3944ecd6cf80Smarks int opcode; 3945ecd6cf80Smarks 3946da6c28aaSamw switch (zc->zc_share.z_sharetype) { 3947da6c28aaSamw case ZFS_SHARE_NFS: 3948da6c28aaSamw case ZFS_UNSHARE_NFS: 3949da6c28aaSamw if (zfs_nfsshare_inited == 0) { 3950da6c28aaSamw mutex_enter(&zfs_share_lock); 3951da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 3952da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3953da6c28aaSamw mutex_exit(&zfs_share_lock); 3954da6c28aaSamw return (ENOSYS); 3955da6c28aaSamw } 3956da6c28aaSamw if (znfsexport_fs == NULL && 3957da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 3958da6c28aaSamw ddi_modsym(nfs_mod, 3959da6c28aaSamw "nfs_export", &error)) == NULL)) { 3960da6c28aaSamw mutex_exit(&zfs_share_lock); 3961da6c28aaSamw return (ENOSYS); 3962da6c28aaSamw } 3963da6c28aaSamw error = zfs_init_sharefs(); 3964da6c28aaSamw if (error) { 3965da6c28aaSamw mutex_exit(&zfs_share_lock); 3966da6c28aaSamw return (ENOSYS); 3967da6c28aaSamw } 3968da6c28aaSamw zfs_nfsshare_inited = 1; 3969ecd6cf80Smarks mutex_exit(&zfs_share_lock); 3970ecd6cf80Smarks } 3971da6c28aaSamw break; 3972da6c28aaSamw case ZFS_SHARE_SMB: 3973da6c28aaSamw case ZFS_UNSHARE_SMB: 3974da6c28aaSamw if (zfs_smbshare_inited == 0) { 3975da6c28aaSamw mutex_enter(&zfs_share_lock); 3976da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 3977da6c28aaSamw ddi_modopen("drv/smbsrv", 3978da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3979da6c28aaSamw mutex_exit(&zfs_share_lock); 3980da6c28aaSamw return (ENOSYS); 3981da6c28aaSamw } 3982da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 3983da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 3984faa1795aSjb "smb_server_share", &error)) == NULL)) { 3985da6c28aaSamw mutex_exit(&zfs_share_lock); 3986da6c28aaSamw return (ENOSYS); 3987da6c28aaSamw } 3988da6c28aaSamw error = zfs_init_sharefs(); 3989da6c28aaSamw if (error) { 3990da6c28aaSamw mutex_exit(&zfs_share_lock); 3991da6c28aaSamw return (ENOSYS); 3992da6c28aaSamw } 3993da6c28aaSamw zfs_smbshare_inited = 1; 3994ecd6cf80Smarks mutex_exit(&zfs_share_lock); 3995ecd6cf80Smarks } 3996da6c28aaSamw break; 3997da6c28aaSamw default: 3998da6c28aaSamw return (EINVAL); 3999da6c28aaSamw } 4000ecd6cf80Smarks 4001da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4002da6c28aaSamw case ZFS_SHARE_NFS: 4003da6c28aaSamw case ZFS_UNSHARE_NFS: 4004da6c28aaSamw if (error = 4005da6c28aaSamw znfsexport_fs((void *) 4006da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 4007da6c28aaSamw return (error); 4008da6c28aaSamw break; 4009da6c28aaSamw case ZFS_SHARE_SMB: 4010da6c28aaSamw case ZFS_UNSHARE_SMB: 4011da6c28aaSamw if (error = zsmbexport_fs((void *) 4012da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 4013da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 4014743a77edSAlan Wright B_TRUE: B_FALSE)) { 4015da6c28aaSamw return (error); 4016ecd6cf80Smarks } 4017da6c28aaSamw break; 4018ecd6cf80Smarks } 4019ecd6cf80Smarks 4020da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 4021da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 4022ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 4023ecd6cf80Smarks 4024da6c28aaSamw /* 4025da6c28aaSamw * Add or remove share from sharetab 4026da6c28aaSamw */ 4027ecd6cf80Smarks error = zshare_fs(opcode, 4028ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 4029ecd6cf80Smarks zc->zc_share.z_sharemax); 4030ecd6cf80Smarks 4031ecd6cf80Smarks return (error); 4032ecd6cf80Smarks 4033ecd6cf80Smarks } 4034ecd6cf80Smarks 4035743a77edSAlan Wright ace_t full_access[] = { 4036743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 4037743a77edSAlan Wright }; 4038743a77edSAlan Wright 4039743a77edSAlan Wright /* 4040743a77edSAlan Wright * Remove all ACL files in shares dir 4041743a77edSAlan Wright */ 4042743a77edSAlan Wright static int 4043743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 4044743a77edSAlan Wright { 4045743a77edSAlan Wright zap_cursor_t zc; 4046743a77edSAlan Wright zap_attribute_t zap; 4047743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 4048743a77edSAlan Wright int error; 4049743a77edSAlan Wright 4050743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 4051743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 4052743a77edSAlan Wright zap_cursor_advance(&zc)) { 4053743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 4054743a77edSAlan Wright NULL, 0)) != 0) 4055743a77edSAlan Wright break; 4056743a77edSAlan Wright } 4057743a77edSAlan Wright zap_cursor_fini(&zc); 4058743a77edSAlan Wright return (error); 4059743a77edSAlan Wright } 4060743a77edSAlan Wright 4061743a77edSAlan Wright static int 4062743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 4063743a77edSAlan Wright { 4064743a77edSAlan Wright vnode_t *vp; 4065743a77edSAlan Wright znode_t *dzp; 4066743a77edSAlan Wright vnode_t *resourcevp = NULL; 4067743a77edSAlan Wright znode_t *sharedir; 4068743a77edSAlan Wright zfsvfs_t *zfsvfs; 4069743a77edSAlan Wright nvlist_t *nvlist; 4070743a77edSAlan Wright char *src, *target; 4071743a77edSAlan Wright vattr_t vattr; 4072743a77edSAlan Wright vsecattr_t vsec; 4073743a77edSAlan Wright int error = 0; 4074743a77edSAlan Wright 4075743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 4076743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 4077743a77edSAlan Wright return (error); 4078743a77edSAlan Wright 4079743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 4080743a77edSAlan Wright 4081743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 4082743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 4083743a77edSAlan Wright zc->zc_name) != 0)) { 4084743a77edSAlan Wright VN_RELE(vp); 4085743a77edSAlan Wright return (EINVAL); 4086743a77edSAlan Wright } 4087743a77edSAlan Wright 4088743a77edSAlan Wright dzp = VTOZ(vp); 4089743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 4090743a77edSAlan Wright ZFS_ENTER(zfsvfs); 4091743a77edSAlan Wright 40929e1320c0SMark Shellenbaum /* 40939e1320c0SMark Shellenbaum * Create share dir if its missing. 40949e1320c0SMark Shellenbaum */ 40959e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 40969e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 40979e1320c0SMark Shellenbaum dmu_tx_t *tx; 40989e1320c0SMark Shellenbaum 40999e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 41009e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 41019e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 41029e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 41039e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 41049e1320c0SMark Shellenbaum if (error) { 41059e1320c0SMark Shellenbaum dmu_tx_abort(tx); 41069e1320c0SMark Shellenbaum } else { 41079e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 41089e1320c0SMark Shellenbaum dmu_tx_commit(tx); 41099e1320c0SMark Shellenbaum } 41109e1320c0SMark Shellenbaum if (error) { 41119e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 41129e1320c0SMark Shellenbaum VN_RELE(vp); 41139e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 41149e1320c0SMark Shellenbaum return (error); 41159e1320c0SMark Shellenbaum } 41169e1320c0SMark Shellenbaum } 41179e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 41189e1320c0SMark Shellenbaum 41199e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 4120743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 41219e1320c0SMark Shellenbaum VN_RELE(vp); 4122743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4123743a77edSAlan Wright return (error); 4124743a77edSAlan Wright } 4125743a77edSAlan Wright 4126743a77edSAlan Wright switch (zc->zc_cookie) { 4127743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 4128743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 4129743a77edSAlan Wright vattr.va_type = VREG; 4130743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 4131743a77edSAlan Wright vattr.va_uid = 0; 4132743a77edSAlan Wright vattr.va_gid = 0; 4133743a77edSAlan Wright 4134743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 4135743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 4136743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 4137743a77edSAlan Wright vsec.vsa_aclcnt = 1; 4138743a77edSAlan Wright 4139743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 4140743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 4141743a77edSAlan Wright if (resourcevp) 4142743a77edSAlan Wright VN_RELE(resourcevp); 4143743a77edSAlan Wright break; 4144743a77edSAlan Wright 4145743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 4146743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 4147743a77edSAlan Wright NULL, 0); 4148743a77edSAlan Wright break; 4149743a77edSAlan Wright 4150743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 4151743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 4152478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 4153743a77edSAlan Wright VN_RELE(vp); 4154743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4155743a77edSAlan Wright return (error); 4156743a77edSAlan Wright } 4157743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 4158743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 4159743a77edSAlan Wright &target)) { 4160743a77edSAlan Wright VN_RELE(vp); 416189459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 4162743a77edSAlan Wright ZFS_EXIT(zfsvfs); 41631195e687SMark J Musante nvlist_free(nvlist); 4164743a77edSAlan Wright return (error); 4165743a77edSAlan Wright } 4166743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 4167743a77edSAlan Wright kcred, NULL, 0); 4168743a77edSAlan Wright nvlist_free(nvlist); 4169743a77edSAlan Wright break; 4170743a77edSAlan Wright 4171743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 4172743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 4173743a77edSAlan Wright break; 4174743a77edSAlan Wright 4175743a77edSAlan Wright default: 4176743a77edSAlan Wright error = EINVAL; 4177743a77edSAlan Wright break; 4178743a77edSAlan Wright } 4179743a77edSAlan Wright 4180743a77edSAlan Wright VN_RELE(vp); 4181743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 4182743a77edSAlan Wright 4183743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4184743a77edSAlan Wright 4185743a77edSAlan Wright return (error); 4186743a77edSAlan Wright } 4187743a77edSAlan Wright 4188842727c2SChris Kirby /* 4189842727c2SChris Kirby * inputs: 4190*c99e4bdcSChris Kirby * zc_name name of filesystem 4191*c99e4bdcSChris Kirby * zc_value short name of snap 4192*c99e4bdcSChris Kirby * zc_string user-supplied tag for this hold 4193*c99e4bdcSChris Kirby * zc_cookie recursive flag 4194*c99e4bdcSChris Kirby * zc_temphold set if hold is temporary 4195*c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 4196842727c2SChris Kirby * 4197842727c2SChris Kirby * outputs: none 4198842727c2SChris Kirby */ 4199842727c2SChris Kirby static int 4200842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc) 4201842727c2SChris Kirby { 4202842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4203842727c2SChris Kirby 4204842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4205842727c2SChris Kirby return (EINVAL); 4206842727c2SChris Kirby 4207842727c2SChris Kirby return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value, 4208*c99e4bdcSChris Kirby zc->zc_string, recursive, zc->zc_temphold, zc->zc_cleanup_fd)); 4209842727c2SChris Kirby } 4210842727c2SChris Kirby 4211842727c2SChris Kirby /* 4212842727c2SChris Kirby * inputs: 4213*c99e4bdcSChris Kirby * zc_name name of dataset from which we're releasing a user hold 4214842727c2SChris Kirby * zc_value short name of snap 4215*c99e4bdcSChris Kirby * zc_string user-supplied tag for this hold 4216842727c2SChris Kirby * zc_cookie recursive flag 4217842727c2SChris Kirby * 4218*c99e4bdcSChris Kirby * outputs: none 4219842727c2SChris Kirby */ 4220842727c2SChris Kirby static int 4221842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc) 4222842727c2SChris Kirby { 4223842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4224842727c2SChris Kirby 4225842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4226842727c2SChris Kirby return (EINVAL); 4227842727c2SChris Kirby 4228842727c2SChris Kirby return (dsl_dataset_user_release(zc->zc_name, zc->zc_value, 4229842727c2SChris Kirby zc->zc_string, recursive)); 4230842727c2SChris Kirby } 4231842727c2SChris Kirby 4232842727c2SChris Kirby /* 4233842727c2SChris Kirby * inputs: 4234842727c2SChris Kirby * zc_name name of filesystem 4235842727c2SChris Kirby * 4236842727c2SChris Kirby * outputs: 4237842727c2SChris Kirby * zc_nvlist_src{_size} nvlist of snapshot holds 4238842727c2SChris Kirby */ 4239842727c2SChris Kirby static int 4240842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc) 4241842727c2SChris Kirby { 4242842727c2SChris Kirby nvlist_t *nvp; 4243842727c2SChris Kirby int error; 4244842727c2SChris Kirby 4245842727c2SChris Kirby if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) { 4246842727c2SChris Kirby error = put_nvlist(zc, nvp); 4247842727c2SChris Kirby nvlist_free(nvp); 4248842727c2SChris Kirby } 4249842727c2SChris Kirby 4250842727c2SChris Kirby return (error); 4251842727c2SChris Kirby } 4252842727c2SChris Kirby 4253ecd6cf80Smarks /* 42542a6b87f0Sek * pool create, destroy, and export don't log the history as part of 42552a6b87f0Sek * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export 42562a6b87f0Sek * do the logging of those commands. 4257ecd6cf80Smarks */ 4258fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = { 425954d692b7SGeorge Wilson { zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE, 426054d692b7SGeorge Wilson B_FALSE }, 426154d692b7SGeorge Wilson { zfs_ioc_pool_destroy, zfs_secpolicy_config, POOL_NAME, B_FALSE, 426254d692b7SGeorge Wilson B_FALSE }, 426354d692b7SGeorge Wilson { zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE, 426454d692b7SGeorge Wilson B_FALSE }, 426554d692b7SGeorge Wilson { zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE, 426654d692b7SGeorge Wilson B_FALSE }, 426754d692b7SGeorge Wilson { zfs_ioc_pool_configs, zfs_secpolicy_none, NO_NAME, B_FALSE, 426854d692b7SGeorge Wilson B_FALSE }, 426954d692b7SGeorge Wilson { zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE, 427054d692b7SGeorge Wilson B_FALSE }, 427154d692b7SGeorge Wilson { zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE, 427254d692b7SGeorge Wilson B_FALSE }, 42733f9d6ad7SLin Ling { zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE, 427454d692b7SGeorge Wilson B_TRUE }, 427554d692b7SGeorge Wilson { zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE, 427654d692b7SGeorge Wilson B_FALSE }, 427754d692b7SGeorge Wilson { zfs_ioc_pool_upgrade, zfs_secpolicy_config, POOL_NAME, B_TRUE, 427854d692b7SGeorge Wilson B_TRUE }, 427954d692b7SGeorge Wilson { zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE, 428054d692b7SGeorge Wilson B_FALSE }, 428154d692b7SGeorge Wilson { zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE, 428254d692b7SGeorge Wilson B_TRUE }, 428354d692b7SGeorge Wilson { zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE, 428454d692b7SGeorge Wilson B_TRUE }, 428554d692b7SGeorge Wilson { zfs_ioc_vdev_set_state, zfs_secpolicy_config, POOL_NAME, B_TRUE, 428654d692b7SGeorge Wilson B_FALSE }, 428754d692b7SGeorge Wilson { zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 428854d692b7SGeorge Wilson B_TRUE }, 428954d692b7SGeorge Wilson { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 429054d692b7SGeorge Wilson B_TRUE }, 429154d692b7SGeorge Wilson { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE, 429254d692b7SGeorge Wilson B_TRUE }, 42936809eb4eSEric Schrock { zfs_ioc_vdev_setfru, zfs_secpolicy_config, POOL_NAME, B_FALSE, 42946809eb4eSEric Schrock B_TRUE }, 429554d692b7SGeorge Wilson { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4296cf2859fcSSanjeev Bagewadi B_TRUE }, 429754d692b7SGeorge Wilson { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 429854d692b7SGeorge Wilson B_FALSE }, 429954d692b7SGeorge Wilson { zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4300cf2859fcSSanjeev Bagewadi B_TRUE }, 430154d692b7SGeorge Wilson { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 4302cf2859fcSSanjeev Bagewadi B_TRUE }, 430354d692b7SGeorge Wilson { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE }, 430454d692b7SGeorge Wilson { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE }, 430554d692b7SGeorge Wilson { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, 430654d692b7SGeorge Wilson B_TRUE}, 430754d692b7SGeorge Wilson { zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE, 430854d692b7SGeorge Wilson B_TRUE }, 430954d692b7SGeorge Wilson { zfs_ioc_rename, zfs_secpolicy_rename, DATASET_NAME, B_TRUE, B_TRUE }, 431054d692b7SGeorge Wilson { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE }, 431154d692b7SGeorge Wilson { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE }, 431254d692b7SGeorge Wilson { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 431354d692b7SGeorge Wilson B_FALSE }, 431454d692b7SGeorge Wilson { zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 431554d692b7SGeorge Wilson B_FALSE }, 431654d692b7SGeorge Wilson { zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE, 431754d692b7SGeorge Wilson B_FALSE }, 431854d692b7SGeorge Wilson { zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE, 431954d692b7SGeorge Wilson B_FALSE }, 432054d692b7SGeorge Wilson { zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE }, 432154d692b7SGeorge Wilson { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE, 432254d692b7SGeorge Wilson B_TRUE }, 4323cbf6f6aaSWilliam Gorrell { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME, 4324cbf6f6aaSWilliam Gorrell B_TRUE, B_TRUE }, 432554d692b7SGeorge Wilson { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE, 432654d692b7SGeorge Wilson B_TRUE }, 432754d692b7SGeorge Wilson { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE, 432854d692b7SGeorge Wilson B_FALSE }, 43296e8a0f56SGeorge Wilson { zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE, 43306e8a0f56SGeorge Wilson B_TRUE }, 433154d692b7SGeorge Wilson { zfs_ioc_pool_set_props, zfs_secpolicy_config, POOL_NAME, B_TRUE, 433254d692b7SGeorge Wilson B_TRUE }, 433354d692b7SGeorge Wilson { zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE, 433454d692b7SGeorge Wilson B_FALSE }, 433554d692b7SGeorge Wilson { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE, 433654d692b7SGeorge Wilson B_TRUE }, 433754d692b7SGeorge Wilson { zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 433854d692b7SGeorge Wilson B_FALSE }, 433954d692b7SGeorge Wilson { zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE }, 434054d692b7SGeorge Wilson { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE, 434154d692b7SGeorge Wilson B_TRUE }, 434254d692b7SGeorge Wilson { zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE, 434314843421SMatthew Ahrens B_FALSE }, 434414843421SMatthew Ahrens { zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, 434514843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_FALSE }, 434614843421SMatthew Ahrens { zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, 434714843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_FALSE }, 434814843421SMatthew Ahrens { zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 434914843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_TRUE }, 4350842727c2SChris Kirby { zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE }, 4351842727c2SChris Kirby { zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE, 4352842727c2SChris Kirby B_TRUE }, 4353842727c2SChris Kirby { zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 435492241e0bSTom Erickson B_TRUE }, 435592241e0bSTom Erickson { zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 43561195e687SMark J Musante B_FALSE }, 43571195e687SMark J Musante { zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE, 43581195e687SMark J Musante B_TRUE } 4359fa9e4066Sahrens }; 4360fa9e4066Sahrens 436154d692b7SGeorge Wilson int 436254d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type) 436354d692b7SGeorge Wilson { 436454d692b7SGeorge Wilson spa_t *spa; 436554d692b7SGeorge Wilson int error; 436654d692b7SGeorge Wilson 436754d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 436854d692b7SGeorge Wilson 436914843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 437054d692b7SGeorge Wilson if (error == 0) { 437154d692b7SGeorge Wilson if (spa_suspended(spa)) 437254d692b7SGeorge Wilson error = EAGAIN; 437354d692b7SGeorge Wilson spa_close(spa, FTAG); 437454d692b7SGeorge Wilson } 437554d692b7SGeorge Wilson return (error); 437654d692b7SGeorge Wilson } 437754d692b7SGeorge Wilson 4378*c99e4bdcSChris Kirby /* 4379*c99e4bdcSChris Kirby * Find a free minor number. 4380*c99e4bdcSChris Kirby */ 4381*c99e4bdcSChris Kirby minor_t 4382*c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 4383*c99e4bdcSChris Kirby { 4384*c99e4bdcSChris Kirby static minor_t last_minor; 4385*c99e4bdcSChris Kirby minor_t m; 4386*c99e4bdcSChris Kirby 4387*c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 4388*c99e4bdcSChris Kirby 4389*c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 4390*c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 4391*c99e4bdcSChris Kirby m = 1; 4392*c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 4393*c99e4bdcSChris Kirby last_minor = m; 4394*c99e4bdcSChris Kirby return (m); 4395*c99e4bdcSChris Kirby } 4396*c99e4bdcSChris Kirby } 4397*c99e4bdcSChris Kirby 4398*c99e4bdcSChris Kirby return (0); 4399*c99e4bdcSChris Kirby } 4400*c99e4bdcSChris Kirby 4401*c99e4bdcSChris Kirby static int 4402*c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 4403*c99e4bdcSChris Kirby { 4404*c99e4bdcSChris Kirby minor_t minor; 4405*c99e4bdcSChris Kirby zfs_soft_state_t *zs; 4406*c99e4bdcSChris Kirby 4407*c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 4408*c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 4409*c99e4bdcSChris Kirby 4410*c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 4411*c99e4bdcSChris Kirby if (minor == 0) 4412*c99e4bdcSChris Kirby return (ENXIO); 4413*c99e4bdcSChris Kirby 4414*c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 4415*c99e4bdcSChris Kirby return (EAGAIN); 4416*c99e4bdcSChris Kirby 4417*c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 4418*c99e4bdcSChris Kirby 4419*c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 4420*c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 4421*c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 4422*c99e4bdcSChris Kirby 4423*c99e4bdcSChris Kirby return (0); 4424*c99e4bdcSChris Kirby } 4425*c99e4bdcSChris Kirby 4426*c99e4bdcSChris Kirby static void 4427*c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 4428*c99e4bdcSChris Kirby { 4429*c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 4430*c99e4bdcSChris Kirby 4431*c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 4432*c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 4433*c99e4bdcSChris Kirby } 4434*c99e4bdcSChris Kirby 4435*c99e4bdcSChris Kirby void * 4436*c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 4437*c99e4bdcSChris Kirby { 4438*c99e4bdcSChris Kirby zfs_soft_state_t *zp; 4439*c99e4bdcSChris Kirby 4440*c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 4441*c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 4442*c99e4bdcSChris Kirby return (NULL); 4443*c99e4bdcSChris Kirby 4444*c99e4bdcSChris Kirby return (zp->zss_data); 4445*c99e4bdcSChris Kirby } 4446*c99e4bdcSChris Kirby 4447*c99e4bdcSChris Kirby static int 4448*c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 4449*c99e4bdcSChris Kirby { 4450*c99e4bdcSChris Kirby int error = 0; 4451*c99e4bdcSChris Kirby 4452*c99e4bdcSChris Kirby if (getminor(*devp) != 0) 4453*c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 4454*c99e4bdcSChris Kirby 4455*c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 4456*c99e4bdcSChris Kirby if (flag & FEXCL) { 4457*c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 4458*c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 4459*c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 4460*c99e4bdcSChris Kirby } 4461*c99e4bdcSChris Kirby 4462*c99e4bdcSChris Kirby return (error); 4463*c99e4bdcSChris Kirby } 4464*c99e4bdcSChris Kirby 4465*c99e4bdcSChris Kirby static int 4466*c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 4467*c99e4bdcSChris Kirby { 4468*c99e4bdcSChris Kirby zfs_onexit_t *zo; 4469*c99e4bdcSChris Kirby minor_t minor = getminor(dev); 4470*c99e4bdcSChris Kirby 4471*c99e4bdcSChris Kirby if (minor == 0) 4472*c99e4bdcSChris Kirby return (0); 4473*c99e4bdcSChris Kirby 4474*c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 4475*c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 4476*c99e4bdcSChris Kirby if (zo == NULL) { 4477*c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 4478*c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 4479*c99e4bdcSChris Kirby } 4480*c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 4481*c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 4482*c99e4bdcSChris Kirby 4483*c99e4bdcSChris Kirby return (0); 4484*c99e4bdcSChris Kirby } 4485*c99e4bdcSChris Kirby 4486fa9e4066Sahrens static int 4487fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 4488fa9e4066Sahrens { 4489fa9e4066Sahrens zfs_cmd_t *zc; 4490fa9e4066Sahrens uint_t vec; 44911d452cf5Sahrens int error, rc; 4492*c99e4bdcSChris Kirby minor_t minor = getminor(dev); 4493fa9e4066Sahrens 4494*c99e4bdcSChris Kirby if (minor != 0 && 4495*c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 4496fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 4497fa9e4066Sahrens 4498fa9e4066Sahrens vec = cmd - ZFS_IOC; 449991ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 4500fa9e4066Sahrens 4501fa9e4066Sahrens if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 4502fa9e4066Sahrens return (EINVAL); 4503fa9e4066Sahrens 4504fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 4505fa9e4066Sahrens 4506478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 45076e27f868SSam Falkner if (error != 0) 45086e27f868SSam Falkner error = EFAULT; 4509fa9e4066Sahrens 4510681d9761SEric Taylor if ((error == 0) && !(flag & FKIOCTL)) 4511ecd6cf80Smarks error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr); 4512fa9e4066Sahrens 4513fa9e4066Sahrens /* 4514fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 4515fa9e4066Sahrens * the lower layers. 4516fa9e4066Sahrens */ 4517fa9e4066Sahrens if (error == 0) { 4518fa9e4066Sahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 4519478ed9adSEric Taylor zc->zc_iflags = flag & FKIOCTL; 4520fa9e4066Sahrens switch (zfs_ioc_vec[vec].zvec_namecheck) { 4521e7437265Sahrens case POOL_NAME: 4522fa9e4066Sahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 4523fa9e4066Sahrens error = EINVAL; 452454d692b7SGeorge Wilson if (zfs_ioc_vec[vec].zvec_pool_check) 452554d692b7SGeorge Wilson error = pool_status_check(zc->zc_name, 452654d692b7SGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck); 4527fa9e4066Sahrens break; 4528fa9e4066Sahrens 4529e7437265Sahrens case DATASET_NAME: 4530fa9e4066Sahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 4531fa9e4066Sahrens error = EINVAL; 453254d692b7SGeorge Wilson if (zfs_ioc_vec[vec].zvec_pool_check) 453354d692b7SGeorge Wilson error = pool_status_check(zc->zc_name, 453454d692b7SGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck); 4535fa9e4066Sahrens break; 45365ad82045Snd 4537e7437265Sahrens case NO_NAME: 45385ad82045Snd break; 4539fa9e4066Sahrens } 4540fa9e4066Sahrens } 4541fa9e4066Sahrens 4542fa9e4066Sahrens if (error == 0) 4543fa9e4066Sahrens error = zfs_ioc_vec[vec].zvec_func(zc); 4544fa9e4066Sahrens 4545478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 4546ecd6cf80Smarks if (error == 0) { 45476e27f868SSam Falkner if (rc != 0) 45486e27f868SSam Falkner error = EFAULT; 454914843421SMatthew Ahrens if (zfs_ioc_vec[vec].zvec_his_log) 4550ecd6cf80Smarks zfs_log_history(zc); 4551ecd6cf80Smarks } 4552fa9e4066Sahrens 4553fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 4554fa9e4066Sahrens return (error); 4555fa9e4066Sahrens } 4556fa9e4066Sahrens 4557fa9e4066Sahrens static int 4558fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4559fa9e4066Sahrens { 4560fa9e4066Sahrens if (cmd != DDI_ATTACH) 4561fa9e4066Sahrens return (DDI_FAILURE); 4562fa9e4066Sahrens 4563fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 4564fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 4565fa9e4066Sahrens return (DDI_FAILURE); 4566fa9e4066Sahrens 4567fa9e4066Sahrens zfs_dip = dip; 4568fa9e4066Sahrens 4569fa9e4066Sahrens ddi_report_dev(dip); 4570fa9e4066Sahrens 4571fa9e4066Sahrens return (DDI_SUCCESS); 4572fa9e4066Sahrens } 4573fa9e4066Sahrens 4574fa9e4066Sahrens static int 4575fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 4576fa9e4066Sahrens { 4577fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 4578fa9e4066Sahrens return (DDI_FAILURE); 4579fa9e4066Sahrens 4580fa9e4066Sahrens if (cmd != DDI_DETACH) 4581fa9e4066Sahrens return (DDI_FAILURE); 4582fa9e4066Sahrens 4583fa9e4066Sahrens zfs_dip = NULL; 4584fa9e4066Sahrens 4585fa9e4066Sahrens ddi_prop_remove_all(dip); 4586fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 4587fa9e4066Sahrens 4588fa9e4066Sahrens return (DDI_SUCCESS); 4589fa9e4066Sahrens } 4590fa9e4066Sahrens 4591fa9e4066Sahrens /*ARGSUSED*/ 4592fa9e4066Sahrens static int 4593fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 4594fa9e4066Sahrens { 4595fa9e4066Sahrens switch (infocmd) { 4596fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 4597fa9e4066Sahrens *result = zfs_dip; 4598fa9e4066Sahrens return (DDI_SUCCESS); 4599fa9e4066Sahrens 4600fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 4601a0965f35Sbonwick *result = (void *)0; 4602fa9e4066Sahrens return (DDI_SUCCESS); 4603fa9e4066Sahrens } 4604fa9e4066Sahrens 4605fa9e4066Sahrens return (DDI_FAILURE); 4606fa9e4066Sahrens } 4607fa9e4066Sahrens 4608fa9e4066Sahrens /* 4609fa9e4066Sahrens * OK, so this is a little weird. 4610fa9e4066Sahrens * 4611fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 4612fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 4613fa9e4066Sahrens * 4614fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 4615fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 4616fa9e4066Sahrens */ 4617fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 4618*c99e4bdcSChris Kirby zfsdev_open, /* open */ 4619*c99e4bdcSChris Kirby zfsdev_close, /* close */ 4620fa9e4066Sahrens zvol_strategy, /* strategy */ 4621fa9e4066Sahrens nodev, /* print */ 4622e7cbe64fSgw zvol_dump, /* dump */ 4623fa9e4066Sahrens zvol_read, /* read */ 4624fa9e4066Sahrens zvol_write, /* write */ 4625fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 4626fa9e4066Sahrens nodev, /* devmap */ 4627fa9e4066Sahrens nodev, /* mmap */ 4628fa9e4066Sahrens nodev, /* segmap */ 4629fa9e4066Sahrens nochpoll, /* poll */ 4630fa9e4066Sahrens ddi_prop_op, /* prop_op */ 4631fa9e4066Sahrens NULL, /* streamtab */ 4632fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 4633fa9e4066Sahrens CB_REV, /* version */ 4634feb08c6bSbillm nodev, /* async read */ 4635feb08c6bSbillm nodev, /* async write */ 4636fa9e4066Sahrens }; 4637fa9e4066Sahrens 4638fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 4639fa9e4066Sahrens DEVO_REV, /* version */ 4640fa9e4066Sahrens 0, /* refcnt */ 4641fa9e4066Sahrens zfs_info, /* info */ 4642fa9e4066Sahrens nulldev, /* identify */ 4643fa9e4066Sahrens nulldev, /* probe */ 4644fa9e4066Sahrens zfs_attach, /* attach */ 4645fa9e4066Sahrens zfs_detach, /* detach */ 4646fa9e4066Sahrens nodev, /* reset */ 4647fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 464819397407SSherry Moore NULL, /* no bus operations */ 464919397407SSherry Moore NULL, /* power */ 465019397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 4651fa9e4066Sahrens }; 4652fa9e4066Sahrens 4653fa9e4066Sahrens static struct modldrv zfs_modldrv = { 465419397407SSherry Moore &mod_driverops, 465519397407SSherry Moore "ZFS storage pool", 465619397407SSherry Moore &zfs_dev_ops 4657fa9e4066Sahrens }; 4658fa9e4066Sahrens 4659fa9e4066Sahrens static struct modlinkage modlinkage = { 4660fa9e4066Sahrens MODREV_1, 4661fa9e4066Sahrens (void *)&zfs_modlfs, 4662fa9e4066Sahrens (void *)&zfs_modldrv, 4663fa9e4066Sahrens NULL 4664fa9e4066Sahrens }; 4665fa9e4066Sahrens 4666ec533521Sfr 4667ec533521Sfr uint_t zfs_fsyncer_key; 4668f18faf3fSek extern uint_t rrw_tsd_key; 4669ec533521Sfr 4670fa9e4066Sahrens int 4671fa9e4066Sahrens _init(void) 4672fa9e4066Sahrens { 4673fa9e4066Sahrens int error; 4674fa9e4066Sahrens 4675a0965f35Sbonwick spa_init(FREAD | FWRITE); 4676a0965f35Sbonwick zfs_init(); 4677a0965f35Sbonwick zvol_init(); 4678a0965f35Sbonwick 4679a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 4680a0965f35Sbonwick zvol_fini(); 4681a0965f35Sbonwick zfs_fini(); 4682a0965f35Sbonwick spa_fini(); 4683fa9e4066Sahrens return (error); 4684a0965f35Sbonwick } 4685fa9e4066Sahrens 4686ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 4687f18faf3fSek tsd_create(&rrw_tsd_key, NULL); 4688ec533521Sfr 4689fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 4690fa9e4066Sahrens ASSERT(error == 0); 4691ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 4692fa9e4066Sahrens 4693fa9e4066Sahrens return (0); 4694fa9e4066Sahrens } 4695fa9e4066Sahrens 4696fa9e4066Sahrens int 4697fa9e4066Sahrens _fini(void) 4698fa9e4066Sahrens { 4699fa9e4066Sahrens int error; 4700fa9e4066Sahrens 4701ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 4702fa9e4066Sahrens return (EBUSY); 4703fa9e4066Sahrens 4704fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 4705fa9e4066Sahrens return (error); 4706fa9e4066Sahrens 4707fa9e4066Sahrens zvol_fini(); 4708fa9e4066Sahrens zfs_fini(); 4709fa9e4066Sahrens spa_fini(); 4710da6c28aaSamw if (zfs_nfsshare_inited) 4711ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 4712da6c28aaSamw if (zfs_smbshare_inited) 4713da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 4714da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 4715ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 4716fa9e4066Sahrens 4717ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 4718fa9e4066Sahrens ldi_ident_release(zfs_li); 4719fa9e4066Sahrens zfs_li = NULL; 4720ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 4721fa9e4066Sahrens 4722fa9e4066Sahrens return (error); 4723fa9e4066Sahrens } 4724fa9e4066Sahrens 4725fa9e4066Sahrens int 4726fa9e4066Sahrens _info(struct modinfo *modinfop) 4727fa9e4066Sahrens { 4728fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 4729fa9e4066Sahrens } 4730