1fa9e4066Sahrens /* 2fa9e4066Sahrens * CDDL HEADER START 3fa9e4066Sahrens * 4fa9e4066Sahrens * The contents of this file are subject to the terms of the 5441d80aaSlling * Common Development and Distribution License (the "License"). 6441d80aaSlling * You may not use this file except in compliance with the License. 7fa9e4066Sahrens * 8fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 10fa9e4066Sahrens * See the License for the specific language governing permissions 11fa9e4066Sahrens * and limitations under the License. 12fa9e4066Sahrens * 13fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 16fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18fa9e4066Sahrens * 19fa9e4066Sahrens * CDDL HEADER END 20fa9e4066Sahrens */ 21fa9e4066Sahrens /* 22379c004dSEric Schrock * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23fa9e4066Sahrens * Use is subject to license terms. 24fa9e4066Sahrens */ 25fa9e4066Sahrens 26fa9e4066Sahrens #include <sys/types.h> 27fa9e4066Sahrens #include <sys/param.h> 28fa9e4066Sahrens #include <sys/errno.h> 29fa9e4066Sahrens #include <sys/uio.h> 30fa9e4066Sahrens #include <sys/buf.h> 31fa9e4066Sahrens #include <sys/modctl.h> 32fa9e4066Sahrens #include <sys/open.h> 33fa9e4066Sahrens #include <sys/file.h> 34fa9e4066Sahrens #include <sys/kmem.h> 35fa9e4066Sahrens #include <sys/conf.h> 36fa9e4066Sahrens #include <sys/cmn_err.h> 37fa9e4066Sahrens #include <sys/stat.h> 38fa9e4066Sahrens #include <sys/zfs_ioctl.h> 39*4201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 40da6c28aaSamw #include <sys/zfs_znode.h> 41fa9e4066Sahrens #include <sys/zap.h> 42fa9e4066Sahrens #include <sys/spa.h> 43b1b8ab34Slling #include <sys/spa_impl.h> 44fa9e4066Sahrens #include <sys/vdev.h> 45*4201a95eSRic Aleshire #include <sys/priv_impl.h> 46fa9e4066Sahrens #include <sys/dmu.h> 47fa9e4066Sahrens #include <sys/dsl_dir.h> 48fa9e4066Sahrens #include <sys/dsl_dataset.h> 49fa9e4066Sahrens #include <sys/dsl_prop.h> 50ecd6cf80Smarks #include <sys/dsl_deleg.h> 51ecd6cf80Smarks #include <sys/dmu_objset.h> 52fa9e4066Sahrens #include <sys/ddi.h> 53fa9e4066Sahrens #include <sys/sunddi.h> 54fa9e4066Sahrens #include <sys/sunldi.h> 55fa9e4066Sahrens #include <sys/policy.h> 56fa9e4066Sahrens #include <sys/zone.h> 57fa9e4066Sahrens #include <sys/nvpair.h> 58fa9e4066Sahrens #include <sys/pathname.h> 59fa9e4066Sahrens #include <sys/mount.h> 60fa9e4066Sahrens #include <sys/sdt.h> 61fa9e4066Sahrens #include <sys/fs/zfs.h> 62fa9e4066Sahrens #include <sys/zfs_ctldir.h> 63da6c28aaSamw #include <sys/zfs_dir.h> 64a2eea2e1Sahrens #include <sys/zvol.h> 65ecd6cf80Smarks #include <sharefs/share.h> 66f18faf3fSek #include <sys/dmu_objset.h> 67fa9e4066Sahrens 68fa9e4066Sahrens #include "zfs_namecheck.h" 69e9dbad6fSeschrock #include "zfs_prop.h" 70ecd6cf80Smarks #include "zfs_deleg.h" 71fa9e4066Sahrens 72fa9e4066Sahrens extern struct modlfs zfs_modlfs; 73fa9e4066Sahrens 74fa9e4066Sahrens extern void zfs_init(void); 75fa9e4066Sahrens extern void zfs_fini(void); 76fa9e4066Sahrens 77fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 78fa9e4066Sahrens dev_info_t *zfs_dip; 79fa9e4066Sahrens 80fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *); 81ecd6cf80Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *); 82fa9e4066Sahrens 8354d692b7SGeorge Wilson typedef enum { 8454d692b7SGeorge Wilson NO_NAME, 8554d692b7SGeorge Wilson POOL_NAME, 8654d692b7SGeorge Wilson DATASET_NAME 8754d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 8854d692b7SGeorge Wilson 89fa9e4066Sahrens typedef struct zfs_ioc_vec { 90fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 91fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 9254d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 93ecd6cf80Smarks boolean_t zvec_his_log; 9454d692b7SGeorge Wilson boolean_t zvec_pool_check; 95fa9e4066Sahrens } zfs_ioc_vec_t; 96fa9e4066Sahrens 9714843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 9814843421SMatthew Ahrens static const char *userquota_perms[] = { 9914843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 10014843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 10114843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 10214843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 10314843421SMatthew Ahrens }; 10414843421SMatthew Ahrens 105*4201a95eSRic Aleshire static char *setsl_tag = "setsl_tag"; 106*4201a95eSRic Aleshire 10714843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 1086e77af0aSDavid Pacheco static void clear_props(char *dataset, nvlist_t *props, nvlist_t *newprops); 1090a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 1100a48a24eStimh boolean_t *); 1110a48a24eStimh int zfs_set_prop_nvlist(const char *, nvlist_t *); 1120a48a24eStimh 113fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 114fa9e4066Sahrens void 115fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 116fa9e4066Sahrens { 117fa9e4066Sahrens const char *newfile; 118fa9e4066Sahrens char buf[256]; 119fa9e4066Sahrens va_list adx; 120fa9e4066Sahrens 121fa9e4066Sahrens /* 122fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 123fa9e4066Sahrens */ 124fa9e4066Sahrens newfile = strrchr(file, '/'); 125fa9e4066Sahrens if (newfile != NULL) { 126fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 127fa9e4066Sahrens } else { 128fa9e4066Sahrens newfile = file; 129fa9e4066Sahrens } 130fa9e4066Sahrens 131fa9e4066Sahrens va_start(adx, fmt); 132fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 133fa9e4066Sahrens va_end(adx); 134fa9e4066Sahrens 135fa9e4066Sahrens /* 136fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 137fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 138fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 139fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 140fa9e4066Sahrens * arg0 = file name 141fa9e4066Sahrens * arg1 = function name 142fa9e4066Sahrens * arg2 = line number 143fa9e4066Sahrens * arg3 = message 144fa9e4066Sahrens */ 145fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 146fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 147fa9e4066Sahrens } 148fa9e4066Sahrens 149ecd6cf80Smarks static void 150228975ccSek history_str_free(char *buf) 151228975ccSek { 152228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 153228975ccSek } 154228975ccSek 155228975ccSek static char * 156228975ccSek history_str_get(zfs_cmd_t *zc) 157ecd6cf80Smarks { 15840feaa91Sahrens char *buf; 159ecd6cf80Smarks 160ecd6cf80Smarks if (zc->zc_history == NULL) 161228975ccSek return (NULL); 162e7437265Sahrens 163ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 164ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 165ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 166228975ccSek history_str_free(buf); 167228975ccSek return (NULL); 168ecd6cf80Smarks } 169ecd6cf80Smarks 170ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 171ecd6cf80Smarks 172228975ccSek return (buf); 173228975ccSek } 174ecd6cf80Smarks 17515e6edf1Sgw /* 17615e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 17715e6edf1Sgw */ 17815e6edf1Sgw static boolean_t 17915e6edf1Sgw zfs_is_bootfs(const char *name) 18015e6edf1Sgw { 181503ad85cSMatthew Ahrens objset_t *os; 18215e6edf1Sgw 183503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 184503ad85cSMatthew Ahrens boolean_t ret; 185b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 186503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 187503ad85cSMatthew Ahrens return (ret); 18815e6edf1Sgw } 189503ad85cSMatthew Ahrens return (B_FALSE); 19015e6edf1Sgw } 19115e6edf1Sgw 192c2a93d44Stimh /* 1930a48a24eStimh * zfs_earlier_version 194c2a93d44Stimh * 195c2a93d44Stimh * Return non-zero if the spa version is less than requested version. 196c2a93d44Stimh */ 197da6c28aaSamw static int 1980a48a24eStimh zfs_earlier_version(const char *name, int version) 199da6c28aaSamw { 200da6c28aaSamw spa_t *spa; 201da6c28aaSamw 202da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 203da6c28aaSamw if (spa_version(spa) < version) { 204da6c28aaSamw spa_close(spa, FTAG); 205da6c28aaSamw return (1); 206da6c28aaSamw } 207da6c28aaSamw spa_close(spa, FTAG); 208da6c28aaSamw } 209da6c28aaSamw return (0); 210da6c28aaSamw } 211da6c28aaSamw 2129e6eda55Smarks /* 213745cd3c5Smaybee * zpl_earlier_version 2149e6eda55Smarks * 215745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 2169e6eda55Smarks */ 217745cd3c5Smaybee static boolean_t 218745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 2199e6eda55Smarks { 2209e6eda55Smarks objset_t *os; 221745cd3c5Smaybee boolean_t rc = B_TRUE; 2229e6eda55Smarks 223503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 224745cd3c5Smaybee uint64_t zplversion; 2259e6eda55Smarks 226503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 227503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 228503ad85cSMatthew Ahrens return (B_TRUE); 229503ad85cSMatthew Ahrens } 230503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 231745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 232745cd3c5Smaybee rc = zplversion < version; 233503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2349e6eda55Smarks } 2359e6eda55Smarks return (rc); 2369e6eda55Smarks } 2379e6eda55Smarks 238228975ccSek static void 239228975ccSek zfs_log_history(zfs_cmd_t *zc) 240228975ccSek { 241228975ccSek spa_t *spa; 242228975ccSek char *buf; 243ecd6cf80Smarks 244228975ccSek if ((buf = history_str_get(zc)) == NULL) 245228975ccSek return; 246228975ccSek 247228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 248228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 249228975ccSek (void) spa_history_log(spa, buf, LOG_CMD_NORMAL); 250228975ccSek spa_close(spa, FTAG); 251228975ccSek } 252228975ccSek history_str_free(buf); 253ecd6cf80Smarks } 254ecd6cf80Smarks 255fa9e4066Sahrens /* 256fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 257fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 258fa9e4066Sahrens */ 259fa9e4066Sahrens /* ARGSUSED */ 260fa9e4066Sahrens static int 261ecd6cf80Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr) 262fa9e4066Sahrens { 263fa9e4066Sahrens return (0); 264fa9e4066Sahrens } 265fa9e4066Sahrens 266fa9e4066Sahrens /* 267fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 268fa9e4066Sahrens * no privileges, but must be visible in the local zone. 269fa9e4066Sahrens */ 270fa9e4066Sahrens /* ARGSUSED */ 271fa9e4066Sahrens static int 272ecd6cf80Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr) 273fa9e4066Sahrens { 274fa9e4066Sahrens if (INGLOBALZONE(curproc) || 275ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 276fa9e4066Sahrens return (0); 277fa9e4066Sahrens 278fa9e4066Sahrens return (ENOENT); 279fa9e4066Sahrens } 280fa9e4066Sahrens 281fa9e4066Sahrens static int 282fa9e4066Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr) 283fa9e4066Sahrens { 284fa9e4066Sahrens uint64_t zoned; 285fa9e4066Sahrens int writable = 1; 286fa9e4066Sahrens 287fa9e4066Sahrens /* 288fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 289fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 290fa9e4066Sahrens */ 291fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 292fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 293fa9e4066Sahrens return (ENOENT); 294fa9e4066Sahrens 295fa9e4066Sahrens if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 296fa9e4066Sahrens return (ENOENT); 297fa9e4066Sahrens 298fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 299fa9e4066Sahrens /* 300fa9e4066Sahrens * If the fs is zoned, only root can access it from the 301fa9e4066Sahrens * global zone. 302fa9e4066Sahrens */ 303fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 304fa9e4066Sahrens return (EPERM); 305fa9e4066Sahrens } else { 306fa9e4066Sahrens /* 307fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 308fa9e4066Sahrens */ 309fa9e4066Sahrens if (!zoned) 310fa9e4066Sahrens return (EPERM); 311fa9e4066Sahrens 312fa9e4066Sahrens /* must be writable by this zone */ 313fa9e4066Sahrens if (!writable) 314fa9e4066Sahrens return (EPERM); 315fa9e4066Sahrens } 316fa9e4066Sahrens return (0); 317fa9e4066Sahrens } 318fa9e4066Sahrens 319fa9e4066Sahrens int 320ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 321fa9e4066Sahrens { 322fa9e4066Sahrens int error; 323fa9e4066Sahrens 324ecd6cf80Smarks error = zfs_dozonecheck(name, cr); 325ecd6cf80Smarks if (error == 0) { 326ecd6cf80Smarks error = secpolicy_zfs(cr); 327db870a07Sahrens if (error) 328ecd6cf80Smarks error = dsl_deleg_access(name, perm, cr); 329ecd6cf80Smarks } 330ecd6cf80Smarks return (error); 331ecd6cf80Smarks } 332ecd6cf80Smarks 333*4201a95eSRic Aleshire /* 334*4201a95eSRic Aleshire * Policy for setting the security label property. 335*4201a95eSRic Aleshire * 336*4201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 337*4201a95eSRic Aleshire * 338*4201a95eSRic Aleshire * If the objset is non-NULL upon return, the caller is responsible 339*4201a95eSRic Aleshire * for dis-owning it, using the tag: setsl_tag. 340*4201a95eSRic Aleshire * 341*4201a95eSRic Aleshire */ 342*4201a95eSRic Aleshire static int 343*4201a95eSRic Aleshire zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr, 344*4201a95eSRic Aleshire objset_t **osp) 345*4201a95eSRic Aleshire { 346*4201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 347*4201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 348*4201a95eSRic Aleshire boolean_t new_default = FALSE; 349*4201a95eSRic Aleshire uint64_t zoned; 350*4201a95eSRic Aleshire int needed_priv = -1; 351*4201a95eSRic Aleshire int error; 352*4201a95eSRic Aleshire 353*4201a95eSRic Aleshire /* First get the existing dataset label. */ 354*4201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 355*4201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 356*4201a95eSRic Aleshire if (error) 357*4201a95eSRic Aleshire return (EPERM); 358*4201a95eSRic Aleshire 359*4201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 360*4201a95eSRic Aleshire new_default = TRUE; 361*4201a95eSRic Aleshire 362*4201a95eSRic Aleshire /* The label must be translatable */ 363*4201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 364*4201a95eSRic Aleshire return (EINVAL); 365*4201a95eSRic Aleshire 366*4201a95eSRic Aleshire /* 367*4201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 368*4201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 369*4201a95eSRic Aleshire * are needed. 370*4201a95eSRic Aleshire */ 371*4201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 372*4201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 373*4201a95eSRic Aleshire return (EPERM); 374*4201a95eSRic Aleshire return (0); 375*4201a95eSRic Aleshire } 376*4201a95eSRic Aleshire 377*4201a95eSRic Aleshire /* 378*4201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 379*4201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 380*4201a95eSRic Aleshire * global zone. 381*4201a95eSRic Aleshire */ 382*4201a95eSRic Aleshire if (dsl_prop_get_integer(name, 383*4201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 384*4201a95eSRic Aleshire return (EPERM); 385*4201a95eSRic Aleshire if (!zoned) { 386*4201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 387*4201a95eSRic Aleshire return (EPERM); 388*4201a95eSRic Aleshire } 389*4201a95eSRic Aleshire 390*4201a95eSRic Aleshire /* 391*4201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 392*4201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 393*4201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 394*4201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 395*4201a95eSRic Aleshire */ 396*4201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 397*4201a95eSRic Aleshire ASSERT(osp != NULL); 398*4201a95eSRic Aleshire /* 399*4201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 400*4201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 401*4201a95eSRic Aleshire */ 402*4201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 403*4201a95eSRic Aleshire setsl_tag, osp); 404*4201a95eSRic Aleshire if (error) 405*4201a95eSRic Aleshire return (EPERM); 406*4201a95eSRic Aleshire 407*4201a95eSRic Aleshire if (new_default) { 408*4201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 409*4201a95eSRic Aleshire goto out_check; 410*4201a95eSRic Aleshire } 411*4201a95eSRic Aleshire 412*4201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 413*4201a95eSRic Aleshire return (EPERM); 414*4201a95eSRic Aleshire 415*4201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 416*4201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 417*4201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 418*4201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 419*4201a95eSRic Aleshire } else { 420*4201a95eSRic Aleshire /* dataset currently has a default label */ 421*4201a95eSRic Aleshire if (!new_default) 422*4201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 423*4201a95eSRic Aleshire } 424*4201a95eSRic Aleshire 425*4201a95eSRic Aleshire out_check: 426*4201a95eSRic Aleshire if (needed_priv != -1) 427*4201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 428*4201a95eSRic Aleshire return (0); 429*4201a95eSRic Aleshire } 430*4201a95eSRic Aleshire 431ecd6cf80Smarks static int 432ecd6cf80Smarks zfs_secpolicy_setprop(const char *name, zfs_prop_t prop, cred_t *cr) 433ecd6cf80Smarks { 434ecd6cf80Smarks /* 435ecd6cf80Smarks * Check permissions for special properties. 436ecd6cf80Smarks */ 437ecd6cf80Smarks switch (prop) { 438ecd6cf80Smarks case ZFS_PROP_ZONED: 439ecd6cf80Smarks /* 440ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 441ecd6cf80Smarks */ 442ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 443ecd6cf80Smarks return (EPERM); 444ecd6cf80Smarks break; 445ecd6cf80Smarks 446ecd6cf80Smarks case ZFS_PROP_QUOTA: 447ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 448ecd6cf80Smarks uint64_t zoned; 449ecd6cf80Smarks char setpoint[MAXNAMELEN]; 450ecd6cf80Smarks /* 451ecd6cf80Smarks * Unprivileged users are allowed to modify the 452ecd6cf80Smarks * quota on things *under* (ie. contained by) 453ecd6cf80Smarks * the thing they own. 454ecd6cf80Smarks */ 455ecd6cf80Smarks if (dsl_prop_get_integer(name, "zoned", &zoned, 456ecd6cf80Smarks setpoint)) 457ecd6cf80Smarks return (EPERM); 458db870a07Sahrens if (!zoned || strlen(name) <= strlen(setpoint)) 459ecd6cf80Smarks return (EPERM); 460ecd6cf80Smarks } 461db870a07Sahrens break; 462*4201a95eSRic Aleshire 463*4201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 464*4201a95eSRic Aleshire if (!is_system_labeled()) 465*4201a95eSRic Aleshire return (EPERM); 466*4201a95eSRic Aleshire break; 467ecd6cf80Smarks } 468ecd6cf80Smarks 46991ebeef5Sahrens return (zfs_secpolicy_write_perms(name, zfs_prop_to_name(prop), cr)); 470ecd6cf80Smarks } 471ecd6cf80Smarks 472ecd6cf80Smarks int 473ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr) 474ecd6cf80Smarks { 475ecd6cf80Smarks int error; 476ecd6cf80Smarks 477ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 478ecd6cf80Smarks if (error) 479fa9e4066Sahrens return (error); 480fa9e4066Sahrens 481ecd6cf80Smarks /* 482ecd6cf80Smarks * permission to set permissions will be evaluated later in 483ecd6cf80Smarks * dsl_deleg_can_allow() 484ecd6cf80Smarks */ 485ecd6cf80Smarks return (0); 486ecd6cf80Smarks } 487ecd6cf80Smarks 488ecd6cf80Smarks int 489ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr) 490ecd6cf80Smarks { 491681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 492681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 493ecd6cf80Smarks } 494ecd6cf80Smarks 495ecd6cf80Smarks int 496ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr) 497ecd6cf80Smarks { 498ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 499ecd6cf80Smarks ZFS_DELEG_PERM_SEND, cr)); 500ecd6cf80Smarks } 501ecd6cf80Smarks 502743a77edSAlan Wright static int 503743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr) 504743a77edSAlan Wright { 505743a77edSAlan Wright vnode_t *vp; 506743a77edSAlan Wright int error; 507743a77edSAlan Wright 508743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 509743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 510743a77edSAlan Wright return (error); 511743a77edSAlan Wright 512743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 513743a77edSAlan Wright 514743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 515743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 516743a77edSAlan Wright zc->zc_name) != 0)) { 517743a77edSAlan Wright VN_RELE(vp); 518743a77edSAlan Wright return (EPERM); 519743a77edSAlan Wright } 520743a77edSAlan Wright 521743a77edSAlan Wright VN_RELE(vp); 522743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 523743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 524743a77edSAlan Wright } 525743a77edSAlan Wright 526ecd6cf80Smarks int 527ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr) 528ecd6cf80Smarks { 529ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 530ecd6cf80Smarks return (EPERM); 531ecd6cf80Smarks 5323cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 533ecd6cf80Smarks return (0); 534ecd6cf80Smarks } else { 535743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 536743a77edSAlan Wright } 537743a77edSAlan Wright } 538ecd6cf80Smarks 539743a77edSAlan Wright int 540743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr) 541743a77edSAlan Wright { 542743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 543743a77edSAlan Wright return (EPERM); 544ecd6cf80Smarks 545743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 546743a77edSAlan Wright return (0); 547743a77edSAlan Wright } else { 548743a77edSAlan Wright return (zfs_secpolicy_deleg_share(zc, cr)); 549ecd6cf80Smarks } 550fa9e4066Sahrens } 551fa9e4066Sahrens 552fa9e4066Sahrens static int 553ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 554fa9e4066Sahrens { 555fa9e4066Sahrens char *cp; 556fa9e4066Sahrens 557fa9e4066Sahrens /* 558fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 559fa9e4066Sahrens */ 560ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 561ecd6cf80Smarks cp = strrchr(parent, '@'); 562fa9e4066Sahrens if (cp != NULL) { 563fa9e4066Sahrens cp[0] = '\0'; 564fa9e4066Sahrens } else { 565ecd6cf80Smarks cp = strrchr(parent, '/'); 566fa9e4066Sahrens if (cp == NULL) 567fa9e4066Sahrens return (ENOENT); 568fa9e4066Sahrens cp[0] = '\0'; 569ecd6cf80Smarks } 570ecd6cf80Smarks 571ecd6cf80Smarks return (0); 572ecd6cf80Smarks } 573ecd6cf80Smarks 574ecd6cf80Smarks int 575ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 576ecd6cf80Smarks { 577ecd6cf80Smarks int error; 578ecd6cf80Smarks 579ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 580ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 581ecd6cf80Smarks return (error); 582ecd6cf80Smarks 583ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 584ecd6cf80Smarks } 585ecd6cf80Smarks 586ecd6cf80Smarks static int 587ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr) 588ecd6cf80Smarks { 589ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 590ecd6cf80Smarks } 591ecd6cf80Smarks 592ecd6cf80Smarks /* 593ecd6cf80Smarks * Must have sys_config privilege to check the iscsi permission 594ecd6cf80Smarks */ 595ecd6cf80Smarks /* ARGSUSED */ 596ecd6cf80Smarks static int 597ecd6cf80Smarks zfs_secpolicy_iscsi(zfs_cmd_t *zc, cred_t *cr) 598ecd6cf80Smarks { 599ecd6cf80Smarks return (secpolicy_zfs(cr)); 600ecd6cf80Smarks } 601ecd6cf80Smarks 602ecd6cf80Smarks int 603ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 604ecd6cf80Smarks { 605ecd6cf80Smarks char parentname[MAXNAMELEN]; 606ecd6cf80Smarks int error; 607ecd6cf80Smarks 608ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 609ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 610ecd6cf80Smarks return (error); 611ecd6cf80Smarks 612ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 613ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 614ecd6cf80Smarks return (error); 615ecd6cf80Smarks 616ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 617ecd6cf80Smarks sizeof (parentname))) != 0) 618ecd6cf80Smarks return (error); 619ecd6cf80Smarks 620ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 621ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 622ecd6cf80Smarks return (error); 623ecd6cf80Smarks 624ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 625ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 626ecd6cf80Smarks return (error); 627ecd6cf80Smarks 628ecd6cf80Smarks return (error); 629ecd6cf80Smarks } 630ecd6cf80Smarks 631ecd6cf80Smarks static int 632ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr) 633ecd6cf80Smarks { 634ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 635ecd6cf80Smarks } 636ecd6cf80Smarks 637ecd6cf80Smarks static int 638ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr) 639ecd6cf80Smarks { 640ecd6cf80Smarks char parentname[MAXNAMELEN]; 641ecd6cf80Smarks objset_t *clone; 642ecd6cf80Smarks int error; 643ecd6cf80Smarks 644ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 645ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 646ecd6cf80Smarks if (error) 647ecd6cf80Smarks return (error); 648ecd6cf80Smarks 649503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &clone); 650ecd6cf80Smarks 651ecd6cf80Smarks if (error == 0) { 652ecd6cf80Smarks dsl_dataset_t *pclone = NULL; 653ecd6cf80Smarks dsl_dir_t *dd; 654503ad85cSMatthew Ahrens dd = clone->os_dsl_dataset->ds_dir; 655ecd6cf80Smarks 656ecd6cf80Smarks rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 657745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 658745cd3c5Smaybee dd->dd_phys->dd_origin_obj, FTAG, &pclone); 659ecd6cf80Smarks rw_exit(&dd->dd_pool->dp_config_rwlock); 660ecd6cf80Smarks if (error) { 661503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 662ecd6cf80Smarks return (error); 663ecd6cf80Smarks } 664ecd6cf80Smarks 665ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 666ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 667ecd6cf80Smarks 668ecd6cf80Smarks dsl_dataset_name(pclone, parentname); 669503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 670745cd3c5Smaybee dsl_dataset_rele(pclone, FTAG); 671ecd6cf80Smarks if (error == 0) 672ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 673ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 674ecd6cf80Smarks } 675ecd6cf80Smarks return (error); 676ecd6cf80Smarks } 677ecd6cf80Smarks 678ecd6cf80Smarks static int 679ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr) 680ecd6cf80Smarks { 681ecd6cf80Smarks int error; 682ecd6cf80Smarks 683ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 684ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 685ecd6cf80Smarks return (error); 686ecd6cf80Smarks 687ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 688ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 689ecd6cf80Smarks return (error); 690ecd6cf80Smarks 691ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 692ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 693ecd6cf80Smarks } 694ecd6cf80Smarks 695ecd6cf80Smarks int 696ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 697ecd6cf80Smarks { 698681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 699681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 700ecd6cf80Smarks } 701ecd6cf80Smarks 702ecd6cf80Smarks static int 703ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr) 704ecd6cf80Smarks { 705ecd6cf80Smarks 706ecd6cf80Smarks return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr)); 707ecd6cf80Smarks } 708ecd6cf80Smarks 709ecd6cf80Smarks static int 710ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr) 711ecd6cf80Smarks { 712ecd6cf80Smarks char parentname[MAXNAMELEN]; 713ecd6cf80Smarks int error; 714ecd6cf80Smarks 715ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 716ecd6cf80Smarks sizeof (parentname))) != 0) 717ecd6cf80Smarks return (error); 718fa9e4066Sahrens 719ecd6cf80Smarks if (zc->zc_value[0] != '\0') { 720ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_value, 721ecd6cf80Smarks ZFS_DELEG_PERM_CLONE, cr)) != 0) 722ecd6cf80Smarks return (error); 723fa9e4066Sahrens } 724fa9e4066Sahrens 725ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 726ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 727ecd6cf80Smarks return (error); 728ecd6cf80Smarks 729ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 730ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 731ecd6cf80Smarks 732ecd6cf80Smarks return (error); 733ecd6cf80Smarks } 734ecd6cf80Smarks 735ecd6cf80Smarks static int 736ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr) 737ecd6cf80Smarks { 738ecd6cf80Smarks int error; 739ecd6cf80Smarks 740ecd6cf80Smarks error = secpolicy_fs_unmount(cr, NULL); 741ecd6cf80Smarks if (error) { 742ecd6cf80Smarks error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr); 743ecd6cf80Smarks } 744ecd6cf80Smarks return (error); 745fa9e4066Sahrens } 746fa9e4066Sahrens 747fa9e4066Sahrens /* 748fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 749fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 750fa9e4066Sahrens */ 751fa9e4066Sahrens /* ARGSUSED */ 752fa9e4066Sahrens static int 753ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr) 754fa9e4066Sahrens { 755fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 756fa9e4066Sahrens return (EPERM); 757fa9e4066Sahrens 758fa9e4066Sahrens return (0); 759fa9e4066Sahrens } 760fa9e4066Sahrens 761ea8dc4b6Seschrock /* 762ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 763ea8dc4b6Seschrock */ 764ea8dc4b6Seschrock /* ARGSUSED */ 765ea8dc4b6Seschrock static int 766ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr) 767ea8dc4b6Seschrock { 768ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 769ea8dc4b6Seschrock } 770ea8dc4b6Seschrock 771e45ce728Sahrens static int 772e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr) 773e45ce728Sahrens { 774e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 775e45ce728Sahrens 776990b4856Slling if (prop == ZPROP_INVAL) { 777e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 778e45ce728Sahrens return (EINVAL); 779e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 780e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 781e45ce728Sahrens } else { 782e45ce728Sahrens if (!zfs_prop_inheritable(prop)) 783e45ce728Sahrens return (EINVAL); 784e45ce728Sahrens return (zfs_secpolicy_setprop(zc->zc_name, prop, cr)); 785e45ce728Sahrens } 786e45ce728Sahrens } 787e45ce728Sahrens 78814843421SMatthew Ahrens static int 78914843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr) 79014843421SMatthew Ahrens { 79114843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 79214843421SMatthew Ahrens if (err) 79314843421SMatthew Ahrens return (err); 79414843421SMatthew Ahrens 79514843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 79614843421SMatthew Ahrens return (EINVAL); 79714843421SMatthew Ahrens 79814843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 79914843421SMatthew Ahrens /* 80014843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 80114843421SMatthew Ahrens * themself, allow it. 80214843421SMatthew Ahrens */ 80314843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 80414843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 80514843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 80614843421SMatthew Ahrens return (0); 80714843421SMatthew Ahrens } else { 80814843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 80914843421SMatthew Ahrens return (0); 81014843421SMatthew Ahrens } 81114843421SMatthew Ahrens } 81214843421SMatthew Ahrens 81314843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 81414843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 81514843421SMatthew Ahrens } 81614843421SMatthew Ahrens 81714843421SMatthew Ahrens static int 81814843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr) 81914843421SMatthew Ahrens { 82014843421SMatthew Ahrens int err = zfs_secpolicy_read(zc, cr); 82114843421SMatthew Ahrens if (err) 82214843421SMatthew Ahrens return (err); 82314843421SMatthew Ahrens 82414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 82514843421SMatthew Ahrens return (EINVAL); 82614843421SMatthew Ahrens 82714843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 82814843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 82914843421SMatthew Ahrens } 83014843421SMatthew Ahrens 83114843421SMatthew Ahrens static int 83214843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr) 83314843421SMatthew Ahrens { 83414843421SMatthew Ahrens return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, cr)); 83514843421SMatthew Ahrens } 83614843421SMatthew Ahrens 837842727c2SChris Kirby static int 838842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr) 839842727c2SChris Kirby { 840842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 841842727c2SChris Kirby ZFS_DELEG_PERM_HOLD, cr)); 842842727c2SChris Kirby } 843842727c2SChris Kirby 844842727c2SChris Kirby static int 845842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr) 846842727c2SChris Kirby { 847842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 848842727c2SChris Kirby ZFS_DELEG_PERM_RELEASE, cr)); 849842727c2SChris Kirby } 850842727c2SChris Kirby 851fa9e4066Sahrens /* 852fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 853fa9e4066Sahrens */ 854fa9e4066Sahrens static int 855478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 856fa9e4066Sahrens { 857fa9e4066Sahrens char *packed; 858fa9e4066Sahrens int error; 859990b4856Slling nvlist_t *list = NULL; 860fa9e4066Sahrens 861fa9e4066Sahrens /* 862e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 863fa9e4066Sahrens */ 864990b4856Slling if (size == 0) 865fa9e4066Sahrens return (EINVAL); 866fa9e4066Sahrens 867fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 868fa9e4066Sahrens 869478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 870478ed9adSEric Taylor iflag)) != 0) { 871fa9e4066Sahrens kmem_free(packed, size); 872fa9e4066Sahrens return (error); 873fa9e4066Sahrens } 874fa9e4066Sahrens 875990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 876fa9e4066Sahrens kmem_free(packed, size); 877fa9e4066Sahrens return (error); 878fa9e4066Sahrens } 879fa9e4066Sahrens 880fa9e4066Sahrens kmem_free(packed, size); 881fa9e4066Sahrens 882990b4856Slling *nvp = list; 883fa9e4066Sahrens return (0); 884fa9e4066Sahrens } 885fa9e4066Sahrens 886e9dbad6fSeschrock static int 887e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 888e9dbad6fSeschrock { 889e9dbad6fSeschrock char *packed = NULL; 890e9dbad6fSeschrock size_t size; 891e9dbad6fSeschrock int error; 892e9dbad6fSeschrock 893e9dbad6fSeschrock VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); 894e9dbad6fSeschrock 895e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 896e9dbad6fSeschrock error = ENOMEM; 897e9dbad6fSeschrock } else { 898da165920Smarks packed = kmem_alloc(size, KM_SLEEP); 899e9dbad6fSeschrock VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, 900e9dbad6fSeschrock KM_SLEEP) == 0); 901478ed9adSEric Taylor error = ddi_copyout(packed, 902478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags); 903e9dbad6fSeschrock kmem_free(packed, size); 904e9dbad6fSeschrock } 905e9dbad6fSeschrock 906e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 907e9dbad6fSeschrock return (error); 908e9dbad6fSeschrock } 909e9dbad6fSeschrock 91014843421SMatthew Ahrens static int 91114843421SMatthew Ahrens getzfsvfs(const char *dsname, zfsvfs_t **zvp) 91214843421SMatthew Ahrens { 91314843421SMatthew Ahrens objset_t *os; 91414843421SMatthew Ahrens int error; 91514843421SMatthew Ahrens 916503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 91714843421SMatthew Ahrens if (error) 91814843421SMatthew Ahrens return (error); 919503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 920503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 921503ad85cSMatthew Ahrens return (EINVAL); 922503ad85cSMatthew Ahrens } 92314843421SMatthew Ahrens 924503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 92514843421SMatthew Ahrens *zvp = dmu_objset_get_user(os); 92614843421SMatthew Ahrens if (*zvp) { 92714843421SMatthew Ahrens VFS_HOLD((*zvp)->z_vfs); 92814843421SMatthew Ahrens } else { 92914843421SMatthew Ahrens error = ESRCH; 93014843421SMatthew Ahrens } 931503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 932503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 93314843421SMatthew Ahrens return (error); 93414843421SMatthew Ahrens } 93514843421SMatthew Ahrens 93614843421SMatthew Ahrens /* 93714843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 93814843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 93914843421SMatthew Ahrens */ 94014843421SMatthew Ahrens static int 941503ad85cSMatthew Ahrens zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zvp) 94214843421SMatthew Ahrens { 94314843421SMatthew Ahrens int error = 0; 94414843421SMatthew Ahrens 94514843421SMatthew Ahrens if (getzfsvfs(name, zvp) != 0) 946503ad85cSMatthew Ahrens error = zfsvfs_create(name, zvp); 94714843421SMatthew Ahrens if (error == 0) { 94814843421SMatthew Ahrens rrw_enter(&(*zvp)->z_teardown_lock, RW_READER, tag); 94914843421SMatthew Ahrens if ((*zvp)->z_unmounted) { 95014843421SMatthew Ahrens /* 95114843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 95214843421SMatthew Ahrens * thread should be just about to disassociate the 95314843421SMatthew Ahrens * objset from the zfsvfs. 95414843421SMatthew Ahrens */ 95514843421SMatthew Ahrens rrw_exit(&(*zvp)->z_teardown_lock, tag); 95614843421SMatthew Ahrens return (EBUSY); 95714843421SMatthew Ahrens } 95814843421SMatthew Ahrens } 95914843421SMatthew Ahrens return (error); 96014843421SMatthew Ahrens } 96114843421SMatthew Ahrens 96214843421SMatthew Ahrens static void 96314843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 96414843421SMatthew Ahrens { 96514843421SMatthew Ahrens rrw_exit(&zfsvfs->z_teardown_lock, tag); 96614843421SMatthew Ahrens 96714843421SMatthew Ahrens if (zfsvfs->z_vfs) { 96814843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 96914843421SMatthew Ahrens } else { 970503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 97114843421SMatthew Ahrens zfsvfs_free(zfsvfs); 97214843421SMatthew Ahrens } 97314843421SMatthew Ahrens } 97414843421SMatthew Ahrens 975fa9e4066Sahrens static int 976fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 977fa9e4066Sahrens { 978fa9e4066Sahrens int error; 979990b4856Slling nvlist_t *config, *props = NULL; 9800a48a24eStimh nvlist_t *rootprops = NULL; 9810a48a24eStimh nvlist_t *zplprops = NULL; 982228975ccSek char *buf; 983fa9e4066Sahrens 984990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 985478ed9adSEric Taylor zc->zc_iflags, &config)) 986fa9e4066Sahrens return (error); 9872a6b87f0Sek 988990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 989478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 990478ed9adSEric Taylor zc->zc_iflags, &props))) { 991990b4856Slling nvlist_free(config); 992990b4856Slling return (error); 993990b4856Slling } 994990b4856Slling 9950a48a24eStimh if (props) { 9960a48a24eStimh nvlist_t *nvl = NULL; 9970a48a24eStimh uint64_t version = SPA_VERSION; 9980a48a24eStimh 9990a48a24eStimh (void) nvlist_lookup_uint64(props, 10000a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 10010a48a24eStimh if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) { 10020a48a24eStimh error = EINVAL; 10030a48a24eStimh goto pool_props_bad; 10040a48a24eStimh } 10050a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 10060a48a24eStimh if (nvl) { 10070a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 10080a48a24eStimh if (error != 0) { 10090a48a24eStimh nvlist_free(config); 10100a48a24eStimh nvlist_free(props); 10110a48a24eStimh return (error); 10120a48a24eStimh } 10130a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 10140a48a24eStimh } 10150a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 10160a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 10170a48a24eStimh zplprops, NULL); 10180a48a24eStimh if (error) 10190a48a24eStimh goto pool_props_bad; 10200a48a24eStimh } 10210a48a24eStimh 10222a6b87f0Sek buf = history_str_get(zc); 1023fa9e4066Sahrens 10240a48a24eStimh error = spa_create(zc->zc_name, config, props, buf, zplprops); 10250a48a24eStimh 10260a48a24eStimh /* 10270a48a24eStimh * Set the remaining root properties 10280a48a24eStimh */ 10290a48a24eStimh if (!error && 10300a48a24eStimh (error = zfs_set_prop_nvlist(zc->zc_name, rootprops)) != 0) 10310a48a24eStimh (void) spa_destroy(zc->zc_name); 1032fa9e4066Sahrens 10332a6b87f0Sek if (buf != NULL) 10342a6b87f0Sek history_str_free(buf); 1035990b4856Slling 10360a48a24eStimh pool_props_bad: 10370a48a24eStimh nvlist_free(rootprops); 10380a48a24eStimh nvlist_free(zplprops); 1039fa9e4066Sahrens nvlist_free(config); 10400a48a24eStimh nvlist_free(props); 1041990b4856Slling 1042fa9e4066Sahrens return (error); 1043fa9e4066Sahrens } 1044fa9e4066Sahrens 1045fa9e4066Sahrens static int 1046fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1047fa9e4066Sahrens { 1048ecd6cf80Smarks int error; 1049ecd6cf80Smarks zfs_log_history(zc); 1050ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1051681d9761SEric Taylor if (error == 0) 1052681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1053ecd6cf80Smarks return (error); 1054fa9e4066Sahrens } 1055fa9e4066Sahrens 1056fa9e4066Sahrens static int 1057fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1058fa9e4066Sahrens { 1059990b4856Slling nvlist_t *config, *props = NULL; 1060fa9e4066Sahrens uint64_t guid; 1061468c413aSTim Haley int error; 1062fa9e4066Sahrens 1063990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1064478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1065990b4856Slling return (error); 1066990b4856Slling 1067990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1068478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1069478ed9adSEric Taylor zc->zc_iflags, &props))) { 1070990b4856Slling nvlist_free(config); 1071fa9e4066Sahrens return (error); 1072990b4856Slling } 1073fa9e4066Sahrens 1074fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1075ea8dc4b6Seschrock guid != zc->zc_guid) 1076fa9e4066Sahrens error = EINVAL; 1077c5904d13Seschrock else if (zc->zc_cookie) 1078468c413aSTim Haley error = spa_import_verbatim(zc->zc_name, config, props); 1079fa9e4066Sahrens else 1080990b4856Slling error = spa_import(zc->zc_name, config, props); 1081fa9e4066Sahrens 1082468c413aSTim Haley if (zc->zc_nvlist_dst != 0) 1083468c413aSTim Haley (void) put_nvlist(zc, config); 1084468c413aSTim Haley 1085fa9e4066Sahrens nvlist_free(config); 1086fa9e4066Sahrens 1087990b4856Slling if (props) 1088990b4856Slling nvlist_free(props); 1089990b4856Slling 1090fa9e4066Sahrens return (error); 1091fa9e4066Sahrens } 1092fa9e4066Sahrens 1093fa9e4066Sahrens static int 1094fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1095fa9e4066Sahrens { 1096ecd6cf80Smarks int error; 109789a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1098394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 109989a89ebfSlling 1100ecd6cf80Smarks zfs_log_history(zc); 1101394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1102681d9761SEric Taylor if (error == 0) 1103681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1104ecd6cf80Smarks return (error); 1105fa9e4066Sahrens } 1106fa9e4066Sahrens 1107fa9e4066Sahrens static int 1108fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1109fa9e4066Sahrens { 1110fa9e4066Sahrens nvlist_t *configs; 1111fa9e4066Sahrens int error; 1112fa9e4066Sahrens 1113fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1114fa9e4066Sahrens return (EEXIST); 1115fa9e4066Sahrens 1116e9dbad6fSeschrock error = put_nvlist(zc, configs); 1117fa9e4066Sahrens 1118fa9e4066Sahrens nvlist_free(configs); 1119fa9e4066Sahrens 1120fa9e4066Sahrens return (error); 1121fa9e4066Sahrens } 1122fa9e4066Sahrens 1123fa9e4066Sahrens static int 1124fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1125fa9e4066Sahrens { 1126fa9e4066Sahrens nvlist_t *config; 1127fa9e4066Sahrens int error; 1128ea8dc4b6Seschrock int ret = 0; 1129fa9e4066Sahrens 1130e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1131e9dbad6fSeschrock sizeof (zc->zc_value)); 1132fa9e4066Sahrens 1133fa9e4066Sahrens if (config != NULL) { 1134e9dbad6fSeschrock ret = put_nvlist(zc, config); 1135fa9e4066Sahrens nvlist_free(config); 1136ea8dc4b6Seschrock 1137ea8dc4b6Seschrock /* 1138ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1139ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1140ea8dc4b6Seschrock * in 'zc_cookie'. 1141ea8dc4b6Seschrock */ 1142ea8dc4b6Seschrock zc->zc_cookie = error; 1143fa9e4066Sahrens } else { 1144ea8dc4b6Seschrock ret = error; 1145fa9e4066Sahrens } 1146fa9e4066Sahrens 1147ea8dc4b6Seschrock return (ret); 1148fa9e4066Sahrens } 1149fa9e4066Sahrens 1150fa9e4066Sahrens /* 1151fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1152fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1153fa9e4066Sahrens */ 1154fa9e4066Sahrens static int 1155fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1156fa9e4066Sahrens { 1157fa9e4066Sahrens nvlist_t *tryconfig, *config; 1158fa9e4066Sahrens int error; 1159fa9e4066Sahrens 1160990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1161478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1162fa9e4066Sahrens return (error); 1163fa9e4066Sahrens 1164fa9e4066Sahrens config = spa_tryimport(tryconfig); 1165fa9e4066Sahrens 1166fa9e4066Sahrens nvlist_free(tryconfig); 1167fa9e4066Sahrens 1168fa9e4066Sahrens if (config == NULL) 1169fa9e4066Sahrens return (EINVAL); 1170fa9e4066Sahrens 1171e9dbad6fSeschrock error = put_nvlist(zc, config); 1172fa9e4066Sahrens nvlist_free(config); 1173fa9e4066Sahrens 1174fa9e4066Sahrens return (error); 1175fa9e4066Sahrens } 1176fa9e4066Sahrens 1177fa9e4066Sahrens static int 1178fa9e4066Sahrens zfs_ioc_pool_scrub(zfs_cmd_t *zc) 1179fa9e4066Sahrens { 1180fa9e4066Sahrens spa_t *spa; 1181fa9e4066Sahrens int error; 1182fa9e4066Sahrens 118306eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 118406eeb2adSek return (error); 118506eeb2adSek 1186088f3894Sahrens error = spa_scrub(spa, zc->zc_cookie); 118706eeb2adSek 118806eeb2adSek spa_close(spa, FTAG); 118906eeb2adSek 1190fa9e4066Sahrens return (error); 1191fa9e4066Sahrens } 1192fa9e4066Sahrens 1193fa9e4066Sahrens static int 1194fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1195fa9e4066Sahrens { 1196fa9e4066Sahrens spa_t *spa; 1197fa9e4066Sahrens int error; 1198fa9e4066Sahrens 1199fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1200fa9e4066Sahrens if (error == 0) { 1201fa9e4066Sahrens spa_freeze(spa); 1202fa9e4066Sahrens spa_close(spa, FTAG); 1203fa9e4066Sahrens } 1204fa9e4066Sahrens return (error); 1205fa9e4066Sahrens } 1206fa9e4066Sahrens 1207eaca9bbdSeschrock static int 1208eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1209eaca9bbdSeschrock { 1210eaca9bbdSeschrock spa_t *spa; 1211eaca9bbdSeschrock int error; 1212eaca9bbdSeschrock 121306eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 121406eeb2adSek return (error); 121506eeb2adSek 1216558d2d50Slling if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) { 1217558d2d50Slling spa_close(spa, FTAG); 1218558d2d50Slling return (EINVAL); 1219558d2d50Slling } 1220558d2d50Slling 1221990b4856Slling spa_upgrade(spa, zc->zc_cookie); 122206eeb2adSek spa_close(spa, FTAG); 122306eeb2adSek 122406eeb2adSek return (error); 122506eeb2adSek } 122606eeb2adSek 122706eeb2adSek static int 122806eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 122906eeb2adSek { 123006eeb2adSek spa_t *spa; 123106eeb2adSek char *hist_buf; 123206eeb2adSek uint64_t size; 123306eeb2adSek int error; 123406eeb2adSek 123506eeb2adSek if ((size = zc->zc_history_len) == 0) 123606eeb2adSek return (EINVAL); 123706eeb2adSek 123806eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 123906eeb2adSek return (error); 124006eeb2adSek 1241e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1242d7306b64Sek spa_close(spa, FTAG); 1243d7306b64Sek return (ENOTSUP); 1244d7306b64Sek } 1245d7306b64Sek 124606eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 124706eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 124806eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1249478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1250478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1251478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 125206eeb2adSek } 125306eeb2adSek 125406eeb2adSek spa_close(spa, FTAG); 125506eeb2adSek kmem_free(hist_buf, size); 125606eeb2adSek return (error); 125706eeb2adSek } 125806eeb2adSek 125955434c77Sek static int 126055434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 126155434c77Sek { 126255434c77Sek int error; 126355434c77Sek 1264b1b8ab34Slling if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)) 126555434c77Sek return (error); 126655434c77Sek 126755434c77Sek return (0); 126855434c77Sek } 126955434c77Sek 1270503ad85cSMatthew Ahrens /* 1271503ad85cSMatthew Ahrens * inputs: 1272503ad85cSMatthew Ahrens * zc_name name of filesystem 1273503ad85cSMatthew Ahrens * zc_obj object to find 1274503ad85cSMatthew Ahrens * 1275503ad85cSMatthew Ahrens * outputs: 1276503ad85cSMatthew Ahrens * zc_value name of object 1277503ad85cSMatthew Ahrens */ 127855434c77Sek static int 127955434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 128055434c77Sek { 1281503ad85cSMatthew Ahrens objset_t *os; 128255434c77Sek int error; 128355434c77Sek 1284503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1285503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 128655434c77Sek return (error); 1287503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1288503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1289503ad85cSMatthew Ahrens return (EINVAL); 1290503ad85cSMatthew Ahrens } 1291503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 129255434c77Sek sizeof (zc->zc_value)); 1293503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 129455434c77Sek 129555434c77Sek return (error); 129655434c77Sek } 129755434c77Sek 1298fa9e4066Sahrens static int 1299fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1300fa9e4066Sahrens { 1301fa9e4066Sahrens spa_t *spa; 1302fa9e4066Sahrens int error; 1303e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1304e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1305fa9e4066Sahrens 1306fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1307fa9e4066Sahrens if (error != 0) 1308fa9e4066Sahrens return (error); 1309fa9e4066Sahrens 1310fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1311478ed9adSEric Taylor zc->zc_iflags, &config); 1312fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1313fa94a07fSbrendan &l2cache, &nl2cache); 1314fa94a07fSbrendan 1315e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1316e7cbe64fSgw &spares, &nspares); 1317e7cbe64fSgw 1318b1b8ab34Slling /* 1319b1b8ab34Slling * A root pool with concatenated devices is not supported. 1320e7cbe64fSgw * Thus, can not add a device to a root pool. 1321e7cbe64fSgw * 1322e7cbe64fSgw * Intent log device can not be added to a rootpool because 1323e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1324e7cbe64fSgw * can not be accessed during the mountroot time. 1325e7cbe64fSgw * 1326e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1327b1b8ab34Slling */ 1328b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 1329b1b8ab34Slling spa_close(spa, FTAG); 1330b1b8ab34Slling return (EDOM); 1331b1b8ab34Slling } 1332b1b8ab34Slling 1333fa94a07fSbrendan if (error == 0) { 1334fa9e4066Sahrens error = spa_vdev_add(spa, config); 1335fa9e4066Sahrens nvlist_free(config); 1336fa9e4066Sahrens } 1337fa9e4066Sahrens spa_close(spa, FTAG); 1338fa9e4066Sahrens return (error); 1339fa9e4066Sahrens } 1340fa9e4066Sahrens 1341fa9e4066Sahrens static int 1342fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1343fa9e4066Sahrens { 134499653d4eSeschrock spa_t *spa; 134599653d4eSeschrock int error; 134699653d4eSeschrock 134799653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 134899653d4eSeschrock if (error != 0) 134999653d4eSeschrock return (error); 135099653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 135199653d4eSeschrock spa_close(spa, FTAG); 135299653d4eSeschrock return (error); 1353fa9e4066Sahrens } 1354fa9e4066Sahrens 1355fa9e4066Sahrens static int 13563d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1357fa9e4066Sahrens { 1358fa9e4066Sahrens spa_t *spa; 1359fa9e4066Sahrens int error; 13603d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1361fa9e4066Sahrens 136206eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1363fa9e4066Sahrens return (error); 13643d7072f8Seschrock switch (zc->zc_cookie) { 13653d7072f8Seschrock case VDEV_STATE_ONLINE: 13663d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 13673d7072f8Seschrock break; 1368fa9e4066Sahrens 13693d7072f8Seschrock case VDEV_STATE_OFFLINE: 13703d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 13713d7072f8Seschrock break; 1372fa9e4066Sahrens 13733d7072f8Seschrock case VDEV_STATE_FAULTED: 1374069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1375069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1376069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1377069f55e2SEric Schrock 1378069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 13793d7072f8Seschrock break; 13803d7072f8Seschrock 13813d7072f8Seschrock case VDEV_STATE_DEGRADED: 1382069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1383069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1384069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1385069f55e2SEric Schrock 1386069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 13873d7072f8Seschrock break; 13883d7072f8Seschrock 13893d7072f8Seschrock default: 13903d7072f8Seschrock error = EINVAL; 13913d7072f8Seschrock } 13923d7072f8Seschrock zc->zc_cookie = newstate; 1393fa9e4066Sahrens spa_close(spa, FTAG); 1394fa9e4066Sahrens return (error); 1395fa9e4066Sahrens } 1396fa9e4066Sahrens 1397fa9e4066Sahrens static int 1398fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1399fa9e4066Sahrens { 1400fa9e4066Sahrens spa_t *spa; 1401fa9e4066Sahrens int replacing = zc->zc_cookie; 1402fa9e4066Sahrens nvlist_t *config; 1403fa9e4066Sahrens int error; 1404fa9e4066Sahrens 140506eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1406fa9e4066Sahrens return (error); 1407fa9e4066Sahrens 1408990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1409478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1410ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1411fa9e4066Sahrens nvlist_free(config); 1412fa9e4066Sahrens } 1413fa9e4066Sahrens 1414fa9e4066Sahrens spa_close(spa, FTAG); 1415fa9e4066Sahrens return (error); 1416fa9e4066Sahrens } 1417fa9e4066Sahrens 1418fa9e4066Sahrens static int 1419fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1420fa9e4066Sahrens { 1421fa9e4066Sahrens spa_t *spa; 1422fa9e4066Sahrens int error; 1423fa9e4066Sahrens 142406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1425fa9e4066Sahrens return (error); 1426fa9e4066Sahrens 14278ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1428fa9e4066Sahrens 1429fa9e4066Sahrens spa_close(spa, FTAG); 1430fa9e4066Sahrens return (error); 1431fa9e4066Sahrens } 1432fa9e4066Sahrens 1433c67d9675Seschrock static int 1434c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 1435c67d9675Seschrock { 1436c67d9675Seschrock spa_t *spa; 1437e9dbad6fSeschrock char *path = zc->zc_value; 1438ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 1439c67d9675Seschrock int error; 1440c67d9675Seschrock 1441c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 1442c67d9675Seschrock if (error != 0) 1443c67d9675Seschrock return (error); 1444c67d9675Seschrock 1445c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 1446c67d9675Seschrock spa_close(spa, FTAG); 1447c67d9675Seschrock return (error); 1448c67d9675Seschrock } 1449c67d9675Seschrock 14506809eb4eSEric Schrock static int 14516809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 14526809eb4eSEric Schrock { 14536809eb4eSEric Schrock spa_t *spa; 14546809eb4eSEric Schrock char *fru = zc->zc_value; 14556809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 14566809eb4eSEric Schrock int error; 14576809eb4eSEric Schrock 14586809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 14596809eb4eSEric Schrock if (error != 0) 14606809eb4eSEric Schrock return (error); 14616809eb4eSEric Schrock 14626809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 14636809eb4eSEric Schrock spa_close(spa, FTAG); 14646809eb4eSEric Schrock return (error); 14656809eb4eSEric Schrock } 14666809eb4eSEric Schrock 14673cb34c60Sahrens /* 14683cb34c60Sahrens * inputs: 14693cb34c60Sahrens * zc_name name of filesystem 14703cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 14713cb34c60Sahrens * 14723cb34c60Sahrens * outputs: 14733cb34c60Sahrens * zc_objset_stats stats 14743cb34c60Sahrens * zc_nvlist_dst property nvlist 14753cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 14763cb34c60Sahrens */ 1477fa9e4066Sahrens static int 1478fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc) 1479fa9e4066Sahrens { 1480fa9e4066Sahrens objset_t *os = NULL; 1481fa9e4066Sahrens int error; 14827f7322feSeschrock nvlist_t *nv; 1483fa9e4066Sahrens 1484503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1485fa9e4066Sahrens return (error); 1486fa9e4066Sahrens 1487a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1488fa9e4066Sahrens 14895ad82045Snd if (zc->zc_nvlist_dst != 0 && 1490745cd3c5Smaybee (error = dsl_prop_get_all(os, &nv, FALSE)) == 0) { 1491a2eea2e1Sahrens dmu_objset_stats(os, nv); 1492432f72fdSahrens /* 1493bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 1494432f72fdSahrens * which we aren't supposed to do with a 1495745cd3c5Smaybee * DS_MODE_USER hold, because it could be 1496432f72fdSahrens * inconsistent. So this is a bit of a workaround... 1497503ad85cSMatthew Ahrens * XXX reading with out owning 1498432f72fdSahrens */ 1499e7437265Sahrens if (!zc->zc_objset_stats.dds_inconsistent) { 1500e7437265Sahrens if (dmu_objset_type(os) == DMU_OST_ZVOL) 1501e7437265Sahrens VERIFY(zvol_get_stats(os, nv) == 0); 1502e7437265Sahrens } 1503e9dbad6fSeschrock error = put_nvlist(zc, nv); 15047f7322feSeschrock nvlist_free(nv); 15057f7322feSeschrock } 1506fa9e4066Sahrens 1507503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1508fa9e4066Sahrens return (error); 1509fa9e4066Sahrens } 1510fa9e4066Sahrens 1511de8267e0Stimh static int 1512de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 1513de8267e0Stimh { 1514de8267e0Stimh uint64_t value; 1515de8267e0Stimh int error; 1516de8267e0Stimh 1517de8267e0Stimh /* 1518de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 1519de8267e0Stimh * the default value (if there is one). 1520de8267e0Stimh */ 1521de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 1522de8267e0Stimh return (error); 1523de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 1524de8267e0Stimh return (0); 1525de8267e0Stimh } 1526de8267e0Stimh 15273cb34c60Sahrens /* 15283cb34c60Sahrens * inputs: 15293cb34c60Sahrens * zc_name name of filesystem 1530de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 15313cb34c60Sahrens * 15323cb34c60Sahrens * outputs: 1533de8267e0Stimh * zc_nvlist_dst zpl property nvlist 1534de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 15353cb34c60Sahrens */ 1536bd00f61bSrm static int 1537de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 1538bd00f61bSrm { 1539de8267e0Stimh objset_t *os; 1540de8267e0Stimh int err; 1541bd00f61bSrm 1542503ad85cSMatthew Ahrens /* XXX reading without owning */ 1543503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1544de8267e0Stimh return (err); 1545bd00f61bSrm 1546bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1547bd00f61bSrm 1548bd00f61bSrm /* 1549de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 1550745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 1551745cd3c5Smaybee * hold, because it could be inconsistent. 1552bd00f61bSrm */ 1553de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 1554de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 1555de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 1556de8267e0Stimh nvlist_t *nv; 1557de8267e0Stimh 1558de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1559de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 1560de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 1561de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 1562de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 1563de8267e0Stimh err = put_nvlist(zc, nv); 1564de8267e0Stimh nvlist_free(nv); 1565de8267e0Stimh } else { 1566de8267e0Stimh err = ENOENT; 1567de8267e0Stimh } 1568503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1569de8267e0Stimh return (err); 1570bd00f61bSrm } 1571bd00f61bSrm 157214843421SMatthew Ahrens static boolean_t 157314843421SMatthew Ahrens dataset_name_hidden(const char *name) 157414843421SMatthew Ahrens { 157514843421SMatthew Ahrens /* 157614843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 157714843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 157814843421SMatthew Ahrens * temporary datasets (which have a % in their name). 157914843421SMatthew Ahrens */ 158014843421SMatthew Ahrens if (strchr(name, '$') != NULL) 158114843421SMatthew Ahrens return (B_TRUE); 158214843421SMatthew Ahrens if (strchr(name, '%') != NULL) 158314843421SMatthew Ahrens return (B_TRUE); 158414843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 158514843421SMatthew Ahrens return (B_TRUE); 158614843421SMatthew Ahrens return (B_FALSE); 158714843421SMatthew Ahrens } 158814843421SMatthew Ahrens 1589de8267e0Stimh /* 1590de8267e0Stimh * inputs: 1591de8267e0Stimh * zc_name name of filesystem 1592de8267e0Stimh * zc_cookie zap cursor 1593de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 1594de8267e0Stimh * 1595de8267e0Stimh * outputs: 1596de8267e0Stimh * zc_name name of next filesystem 159714843421SMatthew Ahrens * zc_cookie zap cursor 1598de8267e0Stimh * zc_objset_stats stats 1599de8267e0Stimh * zc_nvlist_dst property nvlist 1600de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 1601de8267e0Stimh */ 1602fa9e4066Sahrens static int 1603fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 1604fa9e4066Sahrens { 160587e5029aSahrens objset_t *os; 1606fa9e4066Sahrens int error; 1607fa9e4066Sahrens char *p; 1608fa9e4066Sahrens 1609503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 161087e5029aSahrens if (error == ENOENT) 161187e5029aSahrens error = ESRCH; 161287e5029aSahrens return (error); 1613fa9e4066Sahrens } 1614fa9e4066Sahrens 1615fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 1616fa9e4066Sahrens if (p == NULL || p[1] != '\0') 1617fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 1618fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 1619fa9e4066Sahrens 16205c0b6a79SRich Morris /* 16215c0b6a79SRich Morris * Pre-fetch the datasets. dmu_objset_prefetch() always returns 0 16225c0b6a79SRich Morris * but is not declared void because its called by dmu_objset_find(). 16235c0b6a79SRich Morris */ 16247f73c863SRich Morris if (zc->zc_cookie == 0) { 16257f73c863SRich Morris uint64_t cookie = 0; 16267f73c863SRich Morris int len = sizeof (zc->zc_name) - (p - zc->zc_name); 16277f73c863SRich Morris 16287f73c863SRich Morris while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) 16295c0b6a79SRich Morris (void) dmu_objset_prefetch(p, NULL); 16307f73c863SRich Morris } 16317f73c863SRich Morris 1632fa9e4066Sahrens do { 163387e5029aSahrens error = dmu_dir_list_next(os, 163487e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 163587e5029aSahrens NULL, &zc->zc_cookie); 1636fa9e4066Sahrens if (error == ENOENT) 1637fa9e4066Sahrens error = ESRCH; 1638681d9761SEric Taylor } while (error == 0 && dataset_name_hidden(zc->zc_name) && 1639681d9761SEric Taylor !(zc->zc_iflags & FKIOCTL)); 1640503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1641fa9e4066Sahrens 1642681d9761SEric Taylor /* 1643681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 1644681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 1645681d9761SEric Taylor */ 1646681d9761SEric Taylor if (error == 0 && strchr(zc->zc_name, '$') == NULL) 164787e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1648fa9e4066Sahrens return (error); 1649fa9e4066Sahrens } 1650fa9e4066Sahrens 16513cb34c60Sahrens /* 16523cb34c60Sahrens * inputs: 16533cb34c60Sahrens * zc_name name of filesystem 16543cb34c60Sahrens * zc_cookie zap cursor 16553cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 16563cb34c60Sahrens * 16573cb34c60Sahrens * outputs: 16583cb34c60Sahrens * zc_name name of next snapshot 16593cb34c60Sahrens * zc_objset_stats stats 16603cb34c60Sahrens * zc_nvlist_dst property nvlist 16613cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 16623cb34c60Sahrens */ 1663fa9e4066Sahrens static int 1664fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 1665fa9e4066Sahrens { 166687e5029aSahrens objset_t *os; 1667fa9e4066Sahrens int error; 1668fa9e4066Sahrens 16697cbf8b43SRich Morris if (zc->zc_cookie == 0) 16707cbf8b43SRich Morris (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, 16717cbf8b43SRich Morris NULL, DS_FIND_SNAPSHOTS); 16727cbf8b43SRich Morris 1673503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 1674745cd3c5Smaybee if (error) 1675745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 1676fa9e4066Sahrens 1677b81d61a6Slling /* 1678b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 1679b81d61a6Slling * so exit immediately. 1680b81d61a6Slling */ 1681b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 1682503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1683b81d61a6Slling return (ESRCH); 1684fa9e4066Sahrens } 1685fa9e4066Sahrens 168687e5029aSahrens error = dmu_snapshot_list_next(os, 168787e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 1688b38f0970Sck zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL); 1689503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 169087e5029aSahrens if (error == 0) 169187e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 1692745cd3c5Smaybee else if (error == ENOENT) 1693745cd3c5Smaybee error = ESRCH; 1694fa9e4066Sahrens 16953cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 1696745cd3c5Smaybee if (error) 16973cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 1698fa9e4066Sahrens return (error); 1699fa9e4066Sahrens } 1700fa9e4066Sahrens 1701e7cbe64fSgw int 170291ebeef5Sahrens zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) 1703fa9e4066Sahrens { 1704e9dbad6fSeschrock nvpair_t *elem; 17055f389de5SRich Morris int error = 0; 1706e9dbad6fSeschrock uint64_t intval; 1707e9dbad6fSeschrock char *strval; 17085c0b6a79SRich Morris nvlist_t *genericnvl; 170914843421SMatthew Ahrens boolean_t issnap = (strchr(name, '@') != NULL); 1710e9dbad6fSeschrock 1711ecd6cf80Smarks /* 1712ecd6cf80Smarks * First validate permission to set all of the properties 1713ecd6cf80Smarks */ 1714e9dbad6fSeschrock elem = NULL; 1715e9dbad6fSeschrock while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 1716db870a07Sahrens const char *propname = nvpair_name(elem); 1717db870a07Sahrens zfs_prop_t prop = zfs_name_to_prop(propname); 1718e9dbad6fSeschrock 1719990b4856Slling if (prop == ZPROP_INVAL) { 1720e9dbad6fSeschrock /* 1721e9dbad6fSeschrock * If this is a user-defined property, it must be a 1722e9dbad6fSeschrock * string, and there is no further validation to do. 1723e9dbad6fSeschrock */ 172414843421SMatthew Ahrens if (zfs_prop_user(propname) && 172514843421SMatthew Ahrens nvpair_type(elem) == DATA_TYPE_STRING) { 172614843421SMatthew Ahrens if (error = zfs_secpolicy_write_perms(name, 172714843421SMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 172814843421SMatthew Ahrens return (error); 172914843421SMatthew Ahrens continue; 173014843421SMatthew Ahrens } 1731e9dbad6fSeschrock 173214843421SMatthew Ahrens if (!issnap && zfs_prop_userquota(propname) && 173314843421SMatthew Ahrens nvpair_type(elem) == DATA_TYPE_UINT64_ARRAY) { 173414843421SMatthew Ahrens const char *perm; 173514843421SMatthew Ahrens const char *up = zfs_userquota_prop_prefixes 173614843421SMatthew Ahrens [ZFS_PROP_USERQUOTA]; 173714843421SMatthew Ahrens if (strncmp(propname, up, strlen(up)) == 0) 173814843421SMatthew Ahrens perm = ZFS_DELEG_PERM_USERQUOTA; 173914843421SMatthew Ahrens else 174014843421SMatthew Ahrens perm = ZFS_DELEG_PERM_GROUPQUOTA; 174114843421SMatthew Ahrens if (error = zfs_secpolicy_write_perms(name, 174214843421SMatthew Ahrens perm, CRED())) 174314843421SMatthew Ahrens return (error); 174414843421SMatthew Ahrens continue; 174514843421SMatthew Ahrens } 174614843421SMatthew Ahrens 174714843421SMatthew Ahrens return (EINVAL); 1748e9dbad6fSeschrock } 1749fa9e4066Sahrens 175014843421SMatthew Ahrens if (issnap) 175114843421SMatthew Ahrens return (EINVAL); 175214843421SMatthew Ahrens 175391ebeef5Sahrens if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0) 1754db870a07Sahrens return (error); 1755db870a07Sahrens 1756e9dbad6fSeschrock /* 1757db870a07Sahrens * Check that this value is valid for this pool version 1758e9dbad6fSeschrock */ 1759e9dbad6fSeschrock switch (prop) { 1760c9431fa1Sahl case ZFS_PROP_COMPRESSION: 1761c9431fa1Sahl /* 1762c9431fa1Sahl * If the user specified gzip compression, make sure 1763c9431fa1Sahl * the SPA supports it. We ignore any errors here since 1764c9431fa1Sahl * we'll catch them later. 1765c9431fa1Sahl */ 1766c9431fa1Sahl if (nvpair_type(elem) == DATA_TYPE_UINT64 && 176715e6edf1Sgw nvpair_value_uint64(elem, &intval) == 0) { 176815e6edf1Sgw if (intval >= ZIO_COMPRESS_GZIP_1 && 176915e6edf1Sgw intval <= ZIO_COMPRESS_GZIP_9 && 17700a48a24eStimh zfs_earlier_version(name, 1771da6c28aaSamw SPA_VERSION_GZIP_COMPRESSION)) 1772da6c28aaSamw return (ENOTSUP); 177315e6edf1Sgw 1774b24ab676SJeff Bonwick if (intval == ZIO_COMPRESS_ZLE && 1775b24ab676SJeff Bonwick zfs_earlier_version(name, 1776b24ab676SJeff Bonwick SPA_VERSION_ZLE_COMPRESSION)) 1777b24ab676SJeff Bonwick return (ENOTSUP); 1778b24ab676SJeff Bonwick 177915e6edf1Sgw /* 178015e6edf1Sgw * If this is a bootable dataset then 178115e6edf1Sgw * verify that the compression algorithm 178215e6edf1Sgw * is supported for booting. We must return 178315e6edf1Sgw * something other than ENOTSUP since it 178415e6edf1Sgw * implies a downrev pool version. 178515e6edf1Sgw */ 178615e6edf1Sgw if (zfs_is_bootfs(name) && 178715e6edf1Sgw !BOOTFS_COMPRESS_VALID(intval)) 178815e6edf1Sgw return (ERANGE); 1789c9431fa1Sahl } 1790c9431fa1Sahl break; 179140feaa91Sahrens 179240feaa91Sahrens case ZFS_PROP_COPIES: 179314843421SMatthew Ahrens if (zfs_earlier_version(name, SPA_VERSION_DITTO_BLOCKS)) 1794da6c28aaSamw return (ENOTSUP); 179540feaa91Sahrens break; 17969e6eda55Smarks 1797b24ab676SJeff Bonwick case ZFS_PROP_DEDUP: 1798b24ab676SJeff Bonwick if (zfs_earlier_version(name, SPA_VERSION_DEDUP)) 1799b24ab676SJeff Bonwick return (ENOTSUP); 1800b24ab676SJeff Bonwick break; 1801b24ab676SJeff Bonwick 18029e6eda55Smarks case ZFS_PROP_SHARESMB: 1803745cd3c5Smaybee if (zpl_earlier_version(name, ZPL_VERSION_FUID)) 18049e6eda55Smarks return (ENOTSUP); 18059e6eda55Smarks break; 1806d0f3f37eSMark Shellenbaum 1807d0f3f37eSMark Shellenbaum case ZFS_PROP_ACLINHERIT: 1808d0f3f37eSMark Shellenbaum if (nvpair_type(elem) == DATA_TYPE_UINT64 && 1809d0f3f37eSMark Shellenbaum nvpair_value_uint64(elem, &intval) == 0) 1810d0f3f37eSMark Shellenbaum if (intval == ZFS_ACL_PASSTHROUGH_X && 1811d0f3f37eSMark Shellenbaum zfs_earlier_version(name, 1812d0f3f37eSMark Shellenbaum SPA_VERSION_PASSTHROUGH_X)) 1813d0f3f37eSMark Shellenbaum return (ENOTSUP); 181440feaa91Sahrens } 1815ecd6cf80Smarks } 1816ecd6cf80Smarks 18175c0b6a79SRich Morris VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); 1818ecd6cf80Smarks elem = NULL; 1819ecd6cf80Smarks while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 1820db870a07Sahrens const char *propname = nvpair_name(elem); 1821db870a07Sahrens zfs_prop_t prop = zfs_name_to_prop(propname); 1822ecd6cf80Smarks 1823990b4856Slling if (prop == ZPROP_INVAL) { 182414843421SMatthew Ahrens if (zfs_prop_userquota(propname)) { 182514843421SMatthew Ahrens uint64_t *valary; 182614843421SMatthew Ahrens unsigned int vallen; 182714843421SMatthew Ahrens const char *domain; 182814843421SMatthew Ahrens zfs_userquota_prop_t type; 182914843421SMatthew Ahrens uint64_t rid; 183014843421SMatthew Ahrens uint64_t quota; 183114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 183214843421SMatthew Ahrens 183314843421SMatthew Ahrens VERIFY(nvpair_value_uint64_array(elem, 183414843421SMatthew Ahrens &valary, &vallen) == 0); 183514843421SMatthew Ahrens VERIFY(vallen == 3); 183614843421SMatthew Ahrens type = valary[0]; 183714843421SMatthew Ahrens rid = valary[1]; 183814843421SMatthew Ahrens quota = valary[2]; 18392d5843dbSMatthew Ahrens /* 18402d5843dbSMatthew Ahrens * The propname is encoded as 18412d5843dbSMatthew Ahrens * userquota@<rid>-<domain>. 18422d5843dbSMatthew Ahrens */ 18432d5843dbSMatthew Ahrens domain = strchr(propname, '-') + 1; 184414843421SMatthew Ahrens 1845503ad85cSMatthew Ahrens error = zfsvfs_hold(name, FTAG, &zfsvfs); 184614843421SMatthew Ahrens if (error == 0) { 184714843421SMatthew Ahrens error = zfs_set_userquota(zfsvfs, 184814843421SMatthew Ahrens type, domain, rid, quota); 184914843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 185014843421SMatthew Ahrens } 185114843421SMatthew Ahrens if (error == 0) 185214843421SMatthew Ahrens continue; 185314843421SMatthew Ahrens else 185414843421SMatthew Ahrens goto out; 185514843421SMatthew Ahrens } else if (zfs_prop_user(propname)) { 185614843421SMatthew Ahrens VERIFY(nvpair_value_string(elem, &strval) == 0); 185714843421SMatthew Ahrens error = dsl_prop_set(name, propname, 1, 185814843421SMatthew Ahrens strlen(strval) + 1, strval); 185914843421SMatthew Ahrens if (error == 0) 186014843421SMatthew Ahrens continue; 186114843421SMatthew Ahrens else 186214843421SMatthew Ahrens goto out; 186314843421SMatthew Ahrens } 1864ecd6cf80Smarks } 1865e9dbad6fSeschrock 1866e9dbad6fSeschrock switch (prop) { 1867e9dbad6fSeschrock case ZFS_PROP_QUOTA: 1868e9dbad6fSeschrock if ((error = nvpair_value_uint64(elem, &intval)) != 0 || 1869e7437265Sahrens (error = dsl_dir_set_quota(name, intval)) != 0) 18705c0b6a79SRich Morris goto out; 1871e9dbad6fSeschrock break; 1872e9dbad6fSeschrock 1873a9799022Sck case ZFS_PROP_REFQUOTA: 1874a9799022Sck if ((error = nvpair_value_uint64(elem, &intval)) != 0 || 1875a9799022Sck (error = dsl_dataset_set_quota(name, intval)) != 0) 18765c0b6a79SRich Morris goto out; 1877a9799022Sck break; 1878a9799022Sck 1879e9dbad6fSeschrock case ZFS_PROP_RESERVATION: 1880e9dbad6fSeschrock if ((error = nvpair_value_uint64(elem, &intval)) != 0 || 1881e9dbad6fSeschrock (error = dsl_dir_set_reservation(name, 1882e9dbad6fSeschrock intval)) != 0) 18835c0b6a79SRich Morris goto out; 1884e9dbad6fSeschrock break; 1885e9dbad6fSeschrock 1886a9799022Sck case ZFS_PROP_REFRESERVATION: 1887a9799022Sck if ((error = nvpair_value_uint64(elem, &intval)) != 0 || 1888a9799022Sck (error = dsl_dataset_set_reservation(name, 1889a9799022Sck intval)) != 0) 18905c0b6a79SRich Morris goto out; 1891a9799022Sck break; 1892a9799022Sck 1893e9dbad6fSeschrock case ZFS_PROP_VOLSIZE: 1894e9dbad6fSeschrock if ((error = nvpair_value_uint64(elem, &intval)) != 0 || 189591ebeef5Sahrens (error = zvol_set_volsize(name, 189691ebeef5Sahrens ddi_driver_major(zfs_dip), intval)) != 0) 18975c0b6a79SRich Morris goto out; 1898e9dbad6fSeschrock break; 1899e9dbad6fSeschrock 1900e7437265Sahrens case ZFS_PROP_VERSION: 190114843421SMatthew Ahrens { 190214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 190314843421SMatthew Ahrens 190414843421SMatthew Ahrens if ((error = nvpair_value_uint64(elem, &intval)) != 0) 190514843421SMatthew Ahrens goto out; 1906503ad85cSMatthew Ahrens if ((error = zfsvfs_hold(name, FTAG, &zfsvfs)) != 0) 190714843421SMatthew Ahrens goto out; 190814843421SMatthew Ahrens error = zfs_set_version(zfsvfs, intval); 190914843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 191014843421SMatthew Ahrens 191114843421SMatthew Ahrens if (error == 0 && intval >= ZPL_VERSION_USERSPACE) { 191214843421SMatthew Ahrens zfs_cmd_t zc = { 0 }; 191314843421SMatthew Ahrens (void) strcpy(zc.zc_name, name); 191414843421SMatthew Ahrens (void) zfs_ioc_userspace_upgrade(&zc); 191514843421SMatthew Ahrens } 191614843421SMatthew Ahrens if (error) 19175c0b6a79SRich Morris goto out; 1918e9dbad6fSeschrock break; 191914843421SMatthew Ahrens } 1920e9dbad6fSeschrock 1921*4201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 1922*4201a95eSRic Aleshire { 1923*4201a95eSRic Aleshire objset_t *os = NULL; 1924*4201a95eSRic Aleshire 1925*4201a95eSRic Aleshire if ((error = nvpair_value_string(elem, &strval)) != 0) 1926*4201a95eSRic Aleshire goto out; 1927*4201a95eSRic Aleshire if ((error = zfs_set_slabel_policy(name, strval, 1928*4201a95eSRic Aleshire CRED(), &os)) != 0) { 1929*4201a95eSRic Aleshire /* error; first release the dataset if needed */ 1930*4201a95eSRic Aleshire if (os) 1931*4201a95eSRic Aleshire dmu_objset_disown(os, setsl_tag); 1932*4201a95eSRic Aleshire goto out; 1933*4201a95eSRic Aleshire } 1934*4201a95eSRic Aleshire 1935*4201a95eSRic Aleshire error = nvlist_add_nvpair(genericnvl, elem); 1936*4201a95eSRic Aleshire if (os) 1937*4201a95eSRic Aleshire dmu_objset_disown(os, setsl_tag); 1938*4201a95eSRic Aleshire if (error != 0) 1939*4201a95eSRic Aleshire goto out; 1940*4201a95eSRic Aleshire break; 1941*4201a95eSRic Aleshire } 1942*4201a95eSRic Aleshire 1943e9dbad6fSeschrock default: 1944e9dbad6fSeschrock if (nvpair_type(elem) == DATA_TYPE_STRING) { 1945e9dbad6fSeschrock if (zfs_prop_get_type(prop) != 19465c0b6a79SRich Morris PROP_TYPE_STRING) { 19475c0b6a79SRich Morris error = EINVAL; 19485c0b6a79SRich Morris goto out; 19495c0b6a79SRich Morris } 1950e9dbad6fSeschrock } else if (nvpair_type(elem) == DATA_TYPE_UINT64) { 1951a2eea2e1Sahrens const char *unused; 1952a2eea2e1Sahrens 1953acd76fe5Seschrock VERIFY(nvpair_value_uint64(elem, &intval) == 0); 1954e9dbad6fSeschrock 1955e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 195691ebeef5Sahrens case PROP_TYPE_NUMBER: 1957e9dbad6fSeschrock break; 195891ebeef5Sahrens case PROP_TYPE_STRING: 19595c0b6a79SRich Morris error = EINVAL; 19605c0b6a79SRich Morris goto out; 196191ebeef5Sahrens case PROP_TYPE_INDEX: 1962acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 19635c0b6a79SRich Morris intval, &unused) != 0) { 19645c0b6a79SRich Morris error = EINVAL; 19655c0b6a79SRich Morris goto out; 19665c0b6a79SRich Morris } 1967e9dbad6fSeschrock break; 1968e9dbad6fSeschrock default: 1969e7437265Sahrens cmn_err(CE_PANIC, 1970e7437265Sahrens "unknown property type"); 1971e9dbad6fSeschrock break; 1972e9dbad6fSeschrock } 1973e9dbad6fSeschrock } else { 19745c0b6a79SRich Morris error = EINVAL; 19755c0b6a79SRich Morris goto out; 1976e9dbad6fSeschrock } 19775c0b6a79SRich Morris if ((error = nvlist_add_nvpair(genericnvl, elem)) != 0) 19785c0b6a79SRich Morris goto out; 1979e9dbad6fSeschrock } 1980e9dbad6fSeschrock } 1981e9dbad6fSeschrock 19825c0b6a79SRich Morris if (nvlist_next_nvpair(genericnvl, NULL) != NULL) { 19835c0b6a79SRich Morris error = dsl_props_set(name, genericnvl); 19845c0b6a79SRich Morris } 19855c0b6a79SRich Morris out: 19865c0b6a79SRich Morris nvlist_free(genericnvl); 19875c0b6a79SRich Morris return (error); 1988fa9e4066Sahrens } 1989fa9e4066Sahrens 1990ea2f5b9eSMatthew Ahrens /* 1991ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 1992ea2f5b9eSMatthew Ahrens */ 1993ea2f5b9eSMatthew Ahrens static int 1994ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl) 1995ea2f5b9eSMatthew Ahrens { 1996ea2f5b9eSMatthew Ahrens nvpair_t *elem = NULL; 1997ea2f5b9eSMatthew Ahrens int error = 0; 1998ea2f5b9eSMatthew Ahrens 1999ea2f5b9eSMatthew Ahrens while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) { 2000ea2f5b9eSMatthew Ahrens const char *propname = nvpair_name(elem); 2001ea2f5b9eSMatthew Ahrens char *valstr; 2002ea2f5b9eSMatthew Ahrens 2003ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 2004ea2f5b9eSMatthew Ahrens nvpair_type(elem) != DATA_TYPE_STRING) 2005ea2f5b9eSMatthew Ahrens return (EINVAL); 2006ea2f5b9eSMatthew Ahrens 2007ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2008ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2009ea2f5b9eSMatthew Ahrens return (error); 2010ea2f5b9eSMatthew Ahrens 2011ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2012ea2f5b9eSMatthew Ahrens return (ENAMETOOLONG); 2013ea2f5b9eSMatthew Ahrens 2014ea2f5b9eSMatthew Ahrens VERIFY(nvpair_value_string(elem, &valstr) == 0); 2015ea2f5b9eSMatthew Ahrens if (strlen(valstr) >= ZAP_MAXVALUELEN) 2016ea2f5b9eSMatthew Ahrens return (E2BIG); 2017ea2f5b9eSMatthew Ahrens } 2018ea2f5b9eSMatthew Ahrens return (0); 2019ea2f5b9eSMatthew Ahrens } 2020ea2f5b9eSMatthew Ahrens 20213cb34c60Sahrens /* 20223cb34c60Sahrens * inputs: 20233cb34c60Sahrens * zc_name name of filesystem 20245c0b6a79SRich Morris * zc_value name of property to set 20253cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 2026bb0ade09Sahrens * zc_cookie clear existing local props? 20273cb34c60Sahrens * 20283cb34c60Sahrens * outputs: none 20293cb34c60Sahrens */ 2030fa9e4066Sahrens static int 2031e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2032fa9e4066Sahrens { 2033e9dbad6fSeschrock nvlist_t *nvl; 2034e9dbad6fSeschrock int error; 2035e9dbad6fSeschrock 2036990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2037478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2038e9dbad6fSeschrock return (error); 2039e9dbad6fSeschrock 2040bb0ade09Sahrens if (zc->zc_cookie) { 2041bb0ade09Sahrens nvlist_t *origprops; 2042bb0ade09Sahrens objset_t *os; 2043bb0ade09Sahrens 2044503ad85cSMatthew Ahrens if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) { 2045bb0ade09Sahrens if (dsl_prop_get_all(os, &origprops, TRUE) == 0) { 20466e77af0aSDavid Pacheco clear_props(zc->zc_name, origprops, nvl); 2047bb0ade09Sahrens nvlist_free(origprops); 2048bb0ade09Sahrens } 2049503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2050bb0ade09Sahrens } 2051bb0ade09Sahrens 2052bb0ade09Sahrens } 2053bb0ade09Sahrens 205491ebeef5Sahrens error = zfs_set_prop_nvlist(zc->zc_name, nvl); 2055ecd6cf80Smarks 2056e9dbad6fSeschrock nvlist_free(nvl); 2057e9dbad6fSeschrock return (error); 2058fa9e4066Sahrens } 2059fa9e4066Sahrens 20603cb34c60Sahrens /* 20613cb34c60Sahrens * inputs: 20623cb34c60Sahrens * zc_name name of filesystem 20633cb34c60Sahrens * zc_value name of property to inherit 20643cb34c60Sahrens * 20653cb34c60Sahrens * outputs: none 20663cb34c60Sahrens */ 2067e45ce728Sahrens static int 2068e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2069e45ce728Sahrens { 2070e45ce728Sahrens /* the property name has been validated by zfs_secpolicy_inherit() */ 2071e45ce728Sahrens return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL)); 2072e45ce728Sahrens } 2073e45ce728Sahrens 2074b1b8ab34Slling static int 207511a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2076b1b8ab34Slling { 2077990b4856Slling nvlist_t *props; 2078b1b8ab34Slling spa_t *spa; 2079990b4856Slling int error; 2080379c004dSEric Schrock nvpair_t *elem; 2081b1b8ab34Slling 2082990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2083478ed9adSEric Taylor zc->zc_iflags, &props))) 2084b1b8ab34Slling return (error); 2085b1b8ab34Slling 2086379c004dSEric Schrock /* 2087379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2088379c004dSEric Schrock * to handle the faulted case. 2089379c004dSEric Schrock */ 2090379c004dSEric Schrock elem = nvlist_next_nvpair(props, NULL); 2091379c004dSEric Schrock if (elem != NULL && strcmp(nvpair_name(elem), 2092379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 2093379c004dSEric Schrock nvlist_next_nvpair(props, elem) == NULL) { 2094379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2095379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2096379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2097379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2098379c004dSEric Schrock } 2099379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2100b693757aSEric Schrock if (spa != NULL) { 2101b693757aSEric Schrock nvlist_free(props); 2102379c004dSEric Schrock return (0); 2103b693757aSEric Schrock } 2104379c004dSEric Schrock } 2105379c004dSEric Schrock 2106b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2107990b4856Slling nvlist_free(props); 2108b1b8ab34Slling return (error); 2109b1b8ab34Slling } 2110b1b8ab34Slling 2111990b4856Slling error = spa_prop_set(spa, props); 2112b1b8ab34Slling 2113990b4856Slling nvlist_free(props); 2114b1b8ab34Slling spa_close(spa, FTAG); 2115b1b8ab34Slling 2116b1b8ab34Slling return (error); 2117b1b8ab34Slling } 2118b1b8ab34Slling 2119b1b8ab34Slling static int 212011a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2121b1b8ab34Slling { 2122b1b8ab34Slling spa_t *spa; 2123b1b8ab34Slling int error; 2124b1b8ab34Slling nvlist_t *nvp = NULL; 2125b1b8ab34Slling 2126379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2127379c004dSEric Schrock /* 2128379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2129379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2130379c004dSEric Schrock * anyway. 2131379c004dSEric Schrock */ 2132379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2133379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2134379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2135379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2136379c004dSEric Schrock } else { 2137379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2138379c004dSEric Schrock spa_close(spa, FTAG); 2139379c004dSEric Schrock } 2140b1b8ab34Slling 2141b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2142b1b8ab34Slling error = put_nvlist(zc, nvp); 2143b1b8ab34Slling else 2144b1b8ab34Slling error = EFAULT; 2145b1b8ab34Slling 2146379c004dSEric Schrock nvlist_free(nvp); 2147b1b8ab34Slling return (error); 2148b1b8ab34Slling } 2149b1b8ab34Slling 2150ecd6cf80Smarks static int 2151ecd6cf80Smarks zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc) 2152ecd6cf80Smarks { 2153ecd6cf80Smarks nvlist_t *nvp; 2154ecd6cf80Smarks int error; 2155ecd6cf80Smarks uint32_t uid; 2156ecd6cf80Smarks uint32_t gid; 2157ecd6cf80Smarks uint32_t *groups; 2158ecd6cf80Smarks uint_t group_cnt; 2159ecd6cf80Smarks cred_t *usercred; 2160ecd6cf80Smarks 2161990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2162478ed9adSEric Taylor zc->zc_iflags, &nvp)) != 0) { 2163ecd6cf80Smarks return (error); 2164ecd6cf80Smarks } 2165ecd6cf80Smarks 2166ecd6cf80Smarks if ((error = nvlist_lookup_uint32(nvp, 2167ecd6cf80Smarks ZFS_DELEG_PERM_UID, &uid)) != 0) { 2168ecd6cf80Smarks nvlist_free(nvp); 2169ecd6cf80Smarks return (EPERM); 2170ecd6cf80Smarks } 2171ecd6cf80Smarks 2172ecd6cf80Smarks if ((error = nvlist_lookup_uint32(nvp, 2173ecd6cf80Smarks ZFS_DELEG_PERM_GID, &gid)) != 0) { 2174ecd6cf80Smarks nvlist_free(nvp); 2175ecd6cf80Smarks return (EPERM); 2176ecd6cf80Smarks } 2177ecd6cf80Smarks 2178ecd6cf80Smarks if ((error = nvlist_lookup_uint32_array(nvp, ZFS_DELEG_PERM_GROUPS, 2179ecd6cf80Smarks &groups, &group_cnt)) != 0) { 2180ecd6cf80Smarks nvlist_free(nvp); 2181ecd6cf80Smarks return (EPERM); 2182ecd6cf80Smarks } 2183ecd6cf80Smarks usercred = cralloc(); 2184ecd6cf80Smarks if ((crsetugid(usercred, uid, gid) != 0) || 2185ecd6cf80Smarks (crsetgroups(usercred, group_cnt, (gid_t *)groups) != 0)) { 2186ecd6cf80Smarks nvlist_free(nvp); 2187ecd6cf80Smarks crfree(usercred); 2188ecd6cf80Smarks return (EPERM); 2189ecd6cf80Smarks } 2190ecd6cf80Smarks nvlist_free(nvp); 2191ecd6cf80Smarks error = dsl_deleg_access(zc->zc_name, 219291ebeef5Sahrens zfs_prop_to_name(ZFS_PROP_SHAREISCSI), usercred); 2193ecd6cf80Smarks crfree(usercred); 2194ecd6cf80Smarks return (error); 2195ecd6cf80Smarks } 2196ecd6cf80Smarks 21973cb34c60Sahrens /* 21983cb34c60Sahrens * inputs: 21993cb34c60Sahrens * zc_name name of filesystem 22003cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 22013cb34c60Sahrens * zc_perm_action allow/unallow flag 22023cb34c60Sahrens * 22033cb34c60Sahrens * outputs: none 22043cb34c60Sahrens */ 2205ecd6cf80Smarks static int 2206ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2207ecd6cf80Smarks { 2208ecd6cf80Smarks int error; 2209ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2210ecd6cf80Smarks 2211990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2212478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2213ecd6cf80Smarks return (error); 2214ecd6cf80Smarks 2215ecd6cf80Smarks /* 2216ecd6cf80Smarks * Verify nvlist is constructed correctly 2217ecd6cf80Smarks */ 2218ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2219ecd6cf80Smarks nvlist_free(fsaclnv); 2220ecd6cf80Smarks return (EINVAL); 2221ecd6cf80Smarks } 2222ecd6cf80Smarks 2223ecd6cf80Smarks /* 2224ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2225ecd6cf80Smarks * that user is allowed to hand out each permission in 2226ecd6cf80Smarks * the nvlist(s) 2227ecd6cf80Smarks */ 2228ecd6cf80Smarks 222991ebeef5Sahrens error = secpolicy_zfs(CRED()); 2230ecd6cf80Smarks if (error) { 223191ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 223291ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 223391ebeef5Sahrens fsaclnv, CRED()); 223491ebeef5Sahrens } else { 223591ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 223691ebeef5Sahrens fsaclnv, CRED()); 223791ebeef5Sahrens } 2238ecd6cf80Smarks } 2239ecd6cf80Smarks 2240ecd6cf80Smarks if (error == 0) 2241ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2242ecd6cf80Smarks 2243ecd6cf80Smarks nvlist_free(fsaclnv); 2244ecd6cf80Smarks return (error); 2245ecd6cf80Smarks } 2246ecd6cf80Smarks 22473cb34c60Sahrens /* 22483cb34c60Sahrens * inputs: 22493cb34c60Sahrens * zc_name name of filesystem 22503cb34c60Sahrens * 22513cb34c60Sahrens * outputs: 22523cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 22533cb34c60Sahrens */ 2254ecd6cf80Smarks static int 2255ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2256ecd6cf80Smarks { 2257ecd6cf80Smarks nvlist_t *nvp; 2258ecd6cf80Smarks int error; 2259ecd6cf80Smarks 2260ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2261ecd6cf80Smarks error = put_nvlist(zc, nvp); 2262ecd6cf80Smarks nvlist_free(nvp); 2263ecd6cf80Smarks } 2264ecd6cf80Smarks 2265ecd6cf80Smarks return (error); 2266ecd6cf80Smarks } 2267ecd6cf80Smarks 2268fa9e4066Sahrens /* 2269fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2270fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2271fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2272fa9e4066Sahrens */ 2273fa9e4066Sahrens static vfs_t * 2274fa9e4066Sahrens zfs_get_vfs(const char *resource) 2275fa9e4066Sahrens { 2276fa9e4066Sahrens struct vfs *vfsp; 2277fa9e4066Sahrens struct vfs *vfs_found = NULL; 2278fa9e4066Sahrens 2279fa9e4066Sahrens vfs_list_read_lock(); 2280fa9e4066Sahrens vfsp = rootvfs; 2281fa9e4066Sahrens do { 2282fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2283fa9e4066Sahrens VFS_HOLD(vfsp); 2284fa9e4066Sahrens vfs_found = vfsp; 2285fa9e4066Sahrens break; 2286fa9e4066Sahrens } 2287fa9e4066Sahrens vfsp = vfsp->vfs_next; 2288fa9e4066Sahrens } while (vfsp != rootvfs); 2289fa9e4066Sahrens vfs_list_unlock(); 2290fa9e4066Sahrens return (vfs_found); 2291fa9e4066Sahrens } 2292fa9e4066Sahrens 2293ecd6cf80Smarks /* ARGSUSED */ 2294fa9e4066Sahrens static void 2295ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2296fa9e4066Sahrens { 2297da6c28aaSamw zfs_creat_t *zct = arg; 2298da6c28aaSamw 2299de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2300da6c28aaSamw } 2301da6c28aaSamw 2302de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2303da6c28aaSamw 2304da6c28aaSamw /* 2305de8267e0Stimh * inputs: 23060a48a24eStimh * createprops list of properties requested by creator 23070a48a24eStimh * default_zplver zpl version to use if unspecified in createprops 23080a48a24eStimh * fuids_ok fuids allowed in this version of the spa? 23090a48a24eStimh * os parent objset pointer (NULL if root fs) 2310de8267e0Stimh * 2311de8267e0Stimh * outputs: 2312de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 23130a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 2314da6c28aaSamw * 2315de8267e0Stimh * Determine the settings for utf8only, normalization and 2316de8267e0Stimh * casesensitivity. Specific values may have been requested by the 2317de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 2318de8267e0Stimh * the file system is of too early a vintage, a creator can not 2319de8267e0Stimh * request settings for these properties, even if the requested 2320de8267e0Stimh * setting is the default value. We don't actually want to create dsl 2321de8267e0Stimh * properties for these, so remove them from the source nvlist after 2322de8267e0Stimh * processing. 2323da6c28aaSamw */ 2324da6c28aaSamw static int 232514843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 23260a48a24eStimh boolean_t fuids_ok, nvlist_t *createprops, nvlist_t *zplprops, 23270a48a24eStimh boolean_t *is_ci) 2328da6c28aaSamw { 2329de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 2330de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 2331de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 2332da6c28aaSamw 2333de8267e0Stimh ASSERT(zplprops != NULL); 2334da6c28aaSamw 2335de8267e0Stimh /* 2336de8267e0Stimh * Pull out creator prop choices, if any. 2337de8267e0Stimh */ 2338de8267e0Stimh if (createprops) { 23390a48a24eStimh (void) nvlist_lookup_uint64(createprops, 23400a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 2341de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2342de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 2343de8267e0Stimh (void) nvlist_remove_all(createprops, 2344de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 2345de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2346de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 2347de8267e0Stimh (void) nvlist_remove_all(createprops, 2348de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 2349de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2350de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 2351de8267e0Stimh (void) nvlist_remove_all(createprops, 2352de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 2353de8267e0Stimh } 2354da6c28aaSamw 2355c2a93d44Stimh /* 23560a48a24eStimh * If the zpl version requested is whacky or the file system 23570a48a24eStimh * or pool is version is too "young" to support normalization 23580a48a24eStimh * and the creator tried to set a value for one of the props, 23590a48a24eStimh * error out. 2360c2a93d44Stimh */ 23610a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 23620a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 23630a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 2364de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 23650a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 2366de8267e0Stimh return (ENOTSUP); 2367c2a93d44Stimh 2368de8267e0Stimh /* 2369de8267e0Stimh * Put the version in the zplprops 2370de8267e0Stimh */ 2371de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2372de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 2373da6c28aaSamw 2374de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 2375de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 2376de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2377de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 2378da6c28aaSamw 2379c2a93d44Stimh /* 2380de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 2381c2a93d44Stimh */ 2382de8267e0Stimh if (norm) 2383de8267e0Stimh u8 = 1; 2384de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 2385de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 2386de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2387de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 2388de8267e0Stimh 2389de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 2390de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 2391de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2392de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 2393c2a93d44Stimh 2394ab04eb8eStimh if (is_ci) 2395ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 2396ab04eb8eStimh 2397da6c28aaSamw return (0); 2398fa9e4066Sahrens } 2399fa9e4066Sahrens 24000a48a24eStimh static int 24010a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 24020a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 24030a48a24eStimh { 24040a48a24eStimh boolean_t fuids_ok = B_TRUE; 24050a48a24eStimh uint64_t zplver = ZPL_VERSION; 24060a48a24eStimh objset_t *os = NULL; 24070a48a24eStimh char parentname[MAXNAMELEN]; 24080a48a24eStimh char *cp; 24090a48a24eStimh int error; 24100a48a24eStimh 24110a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 24120a48a24eStimh cp = strrchr(parentname, '/'); 24130a48a24eStimh ASSERT(cp != NULL); 24140a48a24eStimh cp[0] = '\0'; 24150a48a24eStimh 241614843421SMatthew Ahrens if (zfs_earlier_version(dataset, SPA_VERSION_USERSPACE)) 241714843421SMatthew Ahrens zplver = ZPL_VERSION_USERSPACE - 1; 24180a48a24eStimh if (zfs_earlier_version(dataset, SPA_VERSION_FUID)) { 24190a48a24eStimh zplver = ZPL_VERSION_FUID - 1; 24200a48a24eStimh fuids_ok = B_FALSE; 24210a48a24eStimh } 24220a48a24eStimh 24230a48a24eStimh /* 24240a48a24eStimh * Open parent object set so we can inherit zplprop values. 24250a48a24eStimh */ 2426503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 24270a48a24eStimh return (error); 24280a48a24eStimh 24290a48a24eStimh error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, createprops, 24300a48a24eStimh zplprops, is_ci); 2431503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 24320a48a24eStimh return (error); 24330a48a24eStimh } 24340a48a24eStimh 24350a48a24eStimh static int 24360a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 24370a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 24380a48a24eStimh { 24390a48a24eStimh boolean_t fuids_ok = B_TRUE; 24400a48a24eStimh uint64_t zplver = ZPL_VERSION; 24410a48a24eStimh int error; 24420a48a24eStimh 24430a48a24eStimh if (spa_vers < SPA_VERSION_FUID) { 24440a48a24eStimh zplver = ZPL_VERSION_FUID - 1; 24450a48a24eStimh fuids_ok = B_FALSE; 24460a48a24eStimh } 24470a48a24eStimh 24480a48a24eStimh error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, createprops, 24490a48a24eStimh zplprops, is_ci); 24500a48a24eStimh return (error); 24510a48a24eStimh } 24520a48a24eStimh 24533cb34c60Sahrens /* 24543cb34c60Sahrens * inputs: 24553cb34c60Sahrens * zc_objset_type type of objset to create (fs vs zvol) 24563cb34c60Sahrens * zc_name name of new objset 24573cb34c60Sahrens * zc_value name of snapshot to clone from (may be empty) 24583cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 24593cb34c60Sahrens * 2460de8267e0Stimh * outputs: none 24613cb34c60Sahrens */ 2462fa9e4066Sahrens static int 2463fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc) 2464fa9e4066Sahrens { 2465fa9e4066Sahrens objset_t *clone; 2466fa9e4066Sahrens int error = 0; 2467da6c28aaSamw zfs_creat_t zct; 2468ecd6cf80Smarks nvlist_t *nvprops = NULL; 2469ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 2470fa9e4066Sahrens dmu_objset_type_t type = zc->zc_objset_type; 2471fa9e4066Sahrens 2472fa9e4066Sahrens switch (type) { 2473fa9e4066Sahrens 2474fa9e4066Sahrens case DMU_OST_ZFS: 2475fa9e4066Sahrens cbfunc = zfs_create_cb; 2476fa9e4066Sahrens break; 2477fa9e4066Sahrens 2478fa9e4066Sahrens case DMU_OST_ZVOL: 2479fa9e4066Sahrens cbfunc = zvol_create_cb; 2480fa9e4066Sahrens break; 2481fa9e4066Sahrens 2482fa9e4066Sahrens default: 24831d452cf5Sahrens cbfunc = NULL; 2484e7cbe64fSgw break; 2485fa9e4066Sahrens } 2486f18faf3fSek if (strchr(zc->zc_name, '@') || 2487f18faf3fSek strchr(zc->zc_name, '%')) 24881d452cf5Sahrens return (EINVAL); 2489fa9e4066Sahrens 2490e9dbad6fSeschrock if (zc->zc_nvlist_src != NULL && 2491990b4856Slling (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2492478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2493e9dbad6fSeschrock return (error); 2494e9dbad6fSeschrock 2495de8267e0Stimh zct.zct_zplprops = NULL; 2496da6c28aaSamw zct.zct_props = nvprops; 2497da6c28aaSamw 2498e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 2499fa9e4066Sahrens /* 2500fa9e4066Sahrens * We're creating a clone of an existing snapshot. 2501fa9e4066Sahrens */ 2502e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 2503e9dbad6fSeschrock if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) { 2504ecd6cf80Smarks nvlist_free(nvprops); 2505fa9e4066Sahrens return (EINVAL); 2506e9dbad6fSeschrock } 2507fa9e4066Sahrens 2508503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_value, FTAG, &clone); 2509e9dbad6fSeschrock if (error) { 2510ecd6cf80Smarks nvlist_free(nvprops); 2511fa9e4066Sahrens return (error); 2512e9dbad6fSeschrock } 2513ab04eb8eStimh 2514ae46e4c7SMatthew Ahrens error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0); 2515503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 2516da6c28aaSamw if (error) { 2517da6c28aaSamw nvlist_free(nvprops); 2518da6c28aaSamw return (error); 2519da6c28aaSamw } 2520fa9e4066Sahrens } else { 2521ab04eb8eStimh boolean_t is_insensitive = B_FALSE; 2522ab04eb8eStimh 2523e9dbad6fSeschrock if (cbfunc == NULL) { 2524ecd6cf80Smarks nvlist_free(nvprops); 25251d452cf5Sahrens return (EINVAL); 2526e9dbad6fSeschrock } 25275c5460e9Seschrock 2528e9dbad6fSeschrock if (type == DMU_OST_ZVOL) { 2529e9dbad6fSeschrock uint64_t volsize, volblocksize; 2530e9dbad6fSeschrock 2531ecd6cf80Smarks if (nvprops == NULL || 2532ecd6cf80Smarks nvlist_lookup_uint64(nvprops, 2533e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 2534e9dbad6fSeschrock &volsize) != 0) { 2535ecd6cf80Smarks nvlist_free(nvprops); 2536e9dbad6fSeschrock return (EINVAL); 2537e9dbad6fSeschrock } 2538e9dbad6fSeschrock 2539ecd6cf80Smarks if ((error = nvlist_lookup_uint64(nvprops, 2540e9dbad6fSeschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 2541e9dbad6fSeschrock &volblocksize)) != 0 && error != ENOENT) { 2542ecd6cf80Smarks nvlist_free(nvprops); 2543e9dbad6fSeschrock return (EINVAL); 2544e9dbad6fSeschrock } 2545e9dbad6fSeschrock 2546e9dbad6fSeschrock if (error != 0) 2547e9dbad6fSeschrock volblocksize = zfs_prop_default_numeric( 2548e9dbad6fSeschrock ZFS_PROP_VOLBLOCKSIZE); 2549e9dbad6fSeschrock 2550e9dbad6fSeschrock if ((error = zvol_check_volblocksize( 2551e9dbad6fSeschrock volblocksize)) != 0 || 2552e9dbad6fSeschrock (error = zvol_check_volsize(volsize, 2553e9dbad6fSeschrock volblocksize)) != 0) { 2554ecd6cf80Smarks nvlist_free(nvprops); 25555c5460e9Seschrock return (error); 2556e9dbad6fSeschrock } 2557e7437265Sahrens } else if (type == DMU_OST_ZFS) { 2558da6c28aaSamw int error; 2559da6c28aaSamw 2560da6c28aaSamw /* 2561da6c28aaSamw * We have to have normalization and 2562da6c28aaSamw * case-folding flags correct when we do the 2563da6c28aaSamw * file system creation, so go figure them out 2564de8267e0Stimh * now. 2565da6c28aaSamw */ 2566de8267e0Stimh VERIFY(nvlist_alloc(&zct.zct_zplprops, 2567de8267e0Stimh NV_UNIQUE_NAME, KM_SLEEP) == 0); 2568de8267e0Stimh error = zfs_fill_zplprops(zc->zc_name, nvprops, 25690a48a24eStimh zct.zct_zplprops, &is_insensitive); 2570da6c28aaSamw if (error != 0) { 2571da6c28aaSamw nvlist_free(nvprops); 2572de8267e0Stimh nvlist_free(zct.zct_zplprops); 2573da6c28aaSamw return (error); 2574da6c28aaSamw } 2575da6c28aaSamw } 2576ae46e4c7SMatthew Ahrens error = dmu_objset_create(zc->zc_name, type, 2577ab04eb8eStimh is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 2578de8267e0Stimh nvlist_free(zct.zct_zplprops); 2579fa9e4066Sahrens } 2580e9dbad6fSeschrock 2581e9dbad6fSeschrock /* 2582e9dbad6fSeschrock * It would be nice to do this atomically. 2583e9dbad6fSeschrock */ 2584e9dbad6fSeschrock if (error == 0) { 258591ebeef5Sahrens if ((error = zfs_set_prop_nvlist(zc->zc_name, nvprops)) != 0) 2586842727c2SChris Kirby (void) dmu_objset_destroy(zc->zc_name, B_FALSE); 2587e9dbad6fSeschrock } 2588ecd6cf80Smarks nvlist_free(nvprops); 2589fa9e4066Sahrens return (error); 2590fa9e4066Sahrens } 2591fa9e4066Sahrens 25923cb34c60Sahrens /* 25933cb34c60Sahrens * inputs: 25943cb34c60Sahrens * zc_name name of filesystem 25953cb34c60Sahrens * zc_value short name of snapshot 25963cb34c60Sahrens * zc_cookie recursive flag 259714843421SMatthew Ahrens * zc_nvlist_src[_size] property list 25983cb34c60Sahrens * 2599681d9761SEric Taylor * outputs: 2600681d9761SEric Taylor * zc_value short snapname (i.e. part after the '@') 26013cb34c60Sahrens */ 2602fa9e4066Sahrens static int 26031d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc) 2604fa9e4066Sahrens { 2605bb0ade09Sahrens nvlist_t *nvprops = NULL; 2606bb0ade09Sahrens int error; 2607bb0ade09Sahrens boolean_t recursive = zc->zc_cookie; 2608bb0ade09Sahrens 2609e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 26101d452cf5Sahrens return (EINVAL); 2611bb0ade09Sahrens 2612bb0ade09Sahrens if (zc->zc_nvlist_src != NULL && 2613bb0ade09Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2614478ed9adSEric Taylor zc->zc_iflags, &nvprops)) != 0) 2615bb0ade09Sahrens return (error); 2616bb0ade09Sahrens 2617ea2f5b9eSMatthew Ahrens error = zfs_check_userprops(zc->zc_name, nvprops); 2618ea2f5b9eSMatthew Ahrens if (error) 2619ea2f5b9eSMatthew Ahrens goto out; 2620bb0ade09Sahrens 2621ea2f5b9eSMatthew Ahrens if (nvprops != NULL && nvlist_next_nvpair(nvprops, NULL) != NULL && 2622ea2f5b9eSMatthew Ahrens zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) { 2623ea2f5b9eSMatthew Ahrens error = ENOTSUP; 2624ea2f5b9eSMatthew Ahrens goto out; 2625bb0ade09Sahrens } 2626ea2f5b9eSMatthew Ahrens 2627ea2f5b9eSMatthew Ahrens error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, 2628ea2f5b9eSMatthew Ahrens nvprops, recursive); 2629ea2f5b9eSMatthew Ahrens 2630ea2f5b9eSMatthew Ahrens out: 2631bb0ade09Sahrens nvlist_free(nvprops); 2632bb0ade09Sahrens return (error); 26331d452cf5Sahrens } 2634fa9e4066Sahrens 2635cdf5b4caSmmusante int 26361d452cf5Sahrens zfs_unmount_snap(char *name, void *arg) 26371d452cf5Sahrens { 26380b69c2f0Sahrens vfs_t *vfsp = NULL; 26391d452cf5Sahrens 2640745cd3c5Smaybee if (arg) { 2641745cd3c5Smaybee char *snapname = arg; 2642745cd3c5Smaybee int len = strlen(name) + strlen(snapname) + 2; 2643745cd3c5Smaybee char *buf = kmem_alloc(len, KM_SLEEP); 26441d452cf5Sahrens 2645745cd3c5Smaybee (void) strcpy(buf, name); 2646745cd3c5Smaybee (void) strcat(buf, "@"); 2647745cd3c5Smaybee (void) strcat(buf, snapname); 2648745cd3c5Smaybee vfsp = zfs_get_vfs(buf); 2649745cd3c5Smaybee kmem_free(buf, len); 26500b69c2f0Sahrens } else if (strchr(name, '@')) { 26511d452cf5Sahrens vfsp = zfs_get_vfs(name); 26521d452cf5Sahrens } 26531d452cf5Sahrens 26541d452cf5Sahrens if (vfsp) { 2655fa9e4066Sahrens /* 26561d452cf5Sahrens * Always force the unmount for snapshots. 2657fa9e4066Sahrens */ 26581d452cf5Sahrens int flag = MS_FORCE; 26591d452cf5Sahrens int err; 26601d452cf5Sahrens 26611d452cf5Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 2662fa9e4066Sahrens VFS_RELE(vfsp); 26631d452cf5Sahrens return (err); 2664fa9e4066Sahrens } 26651d452cf5Sahrens VFS_RELE(vfsp); 26661d452cf5Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0) 26671d452cf5Sahrens return (err); 26681d452cf5Sahrens } 26691d452cf5Sahrens return (0); 26701d452cf5Sahrens } 26711d452cf5Sahrens 26723cb34c60Sahrens /* 26733cb34c60Sahrens * inputs: 2674842727c2SChris Kirby * zc_name name of filesystem 2675842727c2SChris Kirby * zc_value short name of snapshot 2676842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 26773cb34c60Sahrens * 26783cb34c60Sahrens * outputs: none 26793cb34c60Sahrens */ 26801d452cf5Sahrens static int 26811d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc) 26821d452cf5Sahrens { 26831d452cf5Sahrens int err; 26841d452cf5Sahrens 2685e9dbad6fSeschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 26861d452cf5Sahrens return (EINVAL); 26871d452cf5Sahrens err = dmu_objset_find(zc->zc_name, 2688e9dbad6fSeschrock zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN); 26891d452cf5Sahrens if (err) 26901d452cf5Sahrens return (err); 2691842727c2SChris Kirby return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value, 2692842727c2SChris Kirby zc->zc_defer_destroy)); 26931d452cf5Sahrens } 26941d452cf5Sahrens 26953cb34c60Sahrens /* 26963cb34c60Sahrens * inputs: 26973cb34c60Sahrens * zc_name name of dataset to destroy 26983cb34c60Sahrens * zc_objset_type type of objset 2699842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 27003cb34c60Sahrens * 27013cb34c60Sahrens * outputs: none 27023cb34c60Sahrens */ 27031d452cf5Sahrens static int 27041d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 27051d452cf5Sahrens { 2706681d9761SEric Taylor int err; 27071d452cf5Sahrens if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) { 2708681d9761SEric Taylor err = zfs_unmount_snap(zc->zc_name, NULL); 27091d452cf5Sahrens if (err) 27101d452cf5Sahrens return (err); 2711fa9e4066Sahrens } 2712fa9e4066Sahrens 2713681d9761SEric Taylor err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy); 2714681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 27155c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 2716681d9761SEric Taylor return (err); 2717fa9e4066Sahrens } 2718fa9e4066Sahrens 27193cb34c60Sahrens /* 27203cb34c60Sahrens * inputs: 27214ccbb6e7Sahrens * zc_name name of dataset to rollback (to most recent snapshot) 27223cb34c60Sahrens * 27233cb34c60Sahrens * outputs: none 27243cb34c60Sahrens */ 2725fa9e4066Sahrens static int 2726fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc) 2727fa9e4066Sahrens { 2728ae46e4c7SMatthew Ahrens dsl_dataset_t *ds, *clone; 27294ccbb6e7Sahrens int error; 2730ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 2731ae46e4c7SMatthew Ahrens char *clone_name; 2732ae46e4c7SMatthew Ahrens 2733ae46e4c7SMatthew Ahrens error = dsl_dataset_hold(zc->zc_name, FTAG, &ds); 2734ae46e4c7SMatthew Ahrens if (error) 2735ae46e4c7SMatthew Ahrens return (error); 2736ae46e4c7SMatthew Ahrens 2737ae46e4c7SMatthew Ahrens /* must not be a snapshot */ 2738ae46e4c7SMatthew Ahrens if (dsl_dataset_is_snapshot(ds)) { 2739ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 2740ae46e4c7SMatthew Ahrens return (EINVAL); 2741ae46e4c7SMatthew Ahrens } 2742ae46e4c7SMatthew Ahrens 2743ae46e4c7SMatthew Ahrens /* must have a most recent snapshot */ 2744ae46e4c7SMatthew Ahrens if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) { 2745ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 2746ae46e4c7SMatthew Ahrens return (EINVAL); 2747ae46e4c7SMatthew Ahrens } 27484ccbb6e7Sahrens 27494ccbb6e7Sahrens /* 2750ae46e4c7SMatthew Ahrens * Create clone of most recent snapshot. 27514ccbb6e7Sahrens */ 2752ae46e4c7SMatthew Ahrens clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name); 2753ae46e4c7SMatthew Ahrens error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT); 27544ccbb6e7Sahrens if (error) 2755ae46e4c7SMatthew Ahrens goto out; 27564ccbb6e7Sahrens 2757503ad85cSMatthew Ahrens error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone); 2758ae46e4c7SMatthew Ahrens if (error) 2759ae46e4c7SMatthew Ahrens goto out; 2760ae46e4c7SMatthew Ahrens 2761ae46e4c7SMatthew Ahrens /* 2762ae46e4c7SMatthew Ahrens * Do clone swap. 2763ae46e4c7SMatthew Ahrens */ 276414843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 2765503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 276647f263f4Sek if (error == 0) { 276747f263f4Sek int resume_err; 27684ccbb6e7Sahrens 2769ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 2770ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, 2771ae46e4c7SMatthew Ahrens B_TRUE); 2772ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 2773ae46e4c7SMatthew Ahrens ds = NULL; 2774ae46e4c7SMatthew Ahrens } else { 2775ae46e4c7SMatthew Ahrens error = EBUSY; 2776ae46e4c7SMatthew Ahrens } 2777503ad85cSMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, zc->zc_name); 277847f263f4Sek error = error ? error : resume_err; 277947f263f4Sek } 27804ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 27814ccbb6e7Sahrens } else { 2782ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 2783ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, B_TRUE); 2784ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 2785ae46e4c7SMatthew Ahrens ds = NULL; 2786ae46e4c7SMatthew Ahrens } else { 2787ae46e4c7SMatthew Ahrens error = EBUSY; 2788ae46e4c7SMatthew Ahrens } 27894ccbb6e7Sahrens } 27904ccbb6e7Sahrens 2791ae46e4c7SMatthew Ahrens /* 2792ae46e4c7SMatthew Ahrens * Destroy clone (which also closes it). 2793ae46e4c7SMatthew Ahrens */ 2794ae46e4c7SMatthew Ahrens (void) dsl_dataset_destroy(clone, FTAG, B_FALSE); 2795ae46e4c7SMatthew Ahrens 2796ae46e4c7SMatthew Ahrens out: 2797ae46e4c7SMatthew Ahrens strfree(clone_name); 2798ae46e4c7SMatthew Ahrens if (ds) 2799ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 28004ccbb6e7Sahrens return (error); 2801fa9e4066Sahrens } 2802fa9e4066Sahrens 28033cb34c60Sahrens /* 28043cb34c60Sahrens * inputs: 28053cb34c60Sahrens * zc_name old name of dataset 28063cb34c60Sahrens * zc_value new name of dataset 28073cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 28083cb34c60Sahrens * 28093cb34c60Sahrens * outputs: none 28103cb34c60Sahrens */ 2811fa9e4066Sahrens static int 2812fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 2813fa9e4066Sahrens { 28147f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 2815cdf5b4caSmmusante 2816e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 2817f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 2818f18faf3fSek strchr(zc->zc_value, '%')) 2819fa9e4066Sahrens return (EINVAL); 2820fa9e4066Sahrens 2821cdf5b4caSmmusante /* 2822cdf5b4caSmmusante * Unmount snapshot unless we're doing a recursive rename, 2823cdf5b4caSmmusante * in which case the dataset code figures out which snapshots 2824cdf5b4caSmmusante * to unmount. 2825cdf5b4caSmmusante */ 2826cdf5b4caSmmusante if (!recursive && strchr(zc->zc_name, '@') != NULL && 2827fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 28281d452cf5Sahrens int err = zfs_unmount_snap(zc->zc_name, NULL); 28291d452cf5Sahrens if (err) 28301d452cf5Sahrens return (err); 2831fa9e4066Sahrens } 2832681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL) 2833681d9761SEric Taylor (void) zvol_remove_minor(zc->zc_name); 2834cdf5b4caSmmusante return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); 2835fa9e4066Sahrens } 2836fa9e4066Sahrens 2837745cd3c5Smaybee static void 28386e77af0aSDavid Pacheco clear_props(char *dataset, nvlist_t *props, nvlist_t *newprops) 2839745cd3c5Smaybee { 2840745cd3c5Smaybee zfs_cmd_t *zc; 2841745cd3c5Smaybee nvpair_t *prop; 2842745cd3c5Smaybee 2843745cd3c5Smaybee if (props == NULL) 2844745cd3c5Smaybee return; 2845745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 2846745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 2847745cd3c5Smaybee for (prop = nvlist_next_nvpair(props, NULL); prop; 2848745cd3c5Smaybee prop = nvlist_next_nvpair(props, prop)) { 28496e77af0aSDavid Pacheco if (newprops != NULL && 28506e77af0aSDavid Pacheco nvlist_exists(newprops, nvpair_name(prop))) 28516e77af0aSDavid Pacheco continue; 2852745cd3c5Smaybee (void) strcpy(zc->zc_value, nvpair_name(prop)); 2853745cd3c5Smaybee if (zfs_secpolicy_inherit(zc, CRED()) == 0) 2854745cd3c5Smaybee (void) zfs_ioc_inherit_prop(zc); 2855745cd3c5Smaybee } 2856745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 2857745cd3c5Smaybee } 2858745cd3c5Smaybee 28593cb34c60Sahrens /* 28603cb34c60Sahrens * inputs: 28613cb34c60Sahrens * zc_name name of containing filesystem 28623cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 28633cb34c60Sahrens * zc_value name of snapshot to create 28643cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 28653cb34c60Sahrens * zc_cookie file descriptor to recv from 28663cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 28673cb34c60Sahrens * zc_guid force flag 28683cb34c60Sahrens * 28693cb34c60Sahrens * outputs: 28703cb34c60Sahrens * zc_cookie number of bytes read 28713cb34c60Sahrens */ 2872fa9e4066Sahrens static int 28733cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 2874fa9e4066Sahrens { 2875fa9e4066Sahrens file_t *fp; 2876f18faf3fSek objset_t *os; 28773cb34c60Sahrens dmu_recv_cookie_t drc; 2878f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 2879f18faf3fSek int error, fd; 28803cb34c60Sahrens offset_t off; 28813cb34c60Sahrens nvlist_t *props = NULL; 2882745cd3c5Smaybee nvlist_t *origprops = NULL; 28833cb34c60Sahrens objset_t *origin = NULL; 28843cb34c60Sahrens char *tosnap; 28853cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 2886fa9e4066Sahrens 28873ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 2888f18faf3fSek strchr(zc->zc_value, '@') == NULL || 2889f18faf3fSek strchr(zc->zc_value, '%')) 28903ccfa83cSahrens return (EINVAL); 28913ccfa83cSahrens 28923cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 28933cb34c60Sahrens tosnap = strchr(tofs, '@'); 28943cb34c60Sahrens *tosnap = '\0'; 28953cb34c60Sahrens tosnap++; 28963cb34c60Sahrens 28973cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 28983cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2899478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 29003cb34c60Sahrens return (error); 29013cb34c60Sahrens 2902fa9e4066Sahrens fd = zc->zc_cookie; 2903fa9e4066Sahrens fp = getf(fd); 29043cb34c60Sahrens if (fp == NULL) { 29053cb34c60Sahrens nvlist_free(props); 2906fa9e4066Sahrens return (EBADF); 29073cb34c60Sahrens } 2908f18faf3fSek 2909503ad85cSMatthew Ahrens if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) { 2910745cd3c5Smaybee /* 2911745cd3c5Smaybee * If new properties are supplied, they are to completely 2912745cd3c5Smaybee * replace the existing ones, so stash away the existing ones. 2913745cd3c5Smaybee */ 2914503ad85cSMatthew Ahrens (void) dsl_prop_get_all(os, &origprops, B_TRUE); 2915745cd3c5Smaybee 2916503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2917f18faf3fSek } 2918f18faf3fSek 29193cb34c60Sahrens if (zc->zc_string[0]) { 2920503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_string, FTAG, &origin); 2921745cd3c5Smaybee if (error) 2922745cd3c5Smaybee goto out; 29233cb34c60Sahrens } 29243cb34c60Sahrens 29253cb34c60Sahrens error = dmu_recv_begin(tofs, tosnap, &zc->zc_begin_record, 2926f4b94bdeSMatthew Ahrens force, origin, &drc); 29273cb34c60Sahrens if (origin) 2928503ad85cSMatthew Ahrens dmu_objset_rele(origin, FTAG); 2929745cd3c5Smaybee if (error) 2930745cd3c5Smaybee goto out; 2931f18faf3fSek 2932f18faf3fSek /* 2933745cd3c5Smaybee * Reset properties. We do this before we receive the stream 2934745cd3c5Smaybee * so that the properties are applied to the new data. 2935f18faf3fSek */ 29363cb34c60Sahrens if (props) { 29376e77af0aSDavid Pacheco clear_props(tofs, origprops, props); 2938745cd3c5Smaybee /* 2939745cd3c5Smaybee * XXX - Note, this is all-or-nothing; should be best-effort. 2940745cd3c5Smaybee */ 2941745cd3c5Smaybee (void) zfs_set_prop_nvlist(tofs, props); 29423cb34c60Sahrens } 29433cb34c60Sahrens 29443cb34c60Sahrens off = fp->f_offset; 29453cb34c60Sahrens error = dmu_recv_stream(&drc, fp->f_vnode, &off); 2946a2eea2e1Sahrens 2947f4b94bdeSMatthew Ahrens if (error == 0) { 2948f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 2949745cd3c5Smaybee 2950f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 2951f4b94bdeSMatthew Ahrens /* online recv */ 2952f4b94bdeSMatthew Ahrens int end_err; 2953745cd3c5Smaybee 2954503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 2955f4b94bdeSMatthew Ahrens /* 2956f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 2957f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 2958f4b94bdeSMatthew Ahrens */ 2959f4b94bdeSMatthew Ahrens end_err = dmu_recv_end(&drc); 2960f4b94bdeSMatthew Ahrens if (error == 0) { 2961f4b94bdeSMatthew Ahrens int resume_err = 2962503ad85cSMatthew Ahrens zfs_resume_fs(zfsvfs, tofs); 2963f4b94bdeSMatthew Ahrens error = error ? error : resume_err; 2964f4b94bdeSMatthew Ahrens } 2965f4b94bdeSMatthew Ahrens error = error ? error : end_err; 2966f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 2967745cd3c5Smaybee } else { 2968f4b94bdeSMatthew Ahrens error = dmu_recv_end(&drc); 29693cb34c60Sahrens } 297047f263f4Sek } 29713cb34c60Sahrens 29723cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 29733cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 29743cb34c60Sahrens fp->f_offset = off; 2975a2eea2e1Sahrens 2976745cd3c5Smaybee /* 2977745cd3c5Smaybee * On error, restore the original props. 2978745cd3c5Smaybee */ 2979745cd3c5Smaybee if (error && props) { 29806e77af0aSDavid Pacheco clear_props(tofs, props, NULL); 2981745cd3c5Smaybee (void) zfs_set_prop_nvlist(tofs, origprops); 2982745cd3c5Smaybee } 2983745cd3c5Smaybee out: 2984745cd3c5Smaybee nvlist_free(props); 2985745cd3c5Smaybee nvlist_free(origprops); 2986fa9e4066Sahrens releasef(fd); 2987fa9e4066Sahrens return (error); 2988fa9e4066Sahrens } 2989fa9e4066Sahrens 29903cb34c60Sahrens /* 29913cb34c60Sahrens * inputs: 29923cb34c60Sahrens * zc_name name of snapshot to send 29933cb34c60Sahrens * zc_value short name of incremental fromsnap (may be empty) 29943cb34c60Sahrens * zc_cookie file descriptor to send stream to 29953cb34c60Sahrens * zc_obj fromorigin flag (mutually exclusive with zc_value) 29963cb34c60Sahrens * 29973cb34c60Sahrens * outputs: none 29983cb34c60Sahrens */ 2999fa9e4066Sahrens static int 30003cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 3001fa9e4066Sahrens { 3002fa9e4066Sahrens objset_t *fromsnap = NULL; 3003fa9e4066Sahrens objset_t *tosnap; 3004fa9e4066Sahrens file_t *fp; 3005fa9e4066Sahrens int error; 30063cb34c60Sahrens offset_t off; 3007fa9e4066Sahrens 3008503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap); 3009fa9e4066Sahrens if (error) 3010fa9e4066Sahrens return (error); 3011fa9e4066Sahrens 3012e9dbad6fSeschrock if (zc->zc_value[0] != '\0') { 30136a0f0066SEric Taylor char *buf; 3014a2eea2e1Sahrens char *cp; 3015a2eea2e1Sahrens 30166a0f0066SEric Taylor buf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 30176a0f0066SEric Taylor (void) strncpy(buf, zc->zc_name, MAXPATHLEN); 3018a2eea2e1Sahrens cp = strchr(buf, '@'); 3019a2eea2e1Sahrens if (cp) 3020a2eea2e1Sahrens *(cp+1) = 0; 30216a0f0066SEric Taylor (void) strncat(buf, zc->zc_value, MAXPATHLEN); 3022503ad85cSMatthew Ahrens error = dmu_objset_hold(buf, FTAG, &fromsnap); 30236a0f0066SEric Taylor kmem_free(buf, MAXPATHLEN); 3024fa9e4066Sahrens if (error) { 3025503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3026fa9e4066Sahrens return (error); 3027fa9e4066Sahrens } 3028fa9e4066Sahrens } 3029fa9e4066Sahrens 3030fa9e4066Sahrens fp = getf(zc->zc_cookie); 3031fa9e4066Sahrens if (fp == NULL) { 3032503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3033fa9e4066Sahrens if (fromsnap) 3034503ad85cSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 3035fa9e4066Sahrens return (EBADF); 3036fa9e4066Sahrens } 3037fa9e4066Sahrens 30383cb34c60Sahrens off = fp->f_offset; 30393cb34c60Sahrens error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off); 3040fa9e4066Sahrens 30413cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 30423cb34c60Sahrens fp->f_offset = off; 3043fa9e4066Sahrens releasef(zc->zc_cookie); 3044fa9e4066Sahrens if (fromsnap) 3045503ad85cSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 3046503ad85cSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 3047fa9e4066Sahrens return (error); 3048fa9e4066Sahrens } 3049fa9e4066Sahrens 3050ea8dc4b6Seschrock static int 3051ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 3052ea8dc4b6Seschrock { 3053ea8dc4b6Seschrock int id, error; 3054ea8dc4b6Seschrock 3055ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 3056ea8dc4b6Seschrock &zc->zc_inject_record); 3057ea8dc4b6Seschrock 3058ea8dc4b6Seschrock if (error == 0) 3059ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 3060ea8dc4b6Seschrock 3061ea8dc4b6Seschrock return (error); 3062ea8dc4b6Seschrock } 3063ea8dc4b6Seschrock 3064ea8dc4b6Seschrock static int 3065ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 3066ea8dc4b6Seschrock { 3067ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 3068ea8dc4b6Seschrock } 3069ea8dc4b6Seschrock 3070ea8dc4b6Seschrock static int 3071ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 3072ea8dc4b6Seschrock { 3073ea8dc4b6Seschrock int id = (int)zc->zc_guid; 3074ea8dc4b6Seschrock int error; 3075ea8dc4b6Seschrock 3076ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 3077ea8dc4b6Seschrock &zc->zc_inject_record); 3078ea8dc4b6Seschrock 3079ea8dc4b6Seschrock zc->zc_guid = id; 3080ea8dc4b6Seschrock 3081ea8dc4b6Seschrock return (error); 3082ea8dc4b6Seschrock } 3083ea8dc4b6Seschrock 3084ea8dc4b6Seschrock static int 3085ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 3086ea8dc4b6Seschrock { 3087ea8dc4b6Seschrock spa_t *spa; 3088ea8dc4b6Seschrock int error; 3089e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 3090ea8dc4b6Seschrock 3091ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 3092ea8dc4b6Seschrock return (error); 3093ea8dc4b6Seschrock 3094e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 3095ea8dc4b6Seschrock &count); 3096ea8dc4b6Seschrock if (error == 0) 3097e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 3098ea8dc4b6Seschrock else 3099e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 3100ea8dc4b6Seschrock 3101ea8dc4b6Seschrock spa_close(spa, FTAG); 3102ea8dc4b6Seschrock 3103ea8dc4b6Seschrock return (error); 3104ea8dc4b6Seschrock } 3105ea8dc4b6Seschrock 3106ea8dc4b6Seschrock static int 3107ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 3108ea8dc4b6Seschrock { 3109ea8dc4b6Seschrock spa_t *spa; 3110ea8dc4b6Seschrock vdev_t *vd; 3111bb8b5132Sek int error; 3112ea8dc4b6Seschrock 3113b87f3af3Sperrin /* 3114b87f3af3Sperrin * On zpool clear we also fix up missing slogs 3115b87f3af3Sperrin */ 3116b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 3117b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 3118b87f3af3Sperrin if (spa == NULL) { 3119b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3120b87f3af3Sperrin return (EIO); 3121b87f3af3Sperrin } 3122b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 3123b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 3124b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 3125b87f3af3Sperrin } 3126468c413aSTim Haley spa->spa_last_open_failed = 0; 3127b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 3128b87f3af3Sperrin 3129468c413aSTim Haley if (zc->zc_cookie == ZPOOL_NO_REWIND) { 3130468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 3131468c413aSTim Haley } else { 3132468c413aSTim Haley nvlist_t *policy; 3133468c413aSTim Haley nvlist_t *config = NULL; 3134468c413aSTim Haley 3135468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 3136468c413aSTim Haley return (EINVAL); 3137468c413aSTim Haley 3138468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 3139468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 3140468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 3141468c413aSTim Haley policy, &config); 3142468c413aSTim Haley if (config != NULL) { 3143468c413aSTim Haley (void) put_nvlist(zc, config); 3144468c413aSTim Haley nvlist_free(config); 3145468c413aSTim Haley } 3146468c413aSTim Haley nvlist_free(policy); 3147468c413aSTim Haley } 3148468c413aSTim Haley } 3149468c413aSTim Haley 3150468c413aSTim Haley if (error) 3151ea8dc4b6Seschrock return (error); 3152ea8dc4b6Seschrock 31538f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 3154ea8dc4b6Seschrock 3155e9dbad6fSeschrock if (zc->zc_guid == 0) { 3156ea8dc4b6Seschrock vd = NULL; 3157c5904d13Seschrock } else { 3158c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 3159fa94a07fSbrendan if (vd == NULL) { 3160e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 3161fa94a07fSbrendan spa_close(spa, FTAG); 3162fa94a07fSbrendan return (ENODEV); 3163fa94a07fSbrendan } 3164ea8dc4b6Seschrock } 3165ea8dc4b6Seschrock 3166e14bb325SJeff Bonwick vdev_clear(spa, vd); 3167e14bb325SJeff Bonwick 3168e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 3169ea8dc4b6Seschrock 3170e14bb325SJeff Bonwick /* 3171e14bb325SJeff Bonwick * Resume any suspended I/Os. 3172e14bb325SJeff Bonwick */ 317354d692b7SGeorge Wilson if (zio_resume(spa) != 0) 317454d692b7SGeorge Wilson error = EIO; 3175ea8dc4b6Seschrock 3176ea8dc4b6Seschrock spa_close(spa, FTAG); 3177ea8dc4b6Seschrock 317854d692b7SGeorge Wilson return (error); 3179ea8dc4b6Seschrock } 3180ea8dc4b6Seschrock 31813cb34c60Sahrens /* 31823cb34c60Sahrens * inputs: 31833cb34c60Sahrens * zc_name name of filesystem 31843cb34c60Sahrens * zc_value name of origin snapshot 31853cb34c60Sahrens * 3186681d9761SEric Taylor * outputs: 3187681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 31883cb34c60Sahrens */ 318999653d4eSeschrock static int 319099653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 319199653d4eSeschrock { 31920b69c2f0Sahrens char *cp; 31930b69c2f0Sahrens 31940b69c2f0Sahrens /* 31950b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 31960b69c2f0Sahrens * it's easier. 31970b69c2f0Sahrens */ 3198e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 31990b69c2f0Sahrens if (cp) 32000b69c2f0Sahrens *cp = '\0'; 3201e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 32020b69c2f0Sahrens zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); 3203681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 320499653d4eSeschrock } 320599653d4eSeschrock 320614843421SMatthew Ahrens /* 320714843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 320814843421SMatthew Ahrens * 320914843421SMatthew Ahrens * inputs: 321014843421SMatthew Ahrens * zc_name name of filesystem 321114843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 321214843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 321314843421SMatthew Ahrens * zc_guid RID/UID/GID 321414843421SMatthew Ahrens * 321514843421SMatthew Ahrens * outputs: 321614843421SMatthew Ahrens * zc_cookie property value 321714843421SMatthew Ahrens */ 321814843421SMatthew Ahrens static int 321914843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 322014843421SMatthew Ahrens { 322114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 322214843421SMatthew Ahrens int error; 322314843421SMatthew Ahrens 322414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 322514843421SMatthew Ahrens return (EINVAL); 322614843421SMatthew Ahrens 3227503ad85cSMatthew Ahrens error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs); 322814843421SMatthew Ahrens if (error) 322914843421SMatthew Ahrens return (error); 323014843421SMatthew Ahrens 323114843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 323214843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 323314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 323414843421SMatthew Ahrens 323514843421SMatthew Ahrens return (error); 323614843421SMatthew Ahrens } 323714843421SMatthew Ahrens 323814843421SMatthew Ahrens /* 323914843421SMatthew Ahrens * inputs: 324014843421SMatthew Ahrens * zc_name name of filesystem 324114843421SMatthew Ahrens * zc_cookie zap cursor 324214843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 324314843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 324414843421SMatthew Ahrens * 324514843421SMatthew Ahrens * outputs: 324614843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 324714843421SMatthew Ahrens * zc_cookie zap cursor 324814843421SMatthew Ahrens */ 324914843421SMatthew Ahrens static int 325014843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 325114843421SMatthew Ahrens { 325214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 325314843421SMatthew Ahrens int error; 325414843421SMatthew Ahrens 3255503ad85cSMatthew Ahrens error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs); 325614843421SMatthew Ahrens if (error) 325714843421SMatthew Ahrens return (error); 325814843421SMatthew Ahrens 325914843421SMatthew Ahrens int bufsize = zc->zc_nvlist_dst_size; 326014843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 326114843421SMatthew Ahrens 326214843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 326314843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 326414843421SMatthew Ahrens 326514843421SMatthew Ahrens if (error == 0) { 326614843421SMatthew Ahrens error = xcopyout(buf, 326714843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 326814843421SMatthew Ahrens zc->zc_nvlist_dst_size); 326914843421SMatthew Ahrens } 327014843421SMatthew Ahrens kmem_free(buf, bufsize); 327114843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 327214843421SMatthew Ahrens 327314843421SMatthew Ahrens return (error); 327414843421SMatthew Ahrens } 327514843421SMatthew Ahrens 327614843421SMatthew Ahrens /* 327714843421SMatthew Ahrens * inputs: 327814843421SMatthew Ahrens * zc_name name of filesystem 327914843421SMatthew Ahrens * 328014843421SMatthew Ahrens * outputs: 328114843421SMatthew Ahrens * none 328214843421SMatthew Ahrens */ 328314843421SMatthew Ahrens static int 328414843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 328514843421SMatthew Ahrens { 328614843421SMatthew Ahrens objset_t *os; 328714843421SMatthew Ahrens int error; 328814843421SMatthew Ahrens zfsvfs_t *zfsvfs; 328914843421SMatthew Ahrens 329014843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 3291503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 329214843421SMatthew Ahrens /* 329314843421SMatthew Ahrens * If userused is not enabled, it may be because the 329414843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 329514843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 329614843421SMatthew Ahrens */ 3297503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3298503ad85cSMatthew Ahrens if (error == 0) 3299503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 330014843421SMatthew Ahrens } 330114843421SMatthew Ahrens if (error == 0) 330214843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 330314843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 330414843421SMatthew Ahrens } else { 3305503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 3306503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 330714843421SMatthew Ahrens if (error) 330814843421SMatthew Ahrens return (error); 330914843421SMatthew Ahrens 331014843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 3311503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 331214843421SMatthew Ahrens } 331314843421SMatthew Ahrens 331414843421SMatthew Ahrens return (error); 331514843421SMatthew Ahrens } 331614843421SMatthew Ahrens 3317ecd6cf80Smarks /* 3318ecd6cf80Smarks * We don't want to have a hard dependency 3319ecd6cf80Smarks * against some special symbols in sharefs 3320da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 3321ecd6cf80Smarks * the first file system is shared. 3322da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 3323ecd6cf80Smarks */ 3324da6c28aaSamw int (*znfsexport_fs)(void *arg); 3325ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 3326da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 3327da6c28aaSamw 3328da6c28aaSamw int zfs_nfsshare_inited; 3329da6c28aaSamw int zfs_smbshare_inited; 3330ecd6cf80Smarks 3331ecd6cf80Smarks ddi_modhandle_t nfs_mod; 3332ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 3333da6c28aaSamw ddi_modhandle_t smbsrv_mod; 3334ecd6cf80Smarks kmutex_t zfs_share_lock; 3335ecd6cf80Smarks 3336da6c28aaSamw static int 3337da6c28aaSamw zfs_init_sharefs() 3338da6c28aaSamw { 3339da6c28aaSamw int error; 3340da6c28aaSamw 3341da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 3342da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 3343da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 3344da6c28aaSamw ddi_modopen("fs/sharefs", 3345da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3346da6c28aaSamw return (ENOSYS); 3347da6c28aaSamw } 3348da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 3349da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 3350da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 3351da6c28aaSamw return (ENOSYS); 3352da6c28aaSamw } 3353da6c28aaSamw return (0); 3354da6c28aaSamw } 3355da6c28aaSamw 3356ecd6cf80Smarks static int 3357ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 3358ecd6cf80Smarks { 3359ecd6cf80Smarks int error; 3360ecd6cf80Smarks int opcode; 3361ecd6cf80Smarks 3362da6c28aaSamw switch (zc->zc_share.z_sharetype) { 3363da6c28aaSamw case ZFS_SHARE_NFS: 3364da6c28aaSamw case ZFS_UNSHARE_NFS: 3365da6c28aaSamw if (zfs_nfsshare_inited == 0) { 3366da6c28aaSamw mutex_enter(&zfs_share_lock); 3367da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 3368da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3369da6c28aaSamw mutex_exit(&zfs_share_lock); 3370da6c28aaSamw return (ENOSYS); 3371da6c28aaSamw } 3372da6c28aaSamw if (znfsexport_fs == NULL && 3373da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 3374da6c28aaSamw ddi_modsym(nfs_mod, 3375da6c28aaSamw "nfs_export", &error)) == NULL)) { 3376da6c28aaSamw mutex_exit(&zfs_share_lock); 3377da6c28aaSamw return (ENOSYS); 3378da6c28aaSamw } 3379da6c28aaSamw error = zfs_init_sharefs(); 3380da6c28aaSamw if (error) { 3381da6c28aaSamw mutex_exit(&zfs_share_lock); 3382da6c28aaSamw return (ENOSYS); 3383da6c28aaSamw } 3384da6c28aaSamw zfs_nfsshare_inited = 1; 3385ecd6cf80Smarks mutex_exit(&zfs_share_lock); 3386ecd6cf80Smarks } 3387da6c28aaSamw break; 3388da6c28aaSamw case ZFS_SHARE_SMB: 3389da6c28aaSamw case ZFS_UNSHARE_SMB: 3390da6c28aaSamw if (zfs_smbshare_inited == 0) { 3391da6c28aaSamw mutex_enter(&zfs_share_lock); 3392da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 3393da6c28aaSamw ddi_modopen("drv/smbsrv", 3394da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 3395da6c28aaSamw mutex_exit(&zfs_share_lock); 3396da6c28aaSamw return (ENOSYS); 3397da6c28aaSamw } 3398da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 3399da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 3400faa1795aSjb "smb_server_share", &error)) == NULL)) { 3401da6c28aaSamw mutex_exit(&zfs_share_lock); 3402da6c28aaSamw return (ENOSYS); 3403da6c28aaSamw } 3404da6c28aaSamw error = zfs_init_sharefs(); 3405da6c28aaSamw if (error) { 3406da6c28aaSamw mutex_exit(&zfs_share_lock); 3407da6c28aaSamw return (ENOSYS); 3408da6c28aaSamw } 3409da6c28aaSamw zfs_smbshare_inited = 1; 3410ecd6cf80Smarks mutex_exit(&zfs_share_lock); 3411ecd6cf80Smarks } 3412da6c28aaSamw break; 3413da6c28aaSamw default: 3414da6c28aaSamw return (EINVAL); 3415da6c28aaSamw } 3416ecd6cf80Smarks 3417da6c28aaSamw switch (zc->zc_share.z_sharetype) { 3418da6c28aaSamw case ZFS_SHARE_NFS: 3419da6c28aaSamw case ZFS_UNSHARE_NFS: 3420da6c28aaSamw if (error = 3421da6c28aaSamw znfsexport_fs((void *) 3422da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 3423da6c28aaSamw return (error); 3424da6c28aaSamw break; 3425da6c28aaSamw case ZFS_SHARE_SMB: 3426da6c28aaSamw case ZFS_UNSHARE_SMB: 3427da6c28aaSamw if (error = zsmbexport_fs((void *) 3428da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 3429da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 3430743a77edSAlan Wright B_TRUE: B_FALSE)) { 3431da6c28aaSamw return (error); 3432ecd6cf80Smarks } 3433da6c28aaSamw break; 3434ecd6cf80Smarks } 3435ecd6cf80Smarks 3436da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 3437da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 3438ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 3439ecd6cf80Smarks 3440da6c28aaSamw /* 3441da6c28aaSamw * Add or remove share from sharetab 3442da6c28aaSamw */ 3443ecd6cf80Smarks error = zshare_fs(opcode, 3444ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 3445ecd6cf80Smarks zc->zc_share.z_sharemax); 3446ecd6cf80Smarks 3447ecd6cf80Smarks return (error); 3448ecd6cf80Smarks 3449ecd6cf80Smarks } 3450ecd6cf80Smarks 3451743a77edSAlan Wright ace_t full_access[] = { 3452743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 3453743a77edSAlan Wright }; 3454743a77edSAlan Wright 3455743a77edSAlan Wright /* 3456743a77edSAlan Wright * Remove all ACL files in shares dir 3457743a77edSAlan Wright */ 3458743a77edSAlan Wright static int 3459743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 3460743a77edSAlan Wright { 3461743a77edSAlan Wright zap_cursor_t zc; 3462743a77edSAlan Wright zap_attribute_t zap; 3463743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 3464743a77edSAlan Wright int error; 3465743a77edSAlan Wright 3466743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 3467743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 3468743a77edSAlan Wright zap_cursor_advance(&zc)) { 3469743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 3470743a77edSAlan Wright NULL, 0)) != 0) 3471743a77edSAlan Wright break; 3472743a77edSAlan Wright } 3473743a77edSAlan Wright zap_cursor_fini(&zc); 3474743a77edSAlan Wright return (error); 3475743a77edSAlan Wright } 3476743a77edSAlan Wright 3477743a77edSAlan Wright static int 3478743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 3479743a77edSAlan Wright { 3480743a77edSAlan Wright vnode_t *vp; 3481743a77edSAlan Wright znode_t *dzp; 3482743a77edSAlan Wright vnode_t *resourcevp = NULL; 3483743a77edSAlan Wright znode_t *sharedir; 3484743a77edSAlan Wright zfsvfs_t *zfsvfs; 3485743a77edSAlan Wright nvlist_t *nvlist; 3486743a77edSAlan Wright char *src, *target; 3487743a77edSAlan Wright vattr_t vattr; 3488743a77edSAlan Wright vsecattr_t vsec; 3489743a77edSAlan Wright int error = 0; 3490743a77edSAlan Wright 3491743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 3492743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 3493743a77edSAlan Wright return (error); 3494743a77edSAlan Wright 3495743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 3496743a77edSAlan Wright 3497743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 3498743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 3499743a77edSAlan Wright zc->zc_name) != 0)) { 3500743a77edSAlan Wright VN_RELE(vp); 3501743a77edSAlan Wright return (EINVAL); 3502743a77edSAlan Wright } 3503743a77edSAlan Wright 3504743a77edSAlan Wright dzp = VTOZ(vp); 3505743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 3506743a77edSAlan Wright ZFS_ENTER(zfsvfs); 3507743a77edSAlan Wright 35089e1320c0SMark Shellenbaum /* 35099e1320c0SMark Shellenbaum * Create share dir if its missing. 35109e1320c0SMark Shellenbaum */ 35119e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 35129e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 35139e1320c0SMark Shellenbaum dmu_tx_t *tx; 35149e1320c0SMark Shellenbaum 35159e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 35169e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 35179e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 35189e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 35199e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 35209e1320c0SMark Shellenbaum if (error) { 35219e1320c0SMark Shellenbaum dmu_tx_abort(tx); 35229e1320c0SMark Shellenbaum } else { 35239e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 35249e1320c0SMark Shellenbaum dmu_tx_commit(tx); 35259e1320c0SMark Shellenbaum } 35269e1320c0SMark Shellenbaum if (error) { 35279e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 35289e1320c0SMark Shellenbaum VN_RELE(vp); 35299e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 35309e1320c0SMark Shellenbaum return (error); 35319e1320c0SMark Shellenbaum } 35329e1320c0SMark Shellenbaum } 35339e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 35349e1320c0SMark Shellenbaum 35359e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 3536743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 35379e1320c0SMark Shellenbaum VN_RELE(vp); 3538743a77edSAlan Wright ZFS_EXIT(zfsvfs); 3539743a77edSAlan Wright return (error); 3540743a77edSAlan Wright } 3541743a77edSAlan Wright 3542743a77edSAlan Wright switch (zc->zc_cookie) { 3543743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 3544743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 3545743a77edSAlan Wright vattr.va_type = VREG; 3546743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 3547743a77edSAlan Wright vattr.va_uid = 0; 3548743a77edSAlan Wright vattr.va_gid = 0; 3549743a77edSAlan Wright 3550743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 3551743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 3552743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 3553743a77edSAlan Wright vsec.vsa_aclcnt = 1; 3554743a77edSAlan Wright 3555743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 3556743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 3557743a77edSAlan Wright if (resourcevp) 3558743a77edSAlan Wright VN_RELE(resourcevp); 3559743a77edSAlan Wright break; 3560743a77edSAlan Wright 3561743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 3562743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 3563743a77edSAlan Wright NULL, 0); 3564743a77edSAlan Wright break; 3565743a77edSAlan Wright 3566743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 3567743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 3568478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 3569743a77edSAlan Wright VN_RELE(vp); 3570743a77edSAlan Wright ZFS_EXIT(zfsvfs); 3571743a77edSAlan Wright return (error); 3572743a77edSAlan Wright } 3573743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 3574743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 3575743a77edSAlan Wright &target)) { 3576743a77edSAlan Wright VN_RELE(vp); 357789459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 3578743a77edSAlan Wright ZFS_EXIT(zfsvfs); 3579743a77edSAlan Wright return (error); 3580743a77edSAlan Wright } 3581743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 3582743a77edSAlan Wright kcred, NULL, 0); 3583743a77edSAlan Wright nvlist_free(nvlist); 3584743a77edSAlan Wright break; 3585743a77edSAlan Wright 3586743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 3587743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 3588743a77edSAlan Wright break; 3589743a77edSAlan Wright 3590743a77edSAlan Wright default: 3591743a77edSAlan Wright error = EINVAL; 3592743a77edSAlan Wright break; 3593743a77edSAlan Wright } 3594743a77edSAlan Wright 3595743a77edSAlan Wright VN_RELE(vp); 3596743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 3597743a77edSAlan Wright 3598743a77edSAlan Wright ZFS_EXIT(zfsvfs); 3599743a77edSAlan Wright 3600743a77edSAlan Wright return (error); 3601743a77edSAlan Wright } 3602743a77edSAlan Wright 3603842727c2SChris Kirby /* 3604842727c2SChris Kirby * inputs: 3605842727c2SChris Kirby * zc_name name of filesystem 3606842727c2SChris Kirby * zc_value short name of snap 3607842727c2SChris Kirby * zc_string user-supplied tag for this reference 3608842727c2SChris Kirby * zc_cookie recursive flag 3609ca45db41SChris Kirby * zc_temphold set if hold is temporary 3610842727c2SChris Kirby * 3611842727c2SChris Kirby * outputs: none 3612842727c2SChris Kirby */ 3613842727c2SChris Kirby static int 3614842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc) 3615842727c2SChris Kirby { 3616842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 3617842727c2SChris Kirby 3618842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 3619842727c2SChris Kirby return (EINVAL); 3620842727c2SChris Kirby 3621842727c2SChris Kirby return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value, 3622ca45db41SChris Kirby zc->zc_string, recursive, zc->zc_temphold)); 3623842727c2SChris Kirby } 3624842727c2SChris Kirby 3625842727c2SChris Kirby /* 3626842727c2SChris Kirby * inputs: 3627842727c2SChris Kirby * zc_name name of dataset from which we're releasing a user reference 3628842727c2SChris Kirby * zc_value short name of snap 3629842727c2SChris Kirby * zc_string user-supplied tag for this reference 3630842727c2SChris Kirby * zc_cookie recursive flag 3631842727c2SChris Kirby * 3632842727c2SChris Kirby * outputs: none 3633842727c2SChris Kirby */ 3634842727c2SChris Kirby static int 3635842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc) 3636842727c2SChris Kirby { 3637842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 3638842727c2SChris Kirby 3639842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 3640842727c2SChris Kirby return (EINVAL); 3641842727c2SChris Kirby 3642842727c2SChris Kirby return (dsl_dataset_user_release(zc->zc_name, zc->zc_value, 3643842727c2SChris Kirby zc->zc_string, recursive)); 3644842727c2SChris Kirby } 3645842727c2SChris Kirby 3646842727c2SChris Kirby /* 3647842727c2SChris Kirby * inputs: 3648842727c2SChris Kirby * zc_name name of filesystem 3649842727c2SChris Kirby * 3650842727c2SChris Kirby * outputs: 3651842727c2SChris Kirby * zc_nvlist_src{_size} nvlist of snapshot holds 3652842727c2SChris Kirby */ 3653842727c2SChris Kirby static int 3654842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc) 3655842727c2SChris Kirby { 3656842727c2SChris Kirby nvlist_t *nvp; 3657842727c2SChris Kirby int error; 3658842727c2SChris Kirby 3659842727c2SChris Kirby if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) { 3660842727c2SChris Kirby error = put_nvlist(zc, nvp); 3661842727c2SChris Kirby nvlist_free(nvp); 3662842727c2SChris Kirby } 3663842727c2SChris Kirby 3664842727c2SChris Kirby return (error); 3665842727c2SChris Kirby } 3666842727c2SChris Kirby 3667ecd6cf80Smarks /* 36682a6b87f0Sek * pool create, destroy, and export don't log the history as part of 36692a6b87f0Sek * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export 36702a6b87f0Sek * do the logging of those commands. 3671ecd6cf80Smarks */ 3672fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = { 367354d692b7SGeorge Wilson { zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE, 367454d692b7SGeorge Wilson B_FALSE }, 367554d692b7SGeorge Wilson { zfs_ioc_pool_destroy, zfs_secpolicy_config, POOL_NAME, B_FALSE, 367654d692b7SGeorge Wilson B_FALSE }, 367754d692b7SGeorge Wilson { zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE, 367854d692b7SGeorge Wilson B_FALSE }, 367954d692b7SGeorge Wilson { zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE, 368054d692b7SGeorge Wilson B_FALSE }, 368154d692b7SGeorge Wilson { zfs_ioc_pool_configs, zfs_secpolicy_none, NO_NAME, B_FALSE, 368254d692b7SGeorge Wilson B_FALSE }, 368354d692b7SGeorge Wilson { zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE, 368454d692b7SGeorge Wilson B_FALSE }, 368554d692b7SGeorge Wilson { zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE, 368654d692b7SGeorge Wilson B_FALSE }, 368754d692b7SGeorge Wilson { zfs_ioc_pool_scrub, zfs_secpolicy_config, POOL_NAME, B_TRUE, 368854d692b7SGeorge Wilson B_TRUE }, 368954d692b7SGeorge Wilson { zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE, 369054d692b7SGeorge Wilson B_FALSE }, 369154d692b7SGeorge Wilson { zfs_ioc_pool_upgrade, zfs_secpolicy_config, POOL_NAME, B_TRUE, 369254d692b7SGeorge Wilson B_TRUE }, 369354d692b7SGeorge Wilson { zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE, 369454d692b7SGeorge Wilson B_FALSE }, 369554d692b7SGeorge Wilson { zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE, 369654d692b7SGeorge Wilson B_TRUE }, 369754d692b7SGeorge Wilson { zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE, 369854d692b7SGeorge Wilson B_TRUE }, 369954d692b7SGeorge Wilson { zfs_ioc_vdev_set_state, zfs_secpolicy_config, POOL_NAME, B_TRUE, 370054d692b7SGeorge Wilson B_FALSE }, 370154d692b7SGeorge Wilson { zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 370254d692b7SGeorge Wilson B_TRUE }, 370354d692b7SGeorge Wilson { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE, 370454d692b7SGeorge Wilson B_TRUE }, 370554d692b7SGeorge Wilson { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE, 370654d692b7SGeorge Wilson B_TRUE }, 37076809eb4eSEric Schrock { zfs_ioc_vdev_setfru, zfs_secpolicy_config, POOL_NAME, B_FALSE, 37086809eb4eSEric Schrock B_TRUE }, 370954d692b7SGeorge Wilson { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 371054d692b7SGeorge Wilson B_FALSE }, 371154d692b7SGeorge Wilson { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 371254d692b7SGeorge Wilson B_FALSE }, 371354d692b7SGeorge Wilson { zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 371454d692b7SGeorge Wilson B_FALSE }, 371554d692b7SGeorge Wilson { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 371654d692b7SGeorge Wilson B_FALSE }, 371754d692b7SGeorge Wilson { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE }, 371854d692b7SGeorge Wilson { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE }, 371954d692b7SGeorge Wilson { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, 372054d692b7SGeorge Wilson B_TRUE}, 372154d692b7SGeorge Wilson { zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE, 372254d692b7SGeorge Wilson B_TRUE }, 372354d692b7SGeorge Wilson { zfs_ioc_rename, zfs_secpolicy_rename, DATASET_NAME, B_TRUE, B_TRUE }, 372454d692b7SGeorge Wilson { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE }, 372554d692b7SGeorge Wilson { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE }, 372654d692b7SGeorge Wilson { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 372754d692b7SGeorge Wilson B_FALSE }, 372854d692b7SGeorge Wilson { zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE, 372954d692b7SGeorge Wilson B_FALSE }, 373054d692b7SGeorge Wilson { zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE, 373154d692b7SGeorge Wilson B_FALSE }, 373254d692b7SGeorge Wilson { zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE, 373354d692b7SGeorge Wilson B_FALSE }, 373454d692b7SGeorge Wilson { zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE }, 373554d692b7SGeorge Wilson { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE, 373654d692b7SGeorge Wilson B_TRUE }, 373754d692b7SGeorge Wilson { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, 373854d692b7SGeorge Wilson B_TRUE }, 373954d692b7SGeorge Wilson { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE, 374054d692b7SGeorge Wilson B_TRUE }, 374154d692b7SGeorge Wilson { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE, 374254d692b7SGeorge Wilson B_FALSE }, 37436e8a0f56SGeorge Wilson { zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE, 37446e8a0f56SGeorge Wilson B_TRUE }, 374554d692b7SGeorge Wilson { zfs_ioc_pool_set_props, zfs_secpolicy_config, POOL_NAME, B_TRUE, 374654d692b7SGeorge Wilson B_TRUE }, 374754d692b7SGeorge Wilson { zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE, 374854d692b7SGeorge Wilson B_FALSE }, 374954d692b7SGeorge Wilson { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE, 375054d692b7SGeorge Wilson B_TRUE }, 375154d692b7SGeorge Wilson { zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 375254d692b7SGeorge Wilson B_FALSE }, 375354d692b7SGeorge Wilson { zfs_ioc_iscsi_perm_check, zfs_secpolicy_iscsi, DATASET_NAME, B_FALSE, 375454d692b7SGeorge Wilson B_FALSE }, 375554d692b7SGeorge Wilson { zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE }, 375654d692b7SGeorge Wilson { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE, 375754d692b7SGeorge Wilson B_TRUE }, 375854d692b7SGeorge Wilson { zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE, 375914843421SMatthew Ahrens B_FALSE }, 376014843421SMatthew Ahrens { zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, 376114843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_FALSE }, 376214843421SMatthew Ahrens { zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, 376314843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_FALSE }, 376414843421SMatthew Ahrens { zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 376514843421SMatthew Ahrens DATASET_NAME, B_FALSE, B_TRUE }, 3766842727c2SChris Kirby { zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE }, 3767842727c2SChris Kirby { zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE, 3768842727c2SChris Kirby B_TRUE }, 3769842727c2SChris Kirby { zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE, 3770842727c2SChris Kirby B_TRUE } 3771fa9e4066Sahrens }; 3772fa9e4066Sahrens 377354d692b7SGeorge Wilson int 377454d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type) 377554d692b7SGeorge Wilson { 377654d692b7SGeorge Wilson spa_t *spa; 377754d692b7SGeorge Wilson int error; 377854d692b7SGeorge Wilson 377954d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 378054d692b7SGeorge Wilson 378114843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 378254d692b7SGeorge Wilson if (error == 0) { 378354d692b7SGeorge Wilson if (spa_suspended(spa)) 378454d692b7SGeorge Wilson error = EAGAIN; 378554d692b7SGeorge Wilson spa_close(spa, FTAG); 378654d692b7SGeorge Wilson } 378754d692b7SGeorge Wilson return (error); 378854d692b7SGeorge Wilson } 378954d692b7SGeorge Wilson 3790fa9e4066Sahrens static int 3791fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 3792fa9e4066Sahrens { 3793fa9e4066Sahrens zfs_cmd_t *zc; 3794fa9e4066Sahrens uint_t vec; 37951d452cf5Sahrens int error, rc; 3796fa9e4066Sahrens 3797fa9e4066Sahrens if (getminor(dev) != 0) 3798fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 3799fa9e4066Sahrens 3800fa9e4066Sahrens vec = cmd - ZFS_IOC; 380191ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 3802fa9e4066Sahrens 3803fa9e4066Sahrens if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 3804fa9e4066Sahrens return (EINVAL); 3805fa9e4066Sahrens 3806fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 3807fa9e4066Sahrens 3808478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 3809fa9e4066Sahrens 3810681d9761SEric Taylor if ((error == 0) && !(flag & FKIOCTL)) 3811ecd6cf80Smarks error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr); 3812fa9e4066Sahrens 3813fa9e4066Sahrens /* 3814fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 3815fa9e4066Sahrens * the lower layers. 3816fa9e4066Sahrens */ 3817fa9e4066Sahrens if (error == 0) { 3818fa9e4066Sahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 3819478ed9adSEric Taylor zc->zc_iflags = flag & FKIOCTL; 3820fa9e4066Sahrens switch (zfs_ioc_vec[vec].zvec_namecheck) { 3821e7437265Sahrens case POOL_NAME: 3822fa9e4066Sahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 3823fa9e4066Sahrens error = EINVAL; 382454d692b7SGeorge Wilson if (zfs_ioc_vec[vec].zvec_pool_check) 382554d692b7SGeorge Wilson error = pool_status_check(zc->zc_name, 382654d692b7SGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck); 3827fa9e4066Sahrens break; 3828fa9e4066Sahrens 3829e7437265Sahrens case DATASET_NAME: 3830fa9e4066Sahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 3831fa9e4066Sahrens error = EINVAL; 383254d692b7SGeorge Wilson if (zfs_ioc_vec[vec].zvec_pool_check) 383354d692b7SGeorge Wilson error = pool_status_check(zc->zc_name, 383454d692b7SGeorge Wilson zfs_ioc_vec[vec].zvec_namecheck); 3835fa9e4066Sahrens break; 38365ad82045Snd 3837e7437265Sahrens case NO_NAME: 38385ad82045Snd break; 3839fa9e4066Sahrens } 3840fa9e4066Sahrens } 3841fa9e4066Sahrens 3842fa9e4066Sahrens if (error == 0) 3843fa9e4066Sahrens error = zfs_ioc_vec[vec].zvec_func(zc); 3844fa9e4066Sahrens 3845478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 3846ecd6cf80Smarks if (error == 0) { 38471d452cf5Sahrens error = rc; 384814843421SMatthew Ahrens if (zfs_ioc_vec[vec].zvec_his_log) 3849ecd6cf80Smarks zfs_log_history(zc); 3850ecd6cf80Smarks } 3851fa9e4066Sahrens 3852fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 3853fa9e4066Sahrens return (error); 3854fa9e4066Sahrens } 3855fa9e4066Sahrens 3856fa9e4066Sahrens static int 3857fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 3858fa9e4066Sahrens { 3859fa9e4066Sahrens if (cmd != DDI_ATTACH) 3860fa9e4066Sahrens return (DDI_FAILURE); 3861fa9e4066Sahrens 3862fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 3863fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 3864fa9e4066Sahrens return (DDI_FAILURE); 3865fa9e4066Sahrens 3866fa9e4066Sahrens zfs_dip = dip; 3867fa9e4066Sahrens 3868fa9e4066Sahrens ddi_report_dev(dip); 3869fa9e4066Sahrens 3870fa9e4066Sahrens return (DDI_SUCCESS); 3871fa9e4066Sahrens } 3872fa9e4066Sahrens 3873fa9e4066Sahrens static int 3874fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 3875fa9e4066Sahrens { 3876fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 3877fa9e4066Sahrens return (DDI_FAILURE); 3878fa9e4066Sahrens 3879fa9e4066Sahrens if (cmd != DDI_DETACH) 3880fa9e4066Sahrens return (DDI_FAILURE); 3881fa9e4066Sahrens 3882fa9e4066Sahrens zfs_dip = NULL; 3883fa9e4066Sahrens 3884fa9e4066Sahrens ddi_prop_remove_all(dip); 3885fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 3886fa9e4066Sahrens 3887fa9e4066Sahrens return (DDI_SUCCESS); 3888fa9e4066Sahrens } 3889fa9e4066Sahrens 3890fa9e4066Sahrens /*ARGSUSED*/ 3891fa9e4066Sahrens static int 3892fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 3893fa9e4066Sahrens { 3894fa9e4066Sahrens switch (infocmd) { 3895fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 3896fa9e4066Sahrens *result = zfs_dip; 3897fa9e4066Sahrens return (DDI_SUCCESS); 3898fa9e4066Sahrens 3899fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 3900a0965f35Sbonwick *result = (void *)0; 3901fa9e4066Sahrens return (DDI_SUCCESS); 3902fa9e4066Sahrens } 3903fa9e4066Sahrens 3904fa9e4066Sahrens return (DDI_FAILURE); 3905fa9e4066Sahrens } 3906fa9e4066Sahrens 3907fa9e4066Sahrens /* 3908fa9e4066Sahrens * OK, so this is a little weird. 3909fa9e4066Sahrens * 3910fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 3911fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 3912fa9e4066Sahrens * 3913fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 3914fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 3915fa9e4066Sahrens */ 3916fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 3917fa9e4066Sahrens zvol_open, /* open */ 3918fa9e4066Sahrens zvol_close, /* close */ 3919fa9e4066Sahrens zvol_strategy, /* strategy */ 3920fa9e4066Sahrens nodev, /* print */ 3921e7cbe64fSgw zvol_dump, /* dump */ 3922fa9e4066Sahrens zvol_read, /* read */ 3923fa9e4066Sahrens zvol_write, /* write */ 3924fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 3925fa9e4066Sahrens nodev, /* devmap */ 3926fa9e4066Sahrens nodev, /* mmap */ 3927fa9e4066Sahrens nodev, /* segmap */ 3928fa9e4066Sahrens nochpoll, /* poll */ 3929fa9e4066Sahrens ddi_prop_op, /* prop_op */ 3930fa9e4066Sahrens NULL, /* streamtab */ 3931fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 3932fa9e4066Sahrens CB_REV, /* version */ 3933feb08c6bSbillm nodev, /* async read */ 3934feb08c6bSbillm nodev, /* async write */ 3935fa9e4066Sahrens }; 3936fa9e4066Sahrens 3937fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 3938fa9e4066Sahrens DEVO_REV, /* version */ 3939fa9e4066Sahrens 0, /* refcnt */ 3940fa9e4066Sahrens zfs_info, /* info */ 3941fa9e4066Sahrens nulldev, /* identify */ 3942fa9e4066Sahrens nulldev, /* probe */ 3943fa9e4066Sahrens zfs_attach, /* attach */ 3944fa9e4066Sahrens zfs_detach, /* detach */ 3945fa9e4066Sahrens nodev, /* reset */ 3946fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 394719397407SSherry Moore NULL, /* no bus operations */ 394819397407SSherry Moore NULL, /* power */ 394919397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 3950fa9e4066Sahrens }; 3951fa9e4066Sahrens 3952fa9e4066Sahrens static struct modldrv zfs_modldrv = { 395319397407SSherry Moore &mod_driverops, 395419397407SSherry Moore "ZFS storage pool", 395519397407SSherry Moore &zfs_dev_ops 3956fa9e4066Sahrens }; 3957fa9e4066Sahrens 3958fa9e4066Sahrens static struct modlinkage modlinkage = { 3959fa9e4066Sahrens MODREV_1, 3960fa9e4066Sahrens (void *)&zfs_modlfs, 3961fa9e4066Sahrens (void *)&zfs_modldrv, 3962fa9e4066Sahrens NULL 3963fa9e4066Sahrens }; 3964fa9e4066Sahrens 3965ec533521Sfr 3966ec533521Sfr uint_t zfs_fsyncer_key; 3967f18faf3fSek extern uint_t rrw_tsd_key; 3968ec533521Sfr 3969fa9e4066Sahrens int 3970fa9e4066Sahrens _init(void) 3971fa9e4066Sahrens { 3972fa9e4066Sahrens int error; 3973fa9e4066Sahrens 3974a0965f35Sbonwick spa_init(FREAD | FWRITE); 3975a0965f35Sbonwick zfs_init(); 3976a0965f35Sbonwick zvol_init(); 3977a0965f35Sbonwick 3978a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 3979a0965f35Sbonwick zvol_fini(); 3980a0965f35Sbonwick zfs_fini(); 3981a0965f35Sbonwick spa_fini(); 3982fa9e4066Sahrens return (error); 3983a0965f35Sbonwick } 3984fa9e4066Sahrens 3985ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 3986f18faf3fSek tsd_create(&rrw_tsd_key, NULL); 3987ec533521Sfr 3988fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 3989fa9e4066Sahrens ASSERT(error == 0); 3990ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 3991fa9e4066Sahrens 3992fa9e4066Sahrens return (0); 3993fa9e4066Sahrens } 3994fa9e4066Sahrens 3995fa9e4066Sahrens int 3996fa9e4066Sahrens _fini(void) 3997fa9e4066Sahrens { 3998fa9e4066Sahrens int error; 3999fa9e4066Sahrens 4000ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 4001fa9e4066Sahrens return (EBUSY); 4002fa9e4066Sahrens 4003fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 4004fa9e4066Sahrens return (error); 4005fa9e4066Sahrens 4006fa9e4066Sahrens zvol_fini(); 4007fa9e4066Sahrens zfs_fini(); 4008fa9e4066Sahrens spa_fini(); 4009da6c28aaSamw if (zfs_nfsshare_inited) 4010ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 4011da6c28aaSamw if (zfs_smbshare_inited) 4012da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 4013da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 4014ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 4015fa9e4066Sahrens 4016ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 4017fa9e4066Sahrens ldi_ident_release(zfs_li); 4018fa9e4066Sahrens zfs_li = NULL; 4019ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 4020fa9e4066Sahrens 4021fa9e4066Sahrens return (error); 4022fa9e4066Sahrens } 4023fa9e4066Sahrens 4024fa9e4066Sahrens int 4025fa9e4066Sahrens _info(struct modinfo *modinfop) 4026fa9e4066Sahrens { 4027fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 4028fa9e4066Sahrens } 4029