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 */ 21ad135b5dSChristopher Siden 22fa9e4066Sahrens /* 233f9d6ad7SLin Ling * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 241df56adaSMartin Matuska * Portions Copyright 2011 Martin Matuska 25e9103aaeSGarrett D'Amore * Copyright 2011 Nexenta Systems, Inc. All rights reserved. 264e3c9f44SBill Pijewski * Copyright (c) 2012, Joyent, Inc. All rights reserved. 27*4445fffbSMatthew Ahrens * Copyright (c) 2012 by Delphix. All rights reserved. 28*4445fffbSMatthew Ahrens */ 29*4445fffbSMatthew Ahrens 30*4445fffbSMatthew Ahrens /* 31*4445fffbSMatthew Ahrens * ZFS ioctls. 32*4445fffbSMatthew Ahrens * 33*4445fffbSMatthew Ahrens * This file handles the ioctls to /dev/zfs, used for configuring ZFS storage 34*4445fffbSMatthew Ahrens * pools and filesystems, e.g. with /sbin/zfs and /sbin/zpool. 35*4445fffbSMatthew Ahrens * 36*4445fffbSMatthew Ahrens * There are two ways that we handle ioctls: the legacy way where almost 37*4445fffbSMatthew Ahrens * all of the logic is in the ioctl callback, and the new way where most 38*4445fffbSMatthew Ahrens * of the marshalling is handled in the common entry point, zfsdev_ioctl(). 39*4445fffbSMatthew Ahrens * 40*4445fffbSMatthew Ahrens * Non-legacy ioctls should be registered by calling 41*4445fffbSMatthew Ahrens * zfs_ioctl_register() from zfs_ioctl_init(). The ioctl is invoked 42*4445fffbSMatthew Ahrens * from userland by lzc_ioctl(). 43*4445fffbSMatthew Ahrens * 44*4445fffbSMatthew Ahrens * The registration arguments are as follows: 45*4445fffbSMatthew Ahrens * 46*4445fffbSMatthew Ahrens * const char *name 47*4445fffbSMatthew Ahrens * The name of the ioctl. This is used for history logging. If the 48*4445fffbSMatthew Ahrens * ioctl returns successfully (the callback returns 0), and allow_log 49*4445fffbSMatthew Ahrens * is true, then a history log entry will be recorded with the input & 50*4445fffbSMatthew Ahrens * output nvlists. The log entry can be printed with "zpool history -i". 51*4445fffbSMatthew Ahrens * 52*4445fffbSMatthew Ahrens * zfs_ioc_t ioc 53*4445fffbSMatthew Ahrens * The ioctl request number, which userland will pass to ioctl(2). 54*4445fffbSMatthew Ahrens * The ioctl numbers can change from release to release, because 55*4445fffbSMatthew Ahrens * the caller (libzfs) must be matched to the kernel. 56*4445fffbSMatthew Ahrens * 57*4445fffbSMatthew Ahrens * zfs_secpolicy_func_t *secpolicy 58*4445fffbSMatthew Ahrens * This function will be called before the zfs_ioc_func_t, to 59*4445fffbSMatthew Ahrens * determine if this operation is permitted. It should return EPERM 60*4445fffbSMatthew Ahrens * on failure, and 0 on success. Checks include determining if the 61*4445fffbSMatthew Ahrens * dataset is visible in this zone, and if the user has either all 62*4445fffbSMatthew Ahrens * zfs privileges in the zone (SYS_MOUNT), or has been granted permission 63*4445fffbSMatthew Ahrens * to do this operation on this dataset with "zfs allow". 64*4445fffbSMatthew Ahrens * 65*4445fffbSMatthew Ahrens * zfs_ioc_namecheck_t namecheck 66*4445fffbSMatthew Ahrens * This specifies what to expect in the zfs_cmd_t:zc_name -- a pool 67*4445fffbSMatthew Ahrens * name, a dataset name, or nothing. If the name is not well-formed, 68*4445fffbSMatthew Ahrens * the ioctl will fail and the callback will not be called. 69*4445fffbSMatthew Ahrens * Therefore, the callback can assume that the name is well-formed 70*4445fffbSMatthew Ahrens * (e.g. is null-terminated, doesn't have more than one '@' character, 71*4445fffbSMatthew Ahrens * doesn't have invalid characters). 72*4445fffbSMatthew Ahrens * 73*4445fffbSMatthew Ahrens * zfs_ioc_poolcheck_t pool_check 74*4445fffbSMatthew Ahrens * This specifies requirements on the pool state. If the pool does 75*4445fffbSMatthew Ahrens * not meet them (is suspended or is readonly), the ioctl will fail 76*4445fffbSMatthew Ahrens * and the callback will not be called. If any checks are specified 77*4445fffbSMatthew Ahrens * (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. 78*4445fffbSMatthew Ahrens * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | 79*4445fffbSMatthew Ahrens * POOL_CHECK_READONLY). 80*4445fffbSMatthew Ahrens * 81*4445fffbSMatthew Ahrens * boolean_t smush_outnvlist 82*4445fffbSMatthew Ahrens * If smush_outnvlist is true, then the output is presumed to be a 83*4445fffbSMatthew Ahrens * list of errors, and it will be "smushed" down to fit into the 84*4445fffbSMatthew Ahrens * caller's buffer, by removing some entries and replacing them with a 85*4445fffbSMatthew Ahrens * single "N_MORE_ERRORS" entry indicating how many were removed. See 86*4445fffbSMatthew Ahrens * nvlist_smush() for details. If smush_outnvlist is false, and the 87*4445fffbSMatthew Ahrens * outnvlist does not fit into the userland-provided buffer, then the 88*4445fffbSMatthew Ahrens * ioctl will fail with ENOMEM. 89*4445fffbSMatthew Ahrens * 90*4445fffbSMatthew Ahrens * zfs_ioc_func_t *func 91*4445fffbSMatthew Ahrens * The callback function that will perform the operation. 92*4445fffbSMatthew Ahrens * 93*4445fffbSMatthew Ahrens * The callback should return 0 on success, or an error number on 94*4445fffbSMatthew Ahrens * failure. If the function fails, the userland ioctl will return -1, 95*4445fffbSMatthew Ahrens * and errno will be set to the callback's return value. The callback 96*4445fffbSMatthew Ahrens * will be called with the following arguments: 97*4445fffbSMatthew Ahrens * 98*4445fffbSMatthew Ahrens * const char *name 99*4445fffbSMatthew Ahrens * The name of the pool or dataset to operate on, from 100*4445fffbSMatthew Ahrens * zfs_cmd_t:zc_name. The 'namecheck' argument specifies the 101*4445fffbSMatthew Ahrens * expected type (pool, dataset, or none). 102*4445fffbSMatthew Ahrens * 103*4445fffbSMatthew Ahrens * nvlist_t *innvl 104*4445fffbSMatthew Ahrens * The input nvlist, deserialized from zfs_cmd_t:zc_nvlist_src. Or 105*4445fffbSMatthew Ahrens * NULL if no input nvlist was provided. Changes to this nvlist are 106*4445fffbSMatthew Ahrens * ignored. If the input nvlist could not be deserialized, the 107*4445fffbSMatthew Ahrens * ioctl will fail and the callback will not be called. 108*4445fffbSMatthew Ahrens * 109*4445fffbSMatthew Ahrens * nvlist_t *outnvl 110*4445fffbSMatthew Ahrens * The output nvlist, initially empty. The callback can fill it in, 111*4445fffbSMatthew Ahrens * and it will be returned to userland by serializing it into 112*4445fffbSMatthew Ahrens * zfs_cmd_t:zc_nvlist_dst. If it is non-empty, and serialization 113*4445fffbSMatthew Ahrens * fails (e.g. because the caller didn't supply a large enough 114*4445fffbSMatthew Ahrens * buffer), then the overall ioctl will fail. See the 115*4445fffbSMatthew Ahrens * 'smush_nvlist' argument above for additional behaviors. 116*4445fffbSMatthew Ahrens * 117*4445fffbSMatthew Ahrens * There are two typical uses of the output nvlist: 118*4445fffbSMatthew Ahrens * - To return state, e.g. property values. In this case, 119*4445fffbSMatthew Ahrens * smush_outnvlist should be false. If the buffer was not large 120*4445fffbSMatthew Ahrens * enough, the caller will reallocate a larger buffer and try 121*4445fffbSMatthew Ahrens * the ioctl again. 122*4445fffbSMatthew Ahrens * 123*4445fffbSMatthew Ahrens * - To return multiple errors from an ioctl which makes on-disk 124*4445fffbSMatthew Ahrens * changes. In this case, smush_outnvlist should be true. 125*4445fffbSMatthew Ahrens * Ioctls which make on-disk modifications should generally not 126*4445fffbSMatthew Ahrens * use the outnvl if they succeed, because the caller can not 127*4445fffbSMatthew Ahrens * distinguish between the operation failing, and 128*4445fffbSMatthew Ahrens * deserialization failing. 129e9103aaeSGarrett D'Amore */ 130fa9e4066Sahrens 131fa9e4066Sahrens #include <sys/types.h> 132fa9e4066Sahrens #include <sys/param.h> 133fa9e4066Sahrens #include <sys/errno.h> 134fa9e4066Sahrens #include <sys/uio.h> 135fa9e4066Sahrens #include <sys/buf.h> 136fa9e4066Sahrens #include <sys/modctl.h> 137fa9e4066Sahrens #include <sys/open.h> 138fa9e4066Sahrens #include <sys/file.h> 139fa9e4066Sahrens #include <sys/kmem.h> 140fa9e4066Sahrens #include <sys/conf.h> 141fa9e4066Sahrens #include <sys/cmn_err.h> 142fa9e4066Sahrens #include <sys/stat.h> 143fa9e4066Sahrens #include <sys/zfs_ioctl.h> 1444201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 145da6c28aaSamw #include <sys/zfs_znode.h> 146fa9e4066Sahrens #include <sys/zap.h> 147fa9e4066Sahrens #include <sys/spa.h> 148b1b8ab34Slling #include <sys/spa_impl.h> 149fa9e4066Sahrens #include <sys/vdev.h> 1504201a95eSRic Aleshire #include <sys/priv_impl.h> 151fa9e4066Sahrens #include <sys/dmu.h> 152fa9e4066Sahrens #include <sys/dsl_dir.h> 153fa9e4066Sahrens #include <sys/dsl_dataset.h> 154fa9e4066Sahrens #include <sys/dsl_prop.h> 155ecd6cf80Smarks #include <sys/dsl_deleg.h> 156ecd6cf80Smarks #include <sys/dmu_objset.h> 1574e3c9f44SBill Pijewski #include <sys/dmu_impl.h> 158fa9e4066Sahrens #include <sys/ddi.h> 159fa9e4066Sahrens #include <sys/sunddi.h> 160fa9e4066Sahrens #include <sys/sunldi.h> 161fa9e4066Sahrens #include <sys/policy.h> 162fa9e4066Sahrens #include <sys/zone.h> 163fa9e4066Sahrens #include <sys/nvpair.h> 164fa9e4066Sahrens #include <sys/pathname.h> 165fa9e4066Sahrens #include <sys/mount.h> 166fa9e4066Sahrens #include <sys/sdt.h> 167fa9e4066Sahrens #include <sys/fs/zfs.h> 168fa9e4066Sahrens #include <sys/zfs_ctldir.h> 169da6c28aaSamw #include <sys/zfs_dir.h> 170c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 171a2eea2e1Sahrens #include <sys/zvol.h> 1723f9d6ad7SLin Ling #include <sys/dsl_scan.h> 173ecd6cf80Smarks #include <sharefs/share.h> 174f18faf3fSek #include <sys/dmu_objset.h> 175fa9e4066Sahrens 176fa9e4066Sahrens #include "zfs_namecheck.h" 177e9dbad6fSeschrock #include "zfs_prop.h" 178ecd6cf80Smarks #include "zfs_deleg.h" 1790a586ceaSMark Shellenbaum #include "zfs_comutil.h" 180fa9e4066Sahrens 181fa9e4066Sahrens extern struct modlfs zfs_modlfs; 182fa9e4066Sahrens 183fa9e4066Sahrens extern void zfs_init(void); 184fa9e4066Sahrens extern void zfs_fini(void); 185fa9e4066Sahrens 186fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 187fa9e4066Sahrens dev_info_t *zfs_dip; 188fa9e4066Sahrens 189*4445fffbSMatthew Ahrens uint_t zfs_fsyncer_key; 190*4445fffbSMatthew Ahrens extern uint_t rrw_tsd_key; 191*4445fffbSMatthew Ahrens static uint_t zfs_allow_log_key; 192*4445fffbSMatthew Ahrens 193*4445fffbSMatthew Ahrens typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); 194*4445fffbSMatthew Ahrens typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); 195*4445fffbSMatthew Ahrens typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); 196fa9e4066Sahrens 19754d692b7SGeorge Wilson typedef enum { 19854d692b7SGeorge Wilson NO_NAME, 19954d692b7SGeorge Wilson POOL_NAME, 20054d692b7SGeorge Wilson DATASET_NAME 20154d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 20254d692b7SGeorge Wilson 203f9af39baSGeorge Wilson typedef enum { 204f9af39baSGeorge Wilson POOL_CHECK_NONE = 1 << 0, 205f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED = 1 << 1, 206*4445fffbSMatthew Ahrens POOL_CHECK_READONLY = 1 << 2, 207f9af39baSGeorge Wilson } zfs_ioc_poolcheck_t; 208f9af39baSGeorge Wilson 209fa9e4066Sahrens typedef struct zfs_ioc_vec { 210*4445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *zvec_legacy_func; 211fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 212fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 21354d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 214*4445fffbSMatthew Ahrens boolean_t zvec_allow_log; 215f9af39baSGeorge Wilson zfs_ioc_poolcheck_t zvec_pool_check; 216*4445fffbSMatthew Ahrens boolean_t zvec_smush_outnvlist; 217*4445fffbSMatthew Ahrens const char *zvec_name; 218fa9e4066Sahrens } zfs_ioc_vec_t; 219fa9e4066Sahrens 22014843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 22114843421SMatthew Ahrens static const char *userquota_perms[] = { 22214843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 22314843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 22414843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 22514843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 22614843421SMatthew Ahrens }; 22714843421SMatthew Ahrens 22814843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 22992241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 23092241e0bSTom Erickson cred_t *cr); 23192241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 23292241e0bSTom Erickson nvlist_t **errors); 2330a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 2340a48a24eStimh boolean_t *); 235*4445fffbSMatthew Ahrens int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); 236*4445fffbSMatthew Ahrens static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp); 2370a48a24eStimh 238fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 239fa9e4066Sahrens void 240fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 241fa9e4066Sahrens { 242fa9e4066Sahrens const char *newfile; 2433f9d6ad7SLin Ling char buf[512]; 244fa9e4066Sahrens va_list adx; 245fa9e4066Sahrens 246fa9e4066Sahrens /* 247fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 248fa9e4066Sahrens */ 249fa9e4066Sahrens newfile = strrchr(file, '/'); 250fa9e4066Sahrens if (newfile != NULL) { 251fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 252fa9e4066Sahrens } else { 253fa9e4066Sahrens newfile = file; 254fa9e4066Sahrens } 255fa9e4066Sahrens 256fa9e4066Sahrens va_start(adx, fmt); 257fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 258fa9e4066Sahrens va_end(adx); 259fa9e4066Sahrens 260fa9e4066Sahrens /* 261fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 262fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 263fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 264fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 265fa9e4066Sahrens * arg0 = file name 266fa9e4066Sahrens * arg1 = function name 267fa9e4066Sahrens * arg2 = line number 268fa9e4066Sahrens * arg3 = message 269fa9e4066Sahrens */ 270fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 271fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 272fa9e4066Sahrens } 273fa9e4066Sahrens 274ecd6cf80Smarks static void 275228975ccSek history_str_free(char *buf) 276228975ccSek { 277228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 278228975ccSek } 279228975ccSek 280228975ccSek static char * 281228975ccSek history_str_get(zfs_cmd_t *zc) 282ecd6cf80Smarks { 28340feaa91Sahrens char *buf; 284ecd6cf80Smarks 285ecd6cf80Smarks if (zc->zc_history == NULL) 286228975ccSek return (NULL); 287e7437265Sahrens 288ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 289ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 290ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 291228975ccSek history_str_free(buf); 292228975ccSek return (NULL); 293ecd6cf80Smarks } 294ecd6cf80Smarks 295ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 296ecd6cf80Smarks 297228975ccSek return (buf); 298228975ccSek } 299ecd6cf80Smarks 30015e6edf1Sgw /* 30115e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 30215e6edf1Sgw */ 30315e6edf1Sgw static boolean_t 30415e6edf1Sgw zfs_is_bootfs(const char *name) 30515e6edf1Sgw { 306503ad85cSMatthew Ahrens objset_t *os; 30715e6edf1Sgw 308503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 309503ad85cSMatthew Ahrens boolean_t ret; 310b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 311503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 312503ad85cSMatthew Ahrens return (ret); 31315e6edf1Sgw } 314503ad85cSMatthew Ahrens return (B_FALSE); 31515e6edf1Sgw } 31615e6edf1Sgw 317c2a93d44Stimh /* 3180a48a24eStimh * zfs_earlier_version 319c2a93d44Stimh * 320c2a93d44Stimh * Return non-zero if the spa version is less than requested version. 321c2a93d44Stimh */ 322da6c28aaSamw static int 3230a48a24eStimh zfs_earlier_version(const char *name, int version) 324da6c28aaSamw { 325da6c28aaSamw spa_t *spa; 326da6c28aaSamw 327da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 328da6c28aaSamw if (spa_version(spa) < version) { 329da6c28aaSamw spa_close(spa, FTAG); 330da6c28aaSamw return (1); 331da6c28aaSamw } 332da6c28aaSamw spa_close(spa, FTAG); 333da6c28aaSamw } 334da6c28aaSamw return (0); 335da6c28aaSamw } 336da6c28aaSamw 3379e6eda55Smarks /* 338745cd3c5Smaybee * zpl_earlier_version 3399e6eda55Smarks * 340745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 3419e6eda55Smarks */ 342745cd3c5Smaybee static boolean_t 343745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 3449e6eda55Smarks { 3459e6eda55Smarks objset_t *os; 346745cd3c5Smaybee boolean_t rc = B_TRUE; 3479e6eda55Smarks 348503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 349745cd3c5Smaybee uint64_t zplversion; 3509e6eda55Smarks 351503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 352503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 353503ad85cSMatthew Ahrens return (B_TRUE); 354503ad85cSMatthew Ahrens } 355503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 356745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 357745cd3c5Smaybee rc = zplversion < version; 358503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3599e6eda55Smarks } 3609e6eda55Smarks return (rc); 3619e6eda55Smarks } 3629e6eda55Smarks 363228975ccSek static void 364228975ccSek zfs_log_history(zfs_cmd_t *zc) 365228975ccSek { 366228975ccSek spa_t *spa; 367228975ccSek char *buf; 368ecd6cf80Smarks 369228975ccSek if ((buf = history_str_get(zc)) == NULL) 370228975ccSek return; 371228975ccSek 372228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 373228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 374*4445fffbSMatthew Ahrens (void) spa_history_log(spa, buf); 375228975ccSek spa_close(spa, FTAG); 376228975ccSek } 377228975ccSek history_str_free(buf); 378ecd6cf80Smarks } 379ecd6cf80Smarks 380fa9e4066Sahrens /* 381fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 382fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 383fa9e4066Sahrens */ 384fa9e4066Sahrens /* ARGSUSED */ 385fa9e4066Sahrens static int 386*4445fffbSMatthew Ahrens zfs_secpolicy_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 387fa9e4066Sahrens { 388fa9e4066Sahrens return (0); 389fa9e4066Sahrens } 390fa9e4066Sahrens 391fa9e4066Sahrens /* 392fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 393fa9e4066Sahrens * no privileges, but must be visible in the local zone. 394fa9e4066Sahrens */ 395fa9e4066Sahrens /* ARGSUSED */ 396fa9e4066Sahrens static int 397*4445fffbSMatthew Ahrens zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 398fa9e4066Sahrens { 399fa9e4066Sahrens if (INGLOBALZONE(curproc) || 400ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 401fa9e4066Sahrens return (0); 402fa9e4066Sahrens 403fa9e4066Sahrens return (ENOENT); 404fa9e4066Sahrens } 405fa9e4066Sahrens 406fa9e4066Sahrens static int 407a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) 408fa9e4066Sahrens { 409fa9e4066Sahrens int writable = 1; 410fa9e4066Sahrens 411fa9e4066Sahrens /* 412fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 413fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 414fa9e4066Sahrens */ 415fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 416fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 417fa9e4066Sahrens return (ENOENT); 418fa9e4066Sahrens 419fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 420fa9e4066Sahrens /* 421fa9e4066Sahrens * If the fs is zoned, only root can access it from the 422fa9e4066Sahrens * global zone. 423fa9e4066Sahrens */ 424fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 425fa9e4066Sahrens return (EPERM); 426fa9e4066Sahrens } else { 427fa9e4066Sahrens /* 428fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 429fa9e4066Sahrens */ 430fa9e4066Sahrens if (!zoned) 431fa9e4066Sahrens return (EPERM); 432fa9e4066Sahrens 433fa9e4066Sahrens /* must be writable by this zone */ 434fa9e4066Sahrens if (!writable) 435fa9e4066Sahrens return (EPERM); 436fa9e4066Sahrens } 437fa9e4066Sahrens return (0); 438fa9e4066Sahrens } 439fa9e4066Sahrens 440a7f53a56SChris Kirby static int 441a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr) 442a7f53a56SChris Kirby { 443a7f53a56SChris Kirby uint64_t zoned; 444a7f53a56SChris Kirby 445a7f53a56SChris Kirby if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 446a7f53a56SChris Kirby return (ENOENT); 447a7f53a56SChris Kirby 448a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 449a7f53a56SChris Kirby } 450a7f53a56SChris Kirby 451a7f53a56SChris Kirby static int 452a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) 453a7f53a56SChris Kirby { 454a7f53a56SChris Kirby uint64_t zoned; 455a7f53a56SChris Kirby 456a7f53a56SChris Kirby rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER); 457a7f53a56SChris Kirby if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL)) { 458a7f53a56SChris Kirby rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); 459a7f53a56SChris Kirby return (ENOENT); 460a7f53a56SChris Kirby } 461a7f53a56SChris Kirby rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); 462a7f53a56SChris Kirby 463a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 464a7f53a56SChris Kirby } 465a7f53a56SChris Kirby 466*4445fffbSMatthew Ahrens static int 467ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 468fa9e4066Sahrens { 469fa9e4066Sahrens int error; 47019b94df9SMatthew Ahrens dsl_dataset_t *ds; 47119b94df9SMatthew Ahrens 47219b94df9SMatthew Ahrens error = dsl_dataset_hold(name, FTAG, &ds); 47319b94df9SMatthew Ahrens if (error != 0) 47419b94df9SMatthew Ahrens return (error); 475fa9e4066Sahrens 47619b94df9SMatthew Ahrens error = zfs_dozonecheck_ds(name, ds, cr); 477ecd6cf80Smarks if (error == 0) { 478ecd6cf80Smarks error = secpolicy_zfs(cr); 479db870a07Sahrens if (error) 480*4445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 481ecd6cf80Smarks } 48219b94df9SMatthew Ahrens 48319b94df9SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 484ecd6cf80Smarks return (error); 485ecd6cf80Smarks } 486ecd6cf80Smarks 487*4445fffbSMatthew Ahrens static int 488a7f53a56SChris Kirby zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, 489a7f53a56SChris Kirby const char *perm, cred_t *cr) 490a7f53a56SChris Kirby { 491a7f53a56SChris Kirby int error; 492a7f53a56SChris Kirby 493a7f53a56SChris Kirby error = zfs_dozonecheck_ds(name, ds, cr); 494a7f53a56SChris Kirby if (error == 0) { 495a7f53a56SChris Kirby error = secpolicy_zfs(cr); 496a7f53a56SChris Kirby if (error) 497*4445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 498a7f53a56SChris Kirby } 499a7f53a56SChris Kirby return (error); 500a7f53a56SChris Kirby } 501a7f53a56SChris Kirby 5024201a95eSRic Aleshire /* 5034201a95eSRic Aleshire * Policy for setting the security label property. 5044201a95eSRic Aleshire * 5054201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 5064201a95eSRic Aleshire */ 5074201a95eSRic Aleshire static int 50892241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 5094201a95eSRic Aleshire { 5104201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 5114201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 5124201a95eSRic Aleshire boolean_t new_default = FALSE; 5134201a95eSRic Aleshire uint64_t zoned; 5144201a95eSRic Aleshire int needed_priv = -1; 5154201a95eSRic Aleshire int error; 5164201a95eSRic Aleshire 5174201a95eSRic Aleshire /* First get the existing dataset label. */ 5184201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 5194201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 5204201a95eSRic Aleshire if (error) 5214201a95eSRic Aleshire return (EPERM); 5224201a95eSRic Aleshire 5234201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 5244201a95eSRic Aleshire new_default = TRUE; 5254201a95eSRic Aleshire 5264201a95eSRic Aleshire /* The label must be translatable */ 5274201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 5284201a95eSRic Aleshire return (EINVAL); 5294201a95eSRic Aleshire 5304201a95eSRic Aleshire /* 5314201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 5324201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 5334201a95eSRic Aleshire * are needed. 5344201a95eSRic Aleshire */ 5354201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 5364201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 5374201a95eSRic Aleshire return (EPERM); 5384201a95eSRic Aleshire return (0); 5394201a95eSRic Aleshire } 5404201a95eSRic Aleshire 5414201a95eSRic Aleshire /* 5424201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 5434201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 5444201a95eSRic Aleshire * global zone. 5454201a95eSRic Aleshire */ 5464201a95eSRic Aleshire if (dsl_prop_get_integer(name, 5474201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 5484201a95eSRic Aleshire return (EPERM); 5494201a95eSRic Aleshire if (!zoned) { 5504201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 5514201a95eSRic Aleshire return (EPERM); 5524201a95eSRic Aleshire } 5534201a95eSRic Aleshire 5544201a95eSRic Aleshire /* 5554201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 5564201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 5574201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 5584201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 5594201a95eSRic Aleshire */ 5604201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 56192241e0bSTom Erickson objset_t *os; 56292241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 56392241e0bSTom Erickson 5644201a95eSRic Aleshire /* 5654201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 5664201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 5674201a95eSRic Aleshire */ 5684201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 56992241e0bSTom Erickson setsl_tag, &os); 5704201a95eSRic Aleshire if (error) 5714201a95eSRic Aleshire return (EPERM); 5724201a95eSRic Aleshire 57392241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 57492241e0bSTom Erickson 5754201a95eSRic Aleshire if (new_default) { 5764201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 5774201a95eSRic Aleshire goto out_check; 5784201a95eSRic Aleshire } 5794201a95eSRic Aleshire 5804201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 5814201a95eSRic Aleshire return (EPERM); 5824201a95eSRic Aleshire 5834201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 5844201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 5854201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 5864201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 5874201a95eSRic Aleshire } else { 5884201a95eSRic Aleshire /* dataset currently has a default label */ 5894201a95eSRic Aleshire if (!new_default) 5904201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 5914201a95eSRic Aleshire } 5924201a95eSRic Aleshire 5934201a95eSRic Aleshire out_check: 5944201a95eSRic Aleshire if (needed_priv != -1) 5954201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 5964201a95eSRic Aleshire return (0); 5974201a95eSRic Aleshire } 5984201a95eSRic Aleshire 599ecd6cf80Smarks static int 60092241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 60192241e0bSTom Erickson cred_t *cr) 602ecd6cf80Smarks { 60392241e0bSTom Erickson char *strval; 60492241e0bSTom Erickson 605ecd6cf80Smarks /* 606ecd6cf80Smarks * Check permissions for special properties. 607ecd6cf80Smarks */ 608ecd6cf80Smarks switch (prop) { 609ecd6cf80Smarks case ZFS_PROP_ZONED: 610ecd6cf80Smarks /* 611ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 612ecd6cf80Smarks */ 613ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 614ecd6cf80Smarks return (EPERM); 615ecd6cf80Smarks break; 616ecd6cf80Smarks 617ecd6cf80Smarks case ZFS_PROP_QUOTA: 618ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 619ecd6cf80Smarks uint64_t zoned; 620ecd6cf80Smarks char setpoint[MAXNAMELEN]; 621ecd6cf80Smarks /* 622ecd6cf80Smarks * Unprivileged users are allowed to modify the 623ecd6cf80Smarks * quota on things *under* (ie. contained by) 624ecd6cf80Smarks * the thing they own. 625ecd6cf80Smarks */ 62692241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 627ecd6cf80Smarks setpoint)) 628ecd6cf80Smarks return (EPERM); 62992241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 630ecd6cf80Smarks return (EPERM); 631ecd6cf80Smarks } 632db870a07Sahrens break; 6334201a95eSRic Aleshire 6344201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 6354201a95eSRic Aleshire if (!is_system_labeled()) 6364201a95eSRic Aleshire return (EPERM); 63792241e0bSTom Erickson 63892241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 63992241e0bSTom Erickson int err; 64092241e0bSTom Erickson 64192241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 64292241e0bSTom Erickson if (err != 0) 64392241e0bSTom Erickson return (err); 64492241e0bSTom Erickson } 6454201a95eSRic Aleshire break; 646ecd6cf80Smarks } 647ecd6cf80Smarks 64892241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 649ecd6cf80Smarks } 650ecd6cf80Smarks 651*4445fffbSMatthew Ahrens /* ARGSUSED */ 652*4445fffbSMatthew Ahrens static int 653*4445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 654ecd6cf80Smarks { 655ecd6cf80Smarks int error; 656ecd6cf80Smarks 657ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 658ecd6cf80Smarks if (error) 659fa9e4066Sahrens return (error); 660fa9e4066Sahrens 661ecd6cf80Smarks /* 662ecd6cf80Smarks * permission to set permissions will be evaluated later in 663ecd6cf80Smarks * dsl_deleg_can_allow() 664ecd6cf80Smarks */ 665ecd6cf80Smarks return (0); 666ecd6cf80Smarks } 667ecd6cf80Smarks 668*4445fffbSMatthew Ahrens /* ARGSUSED */ 669*4445fffbSMatthew Ahrens static int 670*4445fffbSMatthew Ahrens zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 671ecd6cf80Smarks { 672681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 673681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 674ecd6cf80Smarks } 675ecd6cf80Smarks 676*4445fffbSMatthew Ahrens /* ARGSUSED */ 677*4445fffbSMatthew Ahrens static int 678*4445fffbSMatthew Ahrens zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 679ecd6cf80Smarks { 680a7f53a56SChris Kirby spa_t *spa; 681a7f53a56SChris Kirby dsl_pool_t *dp; 682a7f53a56SChris Kirby dsl_dataset_t *ds; 683a7f53a56SChris Kirby char *cp; 684a7f53a56SChris Kirby int error; 685a7f53a56SChris Kirby 686a7f53a56SChris Kirby /* 687a7f53a56SChris Kirby * Generate the current snapshot name from the given objsetid, then 688a7f53a56SChris Kirby * use that name for the secpolicy/zone checks. 689a7f53a56SChris Kirby */ 690a7f53a56SChris Kirby cp = strchr(zc->zc_name, '@'); 691a7f53a56SChris Kirby if (cp == NULL) 692a7f53a56SChris Kirby return (EINVAL); 693a7f53a56SChris Kirby error = spa_open(zc->zc_name, &spa, FTAG); 694a7f53a56SChris Kirby if (error) 695a7f53a56SChris Kirby return (error); 696a7f53a56SChris Kirby 697a7f53a56SChris Kirby dp = spa_get_dsl(spa); 698a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 699a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 700a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 701a7f53a56SChris Kirby spa_close(spa, FTAG); 702a7f53a56SChris Kirby if (error) 703a7f53a56SChris Kirby return (error); 704a7f53a56SChris Kirby 705a7f53a56SChris Kirby dsl_dataset_name(ds, zc->zc_name); 706a7f53a56SChris Kirby 707a7f53a56SChris Kirby error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, 708a7f53a56SChris Kirby ZFS_DELEG_PERM_SEND, cr); 709a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 710a7f53a56SChris Kirby 711a7f53a56SChris Kirby return (error); 712ecd6cf80Smarks } 713ecd6cf80Smarks 714*4445fffbSMatthew Ahrens /* ARGSUSED */ 715743a77edSAlan Wright static int 716*4445fffbSMatthew Ahrens zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 717*4445fffbSMatthew Ahrens { 718*4445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 719*4445fffbSMatthew Ahrens ZFS_DELEG_PERM_SEND, cr)); 720*4445fffbSMatthew Ahrens } 721*4445fffbSMatthew Ahrens 722*4445fffbSMatthew Ahrens /* ARGSUSED */ 723*4445fffbSMatthew Ahrens static int 724*4445fffbSMatthew Ahrens zfs_secpolicy_deleg_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 725743a77edSAlan Wright { 726743a77edSAlan Wright vnode_t *vp; 727743a77edSAlan Wright int error; 728743a77edSAlan Wright 729743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 730743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 731743a77edSAlan Wright return (error); 732743a77edSAlan Wright 733743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 734743a77edSAlan Wright 735743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 736743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 737743a77edSAlan Wright zc->zc_name) != 0)) { 738743a77edSAlan Wright VN_RELE(vp); 739743a77edSAlan Wright return (EPERM); 740743a77edSAlan Wright } 741743a77edSAlan Wright 742743a77edSAlan Wright VN_RELE(vp); 743743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 744743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 745743a77edSAlan Wright } 746743a77edSAlan Wright 747ecd6cf80Smarks int 748*4445fffbSMatthew Ahrens zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 749ecd6cf80Smarks { 750ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 751ecd6cf80Smarks return (EPERM); 752ecd6cf80Smarks 7533cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 754ecd6cf80Smarks return (0); 755ecd6cf80Smarks } else { 756*4445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 757743a77edSAlan Wright } 758743a77edSAlan Wright } 759ecd6cf80Smarks 760743a77edSAlan Wright int 761*4445fffbSMatthew Ahrens zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 762743a77edSAlan Wright { 763743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 764743a77edSAlan Wright return (EPERM); 765ecd6cf80Smarks 766743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 767743a77edSAlan Wright return (0); 768743a77edSAlan Wright } else { 769*4445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 770ecd6cf80Smarks } 771fa9e4066Sahrens } 772fa9e4066Sahrens 773fa9e4066Sahrens static int 774ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 775fa9e4066Sahrens { 776fa9e4066Sahrens char *cp; 777fa9e4066Sahrens 778fa9e4066Sahrens /* 779fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 780fa9e4066Sahrens */ 781ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 782ecd6cf80Smarks cp = strrchr(parent, '@'); 783fa9e4066Sahrens if (cp != NULL) { 784fa9e4066Sahrens cp[0] = '\0'; 785fa9e4066Sahrens } else { 786ecd6cf80Smarks cp = strrchr(parent, '/'); 787fa9e4066Sahrens if (cp == NULL) 788fa9e4066Sahrens return (ENOENT); 789fa9e4066Sahrens cp[0] = '\0'; 790ecd6cf80Smarks } 791ecd6cf80Smarks 792ecd6cf80Smarks return (0); 793ecd6cf80Smarks } 794ecd6cf80Smarks 795ecd6cf80Smarks int 796ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 797ecd6cf80Smarks { 798ecd6cf80Smarks int error; 799ecd6cf80Smarks 800ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 801ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 802ecd6cf80Smarks return (error); 803ecd6cf80Smarks 804ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 805ecd6cf80Smarks } 806ecd6cf80Smarks 807*4445fffbSMatthew Ahrens /* ARGSUSED */ 808ecd6cf80Smarks static int 809*4445fffbSMatthew Ahrens zfs_secpolicy_destroy(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 810ecd6cf80Smarks { 811ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 812ecd6cf80Smarks } 813ecd6cf80Smarks 814cbf6f6aaSWilliam Gorrell /* 815cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 816*4445fffbSMatthew Ahrens * descendant mount and destroy permissions. 817cbf6f6aaSWilliam Gorrell */ 818*4445fffbSMatthew Ahrens /* ARGSUSED */ 819cbf6f6aaSWilliam Gorrell static int 820*4445fffbSMatthew Ahrens zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 821cbf6f6aaSWilliam Gorrell { 822*4445fffbSMatthew Ahrens nvlist_t *snaps; 823*4445fffbSMatthew Ahrens nvpair_t *pair, *nextpair; 824*4445fffbSMatthew Ahrens int error = 0; 825cbf6f6aaSWilliam Gorrell 826*4445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 827*4445fffbSMatthew Ahrens return (EINVAL); 828*4445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 829*4445fffbSMatthew Ahrens pair = nextpair) { 830*4445fffbSMatthew Ahrens dsl_dataset_t *ds; 831cbf6f6aaSWilliam Gorrell 832*4445fffbSMatthew Ahrens nextpair = nvlist_next_nvpair(snaps, pair); 833*4445fffbSMatthew Ahrens error = dsl_dataset_hold(nvpair_name(pair), FTAG, &ds); 834*4445fffbSMatthew Ahrens if (error == 0) { 835*4445fffbSMatthew Ahrens dsl_dataset_rele(ds, FTAG); 836*4445fffbSMatthew Ahrens } else if (error == ENOENT) { 837*4445fffbSMatthew Ahrens /* 838*4445fffbSMatthew Ahrens * Ignore any snapshots that don't exist (we consider 839*4445fffbSMatthew Ahrens * them "already destroyed"). Remove the name from the 840*4445fffbSMatthew Ahrens * nvl here in case the snapshot is created between 841*4445fffbSMatthew Ahrens * now and when we try to destroy it (in which case 842*4445fffbSMatthew Ahrens * we don't want to destroy it since we haven't 843*4445fffbSMatthew Ahrens * checked for permission). 844*4445fffbSMatthew Ahrens */ 845*4445fffbSMatthew Ahrens fnvlist_remove_nvpair(snaps, pair); 846*4445fffbSMatthew Ahrens error = 0; 847*4445fffbSMatthew Ahrens continue; 848*4445fffbSMatthew Ahrens } else { 849*4445fffbSMatthew Ahrens break; 850*4445fffbSMatthew Ahrens } 851*4445fffbSMatthew Ahrens error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); 852*4445fffbSMatthew Ahrens if (error != 0) 853*4445fffbSMatthew Ahrens break; 854*4445fffbSMatthew Ahrens } 855cbf6f6aaSWilliam Gorrell 856cbf6f6aaSWilliam Gorrell return (error); 857cbf6f6aaSWilliam Gorrell } 858cbf6f6aaSWilliam Gorrell 859ecd6cf80Smarks int 860ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 861ecd6cf80Smarks { 86292241e0bSTom Erickson char parentname[MAXNAMELEN]; 863ecd6cf80Smarks int error; 864ecd6cf80Smarks 865ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 866ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 867ecd6cf80Smarks return (error); 868ecd6cf80Smarks 869ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 870ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 871ecd6cf80Smarks return (error); 872ecd6cf80Smarks 873ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 874ecd6cf80Smarks sizeof (parentname))) != 0) 875ecd6cf80Smarks return (error); 876ecd6cf80Smarks 877ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 878ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 879ecd6cf80Smarks return (error); 880ecd6cf80Smarks 881ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 882ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 883ecd6cf80Smarks return (error); 884ecd6cf80Smarks 885ecd6cf80Smarks return (error); 886ecd6cf80Smarks } 887ecd6cf80Smarks 888*4445fffbSMatthew Ahrens /* ARGSUSED */ 889ecd6cf80Smarks static int 890*4445fffbSMatthew Ahrens zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 891ecd6cf80Smarks { 892ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 893ecd6cf80Smarks } 894ecd6cf80Smarks 895*4445fffbSMatthew Ahrens /* ARGSUSED */ 896ecd6cf80Smarks static int 897*4445fffbSMatthew Ahrens zfs_secpolicy_promote(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 898ecd6cf80Smarks { 89992241e0bSTom Erickson char parentname[MAXNAMELEN]; 900ecd6cf80Smarks objset_t *clone; 901ecd6cf80Smarks int error; 902ecd6cf80Smarks 903ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 904ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 905ecd6cf80Smarks if (error) 906ecd6cf80Smarks return (error); 907ecd6cf80Smarks 908503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &clone); 909ecd6cf80Smarks 910ecd6cf80Smarks if (error == 0) { 911ecd6cf80Smarks dsl_dataset_t *pclone = NULL; 912ecd6cf80Smarks dsl_dir_t *dd; 913503ad85cSMatthew Ahrens dd = clone->os_dsl_dataset->ds_dir; 914ecd6cf80Smarks 915ecd6cf80Smarks rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER); 916745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 917745cd3c5Smaybee dd->dd_phys->dd_origin_obj, FTAG, &pclone); 918ecd6cf80Smarks rw_exit(&dd->dd_pool->dp_config_rwlock); 919ecd6cf80Smarks if (error) { 920503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 921ecd6cf80Smarks return (error); 922ecd6cf80Smarks } 923ecd6cf80Smarks 924ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 925ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 926ecd6cf80Smarks 927ecd6cf80Smarks dsl_dataset_name(pclone, parentname); 928503ad85cSMatthew Ahrens dmu_objset_rele(clone, FTAG); 929745cd3c5Smaybee dsl_dataset_rele(pclone, FTAG); 930ecd6cf80Smarks if (error == 0) 931ecd6cf80Smarks error = zfs_secpolicy_write_perms(parentname, 932ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 933ecd6cf80Smarks } 934ecd6cf80Smarks return (error); 935ecd6cf80Smarks } 936ecd6cf80Smarks 937*4445fffbSMatthew Ahrens /* ARGSUSED */ 938ecd6cf80Smarks static int 939*4445fffbSMatthew Ahrens zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 940ecd6cf80Smarks { 941ecd6cf80Smarks int error; 942ecd6cf80Smarks 943ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 944ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 945ecd6cf80Smarks return (error); 946ecd6cf80Smarks 947ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 948ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 949ecd6cf80Smarks return (error); 950ecd6cf80Smarks 951ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 952ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 953ecd6cf80Smarks } 954ecd6cf80Smarks 955ecd6cf80Smarks int 956ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 957ecd6cf80Smarks { 958681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 959681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 960ecd6cf80Smarks } 961ecd6cf80Smarks 962*4445fffbSMatthew Ahrens /* 963*4445fffbSMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 964*4445fffbSMatthew Ahrens */ 965*4445fffbSMatthew Ahrens /* ARGSUSED */ 966ecd6cf80Smarks static int 967*4445fffbSMatthew Ahrens zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 968ecd6cf80Smarks { 969*4445fffbSMatthew Ahrens nvlist_t *snaps; 970*4445fffbSMatthew Ahrens int error; 971*4445fffbSMatthew Ahrens nvpair_t *pair; 972ecd6cf80Smarks 973*4445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 974*4445fffbSMatthew Ahrens return (EINVAL); 975*4445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 976*4445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 977*4445fffbSMatthew Ahrens char *name = nvpair_name(pair); 978*4445fffbSMatthew Ahrens char *atp = strchr(name, '@'); 979*4445fffbSMatthew Ahrens 980*4445fffbSMatthew Ahrens if (atp == NULL) { 981*4445fffbSMatthew Ahrens error = EINVAL; 982*4445fffbSMatthew Ahrens break; 983*4445fffbSMatthew Ahrens } 984*4445fffbSMatthew Ahrens *atp = '\0'; 985*4445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(name, cr); 986*4445fffbSMatthew Ahrens *atp = '@'; 987*4445fffbSMatthew Ahrens if (error != 0) 988*4445fffbSMatthew Ahrens break; 989*4445fffbSMatthew Ahrens } 990*4445fffbSMatthew Ahrens return (error); 991ecd6cf80Smarks } 992ecd6cf80Smarks 993*4445fffbSMatthew Ahrens /* ARGSUSED */ 994ecd6cf80Smarks static int 995*4445fffbSMatthew Ahrens zfs_secpolicy_log_history(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 996*4445fffbSMatthew Ahrens { 997*4445fffbSMatthew Ahrens /* 998*4445fffbSMatthew Ahrens * Even root must have a proper TSD so that we know what pool 999*4445fffbSMatthew Ahrens * to log to. 1000*4445fffbSMatthew Ahrens */ 1001*4445fffbSMatthew Ahrens if (tsd_get(zfs_allow_log_key) == NULL) 1002*4445fffbSMatthew Ahrens return (EPERM); 1003*4445fffbSMatthew Ahrens return (0); 1004*4445fffbSMatthew Ahrens } 1005*4445fffbSMatthew Ahrens 1006*4445fffbSMatthew Ahrens static int 1007*4445fffbSMatthew Ahrens zfs_secpolicy_create_clone(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1008ecd6cf80Smarks { 100992241e0bSTom Erickson char parentname[MAXNAMELEN]; 101092241e0bSTom Erickson int error; 1011*4445fffbSMatthew Ahrens char *origin; 1012ecd6cf80Smarks 1013ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 1014ecd6cf80Smarks sizeof (parentname))) != 0) 1015ecd6cf80Smarks return (error); 1016fa9e4066Sahrens 1017*4445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin) == 0 && 1018*4445fffbSMatthew Ahrens (error = zfs_secpolicy_write_perms(origin, 1019*4445fffbSMatthew Ahrens ZFS_DELEG_PERM_CLONE, cr)) != 0) 1020*4445fffbSMatthew Ahrens return (error); 1021fa9e4066Sahrens 1022ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 1023ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 1024ecd6cf80Smarks return (error); 1025ecd6cf80Smarks 1026*4445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(parentname, 1027*4445fffbSMatthew Ahrens ZFS_DELEG_PERM_MOUNT, cr)); 1028fa9e4066Sahrens } 1029fa9e4066Sahrens 1030fa9e4066Sahrens /* 1031fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 1032fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 1033fa9e4066Sahrens */ 1034fa9e4066Sahrens /* ARGSUSED */ 1035fa9e4066Sahrens static int 1036*4445fffbSMatthew Ahrens zfs_secpolicy_config(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1037fa9e4066Sahrens { 1038fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 1039fa9e4066Sahrens return (EPERM); 1040fa9e4066Sahrens 1041fa9e4066Sahrens return (0); 1042fa9e4066Sahrens } 1043fa9e4066Sahrens 104499d5e173STim Haley /* 104599d5e173STim Haley * Policy for object to name lookups. 104699d5e173STim Haley */ 104799d5e173STim Haley /* ARGSUSED */ 104899d5e173STim Haley static int 1049*4445fffbSMatthew Ahrens zfs_secpolicy_diff(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 105099d5e173STim Haley { 105199d5e173STim Haley int error; 105299d5e173STim Haley 105399d5e173STim Haley if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0) 105499d5e173STim Haley return (0); 105599d5e173STim Haley 105699d5e173STim Haley error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr); 105799d5e173STim Haley return (error); 105899d5e173STim Haley } 105999d5e173STim Haley 1060ea8dc4b6Seschrock /* 1061ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 1062ea8dc4b6Seschrock */ 1063ea8dc4b6Seschrock /* ARGSUSED */ 1064ea8dc4b6Seschrock static int 1065*4445fffbSMatthew Ahrens zfs_secpolicy_inject(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1066ea8dc4b6Seschrock { 1067ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 1068ea8dc4b6Seschrock } 1069ea8dc4b6Seschrock 1070*4445fffbSMatthew Ahrens /* ARGSUSED */ 1071e45ce728Sahrens static int 1072*4445fffbSMatthew Ahrens zfs_secpolicy_inherit_prop(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1073e45ce728Sahrens { 1074e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 1075e45ce728Sahrens 1076990b4856Slling if (prop == ZPROP_INVAL) { 1077e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 1078e45ce728Sahrens return (EINVAL); 1079e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 1080e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 1081e45ce728Sahrens } else { 108292241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 108392241e0bSTom Erickson NULL, cr)); 1084e45ce728Sahrens } 1085e45ce728Sahrens } 1086e45ce728Sahrens 108714843421SMatthew Ahrens static int 1088*4445fffbSMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 108914843421SMatthew Ahrens { 1090*4445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 109114843421SMatthew Ahrens if (err) 109214843421SMatthew Ahrens return (err); 109314843421SMatthew Ahrens 109414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 109514843421SMatthew Ahrens return (EINVAL); 109614843421SMatthew Ahrens 109714843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 109814843421SMatthew Ahrens /* 109914843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 110014843421SMatthew Ahrens * themself, allow it. 110114843421SMatthew Ahrens */ 110214843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 110314843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 110414843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 110514843421SMatthew Ahrens return (0); 110614843421SMatthew Ahrens } else { 110714843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 110814843421SMatthew Ahrens return (0); 110914843421SMatthew Ahrens } 111014843421SMatthew Ahrens } 111114843421SMatthew Ahrens 111214843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 111314843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 111414843421SMatthew Ahrens } 111514843421SMatthew Ahrens 111614843421SMatthew Ahrens static int 1117*4445fffbSMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 111814843421SMatthew Ahrens { 1119*4445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 112014843421SMatthew Ahrens if (err) 112114843421SMatthew Ahrens return (err); 112214843421SMatthew Ahrens 112314843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 112414843421SMatthew Ahrens return (EINVAL); 112514843421SMatthew Ahrens 112614843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 112714843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 112814843421SMatthew Ahrens } 112914843421SMatthew Ahrens 1130*4445fffbSMatthew Ahrens /* ARGSUSED */ 113114843421SMatthew Ahrens static int 1132*4445fffbSMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 113314843421SMatthew Ahrens { 113492241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 113592241e0bSTom Erickson NULL, cr)); 113614843421SMatthew Ahrens } 113714843421SMatthew Ahrens 1138*4445fffbSMatthew Ahrens /* ARGSUSED */ 1139842727c2SChris Kirby static int 1140*4445fffbSMatthew Ahrens zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1141842727c2SChris Kirby { 1142842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 1143842727c2SChris Kirby ZFS_DELEG_PERM_HOLD, cr)); 1144842727c2SChris Kirby } 1145842727c2SChris Kirby 1146*4445fffbSMatthew Ahrens /* ARGSUSED */ 1147842727c2SChris Kirby static int 1148*4445fffbSMatthew Ahrens zfs_secpolicy_release(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1149842727c2SChris Kirby { 1150842727c2SChris Kirby return (zfs_secpolicy_write_perms(zc->zc_name, 1151842727c2SChris Kirby ZFS_DELEG_PERM_RELEASE, cr)); 1152842727c2SChris Kirby } 1153842727c2SChris Kirby 115499d5e173STim Haley /* 115599d5e173STim Haley * Policy for allowing temporary snapshots to be taken or released 115699d5e173STim Haley */ 115799d5e173STim Haley static int 1158*4445fffbSMatthew Ahrens zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 115999d5e173STim Haley { 116099d5e173STim Haley /* 116199d5e173STim Haley * A temporary snapshot is the same as a snapshot, 116299d5e173STim Haley * hold, destroy and release all rolled into one. 116399d5e173STim Haley * Delegated diff alone is sufficient that we allow this. 116499d5e173STim Haley */ 116599d5e173STim Haley int error; 116699d5e173STim Haley 116799d5e173STim Haley if ((error = zfs_secpolicy_write_perms(zc->zc_name, 116899d5e173STim Haley ZFS_DELEG_PERM_DIFF, cr)) == 0) 116999d5e173STim Haley return (0); 117099d5e173STim Haley 1171*4445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); 117299d5e173STim Haley if (!error) 1173*4445fffbSMatthew Ahrens error = zfs_secpolicy_hold(zc, innvl, cr); 117499d5e173STim Haley if (!error) 1175*4445fffbSMatthew Ahrens error = zfs_secpolicy_release(zc, innvl, cr); 117699d5e173STim Haley if (!error) 1177*4445fffbSMatthew Ahrens error = zfs_secpolicy_destroy(zc, innvl, cr); 117899d5e173STim Haley return (error); 117999d5e173STim Haley } 118099d5e173STim Haley 1181fa9e4066Sahrens /* 1182fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 1183fa9e4066Sahrens */ 1184fa9e4066Sahrens static int 1185478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 1186fa9e4066Sahrens { 1187fa9e4066Sahrens char *packed; 1188fa9e4066Sahrens int error; 1189990b4856Slling nvlist_t *list = NULL; 1190fa9e4066Sahrens 1191fa9e4066Sahrens /* 1192e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 1193fa9e4066Sahrens */ 1194990b4856Slling if (size == 0) 1195fa9e4066Sahrens return (EINVAL); 1196fa9e4066Sahrens 1197fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 1198fa9e4066Sahrens 1199478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 1200478ed9adSEric Taylor iflag)) != 0) { 1201fa9e4066Sahrens kmem_free(packed, size); 1202fa9e4066Sahrens return (error); 1203fa9e4066Sahrens } 1204fa9e4066Sahrens 1205990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 1206fa9e4066Sahrens kmem_free(packed, size); 1207fa9e4066Sahrens return (error); 1208fa9e4066Sahrens } 1209fa9e4066Sahrens 1210fa9e4066Sahrens kmem_free(packed, size); 1211fa9e4066Sahrens 1212990b4856Slling *nvp = list; 1213fa9e4066Sahrens return (0); 1214fa9e4066Sahrens } 1215fa9e4066Sahrens 1216*4445fffbSMatthew Ahrens /* 1217*4445fffbSMatthew Ahrens * Reduce the size of this nvlist until it can be serialized in 'max' bytes. 1218*4445fffbSMatthew Ahrens * Entries will be removed from the end of the nvlist, and one int32 entry 1219*4445fffbSMatthew Ahrens * named "N_MORE_ERRORS" will be added indicating how many entries were 1220*4445fffbSMatthew Ahrens * removed. 1221*4445fffbSMatthew Ahrens */ 122292241e0bSTom Erickson static int 1223*4445fffbSMatthew Ahrens nvlist_smush(nvlist_t *errors, size_t max) 122492241e0bSTom Erickson { 122592241e0bSTom Erickson size_t size; 122692241e0bSTom Erickson 1227*4445fffbSMatthew Ahrens size = fnvlist_size(errors); 122892241e0bSTom Erickson 1229*4445fffbSMatthew Ahrens if (size > max) { 123092241e0bSTom Erickson nvpair_t *more_errors; 123192241e0bSTom Erickson int n = 0; 123292241e0bSTom Erickson 1233*4445fffbSMatthew Ahrens if (max < 1024) 123492241e0bSTom Erickson return (ENOMEM); 123592241e0bSTom Erickson 1236*4445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, 0); 1237*4445fffbSMatthew Ahrens more_errors = nvlist_prev_nvpair(errors, NULL); 123892241e0bSTom Erickson 123992241e0bSTom Erickson do { 1240*4445fffbSMatthew Ahrens nvpair_t *pair = nvlist_prev_nvpair(errors, 124192241e0bSTom Erickson more_errors); 1242*4445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, pair); 124392241e0bSTom Erickson n++; 1244*4445fffbSMatthew Ahrens size = fnvlist_size(errors); 1245*4445fffbSMatthew Ahrens } while (size > max); 124692241e0bSTom Erickson 1247*4445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, more_errors); 1248*4445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, n); 1249*4445fffbSMatthew Ahrens ASSERT3U(fnvlist_size(errors), <=, max); 125092241e0bSTom Erickson } 125192241e0bSTom Erickson 125292241e0bSTom Erickson return (0); 125392241e0bSTom Erickson } 125492241e0bSTom Erickson 1255e9dbad6fSeschrock static int 1256e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 1257e9dbad6fSeschrock { 1258e9dbad6fSeschrock char *packed = NULL; 12596e27f868SSam Falkner int error = 0; 1260e9dbad6fSeschrock size_t size; 1261e9dbad6fSeschrock 1262*4445fffbSMatthew Ahrens size = fnvlist_size(nvl); 1263e9dbad6fSeschrock 1264e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 1265e9dbad6fSeschrock error = ENOMEM; 1266e9dbad6fSeschrock } else { 1267*4445fffbSMatthew Ahrens packed = fnvlist_pack(nvl, &size); 12686e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 12696e27f868SSam Falkner size, zc->zc_iflags) != 0) 12706e27f868SSam Falkner error = EFAULT; 1271*4445fffbSMatthew Ahrens fnvlist_pack_free(packed, size); 1272e9dbad6fSeschrock } 1273e9dbad6fSeschrock 1274e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 1275*4445fffbSMatthew Ahrens zc->zc_nvlist_dst_filled = B_TRUE; 1276e9dbad6fSeschrock return (error); 1277e9dbad6fSeschrock } 1278e9dbad6fSeschrock 127914843421SMatthew Ahrens static int 1280af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 128114843421SMatthew Ahrens { 128214843421SMatthew Ahrens objset_t *os; 128314843421SMatthew Ahrens int error; 128414843421SMatthew Ahrens 1285503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 128614843421SMatthew Ahrens if (error) 128714843421SMatthew Ahrens return (error); 1288503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1289503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1290503ad85cSMatthew Ahrens return (EINVAL); 1291503ad85cSMatthew Ahrens } 129214843421SMatthew Ahrens 1293503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 1294af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 1295af4c679fSSean McEnroe if (*zfvp) { 1296af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 129714843421SMatthew Ahrens } else { 129814843421SMatthew Ahrens error = ESRCH; 129914843421SMatthew Ahrens } 1300503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 1301503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 130214843421SMatthew Ahrens return (error); 130314843421SMatthew Ahrens } 130414843421SMatthew Ahrens 130514843421SMatthew Ahrens /* 130614843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 130714843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 1308ad135b5dSChristopher Siden * If 'writer' is set, the z_teardown_lock will be held for RW_WRITER, 1309ad135b5dSChristopher Siden * which prevents all vnode ops from running. 131014843421SMatthew Ahrens */ 131114843421SMatthew Ahrens static int 13121412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) 131314843421SMatthew Ahrens { 131414843421SMatthew Ahrens int error = 0; 131514843421SMatthew Ahrens 1316af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1317af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 131814843421SMatthew Ahrens if (error == 0) { 13191412a1a2SMark Shellenbaum rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : 13201412a1a2SMark Shellenbaum RW_READER, tag); 1321af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 132214843421SMatthew Ahrens /* 132314843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 132414843421SMatthew Ahrens * thread should be just about to disassociate the 132514843421SMatthew Ahrens * objset from the zfsvfs. 132614843421SMatthew Ahrens */ 1327af4c679fSSean McEnroe rrw_exit(&(*zfvp)->z_teardown_lock, tag); 132814843421SMatthew Ahrens return (EBUSY); 132914843421SMatthew Ahrens } 133014843421SMatthew Ahrens } 133114843421SMatthew Ahrens return (error); 133214843421SMatthew Ahrens } 133314843421SMatthew Ahrens 133414843421SMatthew Ahrens static void 133514843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 133614843421SMatthew Ahrens { 133714843421SMatthew Ahrens rrw_exit(&zfsvfs->z_teardown_lock, tag); 133814843421SMatthew Ahrens 133914843421SMatthew Ahrens if (zfsvfs->z_vfs) { 134014843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 134114843421SMatthew Ahrens } else { 1342503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 134314843421SMatthew Ahrens zfsvfs_free(zfsvfs); 134414843421SMatthew Ahrens } 134514843421SMatthew Ahrens } 134614843421SMatthew Ahrens 1347fa9e4066Sahrens static int 1348fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1349fa9e4066Sahrens { 1350fa9e4066Sahrens int error; 1351990b4856Slling nvlist_t *config, *props = NULL; 13520a48a24eStimh nvlist_t *rootprops = NULL; 13530a48a24eStimh nvlist_t *zplprops = NULL; 1354fa9e4066Sahrens 1355990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1356478ed9adSEric Taylor zc->zc_iflags, &config)) 1357fa9e4066Sahrens return (error); 13582a6b87f0Sek 1359990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1360478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1361478ed9adSEric Taylor zc->zc_iflags, &props))) { 1362990b4856Slling nvlist_free(config); 1363990b4856Slling return (error); 1364990b4856Slling } 1365990b4856Slling 13660a48a24eStimh if (props) { 13670a48a24eStimh nvlist_t *nvl = NULL; 13680a48a24eStimh uint64_t version = SPA_VERSION; 13690a48a24eStimh 13700a48a24eStimh (void) nvlist_lookup_uint64(props, 13710a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 1372ad135b5dSChristopher Siden if (!SPA_VERSION_IS_SUPPORTED(version)) { 13730a48a24eStimh error = EINVAL; 13740a48a24eStimh goto pool_props_bad; 13750a48a24eStimh } 13760a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 13770a48a24eStimh if (nvl) { 13780a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 13790a48a24eStimh if (error != 0) { 13800a48a24eStimh nvlist_free(config); 13810a48a24eStimh nvlist_free(props); 13820a48a24eStimh return (error); 13830a48a24eStimh } 13840a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 13850a48a24eStimh } 13860a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 13870a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 13880a48a24eStimh zplprops, NULL); 13890a48a24eStimh if (error) 13900a48a24eStimh goto pool_props_bad; 13910a48a24eStimh } 13920a48a24eStimh 1393*4445fffbSMatthew Ahrens error = spa_create(zc->zc_name, config, props, zplprops); 13940a48a24eStimh 13950a48a24eStimh /* 13960a48a24eStimh * Set the remaining root properties 13970a48a24eStimh */ 139892241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 139992241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 14000a48a24eStimh (void) spa_destroy(zc->zc_name); 1401fa9e4066Sahrens 14020a48a24eStimh pool_props_bad: 14030a48a24eStimh nvlist_free(rootprops); 14040a48a24eStimh nvlist_free(zplprops); 1405fa9e4066Sahrens nvlist_free(config); 14060a48a24eStimh nvlist_free(props); 1407990b4856Slling 1408fa9e4066Sahrens return (error); 1409fa9e4066Sahrens } 1410fa9e4066Sahrens 1411fa9e4066Sahrens static int 1412fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1413fa9e4066Sahrens { 1414ecd6cf80Smarks int error; 1415ecd6cf80Smarks zfs_log_history(zc); 1416ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1417681d9761SEric Taylor if (error == 0) 1418681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1419ecd6cf80Smarks return (error); 1420fa9e4066Sahrens } 1421fa9e4066Sahrens 1422fa9e4066Sahrens static int 1423fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1424fa9e4066Sahrens { 1425990b4856Slling nvlist_t *config, *props = NULL; 1426fa9e4066Sahrens uint64_t guid; 1427468c413aSTim Haley int error; 1428fa9e4066Sahrens 1429990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1430478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1431990b4856Slling return (error); 1432990b4856Slling 1433990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1434478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1435478ed9adSEric Taylor zc->zc_iflags, &props))) { 1436990b4856Slling nvlist_free(config); 1437fa9e4066Sahrens return (error); 1438990b4856Slling } 1439fa9e4066Sahrens 1440fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1441ea8dc4b6Seschrock guid != zc->zc_guid) 1442fa9e4066Sahrens error = EINVAL; 1443fa9e4066Sahrens else 14444b964adaSGeorge Wilson error = spa_import(zc->zc_name, config, props, zc->zc_cookie); 1445fa9e4066Sahrens 14464b964adaSGeorge Wilson if (zc->zc_nvlist_dst != 0) { 14474b964adaSGeorge Wilson int err; 14484b964adaSGeorge Wilson 14494b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 14504b964adaSGeorge Wilson error = err; 14514b964adaSGeorge Wilson } 1452468c413aSTim Haley 1453fa9e4066Sahrens nvlist_free(config); 1454fa9e4066Sahrens 1455990b4856Slling if (props) 1456990b4856Slling nvlist_free(props); 1457990b4856Slling 1458fa9e4066Sahrens return (error); 1459fa9e4066Sahrens } 1460fa9e4066Sahrens 1461fa9e4066Sahrens static int 1462fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1463fa9e4066Sahrens { 1464ecd6cf80Smarks int error; 146589a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1466394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 146789a89ebfSlling 1468ecd6cf80Smarks zfs_log_history(zc); 1469394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1470681d9761SEric Taylor if (error == 0) 1471681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1472ecd6cf80Smarks return (error); 1473fa9e4066Sahrens } 1474fa9e4066Sahrens 1475fa9e4066Sahrens static int 1476fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1477fa9e4066Sahrens { 1478fa9e4066Sahrens nvlist_t *configs; 1479fa9e4066Sahrens int error; 1480fa9e4066Sahrens 1481fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1482fa9e4066Sahrens return (EEXIST); 1483fa9e4066Sahrens 1484e9dbad6fSeschrock error = put_nvlist(zc, configs); 1485fa9e4066Sahrens 1486fa9e4066Sahrens nvlist_free(configs); 1487fa9e4066Sahrens 1488fa9e4066Sahrens return (error); 1489fa9e4066Sahrens } 1490fa9e4066Sahrens 1491ad135b5dSChristopher Siden /* 1492ad135b5dSChristopher Siden * inputs: 1493ad135b5dSChristopher Siden * zc_name name of the pool 1494ad135b5dSChristopher Siden * 1495ad135b5dSChristopher Siden * outputs: 1496ad135b5dSChristopher Siden * zc_cookie real errno 1497ad135b5dSChristopher Siden * zc_nvlist_dst config nvlist 1498ad135b5dSChristopher Siden * zc_nvlist_dst_size size of config nvlist 1499ad135b5dSChristopher Siden */ 1500fa9e4066Sahrens static int 1501fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1502fa9e4066Sahrens { 1503fa9e4066Sahrens nvlist_t *config; 1504fa9e4066Sahrens int error; 1505ea8dc4b6Seschrock int ret = 0; 1506fa9e4066Sahrens 1507e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1508e9dbad6fSeschrock sizeof (zc->zc_value)); 1509fa9e4066Sahrens 1510fa9e4066Sahrens if (config != NULL) { 1511e9dbad6fSeschrock ret = put_nvlist(zc, config); 1512fa9e4066Sahrens nvlist_free(config); 1513ea8dc4b6Seschrock 1514ea8dc4b6Seschrock /* 1515ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1516ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1517ea8dc4b6Seschrock * in 'zc_cookie'. 1518ea8dc4b6Seschrock */ 1519ea8dc4b6Seschrock zc->zc_cookie = error; 1520fa9e4066Sahrens } else { 1521ea8dc4b6Seschrock ret = error; 1522fa9e4066Sahrens } 1523fa9e4066Sahrens 1524ea8dc4b6Seschrock return (ret); 1525fa9e4066Sahrens } 1526fa9e4066Sahrens 1527fa9e4066Sahrens /* 1528fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1529fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1530fa9e4066Sahrens */ 1531fa9e4066Sahrens static int 1532fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1533fa9e4066Sahrens { 1534fa9e4066Sahrens nvlist_t *tryconfig, *config; 1535fa9e4066Sahrens int error; 1536fa9e4066Sahrens 1537990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1538478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1539fa9e4066Sahrens return (error); 1540fa9e4066Sahrens 1541fa9e4066Sahrens config = spa_tryimport(tryconfig); 1542fa9e4066Sahrens 1543fa9e4066Sahrens nvlist_free(tryconfig); 1544fa9e4066Sahrens 1545fa9e4066Sahrens if (config == NULL) 1546fa9e4066Sahrens return (EINVAL); 1547fa9e4066Sahrens 1548e9dbad6fSeschrock error = put_nvlist(zc, config); 1549fa9e4066Sahrens nvlist_free(config); 1550fa9e4066Sahrens 1551fa9e4066Sahrens return (error); 1552fa9e4066Sahrens } 1553fa9e4066Sahrens 15543f9d6ad7SLin Ling /* 15553f9d6ad7SLin Ling * inputs: 15563f9d6ad7SLin Ling * zc_name name of the pool 15573f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 15583f9d6ad7SLin Ling */ 1559fa9e4066Sahrens static int 15603f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1561fa9e4066Sahrens { 1562fa9e4066Sahrens spa_t *spa; 1563fa9e4066Sahrens int error; 1564fa9e4066Sahrens 156506eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 156606eeb2adSek return (error); 156706eeb2adSek 15683f9d6ad7SLin Ling if (zc->zc_cookie == POOL_SCAN_NONE) 15693f9d6ad7SLin Ling error = spa_scan_stop(spa); 15703f9d6ad7SLin Ling else 15713f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 157206eeb2adSek 157306eeb2adSek spa_close(spa, FTAG); 157406eeb2adSek 1575fa9e4066Sahrens return (error); 1576fa9e4066Sahrens } 1577fa9e4066Sahrens 1578fa9e4066Sahrens static int 1579fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1580fa9e4066Sahrens { 1581fa9e4066Sahrens spa_t *spa; 1582fa9e4066Sahrens int error; 1583fa9e4066Sahrens 1584fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1585fa9e4066Sahrens if (error == 0) { 1586fa9e4066Sahrens spa_freeze(spa); 1587fa9e4066Sahrens spa_close(spa, FTAG); 1588fa9e4066Sahrens } 1589fa9e4066Sahrens return (error); 1590fa9e4066Sahrens } 1591fa9e4066Sahrens 1592eaca9bbdSeschrock static int 1593eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1594eaca9bbdSeschrock { 1595eaca9bbdSeschrock spa_t *spa; 1596eaca9bbdSeschrock int error; 1597eaca9bbdSeschrock 159806eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 159906eeb2adSek return (error); 160006eeb2adSek 1601ad135b5dSChristopher Siden if (zc->zc_cookie < spa_version(spa) || 1602ad135b5dSChristopher Siden !SPA_VERSION_IS_SUPPORTED(zc->zc_cookie)) { 1603558d2d50Slling spa_close(spa, FTAG); 1604558d2d50Slling return (EINVAL); 1605558d2d50Slling } 1606558d2d50Slling 1607990b4856Slling spa_upgrade(spa, zc->zc_cookie); 160806eeb2adSek spa_close(spa, FTAG); 160906eeb2adSek 161006eeb2adSek return (error); 161106eeb2adSek } 161206eeb2adSek 161306eeb2adSek static int 161406eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 161506eeb2adSek { 161606eeb2adSek spa_t *spa; 161706eeb2adSek char *hist_buf; 161806eeb2adSek uint64_t size; 161906eeb2adSek int error; 162006eeb2adSek 162106eeb2adSek if ((size = zc->zc_history_len) == 0) 162206eeb2adSek return (EINVAL); 162306eeb2adSek 162406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 162506eeb2adSek return (error); 162606eeb2adSek 1627e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1628d7306b64Sek spa_close(spa, FTAG); 1629d7306b64Sek return (ENOTSUP); 1630d7306b64Sek } 1631d7306b64Sek 163206eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 163306eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 163406eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1635478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1636478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1637478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 163806eeb2adSek } 163906eeb2adSek 164006eeb2adSek spa_close(spa, FTAG); 164106eeb2adSek kmem_free(hist_buf, size); 164206eeb2adSek return (error); 164306eeb2adSek } 164406eeb2adSek 1645e9103aaeSGarrett D'Amore static int 1646e9103aaeSGarrett D'Amore zfs_ioc_pool_reguid(zfs_cmd_t *zc) 1647e9103aaeSGarrett D'Amore { 1648e9103aaeSGarrett D'Amore spa_t *spa; 1649e9103aaeSGarrett D'Amore int error; 1650e9103aaeSGarrett D'Amore 1651e9103aaeSGarrett D'Amore error = spa_open(zc->zc_name, &spa, FTAG); 1652e9103aaeSGarrett D'Amore if (error == 0) { 1653e9103aaeSGarrett D'Amore error = spa_change_guid(spa); 1654e9103aaeSGarrett D'Amore spa_close(spa, FTAG); 1655e9103aaeSGarrett D'Amore } 1656e9103aaeSGarrett D'Amore return (error); 1657e9103aaeSGarrett D'Amore } 1658e9103aaeSGarrett D'Amore 165955434c77Sek static int 166055434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 166155434c77Sek { 166255434c77Sek int error; 166355434c77Sek 1664b1b8ab34Slling if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)) 166555434c77Sek return (error); 166655434c77Sek 166755434c77Sek return (0); 166855434c77Sek } 166955434c77Sek 1670503ad85cSMatthew Ahrens /* 1671503ad85cSMatthew Ahrens * inputs: 1672503ad85cSMatthew Ahrens * zc_name name of filesystem 1673503ad85cSMatthew Ahrens * zc_obj object to find 1674503ad85cSMatthew Ahrens * 1675503ad85cSMatthew Ahrens * outputs: 1676503ad85cSMatthew Ahrens * zc_value name of object 1677503ad85cSMatthew Ahrens */ 167855434c77Sek static int 167955434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 168055434c77Sek { 1681503ad85cSMatthew Ahrens objset_t *os; 168255434c77Sek int error; 168355434c77Sek 1684503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1685503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 168655434c77Sek return (error); 1687503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1688503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1689503ad85cSMatthew Ahrens return (EINVAL); 1690503ad85cSMatthew Ahrens } 1691503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 169255434c77Sek sizeof (zc->zc_value)); 1693503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 169455434c77Sek 169555434c77Sek return (error); 169655434c77Sek } 169755434c77Sek 169899d5e173STim Haley /* 169999d5e173STim Haley * inputs: 170099d5e173STim Haley * zc_name name of filesystem 170199d5e173STim Haley * zc_obj object to find 170299d5e173STim Haley * 170399d5e173STim Haley * outputs: 170499d5e173STim Haley * zc_stat stats on object 170599d5e173STim Haley * zc_value path to object 170699d5e173STim Haley */ 170799d5e173STim Haley static int 170899d5e173STim Haley zfs_ioc_obj_to_stats(zfs_cmd_t *zc) 170999d5e173STim Haley { 171099d5e173STim Haley objset_t *os; 171199d5e173STim Haley int error; 171299d5e173STim Haley 171399d5e173STim Haley /* XXX reading from objset not owned */ 171499d5e173STim Haley if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 171599d5e173STim Haley return (error); 171699d5e173STim Haley if (dmu_objset_type(os) != DMU_OST_ZFS) { 171799d5e173STim Haley dmu_objset_rele(os, FTAG); 171899d5e173STim Haley return (EINVAL); 171999d5e173STim Haley } 172099d5e173STim Haley error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value, 172199d5e173STim Haley sizeof (zc->zc_value)); 172299d5e173STim Haley dmu_objset_rele(os, FTAG); 172399d5e173STim Haley 172499d5e173STim Haley return (error); 172599d5e173STim Haley } 172699d5e173STim Haley 1727fa9e4066Sahrens static int 1728fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1729fa9e4066Sahrens { 1730fa9e4066Sahrens spa_t *spa; 1731fa9e4066Sahrens int error; 1732e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1733e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1734fa9e4066Sahrens 1735fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1736fa9e4066Sahrens if (error != 0) 1737fa9e4066Sahrens return (error); 1738fa9e4066Sahrens 1739fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1740478ed9adSEric Taylor zc->zc_iflags, &config); 1741fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1742fa94a07fSbrendan &l2cache, &nl2cache); 1743fa94a07fSbrendan 1744e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1745e7cbe64fSgw &spares, &nspares); 1746e7cbe64fSgw 1747b1b8ab34Slling /* 1748b1b8ab34Slling * A root pool with concatenated devices is not supported. 1749e7cbe64fSgw * Thus, can not add a device to a root pool. 1750e7cbe64fSgw * 1751e7cbe64fSgw * Intent log device can not be added to a rootpool because 1752e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1753e7cbe64fSgw * can not be accessed during the mountroot time. 1754e7cbe64fSgw * 1755e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1756b1b8ab34Slling */ 1757b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 17581195e687SMark J Musante nvlist_free(config); 1759b1b8ab34Slling spa_close(spa, FTAG); 1760b1b8ab34Slling return (EDOM); 1761b1b8ab34Slling } 1762b1b8ab34Slling 1763fa94a07fSbrendan if (error == 0) { 1764fa9e4066Sahrens error = spa_vdev_add(spa, config); 1765fa9e4066Sahrens nvlist_free(config); 1766fa9e4066Sahrens } 1767fa9e4066Sahrens spa_close(spa, FTAG); 1768fa9e4066Sahrens return (error); 1769fa9e4066Sahrens } 1770fa9e4066Sahrens 17713f9d6ad7SLin Ling /* 17723f9d6ad7SLin Ling * inputs: 17733f9d6ad7SLin Ling * zc_name name of the pool 17743f9d6ad7SLin Ling * zc_nvlist_conf nvlist of devices to remove 17753f9d6ad7SLin Ling * zc_cookie to stop the remove? 17763f9d6ad7SLin Ling */ 1777fa9e4066Sahrens static int 1778fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1779fa9e4066Sahrens { 178099653d4eSeschrock spa_t *spa; 178199653d4eSeschrock int error; 178299653d4eSeschrock 178399653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 178499653d4eSeschrock if (error != 0) 178599653d4eSeschrock return (error); 178699653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 178799653d4eSeschrock spa_close(spa, FTAG); 178899653d4eSeschrock return (error); 1789fa9e4066Sahrens } 1790fa9e4066Sahrens 1791fa9e4066Sahrens static int 17923d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1793fa9e4066Sahrens { 1794fa9e4066Sahrens spa_t *spa; 1795fa9e4066Sahrens int error; 17963d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1797fa9e4066Sahrens 179806eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1799fa9e4066Sahrens return (error); 18003d7072f8Seschrock switch (zc->zc_cookie) { 18013d7072f8Seschrock case VDEV_STATE_ONLINE: 18023d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 18033d7072f8Seschrock break; 1804fa9e4066Sahrens 18053d7072f8Seschrock case VDEV_STATE_OFFLINE: 18063d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 18073d7072f8Seschrock break; 1808fa9e4066Sahrens 18093d7072f8Seschrock case VDEV_STATE_FAULTED: 1810069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1811069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1812069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1813069f55e2SEric Schrock 1814069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 18153d7072f8Seschrock break; 18163d7072f8Seschrock 18173d7072f8Seschrock case VDEV_STATE_DEGRADED: 1818069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1819069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1820069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1821069f55e2SEric Schrock 1822069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 18233d7072f8Seschrock break; 18243d7072f8Seschrock 18253d7072f8Seschrock default: 18263d7072f8Seschrock error = EINVAL; 18273d7072f8Seschrock } 18283d7072f8Seschrock zc->zc_cookie = newstate; 1829fa9e4066Sahrens spa_close(spa, FTAG); 1830fa9e4066Sahrens return (error); 1831fa9e4066Sahrens } 1832fa9e4066Sahrens 1833fa9e4066Sahrens static int 1834fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1835fa9e4066Sahrens { 1836fa9e4066Sahrens spa_t *spa; 1837fa9e4066Sahrens int replacing = zc->zc_cookie; 1838fa9e4066Sahrens nvlist_t *config; 1839fa9e4066Sahrens int error; 1840fa9e4066Sahrens 184106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1842fa9e4066Sahrens return (error); 1843fa9e4066Sahrens 1844990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1845478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1846ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1847fa9e4066Sahrens nvlist_free(config); 1848fa9e4066Sahrens } 1849fa9e4066Sahrens 1850fa9e4066Sahrens spa_close(spa, FTAG); 1851fa9e4066Sahrens return (error); 1852fa9e4066Sahrens } 1853fa9e4066Sahrens 1854fa9e4066Sahrens static int 1855fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1856fa9e4066Sahrens { 1857fa9e4066Sahrens spa_t *spa; 1858fa9e4066Sahrens int error; 1859fa9e4066Sahrens 186006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1861fa9e4066Sahrens return (error); 1862fa9e4066Sahrens 18638ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1864fa9e4066Sahrens 1865fa9e4066Sahrens spa_close(spa, FTAG); 1866fa9e4066Sahrens return (error); 1867fa9e4066Sahrens } 1868fa9e4066Sahrens 18691195e687SMark J Musante static int 18701195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 18711195e687SMark J Musante { 18721195e687SMark J Musante spa_t *spa; 18731195e687SMark J Musante nvlist_t *config, *props = NULL; 18741195e687SMark J Musante int error; 18751195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 18761195e687SMark J Musante 18771195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 18781195e687SMark J Musante return (error); 18791195e687SMark J Musante 18801195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 18811195e687SMark J Musante zc->zc_iflags, &config)) { 18821195e687SMark J Musante spa_close(spa, FTAG); 18831195e687SMark J Musante return (error); 18841195e687SMark J Musante } 18851195e687SMark J Musante 18861195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 18871195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 18881195e687SMark J Musante zc->zc_iflags, &props))) { 18891195e687SMark J Musante spa_close(spa, FTAG); 18901195e687SMark J Musante nvlist_free(config); 18911195e687SMark J Musante return (error); 18921195e687SMark J Musante } 18931195e687SMark J Musante 18941195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 18951195e687SMark J Musante 18961195e687SMark J Musante spa_close(spa, FTAG); 18971195e687SMark J Musante 18981195e687SMark J Musante nvlist_free(config); 18991195e687SMark J Musante nvlist_free(props); 19001195e687SMark J Musante 19011195e687SMark J Musante return (error); 19021195e687SMark J Musante } 19031195e687SMark J Musante 1904c67d9675Seschrock static int 1905c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 1906c67d9675Seschrock { 1907c67d9675Seschrock spa_t *spa; 1908e9dbad6fSeschrock char *path = zc->zc_value; 1909ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 1910c67d9675Seschrock int error; 1911c67d9675Seschrock 1912c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 1913c67d9675Seschrock if (error != 0) 1914c67d9675Seschrock return (error); 1915c67d9675Seschrock 1916c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 1917c67d9675Seschrock spa_close(spa, FTAG); 1918c67d9675Seschrock return (error); 1919c67d9675Seschrock } 1920c67d9675Seschrock 19216809eb4eSEric Schrock static int 19226809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 19236809eb4eSEric Schrock { 19246809eb4eSEric Schrock spa_t *spa; 19256809eb4eSEric Schrock char *fru = zc->zc_value; 19266809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 19276809eb4eSEric Schrock int error; 19286809eb4eSEric Schrock 19296809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 19306809eb4eSEric Schrock if (error != 0) 19316809eb4eSEric Schrock return (error); 19326809eb4eSEric Schrock 19336809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 19346809eb4eSEric Schrock spa_close(spa, FTAG); 19356809eb4eSEric Schrock return (error); 19366809eb4eSEric Schrock } 19376809eb4eSEric Schrock 1938fa9e4066Sahrens static int 1939a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) 1940fa9e4066Sahrens { 1941a7f53a56SChris Kirby int error = 0; 19427f7322feSeschrock nvlist_t *nv; 1943fa9e4066Sahrens 1944a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 1945fa9e4066Sahrens 19465ad82045Snd if (zc->zc_nvlist_dst != 0 && 194792241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 1948a2eea2e1Sahrens dmu_objset_stats(os, nv); 1949432f72fdSahrens /* 1950bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 1951432f72fdSahrens * which we aren't supposed to do with a 1952745cd3c5Smaybee * DS_MODE_USER hold, because it could be 1953432f72fdSahrens * inconsistent. So this is a bit of a workaround... 1954503ad85cSMatthew Ahrens * XXX reading with out owning 1955432f72fdSahrens */ 195619b94df9SMatthew Ahrens if (!zc->zc_objset_stats.dds_inconsistent && 195719b94df9SMatthew Ahrens dmu_objset_type(os) == DMU_OST_ZVOL) { 195819b94df9SMatthew Ahrens error = zvol_get_stats(os, nv); 195919b94df9SMatthew Ahrens if (error == EIO) 196019b94df9SMatthew Ahrens return (error); 196119b94df9SMatthew Ahrens VERIFY3S(error, ==, 0); 1962e7437265Sahrens } 1963e9dbad6fSeschrock error = put_nvlist(zc, nv); 19647f7322feSeschrock nvlist_free(nv); 19657f7322feSeschrock } 1966fa9e4066Sahrens 1967a7f53a56SChris Kirby return (error); 1968a7f53a56SChris Kirby } 1969a7f53a56SChris Kirby 1970a7f53a56SChris Kirby /* 1971a7f53a56SChris Kirby * inputs: 1972a7f53a56SChris Kirby * zc_name name of filesystem 1973a7f53a56SChris Kirby * zc_nvlist_dst_size size of buffer for property nvlist 1974a7f53a56SChris Kirby * 1975a7f53a56SChris Kirby * outputs: 1976a7f53a56SChris Kirby * zc_objset_stats stats 1977a7f53a56SChris Kirby * zc_nvlist_dst property nvlist 1978a7f53a56SChris Kirby * zc_nvlist_dst_size size of property nvlist 1979a7f53a56SChris Kirby */ 1980a7f53a56SChris Kirby static int 1981a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc) 1982a7f53a56SChris Kirby { 1983a7f53a56SChris Kirby objset_t *os = NULL; 1984a7f53a56SChris Kirby int error; 1985a7f53a56SChris Kirby 1986a7f53a56SChris Kirby if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 1987a7f53a56SChris Kirby return (error); 1988a7f53a56SChris Kirby 1989a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, os); 1990a7f53a56SChris Kirby 1991503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1992a7f53a56SChris Kirby 1993fa9e4066Sahrens return (error); 1994fa9e4066Sahrens } 1995fa9e4066Sahrens 199692241e0bSTom Erickson /* 199792241e0bSTom Erickson * inputs: 199892241e0bSTom Erickson * zc_name name of filesystem 199992241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 200092241e0bSTom Erickson * 200192241e0bSTom Erickson * outputs: 200292241e0bSTom Erickson * zc_nvlist_dst received property nvlist 200392241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 200492241e0bSTom Erickson * 200592241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 200692241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 200792241e0bSTom Erickson * local property values. 200892241e0bSTom Erickson */ 200992241e0bSTom Erickson static int 201092241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 201192241e0bSTom Erickson { 201292241e0bSTom Erickson objset_t *os = NULL; 201392241e0bSTom Erickson int error; 201492241e0bSTom Erickson nvlist_t *nv; 201592241e0bSTom Erickson 201692241e0bSTom Erickson if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) 201792241e0bSTom Erickson return (error); 201892241e0bSTom Erickson 201992241e0bSTom Erickson /* 202092241e0bSTom Erickson * Without this check, we would return local property values if the 202192241e0bSTom Erickson * caller has not already received properties on or after 202292241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 202392241e0bSTom Erickson */ 202492241e0bSTom Erickson if (!dsl_prop_get_hasrecvd(os)) { 202592241e0bSTom Erickson dmu_objset_rele(os, FTAG); 202692241e0bSTom Erickson return (ENOTSUP); 202792241e0bSTom Erickson } 202892241e0bSTom Erickson 202992241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 203092241e0bSTom Erickson (error = dsl_prop_get_received(os, &nv)) == 0) { 203192241e0bSTom Erickson error = put_nvlist(zc, nv); 203292241e0bSTom Erickson nvlist_free(nv); 203392241e0bSTom Erickson } 203492241e0bSTom Erickson 203592241e0bSTom Erickson dmu_objset_rele(os, FTAG); 203692241e0bSTom Erickson return (error); 203792241e0bSTom Erickson } 203892241e0bSTom Erickson 2039de8267e0Stimh static int 2040de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 2041de8267e0Stimh { 2042de8267e0Stimh uint64_t value; 2043de8267e0Stimh int error; 2044de8267e0Stimh 2045de8267e0Stimh /* 2046de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 2047de8267e0Stimh * the default value (if there is one). 2048de8267e0Stimh */ 2049de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 2050de8267e0Stimh return (error); 2051de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 2052de8267e0Stimh return (0); 2053de8267e0Stimh } 2054de8267e0Stimh 20553cb34c60Sahrens /* 20563cb34c60Sahrens * inputs: 20573cb34c60Sahrens * zc_name name of filesystem 2058de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 20593cb34c60Sahrens * 20603cb34c60Sahrens * outputs: 2061de8267e0Stimh * zc_nvlist_dst zpl property nvlist 2062de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 20633cb34c60Sahrens */ 2064bd00f61bSrm static int 2065de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 2066bd00f61bSrm { 2067de8267e0Stimh objset_t *os; 2068de8267e0Stimh int err; 2069bd00f61bSrm 2070503ad85cSMatthew Ahrens /* XXX reading without owning */ 2071503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 2072de8267e0Stimh return (err); 2073bd00f61bSrm 2074bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2075bd00f61bSrm 2076bd00f61bSrm /* 2077de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 2078745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 2079745cd3c5Smaybee * hold, because it could be inconsistent. 2080bd00f61bSrm */ 2081de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 2082de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 2083de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 2084de8267e0Stimh nvlist_t *nv; 2085de8267e0Stimh 2086de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2087de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 2088de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 2089de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 2090de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 2091de8267e0Stimh err = put_nvlist(zc, nv); 2092de8267e0Stimh nvlist_free(nv); 2093de8267e0Stimh } else { 2094de8267e0Stimh err = ENOENT; 2095de8267e0Stimh } 2096503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2097de8267e0Stimh return (err); 2098bd00f61bSrm } 2099bd00f61bSrm 210014843421SMatthew Ahrens static boolean_t 210114843421SMatthew Ahrens dataset_name_hidden(const char *name) 210214843421SMatthew Ahrens { 210314843421SMatthew Ahrens /* 210414843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 210514843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 210614843421SMatthew Ahrens * temporary datasets (which have a % in their name). 210714843421SMatthew Ahrens */ 210814843421SMatthew Ahrens if (strchr(name, '$') != NULL) 210914843421SMatthew Ahrens return (B_TRUE); 211014843421SMatthew Ahrens if (strchr(name, '%') != NULL) 211114843421SMatthew Ahrens return (B_TRUE); 211214843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 211314843421SMatthew Ahrens return (B_TRUE); 211414843421SMatthew Ahrens return (B_FALSE); 211514843421SMatthew Ahrens } 211614843421SMatthew Ahrens 2117de8267e0Stimh /* 2118de8267e0Stimh * inputs: 2119de8267e0Stimh * zc_name name of filesystem 2120de8267e0Stimh * zc_cookie zap cursor 2121de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 2122de8267e0Stimh * 2123de8267e0Stimh * outputs: 2124de8267e0Stimh * zc_name name of next filesystem 212514843421SMatthew Ahrens * zc_cookie zap cursor 2126de8267e0Stimh * zc_objset_stats stats 2127de8267e0Stimh * zc_nvlist_dst property nvlist 2128de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 2129de8267e0Stimh */ 2130fa9e4066Sahrens static int 2131fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 2132fa9e4066Sahrens { 213387e5029aSahrens objset_t *os; 2134fa9e4066Sahrens int error; 2135fa9e4066Sahrens char *p; 2136620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 2137fa9e4066Sahrens 2138620252bcSChris Kirby top: 2139503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 214087e5029aSahrens if (error == ENOENT) 214187e5029aSahrens error = ESRCH; 214287e5029aSahrens return (error); 2143fa9e4066Sahrens } 2144fa9e4066Sahrens 2145fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 2146fa9e4066Sahrens if (p == NULL || p[1] != '\0') 2147fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 2148fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 2149fa9e4066Sahrens 21505c0b6a79SRich Morris /* 21515c0b6a79SRich Morris * Pre-fetch the datasets. dmu_objset_prefetch() always returns 0 21525c0b6a79SRich Morris * but is not declared void because its called by dmu_objset_find(). 21535c0b6a79SRich Morris */ 21547f73c863SRich Morris if (zc->zc_cookie == 0) { 21557f73c863SRich Morris uint64_t cookie = 0; 21567f73c863SRich Morris int len = sizeof (zc->zc_name) - (p - zc->zc_name); 21577f73c863SRich Morris 21581df56adaSMartin Matuska while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0) { 21591df56adaSMartin Matuska if (!dataset_name_hidden(zc->zc_name)) 21601df56adaSMartin Matuska (void) dmu_objset_prefetch(zc->zc_name, NULL); 21611df56adaSMartin Matuska } 21627f73c863SRich Morris } 21637f73c863SRich Morris 2164fa9e4066Sahrens do { 216587e5029aSahrens error = dmu_dir_list_next(os, 216687e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 216787e5029aSahrens NULL, &zc->zc_cookie); 2168fa9e4066Sahrens if (error == ENOENT) 2169fa9e4066Sahrens error = ESRCH; 217019b94df9SMatthew Ahrens } while (error == 0 && dataset_name_hidden(zc->zc_name)); 2171503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2172fa9e4066Sahrens 2173681d9761SEric Taylor /* 2174681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 2175681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 2176681d9761SEric Taylor */ 2177620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 217887e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 2179620252bcSChris Kirby if (error == ENOENT) { 2180620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 2181620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 2182620252bcSChris Kirby goto top; 2183620252bcSChris Kirby } 2184620252bcSChris Kirby } 2185fa9e4066Sahrens return (error); 2186fa9e4066Sahrens } 2187fa9e4066Sahrens 21883cb34c60Sahrens /* 21893cb34c60Sahrens * inputs: 21903cb34c60Sahrens * zc_name name of filesystem 21913cb34c60Sahrens * zc_cookie zap cursor 21923cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 21933cb34c60Sahrens * 21943cb34c60Sahrens * outputs: 21953cb34c60Sahrens * zc_name name of next snapshot 21963cb34c60Sahrens * zc_objset_stats stats 21973cb34c60Sahrens * zc_nvlist_dst property nvlist 21983cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 21993cb34c60Sahrens */ 2200fa9e4066Sahrens static int 2201fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 2202fa9e4066Sahrens { 220387e5029aSahrens objset_t *os; 2204fa9e4066Sahrens int error; 2205fa9e4066Sahrens 2206620252bcSChris Kirby top: 22077cbf8b43SRich Morris if (zc->zc_cookie == 0) 22087cbf8b43SRich Morris (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch, 22097cbf8b43SRich Morris NULL, DS_FIND_SNAPSHOTS); 22107cbf8b43SRich Morris 2211503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 2212745cd3c5Smaybee if (error) 2213745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 2214fa9e4066Sahrens 2215b81d61a6Slling /* 2216b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 2217b81d61a6Slling * so exit immediately. 2218b81d61a6Slling */ 2219b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 2220503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2221b81d61a6Slling return (ESRCH); 2222fa9e4066Sahrens } 2223fa9e4066Sahrens 222487e5029aSahrens error = dmu_snapshot_list_next(os, 222587e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 2226a7f53a56SChris Kirby zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, 2227a7f53a56SChris Kirby NULL); 2228a7f53a56SChris Kirby 2229620252bcSChris Kirby if (error == 0) { 2230a7f53a56SChris Kirby dsl_dataset_t *ds; 2231a7f53a56SChris Kirby dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; 2232a7f53a56SChris Kirby 2233a7f53a56SChris Kirby /* 2234a7f53a56SChris Kirby * Since we probably don't have a hold on this snapshot, 2235a7f53a56SChris Kirby * it's possible that the objsetid could have been destroyed 2236a7f53a56SChris Kirby * and reused for a new objset. It's OK if this happens during 2237a7f53a56SChris Kirby * a zfs send operation, since the new createtxg will be 2238a7f53a56SChris Kirby * beyond the range we're interested in. 2239a7f53a56SChris Kirby */ 2240a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 2241a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); 2242a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 2243a7f53a56SChris Kirby if (error) { 2244a7f53a56SChris Kirby if (error == ENOENT) { 2245a7f53a56SChris Kirby /* Racing with destroy, get the next one. */ 2246a7f53a56SChris Kirby *strchr(zc->zc_name, '@') = '\0'; 2247a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 2248a7f53a56SChris Kirby goto top; 2249a7f53a56SChris Kirby } 2250a7f53a56SChris Kirby } else { 2251a7f53a56SChris Kirby objset_t *ossnap; 2252a7f53a56SChris Kirby 2253a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &ossnap); 2254a7f53a56SChris Kirby if (error == 0) 2255a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, ossnap); 2256a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 2257620252bcSChris Kirby } 2258620252bcSChris Kirby } else if (error == ENOENT) { 2259745cd3c5Smaybee error = ESRCH; 2260620252bcSChris Kirby } 2261fa9e4066Sahrens 2262a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 22633cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 2264745cd3c5Smaybee if (error) 22653cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 2266fa9e4066Sahrens return (error); 2267fa9e4066Sahrens } 2268fa9e4066Sahrens 226992241e0bSTom Erickson static int 227092241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 2271fa9e4066Sahrens { 227292241e0bSTom Erickson const char *propname = nvpair_name(pair); 227392241e0bSTom Erickson uint64_t *valary; 227492241e0bSTom Erickson unsigned int vallen; 227592241e0bSTom Erickson const char *domain; 2276eeb85002STim Haley char *dash; 227792241e0bSTom Erickson zfs_userquota_prop_t type; 227892241e0bSTom Erickson uint64_t rid; 227992241e0bSTom Erickson uint64_t quota; 228092241e0bSTom Erickson zfsvfs_t *zfsvfs; 228192241e0bSTom Erickson int err; 228292241e0bSTom Erickson 228392241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 228492241e0bSTom Erickson nvlist_t *attrs; 228592241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2286eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2287eeb85002STim Haley &pair) != 0) 2288eeb85002STim Haley return (EINVAL); 228992241e0bSTom Erickson } 2290e9dbad6fSeschrock 2291ecd6cf80Smarks /* 2292eeb85002STim Haley * A correctly constructed propname is encoded as 229392241e0bSTom Erickson * userquota@<rid>-<domain>. 2294ecd6cf80Smarks */ 2295eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 2296eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 2297eeb85002STim Haley vallen != 3) 2298eeb85002STim Haley return (EINVAL); 2299eeb85002STim Haley 2300eeb85002STim Haley domain = dash + 1; 2301eeb85002STim Haley type = valary[0]; 2302eeb85002STim Haley rid = valary[1]; 2303eeb85002STim Haley quota = valary[2]; 2304e9dbad6fSeschrock 23051412a1a2SMark Shellenbaum err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); 230692241e0bSTom Erickson if (err == 0) { 230792241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 230892241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 230992241e0bSTom Erickson } 2310e9dbad6fSeschrock 231192241e0bSTom Erickson return (err); 231292241e0bSTom Erickson } 231314843421SMatthew Ahrens 231492241e0bSTom Erickson /* 231592241e0bSTom Erickson * If the named property is one that has a special function to set its value, 231692241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 231792241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 231892241e0bSTom Erickson * 2319eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 232092241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 232192241e0bSTom Erickson */ 232292241e0bSTom Erickson static int 232392241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 232492241e0bSTom Erickson nvpair_t *pair) 232592241e0bSTom Erickson { 232692241e0bSTom Erickson const char *propname = nvpair_name(pair); 232792241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 232892241e0bSTom Erickson uint64_t intval; 232992241e0bSTom Erickson int err; 2330fa9e4066Sahrens 233192241e0bSTom Erickson if (prop == ZPROP_INVAL) { 233292241e0bSTom Erickson if (zfs_prop_userquota(propname)) 233392241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 233492241e0bSTom Erickson return (-1); 233592241e0bSTom Erickson } 233614843421SMatthew Ahrens 233792241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 233892241e0bSTom Erickson nvlist_t *attrs; 233992241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 234092241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 234192241e0bSTom Erickson &pair) == 0); 234292241e0bSTom Erickson } 2343db870a07Sahrens 234492241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 234592241e0bSTom Erickson return (-1); 2346b24ab676SJeff Bonwick 234792241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 234840feaa91Sahrens 234992241e0bSTom Erickson switch (prop) { 235092241e0bSTom Erickson case ZFS_PROP_QUOTA: 235192241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 235292241e0bSTom Erickson break; 235392241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 235492241e0bSTom Erickson err = dsl_dataset_set_quota(dsname, source, intval); 235592241e0bSTom Erickson break; 235692241e0bSTom Erickson case ZFS_PROP_RESERVATION: 235792241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 235892241e0bSTom Erickson break; 235992241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 236092241e0bSTom Erickson err = dsl_dataset_set_reservation(dsname, source, intval); 236192241e0bSTom Erickson break; 236292241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 236392241e0bSTom Erickson err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip), 236492241e0bSTom Erickson intval); 236592241e0bSTom Erickson break; 236692241e0bSTom Erickson case ZFS_PROP_VERSION: 236792241e0bSTom Erickson { 236892241e0bSTom Erickson zfsvfs_t *zfsvfs; 23699e6eda55Smarks 23701412a1a2SMark Shellenbaum if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) 2371b24ab676SJeff Bonwick break; 2372b24ab676SJeff Bonwick 237392241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 237492241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 2375d0f3f37eSMark Shellenbaum 237692241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 2377b16da2e2SGeorge Wilson zfs_cmd_t *zc; 2378b16da2e2SGeorge Wilson 2379b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 2380b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 2381b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 2382b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 238340feaa91Sahrens } 238492241e0bSTom Erickson break; 2385ecd6cf80Smarks } 2386ecd6cf80Smarks 238792241e0bSTom Erickson default: 238892241e0bSTom Erickson err = -1; 238992241e0bSTom Erickson } 2390e9dbad6fSeschrock 239192241e0bSTom Erickson return (err); 239292241e0bSTom Erickson } 2393a9799022Sck 239492241e0bSTom Erickson /* 239592241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 2396*4445fffbSMatthew Ahrens * it continues to set as many as it can and returns the last error 2397*4445fffbSMatthew Ahrens * encountered. If the caller provides a non-NULL errlist, it will be filled in 2398*4445fffbSMatthew Ahrens * with the list of names of all the properties that failed along with the 2399*4445fffbSMatthew Ahrens * corresponding error numbers. 240092241e0bSTom Erickson * 2401*4445fffbSMatthew Ahrens * If every property is set successfully, zero is returned and errlist is not 2402*4445fffbSMatthew Ahrens * modified. 240392241e0bSTom Erickson */ 240492241e0bSTom Erickson int 240592241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 2406*4445fffbSMatthew Ahrens nvlist_t *errlist) 240792241e0bSTom Erickson { 240892241e0bSTom Erickson nvpair_t *pair; 240992241e0bSTom Erickson nvpair_t *propval; 241002e383d1STom Erickson int rv = 0; 241192241e0bSTom Erickson uint64_t intval; 241292241e0bSTom Erickson char *strval; 2413*4445fffbSMatthew Ahrens nvlist_t *genericnvl = fnvlist_alloc(); 2414*4445fffbSMatthew Ahrens nvlist_t *retrynvl = fnvlist_alloc(); 2415a9799022Sck 241692241e0bSTom Erickson retry: 241792241e0bSTom Erickson pair = NULL; 241892241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 241992241e0bSTom Erickson const char *propname = nvpair_name(pair); 242092241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2421cfa69fd2STom Erickson int err = 0; 2422e9dbad6fSeschrock 242392241e0bSTom Erickson /* decode the property value */ 242492241e0bSTom Erickson propval = pair; 242592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 242692241e0bSTom Erickson nvlist_t *attrs; 2427*4445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 2428eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2429eeb85002STim Haley &propval) != 0) 2430eeb85002STim Haley err = EINVAL; 243114843421SMatthew Ahrens } 2432e9dbad6fSeschrock 243392241e0bSTom Erickson /* Validate value type */ 2434eeb85002STim Haley if (err == 0 && prop == ZPROP_INVAL) { 243592241e0bSTom Erickson if (zfs_prop_user(propname)) { 243692241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 243792241e0bSTom Erickson err = EINVAL; 243892241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 243992241e0bSTom Erickson if (nvpair_type(propval) != 244092241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 244192241e0bSTom Erickson err = EINVAL; 244219b94df9SMatthew Ahrens } else { 244319b94df9SMatthew Ahrens err = EINVAL; 24444201a95eSRic Aleshire } 2445eeb85002STim Haley } else if (err == 0) { 244692241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 244792241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 244892241e0bSTom Erickson err = EINVAL; 244992241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2450a2eea2e1Sahrens const char *unused; 2451a2eea2e1Sahrens 2452*4445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 2453e9dbad6fSeschrock 2454e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 245591ebeef5Sahrens case PROP_TYPE_NUMBER: 2456e9dbad6fSeschrock break; 245791ebeef5Sahrens case PROP_TYPE_STRING: 245892241e0bSTom Erickson err = EINVAL; 245992241e0bSTom Erickson break; 246091ebeef5Sahrens case PROP_TYPE_INDEX: 2461acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 246292241e0bSTom Erickson intval, &unused) != 0) 246392241e0bSTom Erickson err = EINVAL; 2464e9dbad6fSeschrock break; 2465e9dbad6fSeschrock default: 2466e7437265Sahrens cmn_err(CE_PANIC, 2467e7437265Sahrens "unknown property type"); 2468e9dbad6fSeschrock } 2469e9dbad6fSeschrock } else { 247092241e0bSTom Erickson err = EINVAL; 2471e9dbad6fSeschrock } 2472e9dbad6fSeschrock } 247392241e0bSTom Erickson 247492241e0bSTom Erickson /* Validate permissions */ 247592241e0bSTom Erickson if (err == 0) 247692241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 247792241e0bSTom Erickson 247892241e0bSTom Erickson if (err == 0) { 247992241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 248092241e0bSTom Erickson if (err == -1) { 248192241e0bSTom Erickson /* 248292241e0bSTom Erickson * For better performance we build up a list of 248392241e0bSTom Erickson * properties to set in a single transaction. 248492241e0bSTom Erickson */ 248592241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 248692241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 248792241e0bSTom Erickson /* 248892241e0bSTom Erickson * This may be a spurious error caused by 248992241e0bSTom Erickson * receiving quota and reservation out of order. 249092241e0bSTom Erickson * Try again in a second pass. 249192241e0bSTom Erickson */ 249292241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 249392241e0bSTom Erickson } 249492241e0bSTom Erickson } 249592241e0bSTom Erickson 2496*4445fffbSMatthew Ahrens if (err != 0) { 2497*4445fffbSMatthew Ahrens if (errlist != NULL) 2498*4445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, err); 2499*4445fffbSMatthew Ahrens rv = err; 2500*4445fffbSMatthew Ahrens } 2501e9dbad6fSeschrock } 2502e9dbad6fSeschrock 250392241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 250492241e0bSTom Erickson nvl = retrynvl; 250592241e0bSTom Erickson goto retry; 250692241e0bSTom Erickson } 250792241e0bSTom Erickson 250892241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 250992241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 251092241e0bSTom Erickson /* 251192241e0bSTom Erickson * If this fails, we still want to set as many properties as we 251292241e0bSTom Erickson * can, so try setting them individually. 251392241e0bSTom Erickson */ 251492241e0bSTom Erickson pair = NULL; 251592241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 251692241e0bSTom Erickson const char *propname = nvpair_name(pair); 2517cfa69fd2STom Erickson int err = 0; 251892241e0bSTom Erickson 251992241e0bSTom Erickson propval = pair; 252092241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 252192241e0bSTom Erickson nvlist_t *attrs; 2522*4445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 2523*4445fffbSMatthew Ahrens propval = fnvlist_lookup_nvpair(attrs, 2524*4445fffbSMatthew Ahrens ZPROP_VALUE); 252592241e0bSTom Erickson } 252692241e0bSTom Erickson 252792241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 2528*4445fffbSMatthew Ahrens strval = fnvpair_value_string(propval); 252992241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 1, 253092241e0bSTom Erickson strlen(strval) + 1, strval); 253192241e0bSTom Erickson } else { 2532*4445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 253392241e0bSTom Erickson err = dsl_prop_set(dsname, propname, source, 8, 253492241e0bSTom Erickson 1, &intval); 253592241e0bSTom Erickson } 253692241e0bSTom Erickson 253792241e0bSTom Erickson if (err != 0) { 2538*4445fffbSMatthew Ahrens if (errlist != NULL) { 2539*4445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, 2540*4445fffbSMatthew Ahrens err); 2541*4445fffbSMatthew Ahrens } 2542*4445fffbSMatthew Ahrens rv = err; 254392241e0bSTom Erickson } 254492241e0bSTom Erickson } 25455c0b6a79SRich Morris } 25465c0b6a79SRich Morris nvlist_free(genericnvl); 254792241e0bSTom Erickson nvlist_free(retrynvl); 254892241e0bSTom Erickson 254992241e0bSTom Erickson return (rv); 2550fa9e4066Sahrens } 2551fa9e4066Sahrens 2552ea2f5b9eSMatthew Ahrens /* 2553ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2554ea2f5b9eSMatthew Ahrens */ 2555ea2f5b9eSMatthew Ahrens static int 2556*4445fffbSMatthew Ahrens zfs_check_userprops(const char *fsname, nvlist_t *nvl) 2557ea2f5b9eSMatthew Ahrens { 255892241e0bSTom Erickson nvpair_t *pair = NULL; 2559ea2f5b9eSMatthew Ahrens int error = 0; 2560ea2f5b9eSMatthew Ahrens 256192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 256292241e0bSTom Erickson const char *propname = nvpair_name(pair); 2563ea2f5b9eSMatthew Ahrens char *valstr; 2564ea2f5b9eSMatthew Ahrens 2565ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 256692241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2567ea2f5b9eSMatthew Ahrens return (EINVAL); 2568ea2f5b9eSMatthew Ahrens 2569ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2570ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2571ea2f5b9eSMatthew Ahrens return (error); 2572ea2f5b9eSMatthew Ahrens 2573ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2574ea2f5b9eSMatthew Ahrens return (ENAMETOOLONG); 2575ea2f5b9eSMatthew Ahrens 257692241e0bSTom Erickson VERIFY(nvpair_value_string(pair, &valstr) == 0); 2577ea2f5b9eSMatthew Ahrens if (strlen(valstr) >= ZAP_MAXVALUELEN) 2578ea2f5b9eSMatthew Ahrens return (E2BIG); 2579ea2f5b9eSMatthew Ahrens } 2580ea2f5b9eSMatthew Ahrens return (0); 2581ea2f5b9eSMatthew Ahrens } 2582ea2f5b9eSMatthew Ahrens 258392241e0bSTom Erickson static void 258492241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 258592241e0bSTom Erickson { 258692241e0bSTom Erickson nvpair_t *pair; 258792241e0bSTom Erickson 258892241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 258992241e0bSTom Erickson 259092241e0bSTom Erickson pair = NULL; 259192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 259292241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 259392241e0bSTom Erickson continue; 259492241e0bSTom Erickson 259592241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 259692241e0bSTom Erickson } 259792241e0bSTom Erickson } 259892241e0bSTom Erickson 259992241e0bSTom Erickson static int 260092241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props, 260192241e0bSTom Erickson nvlist_t *skipped) 260292241e0bSTom Erickson { 260392241e0bSTom Erickson int err = 0; 260492241e0bSTom Erickson nvlist_t *cleared_props = NULL; 260592241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 260692241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 260792241e0bSTom Erickson /* 260892241e0bSTom Erickson * Acts on local properties until the dataset has received 260992241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 261092241e0bSTom Erickson */ 261192241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 261292241e0bSTom Erickson (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0)); 261392241e0bSTom Erickson err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL); 261492241e0bSTom Erickson } 261592241e0bSTom Erickson nvlist_free(cleared_props); 261692241e0bSTom Erickson return (err); 261792241e0bSTom Erickson } 261892241e0bSTom Erickson 26193cb34c60Sahrens /* 26203cb34c60Sahrens * inputs: 26213cb34c60Sahrens * zc_name name of filesystem 26225c0b6a79SRich Morris * zc_value name of property to set 26233cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 262492241e0bSTom Erickson * zc_cookie received properties flag 26253cb34c60Sahrens * 262692241e0bSTom Erickson * outputs: 262792241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 26283cb34c60Sahrens */ 2629fa9e4066Sahrens static int 2630e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2631fa9e4066Sahrens { 2632e9dbad6fSeschrock nvlist_t *nvl; 263392241e0bSTom Erickson boolean_t received = zc->zc_cookie; 263492241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 263592241e0bSTom Erickson ZPROP_SRC_LOCAL); 2636*4445fffbSMatthew Ahrens nvlist_t *errors; 2637e9dbad6fSeschrock int error; 2638e9dbad6fSeschrock 2639990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2640478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2641e9dbad6fSeschrock return (error); 2642e9dbad6fSeschrock 264392241e0bSTom Erickson if (received) { 2644bb0ade09Sahrens nvlist_t *origprops; 2645bb0ade09Sahrens objset_t *os; 2646bb0ade09Sahrens 2647503ad85cSMatthew Ahrens if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) { 264892241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 264992241e0bSTom Erickson (void) clear_received_props(os, 265092241e0bSTom Erickson zc->zc_name, origprops, nvl); 2651bb0ade09Sahrens nvlist_free(origprops); 2652bb0ade09Sahrens } 265392241e0bSTom Erickson 265492241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 2655503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2656bb0ade09Sahrens } 2657bb0ade09Sahrens } 2658bb0ade09Sahrens 2659*4445fffbSMatthew Ahrens errors = fnvlist_alloc(); 2660*4445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors); 266192241e0bSTom Erickson 266292241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 266392241e0bSTom Erickson (void) put_nvlist(zc, errors); 266492241e0bSTom Erickson } 2665ecd6cf80Smarks 266692241e0bSTom Erickson nvlist_free(errors); 2667e9dbad6fSeschrock nvlist_free(nvl); 2668e9dbad6fSeschrock return (error); 2669fa9e4066Sahrens } 2670fa9e4066Sahrens 26713cb34c60Sahrens /* 26723cb34c60Sahrens * inputs: 26733cb34c60Sahrens * zc_name name of filesystem 26743cb34c60Sahrens * zc_value name of property to inherit 267592241e0bSTom Erickson * zc_cookie revert to received value if TRUE 26763cb34c60Sahrens * 26773cb34c60Sahrens * outputs: none 26783cb34c60Sahrens */ 2679e45ce728Sahrens static int 2680e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2681e45ce728Sahrens { 268292241e0bSTom Erickson const char *propname = zc->zc_value; 268392241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 268492241e0bSTom Erickson boolean_t received = zc->zc_cookie; 268592241e0bSTom Erickson zprop_source_t source = (received 268692241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 268792241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 268892241e0bSTom Erickson 268992241e0bSTom Erickson if (received) { 269092241e0bSTom Erickson nvlist_t *dummy; 269192241e0bSTom Erickson nvpair_t *pair; 269292241e0bSTom Erickson zprop_type_t type; 269392241e0bSTom Erickson int err; 269492241e0bSTom Erickson 269592241e0bSTom Erickson /* 269692241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 269792241e0bSTom Erickson * nvpair with type info. 269892241e0bSTom Erickson */ 269992241e0bSTom Erickson if (prop == ZPROP_INVAL) { 270092241e0bSTom Erickson if (!zfs_prop_user(propname)) 270192241e0bSTom Erickson return (EINVAL); 270292241e0bSTom Erickson 270392241e0bSTom Erickson type = PROP_TYPE_STRING; 2704a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2705a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2706a79992aaSTom Erickson return (EINVAL); 270792241e0bSTom Erickson } else { 270892241e0bSTom Erickson type = zfs_prop_get_type(prop); 270992241e0bSTom Erickson } 271092241e0bSTom Erickson 271192241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 271292241e0bSTom Erickson 271392241e0bSTom Erickson switch (type) { 271492241e0bSTom Erickson case PROP_TYPE_STRING: 271592241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 271692241e0bSTom Erickson break; 271792241e0bSTom Erickson case PROP_TYPE_NUMBER: 271892241e0bSTom Erickson case PROP_TYPE_INDEX: 271992241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 272092241e0bSTom Erickson break; 272192241e0bSTom Erickson default: 272292241e0bSTom Erickson nvlist_free(dummy); 272392241e0bSTom Erickson return (EINVAL); 272492241e0bSTom Erickson } 272592241e0bSTom Erickson 272692241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 272792241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 272892241e0bSTom Erickson nvlist_free(dummy); 272992241e0bSTom Erickson if (err != -1) 273092241e0bSTom Erickson return (err); /* special property already handled */ 273192241e0bSTom Erickson } else { 273292241e0bSTom Erickson /* 273392241e0bSTom Erickson * Only check this in the non-received case. We want to allow 273492241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 273592241e0bSTom Erickson * and reservation to the received or default values even though 273692241e0bSTom Erickson * they are not considered inheritable. 273792241e0bSTom Erickson */ 273892241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 273992241e0bSTom Erickson return (EINVAL); 274092241e0bSTom Erickson } 274192241e0bSTom Erickson 2742*4445fffbSMatthew Ahrens /* property name has been validated by zfs_secpolicy_inherit_prop() */ 274392241e0bSTom Erickson return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL)); 2744e45ce728Sahrens } 2745e45ce728Sahrens 2746b1b8ab34Slling static int 274711a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2748b1b8ab34Slling { 2749990b4856Slling nvlist_t *props; 2750b1b8ab34Slling spa_t *spa; 2751990b4856Slling int error; 275292241e0bSTom Erickson nvpair_t *pair; 2753b1b8ab34Slling 275492241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 275592241e0bSTom Erickson zc->zc_iflags, &props)) 2756b1b8ab34Slling return (error); 2757b1b8ab34Slling 2758379c004dSEric Schrock /* 2759379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2760379c004dSEric Schrock * to handle the faulted case. 2761379c004dSEric Schrock */ 276292241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 276392241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2764379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 276592241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2766379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2767379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2768379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2769379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2770379c004dSEric Schrock } 2771379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2772b693757aSEric Schrock if (spa != NULL) { 2773b693757aSEric Schrock nvlist_free(props); 2774379c004dSEric Schrock return (0); 2775b693757aSEric Schrock } 2776379c004dSEric Schrock } 2777379c004dSEric Schrock 2778b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2779990b4856Slling nvlist_free(props); 2780b1b8ab34Slling return (error); 2781b1b8ab34Slling } 2782b1b8ab34Slling 2783990b4856Slling error = spa_prop_set(spa, props); 2784b1b8ab34Slling 2785990b4856Slling nvlist_free(props); 2786b1b8ab34Slling spa_close(spa, FTAG); 2787b1b8ab34Slling 2788b1b8ab34Slling return (error); 2789b1b8ab34Slling } 2790b1b8ab34Slling 2791b1b8ab34Slling static int 279211a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2793b1b8ab34Slling { 2794b1b8ab34Slling spa_t *spa; 2795b1b8ab34Slling int error; 2796b1b8ab34Slling nvlist_t *nvp = NULL; 2797b1b8ab34Slling 2798379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2799379c004dSEric Schrock /* 2800379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2801379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2802379c004dSEric Schrock * anyway. 2803379c004dSEric Schrock */ 2804379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2805379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2806379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2807379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2808379c004dSEric Schrock } else { 2809379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2810379c004dSEric Schrock spa_close(spa, FTAG); 2811379c004dSEric Schrock } 2812b1b8ab34Slling 2813b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2814b1b8ab34Slling error = put_nvlist(zc, nvp); 2815b1b8ab34Slling else 2816b1b8ab34Slling error = EFAULT; 2817b1b8ab34Slling 2818379c004dSEric Schrock nvlist_free(nvp); 2819b1b8ab34Slling return (error); 2820b1b8ab34Slling } 2821b1b8ab34Slling 28223cb34c60Sahrens /* 28233cb34c60Sahrens * inputs: 28243cb34c60Sahrens * zc_name name of filesystem 28253cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 28263cb34c60Sahrens * zc_perm_action allow/unallow flag 28273cb34c60Sahrens * 28283cb34c60Sahrens * outputs: none 28293cb34c60Sahrens */ 2830ecd6cf80Smarks static int 2831ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2832ecd6cf80Smarks { 2833ecd6cf80Smarks int error; 2834ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2835ecd6cf80Smarks 2836990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2837478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2838ecd6cf80Smarks return (error); 2839ecd6cf80Smarks 2840ecd6cf80Smarks /* 2841ecd6cf80Smarks * Verify nvlist is constructed correctly 2842ecd6cf80Smarks */ 2843ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2844ecd6cf80Smarks nvlist_free(fsaclnv); 2845ecd6cf80Smarks return (EINVAL); 2846ecd6cf80Smarks } 2847ecd6cf80Smarks 2848ecd6cf80Smarks /* 2849ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2850ecd6cf80Smarks * that user is allowed to hand out each permission in 2851ecd6cf80Smarks * the nvlist(s) 2852ecd6cf80Smarks */ 2853ecd6cf80Smarks 285491ebeef5Sahrens error = secpolicy_zfs(CRED()); 2855ecd6cf80Smarks if (error) { 285691ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 285791ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 285891ebeef5Sahrens fsaclnv, CRED()); 285991ebeef5Sahrens } else { 286091ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 286191ebeef5Sahrens fsaclnv, CRED()); 286291ebeef5Sahrens } 2863ecd6cf80Smarks } 2864ecd6cf80Smarks 2865ecd6cf80Smarks if (error == 0) 2866ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2867ecd6cf80Smarks 2868ecd6cf80Smarks nvlist_free(fsaclnv); 2869ecd6cf80Smarks return (error); 2870ecd6cf80Smarks } 2871ecd6cf80Smarks 28723cb34c60Sahrens /* 28733cb34c60Sahrens * inputs: 28743cb34c60Sahrens * zc_name name of filesystem 28753cb34c60Sahrens * 28763cb34c60Sahrens * outputs: 28773cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 28783cb34c60Sahrens */ 2879ecd6cf80Smarks static int 2880ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2881ecd6cf80Smarks { 2882ecd6cf80Smarks nvlist_t *nvp; 2883ecd6cf80Smarks int error; 2884ecd6cf80Smarks 2885ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2886ecd6cf80Smarks error = put_nvlist(zc, nvp); 2887ecd6cf80Smarks nvlist_free(nvp); 2888ecd6cf80Smarks } 2889ecd6cf80Smarks 2890ecd6cf80Smarks return (error); 2891ecd6cf80Smarks } 2892ecd6cf80Smarks 2893fa9e4066Sahrens /* 2894fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2895fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2896fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2897fa9e4066Sahrens */ 2898fa9e4066Sahrens static vfs_t * 2899fa9e4066Sahrens zfs_get_vfs(const char *resource) 2900fa9e4066Sahrens { 2901fa9e4066Sahrens struct vfs *vfsp; 2902fa9e4066Sahrens struct vfs *vfs_found = NULL; 2903fa9e4066Sahrens 2904fa9e4066Sahrens vfs_list_read_lock(); 2905fa9e4066Sahrens vfsp = rootvfs; 2906fa9e4066Sahrens do { 2907fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2908fa9e4066Sahrens VFS_HOLD(vfsp); 2909fa9e4066Sahrens vfs_found = vfsp; 2910fa9e4066Sahrens break; 2911fa9e4066Sahrens } 2912fa9e4066Sahrens vfsp = vfsp->vfs_next; 2913fa9e4066Sahrens } while (vfsp != rootvfs); 2914fa9e4066Sahrens vfs_list_unlock(); 2915fa9e4066Sahrens return (vfs_found); 2916fa9e4066Sahrens } 2917fa9e4066Sahrens 2918ecd6cf80Smarks /* ARGSUSED */ 2919fa9e4066Sahrens static void 2920ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2921fa9e4066Sahrens { 2922da6c28aaSamw zfs_creat_t *zct = arg; 2923da6c28aaSamw 2924de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2925da6c28aaSamw } 2926da6c28aaSamw 2927de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2928da6c28aaSamw 2929da6c28aaSamw /* 2930de8267e0Stimh * inputs: 29310a48a24eStimh * createprops list of properties requested by creator 29320a48a24eStimh * default_zplver zpl version to use if unspecified in createprops 29330a48a24eStimh * fuids_ok fuids allowed in this version of the spa? 29340a48a24eStimh * os parent objset pointer (NULL if root fs) 2935de8267e0Stimh * 2936de8267e0Stimh * outputs: 2937de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 29380a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 2939da6c28aaSamw * 2940de8267e0Stimh * Determine the settings for utf8only, normalization and 2941de8267e0Stimh * casesensitivity. Specific values may have been requested by the 2942de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 2943de8267e0Stimh * the file system is of too early a vintage, a creator can not 2944de8267e0Stimh * request settings for these properties, even if the requested 2945de8267e0Stimh * setting is the default value. We don't actually want to create dsl 2946de8267e0Stimh * properties for these, so remove them from the source nvlist after 2947de8267e0Stimh * processing. 2948da6c28aaSamw */ 2949da6c28aaSamw static int 295014843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 29510a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 29520a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 2953da6c28aaSamw { 2954de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 2955de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 2956de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 2957da6c28aaSamw 2958de8267e0Stimh ASSERT(zplprops != NULL); 2959da6c28aaSamw 2960de8267e0Stimh /* 2961de8267e0Stimh * Pull out creator prop choices, if any. 2962de8267e0Stimh */ 2963de8267e0Stimh if (createprops) { 29640a48a24eStimh (void) nvlist_lookup_uint64(createprops, 29650a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 2966de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2967de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 2968de8267e0Stimh (void) nvlist_remove_all(createprops, 2969de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 2970de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2971de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 2972de8267e0Stimh (void) nvlist_remove_all(createprops, 2973de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 2974de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 2975de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 2976de8267e0Stimh (void) nvlist_remove_all(createprops, 2977de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 2978de8267e0Stimh } 2979da6c28aaSamw 2980c2a93d44Stimh /* 29810a48a24eStimh * If the zpl version requested is whacky or the file system 29820a48a24eStimh * or pool is version is too "young" to support normalization 29830a48a24eStimh * and the creator tried to set a value for one of the props, 29840a48a24eStimh * error out. 2985c2a93d44Stimh */ 29860a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 29870a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 29880a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 29890a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 2990de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 29910a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 2992de8267e0Stimh return (ENOTSUP); 2993c2a93d44Stimh 2994de8267e0Stimh /* 2995de8267e0Stimh * Put the version in the zplprops 2996de8267e0Stimh */ 2997de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 2998de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 2999da6c28aaSamw 3000de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 3001de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 3002de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3003de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 3004da6c28aaSamw 3005c2a93d44Stimh /* 3006de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 3007c2a93d44Stimh */ 3008de8267e0Stimh if (norm) 3009de8267e0Stimh u8 = 1; 3010de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 3011de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 3012de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3013de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 3014de8267e0Stimh 3015de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 3016de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 3017de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3018de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 3019c2a93d44Stimh 3020ab04eb8eStimh if (is_ci) 3021ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 3022ab04eb8eStimh 3023da6c28aaSamw return (0); 3024fa9e4066Sahrens } 3025fa9e4066Sahrens 30260a48a24eStimh static int 30270a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 30280a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 30290a48a24eStimh { 30300a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 30310a48a24eStimh uint64_t zplver = ZPL_VERSION; 30320a48a24eStimh objset_t *os = NULL; 30330a48a24eStimh char parentname[MAXNAMELEN]; 30340a48a24eStimh char *cp; 30350a586ceaSMark Shellenbaum spa_t *spa; 30360a586ceaSMark Shellenbaum uint64_t spa_vers; 30370a48a24eStimh int error; 30380a48a24eStimh 30390a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 30400a48a24eStimh cp = strrchr(parentname, '/'); 30410a48a24eStimh ASSERT(cp != NULL); 30420a48a24eStimh cp[0] = '\0'; 30430a48a24eStimh 30440a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 30450a586ceaSMark Shellenbaum return (error); 30460a586ceaSMark Shellenbaum 30470a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 30480a586ceaSMark Shellenbaum spa_close(spa, FTAG); 30490a586ceaSMark Shellenbaum 30500a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 30510a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 30520a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 30530a48a24eStimh 30540a48a24eStimh /* 30550a48a24eStimh * Open parent object set so we can inherit zplprop values. 30560a48a24eStimh */ 3057503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 30580a48a24eStimh return (error); 30590a48a24eStimh 30600a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 30610a48a24eStimh zplprops, is_ci); 3062503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 30630a48a24eStimh return (error); 30640a48a24eStimh } 30650a48a24eStimh 30660a48a24eStimh static int 30670a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 30680a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 30690a48a24eStimh { 30700a586ceaSMark Shellenbaum boolean_t fuids_ok; 30710a586ceaSMark Shellenbaum boolean_t sa_ok; 30720a48a24eStimh uint64_t zplver = ZPL_VERSION; 30730a48a24eStimh int error; 30740a48a24eStimh 30750a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 30760a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 30770a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 30780a48a24eStimh 30790a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 30800a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 30810a48a24eStimh return (error); 30820a48a24eStimh } 30830a48a24eStimh 30843cb34c60Sahrens /* 3085*4445fffbSMatthew Ahrens * innvl: { 3086*4445fffbSMatthew Ahrens * "type" -> dmu_objset_type_t (int32) 3087*4445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 3088*4445fffbSMatthew Ahrens * } 30893cb34c60Sahrens * 3090*4445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 30913cb34c60Sahrens */ 3092fa9e4066Sahrens static int 3093*4445fffbSMatthew Ahrens zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3094fa9e4066Sahrens { 3095fa9e4066Sahrens int error = 0; 3096*4445fffbSMatthew Ahrens zfs_creat_t zct = { 0 }; 3097ecd6cf80Smarks nvlist_t *nvprops = NULL; 3098ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 3099*4445fffbSMatthew Ahrens int32_t type32; 3100*4445fffbSMatthew Ahrens dmu_objset_type_t type; 3101*4445fffbSMatthew Ahrens boolean_t is_insensitive = B_FALSE; 3102fa9e4066Sahrens 3103*4445fffbSMatthew Ahrens if (nvlist_lookup_int32(innvl, "type", &type32) != 0) 3104*4445fffbSMatthew Ahrens return (EINVAL); 3105*4445fffbSMatthew Ahrens type = type32; 3106*4445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3107fa9e4066Sahrens 3108*4445fffbSMatthew Ahrens switch (type) { 3109fa9e4066Sahrens case DMU_OST_ZFS: 3110fa9e4066Sahrens cbfunc = zfs_create_cb; 3111fa9e4066Sahrens break; 3112fa9e4066Sahrens 3113fa9e4066Sahrens case DMU_OST_ZVOL: 3114fa9e4066Sahrens cbfunc = zvol_create_cb; 3115fa9e4066Sahrens break; 3116fa9e4066Sahrens 3117fa9e4066Sahrens default: 31181d452cf5Sahrens cbfunc = NULL; 3119e7cbe64fSgw break; 3120fa9e4066Sahrens } 3121*4445fffbSMatthew Ahrens if (strchr(fsname, '@') || 3122*4445fffbSMatthew Ahrens strchr(fsname, '%')) 31231d452cf5Sahrens return (EINVAL); 3124fa9e4066Sahrens 3125da6c28aaSamw zct.zct_props = nvprops; 3126da6c28aaSamw 3127*4445fffbSMatthew Ahrens if (cbfunc == NULL) 3128*4445fffbSMatthew Ahrens return (EINVAL); 3129*4445fffbSMatthew Ahrens 3130*4445fffbSMatthew Ahrens if (type == DMU_OST_ZVOL) { 3131*4445fffbSMatthew Ahrens uint64_t volsize, volblocksize; 3132*4445fffbSMatthew Ahrens 3133*4445fffbSMatthew Ahrens if (nvprops == NULL) 3134*4445fffbSMatthew Ahrens return (EINVAL); 3135*4445fffbSMatthew Ahrens if (nvlist_lookup_uint64(nvprops, 3136*4445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) != 0) 3137fa9e4066Sahrens return (EINVAL); 3138fa9e4066Sahrens 3139*4445fffbSMatthew Ahrens if ((error = nvlist_lookup_uint64(nvprops, 3140*4445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 3141*4445fffbSMatthew Ahrens &volblocksize)) != 0 && error != ENOENT) 3142*4445fffbSMatthew Ahrens return (EINVAL); 3143*4445fffbSMatthew Ahrens 3144*4445fffbSMatthew Ahrens if (error != 0) 3145*4445fffbSMatthew Ahrens volblocksize = zfs_prop_default_numeric( 3146*4445fffbSMatthew Ahrens ZFS_PROP_VOLBLOCKSIZE); 3147*4445fffbSMatthew Ahrens 3148*4445fffbSMatthew Ahrens if ((error = zvol_check_volblocksize( 3149*4445fffbSMatthew Ahrens volblocksize)) != 0 || 3150*4445fffbSMatthew Ahrens (error = zvol_check_volsize(volsize, 3151*4445fffbSMatthew Ahrens volblocksize)) != 0) 3152fa9e4066Sahrens return (error); 3153*4445fffbSMatthew Ahrens } else if (type == DMU_OST_ZFS) { 3154*4445fffbSMatthew Ahrens int error; 3155ab04eb8eStimh 3156*4445fffbSMatthew Ahrens /* 3157*4445fffbSMatthew Ahrens * We have to have normalization and 3158*4445fffbSMatthew Ahrens * case-folding flags correct when we do the 3159*4445fffbSMatthew Ahrens * file system creation, so go figure them out 3160*4445fffbSMatthew Ahrens * now. 3161*4445fffbSMatthew Ahrens */ 3162*4445fffbSMatthew Ahrens VERIFY(nvlist_alloc(&zct.zct_zplprops, 3163*4445fffbSMatthew Ahrens NV_UNIQUE_NAME, KM_SLEEP) == 0); 3164*4445fffbSMatthew Ahrens error = zfs_fill_zplprops(fsname, nvprops, 3165*4445fffbSMatthew Ahrens zct.zct_zplprops, &is_insensitive); 3166*4445fffbSMatthew Ahrens if (error != 0) { 3167*4445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 3168da6c28aaSamw return (error); 3169da6c28aaSamw } 3170*4445fffbSMatthew Ahrens } 3171ab04eb8eStimh 3172*4445fffbSMatthew Ahrens error = dmu_objset_create(fsname, type, 3173*4445fffbSMatthew Ahrens is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 3174*4445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 31755c5460e9Seschrock 3176*4445fffbSMatthew Ahrens /* 3177*4445fffbSMatthew Ahrens * It would be nice to do this atomically. 3178*4445fffbSMatthew Ahrens */ 3179*4445fffbSMatthew Ahrens if (error == 0) { 3180*4445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 3181*4445fffbSMatthew Ahrens nvprops, outnvl); 3182*4445fffbSMatthew Ahrens if (error != 0) 3183*4445fffbSMatthew Ahrens (void) dmu_objset_destroy(fsname, B_FALSE); 3184*4445fffbSMatthew Ahrens } 3185*4445fffbSMatthew Ahrens return (error); 3186*4445fffbSMatthew Ahrens } 3187e9dbad6fSeschrock 3188*4445fffbSMatthew Ahrens /* 3189*4445fffbSMatthew Ahrens * innvl: { 3190*4445fffbSMatthew Ahrens * "origin" -> name of origin snapshot 3191*4445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 3192*4445fffbSMatthew Ahrens * } 3193*4445fffbSMatthew Ahrens * 3194*4445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 3195*4445fffbSMatthew Ahrens */ 3196*4445fffbSMatthew Ahrens static int 3197*4445fffbSMatthew Ahrens zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3198*4445fffbSMatthew Ahrens { 3199*4445fffbSMatthew Ahrens int error = 0; 3200*4445fffbSMatthew Ahrens nvlist_t *nvprops = NULL; 3201*4445fffbSMatthew Ahrens char *origin_name; 3202*4445fffbSMatthew Ahrens dsl_dataset_t *origin; 3203e9dbad6fSeschrock 3204*4445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) 3205*4445fffbSMatthew Ahrens return (EINVAL); 3206*4445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3207e9dbad6fSeschrock 3208*4445fffbSMatthew Ahrens if (strchr(fsname, '@') || 3209*4445fffbSMatthew Ahrens strchr(fsname, '%')) 3210*4445fffbSMatthew Ahrens return (EINVAL); 3211e9dbad6fSeschrock 3212*4445fffbSMatthew Ahrens if (dataset_namecheck(origin_name, NULL, NULL) != 0) 3213*4445fffbSMatthew Ahrens return (EINVAL); 3214da6c28aaSamw 3215*4445fffbSMatthew Ahrens error = dsl_dataset_hold(origin_name, FTAG, &origin); 3216*4445fffbSMatthew Ahrens if (error) 3217*4445fffbSMatthew Ahrens return (error); 3218*4445fffbSMatthew Ahrens 3219*4445fffbSMatthew Ahrens error = dmu_objset_clone(fsname, origin, 0); 3220*4445fffbSMatthew Ahrens dsl_dataset_rele(origin, FTAG); 3221*4445fffbSMatthew Ahrens if (error) 3222*4445fffbSMatthew Ahrens return (error); 3223e9dbad6fSeschrock 3224e9dbad6fSeschrock /* 3225e9dbad6fSeschrock * It would be nice to do this atomically. 3226e9dbad6fSeschrock */ 3227e9dbad6fSeschrock if (error == 0) { 3228*4445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 3229*4445fffbSMatthew Ahrens nvprops, outnvl); 323092241e0bSTom Erickson if (error != 0) 3231*4445fffbSMatthew Ahrens (void) dmu_objset_destroy(fsname, B_FALSE); 3232e9dbad6fSeschrock } 3233fa9e4066Sahrens return (error); 3234fa9e4066Sahrens } 3235fa9e4066Sahrens 32363cb34c60Sahrens /* 3237*4445fffbSMatthew Ahrens * innvl: { 3238*4445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 3239*4445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value (string) } 3240*4445fffbSMatthew Ahrens * } 3241*4445fffbSMatthew Ahrens * 3242*4445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 32433cb34c60Sahrens * 32443cb34c60Sahrens */ 3245fa9e4066Sahrens static int 3246*4445fffbSMatthew Ahrens zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3247fa9e4066Sahrens { 3248*4445fffbSMatthew Ahrens nvlist_t *snaps; 3249*4445fffbSMatthew Ahrens nvlist_t *props = NULL; 3250*4445fffbSMatthew Ahrens int error, poollen; 3251*4445fffbSMatthew Ahrens nvpair_t *pair; 3252bb0ade09Sahrens 3253*4445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &props); 3254*4445fffbSMatthew Ahrens if ((error = zfs_check_userprops(poolname, props)) != 0) 3255*4445fffbSMatthew Ahrens return (error); 3256*4445fffbSMatthew Ahrens 3257*4445fffbSMatthew Ahrens if (!nvlist_empty(props) && 3258*4445fffbSMatthew Ahrens zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) 3259*4445fffbSMatthew Ahrens return (ENOTSUP); 3260*4445fffbSMatthew Ahrens 3261*4445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 32621d452cf5Sahrens return (EINVAL); 3263*4445fffbSMatthew Ahrens poollen = strlen(poolname); 3264*4445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 3265*4445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 3266*4445fffbSMatthew Ahrens const char *name = nvpair_name(pair); 3267*4445fffbSMatthew Ahrens const char *cp = strchr(name, '@'); 3268bb0ade09Sahrens 3269*4445fffbSMatthew Ahrens /* 3270*4445fffbSMatthew Ahrens * The snap name must contain an @, and the part after it must 3271*4445fffbSMatthew Ahrens * contain only valid characters. 3272*4445fffbSMatthew Ahrens */ 3273*4445fffbSMatthew Ahrens if (cp == NULL || snapshot_namecheck(cp + 1, NULL, NULL) != 0) 3274*4445fffbSMatthew Ahrens return (EINVAL); 3275bb0ade09Sahrens 3276*4445fffbSMatthew Ahrens /* 3277*4445fffbSMatthew Ahrens * The snap must be in the specified pool. 3278*4445fffbSMatthew Ahrens */ 3279*4445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 3280*4445fffbSMatthew Ahrens (name[poollen] != '/' && name[poollen] != '@')) 3281*4445fffbSMatthew Ahrens return (EXDEV); 3282*4445fffbSMatthew Ahrens 3283*4445fffbSMatthew Ahrens /* This must be the only snap of this fs. */ 3284*4445fffbSMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(snaps, pair); 3285*4445fffbSMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(snaps, pair2)) { 3286*4445fffbSMatthew Ahrens if (strncmp(name, nvpair_name(pair2), cp - name + 1) 3287*4445fffbSMatthew Ahrens == 0) { 3288*4445fffbSMatthew Ahrens return (EXDEV); 3289*4445fffbSMatthew Ahrens } 3290*4445fffbSMatthew Ahrens } 3291*4445fffbSMatthew Ahrens } 3292bb0ade09Sahrens 3293*4445fffbSMatthew Ahrens error = dmu_objset_snapshot(snaps, props, outnvl); 3294*4445fffbSMatthew Ahrens return (error); 3295*4445fffbSMatthew Ahrens } 3296*4445fffbSMatthew Ahrens 3297*4445fffbSMatthew Ahrens /* 3298*4445fffbSMatthew Ahrens * innvl: "message" -> string 3299*4445fffbSMatthew Ahrens */ 3300*4445fffbSMatthew Ahrens /* ARGSUSED */ 3301*4445fffbSMatthew Ahrens static int 3302*4445fffbSMatthew Ahrens zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 3303*4445fffbSMatthew Ahrens { 3304*4445fffbSMatthew Ahrens char *message; 3305*4445fffbSMatthew Ahrens spa_t *spa; 3306*4445fffbSMatthew Ahrens int error; 3307*4445fffbSMatthew Ahrens char *poolname; 3308*4445fffbSMatthew Ahrens 3309*4445fffbSMatthew Ahrens /* 3310*4445fffbSMatthew Ahrens * The poolname in the ioctl is not set, we get it from the TSD, 3311*4445fffbSMatthew Ahrens * which was set at the end of the last successful ioctl that allows 3312*4445fffbSMatthew Ahrens * logging. The secpolicy func already checked that it is set. 3313*4445fffbSMatthew Ahrens * Only one log ioctl is allowed after each successful ioctl, so 3314*4445fffbSMatthew Ahrens * we clear the TSD here. 3315*4445fffbSMatthew Ahrens */ 3316*4445fffbSMatthew Ahrens poolname = tsd_get(zfs_allow_log_key); 3317*4445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, NULL); 3318*4445fffbSMatthew Ahrens error = spa_open(poolname, &spa, FTAG); 3319*4445fffbSMatthew Ahrens strfree(poolname); 3320*4445fffbSMatthew Ahrens if (error != 0) 3321*4445fffbSMatthew Ahrens return (error); 3322*4445fffbSMatthew Ahrens 3323*4445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "message", &message) != 0) { 3324*4445fffbSMatthew Ahrens spa_close(spa, FTAG); 3325*4445fffbSMatthew Ahrens return (EINVAL); 3326bb0ade09Sahrens } 3327ea2f5b9eSMatthew Ahrens 3328*4445fffbSMatthew Ahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 3329*4445fffbSMatthew Ahrens spa_close(spa, FTAG); 3330*4445fffbSMatthew Ahrens return (ENOTSUP); 3331*4445fffbSMatthew Ahrens } 3332ea2f5b9eSMatthew Ahrens 3333*4445fffbSMatthew Ahrens error = spa_history_log(spa, message); 3334*4445fffbSMatthew Ahrens spa_close(spa, FTAG); 3335bb0ade09Sahrens return (error); 33361d452cf5Sahrens } 3337fa9e4066Sahrens 3338*4445fffbSMatthew Ahrens /* ARGSUSED */ 3339cdf5b4caSmmusante int 3340fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg) 33411d452cf5Sahrens { 3342*4445fffbSMatthew Ahrens vfs_t *vfsp; 3343*4445fffbSMatthew Ahrens int err; 33441d452cf5Sahrens 3345*4445fffbSMatthew Ahrens if (strchr(name, '@') == NULL) 3346*4445fffbSMatthew Ahrens return (0); 33471d452cf5Sahrens 3348*4445fffbSMatthew Ahrens vfsp = zfs_get_vfs(name); 3349*4445fffbSMatthew Ahrens if (vfsp == NULL) 3350*4445fffbSMatthew Ahrens return (0); 33511d452cf5Sahrens 3352*4445fffbSMatthew Ahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) { 33531d452cf5Sahrens VFS_RELE(vfsp); 3354*4445fffbSMatthew Ahrens return (err); 33551d452cf5Sahrens } 3356*4445fffbSMatthew Ahrens VFS_RELE(vfsp); 3357*4445fffbSMatthew Ahrens 3358*4445fffbSMatthew Ahrens /* 3359*4445fffbSMatthew Ahrens * Always force the unmount for snapshots. 3360*4445fffbSMatthew Ahrens */ 3361*4445fffbSMatthew Ahrens return (dounmount(vfsp, MS_FORCE, kcred)); 33621d452cf5Sahrens } 33631d452cf5Sahrens 33643cb34c60Sahrens /* 3365*4445fffbSMatthew Ahrens * innvl: { 3366*4445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 3367*4445fffbSMatthew Ahrens * (optional boolean) "defer" 3368*4445fffbSMatthew Ahrens * } 3369*4445fffbSMatthew Ahrens * 3370*4445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 33713cb34c60Sahrens * 33723cb34c60Sahrens */ 33731d452cf5Sahrens static int 3374*4445fffbSMatthew Ahrens zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 33751d452cf5Sahrens { 3376*4445fffbSMatthew Ahrens int poollen; 3377*4445fffbSMatthew Ahrens nvlist_t *snaps; 337819b94df9SMatthew Ahrens nvpair_t *pair; 3379*4445fffbSMatthew Ahrens boolean_t defer; 33801d452cf5Sahrens 3381*4445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3382*4445fffbSMatthew Ahrens return (EINVAL); 3383*4445fffbSMatthew Ahrens defer = nvlist_exists(innvl, "defer"); 338419b94df9SMatthew Ahrens 3385*4445fffbSMatthew Ahrens poollen = strlen(poolname); 3386*4445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 3387*4445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 338819b94df9SMatthew Ahrens const char *name = nvpair_name(pair); 3389*4445fffbSMatthew Ahrens 339019b94df9SMatthew Ahrens /* 3391*4445fffbSMatthew Ahrens * The snap must be in the specified pool. 339219b94df9SMatthew Ahrens */ 3393*4445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 3394*4445fffbSMatthew Ahrens (name[poollen] != '/' && name[poollen] != '@')) 3395*4445fffbSMatthew Ahrens return (EXDEV); 339619b94df9SMatthew Ahrens 3397*4445fffbSMatthew Ahrens /* 3398*4445fffbSMatthew Ahrens * Ignore failures to unmount; dmu_snapshots_destroy_nvl() 3399*4445fffbSMatthew Ahrens * will deal with this gracefully (by filling in outnvl). 3400*4445fffbSMatthew Ahrens */ 340119b94df9SMatthew Ahrens (void) zfs_unmount_snap(name, NULL); 340219b94df9SMatthew Ahrens } 340319b94df9SMatthew Ahrens 3404*4445fffbSMatthew Ahrens return (dmu_snapshots_destroy_nvl(snaps, defer, outnvl)); 34051d452cf5Sahrens } 34061d452cf5Sahrens 34073cb34c60Sahrens /* 34083cb34c60Sahrens * inputs: 34093cb34c60Sahrens * zc_name name of dataset to destroy 34103cb34c60Sahrens * zc_objset_type type of objset 3411842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 34123cb34c60Sahrens * 34133cb34c60Sahrens * outputs: none 34143cb34c60Sahrens */ 34151d452cf5Sahrens static int 34161d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 34171d452cf5Sahrens { 3418681d9761SEric Taylor int err; 34191d452cf5Sahrens if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) { 3420681d9761SEric Taylor err = zfs_unmount_snap(zc->zc_name, NULL); 34211d452cf5Sahrens if (err) 34221d452cf5Sahrens return (err); 3423fa9e4066Sahrens } 3424fa9e4066Sahrens 3425681d9761SEric Taylor err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy); 3426681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 34275c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 3428681d9761SEric Taylor return (err); 3429fa9e4066Sahrens } 3430fa9e4066Sahrens 34313cb34c60Sahrens /* 34323cb34c60Sahrens * inputs: 34334ccbb6e7Sahrens * zc_name name of dataset to rollback (to most recent snapshot) 34343cb34c60Sahrens * 34353cb34c60Sahrens * outputs: none 34363cb34c60Sahrens */ 3437fa9e4066Sahrens static int 3438fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc) 3439fa9e4066Sahrens { 3440ae46e4c7SMatthew Ahrens dsl_dataset_t *ds, *clone; 34414ccbb6e7Sahrens int error; 3442ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 3443ae46e4c7SMatthew Ahrens char *clone_name; 3444ae46e4c7SMatthew Ahrens 3445ae46e4c7SMatthew Ahrens error = dsl_dataset_hold(zc->zc_name, FTAG, &ds); 3446ae46e4c7SMatthew Ahrens if (error) 3447ae46e4c7SMatthew Ahrens return (error); 3448ae46e4c7SMatthew Ahrens 3449ae46e4c7SMatthew Ahrens /* must not be a snapshot */ 3450ae46e4c7SMatthew Ahrens if (dsl_dataset_is_snapshot(ds)) { 3451ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 3452ae46e4c7SMatthew Ahrens return (EINVAL); 3453ae46e4c7SMatthew Ahrens } 3454ae46e4c7SMatthew Ahrens 3455ae46e4c7SMatthew Ahrens /* must have a most recent snapshot */ 3456ae46e4c7SMatthew Ahrens if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) { 3457ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 3458ae46e4c7SMatthew Ahrens return (EINVAL); 3459ae46e4c7SMatthew Ahrens } 34604ccbb6e7Sahrens 34614ccbb6e7Sahrens /* 3462ae46e4c7SMatthew Ahrens * Create clone of most recent snapshot. 34634ccbb6e7Sahrens */ 3464ae46e4c7SMatthew Ahrens clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name); 3465ae46e4c7SMatthew Ahrens error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT); 34664ccbb6e7Sahrens if (error) 3467ae46e4c7SMatthew Ahrens goto out; 34684ccbb6e7Sahrens 3469503ad85cSMatthew Ahrens error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone); 3470ae46e4c7SMatthew Ahrens if (error) 3471ae46e4c7SMatthew Ahrens goto out; 3472ae46e4c7SMatthew Ahrens 3473ae46e4c7SMatthew Ahrens /* 3474ae46e4c7SMatthew Ahrens * Do clone swap. 3475ae46e4c7SMatthew Ahrens */ 347614843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 3477503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 347847f263f4Sek if (error == 0) { 347947f263f4Sek int resume_err; 34804ccbb6e7Sahrens 3481ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 3482ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, 3483ae46e4c7SMatthew Ahrens B_TRUE); 3484ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 3485ae46e4c7SMatthew Ahrens ds = NULL; 3486ae46e4c7SMatthew Ahrens } else { 3487ae46e4c7SMatthew Ahrens error = EBUSY; 3488ae46e4c7SMatthew Ahrens } 3489503ad85cSMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, zc->zc_name); 349047f263f4Sek error = error ? error : resume_err; 349147f263f4Sek } 34924ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 34934ccbb6e7Sahrens } else { 3494ae46e4c7SMatthew Ahrens if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) { 3495ae46e4c7SMatthew Ahrens error = dsl_dataset_clone_swap(clone, ds, B_TRUE); 3496ae46e4c7SMatthew Ahrens dsl_dataset_disown(ds, FTAG); 3497ae46e4c7SMatthew Ahrens ds = NULL; 3498ae46e4c7SMatthew Ahrens } else { 3499ae46e4c7SMatthew Ahrens error = EBUSY; 3500ae46e4c7SMatthew Ahrens } 35014ccbb6e7Sahrens } 35024ccbb6e7Sahrens 3503ae46e4c7SMatthew Ahrens /* 3504ae46e4c7SMatthew Ahrens * Destroy clone (which also closes it). 3505ae46e4c7SMatthew Ahrens */ 3506ae46e4c7SMatthew Ahrens (void) dsl_dataset_destroy(clone, FTAG, B_FALSE); 3507ae46e4c7SMatthew Ahrens 3508ae46e4c7SMatthew Ahrens out: 3509ae46e4c7SMatthew Ahrens strfree(clone_name); 3510ae46e4c7SMatthew Ahrens if (ds) 3511ae46e4c7SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 35124ccbb6e7Sahrens return (error); 3513fa9e4066Sahrens } 3514fa9e4066Sahrens 35153cb34c60Sahrens /* 35163cb34c60Sahrens * inputs: 35173cb34c60Sahrens * zc_name old name of dataset 35183cb34c60Sahrens * zc_value new name of dataset 35193cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 35203cb34c60Sahrens * 35213cb34c60Sahrens * outputs: none 35223cb34c60Sahrens */ 3523fa9e4066Sahrens static int 3524fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3525fa9e4066Sahrens { 35267f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 3527cdf5b4caSmmusante 3528e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3529f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3530f18faf3fSek strchr(zc->zc_value, '%')) 3531fa9e4066Sahrens return (EINVAL); 3532fa9e4066Sahrens 3533cdf5b4caSmmusante /* 3534cdf5b4caSmmusante * Unmount snapshot unless we're doing a recursive rename, 3535cdf5b4caSmmusante * in which case the dataset code figures out which snapshots 3536cdf5b4caSmmusante * to unmount. 3537cdf5b4caSmmusante */ 3538cdf5b4caSmmusante if (!recursive && strchr(zc->zc_name, '@') != NULL && 3539fa9e4066Sahrens zc->zc_objset_type == DMU_OST_ZFS) { 35401d452cf5Sahrens int err = zfs_unmount_snap(zc->zc_name, NULL); 35411d452cf5Sahrens if (err) 35421d452cf5Sahrens return (err); 3543fa9e4066Sahrens } 3544681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL) 3545681d9761SEric Taylor (void) zvol_remove_minor(zc->zc_name); 3546cdf5b4caSmmusante return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); 3547fa9e4066Sahrens } 3548fa9e4066Sahrens 354992241e0bSTom Erickson static int 355092241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 355192241e0bSTom Erickson { 355292241e0bSTom Erickson const char *propname = nvpair_name(pair); 355392241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 355492241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 355592241e0bSTom Erickson uint64_t intval; 355692241e0bSTom Erickson int err; 355792241e0bSTom Erickson 355892241e0bSTom Erickson if (prop == ZPROP_INVAL) { 355992241e0bSTom Erickson if (zfs_prop_user(propname)) { 356092241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 356192241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 356292241e0bSTom Erickson return (err); 356392241e0bSTom Erickson return (0); 356492241e0bSTom Erickson } 356592241e0bSTom Erickson 356692241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 356792241e0bSTom Erickson const char *perm = NULL; 356892241e0bSTom Erickson const char *uq_prefix = 356992241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 357092241e0bSTom Erickson const char *gq_prefix = 357192241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 357292241e0bSTom Erickson 357392241e0bSTom Erickson if (strncmp(propname, uq_prefix, 357492241e0bSTom Erickson strlen(uq_prefix)) == 0) { 357592241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 357692241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 357792241e0bSTom Erickson strlen(gq_prefix)) == 0) { 357892241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 357992241e0bSTom Erickson } else { 358092241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 358192241e0bSTom Erickson return (EINVAL); 358292241e0bSTom Erickson } 358392241e0bSTom Erickson 358492241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 358592241e0bSTom Erickson return (err); 358692241e0bSTom Erickson return (0); 358792241e0bSTom Erickson } 358892241e0bSTom Erickson 358992241e0bSTom Erickson return (EINVAL); 359092241e0bSTom Erickson } 359192241e0bSTom Erickson 359292241e0bSTom Erickson if (issnap) 359392241e0bSTom Erickson return (EINVAL); 359492241e0bSTom Erickson 359592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 359692241e0bSTom Erickson /* 359792241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 359892241e0bSTom Erickson * format. 359992241e0bSTom Erickson */ 360092241e0bSTom Erickson nvlist_t *attrs; 360192241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 360292241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 360392241e0bSTom Erickson &pair) == 0); 360492241e0bSTom Erickson } 360592241e0bSTom Erickson 360692241e0bSTom Erickson /* 360792241e0bSTom Erickson * Check that this value is valid for this pool version 360892241e0bSTom Erickson */ 360992241e0bSTom Erickson switch (prop) { 361092241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 361192241e0bSTom Erickson /* 361292241e0bSTom Erickson * If the user specified gzip compression, make sure 361392241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 361492241e0bSTom Erickson * we'll catch them later. 361592241e0bSTom Erickson */ 361692241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 361792241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 361892241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 361992241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 362092241e0bSTom Erickson zfs_earlier_version(dsname, 362192241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 362292241e0bSTom Erickson return (ENOTSUP); 362392241e0bSTom Erickson } 362492241e0bSTom Erickson 362592241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 362692241e0bSTom Erickson zfs_earlier_version(dsname, 362792241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 362892241e0bSTom Erickson return (ENOTSUP); 362992241e0bSTom Erickson 363092241e0bSTom Erickson /* 363192241e0bSTom Erickson * If this is a bootable dataset then 363292241e0bSTom Erickson * verify that the compression algorithm 363392241e0bSTom Erickson * is supported for booting. We must return 363492241e0bSTom Erickson * something other than ENOTSUP since it 363592241e0bSTom Erickson * implies a downrev pool version. 363692241e0bSTom Erickson */ 363792241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 363892241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 363992241e0bSTom Erickson return (ERANGE); 364092241e0bSTom Erickson } 364192241e0bSTom Erickson } 364292241e0bSTom Erickson break; 364392241e0bSTom Erickson 364492241e0bSTom Erickson case ZFS_PROP_COPIES: 364592241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 364692241e0bSTom Erickson return (ENOTSUP); 364792241e0bSTom Erickson break; 364892241e0bSTom Erickson 364992241e0bSTom Erickson case ZFS_PROP_DEDUP: 365092241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 365192241e0bSTom Erickson return (ENOTSUP); 365292241e0bSTom Erickson break; 365392241e0bSTom Erickson 365492241e0bSTom Erickson case ZFS_PROP_SHARESMB: 365592241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 365692241e0bSTom Erickson return (ENOTSUP); 365792241e0bSTom Erickson break; 365892241e0bSTom Erickson 365992241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 366092241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 366192241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 366292241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 366392241e0bSTom Erickson zfs_earlier_version(dsname, 366492241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 366592241e0bSTom Erickson return (ENOTSUP); 366692241e0bSTom Erickson } 366792241e0bSTom Erickson break; 366892241e0bSTom Erickson } 366992241e0bSTom Erickson 367092241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 367192241e0bSTom Erickson } 367292241e0bSTom Erickson 367392241e0bSTom Erickson /* 367492241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 367592241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 367692241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 367792241e0bSTom Erickson * 367892241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 367992241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 368092241e0bSTom Erickson * of all the properties that failed a permission check along with the 368192241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 368292241e0bSTom Erickson * returned errlist. 368392241e0bSTom Erickson * 368492241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 368592241e0bSTom Erickson * pointed at by errlist is NULL. 368692241e0bSTom Erickson */ 368792241e0bSTom Erickson static int 368892241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 3689745cd3c5Smaybee { 3690745cd3c5Smaybee zfs_cmd_t *zc; 369192241e0bSTom Erickson nvpair_t *pair, *next_pair; 369292241e0bSTom Erickson nvlist_t *errors; 369392241e0bSTom Erickson int err, rv = 0; 3694745cd3c5Smaybee 3695745cd3c5Smaybee if (props == NULL) 369692241e0bSTom Erickson return (0); 369792241e0bSTom Erickson 369892241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 369992241e0bSTom Erickson 3700745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 3701745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 370292241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 370392241e0bSTom Erickson while (pair != NULL) { 370492241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 370592241e0bSTom Erickson 370692241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 370792241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 3708*4445fffbSMatthew Ahrens (err = zfs_secpolicy_inherit_prop(zc, NULL, CRED())) != 0) { 370992241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 371092241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 371192241e0bSTom Erickson zc->zc_value, err) == 0); 371292241e0bSTom Erickson } 371392241e0bSTom Erickson pair = next_pair; 3714745cd3c5Smaybee } 3715745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 371692241e0bSTom Erickson 371792241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 371892241e0bSTom Erickson nvlist_free(errors); 371992241e0bSTom Erickson errors = NULL; 372092241e0bSTom Erickson } else { 372192241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 372292241e0bSTom Erickson } 372392241e0bSTom Erickson 372492241e0bSTom Erickson if (errlist == NULL) 372592241e0bSTom Erickson nvlist_free(errors); 372692241e0bSTom Erickson else 372792241e0bSTom Erickson *errlist = errors; 372892241e0bSTom Erickson 372992241e0bSTom Erickson return (rv); 373092241e0bSTom Erickson } 373192241e0bSTom Erickson 373292241e0bSTom Erickson static boolean_t 373392241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 373492241e0bSTom Erickson { 373592241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 373692241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 373792241e0bSTom Erickson nvlist_t *attrs; 373892241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 373992241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 374092241e0bSTom Erickson &p1) == 0); 374192241e0bSTom Erickson } 374292241e0bSTom Erickson 374392241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 374492241e0bSTom Erickson nvlist_t *attrs; 374592241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 374692241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 374792241e0bSTom Erickson &p2) == 0); 374892241e0bSTom Erickson } 374992241e0bSTom Erickson 375092241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 375192241e0bSTom Erickson return (B_FALSE); 375292241e0bSTom Erickson 375392241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 375492241e0bSTom Erickson char *valstr1, *valstr2; 375592241e0bSTom Erickson 375692241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 375792241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 375892241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 375992241e0bSTom Erickson } else { 376092241e0bSTom Erickson uint64_t intval1, intval2; 376192241e0bSTom Erickson 376292241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 376392241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 376492241e0bSTom Erickson return (intval1 == intval2); 376592241e0bSTom Erickson } 3766745cd3c5Smaybee } 3767745cd3c5Smaybee 376892241e0bSTom Erickson /* 376992241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 377092241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 377192241e0bSTom Erickson * do not need to clear or restore properties that won't change. 377292241e0bSTom Erickson */ 377392241e0bSTom Erickson static void 377492241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 377592241e0bSTom Erickson { 377692241e0bSTom Erickson nvpair_t *pair, *next_pair; 377792241e0bSTom Erickson 377892241e0bSTom Erickson if (origprops == NULL) 377992241e0bSTom Erickson return; /* all props need to be received */ 378092241e0bSTom Erickson 378192241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 378292241e0bSTom Erickson while (pair != NULL) { 378392241e0bSTom Erickson const char *propname = nvpair_name(pair); 378492241e0bSTom Erickson nvpair_t *match; 378592241e0bSTom Erickson 378692241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 378792241e0bSTom Erickson 378892241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 378992241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 379092241e0bSTom Erickson goto next; /* need to set received value */ 379192241e0bSTom Erickson 379292241e0bSTom Erickson /* don't clear the existing received value */ 379392241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 379492241e0bSTom Erickson /* don't bother receiving the property */ 379592241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 379692241e0bSTom Erickson next: 379792241e0bSTom Erickson pair = next_pair; 379892241e0bSTom Erickson } 379992241e0bSTom Erickson } 380092241e0bSTom Erickson 380192241e0bSTom Erickson #ifdef DEBUG 380292241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 380392241e0bSTom Erickson #endif 380492241e0bSTom Erickson 38053cb34c60Sahrens /* 38063cb34c60Sahrens * inputs: 38073cb34c60Sahrens * zc_name name of containing filesystem 38083cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 38093cb34c60Sahrens * zc_value name of snapshot to create 38103cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 38113cb34c60Sahrens * zc_cookie file descriptor to recv from 38123cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 38133cb34c60Sahrens * zc_guid force flag 3814c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor 3815c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping (or zero on first call) 38163cb34c60Sahrens * 38173cb34c60Sahrens * outputs: 38183cb34c60Sahrens * zc_cookie number of bytes read 381992241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 382092241e0bSTom Erickson * zc_obj zprop_errflags_t 3821c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping 38223cb34c60Sahrens */ 3823fa9e4066Sahrens static int 38243cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 3825fa9e4066Sahrens { 3826fa9e4066Sahrens file_t *fp; 3827f18faf3fSek objset_t *os; 38283cb34c60Sahrens dmu_recv_cookie_t drc; 3829f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 383092241e0bSTom Erickson int fd; 383192241e0bSTom Erickson int error = 0; 383292241e0bSTom Erickson int props_error = 0; 383392241e0bSTom Erickson nvlist_t *errors; 38343cb34c60Sahrens offset_t off; 383592241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 383692241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 38373cb34c60Sahrens objset_t *origin = NULL; 38383cb34c60Sahrens char *tosnap; 38393cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 384092241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 3841fa9e4066Sahrens 38423ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3843f18faf3fSek strchr(zc->zc_value, '@') == NULL || 3844f18faf3fSek strchr(zc->zc_value, '%')) 38453ccfa83cSahrens return (EINVAL); 38463ccfa83cSahrens 38473cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 38483cb34c60Sahrens tosnap = strchr(tofs, '@'); 384992241e0bSTom Erickson *tosnap++ = '\0'; 38503cb34c60Sahrens 38513cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 38523cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 3853478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 38543cb34c60Sahrens return (error); 38553cb34c60Sahrens 3856fa9e4066Sahrens fd = zc->zc_cookie; 3857fa9e4066Sahrens fp = getf(fd); 38583cb34c60Sahrens if (fp == NULL) { 38593cb34c60Sahrens nvlist_free(props); 3860fa9e4066Sahrens return (EBADF); 38613cb34c60Sahrens } 3862f18faf3fSek 386392241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 386492241e0bSTom Erickson 3865503ad85cSMatthew Ahrens if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) { 386692241e0bSTom Erickson if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) && 386792241e0bSTom Erickson !dsl_prop_get_hasrecvd(os)) { 386892241e0bSTom Erickson first_recvd_props = B_TRUE; 386992241e0bSTom Erickson } 387092241e0bSTom Erickson 3871745cd3c5Smaybee /* 387292241e0bSTom Erickson * If new received properties are supplied, they are to 387392241e0bSTom Erickson * completely replace the existing received properties, so stash 387492241e0bSTom Erickson * away the existing ones. 3875745cd3c5Smaybee */ 387692241e0bSTom Erickson if (dsl_prop_get_received(os, &origprops) == 0) { 387792241e0bSTom Erickson nvlist_t *errlist = NULL; 387892241e0bSTom Erickson /* 387992241e0bSTom Erickson * Don't bother writing a property if its value won't 388092241e0bSTom Erickson * change (and avoid the unnecessary security checks). 388192241e0bSTom Erickson * 388292241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 388392241e0bSTom Erickson * special case where we blow away all local properties 388492241e0bSTom Erickson * regardless. 388592241e0bSTom Erickson */ 388692241e0bSTom Erickson if (!first_recvd_props) 388792241e0bSTom Erickson props_reduce(props, origprops); 388892241e0bSTom Erickson if (zfs_check_clearable(tofs, origprops, 388992241e0bSTom Erickson &errlist) != 0) 389092241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 389192241e0bSTom Erickson nvlist_free(errlist); 389292241e0bSTom Erickson } 3893745cd3c5Smaybee 3894503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3895f18faf3fSek } 3896f18faf3fSek 38973cb34c60Sahrens if (zc->zc_string[0]) { 3898503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_string, FTAG, &origin); 3899745cd3c5Smaybee if (error) 3900745cd3c5Smaybee goto out; 39013cb34c60Sahrens } 39023cb34c60Sahrens 39039e69d7d0SLori Alt error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds, 39049e69d7d0SLori Alt &zc->zc_begin_record, force, origin, &drc); 39053cb34c60Sahrens if (origin) 3906503ad85cSMatthew Ahrens dmu_objset_rele(origin, FTAG); 3907745cd3c5Smaybee if (error) 3908745cd3c5Smaybee goto out; 3909f18faf3fSek 3910f18faf3fSek /* 391192241e0bSTom Erickson * Set properties before we receive the stream so that they are applied 391292241e0bSTom Erickson * to the new data. Note that we must call dmu_recv_stream() if 391392241e0bSTom Erickson * dmu_recv_begin() succeeds. 3914f18faf3fSek */ 39153cb34c60Sahrens if (props) { 391692241e0bSTom Erickson if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) { 391792241e0bSTom Erickson if (drc.drc_newfs) { 391892241e0bSTom Erickson if (spa_version(os->os_spa) >= 391992241e0bSTom Erickson SPA_VERSION_RECVD_PROPS) 392092241e0bSTom Erickson first_recvd_props = B_TRUE; 392192241e0bSTom Erickson } else if (origprops != NULL) { 392292241e0bSTom Erickson if (clear_received_props(os, tofs, origprops, 392392241e0bSTom Erickson first_recvd_props ? NULL : props) != 0) 392492241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 392592241e0bSTom Erickson } else { 392692241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 392792241e0bSTom Erickson } 392892241e0bSTom Erickson dsl_prop_set_hasrecvd(os); 392992241e0bSTom Erickson } else if (!drc.drc_newfs) { 393092241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 393192241e0bSTom Erickson } 393292241e0bSTom Erickson 393392241e0bSTom Erickson (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 3934*4445fffbSMatthew Ahrens props, errors); 393592241e0bSTom Erickson } 393692241e0bSTom Erickson 3937*4445fffbSMatthew Ahrens if (zc->zc_nvlist_dst_size != 0 && 3938*4445fffbSMatthew Ahrens (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || 3939*4445fffbSMatthew Ahrens put_nvlist(zc, errors) != 0)) { 3940745cd3c5Smaybee /* 394192241e0bSTom Erickson * Caller made zc->zc_nvlist_dst less than the minimum expected 394292241e0bSTom Erickson * size or supplied an invalid address. 3943745cd3c5Smaybee */ 394492241e0bSTom Erickson props_error = EINVAL; 39453cb34c60Sahrens } 39463cb34c60Sahrens 39473cb34c60Sahrens off = fp->f_offset; 3948c99e4bdcSChris Kirby error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, 3949c99e4bdcSChris Kirby &zc->zc_action_handle); 3950a2eea2e1Sahrens 3951f4b94bdeSMatthew Ahrens if (error == 0) { 3952f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 3953745cd3c5Smaybee 3954f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 3955f4b94bdeSMatthew Ahrens /* online recv */ 3956f4b94bdeSMatthew Ahrens int end_err; 3957745cd3c5Smaybee 3958503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 3959f4b94bdeSMatthew Ahrens /* 3960f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 3961f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 3962f4b94bdeSMatthew Ahrens */ 3963f4b94bdeSMatthew Ahrens end_err = dmu_recv_end(&drc); 39645c703fceSGeorge Wilson if (error == 0) 39655c703fceSGeorge Wilson error = zfs_resume_fs(zfsvfs, tofs); 3966f4b94bdeSMatthew Ahrens error = error ? error : end_err; 3967f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 3968745cd3c5Smaybee } else { 3969f4b94bdeSMatthew Ahrens error = dmu_recv_end(&drc); 39703cb34c60Sahrens } 397147f263f4Sek } 39723cb34c60Sahrens 39733cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 39743cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 39753cb34c60Sahrens fp->f_offset = off; 3976a2eea2e1Sahrens 397792241e0bSTom Erickson #ifdef DEBUG 397892241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 397992241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 398092241e0bSTom Erickson error = 1; 398192241e0bSTom Erickson } 398292241e0bSTom Erickson #endif 3983745cd3c5Smaybee /* 3984745cd3c5Smaybee * On error, restore the original props. 3985745cd3c5Smaybee */ 3986745cd3c5Smaybee if (error && props) { 398792241e0bSTom Erickson if (dmu_objset_hold(tofs, FTAG, &os) == 0) { 398892241e0bSTom Erickson if (clear_received_props(os, tofs, props, NULL) != 0) { 398992241e0bSTom Erickson /* 399092241e0bSTom Erickson * We failed to clear the received properties. 399192241e0bSTom Erickson * Since we may have left a $recvd value on the 399292241e0bSTom Erickson * system, we can't clear the $hasrecvd flag. 399392241e0bSTom Erickson */ 399492241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 399592241e0bSTom Erickson } else if (first_recvd_props) { 399692241e0bSTom Erickson dsl_prop_unset_hasrecvd(os); 399792241e0bSTom Erickson } 399892241e0bSTom Erickson dmu_objset_rele(os, FTAG); 399992241e0bSTom Erickson } else if (!drc.drc_newfs) { 400092241e0bSTom Erickson /* We failed to clear the received properties. */ 400192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 400292241e0bSTom Erickson } 400392241e0bSTom Erickson 400492241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 400592241e0bSTom Erickson /* We failed to stash the original properties. */ 400692241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 400792241e0bSTom Erickson } 400892241e0bSTom Erickson 400992241e0bSTom Erickson /* 401092241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 401192241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 401292241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 401392241e0bSTom Erickson * first new-style receive. 401492241e0bSTom Erickson */ 401592241e0bSTom Erickson if (origprops != NULL && 401692241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 401792241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 401892241e0bSTom Erickson origprops, NULL) != 0) { 401992241e0bSTom Erickson /* 402092241e0bSTom Erickson * We stashed the original properties but failed to 402192241e0bSTom Erickson * restore them. 402292241e0bSTom Erickson */ 402392241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 402492241e0bSTom Erickson } 4025745cd3c5Smaybee } 4026745cd3c5Smaybee out: 4027745cd3c5Smaybee nvlist_free(props); 4028745cd3c5Smaybee nvlist_free(origprops); 402992241e0bSTom Erickson nvlist_free(errors); 4030fa9e4066Sahrens releasef(fd); 403192241e0bSTom Erickson 403292241e0bSTom Erickson if (error == 0) 403392241e0bSTom Erickson error = props_error; 403492241e0bSTom Erickson 4035fa9e4066Sahrens return (error); 4036fa9e4066Sahrens } 4037fa9e4066Sahrens 40383cb34c60Sahrens /* 40393cb34c60Sahrens * inputs: 40403cb34c60Sahrens * zc_name name of snapshot to send 40413cb34c60Sahrens * zc_cookie file descriptor to send stream to 4042a7f53a56SChris Kirby * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) 4043a7f53a56SChris Kirby * zc_sendobj objsetid of snapshot to send 4044a7f53a56SChris Kirby * zc_fromobj objsetid of incremental fromsnap (may be zero) 404519b94df9SMatthew Ahrens * zc_guid if set, estimate size of stream only. zc_cookie is ignored. 404619b94df9SMatthew Ahrens * output size in zc_objset_type. 40473cb34c60Sahrens * 40483cb34c60Sahrens * outputs: none 40493cb34c60Sahrens */ 4050fa9e4066Sahrens static int 40513cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 4052fa9e4066Sahrens { 4053fa9e4066Sahrens objset_t *fromsnap = NULL; 4054fa9e4066Sahrens objset_t *tosnap; 4055fa9e4066Sahrens int error; 40563cb34c60Sahrens offset_t off; 4057a7f53a56SChris Kirby dsl_dataset_t *ds; 4058a7f53a56SChris Kirby dsl_dataset_t *dsfrom = NULL; 4059a7f53a56SChris Kirby spa_t *spa; 4060a7f53a56SChris Kirby dsl_pool_t *dp; 406119b94df9SMatthew Ahrens boolean_t estimate = (zc->zc_guid != 0); 4062fa9e4066Sahrens 4063a7f53a56SChris Kirby error = spa_open(zc->zc_name, &spa, FTAG); 4064fa9e4066Sahrens if (error) 4065fa9e4066Sahrens return (error); 4066fa9e4066Sahrens 4067a7f53a56SChris Kirby dp = spa_get_dsl(spa); 4068a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 4069a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 4070a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 4071*4445fffbSMatthew Ahrens spa_close(spa, FTAG); 4072*4445fffbSMatthew Ahrens if (error) 4073a7f53a56SChris Kirby return (error); 4074a7f53a56SChris Kirby 4075a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &tosnap); 4076a7f53a56SChris Kirby if (error) { 4077a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4078a7f53a56SChris Kirby return (error); 4079a7f53a56SChris Kirby } 4080a7f53a56SChris Kirby 4081a7f53a56SChris Kirby if (zc->zc_fromobj != 0) { 4082a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 4083a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &dsfrom); 4084a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 4085a7f53a56SChris Kirby if (error) { 4086a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4087a7f53a56SChris Kirby return (error); 4088a7f53a56SChris Kirby } 4089a7f53a56SChris Kirby error = dmu_objset_from_ds(dsfrom, &fromsnap); 4090fa9e4066Sahrens if (error) { 4091a7f53a56SChris Kirby dsl_dataset_rele(dsfrom, FTAG); 4092a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4093fa9e4066Sahrens return (error); 4094fa9e4066Sahrens } 4095*4445fffbSMatthew Ahrens } 4096*4445fffbSMatthew Ahrens 4097*4445fffbSMatthew Ahrens if (zc->zc_obj) { 4098*4445fffbSMatthew Ahrens dsl_pool_t *dp = ds->ds_dir->dd_pool; 4099*4445fffbSMatthew Ahrens 4100*4445fffbSMatthew Ahrens if (fromsnap != NULL) { 4101*4445fffbSMatthew Ahrens dsl_dataset_rele(dsfrom, FTAG); 4102*4445fffbSMatthew Ahrens dsl_dataset_rele(ds, FTAG); 4103*4445fffbSMatthew Ahrens return (EINVAL); 4104*4445fffbSMatthew Ahrens } 4105*4445fffbSMatthew Ahrens 4106*4445fffbSMatthew Ahrens if (dsl_dir_is_clone(ds->ds_dir)) { 4107*4445fffbSMatthew Ahrens rw_enter(&dp->dp_config_rwlock, RW_READER); 4108*4445fffbSMatthew Ahrens error = dsl_dataset_hold_obj(dp, 4109*4445fffbSMatthew Ahrens ds->ds_dir->dd_phys->dd_origin_obj, FTAG, &dsfrom); 4110*4445fffbSMatthew Ahrens rw_exit(&dp->dp_config_rwlock); 4111*4445fffbSMatthew Ahrens if (error) { 4112*4445fffbSMatthew Ahrens dsl_dataset_rele(ds, FTAG); 4113*4445fffbSMatthew Ahrens return (error); 4114*4445fffbSMatthew Ahrens } 4115*4445fffbSMatthew Ahrens error = dmu_objset_from_ds(dsfrom, &fromsnap); 4116*4445fffbSMatthew Ahrens if (error) { 4117*4445fffbSMatthew Ahrens dsl_dataset_rele(dsfrom, FTAG); 4118*4445fffbSMatthew Ahrens dsl_dataset_rele(ds, FTAG); 4119*4445fffbSMatthew Ahrens return (error); 4120*4445fffbSMatthew Ahrens } 4121*4445fffbSMatthew Ahrens } 4122fa9e4066Sahrens } 4123fa9e4066Sahrens 412419b94df9SMatthew Ahrens if (estimate) { 4125*4445fffbSMatthew Ahrens error = dmu_send_estimate(tosnap, fromsnap, 412619b94df9SMatthew Ahrens &zc->zc_objset_type); 412719b94df9SMatthew Ahrens } else { 412819b94df9SMatthew Ahrens file_t *fp = getf(zc->zc_cookie); 412919b94df9SMatthew Ahrens if (fp == NULL) { 413019b94df9SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 413119b94df9SMatthew Ahrens if (dsfrom) 413219b94df9SMatthew Ahrens dsl_dataset_rele(dsfrom, FTAG); 413319b94df9SMatthew Ahrens return (EBADF); 413419b94df9SMatthew Ahrens } 4135fa9e4066Sahrens 413619b94df9SMatthew Ahrens off = fp->f_offset; 4137*4445fffbSMatthew Ahrens error = dmu_send(tosnap, fromsnap, 41384e3c9f44SBill Pijewski zc->zc_cookie, fp->f_vnode, &off); 4139fa9e4066Sahrens 414019b94df9SMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 414119b94df9SMatthew Ahrens fp->f_offset = off; 414219b94df9SMatthew Ahrens releasef(zc->zc_cookie); 414319b94df9SMatthew Ahrens } 4144a7f53a56SChris Kirby if (dsfrom) 4145a7f53a56SChris Kirby dsl_dataset_rele(dsfrom, FTAG); 4146a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4147fa9e4066Sahrens return (error); 4148fa9e4066Sahrens } 4149fa9e4066Sahrens 41504e3c9f44SBill Pijewski /* 41514e3c9f44SBill Pijewski * inputs: 41524e3c9f44SBill Pijewski * zc_name name of snapshot on which to report progress 41534e3c9f44SBill Pijewski * zc_cookie file descriptor of send stream 41544e3c9f44SBill Pijewski * 41554e3c9f44SBill Pijewski * outputs: 41564e3c9f44SBill Pijewski * zc_cookie number of bytes written in send stream thus far 41574e3c9f44SBill Pijewski */ 41584e3c9f44SBill Pijewski static int 41594e3c9f44SBill Pijewski zfs_ioc_send_progress(zfs_cmd_t *zc) 41604e3c9f44SBill Pijewski { 41614e3c9f44SBill Pijewski dsl_dataset_t *ds; 41624e3c9f44SBill Pijewski dmu_sendarg_t *dsp = NULL; 41634e3c9f44SBill Pijewski int error; 41644e3c9f44SBill Pijewski 41654e3c9f44SBill Pijewski if ((error = dsl_dataset_hold(zc->zc_name, FTAG, &ds)) != 0) 41664e3c9f44SBill Pijewski return (error); 41674e3c9f44SBill Pijewski 41684e3c9f44SBill Pijewski mutex_enter(&ds->ds_sendstream_lock); 41694e3c9f44SBill Pijewski 41704e3c9f44SBill Pijewski /* 41714e3c9f44SBill Pijewski * Iterate over all the send streams currently active on this dataset. 41724e3c9f44SBill Pijewski * If there's one which matches the specified file descriptor _and_ the 41734e3c9f44SBill Pijewski * stream was started by the current process, return the progress of 41744e3c9f44SBill Pijewski * that stream. 41754e3c9f44SBill Pijewski */ 41764e3c9f44SBill Pijewski for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL; 41774e3c9f44SBill Pijewski dsp = list_next(&ds->ds_sendstreams, dsp)) { 41784e3c9f44SBill Pijewski if (dsp->dsa_outfd == zc->zc_cookie && 41794e3c9f44SBill Pijewski dsp->dsa_proc == curproc) 41804e3c9f44SBill Pijewski break; 41814e3c9f44SBill Pijewski } 41824e3c9f44SBill Pijewski 41834e3c9f44SBill Pijewski if (dsp != NULL) 41844e3c9f44SBill Pijewski zc->zc_cookie = *(dsp->dsa_off); 41854e3c9f44SBill Pijewski else 41864e3c9f44SBill Pijewski error = ENOENT; 41874e3c9f44SBill Pijewski 41884e3c9f44SBill Pijewski mutex_exit(&ds->ds_sendstream_lock); 41894e3c9f44SBill Pijewski dsl_dataset_rele(ds, FTAG); 41904e3c9f44SBill Pijewski return (error); 41914e3c9f44SBill Pijewski } 41924e3c9f44SBill Pijewski 4193ea8dc4b6Seschrock static int 4194ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 4195ea8dc4b6Seschrock { 4196ea8dc4b6Seschrock int id, error; 4197ea8dc4b6Seschrock 4198ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 4199ea8dc4b6Seschrock &zc->zc_inject_record); 4200ea8dc4b6Seschrock 4201ea8dc4b6Seschrock if (error == 0) 4202ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 4203ea8dc4b6Seschrock 4204ea8dc4b6Seschrock return (error); 4205ea8dc4b6Seschrock } 4206ea8dc4b6Seschrock 4207ea8dc4b6Seschrock static int 4208ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 4209ea8dc4b6Seschrock { 4210ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 4211ea8dc4b6Seschrock } 4212ea8dc4b6Seschrock 4213ea8dc4b6Seschrock static int 4214ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 4215ea8dc4b6Seschrock { 4216ea8dc4b6Seschrock int id = (int)zc->zc_guid; 4217ea8dc4b6Seschrock int error; 4218ea8dc4b6Seschrock 4219ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 4220ea8dc4b6Seschrock &zc->zc_inject_record); 4221ea8dc4b6Seschrock 4222ea8dc4b6Seschrock zc->zc_guid = id; 4223ea8dc4b6Seschrock 4224ea8dc4b6Seschrock return (error); 4225ea8dc4b6Seschrock } 4226ea8dc4b6Seschrock 4227ea8dc4b6Seschrock static int 4228ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 4229ea8dc4b6Seschrock { 4230ea8dc4b6Seschrock spa_t *spa; 4231ea8dc4b6Seschrock int error; 4232e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 4233ea8dc4b6Seschrock 4234ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 4235ea8dc4b6Seschrock return (error); 4236ea8dc4b6Seschrock 4237e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 4238ea8dc4b6Seschrock &count); 4239ea8dc4b6Seschrock if (error == 0) 4240e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 4241ea8dc4b6Seschrock else 4242e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 4243ea8dc4b6Seschrock 4244ea8dc4b6Seschrock spa_close(spa, FTAG); 4245ea8dc4b6Seschrock 4246ea8dc4b6Seschrock return (error); 4247ea8dc4b6Seschrock } 4248ea8dc4b6Seschrock 4249ea8dc4b6Seschrock static int 4250ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 4251ea8dc4b6Seschrock { 4252ea8dc4b6Seschrock spa_t *spa; 4253ea8dc4b6Seschrock vdev_t *vd; 4254bb8b5132Sek int error; 4255ea8dc4b6Seschrock 4256b87f3af3Sperrin /* 4257b87f3af3Sperrin * On zpool clear we also fix up missing slogs 4258b87f3af3Sperrin */ 4259b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 4260b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 4261b87f3af3Sperrin if (spa == NULL) { 4262b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4263b87f3af3Sperrin return (EIO); 4264b87f3af3Sperrin } 4265b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 4266b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 4267b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 4268b87f3af3Sperrin } 4269468c413aSTim Haley spa->spa_last_open_failed = 0; 4270b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4271b87f3af3Sperrin 4272c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 4273468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 4274468c413aSTim Haley } else { 4275468c413aSTim Haley nvlist_t *policy; 4276468c413aSTim Haley nvlist_t *config = NULL; 4277468c413aSTim Haley 4278468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 4279468c413aSTim Haley return (EINVAL); 4280468c413aSTim Haley 4281468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 4282468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 4283468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 4284468c413aSTim Haley policy, &config); 4285468c413aSTim Haley if (config != NULL) { 42864b964adaSGeorge Wilson int err; 42874b964adaSGeorge Wilson 42884b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 42894b964adaSGeorge Wilson error = err; 4290468c413aSTim Haley nvlist_free(config); 4291468c413aSTim Haley } 4292468c413aSTim Haley nvlist_free(policy); 4293468c413aSTim Haley } 4294468c413aSTim Haley } 4295468c413aSTim Haley 4296468c413aSTim Haley if (error) 4297ea8dc4b6Seschrock return (error); 4298ea8dc4b6Seschrock 42998f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4300ea8dc4b6Seschrock 4301e9dbad6fSeschrock if (zc->zc_guid == 0) { 4302ea8dc4b6Seschrock vd = NULL; 4303c5904d13Seschrock } else { 4304c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 4305fa94a07fSbrendan if (vd == NULL) { 4306e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 4307fa94a07fSbrendan spa_close(spa, FTAG); 4308fa94a07fSbrendan return (ENODEV); 4309fa94a07fSbrendan } 4310ea8dc4b6Seschrock } 4311ea8dc4b6Seschrock 4312e14bb325SJeff Bonwick vdev_clear(spa, vd); 4313e14bb325SJeff Bonwick 4314e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 4315ea8dc4b6Seschrock 4316e14bb325SJeff Bonwick /* 4317e14bb325SJeff Bonwick * Resume any suspended I/Os. 4318e14bb325SJeff Bonwick */ 431954d692b7SGeorge Wilson if (zio_resume(spa) != 0) 432054d692b7SGeorge Wilson error = EIO; 4321ea8dc4b6Seschrock 4322ea8dc4b6Seschrock spa_close(spa, FTAG); 4323ea8dc4b6Seschrock 432454d692b7SGeorge Wilson return (error); 4325ea8dc4b6Seschrock } 4326ea8dc4b6Seschrock 43274263d13fSGeorge Wilson static int 43284263d13fSGeorge Wilson zfs_ioc_pool_reopen(zfs_cmd_t *zc) 43294263d13fSGeorge Wilson { 43304263d13fSGeorge Wilson spa_t *spa; 43314263d13fSGeorge Wilson int error; 43324263d13fSGeorge Wilson 43334263d13fSGeorge Wilson error = spa_open(zc->zc_name, &spa, FTAG); 43344263d13fSGeorge Wilson if (error) 43354263d13fSGeorge Wilson return (error); 43364263d13fSGeorge Wilson 43374263d13fSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 43384263d13fSGeorge Wilson vdev_reopen(spa->spa_root_vdev); 43394263d13fSGeorge Wilson (void) spa_vdev_state_exit(spa, NULL, 0); 43404263d13fSGeorge Wilson spa_close(spa, FTAG); 43414263d13fSGeorge Wilson return (0); 43424263d13fSGeorge Wilson } 43433cb34c60Sahrens /* 43443cb34c60Sahrens * inputs: 43453cb34c60Sahrens * zc_name name of filesystem 43463cb34c60Sahrens * zc_value name of origin snapshot 43473cb34c60Sahrens * 4348681d9761SEric Taylor * outputs: 4349681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 43503cb34c60Sahrens */ 435199653d4eSeschrock static int 435299653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 435399653d4eSeschrock { 43540b69c2f0Sahrens char *cp; 43550b69c2f0Sahrens 43560b69c2f0Sahrens /* 43570b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 43580b69c2f0Sahrens * it's easier. 43590b69c2f0Sahrens */ 4360e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 43610b69c2f0Sahrens if (cp) 43620b69c2f0Sahrens *cp = '\0'; 4363e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 43640b69c2f0Sahrens zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS); 4365681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 436699653d4eSeschrock } 436799653d4eSeschrock 436814843421SMatthew Ahrens /* 436914843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 437014843421SMatthew Ahrens * 437114843421SMatthew Ahrens * inputs: 437214843421SMatthew Ahrens * zc_name name of filesystem 437314843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 437414843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 437514843421SMatthew Ahrens * zc_guid RID/UID/GID 437614843421SMatthew Ahrens * 437714843421SMatthew Ahrens * outputs: 437814843421SMatthew Ahrens * zc_cookie property value 437914843421SMatthew Ahrens */ 438014843421SMatthew Ahrens static int 438114843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 438214843421SMatthew Ahrens { 438314843421SMatthew Ahrens zfsvfs_t *zfsvfs; 438414843421SMatthew Ahrens int error; 438514843421SMatthew Ahrens 438614843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 438714843421SMatthew Ahrens return (EINVAL); 438814843421SMatthew Ahrens 43891412a1a2SMark Shellenbaum error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 439014843421SMatthew Ahrens if (error) 439114843421SMatthew Ahrens return (error); 439214843421SMatthew Ahrens 439314843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 439414843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 439514843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 439614843421SMatthew Ahrens 439714843421SMatthew Ahrens return (error); 439814843421SMatthew Ahrens } 439914843421SMatthew Ahrens 440014843421SMatthew Ahrens /* 440114843421SMatthew Ahrens * inputs: 440214843421SMatthew Ahrens * zc_name name of filesystem 440314843421SMatthew Ahrens * zc_cookie zap cursor 440414843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 440514843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 440614843421SMatthew Ahrens * 440714843421SMatthew Ahrens * outputs: 440814843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 440914843421SMatthew Ahrens * zc_cookie zap cursor 441014843421SMatthew Ahrens */ 441114843421SMatthew Ahrens static int 441214843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 441314843421SMatthew Ahrens { 441414843421SMatthew Ahrens zfsvfs_t *zfsvfs; 4415eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 441614843421SMatthew Ahrens 4417eeb85002STim Haley if (bufsize <= 0) 4418eeb85002STim Haley return (ENOMEM); 4419eeb85002STim Haley 44201412a1a2SMark Shellenbaum int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 442114843421SMatthew Ahrens if (error) 442214843421SMatthew Ahrens return (error); 442314843421SMatthew Ahrens 442414843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 442514843421SMatthew Ahrens 442614843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 442714843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 442814843421SMatthew Ahrens 442914843421SMatthew Ahrens if (error == 0) { 443014843421SMatthew Ahrens error = xcopyout(buf, 443114843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 443214843421SMatthew Ahrens zc->zc_nvlist_dst_size); 443314843421SMatthew Ahrens } 443414843421SMatthew Ahrens kmem_free(buf, bufsize); 443514843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 443614843421SMatthew Ahrens 443714843421SMatthew Ahrens return (error); 443814843421SMatthew Ahrens } 443914843421SMatthew Ahrens 444014843421SMatthew Ahrens /* 444114843421SMatthew Ahrens * inputs: 444214843421SMatthew Ahrens * zc_name name of filesystem 444314843421SMatthew Ahrens * 444414843421SMatthew Ahrens * outputs: 444514843421SMatthew Ahrens * none 444614843421SMatthew Ahrens */ 444714843421SMatthew Ahrens static int 444814843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 444914843421SMatthew Ahrens { 445014843421SMatthew Ahrens objset_t *os; 44511195e687SMark J Musante int error = 0; 445214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 445314843421SMatthew Ahrens 445414843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 4455503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 445614843421SMatthew Ahrens /* 445714843421SMatthew Ahrens * If userused is not enabled, it may be because the 445814843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 445914843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 446014843421SMatthew Ahrens */ 4461503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 4462503ad85cSMatthew Ahrens if (error == 0) 4463503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 446414843421SMatthew Ahrens } 446514843421SMatthew Ahrens if (error == 0) 446614843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 446714843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 446814843421SMatthew Ahrens } else { 4469503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 4470503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 447114843421SMatthew Ahrens if (error) 447214843421SMatthew Ahrens return (error); 447314843421SMatthew Ahrens 447414843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 4475503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 447614843421SMatthew Ahrens } 447714843421SMatthew Ahrens 447814843421SMatthew Ahrens return (error); 447914843421SMatthew Ahrens } 448014843421SMatthew Ahrens 4481ecd6cf80Smarks /* 4482ecd6cf80Smarks * We don't want to have a hard dependency 4483ecd6cf80Smarks * against some special symbols in sharefs 4484da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 4485ecd6cf80Smarks * the first file system is shared. 4486da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 4487ecd6cf80Smarks */ 4488da6c28aaSamw int (*znfsexport_fs)(void *arg); 4489ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 4490da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 4491da6c28aaSamw 4492da6c28aaSamw int zfs_nfsshare_inited; 4493da6c28aaSamw int zfs_smbshare_inited; 4494ecd6cf80Smarks 4495ecd6cf80Smarks ddi_modhandle_t nfs_mod; 4496ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 4497da6c28aaSamw ddi_modhandle_t smbsrv_mod; 4498ecd6cf80Smarks kmutex_t zfs_share_lock; 4499ecd6cf80Smarks 4500da6c28aaSamw static int 4501da6c28aaSamw zfs_init_sharefs() 4502da6c28aaSamw { 4503da6c28aaSamw int error; 4504da6c28aaSamw 4505da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 4506da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 4507da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 4508da6c28aaSamw ddi_modopen("fs/sharefs", 4509da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4510da6c28aaSamw return (ENOSYS); 4511da6c28aaSamw } 4512da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 4513da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 4514da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 4515da6c28aaSamw return (ENOSYS); 4516da6c28aaSamw } 4517da6c28aaSamw return (0); 4518da6c28aaSamw } 4519da6c28aaSamw 4520ecd6cf80Smarks static int 4521ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 4522ecd6cf80Smarks { 4523ecd6cf80Smarks int error; 4524ecd6cf80Smarks int opcode; 4525ecd6cf80Smarks 4526da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4527da6c28aaSamw case ZFS_SHARE_NFS: 4528da6c28aaSamw case ZFS_UNSHARE_NFS: 4529da6c28aaSamw if (zfs_nfsshare_inited == 0) { 4530da6c28aaSamw mutex_enter(&zfs_share_lock); 4531da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 4532da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4533da6c28aaSamw mutex_exit(&zfs_share_lock); 4534da6c28aaSamw return (ENOSYS); 4535da6c28aaSamw } 4536da6c28aaSamw if (znfsexport_fs == NULL && 4537da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 4538da6c28aaSamw ddi_modsym(nfs_mod, 4539da6c28aaSamw "nfs_export", &error)) == NULL)) { 4540da6c28aaSamw mutex_exit(&zfs_share_lock); 4541da6c28aaSamw return (ENOSYS); 4542da6c28aaSamw } 4543da6c28aaSamw error = zfs_init_sharefs(); 4544da6c28aaSamw if (error) { 4545da6c28aaSamw mutex_exit(&zfs_share_lock); 4546da6c28aaSamw return (ENOSYS); 4547da6c28aaSamw } 4548da6c28aaSamw zfs_nfsshare_inited = 1; 4549ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4550ecd6cf80Smarks } 4551da6c28aaSamw break; 4552da6c28aaSamw case ZFS_SHARE_SMB: 4553da6c28aaSamw case ZFS_UNSHARE_SMB: 4554da6c28aaSamw if (zfs_smbshare_inited == 0) { 4555da6c28aaSamw mutex_enter(&zfs_share_lock); 4556da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 4557da6c28aaSamw ddi_modopen("drv/smbsrv", 4558da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4559da6c28aaSamw mutex_exit(&zfs_share_lock); 4560da6c28aaSamw return (ENOSYS); 4561da6c28aaSamw } 4562da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 4563da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 4564faa1795aSjb "smb_server_share", &error)) == NULL)) { 4565da6c28aaSamw mutex_exit(&zfs_share_lock); 4566da6c28aaSamw return (ENOSYS); 4567da6c28aaSamw } 4568da6c28aaSamw error = zfs_init_sharefs(); 4569da6c28aaSamw if (error) { 4570da6c28aaSamw mutex_exit(&zfs_share_lock); 4571da6c28aaSamw return (ENOSYS); 4572da6c28aaSamw } 4573da6c28aaSamw zfs_smbshare_inited = 1; 4574ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4575ecd6cf80Smarks } 4576da6c28aaSamw break; 4577da6c28aaSamw default: 4578da6c28aaSamw return (EINVAL); 4579da6c28aaSamw } 4580ecd6cf80Smarks 4581da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4582da6c28aaSamw case ZFS_SHARE_NFS: 4583da6c28aaSamw case ZFS_UNSHARE_NFS: 4584da6c28aaSamw if (error = 4585da6c28aaSamw znfsexport_fs((void *) 4586da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 4587da6c28aaSamw return (error); 4588da6c28aaSamw break; 4589da6c28aaSamw case ZFS_SHARE_SMB: 4590da6c28aaSamw case ZFS_UNSHARE_SMB: 4591da6c28aaSamw if (error = zsmbexport_fs((void *) 4592da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 4593da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 4594743a77edSAlan Wright B_TRUE: B_FALSE)) { 4595da6c28aaSamw return (error); 4596ecd6cf80Smarks } 4597da6c28aaSamw break; 4598ecd6cf80Smarks } 4599ecd6cf80Smarks 4600da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 4601da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 4602ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 4603ecd6cf80Smarks 4604da6c28aaSamw /* 4605da6c28aaSamw * Add or remove share from sharetab 4606da6c28aaSamw */ 4607ecd6cf80Smarks error = zshare_fs(opcode, 4608ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 4609ecd6cf80Smarks zc->zc_share.z_sharemax); 4610ecd6cf80Smarks 4611ecd6cf80Smarks return (error); 4612ecd6cf80Smarks 4613ecd6cf80Smarks } 4614ecd6cf80Smarks 4615743a77edSAlan Wright ace_t full_access[] = { 4616743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 4617743a77edSAlan Wright }; 4618743a77edSAlan Wright 461999d5e173STim Haley /* 462099d5e173STim Haley * inputs: 462199d5e173STim Haley * zc_name name of containing filesystem 462299d5e173STim Haley * zc_obj object # beyond which we want next in-use object # 462399d5e173STim Haley * 462499d5e173STim Haley * outputs: 462599d5e173STim Haley * zc_obj next in-use object # 462699d5e173STim Haley */ 462799d5e173STim Haley static int 462899d5e173STim Haley zfs_ioc_next_obj(zfs_cmd_t *zc) 462999d5e173STim Haley { 463099d5e173STim Haley objset_t *os = NULL; 463199d5e173STim Haley int error; 463299d5e173STim Haley 463399d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &os); 463499d5e173STim Haley if (error) 463599d5e173STim Haley return (error); 463699d5e173STim Haley 463799d5e173STim Haley error = dmu_object_next(os, &zc->zc_obj, B_FALSE, 463899d5e173STim Haley os->os_dsl_dataset->ds_phys->ds_prev_snap_txg); 463999d5e173STim Haley 464099d5e173STim Haley dmu_objset_rele(os, FTAG); 464199d5e173STim Haley return (error); 464299d5e173STim Haley } 464399d5e173STim Haley 464499d5e173STim Haley /* 464599d5e173STim Haley * inputs: 464699d5e173STim Haley * zc_name name of filesystem 464799d5e173STim Haley * zc_value prefix name for snapshot 464899d5e173STim Haley * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 464999d5e173STim Haley * 465099d5e173STim Haley * outputs: 4651*4445fffbSMatthew Ahrens * zc_value short name of new snapshot 465299d5e173STim Haley */ 465399d5e173STim Haley static int 465499d5e173STim Haley zfs_ioc_tmp_snapshot(zfs_cmd_t *zc) 465599d5e173STim Haley { 465699d5e173STim Haley char *snap_name; 465799d5e173STim Haley int error; 465899d5e173STim Haley 4659*4445fffbSMatthew Ahrens snap_name = kmem_asprintf("%s@%s-%016llx", zc->zc_name, zc->zc_value, 466099d5e173STim Haley (u_longlong_t)ddi_get_lbolt64()); 466199d5e173STim Haley 4662*4445fffbSMatthew Ahrens if (strlen(snap_name) >= MAXPATHLEN) { 466399d5e173STim Haley strfree(snap_name); 466499d5e173STim Haley return (E2BIG); 466599d5e173STim Haley } 466699d5e173STim Haley 4667*4445fffbSMatthew Ahrens error = dmu_objset_snapshot_tmp(snap_name, "%temp", zc->zc_cleanup_fd); 466899d5e173STim Haley if (error != 0) { 466999d5e173STim Haley strfree(snap_name); 467099d5e173STim Haley return (error); 467199d5e173STim Haley } 467299d5e173STim Haley 4673*4445fffbSMatthew Ahrens (void) strcpy(zc->zc_value, strchr(snap_name, '@') + 1); 467499d5e173STim Haley strfree(snap_name); 467599d5e173STim Haley return (0); 467699d5e173STim Haley } 467799d5e173STim Haley 467899d5e173STim Haley /* 467999d5e173STim Haley * inputs: 468099d5e173STim Haley * zc_name name of "to" snapshot 468199d5e173STim Haley * zc_value name of "from" snapshot 468299d5e173STim Haley * zc_cookie file descriptor to write diff data on 468399d5e173STim Haley * 468499d5e173STim Haley * outputs: 468599d5e173STim Haley * dmu_diff_record_t's to the file descriptor 468699d5e173STim Haley */ 468799d5e173STim Haley static int 468899d5e173STim Haley zfs_ioc_diff(zfs_cmd_t *zc) 468999d5e173STim Haley { 469099d5e173STim Haley objset_t *fromsnap; 469199d5e173STim Haley objset_t *tosnap; 469299d5e173STim Haley file_t *fp; 469399d5e173STim Haley offset_t off; 469499d5e173STim Haley int error; 469599d5e173STim Haley 469699d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap); 469799d5e173STim Haley if (error) 469899d5e173STim Haley return (error); 469999d5e173STim Haley 470099d5e173STim Haley error = dmu_objset_hold(zc->zc_value, FTAG, &fromsnap); 470199d5e173STim Haley if (error) { 470299d5e173STim Haley dmu_objset_rele(tosnap, FTAG); 470399d5e173STim Haley return (error); 470499d5e173STim Haley } 470599d5e173STim Haley 470699d5e173STim Haley fp = getf(zc->zc_cookie); 470799d5e173STim Haley if (fp == NULL) { 470899d5e173STim Haley dmu_objset_rele(fromsnap, FTAG); 470999d5e173STim Haley dmu_objset_rele(tosnap, FTAG); 471099d5e173STim Haley return (EBADF); 471199d5e173STim Haley } 471299d5e173STim Haley 471399d5e173STim Haley off = fp->f_offset; 471499d5e173STim Haley 471599d5e173STim Haley error = dmu_diff(tosnap, fromsnap, fp->f_vnode, &off); 471699d5e173STim Haley 471799d5e173STim Haley if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 471899d5e173STim Haley fp->f_offset = off; 471999d5e173STim Haley releasef(zc->zc_cookie); 472099d5e173STim Haley 472199d5e173STim Haley dmu_objset_rele(fromsnap, FTAG); 472299d5e173STim Haley dmu_objset_rele(tosnap, FTAG); 472399d5e173STim Haley return (error); 472499d5e173STim Haley } 472599d5e173STim Haley 4726743a77edSAlan Wright /* 4727743a77edSAlan Wright * Remove all ACL files in shares dir 4728743a77edSAlan Wright */ 4729743a77edSAlan Wright static int 4730743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 4731743a77edSAlan Wright { 4732743a77edSAlan Wright zap_cursor_t zc; 4733743a77edSAlan Wright zap_attribute_t zap; 4734743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 4735743a77edSAlan Wright int error; 4736743a77edSAlan Wright 4737743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 4738743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 4739743a77edSAlan Wright zap_cursor_advance(&zc)) { 4740743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 4741743a77edSAlan Wright NULL, 0)) != 0) 4742743a77edSAlan Wright break; 4743743a77edSAlan Wright } 4744743a77edSAlan Wright zap_cursor_fini(&zc); 4745743a77edSAlan Wright return (error); 4746743a77edSAlan Wright } 4747743a77edSAlan Wright 4748743a77edSAlan Wright static int 4749743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 4750743a77edSAlan Wright { 4751743a77edSAlan Wright vnode_t *vp; 4752743a77edSAlan Wright znode_t *dzp; 4753743a77edSAlan Wright vnode_t *resourcevp = NULL; 4754743a77edSAlan Wright znode_t *sharedir; 4755743a77edSAlan Wright zfsvfs_t *zfsvfs; 4756743a77edSAlan Wright nvlist_t *nvlist; 4757743a77edSAlan Wright char *src, *target; 4758743a77edSAlan Wright vattr_t vattr; 4759743a77edSAlan Wright vsecattr_t vsec; 4760743a77edSAlan Wright int error = 0; 4761743a77edSAlan Wright 4762743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 4763743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 4764743a77edSAlan Wright return (error); 4765743a77edSAlan Wright 4766743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 4767743a77edSAlan Wright 4768743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 4769743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 4770743a77edSAlan Wright zc->zc_name) != 0)) { 4771743a77edSAlan Wright VN_RELE(vp); 4772743a77edSAlan Wright return (EINVAL); 4773743a77edSAlan Wright } 4774743a77edSAlan Wright 4775743a77edSAlan Wright dzp = VTOZ(vp); 4776743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 4777743a77edSAlan Wright ZFS_ENTER(zfsvfs); 4778743a77edSAlan Wright 47799e1320c0SMark Shellenbaum /* 47809e1320c0SMark Shellenbaum * Create share dir if its missing. 47819e1320c0SMark Shellenbaum */ 47829e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 47839e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 47849e1320c0SMark Shellenbaum dmu_tx_t *tx; 47859e1320c0SMark Shellenbaum 47869e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 47879e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 47889e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 47899e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 47909e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 47919e1320c0SMark Shellenbaum if (error) { 47929e1320c0SMark Shellenbaum dmu_tx_abort(tx); 47939e1320c0SMark Shellenbaum } else { 47949e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 47959e1320c0SMark Shellenbaum dmu_tx_commit(tx); 47969e1320c0SMark Shellenbaum } 47979e1320c0SMark Shellenbaum if (error) { 47989e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 47999e1320c0SMark Shellenbaum VN_RELE(vp); 48009e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 48019e1320c0SMark Shellenbaum return (error); 48029e1320c0SMark Shellenbaum } 48039e1320c0SMark Shellenbaum } 48049e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 48059e1320c0SMark Shellenbaum 48069e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 4807743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 48089e1320c0SMark Shellenbaum VN_RELE(vp); 4809743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4810743a77edSAlan Wright return (error); 4811743a77edSAlan Wright } 4812743a77edSAlan Wright 4813743a77edSAlan Wright switch (zc->zc_cookie) { 4814743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 4815743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 4816743a77edSAlan Wright vattr.va_type = VREG; 4817743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 4818743a77edSAlan Wright vattr.va_uid = 0; 4819743a77edSAlan Wright vattr.va_gid = 0; 4820743a77edSAlan Wright 4821743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 4822743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 4823743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 4824743a77edSAlan Wright vsec.vsa_aclcnt = 1; 4825743a77edSAlan Wright 4826743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 4827743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 4828743a77edSAlan Wright if (resourcevp) 4829743a77edSAlan Wright VN_RELE(resourcevp); 4830743a77edSAlan Wright break; 4831743a77edSAlan Wright 4832743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 4833743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 4834743a77edSAlan Wright NULL, 0); 4835743a77edSAlan Wright break; 4836743a77edSAlan Wright 4837743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 4838743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 4839478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 4840743a77edSAlan Wright VN_RELE(vp); 4841743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4842743a77edSAlan Wright return (error); 4843743a77edSAlan Wright } 4844743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 4845743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 4846743a77edSAlan Wright &target)) { 4847743a77edSAlan Wright VN_RELE(vp); 484889459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 4849743a77edSAlan Wright ZFS_EXIT(zfsvfs); 48501195e687SMark J Musante nvlist_free(nvlist); 4851743a77edSAlan Wright return (error); 4852743a77edSAlan Wright } 4853743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 4854743a77edSAlan Wright kcred, NULL, 0); 4855743a77edSAlan Wright nvlist_free(nvlist); 4856743a77edSAlan Wright break; 4857743a77edSAlan Wright 4858743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 4859743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 4860743a77edSAlan Wright break; 4861743a77edSAlan Wright 4862743a77edSAlan Wright default: 4863743a77edSAlan Wright error = EINVAL; 4864743a77edSAlan Wright break; 4865743a77edSAlan Wright } 4866743a77edSAlan Wright 4867743a77edSAlan Wright VN_RELE(vp); 4868743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 4869743a77edSAlan Wright 4870743a77edSAlan Wright ZFS_EXIT(zfsvfs); 4871743a77edSAlan Wright 4872743a77edSAlan Wright return (error); 4873743a77edSAlan Wright } 4874743a77edSAlan Wright 4875842727c2SChris Kirby /* 4876842727c2SChris Kirby * inputs: 4877c99e4bdcSChris Kirby * zc_name name of filesystem 4878c99e4bdcSChris Kirby * zc_value short name of snap 4879c99e4bdcSChris Kirby * zc_string user-supplied tag for this hold 4880c99e4bdcSChris Kirby * zc_cookie recursive flag 4881c99e4bdcSChris Kirby * zc_temphold set if hold is temporary 4882c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 4883a7f53a56SChris Kirby * zc_sendobj if non-zero, the objid for zc_name@zc_value 4884a7f53a56SChris Kirby * zc_createtxg if zc_sendobj is non-zero, snap must have zc_createtxg 4885842727c2SChris Kirby * 4886842727c2SChris Kirby * outputs: none 4887842727c2SChris Kirby */ 4888842727c2SChris Kirby static int 4889842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc) 4890842727c2SChris Kirby { 4891842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4892a7f53a56SChris Kirby spa_t *spa; 4893a7f53a56SChris Kirby dsl_pool_t *dp; 4894a7f53a56SChris Kirby dsl_dataset_t *ds; 4895a7f53a56SChris Kirby int error; 4896a7f53a56SChris Kirby minor_t minor = 0; 4897842727c2SChris Kirby 4898842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4899842727c2SChris Kirby return (EINVAL); 4900842727c2SChris Kirby 4901a7f53a56SChris Kirby if (zc->zc_sendobj == 0) { 4902a7f53a56SChris Kirby return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value, 4903a7f53a56SChris Kirby zc->zc_string, recursive, zc->zc_temphold, 4904a7f53a56SChris Kirby zc->zc_cleanup_fd)); 4905a7f53a56SChris Kirby } 4906a7f53a56SChris Kirby 4907a7f53a56SChris Kirby if (recursive) 4908a7f53a56SChris Kirby return (EINVAL); 4909a7f53a56SChris Kirby 4910a7f53a56SChris Kirby error = spa_open(zc->zc_name, &spa, FTAG); 4911a7f53a56SChris Kirby if (error) 4912a7f53a56SChris Kirby return (error); 4913a7f53a56SChris Kirby 4914a7f53a56SChris Kirby dp = spa_get_dsl(spa); 4915a7f53a56SChris Kirby rw_enter(&dp->dp_config_rwlock, RW_READER); 4916a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 4917a7f53a56SChris Kirby rw_exit(&dp->dp_config_rwlock); 4918a7f53a56SChris Kirby spa_close(spa, FTAG); 4919a7f53a56SChris Kirby if (error) 4920a7f53a56SChris Kirby return (error); 4921a7f53a56SChris Kirby 4922a7f53a56SChris Kirby /* 4923a7f53a56SChris Kirby * Until we have a hold on this snapshot, it's possible that 4924a7f53a56SChris Kirby * zc_sendobj could've been destroyed and reused as part 4925a7f53a56SChris Kirby * of a later txg. Make sure we're looking at the right object. 4926a7f53a56SChris Kirby */ 4927a7f53a56SChris Kirby if (zc->zc_createtxg != ds->ds_phys->ds_creation_txg) { 4928a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4929a7f53a56SChris Kirby return (ENOENT); 4930a7f53a56SChris Kirby } 4931a7f53a56SChris Kirby 4932a7f53a56SChris Kirby if (zc->zc_cleanup_fd != -1 && zc->zc_temphold) { 4933a7f53a56SChris Kirby error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor); 4934a7f53a56SChris Kirby if (error) { 4935a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4936a7f53a56SChris Kirby return (error); 4937a7f53a56SChris Kirby } 4938a7f53a56SChris Kirby } 4939a7f53a56SChris Kirby 4940a7f53a56SChris Kirby error = dsl_dataset_user_hold_for_send(ds, zc->zc_string, 4941a7f53a56SChris Kirby zc->zc_temphold); 4942a7f53a56SChris Kirby if (minor != 0) { 4943a7f53a56SChris Kirby if (error == 0) { 4944a7f53a56SChris Kirby dsl_register_onexit_hold_cleanup(ds, zc->zc_string, 4945a7f53a56SChris Kirby minor); 4946a7f53a56SChris Kirby } 4947a7f53a56SChris Kirby zfs_onexit_fd_rele(zc->zc_cleanup_fd); 4948a7f53a56SChris Kirby } 4949a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 4950a7f53a56SChris Kirby 4951a7f53a56SChris Kirby return (error); 4952842727c2SChris Kirby } 4953842727c2SChris Kirby 4954842727c2SChris Kirby /* 4955842727c2SChris Kirby * inputs: 4956c99e4bdcSChris Kirby * zc_name name of dataset from which we're releasing a user hold 4957842727c2SChris Kirby * zc_value short name of snap 4958c99e4bdcSChris Kirby * zc_string user-supplied tag for this hold 4959842727c2SChris Kirby * zc_cookie recursive flag 4960842727c2SChris Kirby * 4961c99e4bdcSChris Kirby * outputs: none 4962842727c2SChris Kirby */ 4963842727c2SChris Kirby static int 4964842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc) 4965842727c2SChris Kirby { 4966842727c2SChris Kirby boolean_t recursive = zc->zc_cookie; 4967842727c2SChris Kirby 4968842727c2SChris Kirby if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0) 4969842727c2SChris Kirby return (EINVAL); 4970842727c2SChris Kirby 4971842727c2SChris Kirby return (dsl_dataset_user_release(zc->zc_name, zc->zc_value, 4972842727c2SChris Kirby zc->zc_string, recursive)); 4973842727c2SChris Kirby } 4974842727c2SChris Kirby 4975842727c2SChris Kirby /* 4976842727c2SChris Kirby * inputs: 4977842727c2SChris Kirby * zc_name name of filesystem 4978842727c2SChris Kirby * 4979842727c2SChris Kirby * outputs: 4980842727c2SChris Kirby * zc_nvlist_src{_size} nvlist of snapshot holds 4981842727c2SChris Kirby */ 4982842727c2SChris Kirby static int 4983842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc) 4984842727c2SChris Kirby { 4985842727c2SChris Kirby nvlist_t *nvp; 4986842727c2SChris Kirby int error; 4987842727c2SChris Kirby 4988842727c2SChris Kirby if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) { 4989842727c2SChris Kirby error = put_nvlist(zc, nvp); 4990842727c2SChris Kirby nvlist_free(nvp); 4991842727c2SChris Kirby } 4992842727c2SChris Kirby 4993842727c2SChris Kirby return (error); 4994842727c2SChris Kirby } 4995842727c2SChris Kirby 499619b94df9SMatthew Ahrens /* 499719b94df9SMatthew Ahrens * inputs: 499819b94df9SMatthew Ahrens * zc_name name of new filesystem or snapshot 499919b94df9SMatthew Ahrens * zc_value full name of old snapshot 500019b94df9SMatthew Ahrens * 500119b94df9SMatthew Ahrens * outputs: 500219b94df9SMatthew Ahrens * zc_cookie space in bytes 500319b94df9SMatthew Ahrens * zc_objset_type compressed space in bytes 500419b94df9SMatthew Ahrens * zc_perm_action uncompressed space in bytes 500519b94df9SMatthew Ahrens */ 500619b94df9SMatthew Ahrens static int 500719b94df9SMatthew Ahrens zfs_ioc_space_written(zfs_cmd_t *zc) 500819b94df9SMatthew Ahrens { 500919b94df9SMatthew Ahrens int error; 501019b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 501119b94df9SMatthew Ahrens 501219b94df9SMatthew Ahrens error = dsl_dataset_hold(zc->zc_name, FTAG, &new); 501319b94df9SMatthew Ahrens if (error != 0) 501419b94df9SMatthew Ahrens return (error); 501519b94df9SMatthew Ahrens error = dsl_dataset_hold(zc->zc_value, FTAG, &old); 501619b94df9SMatthew Ahrens if (error != 0) { 501719b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 501819b94df9SMatthew Ahrens return (error); 501919b94df9SMatthew Ahrens } 502019b94df9SMatthew Ahrens 502119b94df9SMatthew Ahrens error = dsl_dataset_space_written(old, new, &zc->zc_cookie, 502219b94df9SMatthew Ahrens &zc->zc_objset_type, &zc->zc_perm_action); 502319b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 502419b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 502519b94df9SMatthew Ahrens return (error); 502619b94df9SMatthew Ahrens } 502719b94df9SMatthew Ahrens /* 5028*4445fffbSMatthew Ahrens * innvl: { 5029*4445fffbSMatthew Ahrens * "firstsnap" -> snapshot name 5030*4445fffbSMatthew Ahrens * } 503119b94df9SMatthew Ahrens * 5032*4445fffbSMatthew Ahrens * outnvl: { 5033*4445fffbSMatthew Ahrens * "used" -> space in bytes 5034*4445fffbSMatthew Ahrens * "compressed" -> compressed space in bytes 5035*4445fffbSMatthew Ahrens * "uncompressed" -> uncompressed space in bytes 5036*4445fffbSMatthew Ahrens * } 503719b94df9SMatthew Ahrens */ 503819b94df9SMatthew Ahrens static int 5039*4445fffbSMatthew Ahrens zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) 504019b94df9SMatthew Ahrens { 504119b94df9SMatthew Ahrens int error; 504219b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 5043*4445fffbSMatthew Ahrens char *firstsnap; 5044*4445fffbSMatthew Ahrens uint64_t used, comp, uncomp; 504519b94df9SMatthew Ahrens 5046*4445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) 5047*4445fffbSMatthew Ahrens return (EINVAL); 5048*4445fffbSMatthew Ahrens 5049*4445fffbSMatthew Ahrens error = dsl_dataset_hold(lastsnap, FTAG, &new); 505019b94df9SMatthew Ahrens if (error != 0) 505119b94df9SMatthew Ahrens return (error); 5052*4445fffbSMatthew Ahrens error = dsl_dataset_hold(firstsnap, FTAG, &old); 505319b94df9SMatthew Ahrens if (error != 0) { 505419b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 505519b94df9SMatthew Ahrens return (error); 505619b94df9SMatthew Ahrens } 505719b94df9SMatthew Ahrens 5058*4445fffbSMatthew Ahrens error = dsl_dataset_space_wouldfree(old, new, &used, &comp, &uncomp); 505919b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 506019b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 5061*4445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "used", used); 5062*4445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "compressed", comp); 5063*4445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "uncompressed", uncomp); 506419b94df9SMatthew Ahrens return (error); 506519b94df9SMatthew Ahrens } 506619b94df9SMatthew Ahrens 5067ecd6cf80Smarks /* 5068*4445fffbSMatthew Ahrens * innvl: { 5069*4445fffbSMatthew Ahrens * "fd" -> file descriptor to write stream to (int32) 5070*4445fffbSMatthew Ahrens * (optional) "fromsnap" -> full snap name to send an incremental from 5071*4445fffbSMatthew Ahrens * } 5072*4445fffbSMatthew Ahrens * 5073*4445fffbSMatthew Ahrens * outnvl is unused 5074ecd6cf80Smarks */ 5075*4445fffbSMatthew Ahrens /* ARGSUSED */ 5076*4445fffbSMatthew Ahrens static int 5077*4445fffbSMatthew Ahrens zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 5078*4445fffbSMatthew Ahrens { 5079*4445fffbSMatthew Ahrens objset_t *fromsnap = NULL; 5080*4445fffbSMatthew Ahrens objset_t *tosnap; 5081*4445fffbSMatthew Ahrens int error; 5082*4445fffbSMatthew Ahrens offset_t off; 5083*4445fffbSMatthew Ahrens char *fromname; 5084*4445fffbSMatthew Ahrens int fd; 5085*4445fffbSMatthew Ahrens 5086*4445fffbSMatthew Ahrens error = nvlist_lookup_int32(innvl, "fd", &fd); 5087*4445fffbSMatthew Ahrens if (error != 0) 5088*4445fffbSMatthew Ahrens return (EINVAL); 5089*4445fffbSMatthew Ahrens 5090*4445fffbSMatthew Ahrens error = dmu_objset_hold(snapname, FTAG, &tosnap); 5091*4445fffbSMatthew Ahrens if (error) 5092*4445fffbSMatthew Ahrens return (error); 5093*4445fffbSMatthew Ahrens 5094*4445fffbSMatthew Ahrens error = nvlist_lookup_string(innvl, "fromsnap", &fromname); 5095*4445fffbSMatthew Ahrens if (error == 0) { 5096*4445fffbSMatthew Ahrens error = dmu_objset_hold(fromname, FTAG, &fromsnap); 5097*4445fffbSMatthew Ahrens if (error) { 5098*4445fffbSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 5099*4445fffbSMatthew Ahrens return (error); 5100*4445fffbSMatthew Ahrens } 5101*4445fffbSMatthew Ahrens } 5102*4445fffbSMatthew Ahrens 5103*4445fffbSMatthew Ahrens file_t *fp = getf(fd); 5104*4445fffbSMatthew Ahrens if (fp == NULL) { 5105*4445fffbSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 5106*4445fffbSMatthew Ahrens if (fromsnap != NULL) 5107*4445fffbSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 5108*4445fffbSMatthew Ahrens return (EBADF); 5109*4445fffbSMatthew Ahrens } 5110*4445fffbSMatthew Ahrens 5111*4445fffbSMatthew Ahrens off = fp->f_offset; 5112*4445fffbSMatthew Ahrens error = dmu_send(tosnap, fromsnap, fd, fp->f_vnode, &off); 5113*4445fffbSMatthew Ahrens 5114*4445fffbSMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 5115*4445fffbSMatthew Ahrens fp->f_offset = off; 5116*4445fffbSMatthew Ahrens releasef(fd); 5117*4445fffbSMatthew Ahrens if (fromsnap != NULL) 5118*4445fffbSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 5119*4445fffbSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 5120*4445fffbSMatthew Ahrens return (error); 5121*4445fffbSMatthew Ahrens } 5122*4445fffbSMatthew Ahrens 5123*4445fffbSMatthew Ahrens /* 5124*4445fffbSMatthew Ahrens * Determine approximately how large a zfs send stream will be -- the number 5125*4445fffbSMatthew Ahrens * of bytes that will be written to the fd supplied to zfs_ioc_send_new(). 5126*4445fffbSMatthew Ahrens * 5127*4445fffbSMatthew Ahrens * innvl: { 5128*4445fffbSMatthew Ahrens * (optional) "fromsnap" -> full snap name to send an incremental from 5129*4445fffbSMatthew Ahrens * } 5130*4445fffbSMatthew Ahrens * 5131*4445fffbSMatthew Ahrens * outnvl: { 5132*4445fffbSMatthew Ahrens * "space" -> bytes of space (uint64) 5133*4445fffbSMatthew Ahrens * } 5134*4445fffbSMatthew Ahrens */ 5135*4445fffbSMatthew Ahrens static int 5136*4445fffbSMatthew Ahrens zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 5137*4445fffbSMatthew Ahrens { 5138*4445fffbSMatthew Ahrens objset_t *fromsnap = NULL; 5139*4445fffbSMatthew Ahrens objset_t *tosnap; 5140*4445fffbSMatthew Ahrens int error; 5141*4445fffbSMatthew Ahrens char *fromname; 5142*4445fffbSMatthew Ahrens uint64_t space; 5143*4445fffbSMatthew Ahrens 5144*4445fffbSMatthew Ahrens error = dmu_objset_hold(snapname, FTAG, &tosnap); 5145*4445fffbSMatthew Ahrens if (error) 5146*4445fffbSMatthew Ahrens return (error); 5147*4445fffbSMatthew Ahrens 5148*4445fffbSMatthew Ahrens error = nvlist_lookup_string(innvl, "fromsnap", &fromname); 5149*4445fffbSMatthew Ahrens if (error == 0) { 5150*4445fffbSMatthew Ahrens error = dmu_objset_hold(fromname, FTAG, &fromsnap); 5151*4445fffbSMatthew Ahrens if (error) { 5152*4445fffbSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 5153*4445fffbSMatthew Ahrens return (error); 5154*4445fffbSMatthew Ahrens } 5155*4445fffbSMatthew Ahrens } 5156*4445fffbSMatthew Ahrens 5157*4445fffbSMatthew Ahrens error = dmu_send_estimate(tosnap, fromsnap, &space); 5158*4445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "space", space); 5159*4445fffbSMatthew Ahrens 5160*4445fffbSMatthew Ahrens if (fromsnap != NULL) 5161*4445fffbSMatthew Ahrens dmu_objset_rele(fromsnap, FTAG); 5162*4445fffbSMatthew Ahrens dmu_objset_rele(tosnap, FTAG); 5163*4445fffbSMatthew Ahrens return (error); 5164*4445fffbSMatthew Ahrens } 5165*4445fffbSMatthew Ahrens 5166*4445fffbSMatthew Ahrens 5167*4445fffbSMatthew Ahrens static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; 5168*4445fffbSMatthew Ahrens 5169*4445fffbSMatthew Ahrens static void 5170*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 5171*4445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 5172*4445fffbSMatthew Ahrens boolean_t log_history, zfs_ioc_poolcheck_t pool_check) 5173*4445fffbSMatthew Ahrens { 5174*4445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 5175*4445fffbSMatthew Ahrens 5176*4445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 5177*4445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 5178*4445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 5179*4445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 5180*4445fffbSMatthew Ahrens 5181*4445fffbSMatthew Ahrens vec->zvec_legacy_func = func; 5182*4445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 5183*4445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 5184*4445fffbSMatthew Ahrens vec->zvec_allow_log = log_history; 5185*4445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 5186*4445fffbSMatthew Ahrens } 5187*4445fffbSMatthew Ahrens 5188*4445fffbSMatthew Ahrens /* 5189*4445fffbSMatthew Ahrens * See the block comment at the beginning of this file for details on 5190*4445fffbSMatthew Ahrens * each argument to this function. 5191*4445fffbSMatthew Ahrens */ 5192*4445fffbSMatthew Ahrens static void 5193*4445fffbSMatthew Ahrens zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, 5194*4445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 5195*4445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, 5196*4445fffbSMatthew Ahrens boolean_t allow_log) 5197*4445fffbSMatthew Ahrens { 5198*4445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 5199*4445fffbSMatthew Ahrens 5200*4445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 5201*4445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 5202*4445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 5203*4445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 5204*4445fffbSMatthew Ahrens 5205*4445fffbSMatthew Ahrens /* if we are logging, the name must be valid */ 5206*4445fffbSMatthew Ahrens ASSERT(!allow_log || namecheck != NO_NAME); 5207*4445fffbSMatthew Ahrens 5208*4445fffbSMatthew Ahrens vec->zvec_name = name; 5209*4445fffbSMatthew Ahrens vec->zvec_func = func; 5210*4445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 5211*4445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 5212*4445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 5213*4445fffbSMatthew Ahrens vec->zvec_smush_outnvlist = smush_outnvlist; 5214*4445fffbSMatthew Ahrens vec->zvec_allow_log = allow_log; 5215*4445fffbSMatthew Ahrens } 5216*4445fffbSMatthew Ahrens 5217*4445fffbSMatthew Ahrens static void 5218*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 5219*4445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, boolean_t log_history, 5220*4445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check) 5221*4445fffbSMatthew Ahrens { 5222*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 5223*4445fffbSMatthew Ahrens POOL_NAME, log_history, pool_check); 5224*4445fffbSMatthew Ahrens } 5225*4445fffbSMatthew Ahrens 5226*4445fffbSMatthew Ahrens static void 5227*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 5228*4445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check) 5229*4445fffbSMatthew Ahrens { 5230*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 5231*4445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, pool_check); 5232*4445fffbSMatthew Ahrens } 5233*4445fffbSMatthew Ahrens 5234*4445fffbSMatthew Ahrens static void 5235*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 5236*4445fffbSMatthew Ahrens { 5237*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config, 5238*4445fffbSMatthew Ahrens POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 5239*4445fffbSMatthew Ahrens } 5240*4445fffbSMatthew Ahrens 5241*4445fffbSMatthew Ahrens static void 5242*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 5243*4445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy) 5244*4445fffbSMatthew Ahrens { 5245*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 5246*4445fffbSMatthew Ahrens NO_NAME, B_FALSE, POOL_CHECK_NONE); 5247*4445fffbSMatthew Ahrens } 5248*4445fffbSMatthew Ahrens 5249*4445fffbSMatthew Ahrens static void 5250*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc, 5251*4445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) 5252*4445fffbSMatthew Ahrens { 5253*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 5254*4445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED); 5255*4445fffbSMatthew Ahrens } 5256*4445fffbSMatthew Ahrens 5257*4445fffbSMatthew Ahrens static void 5258*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 5259*4445fffbSMatthew Ahrens { 5260*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ioc, func, 5261*4445fffbSMatthew Ahrens zfs_secpolicy_read); 5262*4445fffbSMatthew Ahrens } 5263*4445fffbSMatthew Ahrens 5264*4445fffbSMatthew Ahrens static void 5265*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 5266*4445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy) 5267*4445fffbSMatthew Ahrens { 5268*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 5269*4445fffbSMatthew Ahrens DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 5270*4445fffbSMatthew Ahrens } 5271*4445fffbSMatthew Ahrens 5272*4445fffbSMatthew Ahrens static void 5273*4445fffbSMatthew Ahrens zfs_ioctl_init(void) 5274*4445fffbSMatthew Ahrens { 5275*4445fffbSMatthew Ahrens zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, 5276*4445fffbSMatthew Ahrens zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, 5277*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 5278*4445fffbSMatthew Ahrens 5279*4445fffbSMatthew Ahrens zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, 5280*4445fffbSMatthew Ahrens zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, 5281*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); 5282*4445fffbSMatthew Ahrens 5283*4445fffbSMatthew Ahrens zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, 5284*4445fffbSMatthew Ahrens zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, 5285*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 5286*4445fffbSMatthew Ahrens 5287*4445fffbSMatthew Ahrens zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, 5288*4445fffbSMatthew Ahrens zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, 5289*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 5290*4445fffbSMatthew Ahrens 5291*4445fffbSMatthew Ahrens zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, 5292*4445fffbSMatthew Ahrens zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, 5293*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 5294*4445fffbSMatthew Ahrens 5295*4445fffbSMatthew Ahrens zfs_ioctl_register("create", ZFS_IOC_CREATE, 5296*4445fffbSMatthew Ahrens zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, 5297*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 5298*4445fffbSMatthew Ahrens 5299*4445fffbSMatthew Ahrens zfs_ioctl_register("clone", ZFS_IOC_CLONE, 5300*4445fffbSMatthew Ahrens zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, 5301*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 5302*4445fffbSMatthew Ahrens 5303*4445fffbSMatthew Ahrens zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, 5304*4445fffbSMatthew Ahrens zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, 5305*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 5306*4445fffbSMatthew Ahrens 5307*4445fffbSMatthew Ahrens /* IOCTLS that use the legacy function signature */ 5308*4445fffbSMatthew Ahrens 5309*4445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, 5310*4445fffbSMatthew Ahrens zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); 5311*4445fffbSMatthew Ahrens 5312*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, 5313*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 5314*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN, 5315*4445fffbSMatthew Ahrens zfs_ioc_pool_scan); 5316*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE, 5317*4445fffbSMatthew Ahrens zfs_ioc_pool_upgrade); 5318*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD, 5319*4445fffbSMatthew Ahrens zfs_ioc_vdev_add); 5320*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE, 5321*4445fffbSMatthew Ahrens zfs_ioc_vdev_remove); 5322*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SET_STATE, 5323*4445fffbSMatthew Ahrens zfs_ioc_vdev_set_state); 5324*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH, 5325*4445fffbSMatthew Ahrens zfs_ioc_vdev_attach); 5326*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH, 5327*4445fffbSMatthew Ahrens zfs_ioc_vdev_detach); 5328*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH, 5329*4445fffbSMatthew Ahrens zfs_ioc_vdev_setpath); 5330*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU, 5331*4445fffbSMatthew Ahrens zfs_ioc_vdev_setfru); 5332*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS, 5333*4445fffbSMatthew Ahrens zfs_ioc_pool_set_props); 5334*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SPLIT, 5335*4445fffbSMatthew Ahrens zfs_ioc_vdev_split); 5336*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_REGUID, 5337*4445fffbSMatthew Ahrens zfs_ioc_pool_reguid); 5338*4445fffbSMatthew Ahrens 5339*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_CONFIGS, 5340*4445fffbSMatthew Ahrens zfs_ioc_pool_configs, zfs_secpolicy_none); 5341*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_TRYIMPORT, 5342*4445fffbSMatthew Ahrens zfs_ioc_pool_tryimport, zfs_secpolicy_config); 5343*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_FAULT, 5344*4445fffbSMatthew Ahrens zfs_ioc_inject_fault, zfs_secpolicy_inject); 5345*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_CLEAR_FAULT, 5346*4445fffbSMatthew Ahrens zfs_ioc_clear_fault, zfs_secpolicy_inject); 5347*4445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_LIST_NEXT, 5348*4445fffbSMatthew Ahrens zfs_ioc_inject_list_next, zfs_secpolicy_inject); 5349*4445fffbSMatthew Ahrens 5350*4445fffbSMatthew Ahrens /* 5351*4445fffbSMatthew Ahrens * pool destroy, and export don't log the history as part of 5352*4445fffbSMatthew Ahrens * zfsdev_ioctl, but rather zfs_ioc_pool_export 5353*4445fffbSMatthew Ahrens * does the logging of those commands. 5354*4445fffbSMatthew Ahrens */ 5355*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_DESTROY, zfs_ioc_pool_destroy, 5356*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 5357*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_EXPORT, zfs_ioc_pool_export, 5358*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 5359*4445fffbSMatthew Ahrens 5360*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_STATS, zfs_ioc_pool_stats, 5361*4445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 5362*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_PROPS, zfs_ioc_pool_get_props, 5363*4445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 5364*4445fffbSMatthew Ahrens 5365*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_ERROR_LOG, zfs_ioc_error_log, 5366*4445fffbSMatthew Ahrens zfs_secpolicy_inject, B_FALSE, POOL_CHECK_SUSPENDED); 5367*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_DSOBJ_TO_DSNAME, 5368*4445fffbSMatthew Ahrens zfs_ioc_dsobj_to_dsname, 5369*4445fffbSMatthew Ahrens zfs_secpolicy_diff, B_FALSE, POOL_CHECK_SUSPENDED); 5370*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_HISTORY, 5371*4445fffbSMatthew Ahrens zfs_ioc_pool_get_history, 5372*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); 5373*4445fffbSMatthew Ahrens 5374*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, 5375*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 5376*4445fffbSMatthew Ahrens 5377*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, 5378*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); 5379*4445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, 5380*4445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); 5381*4445fffbSMatthew Ahrens 5382*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, 5383*4445fffbSMatthew Ahrens zfs_ioc_space_written); 5384*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_GET_HOLDS, 5385*4445fffbSMatthew Ahrens zfs_ioc_get_holds); 5386*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, 5387*4445fffbSMatthew Ahrens zfs_ioc_objset_recvd_props); 5388*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, 5389*4445fffbSMatthew Ahrens zfs_ioc_next_obj); 5390*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, 5391*4445fffbSMatthew Ahrens zfs_ioc_get_fsacl); 5392*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_STATS, 5393*4445fffbSMatthew Ahrens zfs_ioc_objset_stats); 5394*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_ZPLPROPS, 5395*4445fffbSMatthew Ahrens zfs_ioc_objset_zplprops); 5396*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_DATASET_LIST_NEXT, 5397*4445fffbSMatthew Ahrens zfs_ioc_dataset_list_next); 5398*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SNAPSHOT_LIST_NEXT, 5399*4445fffbSMatthew Ahrens zfs_ioc_snapshot_list_next); 5400*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SEND_PROGRESS, 5401*4445fffbSMatthew Ahrens zfs_ioc_send_progress); 5402*4445fffbSMatthew Ahrens 5403*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_DIFF, 5404*4445fffbSMatthew Ahrens zfs_ioc_diff, zfs_secpolicy_diff); 5405*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_STATS, 5406*4445fffbSMatthew Ahrens zfs_ioc_obj_to_stats, zfs_secpolicy_diff); 5407*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_PATH, 5408*4445fffbSMatthew Ahrens zfs_ioc_obj_to_path, zfs_secpolicy_diff); 5409*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_ONE, 5410*4445fffbSMatthew Ahrens zfs_ioc_userspace_one, zfs_secpolicy_userspace_one); 5411*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_MANY, 5412*4445fffbSMatthew Ahrens zfs_ioc_userspace_many, zfs_secpolicy_userspace_many); 5413*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND, 5414*4445fffbSMatthew Ahrens zfs_ioc_send, zfs_secpolicy_send); 5415*4445fffbSMatthew Ahrens 5416*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, 5417*4445fffbSMatthew Ahrens zfs_secpolicy_none); 5418*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, 5419*4445fffbSMatthew Ahrens zfs_secpolicy_destroy); 5420*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_ROLLBACK, zfs_ioc_rollback, 5421*4445fffbSMatthew Ahrens zfs_secpolicy_rollback); 5422*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename, 5423*4445fffbSMatthew Ahrens zfs_secpolicy_rename); 5424*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv, 5425*4445fffbSMatthew Ahrens zfs_secpolicy_recv); 5426*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote, 5427*4445fffbSMatthew Ahrens zfs_secpolicy_promote); 5428*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_HOLD, zfs_ioc_hold, 5429*4445fffbSMatthew Ahrens zfs_secpolicy_hold); 5430*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RELEASE, zfs_ioc_release, 5431*4445fffbSMatthew Ahrens zfs_secpolicy_release); 5432*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP, 5433*4445fffbSMatthew Ahrens zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop); 5434*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl, 5435*4445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl); 5436*4445fffbSMatthew Ahrens 5437*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share, 5438*4445fffbSMatthew Ahrens zfs_secpolicy_share, POOL_CHECK_NONE); 5439*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl, 5440*4445fffbSMatthew Ahrens zfs_secpolicy_smb_acl, POOL_CHECK_NONE); 5441*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERSPACE_UPGRADE, 5442*4445fffbSMatthew Ahrens zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 5443*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 5444*4445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT, 5445*4445fffbSMatthew Ahrens zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, 5446*4445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 5447*4445fffbSMatthew Ahrens } 5448fa9e4066Sahrens 544954d692b7SGeorge Wilson int 5450f9af39baSGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type, 5451f9af39baSGeorge Wilson zfs_ioc_poolcheck_t check) 545254d692b7SGeorge Wilson { 545354d692b7SGeorge Wilson spa_t *spa; 545454d692b7SGeorge Wilson int error; 545554d692b7SGeorge Wilson 545654d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 545754d692b7SGeorge Wilson 5458f9af39baSGeorge Wilson if (check & POOL_CHECK_NONE) 5459f9af39baSGeorge Wilson return (0); 5460f9af39baSGeorge Wilson 546114843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 546254d692b7SGeorge Wilson if (error == 0) { 5463f9af39baSGeorge Wilson if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa)) 546454d692b7SGeorge Wilson error = EAGAIN; 5465f9af39baSGeorge Wilson else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa)) 5466f9af39baSGeorge Wilson error = EROFS; 546754d692b7SGeorge Wilson spa_close(spa, FTAG); 546854d692b7SGeorge Wilson } 546954d692b7SGeorge Wilson return (error); 547054d692b7SGeorge Wilson } 547154d692b7SGeorge Wilson 5472c99e4bdcSChris Kirby /* 5473c99e4bdcSChris Kirby * Find a free minor number. 5474c99e4bdcSChris Kirby */ 5475c99e4bdcSChris Kirby minor_t 5476c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 5477c99e4bdcSChris Kirby { 5478c99e4bdcSChris Kirby static minor_t last_minor; 5479c99e4bdcSChris Kirby minor_t m; 5480c99e4bdcSChris Kirby 5481c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5482c99e4bdcSChris Kirby 5483c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 5484c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 5485c99e4bdcSChris Kirby m = 1; 5486c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 5487c99e4bdcSChris Kirby last_minor = m; 5488c99e4bdcSChris Kirby return (m); 5489c99e4bdcSChris Kirby } 5490c99e4bdcSChris Kirby } 5491c99e4bdcSChris Kirby 5492c99e4bdcSChris Kirby return (0); 5493c99e4bdcSChris Kirby } 5494c99e4bdcSChris Kirby 5495c99e4bdcSChris Kirby static int 5496c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 5497c99e4bdcSChris Kirby { 5498c99e4bdcSChris Kirby minor_t minor; 5499c99e4bdcSChris Kirby zfs_soft_state_t *zs; 5500c99e4bdcSChris Kirby 5501c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5502c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 5503c99e4bdcSChris Kirby 5504c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 5505c99e4bdcSChris Kirby if (minor == 0) 5506c99e4bdcSChris Kirby return (ENXIO); 5507c99e4bdcSChris Kirby 5508c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 5509c99e4bdcSChris Kirby return (EAGAIN); 5510c99e4bdcSChris Kirby 5511c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 5512c99e4bdcSChris Kirby 5513c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 5514c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 5515c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 5516c99e4bdcSChris Kirby 5517c99e4bdcSChris Kirby return (0); 5518c99e4bdcSChris Kirby } 5519c99e4bdcSChris Kirby 5520c99e4bdcSChris Kirby static void 5521c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 5522c99e4bdcSChris Kirby { 5523c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5524c99e4bdcSChris Kirby 5525c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 5526c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 5527c99e4bdcSChris Kirby } 5528c99e4bdcSChris Kirby 5529c99e4bdcSChris Kirby void * 5530c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 5531c99e4bdcSChris Kirby { 5532c99e4bdcSChris Kirby zfs_soft_state_t *zp; 5533c99e4bdcSChris Kirby 5534c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 5535c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 5536c99e4bdcSChris Kirby return (NULL); 5537c99e4bdcSChris Kirby 5538c99e4bdcSChris Kirby return (zp->zss_data); 5539c99e4bdcSChris Kirby } 5540c99e4bdcSChris Kirby 5541c99e4bdcSChris Kirby static int 5542c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 5543c99e4bdcSChris Kirby { 5544c99e4bdcSChris Kirby int error = 0; 5545c99e4bdcSChris Kirby 5546c99e4bdcSChris Kirby if (getminor(*devp) != 0) 5547c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 5548c99e4bdcSChris Kirby 5549c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 5550c99e4bdcSChris Kirby if (flag & FEXCL) { 5551c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 5552c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 5553c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5554c99e4bdcSChris Kirby } 5555c99e4bdcSChris Kirby 5556c99e4bdcSChris Kirby return (error); 5557c99e4bdcSChris Kirby } 5558c99e4bdcSChris Kirby 5559c99e4bdcSChris Kirby static int 5560c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 5561c99e4bdcSChris Kirby { 5562c99e4bdcSChris Kirby zfs_onexit_t *zo; 5563c99e4bdcSChris Kirby minor_t minor = getminor(dev); 5564c99e4bdcSChris Kirby 5565c99e4bdcSChris Kirby if (minor == 0) 5566c99e4bdcSChris Kirby return (0); 5567c99e4bdcSChris Kirby 5568c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 5569c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 5570c99e4bdcSChris Kirby if (zo == NULL) { 5571c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5572c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 5573c99e4bdcSChris Kirby } 5574c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 5575c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5576c99e4bdcSChris Kirby 5577c99e4bdcSChris Kirby return (0); 5578c99e4bdcSChris Kirby } 5579c99e4bdcSChris Kirby 5580fa9e4066Sahrens static int 5581fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 5582fa9e4066Sahrens { 5583fa9e4066Sahrens zfs_cmd_t *zc; 5584*4445fffbSMatthew Ahrens uint_t vecnum; 5585*4445fffbSMatthew Ahrens int error, rc, len; 5586c99e4bdcSChris Kirby minor_t minor = getminor(dev); 5587*4445fffbSMatthew Ahrens const zfs_ioc_vec_t *vec; 5588*4445fffbSMatthew Ahrens char *saved_poolname = NULL; 5589*4445fffbSMatthew Ahrens nvlist_t *innvl = NULL; 5590fa9e4066Sahrens 5591c99e4bdcSChris Kirby if (minor != 0 && 5592c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 5593fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 5594fa9e4066Sahrens 5595*4445fffbSMatthew Ahrens vecnum = cmd - ZFS_IOC_FIRST; 559691ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 5597fa9e4066Sahrens 5598*4445fffbSMatthew Ahrens if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 5599fa9e4066Sahrens return (EINVAL); 5600*4445fffbSMatthew Ahrens vec = &zfs_ioc_vec[vecnum]; 5601fa9e4066Sahrens 5602fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 5603fa9e4066Sahrens 5604478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 5605*4445fffbSMatthew Ahrens if (error != 0) { 56066e27f868SSam Falkner error = EFAULT; 5607*4445fffbSMatthew Ahrens goto out; 5608*4445fffbSMatthew Ahrens } 5609fa9e4066Sahrens 5610*4445fffbSMatthew Ahrens zc->zc_iflags = flag & FKIOCTL; 5611*4445fffbSMatthew Ahrens if (zc->zc_nvlist_src_size != 0) { 5612*4445fffbSMatthew Ahrens error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 5613*4445fffbSMatthew Ahrens zc->zc_iflags, &innvl); 5614*4445fffbSMatthew Ahrens if (error != 0) 5615*4445fffbSMatthew Ahrens goto out; 5616*4445fffbSMatthew Ahrens } 5617fa9e4066Sahrens 5618fa9e4066Sahrens /* 5619fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 5620fa9e4066Sahrens * the lower layers. 5621fa9e4066Sahrens */ 5622*4445fffbSMatthew Ahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 5623*4445fffbSMatthew Ahrens switch (vec->zvec_namecheck) { 5624*4445fffbSMatthew Ahrens case POOL_NAME: 5625*4445fffbSMatthew Ahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 5626*4445fffbSMatthew Ahrens error = EINVAL; 5627*4445fffbSMatthew Ahrens else 5628f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 5629*4445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 5630*4445fffbSMatthew Ahrens break; 5631fa9e4066Sahrens 5632*4445fffbSMatthew Ahrens case DATASET_NAME: 5633*4445fffbSMatthew Ahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 5634*4445fffbSMatthew Ahrens error = EINVAL; 5635*4445fffbSMatthew Ahrens else 5636f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 5637*4445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 5638*4445fffbSMatthew Ahrens break; 56395ad82045Snd 5640*4445fffbSMatthew Ahrens case NO_NAME: 5641*4445fffbSMatthew Ahrens break; 5642fa9e4066Sahrens } 5643fa9e4066Sahrens 5644fa9e4066Sahrens 5645*4445fffbSMatthew Ahrens if (error == 0 && !(flag & FKIOCTL)) 5646*4445fffbSMatthew Ahrens error = vec->zvec_secpolicy(zc, innvl, cr); 5647*4445fffbSMatthew Ahrens 5648*4445fffbSMatthew Ahrens if (error != 0) 5649*4445fffbSMatthew Ahrens goto out; 5650*4445fffbSMatthew Ahrens 5651*4445fffbSMatthew Ahrens /* legacy ioctls can modify zc_name */ 5652*4445fffbSMatthew Ahrens len = strcspn(zc->zc_name, "/@") + 1; 5653*4445fffbSMatthew Ahrens saved_poolname = kmem_alloc(len, KM_SLEEP); 5654*4445fffbSMatthew Ahrens (void) strlcpy(saved_poolname, zc->zc_name, len); 5655*4445fffbSMatthew Ahrens 5656*4445fffbSMatthew Ahrens if (vec->zvec_func != NULL) { 5657*4445fffbSMatthew Ahrens nvlist_t *outnvl; 5658*4445fffbSMatthew Ahrens int puterror = 0; 5659*4445fffbSMatthew Ahrens spa_t *spa; 5660*4445fffbSMatthew Ahrens nvlist_t *lognv = NULL; 5661*4445fffbSMatthew Ahrens 5662*4445fffbSMatthew Ahrens ASSERT(vec->zvec_legacy_func == NULL); 5663*4445fffbSMatthew Ahrens 5664*4445fffbSMatthew Ahrens /* 5665*4445fffbSMatthew Ahrens * Add the innvl to the lognv before calling the func, 5666*4445fffbSMatthew Ahrens * in case the func changes the innvl. 5667*4445fffbSMatthew Ahrens */ 5668*4445fffbSMatthew Ahrens if (vec->zvec_allow_log) { 5669*4445fffbSMatthew Ahrens lognv = fnvlist_alloc(); 5670*4445fffbSMatthew Ahrens fnvlist_add_string(lognv, ZPOOL_HIST_IOCTL, 5671*4445fffbSMatthew Ahrens vec->zvec_name); 5672*4445fffbSMatthew Ahrens if (!nvlist_empty(innvl)) { 5673*4445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_INPUT_NVL, 5674*4445fffbSMatthew Ahrens innvl); 5675*4445fffbSMatthew Ahrens } 5676*4445fffbSMatthew Ahrens } 5677*4445fffbSMatthew Ahrens 5678*4445fffbSMatthew Ahrens outnvl = fnvlist_alloc(); 5679*4445fffbSMatthew Ahrens error = vec->zvec_func(zc->zc_name, innvl, outnvl); 5680*4445fffbSMatthew Ahrens 5681*4445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log && 5682*4445fffbSMatthew Ahrens spa_open(zc->zc_name, &spa, FTAG) == 0) { 5683*4445fffbSMatthew Ahrens if (!nvlist_empty(outnvl)) { 5684*4445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, 5685*4445fffbSMatthew Ahrens outnvl); 5686*4445fffbSMatthew Ahrens } 5687*4445fffbSMatthew Ahrens (void) spa_history_log_nvl(spa, lognv); 5688*4445fffbSMatthew Ahrens spa_close(spa, FTAG); 5689*4445fffbSMatthew Ahrens } 5690*4445fffbSMatthew Ahrens fnvlist_free(lognv); 5691*4445fffbSMatthew Ahrens 5692*4445fffbSMatthew Ahrens if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) { 5693*4445fffbSMatthew Ahrens int smusherror = 0; 5694*4445fffbSMatthew Ahrens if (vec->zvec_smush_outnvlist) { 5695*4445fffbSMatthew Ahrens smusherror = nvlist_smush(outnvl, 5696*4445fffbSMatthew Ahrens zc->zc_nvlist_dst_size); 5697*4445fffbSMatthew Ahrens } 5698*4445fffbSMatthew Ahrens if (smusherror == 0) 5699*4445fffbSMatthew Ahrens puterror = put_nvlist(zc, outnvl); 5700*4445fffbSMatthew Ahrens } 5701*4445fffbSMatthew Ahrens 5702*4445fffbSMatthew Ahrens if (puterror != 0) 5703*4445fffbSMatthew Ahrens error = puterror; 5704*4445fffbSMatthew Ahrens 5705*4445fffbSMatthew Ahrens nvlist_free(outnvl); 5706*4445fffbSMatthew Ahrens } else { 5707*4445fffbSMatthew Ahrens error = vec->zvec_legacy_func(zc); 5708*4445fffbSMatthew Ahrens } 5709*4445fffbSMatthew Ahrens 5710*4445fffbSMatthew Ahrens out: 5711*4445fffbSMatthew Ahrens nvlist_free(innvl); 5712478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 5713*4445fffbSMatthew Ahrens if (error == 0 && rc != 0) 5714*4445fffbSMatthew Ahrens error = EFAULT; 5715*4445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log) { 5716*4445fffbSMatthew Ahrens char *s = tsd_get(zfs_allow_log_key); 5717*4445fffbSMatthew Ahrens if (s != NULL) 5718*4445fffbSMatthew Ahrens strfree(s); 5719*4445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, saved_poolname); 5720*4445fffbSMatthew Ahrens } else { 5721*4445fffbSMatthew Ahrens if (saved_poolname != NULL) 5722*4445fffbSMatthew Ahrens strfree(saved_poolname); 5723ecd6cf80Smarks } 5724fa9e4066Sahrens 5725fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 5726fa9e4066Sahrens return (error); 5727fa9e4066Sahrens } 5728fa9e4066Sahrens 5729fa9e4066Sahrens static int 5730fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5731fa9e4066Sahrens { 5732fa9e4066Sahrens if (cmd != DDI_ATTACH) 5733fa9e4066Sahrens return (DDI_FAILURE); 5734fa9e4066Sahrens 5735fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 5736fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 5737fa9e4066Sahrens return (DDI_FAILURE); 5738fa9e4066Sahrens 5739fa9e4066Sahrens zfs_dip = dip; 5740fa9e4066Sahrens 5741fa9e4066Sahrens ddi_report_dev(dip); 5742fa9e4066Sahrens 5743fa9e4066Sahrens return (DDI_SUCCESS); 5744fa9e4066Sahrens } 5745fa9e4066Sahrens 5746fa9e4066Sahrens static int 5747fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 5748fa9e4066Sahrens { 5749fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 5750fa9e4066Sahrens return (DDI_FAILURE); 5751fa9e4066Sahrens 5752fa9e4066Sahrens if (cmd != DDI_DETACH) 5753fa9e4066Sahrens return (DDI_FAILURE); 5754fa9e4066Sahrens 5755fa9e4066Sahrens zfs_dip = NULL; 5756fa9e4066Sahrens 5757fa9e4066Sahrens ddi_prop_remove_all(dip); 5758fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 5759fa9e4066Sahrens 5760fa9e4066Sahrens return (DDI_SUCCESS); 5761fa9e4066Sahrens } 5762fa9e4066Sahrens 5763fa9e4066Sahrens /*ARGSUSED*/ 5764fa9e4066Sahrens static int 5765fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 5766fa9e4066Sahrens { 5767fa9e4066Sahrens switch (infocmd) { 5768fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 5769fa9e4066Sahrens *result = zfs_dip; 5770fa9e4066Sahrens return (DDI_SUCCESS); 5771fa9e4066Sahrens 5772fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 5773a0965f35Sbonwick *result = (void *)0; 5774fa9e4066Sahrens return (DDI_SUCCESS); 5775fa9e4066Sahrens } 5776fa9e4066Sahrens 5777fa9e4066Sahrens return (DDI_FAILURE); 5778fa9e4066Sahrens } 5779fa9e4066Sahrens 5780fa9e4066Sahrens /* 5781fa9e4066Sahrens * OK, so this is a little weird. 5782fa9e4066Sahrens * 5783fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 5784fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 5785fa9e4066Sahrens * 5786fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 5787fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 5788fa9e4066Sahrens */ 5789fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 5790c99e4bdcSChris Kirby zfsdev_open, /* open */ 5791c99e4bdcSChris Kirby zfsdev_close, /* close */ 5792fa9e4066Sahrens zvol_strategy, /* strategy */ 5793fa9e4066Sahrens nodev, /* print */ 5794e7cbe64fSgw zvol_dump, /* dump */ 5795fa9e4066Sahrens zvol_read, /* read */ 5796fa9e4066Sahrens zvol_write, /* write */ 5797fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 5798fa9e4066Sahrens nodev, /* devmap */ 5799fa9e4066Sahrens nodev, /* mmap */ 5800fa9e4066Sahrens nodev, /* segmap */ 5801fa9e4066Sahrens nochpoll, /* poll */ 5802fa9e4066Sahrens ddi_prop_op, /* prop_op */ 5803fa9e4066Sahrens NULL, /* streamtab */ 5804fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 5805fa9e4066Sahrens CB_REV, /* version */ 5806feb08c6bSbillm nodev, /* async read */ 5807feb08c6bSbillm nodev, /* async write */ 5808fa9e4066Sahrens }; 5809fa9e4066Sahrens 5810fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 5811fa9e4066Sahrens DEVO_REV, /* version */ 5812fa9e4066Sahrens 0, /* refcnt */ 5813fa9e4066Sahrens zfs_info, /* info */ 5814fa9e4066Sahrens nulldev, /* identify */ 5815fa9e4066Sahrens nulldev, /* probe */ 5816fa9e4066Sahrens zfs_attach, /* attach */ 5817fa9e4066Sahrens zfs_detach, /* detach */ 5818fa9e4066Sahrens nodev, /* reset */ 5819fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 582019397407SSherry Moore NULL, /* no bus operations */ 582119397407SSherry Moore NULL, /* power */ 582219397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 5823fa9e4066Sahrens }; 5824fa9e4066Sahrens 5825fa9e4066Sahrens static struct modldrv zfs_modldrv = { 582619397407SSherry Moore &mod_driverops, 582719397407SSherry Moore "ZFS storage pool", 582819397407SSherry Moore &zfs_dev_ops 5829fa9e4066Sahrens }; 5830fa9e4066Sahrens 5831fa9e4066Sahrens static struct modlinkage modlinkage = { 5832fa9e4066Sahrens MODREV_1, 5833fa9e4066Sahrens (void *)&zfs_modlfs, 5834fa9e4066Sahrens (void *)&zfs_modldrv, 5835fa9e4066Sahrens NULL 5836fa9e4066Sahrens }; 5837fa9e4066Sahrens 5838*4445fffbSMatthew Ahrens static void 5839*4445fffbSMatthew Ahrens zfs_allow_log_destroy(void *arg) 5840*4445fffbSMatthew Ahrens { 5841*4445fffbSMatthew Ahrens char *poolname = arg; 5842*4445fffbSMatthew Ahrens strfree(poolname); 5843*4445fffbSMatthew Ahrens } 5844ec533521Sfr 5845fa9e4066Sahrens int 5846fa9e4066Sahrens _init(void) 5847fa9e4066Sahrens { 5848fa9e4066Sahrens int error; 5849fa9e4066Sahrens 5850a0965f35Sbonwick spa_init(FREAD | FWRITE); 5851a0965f35Sbonwick zfs_init(); 5852a0965f35Sbonwick zvol_init(); 5853*4445fffbSMatthew Ahrens zfs_ioctl_init(); 5854a0965f35Sbonwick 5855a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 5856a0965f35Sbonwick zvol_fini(); 5857a0965f35Sbonwick zfs_fini(); 5858a0965f35Sbonwick spa_fini(); 5859fa9e4066Sahrens return (error); 5860a0965f35Sbonwick } 5861fa9e4066Sahrens 5862ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 5863*4445fffbSMatthew Ahrens tsd_create(&rrw_tsd_key, rrw_tsd_destroy); 5864*4445fffbSMatthew Ahrens tsd_create(&zfs_allow_log_key, zfs_allow_log_destroy); 5865ec533521Sfr 5866fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 5867fa9e4066Sahrens ASSERT(error == 0); 5868ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 5869fa9e4066Sahrens 5870fa9e4066Sahrens return (0); 5871fa9e4066Sahrens } 5872fa9e4066Sahrens 5873fa9e4066Sahrens int 5874fa9e4066Sahrens _fini(void) 5875fa9e4066Sahrens { 5876fa9e4066Sahrens int error; 5877fa9e4066Sahrens 5878ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 5879fa9e4066Sahrens return (EBUSY); 5880fa9e4066Sahrens 5881fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 5882fa9e4066Sahrens return (error); 5883fa9e4066Sahrens 5884fa9e4066Sahrens zvol_fini(); 5885fa9e4066Sahrens zfs_fini(); 5886fa9e4066Sahrens spa_fini(); 5887da6c28aaSamw if (zfs_nfsshare_inited) 5888ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 5889da6c28aaSamw if (zfs_smbshare_inited) 5890da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 5891da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 5892ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 5893fa9e4066Sahrens 5894ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 5895fa9e4066Sahrens ldi_ident_release(zfs_li); 5896fa9e4066Sahrens zfs_li = NULL; 5897ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 5898fa9e4066Sahrens 5899fa9e4066Sahrens return (error); 5900fa9e4066Sahrens } 5901fa9e4066Sahrens 5902fa9e4066Sahrens int 5903fa9e4066Sahrens _info(struct modinfo *modinfop) 5904fa9e4066Sahrens { 5905fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 5906fa9e4066Sahrens } 5907