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 25*5878fad7SDan McDonald * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. 26752fd8daSJosef 'Jeff' Sipek * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 27a2afb611SJerry Jelinek * Copyright (c) 2014, Joyent, Inc. All rights reserved. 286de9bb56SMatthew Ahrens * Copyright (c) 2011, 2015 by Delphix. All rights reserved. 29a6f561b4SSašo Kiselkov * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. 30a7a845e4SSteven Hartland * Copyright (c) 2013 Steven Hartland. All rights reserved. 314445fffbSMatthew Ahrens */ 324445fffbSMatthew Ahrens 334445fffbSMatthew Ahrens /* 344445fffbSMatthew Ahrens * ZFS ioctls. 354445fffbSMatthew Ahrens * 364445fffbSMatthew Ahrens * This file handles the ioctls to /dev/zfs, used for configuring ZFS storage 374445fffbSMatthew Ahrens * pools and filesystems, e.g. with /sbin/zfs and /sbin/zpool. 384445fffbSMatthew Ahrens * 394445fffbSMatthew Ahrens * There are two ways that we handle ioctls: the legacy way where almost 404445fffbSMatthew Ahrens * all of the logic is in the ioctl callback, and the new way where most 414445fffbSMatthew Ahrens * of the marshalling is handled in the common entry point, zfsdev_ioctl(). 424445fffbSMatthew Ahrens * 434445fffbSMatthew Ahrens * Non-legacy ioctls should be registered by calling 444445fffbSMatthew Ahrens * zfs_ioctl_register() from zfs_ioctl_init(). The ioctl is invoked 454445fffbSMatthew Ahrens * from userland by lzc_ioctl(). 464445fffbSMatthew Ahrens * 474445fffbSMatthew Ahrens * The registration arguments are as follows: 484445fffbSMatthew Ahrens * 494445fffbSMatthew Ahrens * const char *name 504445fffbSMatthew Ahrens * The name of the ioctl. This is used for history logging. If the 514445fffbSMatthew Ahrens * ioctl returns successfully (the callback returns 0), and allow_log 524445fffbSMatthew Ahrens * is true, then a history log entry will be recorded with the input & 534445fffbSMatthew Ahrens * output nvlists. The log entry can be printed with "zpool history -i". 544445fffbSMatthew Ahrens * 554445fffbSMatthew Ahrens * zfs_ioc_t ioc 564445fffbSMatthew Ahrens * The ioctl request number, which userland will pass to ioctl(2). 574445fffbSMatthew Ahrens * The ioctl numbers can change from release to release, because 584445fffbSMatthew Ahrens * the caller (libzfs) must be matched to the kernel. 594445fffbSMatthew Ahrens * 604445fffbSMatthew Ahrens * zfs_secpolicy_func_t *secpolicy 614445fffbSMatthew Ahrens * This function will be called before the zfs_ioc_func_t, to 624445fffbSMatthew Ahrens * determine if this operation is permitted. It should return EPERM 634445fffbSMatthew Ahrens * on failure, and 0 on success. Checks include determining if the 644445fffbSMatthew Ahrens * dataset is visible in this zone, and if the user has either all 654445fffbSMatthew Ahrens * zfs privileges in the zone (SYS_MOUNT), or has been granted permission 664445fffbSMatthew Ahrens * to do this operation on this dataset with "zfs allow". 674445fffbSMatthew Ahrens * 684445fffbSMatthew Ahrens * zfs_ioc_namecheck_t namecheck 694445fffbSMatthew Ahrens * This specifies what to expect in the zfs_cmd_t:zc_name -- a pool 704445fffbSMatthew Ahrens * name, a dataset name, or nothing. If the name is not well-formed, 714445fffbSMatthew Ahrens * the ioctl will fail and the callback will not be called. 724445fffbSMatthew Ahrens * Therefore, the callback can assume that the name is well-formed 734445fffbSMatthew Ahrens * (e.g. is null-terminated, doesn't have more than one '@' character, 744445fffbSMatthew Ahrens * doesn't have invalid characters). 754445fffbSMatthew Ahrens * 764445fffbSMatthew Ahrens * zfs_ioc_poolcheck_t pool_check 774445fffbSMatthew Ahrens * This specifies requirements on the pool state. If the pool does 784445fffbSMatthew Ahrens * not meet them (is suspended or is readonly), the ioctl will fail 794445fffbSMatthew Ahrens * and the callback will not be called. If any checks are specified 804445fffbSMatthew Ahrens * (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. 814445fffbSMatthew Ahrens * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | 824445fffbSMatthew Ahrens * POOL_CHECK_READONLY). 834445fffbSMatthew Ahrens * 844445fffbSMatthew Ahrens * boolean_t smush_outnvlist 854445fffbSMatthew Ahrens * If smush_outnvlist is true, then the output is presumed to be a 864445fffbSMatthew Ahrens * list of errors, and it will be "smushed" down to fit into the 874445fffbSMatthew Ahrens * caller's buffer, by removing some entries and replacing them with a 884445fffbSMatthew Ahrens * single "N_MORE_ERRORS" entry indicating how many were removed. See 894445fffbSMatthew Ahrens * nvlist_smush() for details. If smush_outnvlist is false, and the 904445fffbSMatthew Ahrens * outnvlist does not fit into the userland-provided buffer, then the 914445fffbSMatthew Ahrens * ioctl will fail with ENOMEM. 924445fffbSMatthew Ahrens * 934445fffbSMatthew Ahrens * zfs_ioc_func_t *func 944445fffbSMatthew Ahrens * The callback function that will perform the operation. 954445fffbSMatthew Ahrens * 964445fffbSMatthew Ahrens * The callback should return 0 on success, or an error number on 974445fffbSMatthew Ahrens * failure. If the function fails, the userland ioctl will return -1, 984445fffbSMatthew Ahrens * and errno will be set to the callback's return value. The callback 994445fffbSMatthew Ahrens * will be called with the following arguments: 1004445fffbSMatthew Ahrens * 1014445fffbSMatthew Ahrens * const char *name 1024445fffbSMatthew Ahrens * The name of the pool or dataset to operate on, from 1034445fffbSMatthew Ahrens * zfs_cmd_t:zc_name. The 'namecheck' argument specifies the 1044445fffbSMatthew Ahrens * expected type (pool, dataset, or none). 1054445fffbSMatthew Ahrens * 1064445fffbSMatthew Ahrens * nvlist_t *innvl 1074445fffbSMatthew Ahrens * The input nvlist, deserialized from zfs_cmd_t:zc_nvlist_src. Or 1084445fffbSMatthew Ahrens * NULL if no input nvlist was provided. Changes to this nvlist are 1094445fffbSMatthew Ahrens * ignored. If the input nvlist could not be deserialized, the 1104445fffbSMatthew Ahrens * ioctl will fail and the callback will not be called. 1114445fffbSMatthew Ahrens * 1124445fffbSMatthew Ahrens * nvlist_t *outnvl 1134445fffbSMatthew Ahrens * The output nvlist, initially empty. The callback can fill it in, 1144445fffbSMatthew Ahrens * and it will be returned to userland by serializing it into 1154445fffbSMatthew Ahrens * zfs_cmd_t:zc_nvlist_dst. If it is non-empty, and serialization 1164445fffbSMatthew Ahrens * fails (e.g. because the caller didn't supply a large enough 1174445fffbSMatthew Ahrens * buffer), then the overall ioctl will fail. See the 1184445fffbSMatthew Ahrens * 'smush_nvlist' argument above for additional behaviors. 1194445fffbSMatthew Ahrens * 1204445fffbSMatthew Ahrens * There are two typical uses of the output nvlist: 1214445fffbSMatthew Ahrens * - To return state, e.g. property values. In this case, 1224445fffbSMatthew Ahrens * smush_outnvlist should be false. If the buffer was not large 1234445fffbSMatthew Ahrens * enough, the caller will reallocate a larger buffer and try 1244445fffbSMatthew Ahrens * the ioctl again. 1254445fffbSMatthew Ahrens * 1264445fffbSMatthew Ahrens * - To return multiple errors from an ioctl which makes on-disk 1274445fffbSMatthew Ahrens * changes. In this case, smush_outnvlist should be true. 1284445fffbSMatthew Ahrens * Ioctls which make on-disk modifications should generally not 1294445fffbSMatthew Ahrens * use the outnvl if they succeed, because the caller can not 1304445fffbSMatthew Ahrens * distinguish between the operation failing, and 1314445fffbSMatthew Ahrens * deserialization failing. 132e9103aaeSGarrett D'Amore */ 133fa9e4066Sahrens 134fa9e4066Sahrens #include <sys/types.h> 135fa9e4066Sahrens #include <sys/param.h> 136fa9e4066Sahrens #include <sys/errno.h> 137fa9e4066Sahrens #include <sys/uio.h> 138fa9e4066Sahrens #include <sys/buf.h> 139fa9e4066Sahrens #include <sys/modctl.h> 140fa9e4066Sahrens #include <sys/open.h> 141fa9e4066Sahrens #include <sys/file.h> 142fa9e4066Sahrens #include <sys/kmem.h> 143fa9e4066Sahrens #include <sys/conf.h> 144fa9e4066Sahrens #include <sys/cmn_err.h> 145fa9e4066Sahrens #include <sys/stat.h> 146fa9e4066Sahrens #include <sys/zfs_ioctl.h> 1474201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 148da6c28aaSamw #include <sys/zfs_znode.h> 149fa9e4066Sahrens #include <sys/zap.h> 150fa9e4066Sahrens #include <sys/spa.h> 151b1b8ab34Slling #include <sys/spa_impl.h> 152fa9e4066Sahrens #include <sys/vdev.h> 1534201a95eSRic Aleshire #include <sys/priv_impl.h> 154fa9e4066Sahrens #include <sys/dmu.h> 155fa9e4066Sahrens #include <sys/dsl_dir.h> 156fa9e4066Sahrens #include <sys/dsl_dataset.h> 157fa9e4066Sahrens #include <sys/dsl_prop.h> 158ecd6cf80Smarks #include <sys/dsl_deleg.h> 159ecd6cf80Smarks #include <sys/dmu_objset.h> 1604e3c9f44SBill Pijewski #include <sys/dmu_impl.h> 1613b2aab18SMatthew Ahrens #include <sys/dmu_tx.h> 162fa9e4066Sahrens #include <sys/ddi.h> 163fa9e4066Sahrens #include <sys/sunddi.h> 164fa9e4066Sahrens #include <sys/sunldi.h> 165fa9e4066Sahrens #include <sys/policy.h> 166fa9e4066Sahrens #include <sys/zone.h> 167fa9e4066Sahrens #include <sys/nvpair.h> 168fa9e4066Sahrens #include <sys/pathname.h> 169fa9e4066Sahrens #include <sys/mount.h> 170fa9e4066Sahrens #include <sys/sdt.h> 171fa9e4066Sahrens #include <sys/fs/zfs.h> 172fa9e4066Sahrens #include <sys/zfs_ctldir.h> 173da6c28aaSamw #include <sys/zfs_dir.h> 174c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 175a2eea2e1Sahrens #include <sys/zvol.h> 1763f9d6ad7SLin Ling #include <sys/dsl_scan.h> 177ecd6cf80Smarks #include <sharefs/share.h> 178f18faf3fSek #include <sys/dmu_objset.h> 1793b2aab18SMatthew Ahrens #include <sys/dmu_send.h> 1803b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h> 18178f17100SMatthew Ahrens #include <sys/dsl_bookmark.h> 1823b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h> 183a6f561b4SSašo Kiselkov #include <sys/zfeature.h> 18445818ee1SMatthew Ahrens #include <sys/zio_checksum.h> 185fa9e4066Sahrens 186fa9e4066Sahrens #include "zfs_namecheck.h" 187e9dbad6fSeschrock #include "zfs_prop.h" 188ecd6cf80Smarks #include "zfs_deleg.h" 1890a586ceaSMark Shellenbaum #include "zfs_comutil.h" 190fa9e4066Sahrens 191fa9e4066Sahrens extern struct modlfs zfs_modlfs; 192fa9e4066Sahrens 193fa9e4066Sahrens extern void zfs_init(void); 194fa9e4066Sahrens extern void zfs_fini(void); 195fa9e4066Sahrens 196fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 197fa9e4066Sahrens dev_info_t *zfs_dip; 198fa9e4066Sahrens 1994445fffbSMatthew Ahrens uint_t zfs_fsyncer_key; 2004445fffbSMatthew Ahrens extern uint_t rrw_tsd_key; 2014445fffbSMatthew Ahrens static uint_t zfs_allow_log_key; 2024445fffbSMatthew Ahrens 2034445fffbSMatthew Ahrens typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); 2044445fffbSMatthew Ahrens typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); 2054445fffbSMatthew Ahrens typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); 206fa9e4066Sahrens 20754d692b7SGeorge Wilson typedef enum { 20854d692b7SGeorge Wilson NO_NAME, 20954d692b7SGeorge Wilson POOL_NAME, 21054d692b7SGeorge Wilson DATASET_NAME 21154d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 21254d692b7SGeorge Wilson 213f9af39baSGeorge Wilson typedef enum { 214f9af39baSGeorge Wilson POOL_CHECK_NONE = 1 << 0, 215f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED = 1 << 1, 2164445fffbSMatthew Ahrens POOL_CHECK_READONLY = 1 << 2, 217f9af39baSGeorge Wilson } zfs_ioc_poolcheck_t; 218f9af39baSGeorge Wilson 219fa9e4066Sahrens typedef struct zfs_ioc_vec { 2204445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *zvec_legacy_func; 221fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 222fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 22354d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 2244445fffbSMatthew Ahrens boolean_t zvec_allow_log; 225f9af39baSGeorge Wilson zfs_ioc_poolcheck_t zvec_pool_check; 2264445fffbSMatthew Ahrens boolean_t zvec_smush_outnvlist; 2274445fffbSMatthew Ahrens const char *zvec_name; 228fa9e4066Sahrens } zfs_ioc_vec_t; 229fa9e4066Sahrens 23014843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 23114843421SMatthew Ahrens static const char *userquota_perms[] = { 23214843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 23314843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 23414843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 23514843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 23614843421SMatthew Ahrens }; 23714843421SMatthew Ahrens 23814843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 23992241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 24092241e0bSTom Erickson cred_t *cr); 24192241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 24292241e0bSTom Erickson nvlist_t **errors); 2430a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 2440a48a24eStimh boolean_t *); 2454445fffbSMatthew Ahrens int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); 2464445fffbSMatthew Ahrens static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp); 2470a48a24eStimh 2482acef22dSMatthew Ahrens static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature); 249a6f561b4SSašo Kiselkov 250fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 251fa9e4066Sahrens void 252fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 253fa9e4066Sahrens { 254fa9e4066Sahrens const char *newfile; 2553f9d6ad7SLin Ling char buf[512]; 256fa9e4066Sahrens va_list adx; 257fa9e4066Sahrens 258fa9e4066Sahrens /* 259fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 260fa9e4066Sahrens */ 261fa9e4066Sahrens newfile = strrchr(file, '/'); 262fa9e4066Sahrens if (newfile != NULL) { 263fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 264fa9e4066Sahrens } else { 265fa9e4066Sahrens newfile = file; 266fa9e4066Sahrens } 267fa9e4066Sahrens 268fa9e4066Sahrens va_start(adx, fmt); 269fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 270fa9e4066Sahrens va_end(adx); 271fa9e4066Sahrens 272fa9e4066Sahrens /* 273fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 274fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 275fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 276fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 277fa9e4066Sahrens * arg0 = file name 278fa9e4066Sahrens * arg1 = function name 279fa9e4066Sahrens * arg2 = line number 280fa9e4066Sahrens * arg3 = message 281fa9e4066Sahrens */ 282fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 283fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 284fa9e4066Sahrens } 285fa9e4066Sahrens 286ecd6cf80Smarks static void 287228975ccSek history_str_free(char *buf) 288228975ccSek { 289228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 290228975ccSek } 291228975ccSek 292228975ccSek static char * 293228975ccSek history_str_get(zfs_cmd_t *zc) 294ecd6cf80Smarks { 29540feaa91Sahrens char *buf; 296ecd6cf80Smarks 297ecd6cf80Smarks if (zc->zc_history == NULL) 298228975ccSek return (NULL); 299e7437265Sahrens 300ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 301ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 302ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 303228975ccSek history_str_free(buf); 304228975ccSek return (NULL); 305ecd6cf80Smarks } 306ecd6cf80Smarks 307ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 308ecd6cf80Smarks 309228975ccSek return (buf); 310228975ccSek } 311ecd6cf80Smarks 31215e6edf1Sgw /* 31315e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 31415e6edf1Sgw */ 31515e6edf1Sgw static boolean_t 31615e6edf1Sgw zfs_is_bootfs(const char *name) 31715e6edf1Sgw { 318503ad85cSMatthew Ahrens objset_t *os; 31915e6edf1Sgw 320503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 321503ad85cSMatthew Ahrens boolean_t ret; 322b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 323503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 324503ad85cSMatthew Ahrens return (ret); 32515e6edf1Sgw } 326503ad85cSMatthew Ahrens return (B_FALSE); 32715e6edf1Sgw } 32815e6edf1Sgw 329c2a93d44Stimh /* 330f7170741SWill Andrews * Return non-zero if the spa version is less than requested version. 331c2a93d44Stimh */ 332da6c28aaSamw static int 3330a48a24eStimh zfs_earlier_version(const char *name, int version) 334da6c28aaSamw { 335da6c28aaSamw spa_t *spa; 336da6c28aaSamw 337da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 338da6c28aaSamw if (spa_version(spa) < version) { 339da6c28aaSamw spa_close(spa, FTAG); 340da6c28aaSamw return (1); 341da6c28aaSamw } 342da6c28aaSamw spa_close(spa, FTAG); 343da6c28aaSamw } 344da6c28aaSamw return (0); 345da6c28aaSamw } 346da6c28aaSamw 3479e6eda55Smarks /* 348745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 3499e6eda55Smarks */ 350745cd3c5Smaybee static boolean_t 351745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 3529e6eda55Smarks { 3539e6eda55Smarks objset_t *os; 354745cd3c5Smaybee boolean_t rc = B_TRUE; 3559e6eda55Smarks 356503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 357745cd3c5Smaybee uint64_t zplversion; 3589e6eda55Smarks 359503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 360503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 361503ad85cSMatthew Ahrens return (B_TRUE); 362503ad85cSMatthew Ahrens } 363503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 364745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 365745cd3c5Smaybee rc = zplversion < version; 366503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3679e6eda55Smarks } 3689e6eda55Smarks return (rc); 3699e6eda55Smarks } 3709e6eda55Smarks 371228975ccSek static void 372228975ccSek zfs_log_history(zfs_cmd_t *zc) 373228975ccSek { 374228975ccSek spa_t *spa; 375228975ccSek char *buf; 376ecd6cf80Smarks 377228975ccSek if ((buf = history_str_get(zc)) == NULL) 378228975ccSek return; 379228975ccSek 380228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 381228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 3824445fffbSMatthew Ahrens (void) spa_history_log(spa, buf); 383228975ccSek spa_close(spa, FTAG); 384228975ccSek } 385228975ccSek history_str_free(buf); 386ecd6cf80Smarks } 387ecd6cf80Smarks 388fa9e4066Sahrens /* 389fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 390fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 391fa9e4066Sahrens */ 392fa9e4066Sahrens /* ARGSUSED */ 393fa9e4066Sahrens static int 3944445fffbSMatthew Ahrens zfs_secpolicy_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 395fa9e4066Sahrens { 396fa9e4066Sahrens return (0); 397fa9e4066Sahrens } 398fa9e4066Sahrens 399fa9e4066Sahrens /* 400fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 401fa9e4066Sahrens * no privileges, but must be visible in the local zone. 402fa9e4066Sahrens */ 403fa9e4066Sahrens /* ARGSUSED */ 404fa9e4066Sahrens static int 4054445fffbSMatthew Ahrens zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 406fa9e4066Sahrens { 407fa9e4066Sahrens if (INGLOBALZONE(curproc) || 408ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 409fa9e4066Sahrens return (0); 410fa9e4066Sahrens 411be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 412fa9e4066Sahrens } 413fa9e4066Sahrens 414fa9e4066Sahrens static int 415a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) 416fa9e4066Sahrens { 417fa9e4066Sahrens int writable = 1; 418fa9e4066Sahrens 419fa9e4066Sahrens /* 420fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 421fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 422fa9e4066Sahrens */ 423fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 424fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 425be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 426fa9e4066Sahrens 427fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 428fa9e4066Sahrens /* 429fa9e4066Sahrens * If the fs is zoned, only root can access it from the 430fa9e4066Sahrens * global zone. 431fa9e4066Sahrens */ 432fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 433be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 434fa9e4066Sahrens } else { 435fa9e4066Sahrens /* 436fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 437fa9e4066Sahrens */ 438fa9e4066Sahrens if (!zoned) 439be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 440fa9e4066Sahrens 441fa9e4066Sahrens /* must be writable by this zone */ 442fa9e4066Sahrens if (!writable) 443be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 444fa9e4066Sahrens } 445fa9e4066Sahrens return (0); 446fa9e4066Sahrens } 447fa9e4066Sahrens 448a7f53a56SChris Kirby static int 449a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr) 450a7f53a56SChris Kirby { 451a7f53a56SChris Kirby uint64_t zoned; 452a7f53a56SChris Kirby 453a7f53a56SChris Kirby if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 454be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 455a7f53a56SChris Kirby 456a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 457a7f53a56SChris Kirby } 458a7f53a56SChris Kirby 459a7f53a56SChris Kirby static int 460a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) 461a7f53a56SChris Kirby { 462a7f53a56SChris Kirby uint64_t zoned; 463a7f53a56SChris Kirby 4643b2aab18SMatthew Ahrens if (dsl_prop_get_int_ds(ds, "zoned", &zoned)) 465be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 466a7f53a56SChris Kirby 467a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 468a7f53a56SChris Kirby } 469a7f53a56SChris Kirby 4704445fffbSMatthew Ahrens static int 4713b2aab18SMatthew Ahrens zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, 4723b2aab18SMatthew Ahrens const char *perm, cred_t *cr) 473fa9e4066Sahrens { 474fa9e4066Sahrens int error; 475fa9e4066Sahrens 47619b94df9SMatthew Ahrens error = zfs_dozonecheck_ds(name, ds, cr); 477ecd6cf80Smarks if (error == 0) { 478ecd6cf80Smarks error = secpolicy_zfs(cr); 4793b2aab18SMatthew Ahrens if (error != 0) 4804445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 481ecd6cf80Smarks } 482ecd6cf80Smarks return (error); 483ecd6cf80Smarks } 484ecd6cf80Smarks 4854445fffbSMatthew Ahrens static int 4863b2aab18SMatthew Ahrens zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 487a7f53a56SChris Kirby { 488a7f53a56SChris Kirby int error; 4893b2aab18SMatthew Ahrens dsl_dataset_t *ds; 4903b2aab18SMatthew Ahrens dsl_pool_t *dp; 491a7f53a56SChris Kirby 4923b2aab18SMatthew Ahrens error = dsl_pool_hold(name, FTAG, &dp); 4933b2aab18SMatthew Ahrens if (error != 0) 4943b2aab18SMatthew Ahrens return (error); 4953b2aab18SMatthew Ahrens 4963b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, name, FTAG, &ds); 4973b2aab18SMatthew Ahrens if (error != 0) { 4983b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 4993b2aab18SMatthew Ahrens return (error); 500a7f53a56SChris Kirby } 5013b2aab18SMatthew Ahrens 5023b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(name, ds, perm, cr); 5033b2aab18SMatthew Ahrens 5043b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 5053b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 506a7f53a56SChris Kirby return (error); 507a7f53a56SChris Kirby } 508a7f53a56SChris Kirby 5094201a95eSRic Aleshire /* 5104201a95eSRic Aleshire * Policy for setting the security label property. 5114201a95eSRic Aleshire * 5124201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 5134201a95eSRic Aleshire */ 5144201a95eSRic Aleshire static int 51592241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 5164201a95eSRic Aleshire { 5174201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 5184201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 5194201a95eSRic Aleshire boolean_t new_default = FALSE; 5204201a95eSRic Aleshire uint64_t zoned; 5214201a95eSRic Aleshire int needed_priv = -1; 5224201a95eSRic Aleshire int error; 5234201a95eSRic Aleshire 5244201a95eSRic Aleshire /* First get the existing dataset label. */ 5254201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 5264201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 5273b2aab18SMatthew Ahrens if (error != 0) 528be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5294201a95eSRic Aleshire 5304201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 5314201a95eSRic Aleshire new_default = TRUE; 5324201a95eSRic Aleshire 5334201a95eSRic Aleshire /* The label must be translatable */ 5344201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 535be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5364201a95eSRic Aleshire 5374201a95eSRic Aleshire /* 5384201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 5394201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 5404201a95eSRic Aleshire * are needed. 5414201a95eSRic Aleshire */ 5424201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 5434201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 544be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5454201a95eSRic Aleshire return (0); 5464201a95eSRic Aleshire } 5474201a95eSRic Aleshire 5484201a95eSRic Aleshire /* 5494201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 5504201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 5514201a95eSRic Aleshire * global zone. 5524201a95eSRic Aleshire */ 5534201a95eSRic Aleshire if (dsl_prop_get_integer(name, 5544201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 555be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5564201a95eSRic Aleshire if (!zoned) { 5574201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 558be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5594201a95eSRic Aleshire } 5604201a95eSRic Aleshire 5614201a95eSRic Aleshire /* 5624201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 5634201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 5644201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 5654201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 5664201a95eSRic Aleshire */ 5674201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 56892241e0bSTom Erickson objset_t *os; 56992241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 57092241e0bSTom Erickson 5714201a95eSRic Aleshire /* 5724201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 5734201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 5744201a95eSRic Aleshire */ 5754201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 57692241e0bSTom Erickson setsl_tag, &os); 5773b2aab18SMatthew Ahrens if (error != 0) 578be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5794201a95eSRic Aleshire 58092241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 58192241e0bSTom Erickson 5824201a95eSRic Aleshire if (new_default) { 5834201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 5844201a95eSRic Aleshire goto out_check; 5854201a95eSRic Aleshire } 5864201a95eSRic Aleshire 5874201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 588be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5894201a95eSRic Aleshire 5904201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 5914201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 5924201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 5934201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 5944201a95eSRic Aleshire } else { 5954201a95eSRic Aleshire /* dataset currently has a default label */ 5964201a95eSRic Aleshire if (!new_default) 5974201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 5984201a95eSRic Aleshire } 5994201a95eSRic Aleshire 6004201a95eSRic Aleshire out_check: 6014201a95eSRic Aleshire if (needed_priv != -1) 6024201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 6034201a95eSRic Aleshire return (0); 6044201a95eSRic Aleshire } 6054201a95eSRic Aleshire 606ecd6cf80Smarks static int 60792241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 60892241e0bSTom Erickson cred_t *cr) 609ecd6cf80Smarks { 61092241e0bSTom Erickson char *strval; 61192241e0bSTom Erickson 612ecd6cf80Smarks /* 613ecd6cf80Smarks * Check permissions for special properties. 614ecd6cf80Smarks */ 615ecd6cf80Smarks switch (prop) { 616ecd6cf80Smarks case ZFS_PROP_ZONED: 617ecd6cf80Smarks /* 618ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 619ecd6cf80Smarks */ 620ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 621be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 622ecd6cf80Smarks break; 623ecd6cf80Smarks 624ecd6cf80Smarks case ZFS_PROP_QUOTA: 625a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 626a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 627ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 628ecd6cf80Smarks uint64_t zoned; 629ecd6cf80Smarks char setpoint[MAXNAMELEN]; 630ecd6cf80Smarks /* 631ecd6cf80Smarks * Unprivileged users are allowed to modify the 632a2afb611SJerry Jelinek * limit on things *under* (ie. contained by) 633ecd6cf80Smarks * the thing they own. 634ecd6cf80Smarks */ 63592241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 636ecd6cf80Smarks setpoint)) 637be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 63892241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 639be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 640ecd6cf80Smarks } 641db870a07Sahrens break; 6424201a95eSRic Aleshire 6434201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 6444201a95eSRic Aleshire if (!is_system_labeled()) 645be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 64692241e0bSTom Erickson 64792241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 64892241e0bSTom Erickson int err; 64992241e0bSTom Erickson 65092241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 65192241e0bSTom Erickson if (err != 0) 65292241e0bSTom Erickson return (err); 65392241e0bSTom Erickson } 6544201a95eSRic Aleshire break; 655ecd6cf80Smarks } 656ecd6cf80Smarks 65792241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 658ecd6cf80Smarks } 659ecd6cf80Smarks 6604445fffbSMatthew Ahrens /* ARGSUSED */ 6614445fffbSMatthew Ahrens static int 6624445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 663ecd6cf80Smarks { 664ecd6cf80Smarks int error; 665ecd6cf80Smarks 666ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 6673b2aab18SMatthew Ahrens if (error != 0) 668fa9e4066Sahrens return (error); 669fa9e4066Sahrens 670ecd6cf80Smarks /* 671ecd6cf80Smarks * permission to set permissions will be evaluated later in 672ecd6cf80Smarks * dsl_deleg_can_allow() 673ecd6cf80Smarks */ 674ecd6cf80Smarks return (0); 675ecd6cf80Smarks } 676ecd6cf80Smarks 6774445fffbSMatthew Ahrens /* ARGSUSED */ 6784445fffbSMatthew Ahrens static int 6794445fffbSMatthew Ahrens zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 680ecd6cf80Smarks { 681681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 682681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 683ecd6cf80Smarks } 684ecd6cf80Smarks 6854445fffbSMatthew Ahrens /* ARGSUSED */ 6864445fffbSMatthew Ahrens static int 6874445fffbSMatthew Ahrens zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 688ecd6cf80Smarks { 689a7f53a56SChris Kirby dsl_pool_t *dp; 690a7f53a56SChris Kirby dsl_dataset_t *ds; 691a7f53a56SChris Kirby char *cp; 692a7f53a56SChris Kirby int error; 693a7f53a56SChris Kirby 694a7f53a56SChris Kirby /* 695a7f53a56SChris Kirby * Generate the current snapshot name from the given objsetid, then 696a7f53a56SChris Kirby * use that name for the secpolicy/zone checks. 697a7f53a56SChris Kirby */ 698a7f53a56SChris Kirby cp = strchr(zc->zc_name, '@'); 699a7f53a56SChris Kirby if (cp == NULL) 700be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 7013b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 7023b2aab18SMatthew Ahrens if (error != 0) 703a7f53a56SChris Kirby return (error); 704a7f53a56SChris Kirby 705a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 7063b2aab18SMatthew Ahrens if (error != 0) { 7073b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 708a7f53a56SChris Kirby return (error); 7093b2aab18SMatthew Ahrens } 710a7f53a56SChris Kirby 711a7f53a56SChris Kirby dsl_dataset_name(ds, zc->zc_name); 712a7f53a56SChris Kirby 713a7f53a56SChris Kirby error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, 714a7f53a56SChris Kirby ZFS_DELEG_PERM_SEND, cr); 715a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 7163b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 717a7f53a56SChris Kirby 718a7f53a56SChris Kirby return (error); 719ecd6cf80Smarks } 720ecd6cf80Smarks 7214445fffbSMatthew Ahrens /* ARGSUSED */ 722743a77edSAlan Wright static int 7234445fffbSMatthew Ahrens zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 7244445fffbSMatthew Ahrens { 7254445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 7264445fffbSMatthew Ahrens ZFS_DELEG_PERM_SEND, cr)); 7274445fffbSMatthew Ahrens } 7284445fffbSMatthew Ahrens 7294445fffbSMatthew Ahrens /* ARGSUSED */ 7304445fffbSMatthew Ahrens static int 7314445fffbSMatthew Ahrens zfs_secpolicy_deleg_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 732743a77edSAlan Wright { 733743a77edSAlan Wright vnode_t *vp; 734743a77edSAlan Wright int error; 735743a77edSAlan Wright 736743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 737743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 738743a77edSAlan Wright return (error); 739743a77edSAlan Wright 740743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 741743a77edSAlan Wright 742743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 743743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 744743a77edSAlan Wright zc->zc_name) != 0)) { 745743a77edSAlan Wright VN_RELE(vp); 746be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 747743a77edSAlan Wright } 748743a77edSAlan Wright 749743a77edSAlan Wright VN_RELE(vp); 750743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 751743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 752743a77edSAlan Wright } 753743a77edSAlan Wright 754ecd6cf80Smarks int 7554445fffbSMatthew Ahrens zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 756ecd6cf80Smarks { 757ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 758be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 759ecd6cf80Smarks 7603cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 761ecd6cf80Smarks return (0); 762ecd6cf80Smarks } else { 7634445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 764743a77edSAlan Wright } 765743a77edSAlan Wright } 766ecd6cf80Smarks 767743a77edSAlan Wright int 7684445fffbSMatthew Ahrens zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 769743a77edSAlan Wright { 770743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 771be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 772ecd6cf80Smarks 773743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 774743a77edSAlan Wright return (0); 775743a77edSAlan Wright } else { 7764445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 777ecd6cf80Smarks } 778fa9e4066Sahrens } 779fa9e4066Sahrens 780fa9e4066Sahrens static int 781ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 782fa9e4066Sahrens { 783fa9e4066Sahrens char *cp; 784fa9e4066Sahrens 785fa9e4066Sahrens /* 786fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 787fa9e4066Sahrens */ 788ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 789ecd6cf80Smarks cp = strrchr(parent, '@'); 790fa9e4066Sahrens if (cp != NULL) { 791fa9e4066Sahrens cp[0] = '\0'; 792fa9e4066Sahrens } else { 793ecd6cf80Smarks cp = strrchr(parent, '/'); 794fa9e4066Sahrens if (cp == NULL) 795be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 796fa9e4066Sahrens cp[0] = '\0'; 797ecd6cf80Smarks } 798ecd6cf80Smarks 799ecd6cf80Smarks return (0); 800ecd6cf80Smarks } 801ecd6cf80Smarks 802ecd6cf80Smarks int 803ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 804ecd6cf80Smarks { 805ecd6cf80Smarks int error; 806ecd6cf80Smarks 807ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 808ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 809ecd6cf80Smarks return (error); 810ecd6cf80Smarks 811ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 812ecd6cf80Smarks } 813ecd6cf80Smarks 8144445fffbSMatthew Ahrens /* ARGSUSED */ 815ecd6cf80Smarks static int 8164445fffbSMatthew Ahrens zfs_secpolicy_destroy(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 817ecd6cf80Smarks { 818ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 819ecd6cf80Smarks } 820ecd6cf80Smarks 821cbf6f6aaSWilliam Gorrell /* 822cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 8234445fffbSMatthew Ahrens * descendant mount and destroy permissions. 824cbf6f6aaSWilliam Gorrell */ 8254445fffbSMatthew Ahrens /* ARGSUSED */ 826cbf6f6aaSWilliam Gorrell static int 8274445fffbSMatthew Ahrens zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 828cbf6f6aaSWilliam Gorrell { 8294445fffbSMatthew Ahrens nvlist_t *snaps; 8304445fffbSMatthew Ahrens nvpair_t *pair, *nextpair; 8314445fffbSMatthew Ahrens int error = 0; 832cbf6f6aaSWilliam Gorrell 8334445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 834be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 8354445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 8364445fffbSMatthew Ahrens pair = nextpair) { 8374445fffbSMatthew Ahrens nextpair = nvlist_next_nvpair(snaps, pair); 83878f17100SMatthew Ahrens error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); 83978f17100SMatthew Ahrens if (error == ENOENT) { 8404445fffbSMatthew Ahrens /* 8414445fffbSMatthew Ahrens * Ignore any snapshots that don't exist (we consider 8424445fffbSMatthew Ahrens * them "already destroyed"). Remove the name from the 8434445fffbSMatthew Ahrens * nvl here in case the snapshot is created between 8444445fffbSMatthew Ahrens * now and when we try to destroy it (in which case 8454445fffbSMatthew Ahrens * we don't want to destroy it since we haven't 8464445fffbSMatthew Ahrens * checked for permission). 8474445fffbSMatthew Ahrens */ 8484445fffbSMatthew Ahrens fnvlist_remove_nvpair(snaps, pair); 8494445fffbSMatthew Ahrens error = 0; 8504445fffbSMatthew Ahrens } 8514445fffbSMatthew Ahrens if (error != 0) 8524445fffbSMatthew Ahrens break; 8534445fffbSMatthew Ahrens } 854cbf6f6aaSWilliam Gorrell 855cbf6f6aaSWilliam Gorrell return (error); 856cbf6f6aaSWilliam Gorrell } 857cbf6f6aaSWilliam Gorrell 858ecd6cf80Smarks int 859ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 860ecd6cf80Smarks { 86192241e0bSTom Erickson char parentname[MAXNAMELEN]; 862ecd6cf80Smarks int error; 863ecd6cf80Smarks 864ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 865ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 866ecd6cf80Smarks return (error); 867ecd6cf80Smarks 868ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 869ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 870ecd6cf80Smarks return (error); 871ecd6cf80Smarks 872ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 873ecd6cf80Smarks sizeof (parentname))) != 0) 874ecd6cf80Smarks return (error); 875ecd6cf80Smarks 876ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 877ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 878ecd6cf80Smarks return (error); 879ecd6cf80Smarks 880ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 881ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 882ecd6cf80Smarks return (error); 883ecd6cf80Smarks 884ecd6cf80Smarks return (error); 885ecd6cf80Smarks } 886ecd6cf80Smarks 8874445fffbSMatthew Ahrens /* ARGSUSED */ 888ecd6cf80Smarks static int 8894445fffbSMatthew Ahrens zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 890ecd6cf80Smarks { 891ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 892ecd6cf80Smarks } 893ecd6cf80Smarks 8944445fffbSMatthew Ahrens /* ARGSUSED */ 895ecd6cf80Smarks static int 8964445fffbSMatthew Ahrens zfs_secpolicy_promote(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 897ecd6cf80Smarks { 8983b2aab18SMatthew Ahrens dsl_pool_t *dp; 8993b2aab18SMatthew Ahrens dsl_dataset_t *clone; 900ecd6cf80Smarks int error; 901ecd6cf80Smarks 902ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 903ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9043b2aab18SMatthew Ahrens if (error != 0) 9053b2aab18SMatthew Ahrens return (error); 9063b2aab18SMatthew Ahrens 9073b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 9083b2aab18SMatthew Ahrens if (error != 0) 909ecd6cf80Smarks return (error); 910ecd6cf80Smarks 9113b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &clone); 912ecd6cf80Smarks 913ecd6cf80Smarks if (error == 0) { 9143b2aab18SMatthew Ahrens char parentname[MAXNAMELEN]; 9153b2aab18SMatthew Ahrens dsl_dataset_t *origin = NULL; 916ecd6cf80Smarks dsl_dir_t *dd; 9173b2aab18SMatthew Ahrens dd = clone->ds_dir; 918ecd6cf80Smarks 919745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 920c1379625SJustin T. Gibbs dsl_dir_phys(dd)->dd_origin_obj, FTAG, &origin); 9213b2aab18SMatthew Ahrens if (error != 0) { 9223b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9233b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 924ecd6cf80Smarks return (error); 925ecd6cf80Smarks } 926ecd6cf80Smarks 9273b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(zc->zc_name, clone, 928ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 929ecd6cf80Smarks 9303b2aab18SMatthew Ahrens dsl_dataset_name(origin, parentname); 9313b2aab18SMatthew Ahrens if (error == 0) { 9323b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(parentname, origin, 933ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9343b2aab18SMatthew Ahrens } 9353b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9363b2aab18SMatthew Ahrens dsl_dataset_rele(origin, FTAG); 937ecd6cf80Smarks } 9383b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 939ecd6cf80Smarks return (error); 940ecd6cf80Smarks } 941ecd6cf80Smarks 9424445fffbSMatthew Ahrens /* ARGSUSED */ 943ecd6cf80Smarks static int 9444445fffbSMatthew Ahrens zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 945ecd6cf80Smarks { 946ecd6cf80Smarks int error; 947ecd6cf80Smarks 948ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 949ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 950ecd6cf80Smarks return (error); 951ecd6cf80Smarks 952ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 953ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 954ecd6cf80Smarks return (error); 955ecd6cf80Smarks 956ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 957ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 958ecd6cf80Smarks } 959ecd6cf80Smarks 960ecd6cf80Smarks int 961ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 962ecd6cf80Smarks { 963681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 964681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 965ecd6cf80Smarks } 966ecd6cf80Smarks 9674445fffbSMatthew Ahrens /* 9684445fffbSMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 9694445fffbSMatthew Ahrens */ 9704445fffbSMatthew Ahrens /* ARGSUSED */ 971ecd6cf80Smarks static int 9724445fffbSMatthew Ahrens zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 973ecd6cf80Smarks { 9744445fffbSMatthew Ahrens nvlist_t *snaps; 975d5285caeSGeorge Wilson int error = 0; 9764445fffbSMatthew Ahrens nvpair_t *pair; 977ecd6cf80Smarks 9784445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 979be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 9804445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 9814445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 9824445fffbSMatthew Ahrens char *name = nvpair_name(pair); 9834445fffbSMatthew Ahrens char *atp = strchr(name, '@'); 9844445fffbSMatthew Ahrens 9854445fffbSMatthew Ahrens if (atp == NULL) { 986be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 9874445fffbSMatthew Ahrens break; 9884445fffbSMatthew Ahrens } 9894445fffbSMatthew Ahrens *atp = '\0'; 9904445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(name, cr); 9914445fffbSMatthew Ahrens *atp = '@'; 9924445fffbSMatthew Ahrens if (error != 0) 9934445fffbSMatthew Ahrens break; 9944445fffbSMatthew Ahrens } 9954445fffbSMatthew Ahrens return (error); 996ecd6cf80Smarks } 997ecd6cf80Smarks 99878f17100SMatthew Ahrens /* 99978f17100SMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 100078f17100SMatthew Ahrens */ 100178f17100SMatthew Ahrens /* ARGSUSED */ 100278f17100SMatthew Ahrens static int 100378f17100SMatthew Ahrens zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 100478f17100SMatthew Ahrens { 100578f17100SMatthew Ahrens int error = 0; 100678f17100SMatthew Ahrens 100778f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 100878f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 100978f17100SMatthew Ahrens char *name = nvpair_name(pair); 101078f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 101178f17100SMatthew Ahrens 101278f17100SMatthew Ahrens if (hashp == NULL) { 101378f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 101478f17100SMatthew Ahrens break; 101578f17100SMatthew Ahrens } 101678f17100SMatthew Ahrens *hashp = '\0'; 101778f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 101878f17100SMatthew Ahrens ZFS_DELEG_PERM_BOOKMARK, cr); 101978f17100SMatthew Ahrens *hashp = '#'; 102078f17100SMatthew Ahrens if (error != 0) 102178f17100SMatthew Ahrens break; 102278f17100SMatthew Ahrens } 102378f17100SMatthew Ahrens return (error); 102478f17100SMatthew Ahrens } 102578f17100SMatthew Ahrens 102678f17100SMatthew Ahrens /* ARGSUSED */ 102778f17100SMatthew Ahrens static int 102878f17100SMatthew Ahrens zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 102978f17100SMatthew Ahrens { 103078f17100SMatthew Ahrens nvpair_t *pair, *nextpair; 103178f17100SMatthew Ahrens int error = 0; 103278f17100SMatthew Ahrens 103378f17100SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 103478f17100SMatthew Ahrens pair = nextpair) { 103578f17100SMatthew Ahrens char *name = nvpair_name(pair); 103678f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 103778f17100SMatthew Ahrens nextpair = nvlist_next_nvpair(innvl, pair); 103878f17100SMatthew Ahrens 103978f17100SMatthew Ahrens if (hashp == NULL) { 104078f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 104178f17100SMatthew Ahrens break; 104278f17100SMatthew Ahrens } 104378f17100SMatthew Ahrens 104478f17100SMatthew Ahrens *hashp = '\0'; 104578f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 104678f17100SMatthew Ahrens ZFS_DELEG_PERM_DESTROY, cr); 104778f17100SMatthew Ahrens *hashp = '#'; 104878f17100SMatthew Ahrens if (error == ENOENT) { 104978f17100SMatthew Ahrens /* 105078f17100SMatthew Ahrens * Ignore any filesystems that don't exist (we consider 105178f17100SMatthew Ahrens * their bookmarks "already destroyed"). Remove 105278f17100SMatthew Ahrens * the name from the nvl here in case the filesystem 105378f17100SMatthew Ahrens * is created between now and when we try to destroy 105478f17100SMatthew Ahrens * the bookmark (in which case we don't want to 105578f17100SMatthew Ahrens * destroy it since we haven't checked for permission). 105678f17100SMatthew Ahrens */ 105778f17100SMatthew Ahrens fnvlist_remove_nvpair(innvl, pair); 105878f17100SMatthew Ahrens error = 0; 105978f17100SMatthew Ahrens } 106078f17100SMatthew Ahrens if (error != 0) 106178f17100SMatthew Ahrens break; 106278f17100SMatthew Ahrens } 106378f17100SMatthew Ahrens 106478f17100SMatthew Ahrens return (error); 106578f17100SMatthew Ahrens } 106678f17100SMatthew Ahrens 10674445fffbSMatthew Ahrens /* ARGSUSED */ 1068ecd6cf80Smarks static int 10694445fffbSMatthew Ahrens zfs_secpolicy_log_history(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 10704445fffbSMatthew Ahrens { 10714445fffbSMatthew Ahrens /* 10724445fffbSMatthew Ahrens * Even root must have a proper TSD so that we know what pool 10734445fffbSMatthew Ahrens * to log to. 10744445fffbSMatthew Ahrens */ 10754445fffbSMatthew Ahrens if (tsd_get(zfs_allow_log_key) == NULL) 1076be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 10774445fffbSMatthew Ahrens return (0); 10784445fffbSMatthew Ahrens } 10794445fffbSMatthew Ahrens 10804445fffbSMatthew Ahrens static int 10814445fffbSMatthew Ahrens zfs_secpolicy_create_clone(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1082ecd6cf80Smarks { 108392241e0bSTom Erickson char parentname[MAXNAMELEN]; 108492241e0bSTom Erickson int error; 10854445fffbSMatthew Ahrens char *origin; 1086ecd6cf80Smarks 1087ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 1088ecd6cf80Smarks sizeof (parentname))) != 0) 1089ecd6cf80Smarks return (error); 1090fa9e4066Sahrens 10914445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin) == 0 && 10924445fffbSMatthew Ahrens (error = zfs_secpolicy_write_perms(origin, 10934445fffbSMatthew Ahrens ZFS_DELEG_PERM_CLONE, cr)) != 0) 10944445fffbSMatthew Ahrens return (error); 1095fa9e4066Sahrens 1096ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 1097ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 1098ecd6cf80Smarks return (error); 1099ecd6cf80Smarks 11004445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(parentname, 11014445fffbSMatthew Ahrens ZFS_DELEG_PERM_MOUNT, cr)); 1102fa9e4066Sahrens } 1103fa9e4066Sahrens 1104fa9e4066Sahrens /* 1105fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 1106fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 1107fa9e4066Sahrens */ 1108fa9e4066Sahrens /* ARGSUSED */ 1109fa9e4066Sahrens static int 11104445fffbSMatthew Ahrens zfs_secpolicy_config(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1111fa9e4066Sahrens { 1112fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 1113be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 1114fa9e4066Sahrens 1115fa9e4066Sahrens return (0); 1116fa9e4066Sahrens } 1117fa9e4066Sahrens 111899d5e173STim Haley /* 111999d5e173STim Haley * Policy for object to name lookups. 112099d5e173STim Haley */ 112199d5e173STim Haley /* ARGSUSED */ 112299d5e173STim Haley static int 11234445fffbSMatthew Ahrens zfs_secpolicy_diff(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 112499d5e173STim Haley { 112599d5e173STim Haley int error; 112699d5e173STim Haley 112799d5e173STim Haley if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0) 112899d5e173STim Haley return (0); 112999d5e173STim Haley 113099d5e173STim Haley error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr); 113199d5e173STim Haley return (error); 113299d5e173STim Haley } 113399d5e173STim Haley 1134ea8dc4b6Seschrock /* 1135ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 1136ea8dc4b6Seschrock */ 1137ea8dc4b6Seschrock /* ARGSUSED */ 1138ea8dc4b6Seschrock static int 11394445fffbSMatthew Ahrens zfs_secpolicy_inject(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1140ea8dc4b6Seschrock { 1141ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 1142ea8dc4b6Seschrock } 1143ea8dc4b6Seschrock 11444445fffbSMatthew Ahrens /* ARGSUSED */ 1145e45ce728Sahrens static int 11464445fffbSMatthew Ahrens zfs_secpolicy_inherit_prop(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1147e45ce728Sahrens { 1148e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 1149e45ce728Sahrens 1150990b4856Slling if (prop == ZPROP_INVAL) { 1151e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 1152be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1153e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 1154e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 1155e45ce728Sahrens } else { 115692241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 115792241e0bSTom Erickson NULL, cr)); 1158e45ce728Sahrens } 1159e45ce728Sahrens } 1160e45ce728Sahrens 116114843421SMatthew Ahrens static int 11624445fffbSMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 116314843421SMatthew Ahrens { 11644445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 116514843421SMatthew Ahrens if (err) 116614843421SMatthew Ahrens return (err); 116714843421SMatthew Ahrens 116814843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1169be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 117014843421SMatthew Ahrens 117114843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 117214843421SMatthew Ahrens /* 117314843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 117414843421SMatthew Ahrens * themself, allow it. 117514843421SMatthew Ahrens */ 117614843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 117714843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 117814843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 117914843421SMatthew Ahrens return (0); 118014843421SMatthew Ahrens } else { 118114843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 118214843421SMatthew Ahrens return (0); 118314843421SMatthew Ahrens } 118414843421SMatthew Ahrens } 118514843421SMatthew Ahrens 118614843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 118714843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 118814843421SMatthew Ahrens } 118914843421SMatthew Ahrens 119014843421SMatthew Ahrens static int 11914445fffbSMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 119214843421SMatthew Ahrens { 11934445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 119414843421SMatthew Ahrens if (err) 119514843421SMatthew Ahrens return (err); 119614843421SMatthew Ahrens 119714843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1198be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 119914843421SMatthew Ahrens 120014843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 120114843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 120214843421SMatthew Ahrens } 120314843421SMatthew Ahrens 12044445fffbSMatthew Ahrens /* ARGSUSED */ 120514843421SMatthew Ahrens static int 12064445fffbSMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 120714843421SMatthew Ahrens { 120892241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 120992241e0bSTom Erickson NULL, cr)); 121014843421SMatthew Ahrens } 121114843421SMatthew Ahrens 12124445fffbSMatthew Ahrens /* ARGSUSED */ 1213842727c2SChris Kirby static int 12144445fffbSMatthew Ahrens zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1215842727c2SChris Kirby { 12163b2aab18SMatthew Ahrens nvpair_t *pair; 12173b2aab18SMatthew Ahrens nvlist_t *holds; 12183b2aab18SMatthew Ahrens int error; 12193b2aab18SMatthew Ahrens 12203b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(innvl, "holds", &holds); 12213b2aab18SMatthew Ahrens if (error != 0) 1222be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 12233b2aab18SMatthew Ahrens 12243b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 12253b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(holds, pair)) { 12263b2aab18SMatthew Ahrens char fsname[MAXNAMELEN]; 12273b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12283b2aab18SMatthew Ahrens if (error != 0) 12293b2aab18SMatthew Ahrens return (error); 12303b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12313b2aab18SMatthew Ahrens ZFS_DELEG_PERM_HOLD, cr); 12323b2aab18SMatthew Ahrens if (error != 0) 12333b2aab18SMatthew Ahrens return (error); 12343b2aab18SMatthew Ahrens } 12353b2aab18SMatthew Ahrens return (0); 1236842727c2SChris Kirby } 1237842727c2SChris Kirby 12384445fffbSMatthew Ahrens /* ARGSUSED */ 1239842727c2SChris Kirby static int 12404445fffbSMatthew Ahrens zfs_secpolicy_release(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1241842727c2SChris Kirby { 12423b2aab18SMatthew Ahrens nvpair_t *pair; 12433b2aab18SMatthew Ahrens int error; 12443b2aab18SMatthew Ahrens 12453b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 12463b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(innvl, pair)) { 12473b2aab18SMatthew Ahrens char fsname[MAXNAMELEN]; 12483b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12493b2aab18SMatthew Ahrens if (error != 0) 12503b2aab18SMatthew Ahrens return (error); 12513b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12523b2aab18SMatthew Ahrens ZFS_DELEG_PERM_RELEASE, cr); 12533b2aab18SMatthew Ahrens if (error != 0) 12543b2aab18SMatthew Ahrens return (error); 12553b2aab18SMatthew Ahrens } 12563b2aab18SMatthew Ahrens return (0); 1257842727c2SChris Kirby } 1258842727c2SChris Kirby 125999d5e173STim Haley /* 126099d5e173STim Haley * Policy for allowing temporary snapshots to be taken or released 126199d5e173STim Haley */ 126299d5e173STim Haley static int 12634445fffbSMatthew Ahrens zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 126499d5e173STim Haley { 126599d5e173STim Haley /* 126699d5e173STim Haley * A temporary snapshot is the same as a snapshot, 126799d5e173STim Haley * hold, destroy and release all rolled into one. 126899d5e173STim Haley * Delegated diff alone is sufficient that we allow this. 126999d5e173STim Haley */ 127099d5e173STim Haley int error; 127199d5e173STim Haley 127299d5e173STim Haley if ((error = zfs_secpolicy_write_perms(zc->zc_name, 127399d5e173STim Haley ZFS_DELEG_PERM_DIFF, cr)) == 0) 127499d5e173STim Haley return (0); 127599d5e173STim Haley 12764445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); 12773b2aab18SMatthew Ahrens if (error == 0) 12784445fffbSMatthew Ahrens error = zfs_secpolicy_hold(zc, innvl, cr); 12793b2aab18SMatthew Ahrens if (error == 0) 12804445fffbSMatthew Ahrens error = zfs_secpolicy_release(zc, innvl, cr); 12813b2aab18SMatthew Ahrens if (error == 0) 12824445fffbSMatthew Ahrens error = zfs_secpolicy_destroy(zc, innvl, cr); 128399d5e173STim Haley return (error); 128499d5e173STim Haley } 128599d5e173STim Haley 1286fa9e4066Sahrens /* 1287fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 1288fa9e4066Sahrens */ 1289fa9e4066Sahrens static int 1290478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 1291fa9e4066Sahrens { 1292fa9e4066Sahrens char *packed; 1293fa9e4066Sahrens int error; 1294990b4856Slling nvlist_t *list = NULL; 1295fa9e4066Sahrens 1296fa9e4066Sahrens /* 1297e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 1298fa9e4066Sahrens */ 1299990b4856Slling if (size == 0) 1300be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1301fa9e4066Sahrens 1302fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 1303fa9e4066Sahrens 1304478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 1305478ed9adSEric Taylor iflag)) != 0) { 1306fa9e4066Sahrens kmem_free(packed, size); 1307c71c00bbSRichard Yao return (SET_ERROR(EFAULT)); 1308fa9e4066Sahrens } 1309fa9e4066Sahrens 1310990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 1311fa9e4066Sahrens kmem_free(packed, size); 1312fa9e4066Sahrens return (error); 1313fa9e4066Sahrens } 1314fa9e4066Sahrens 1315fa9e4066Sahrens kmem_free(packed, size); 1316fa9e4066Sahrens 1317990b4856Slling *nvp = list; 1318fa9e4066Sahrens return (0); 1319fa9e4066Sahrens } 1320fa9e4066Sahrens 13214445fffbSMatthew Ahrens /* 13224445fffbSMatthew Ahrens * Reduce the size of this nvlist until it can be serialized in 'max' bytes. 13234445fffbSMatthew Ahrens * Entries will be removed from the end of the nvlist, and one int32 entry 13244445fffbSMatthew Ahrens * named "N_MORE_ERRORS" will be added indicating how many entries were 13254445fffbSMatthew Ahrens * removed. 13264445fffbSMatthew Ahrens */ 132792241e0bSTom Erickson static int 13284445fffbSMatthew Ahrens nvlist_smush(nvlist_t *errors, size_t max) 132992241e0bSTom Erickson { 133092241e0bSTom Erickson size_t size; 133192241e0bSTom Erickson 13324445fffbSMatthew Ahrens size = fnvlist_size(errors); 133392241e0bSTom Erickson 13344445fffbSMatthew Ahrens if (size > max) { 133592241e0bSTom Erickson nvpair_t *more_errors; 133692241e0bSTom Erickson int n = 0; 133792241e0bSTom Erickson 13384445fffbSMatthew Ahrens if (max < 1024) 1339be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 134092241e0bSTom Erickson 13414445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, 0); 13424445fffbSMatthew Ahrens more_errors = nvlist_prev_nvpair(errors, NULL); 134392241e0bSTom Erickson 134492241e0bSTom Erickson do { 13454445fffbSMatthew Ahrens nvpair_t *pair = nvlist_prev_nvpair(errors, 134692241e0bSTom Erickson more_errors); 13474445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, pair); 134892241e0bSTom Erickson n++; 13494445fffbSMatthew Ahrens size = fnvlist_size(errors); 13504445fffbSMatthew Ahrens } while (size > max); 135192241e0bSTom Erickson 13524445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, more_errors); 13534445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, n); 13544445fffbSMatthew Ahrens ASSERT3U(fnvlist_size(errors), <=, max); 135592241e0bSTom Erickson } 135692241e0bSTom Erickson 135792241e0bSTom Erickson return (0); 135892241e0bSTom Erickson } 135992241e0bSTom Erickson 1360e9dbad6fSeschrock static int 1361e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 1362e9dbad6fSeschrock { 1363e9dbad6fSeschrock char *packed = NULL; 13646e27f868SSam Falkner int error = 0; 1365e9dbad6fSeschrock size_t size; 1366e9dbad6fSeschrock 13674445fffbSMatthew Ahrens size = fnvlist_size(nvl); 1368e9dbad6fSeschrock 1369e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 1370be6fd75aSMatthew Ahrens error = SET_ERROR(ENOMEM); 1371e9dbad6fSeschrock } else { 13724445fffbSMatthew Ahrens packed = fnvlist_pack(nvl, &size); 13736e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 13746e27f868SSam Falkner size, zc->zc_iflags) != 0) 1375be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 13764445fffbSMatthew Ahrens fnvlist_pack_free(packed, size); 1377e9dbad6fSeschrock } 1378e9dbad6fSeschrock 1379e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 13804445fffbSMatthew Ahrens zc->zc_nvlist_dst_filled = B_TRUE; 1381e9dbad6fSeschrock return (error); 1382e9dbad6fSeschrock } 1383e9dbad6fSeschrock 138414843421SMatthew Ahrens static int 1385af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 138614843421SMatthew Ahrens { 138714843421SMatthew Ahrens objset_t *os; 138814843421SMatthew Ahrens int error; 138914843421SMatthew Ahrens 1390503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 13913b2aab18SMatthew Ahrens if (error != 0) 139214843421SMatthew Ahrens return (error); 1393503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1394503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1395be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1396503ad85cSMatthew Ahrens } 139714843421SMatthew Ahrens 1398503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 1399af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 1400af4c679fSSean McEnroe if (*zfvp) { 1401af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 140214843421SMatthew Ahrens } else { 1403be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 140414843421SMatthew Ahrens } 1405503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 1406503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 140714843421SMatthew Ahrens return (error); 140814843421SMatthew Ahrens } 140914843421SMatthew Ahrens 141014843421SMatthew Ahrens /* 141114843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 141214843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 1413ad135b5dSChristopher Siden * If 'writer' is set, the z_teardown_lock will be held for RW_WRITER, 1414ad135b5dSChristopher Siden * which prevents all vnode ops from running. 141514843421SMatthew Ahrens */ 141614843421SMatthew Ahrens static int 14171412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) 141814843421SMatthew Ahrens { 141914843421SMatthew Ahrens int error = 0; 142014843421SMatthew Ahrens 1421af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1422af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 142314843421SMatthew Ahrens if (error == 0) { 1424c9030f6cSAlexander Motin rrm_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : 14251412a1a2SMark Shellenbaum RW_READER, tag); 1426af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 142714843421SMatthew Ahrens /* 142814843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 142914843421SMatthew Ahrens * thread should be just about to disassociate the 143014843421SMatthew Ahrens * objset from the zfsvfs. 143114843421SMatthew Ahrens */ 1432c9030f6cSAlexander Motin rrm_exit(&(*zfvp)->z_teardown_lock, tag); 1433be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 143414843421SMatthew Ahrens } 143514843421SMatthew Ahrens } 143614843421SMatthew Ahrens return (error); 143714843421SMatthew Ahrens } 143814843421SMatthew Ahrens 143914843421SMatthew Ahrens static void 144014843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 144114843421SMatthew Ahrens { 1442c9030f6cSAlexander Motin rrm_exit(&zfsvfs->z_teardown_lock, tag); 144314843421SMatthew Ahrens 144414843421SMatthew Ahrens if (zfsvfs->z_vfs) { 144514843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 144614843421SMatthew Ahrens } else { 1447503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 144814843421SMatthew Ahrens zfsvfs_free(zfsvfs); 144914843421SMatthew Ahrens } 145014843421SMatthew Ahrens } 145114843421SMatthew Ahrens 1452fa9e4066Sahrens static int 1453fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1454fa9e4066Sahrens { 1455fa9e4066Sahrens int error; 1456990b4856Slling nvlist_t *config, *props = NULL; 14570a48a24eStimh nvlist_t *rootprops = NULL; 14580a48a24eStimh nvlist_t *zplprops = NULL; 1459fa9e4066Sahrens 1460990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1461478ed9adSEric Taylor zc->zc_iflags, &config)) 1462fa9e4066Sahrens return (error); 14632a6b87f0Sek 1464990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1465478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1466478ed9adSEric Taylor zc->zc_iflags, &props))) { 1467990b4856Slling nvlist_free(config); 1468990b4856Slling return (error); 1469990b4856Slling } 1470990b4856Slling 14710a48a24eStimh if (props) { 14720a48a24eStimh nvlist_t *nvl = NULL; 14730a48a24eStimh uint64_t version = SPA_VERSION; 14740a48a24eStimh 14750a48a24eStimh (void) nvlist_lookup_uint64(props, 14760a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 1477ad135b5dSChristopher Siden if (!SPA_VERSION_IS_SUPPORTED(version)) { 1478be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 14790a48a24eStimh goto pool_props_bad; 14800a48a24eStimh } 14810a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 14820a48a24eStimh if (nvl) { 14830a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 14840a48a24eStimh if (error != 0) { 14850a48a24eStimh nvlist_free(config); 14860a48a24eStimh nvlist_free(props); 14870a48a24eStimh return (error); 14880a48a24eStimh } 14890a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 14900a48a24eStimh } 14910a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 14920a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 14930a48a24eStimh zplprops, NULL); 14943b2aab18SMatthew Ahrens if (error != 0) 14950a48a24eStimh goto pool_props_bad; 14960a48a24eStimh } 14970a48a24eStimh 14984445fffbSMatthew Ahrens error = spa_create(zc->zc_name, config, props, zplprops); 14990a48a24eStimh 15000a48a24eStimh /* 15010a48a24eStimh * Set the remaining root properties 15020a48a24eStimh */ 150392241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 150492241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 15050a48a24eStimh (void) spa_destroy(zc->zc_name); 1506fa9e4066Sahrens 15070a48a24eStimh pool_props_bad: 15080a48a24eStimh nvlist_free(rootprops); 15090a48a24eStimh nvlist_free(zplprops); 1510fa9e4066Sahrens nvlist_free(config); 15110a48a24eStimh nvlist_free(props); 1512990b4856Slling 1513fa9e4066Sahrens return (error); 1514fa9e4066Sahrens } 1515fa9e4066Sahrens 1516fa9e4066Sahrens static int 1517fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1518fa9e4066Sahrens { 1519ecd6cf80Smarks int error; 1520ecd6cf80Smarks zfs_log_history(zc); 1521ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1522681d9761SEric Taylor if (error == 0) 1523681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1524ecd6cf80Smarks return (error); 1525fa9e4066Sahrens } 1526fa9e4066Sahrens 1527fa9e4066Sahrens static int 1528fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1529fa9e4066Sahrens { 1530990b4856Slling nvlist_t *config, *props = NULL; 1531fa9e4066Sahrens uint64_t guid; 1532468c413aSTim Haley int error; 1533fa9e4066Sahrens 1534990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1535478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1536990b4856Slling return (error); 1537990b4856Slling 1538990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1539478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1540478ed9adSEric Taylor zc->zc_iflags, &props))) { 1541990b4856Slling nvlist_free(config); 1542fa9e4066Sahrens return (error); 1543990b4856Slling } 1544fa9e4066Sahrens 1545fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1546ea8dc4b6Seschrock guid != zc->zc_guid) 1547be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 1548fa9e4066Sahrens else 15494b964adaSGeorge Wilson error = spa_import(zc->zc_name, config, props, zc->zc_cookie); 1550fa9e4066Sahrens 15514b964adaSGeorge Wilson if (zc->zc_nvlist_dst != 0) { 15524b964adaSGeorge Wilson int err; 15534b964adaSGeorge Wilson 15544b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 15554b964adaSGeorge Wilson error = err; 15564b964adaSGeorge Wilson } 1557468c413aSTim Haley 1558fa9e4066Sahrens nvlist_free(config); 1559fa9e4066Sahrens 1560990b4856Slling if (props) 1561990b4856Slling nvlist_free(props); 1562990b4856Slling 1563fa9e4066Sahrens return (error); 1564fa9e4066Sahrens } 1565fa9e4066Sahrens 1566fa9e4066Sahrens static int 1567fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1568fa9e4066Sahrens { 1569ecd6cf80Smarks int error; 157089a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1571394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 157289a89ebfSlling 1573ecd6cf80Smarks zfs_log_history(zc); 1574394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1575681d9761SEric Taylor if (error == 0) 1576681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1577ecd6cf80Smarks return (error); 1578fa9e4066Sahrens } 1579fa9e4066Sahrens 1580fa9e4066Sahrens static int 1581fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1582fa9e4066Sahrens { 1583fa9e4066Sahrens nvlist_t *configs; 1584fa9e4066Sahrens int error; 1585fa9e4066Sahrens 1586fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1587be6fd75aSMatthew Ahrens return (SET_ERROR(EEXIST)); 1588fa9e4066Sahrens 1589e9dbad6fSeschrock error = put_nvlist(zc, configs); 1590fa9e4066Sahrens 1591fa9e4066Sahrens nvlist_free(configs); 1592fa9e4066Sahrens 1593fa9e4066Sahrens return (error); 1594fa9e4066Sahrens } 1595fa9e4066Sahrens 1596ad135b5dSChristopher Siden /* 1597ad135b5dSChristopher Siden * inputs: 1598ad135b5dSChristopher Siden * zc_name name of the pool 1599ad135b5dSChristopher Siden * 1600ad135b5dSChristopher Siden * outputs: 1601ad135b5dSChristopher Siden * zc_cookie real errno 1602ad135b5dSChristopher Siden * zc_nvlist_dst config nvlist 1603ad135b5dSChristopher Siden * zc_nvlist_dst_size size of config nvlist 1604ad135b5dSChristopher Siden */ 1605fa9e4066Sahrens static int 1606fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1607fa9e4066Sahrens { 1608fa9e4066Sahrens nvlist_t *config; 1609fa9e4066Sahrens int error; 1610ea8dc4b6Seschrock int ret = 0; 1611fa9e4066Sahrens 1612e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1613e9dbad6fSeschrock sizeof (zc->zc_value)); 1614fa9e4066Sahrens 1615fa9e4066Sahrens if (config != NULL) { 1616e9dbad6fSeschrock ret = put_nvlist(zc, config); 1617fa9e4066Sahrens nvlist_free(config); 1618ea8dc4b6Seschrock 1619ea8dc4b6Seschrock /* 1620ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1621ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1622ea8dc4b6Seschrock * in 'zc_cookie'. 1623ea8dc4b6Seschrock */ 1624ea8dc4b6Seschrock zc->zc_cookie = error; 1625fa9e4066Sahrens } else { 1626ea8dc4b6Seschrock ret = error; 1627fa9e4066Sahrens } 1628fa9e4066Sahrens 1629ea8dc4b6Seschrock return (ret); 1630fa9e4066Sahrens } 1631fa9e4066Sahrens 1632fa9e4066Sahrens /* 1633fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1634fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1635fa9e4066Sahrens */ 1636fa9e4066Sahrens static int 1637fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1638fa9e4066Sahrens { 1639fa9e4066Sahrens nvlist_t *tryconfig, *config; 1640fa9e4066Sahrens int error; 1641fa9e4066Sahrens 1642990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1643478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1644fa9e4066Sahrens return (error); 1645fa9e4066Sahrens 1646fa9e4066Sahrens config = spa_tryimport(tryconfig); 1647fa9e4066Sahrens 1648fa9e4066Sahrens nvlist_free(tryconfig); 1649fa9e4066Sahrens 1650fa9e4066Sahrens if (config == NULL) 1651be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1652fa9e4066Sahrens 1653e9dbad6fSeschrock error = put_nvlist(zc, config); 1654fa9e4066Sahrens nvlist_free(config); 1655fa9e4066Sahrens 1656fa9e4066Sahrens return (error); 1657fa9e4066Sahrens } 1658fa9e4066Sahrens 16593f9d6ad7SLin Ling /* 16603f9d6ad7SLin Ling * inputs: 16613f9d6ad7SLin Ling * zc_name name of the pool 16623f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 16633f9d6ad7SLin Ling */ 1664fa9e4066Sahrens static int 16653f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1666fa9e4066Sahrens { 1667fa9e4066Sahrens spa_t *spa; 1668fa9e4066Sahrens int error; 1669fa9e4066Sahrens 167006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 167106eeb2adSek return (error); 167206eeb2adSek 16733f9d6ad7SLin Ling if (zc->zc_cookie == POOL_SCAN_NONE) 16743f9d6ad7SLin Ling error = spa_scan_stop(spa); 16753f9d6ad7SLin Ling else 16763f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 167706eeb2adSek 167806eeb2adSek spa_close(spa, FTAG); 167906eeb2adSek 1680fa9e4066Sahrens return (error); 1681fa9e4066Sahrens } 1682fa9e4066Sahrens 1683fa9e4066Sahrens static int 1684fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1685fa9e4066Sahrens { 1686fa9e4066Sahrens spa_t *spa; 1687fa9e4066Sahrens int error; 1688fa9e4066Sahrens 1689fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1690fa9e4066Sahrens if (error == 0) { 1691fa9e4066Sahrens spa_freeze(spa); 1692fa9e4066Sahrens spa_close(spa, FTAG); 1693fa9e4066Sahrens } 1694fa9e4066Sahrens return (error); 1695fa9e4066Sahrens } 1696fa9e4066Sahrens 1697eaca9bbdSeschrock static int 1698eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1699eaca9bbdSeschrock { 1700eaca9bbdSeschrock spa_t *spa; 1701eaca9bbdSeschrock int error; 1702eaca9bbdSeschrock 170306eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 170406eeb2adSek return (error); 170506eeb2adSek 1706ad135b5dSChristopher Siden if (zc->zc_cookie < spa_version(spa) || 1707ad135b5dSChristopher Siden !SPA_VERSION_IS_SUPPORTED(zc->zc_cookie)) { 1708558d2d50Slling spa_close(spa, FTAG); 1709be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1710558d2d50Slling } 1711558d2d50Slling 1712990b4856Slling spa_upgrade(spa, zc->zc_cookie); 171306eeb2adSek spa_close(spa, FTAG); 171406eeb2adSek 171506eeb2adSek return (error); 171606eeb2adSek } 171706eeb2adSek 171806eeb2adSek static int 171906eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 172006eeb2adSek { 172106eeb2adSek spa_t *spa; 172206eeb2adSek char *hist_buf; 172306eeb2adSek uint64_t size; 172406eeb2adSek int error; 172506eeb2adSek 172606eeb2adSek if ((size = zc->zc_history_len) == 0) 1727be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 172806eeb2adSek 172906eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 173006eeb2adSek return (error); 173106eeb2adSek 1732e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1733d7306b64Sek spa_close(spa, FTAG); 1734be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 1735d7306b64Sek } 1736d7306b64Sek 173706eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 173806eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 173906eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1740478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1741478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1742478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 174306eeb2adSek } 174406eeb2adSek 174506eeb2adSek spa_close(spa, FTAG); 174606eeb2adSek kmem_free(hist_buf, size); 174706eeb2adSek return (error); 174806eeb2adSek } 174906eeb2adSek 1750e9103aaeSGarrett D'Amore static int 1751e9103aaeSGarrett D'Amore zfs_ioc_pool_reguid(zfs_cmd_t *zc) 1752e9103aaeSGarrett D'Amore { 1753e9103aaeSGarrett D'Amore spa_t *spa; 1754e9103aaeSGarrett D'Amore int error; 1755e9103aaeSGarrett D'Amore 1756e9103aaeSGarrett D'Amore error = spa_open(zc->zc_name, &spa, FTAG); 1757e9103aaeSGarrett D'Amore if (error == 0) { 1758e9103aaeSGarrett D'Amore error = spa_change_guid(spa); 1759e9103aaeSGarrett D'Amore spa_close(spa, FTAG); 1760e9103aaeSGarrett D'Amore } 1761e9103aaeSGarrett D'Amore return (error); 1762e9103aaeSGarrett D'Amore } 1763e9103aaeSGarrett D'Amore 176455434c77Sek static int 176555434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 176655434c77Sek { 17673b2aab18SMatthew Ahrens return (dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)); 176855434c77Sek } 176955434c77Sek 1770503ad85cSMatthew Ahrens /* 1771503ad85cSMatthew Ahrens * inputs: 1772503ad85cSMatthew Ahrens * zc_name name of filesystem 1773503ad85cSMatthew Ahrens * zc_obj object to find 1774503ad85cSMatthew Ahrens * 1775503ad85cSMatthew Ahrens * outputs: 1776503ad85cSMatthew Ahrens * zc_value name of object 1777503ad85cSMatthew Ahrens */ 177855434c77Sek static int 177955434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 178055434c77Sek { 1781503ad85cSMatthew Ahrens objset_t *os; 178255434c77Sek int error; 178355434c77Sek 1784503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1785503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 178655434c77Sek return (error); 1787503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1788503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1789be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1790503ad85cSMatthew Ahrens } 1791503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 179255434c77Sek sizeof (zc->zc_value)); 1793503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 179455434c77Sek 179555434c77Sek return (error); 179655434c77Sek } 179755434c77Sek 179899d5e173STim Haley /* 179999d5e173STim Haley * inputs: 180099d5e173STim Haley * zc_name name of filesystem 180199d5e173STim Haley * zc_obj object to find 180299d5e173STim Haley * 180399d5e173STim Haley * outputs: 180499d5e173STim Haley * zc_stat stats on object 180599d5e173STim Haley * zc_value path to object 180699d5e173STim Haley */ 180799d5e173STim Haley static int 180899d5e173STim Haley zfs_ioc_obj_to_stats(zfs_cmd_t *zc) 180999d5e173STim Haley { 181099d5e173STim Haley objset_t *os; 181199d5e173STim Haley int error; 181299d5e173STim Haley 181399d5e173STim Haley /* XXX reading from objset not owned */ 181499d5e173STim Haley if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 181599d5e173STim Haley return (error); 181699d5e173STim Haley if (dmu_objset_type(os) != DMU_OST_ZFS) { 181799d5e173STim Haley dmu_objset_rele(os, FTAG); 1818be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 181999d5e173STim Haley } 182099d5e173STim Haley error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value, 182199d5e173STim Haley sizeof (zc->zc_value)); 182299d5e173STim Haley dmu_objset_rele(os, FTAG); 182399d5e173STim Haley 182499d5e173STim Haley return (error); 182599d5e173STim Haley } 182699d5e173STim Haley 1827fa9e4066Sahrens static int 1828fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1829fa9e4066Sahrens { 1830fa9e4066Sahrens spa_t *spa; 1831fa9e4066Sahrens int error; 1832e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1833e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1834fa9e4066Sahrens 1835fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1836fa9e4066Sahrens if (error != 0) 1837fa9e4066Sahrens return (error); 1838fa9e4066Sahrens 1839fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1840478ed9adSEric Taylor zc->zc_iflags, &config); 1841fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1842fa94a07fSbrendan &l2cache, &nl2cache); 1843fa94a07fSbrendan 1844e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1845e7cbe64fSgw &spares, &nspares); 1846e7cbe64fSgw 1847b1b8ab34Slling /* 1848b1b8ab34Slling * A root pool with concatenated devices is not supported. 1849e7cbe64fSgw * Thus, can not add a device to a root pool. 1850e7cbe64fSgw * 1851e7cbe64fSgw * Intent log device can not be added to a rootpool because 1852e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1853e7cbe64fSgw * can not be accessed during the mountroot time. 1854e7cbe64fSgw * 1855e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1856b1b8ab34Slling */ 1857b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 18581195e687SMark J Musante nvlist_free(config); 1859b1b8ab34Slling spa_close(spa, FTAG); 1860be6fd75aSMatthew Ahrens return (SET_ERROR(EDOM)); 1861b1b8ab34Slling } 1862b1b8ab34Slling 1863fa94a07fSbrendan if (error == 0) { 1864fa9e4066Sahrens error = spa_vdev_add(spa, config); 1865fa9e4066Sahrens nvlist_free(config); 1866fa9e4066Sahrens } 1867fa9e4066Sahrens spa_close(spa, FTAG); 1868fa9e4066Sahrens return (error); 1869fa9e4066Sahrens } 1870fa9e4066Sahrens 18713f9d6ad7SLin Ling /* 18723f9d6ad7SLin Ling * inputs: 18733f9d6ad7SLin Ling * zc_name name of the pool 18743f9d6ad7SLin Ling * zc_nvlist_conf nvlist of devices to remove 18753f9d6ad7SLin Ling * zc_cookie to stop the remove? 18763f9d6ad7SLin Ling */ 1877fa9e4066Sahrens static int 1878fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1879fa9e4066Sahrens { 188099653d4eSeschrock spa_t *spa; 188199653d4eSeschrock int error; 188299653d4eSeschrock 188399653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 188499653d4eSeschrock if (error != 0) 188599653d4eSeschrock return (error); 188699653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 188799653d4eSeschrock spa_close(spa, FTAG); 188899653d4eSeschrock return (error); 1889fa9e4066Sahrens } 1890fa9e4066Sahrens 1891fa9e4066Sahrens static int 18923d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1893fa9e4066Sahrens { 1894fa9e4066Sahrens spa_t *spa; 1895fa9e4066Sahrens int error; 18963d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1897fa9e4066Sahrens 189806eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1899fa9e4066Sahrens return (error); 19003d7072f8Seschrock switch (zc->zc_cookie) { 19013d7072f8Seschrock case VDEV_STATE_ONLINE: 19023d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 19033d7072f8Seschrock break; 1904fa9e4066Sahrens 19053d7072f8Seschrock case VDEV_STATE_OFFLINE: 19063d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 19073d7072f8Seschrock break; 1908fa9e4066Sahrens 19093d7072f8Seschrock case VDEV_STATE_FAULTED: 1910069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1911069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1912069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1913069f55e2SEric Schrock 1914069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 19153d7072f8Seschrock break; 19163d7072f8Seschrock 19173d7072f8Seschrock case VDEV_STATE_DEGRADED: 1918069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1919069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1920069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1921069f55e2SEric Schrock 1922069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 19233d7072f8Seschrock break; 19243d7072f8Seschrock 19253d7072f8Seschrock default: 1926be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 19273d7072f8Seschrock } 19283d7072f8Seschrock zc->zc_cookie = newstate; 1929fa9e4066Sahrens spa_close(spa, FTAG); 1930fa9e4066Sahrens return (error); 1931fa9e4066Sahrens } 1932fa9e4066Sahrens 1933fa9e4066Sahrens static int 1934fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1935fa9e4066Sahrens { 1936fa9e4066Sahrens spa_t *spa; 1937fa9e4066Sahrens int replacing = zc->zc_cookie; 1938fa9e4066Sahrens nvlist_t *config; 1939fa9e4066Sahrens int error; 1940fa9e4066Sahrens 194106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1942fa9e4066Sahrens return (error); 1943fa9e4066Sahrens 1944990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1945478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1946ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1947fa9e4066Sahrens nvlist_free(config); 1948fa9e4066Sahrens } 1949fa9e4066Sahrens 1950fa9e4066Sahrens spa_close(spa, FTAG); 1951fa9e4066Sahrens return (error); 1952fa9e4066Sahrens } 1953fa9e4066Sahrens 1954fa9e4066Sahrens static int 1955fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1956fa9e4066Sahrens { 1957fa9e4066Sahrens spa_t *spa; 1958fa9e4066Sahrens int error; 1959fa9e4066Sahrens 196006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1961fa9e4066Sahrens return (error); 1962fa9e4066Sahrens 19638ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1964fa9e4066Sahrens 1965fa9e4066Sahrens spa_close(spa, FTAG); 1966fa9e4066Sahrens return (error); 1967fa9e4066Sahrens } 1968fa9e4066Sahrens 19691195e687SMark J Musante static int 19701195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 19711195e687SMark J Musante { 19721195e687SMark J Musante spa_t *spa; 19731195e687SMark J Musante nvlist_t *config, *props = NULL; 19741195e687SMark J Musante int error; 19751195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 19761195e687SMark J Musante 19771195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 19781195e687SMark J Musante return (error); 19791195e687SMark J Musante 19801195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 19811195e687SMark J Musante zc->zc_iflags, &config)) { 19821195e687SMark J Musante spa_close(spa, FTAG); 19831195e687SMark J Musante return (error); 19841195e687SMark J Musante } 19851195e687SMark J Musante 19861195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 19871195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 19881195e687SMark J Musante zc->zc_iflags, &props))) { 19891195e687SMark J Musante spa_close(spa, FTAG); 19901195e687SMark J Musante nvlist_free(config); 19911195e687SMark J Musante return (error); 19921195e687SMark J Musante } 19931195e687SMark J Musante 19941195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 19951195e687SMark J Musante 19961195e687SMark J Musante spa_close(spa, FTAG); 19971195e687SMark J Musante 19981195e687SMark J Musante nvlist_free(config); 19991195e687SMark J Musante nvlist_free(props); 20001195e687SMark J Musante 20011195e687SMark J Musante return (error); 20021195e687SMark J Musante } 20031195e687SMark J Musante 2004c67d9675Seschrock static int 2005c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 2006c67d9675Seschrock { 2007c67d9675Seschrock spa_t *spa; 2008e9dbad6fSeschrock char *path = zc->zc_value; 2009ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 2010c67d9675Seschrock int error; 2011c67d9675Seschrock 2012c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 2013c67d9675Seschrock if (error != 0) 2014c67d9675Seschrock return (error); 2015c67d9675Seschrock 2016c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 2017c67d9675Seschrock spa_close(spa, FTAG); 2018c67d9675Seschrock return (error); 2019c67d9675Seschrock } 2020c67d9675Seschrock 20216809eb4eSEric Schrock static int 20226809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 20236809eb4eSEric Schrock { 20246809eb4eSEric Schrock spa_t *spa; 20256809eb4eSEric Schrock char *fru = zc->zc_value; 20266809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 20276809eb4eSEric Schrock int error; 20286809eb4eSEric Schrock 20296809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 20306809eb4eSEric Schrock if (error != 0) 20316809eb4eSEric Schrock return (error); 20326809eb4eSEric Schrock 20336809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 20346809eb4eSEric Schrock spa_close(spa, FTAG); 20356809eb4eSEric Schrock return (error); 20366809eb4eSEric Schrock } 20376809eb4eSEric Schrock 2038fa9e4066Sahrens static int 2039a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) 2040fa9e4066Sahrens { 2041a7f53a56SChris Kirby int error = 0; 20427f7322feSeschrock nvlist_t *nv; 2043fa9e4066Sahrens 2044a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2045fa9e4066Sahrens 20465ad82045Snd if (zc->zc_nvlist_dst != 0 && 204792241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 2048a2eea2e1Sahrens dmu_objset_stats(os, nv); 2049432f72fdSahrens /* 2050bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 2051432f72fdSahrens * which we aren't supposed to do with a 2052745cd3c5Smaybee * DS_MODE_USER hold, because it could be 2053432f72fdSahrens * inconsistent. So this is a bit of a workaround... 2054503ad85cSMatthew Ahrens * XXX reading with out owning 2055432f72fdSahrens */ 205619b94df9SMatthew Ahrens if (!zc->zc_objset_stats.dds_inconsistent && 205719b94df9SMatthew Ahrens dmu_objset_type(os) == DMU_OST_ZVOL) { 205819b94df9SMatthew Ahrens error = zvol_get_stats(os, nv); 205919b94df9SMatthew Ahrens if (error == EIO) 206019b94df9SMatthew Ahrens return (error); 2061fb09f5aaSMadhav Suresh VERIFY0(error); 2062e7437265Sahrens } 2063e9dbad6fSeschrock error = put_nvlist(zc, nv); 20647f7322feSeschrock nvlist_free(nv); 20657f7322feSeschrock } 2066fa9e4066Sahrens 2067a7f53a56SChris Kirby return (error); 2068a7f53a56SChris Kirby } 2069a7f53a56SChris Kirby 2070a7f53a56SChris Kirby /* 2071a7f53a56SChris Kirby * inputs: 2072a7f53a56SChris Kirby * zc_name name of filesystem 2073a7f53a56SChris Kirby * zc_nvlist_dst_size size of buffer for property nvlist 2074a7f53a56SChris Kirby * 2075a7f53a56SChris Kirby * outputs: 2076a7f53a56SChris Kirby * zc_objset_stats stats 2077a7f53a56SChris Kirby * zc_nvlist_dst property nvlist 2078a7f53a56SChris Kirby * zc_nvlist_dst_size size of property nvlist 2079a7f53a56SChris Kirby */ 2080a7f53a56SChris Kirby static int 2081a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc) 2082a7f53a56SChris Kirby { 20833b2aab18SMatthew Ahrens objset_t *os; 2084a7f53a56SChris Kirby int error; 2085a7f53a56SChris Kirby 20863b2aab18SMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 20873b2aab18SMatthew Ahrens if (error == 0) { 20883b2aab18SMatthew Ahrens error = zfs_ioc_objset_stats_impl(zc, os); 20893b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 20903b2aab18SMatthew Ahrens } 2091a7f53a56SChris Kirby 2092fa9e4066Sahrens return (error); 2093fa9e4066Sahrens } 2094fa9e4066Sahrens 209592241e0bSTom Erickson /* 209692241e0bSTom Erickson * inputs: 209792241e0bSTom Erickson * zc_name name of filesystem 209892241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 209992241e0bSTom Erickson * 210092241e0bSTom Erickson * outputs: 210192241e0bSTom Erickson * zc_nvlist_dst received property nvlist 210292241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 210392241e0bSTom Erickson * 210492241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 210592241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 210692241e0bSTom Erickson * local property values. 210792241e0bSTom Erickson */ 210892241e0bSTom Erickson static int 210992241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 211092241e0bSTom Erickson { 21113b2aab18SMatthew Ahrens int error = 0; 211292241e0bSTom Erickson nvlist_t *nv; 211392241e0bSTom Erickson 211492241e0bSTom Erickson /* 211592241e0bSTom Erickson * Without this check, we would return local property values if the 211692241e0bSTom Erickson * caller has not already received properties on or after 211792241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 211892241e0bSTom Erickson */ 21193b2aab18SMatthew Ahrens if (!dsl_prop_get_hasrecvd(zc->zc_name)) 2120be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 212192241e0bSTom Erickson 212292241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 21233b2aab18SMatthew Ahrens (error = dsl_prop_get_received(zc->zc_name, &nv)) == 0) { 212492241e0bSTom Erickson error = put_nvlist(zc, nv); 212592241e0bSTom Erickson nvlist_free(nv); 212692241e0bSTom Erickson } 212792241e0bSTom Erickson 212892241e0bSTom Erickson return (error); 212992241e0bSTom Erickson } 213092241e0bSTom Erickson 2131de8267e0Stimh static int 2132de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 2133de8267e0Stimh { 2134de8267e0Stimh uint64_t value; 2135de8267e0Stimh int error; 2136de8267e0Stimh 2137de8267e0Stimh /* 2138de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 2139de8267e0Stimh * the default value (if there is one). 2140de8267e0Stimh */ 2141de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 2142de8267e0Stimh return (error); 2143de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 2144de8267e0Stimh return (0); 2145de8267e0Stimh } 2146de8267e0Stimh 21473cb34c60Sahrens /* 21483cb34c60Sahrens * inputs: 21493cb34c60Sahrens * zc_name name of filesystem 2150de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 21513cb34c60Sahrens * 21523cb34c60Sahrens * outputs: 2153de8267e0Stimh * zc_nvlist_dst zpl property nvlist 2154de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 21553cb34c60Sahrens */ 2156bd00f61bSrm static int 2157de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 2158bd00f61bSrm { 2159de8267e0Stimh objset_t *os; 2160de8267e0Stimh int err; 2161bd00f61bSrm 2162503ad85cSMatthew Ahrens /* XXX reading without owning */ 2163503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 2164de8267e0Stimh return (err); 2165bd00f61bSrm 2166bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2167bd00f61bSrm 2168bd00f61bSrm /* 2169de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 2170745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 2171745cd3c5Smaybee * hold, because it could be inconsistent. 2172bd00f61bSrm */ 2173de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 2174de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 2175de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 2176de8267e0Stimh nvlist_t *nv; 2177de8267e0Stimh 2178de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2179de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 2180de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 2181de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 2182de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 2183de8267e0Stimh err = put_nvlist(zc, nv); 2184de8267e0Stimh nvlist_free(nv); 2185de8267e0Stimh } else { 2186be6fd75aSMatthew Ahrens err = SET_ERROR(ENOENT); 2187de8267e0Stimh } 2188503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2189de8267e0Stimh return (err); 2190bd00f61bSrm } 2191bd00f61bSrm 219214843421SMatthew Ahrens static boolean_t 219314843421SMatthew Ahrens dataset_name_hidden(const char *name) 219414843421SMatthew Ahrens { 219514843421SMatthew Ahrens /* 219614843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 219714843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 219814843421SMatthew Ahrens * temporary datasets (which have a % in their name). 219914843421SMatthew Ahrens */ 220014843421SMatthew Ahrens if (strchr(name, '$') != NULL) 220114843421SMatthew Ahrens return (B_TRUE); 220214843421SMatthew Ahrens if (strchr(name, '%') != NULL) 220314843421SMatthew Ahrens return (B_TRUE); 220414843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 220514843421SMatthew Ahrens return (B_TRUE); 220614843421SMatthew Ahrens return (B_FALSE); 220714843421SMatthew Ahrens } 220814843421SMatthew Ahrens 2209de8267e0Stimh /* 2210de8267e0Stimh * inputs: 2211de8267e0Stimh * zc_name name of filesystem 2212de8267e0Stimh * zc_cookie zap cursor 2213de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 2214de8267e0Stimh * 2215de8267e0Stimh * outputs: 2216de8267e0Stimh * zc_name name of next filesystem 221714843421SMatthew Ahrens * zc_cookie zap cursor 2218de8267e0Stimh * zc_objset_stats stats 2219de8267e0Stimh * zc_nvlist_dst property nvlist 2220de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 2221de8267e0Stimh */ 2222fa9e4066Sahrens static int 2223fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 2224fa9e4066Sahrens { 222587e5029aSahrens objset_t *os; 2226fa9e4066Sahrens int error; 2227fa9e4066Sahrens char *p; 2228620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 2229fa9e4066Sahrens 2230620252bcSChris Kirby top: 2231503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 223287e5029aSahrens if (error == ENOENT) 2233be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 223487e5029aSahrens return (error); 2235fa9e4066Sahrens } 2236fa9e4066Sahrens 2237fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 2238fa9e4066Sahrens if (p == NULL || p[1] != '\0') 2239fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 2240fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 2241fa9e4066Sahrens 2242fa9e4066Sahrens do { 224387e5029aSahrens error = dmu_dir_list_next(os, 224487e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 224587e5029aSahrens NULL, &zc->zc_cookie); 2246fa9e4066Sahrens if (error == ENOENT) 2247be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 224819b94df9SMatthew Ahrens } while (error == 0 && dataset_name_hidden(zc->zc_name)); 2249503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2250fa9e4066Sahrens 2251681d9761SEric Taylor /* 2252681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 2253681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 2254681d9761SEric Taylor */ 2255620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 225687e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 2257620252bcSChris Kirby if (error == ENOENT) { 2258620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 2259620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 2260620252bcSChris Kirby goto top; 2261620252bcSChris Kirby } 2262620252bcSChris Kirby } 2263fa9e4066Sahrens return (error); 2264fa9e4066Sahrens } 2265fa9e4066Sahrens 22663cb34c60Sahrens /* 22673cb34c60Sahrens * inputs: 22683cb34c60Sahrens * zc_name name of filesystem 22693cb34c60Sahrens * zc_cookie zap cursor 22703cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 22713cb34c60Sahrens * 22723cb34c60Sahrens * outputs: 22733cb34c60Sahrens * zc_name name of next snapshot 22743cb34c60Sahrens * zc_objset_stats stats 22753cb34c60Sahrens * zc_nvlist_dst property nvlist 22763cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 22773cb34c60Sahrens */ 2278fa9e4066Sahrens static int 2279fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 2280fa9e4066Sahrens { 228187e5029aSahrens objset_t *os; 2282fa9e4066Sahrens int error; 2283fa9e4066Sahrens 2284503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 22853b2aab18SMatthew Ahrens if (error != 0) { 2286745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 22873b2aab18SMatthew Ahrens } 2288fa9e4066Sahrens 2289b81d61a6Slling /* 2290b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 2291b81d61a6Slling * so exit immediately. 2292b81d61a6Slling */ 2293b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 2294503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2295be6fd75aSMatthew Ahrens return (SET_ERROR(ESRCH)); 2296fa9e4066Sahrens } 2297fa9e4066Sahrens 229887e5029aSahrens error = dmu_snapshot_list_next(os, 229987e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 2300a7f53a56SChris Kirby zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, 2301a7f53a56SChris Kirby NULL); 2302a7f53a56SChris Kirby 2303620252bcSChris Kirby if (error == 0) { 2304a7f53a56SChris Kirby dsl_dataset_t *ds; 2305a7f53a56SChris Kirby dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; 2306a7f53a56SChris Kirby 2307a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); 23083b2aab18SMatthew Ahrens if (error == 0) { 2309a7f53a56SChris Kirby objset_t *ossnap; 2310a7f53a56SChris Kirby 2311a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &ossnap); 2312a7f53a56SChris Kirby if (error == 0) 2313a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, ossnap); 2314a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 2315620252bcSChris Kirby } 2316620252bcSChris Kirby } else if (error == ENOENT) { 2317be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 2318620252bcSChris Kirby } 2319fa9e4066Sahrens 2320a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 23213cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 23223b2aab18SMatthew Ahrens if (error != 0) 23233cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 2324fa9e4066Sahrens return (error); 2325fa9e4066Sahrens } 2326fa9e4066Sahrens 232792241e0bSTom Erickson static int 232892241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 2329fa9e4066Sahrens { 233092241e0bSTom Erickson const char *propname = nvpair_name(pair); 233192241e0bSTom Erickson uint64_t *valary; 233292241e0bSTom Erickson unsigned int vallen; 233392241e0bSTom Erickson const char *domain; 2334eeb85002STim Haley char *dash; 233592241e0bSTom Erickson zfs_userquota_prop_t type; 233692241e0bSTom Erickson uint64_t rid; 233792241e0bSTom Erickson uint64_t quota; 233892241e0bSTom Erickson zfsvfs_t *zfsvfs; 233992241e0bSTom Erickson int err; 234092241e0bSTom Erickson 234192241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 234292241e0bSTom Erickson nvlist_t *attrs; 234392241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2344eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2345eeb85002STim Haley &pair) != 0) 2346be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 234792241e0bSTom Erickson } 2348e9dbad6fSeschrock 2349ecd6cf80Smarks /* 2350eeb85002STim Haley * A correctly constructed propname is encoded as 235192241e0bSTom Erickson * userquota@<rid>-<domain>. 2352ecd6cf80Smarks */ 2353eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 2354eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 2355eeb85002STim Haley vallen != 3) 2356be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2357eeb85002STim Haley 2358eeb85002STim Haley domain = dash + 1; 2359eeb85002STim Haley type = valary[0]; 2360eeb85002STim Haley rid = valary[1]; 2361eeb85002STim Haley quota = valary[2]; 2362e9dbad6fSeschrock 23631412a1a2SMark Shellenbaum err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); 236492241e0bSTom Erickson if (err == 0) { 236592241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 236692241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 236792241e0bSTom Erickson } 2368e9dbad6fSeschrock 236992241e0bSTom Erickson return (err); 237092241e0bSTom Erickson } 237114843421SMatthew Ahrens 237292241e0bSTom Erickson /* 237392241e0bSTom Erickson * If the named property is one that has a special function to set its value, 237492241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 237592241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 237692241e0bSTom Erickson * 2377eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 237892241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 237992241e0bSTom Erickson */ 238092241e0bSTom Erickson static int 238192241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 238292241e0bSTom Erickson nvpair_t *pair) 238392241e0bSTom Erickson { 238492241e0bSTom Erickson const char *propname = nvpair_name(pair); 238592241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 238692241e0bSTom Erickson uint64_t intval; 2387b5152584SMatthew Ahrens int err = -1; 2388fa9e4066Sahrens 238992241e0bSTom Erickson if (prop == ZPROP_INVAL) { 239092241e0bSTom Erickson if (zfs_prop_userquota(propname)) 239192241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 239292241e0bSTom Erickson return (-1); 239392241e0bSTom Erickson } 239414843421SMatthew Ahrens 239592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 239692241e0bSTom Erickson nvlist_t *attrs; 239792241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 239892241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 239992241e0bSTom Erickson &pair) == 0); 240092241e0bSTom Erickson } 2401db870a07Sahrens 240292241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 240392241e0bSTom Erickson return (-1); 2404b24ab676SJeff Bonwick 240592241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 240640feaa91Sahrens 240792241e0bSTom Erickson switch (prop) { 240892241e0bSTom Erickson case ZFS_PROP_QUOTA: 240992241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 241092241e0bSTom Erickson break; 241192241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 24123b2aab18SMatthew Ahrens err = dsl_dataset_set_refquota(dsname, source, intval); 241392241e0bSTom Erickson break; 2414a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 2415a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 2416a2afb611SJerry Jelinek if (intval == UINT64_MAX) { 2417a2afb611SJerry Jelinek /* clearing the limit, just do it */ 2418a2afb611SJerry Jelinek err = 0; 2419a2afb611SJerry Jelinek } else { 2420a2afb611SJerry Jelinek err = dsl_dir_activate_fs_ss_limit(dsname); 2421a2afb611SJerry Jelinek } 2422a2afb611SJerry Jelinek /* 2423a2afb611SJerry Jelinek * Set err to -1 to force the zfs_set_prop_nvlist code down the 2424a2afb611SJerry Jelinek * default path to set the value in the nvlist. 2425a2afb611SJerry Jelinek */ 2426a2afb611SJerry Jelinek if (err == 0) 2427a2afb611SJerry Jelinek err = -1; 2428a2afb611SJerry Jelinek break; 242992241e0bSTom Erickson case ZFS_PROP_RESERVATION: 243092241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 243192241e0bSTom Erickson break; 243292241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 24333b2aab18SMatthew Ahrens err = dsl_dataset_set_refreservation(dsname, source, intval); 243492241e0bSTom Erickson break; 243592241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 2436c61ea566SGeorge Wilson err = zvol_set_volsize(dsname, intval); 243792241e0bSTom Erickson break; 243892241e0bSTom Erickson case ZFS_PROP_VERSION: 243992241e0bSTom Erickson { 244092241e0bSTom Erickson zfsvfs_t *zfsvfs; 24419e6eda55Smarks 24421412a1a2SMark Shellenbaum if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) 2443b24ab676SJeff Bonwick break; 2444b24ab676SJeff Bonwick 244592241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 244692241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 2447d0f3f37eSMark Shellenbaum 244892241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 2449b16da2e2SGeorge Wilson zfs_cmd_t *zc; 2450b16da2e2SGeorge Wilson 2451b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 2452b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 2453b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 2454b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 245540feaa91Sahrens } 245692241e0bSTom Erickson break; 2457ecd6cf80Smarks } 245892241e0bSTom Erickson default: 245992241e0bSTom Erickson err = -1; 246092241e0bSTom Erickson } 2461e9dbad6fSeschrock 246292241e0bSTom Erickson return (err); 246392241e0bSTom Erickson } 2464a9799022Sck 246592241e0bSTom Erickson /* 246692241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 24674445fffbSMatthew Ahrens * it continues to set as many as it can and returns the last error 24684445fffbSMatthew Ahrens * encountered. If the caller provides a non-NULL errlist, it will be filled in 24694445fffbSMatthew Ahrens * with the list of names of all the properties that failed along with the 24704445fffbSMatthew Ahrens * corresponding error numbers. 247192241e0bSTom Erickson * 24724445fffbSMatthew Ahrens * If every property is set successfully, zero is returned and errlist is not 24734445fffbSMatthew Ahrens * modified. 247492241e0bSTom Erickson */ 247592241e0bSTom Erickson int 247692241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 24774445fffbSMatthew Ahrens nvlist_t *errlist) 247892241e0bSTom Erickson { 247992241e0bSTom Erickson nvpair_t *pair; 248092241e0bSTom Erickson nvpair_t *propval; 248102e383d1STom Erickson int rv = 0; 248292241e0bSTom Erickson uint64_t intval; 248392241e0bSTom Erickson char *strval; 24844445fffbSMatthew Ahrens nvlist_t *genericnvl = fnvlist_alloc(); 24854445fffbSMatthew Ahrens nvlist_t *retrynvl = fnvlist_alloc(); 2486a9799022Sck 248792241e0bSTom Erickson retry: 248892241e0bSTom Erickson pair = NULL; 248992241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 249092241e0bSTom Erickson const char *propname = nvpair_name(pair); 249192241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2492cfa69fd2STom Erickson int err = 0; 2493e9dbad6fSeschrock 249492241e0bSTom Erickson /* decode the property value */ 249592241e0bSTom Erickson propval = pair; 249692241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 249792241e0bSTom Erickson nvlist_t *attrs; 24984445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 2499eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2500eeb85002STim Haley &propval) != 0) 2501be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 250214843421SMatthew Ahrens } 2503e9dbad6fSeschrock 250492241e0bSTom Erickson /* Validate value type */ 2505eeb85002STim Haley if (err == 0 && prop == ZPROP_INVAL) { 250692241e0bSTom Erickson if (zfs_prop_user(propname)) { 250792241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 2508be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 250992241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 251092241e0bSTom Erickson if (nvpair_type(propval) != 251192241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 2512be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 251319b94df9SMatthew Ahrens } else { 2514be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 25154201a95eSRic Aleshire } 2516eeb85002STim Haley } else if (err == 0) { 251792241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 251892241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 2519be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 252092241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2521a2eea2e1Sahrens const char *unused; 2522a2eea2e1Sahrens 25234445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 2524e9dbad6fSeschrock 2525e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 252691ebeef5Sahrens case PROP_TYPE_NUMBER: 2527e9dbad6fSeschrock break; 252891ebeef5Sahrens case PROP_TYPE_STRING: 2529be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 253092241e0bSTom Erickson break; 253191ebeef5Sahrens case PROP_TYPE_INDEX: 2532acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 253392241e0bSTom Erickson intval, &unused) != 0) 2534be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2535e9dbad6fSeschrock break; 2536e9dbad6fSeschrock default: 2537e7437265Sahrens cmn_err(CE_PANIC, 2538e7437265Sahrens "unknown property type"); 2539e9dbad6fSeschrock } 2540e9dbad6fSeschrock } else { 2541be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2542e9dbad6fSeschrock } 2543e9dbad6fSeschrock } 254492241e0bSTom Erickson 254592241e0bSTom Erickson /* Validate permissions */ 254692241e0bSTom Erickson if (err == 0) 254792241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 254892241e0bSTom Erickson 254992241e0bSTom Erickson if (err == 0) { 255092241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 255192241e0bSTom Erickson if (err == -1) { 255292241e0bSTom Erickson /* 255392241e0bSTom Erickson * For better performance we build up a list of 255492241e0bSTom Erickson * properties to set in a single transaction. 255592241e0bSTom Erickson */ 255692241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 255792241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 255892241e0bSTom Erickson /* 255992241e0bSTom Erickson * This may be a spurious error caused by 256092241e0bSTom Erickson * receiving quota and reservation out of order. 256192241e0bSTom Erickson * Try again in a second pass. 256292241e0bSTom Erickson */ 256392241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 256492241e0bSTom Erickson } 256592241e0bSTom Erickson } 256692241e0bSTom Erickson 25674445fffbSMatthew Ahrens if (err != 0) { 25684445fffbSMatthew Ahrens if (errlist != NULL) 25694445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, err); 25704445fffbSMatthew Ahrens rv = err; 25714445fffbSMatthew Ahrens } 2572e9dbad6fSeschrock } 2573e9dbad6fSeschrock 257492241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 257592241e0bSTom Erickson nvl = retrynvl; 257692241e0bSTom Erickson goto retry; 257792241e0bSTom Erickson } 257892241e0bSTom Erickson 257992241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 258092241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 258192241e0bSTom Erickson /* 258292241e0bSTom Erickson * If this fails, we still want to set as many properties as we 258392241e0bSTom Erickson * can, so try setting them individually. 258492241e0bSTom Erickson */ 258592241e0bSTom Erickson pair = NULL; 258692241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 258792241e0bSTom Erickson const char *propname = nvpair_name(pair); 2588cfa69fd2STom Erickson int err = 0; 258992241e0bSTom Erickson 259092241e0bSTom Erickson propval = pair; 259192241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 259292241e0bSTom Erickson nvlist_t *attrs; 25934445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 25944445fffbSMatthew Ahrens propval = fnvlist_lookup_nvpair(attrs, 25954445fffbSMatthew Ahrens ZPROP_VALUE); 259692241e0bSTom Erickson } 259792241e0bSTom Erickson 259892241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 25994445fffbSMatthew Ahrens strval = fnvpair_value_string(propval); 26003b2aab18SMatthew Ahrens err = dsl_prop_set_string(dsname, propname, 26013b2aab18SMatthew Ahrens source, strval); 260292241e0bSTom Erickson } else { 26034445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 26043b2aab18SMatthew Ahrens err = dsl_prop_set_int(dsname, propname, source, 26053b2aab18SMatthew Ahrens intval); 260692241e0bSTom Erickson } 260792241e0bSTom Erickson 260892241e0bSTom Erickson if (err != 0) { 26094445fffbSMatthew Ahrens if (errlist != NULL) { 26104445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, 26114445fffbSMatthew Ahrens err); 26124445fffbSMatthew Ahrens } 26134445fffbSMatthew Ahrens rv = err; 261492241e0bSTom Erickson } 261592241e0bSTom Erickson } 26165c0b6a79SRich Morris } 26175c0b6a79SRich Morris nvlist_free(genericnvl); 261892241e0bSTom Erickson nvlist_free(retrynvl); 261992241e0bSTom Erickson 262092241e0bSTom Erickson return (rv); 2621fa9e4066Sahrens } 2622fa9e4066Sahrens 2623ea2f5b9eSMatthew Ahrens /* 2624ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2625ea2f5b9eSMatthew Ahrens */ 2626ea2f5b9eSMatthew Ahrens static int 26274445fffbSMatthew Ahrens zfs_check_userprops(const char *fsname, nvlist_t *nvl) 2628ea2f5b9eSMatthew Ahrens { 262992241e0bSTom Erickson nvpair_t *pair = NULL; 2630ea2f5b9eSMatthew Ahrens int error = 0; 2631ea2f5b9eSMatthew Ahrens 263292241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 263392241e0bSTom Erickson const char *propname = nvpair_name(pair); 2634ea2f5b9eSMatthew Ahrens 2635ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 263692241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2637be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2638ea2f5b9eSMatthew Ahrens 2639ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2640ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2641ea2f5b9eSMatthew Ahrens return (error); 2642ea2f5b9eSMatthew Ahrens 2643ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2644be6fd75aSMatthew Ahrens return (SET_ERROR(ENAMETOOLONG)); 2645ea2f5b9eSMatthew Ahrens 264678f17100SMatthew Ahrens if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN) 2647ea2f5b9eSMatthew Ahrens return (E2BIG); 2648ea2f5b9eSMatthew Ahrens } 2649ea2f5b9eSMatthew Ahrens return (0); 2650ea2f5b9eSMatthew Ahrens } 2651ea2f5b9eSMatthew Ahrens 265292241e0bSTom Erickson static void 265392241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 265492241e0bSTom Erickson { 265592241e0bSTom Erickson nvpair_t *pair; 265692241e0bSTom Erickson 265792241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 265892241e0bSTom Erickson 265992241e0bSTom Erickson pair = NULL; 266092241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 266192241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 266292241e0bSTom Erickson continue; 266392241e0bSTom Erickson 266492241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 266592241e0bSTom Erickson } 266692241e0bSTom Erickson } 266792241e0bSTom Erickson 266892241e0bSTom Erickson static int 26693b2aab18SMatthew Ahrens clear_received_props(const char *dsname, nvlist_t *props, 267092241e0bSTom Erickson nvlist_t *skipped) 267192241e0bSTom Erickson { 267292241e0bSTom Erickson int err = 0; 267392241e0bSTom Erickson nvlist_t *cleared_props = NULL; 267492241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 267592241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 267692241e0bSTom Erickson /* 267792241e0bSTom Erickson * Acts on local properties until the dataset has received 267892241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 267992241e0bSTom Erickson */ 268092241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 26813b2aab18SMatthew Ahrens (dsl_prop_get_hasrecvd(dsname) ? ZPROP_SRC_RECEIVED : 0)); 26823b2aab18SMatthew Ahrens err = zfs_set_prop_nvlist(dsname, flags, cleared_props, NULL); 268392241e0bSTom Erickson } 268492241e0bSTom Erickson nvlist_free(cleared_props); 268592241e0bSTom Erickson return (err); 268692241e0bSTom Erickson } 268792241e0bSTom Erickson 26883cb34c60Sahrens /* 26893cb34c60Sahrens * inputs: 26903cb34c60Sahrens * zc_name name of filesystem 26915c0b6a79SRich Morris * zc_value name of property to set 26923cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 269392241e0bSTom Erickson * zc_cookie received properties flag 26943cb34c60Sahrens * 269592241e0bSTom Erickson * outputs: 269692241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 26973cb34c60Sahrens */ 2698fa9e4066Sahrens static int 2699e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2700fa9e4066Sahrens { 2701e9dbad6fSeschrock nvlist_t *nvl; 270292241e0bSTom Erickson boolean_t received = zc->zc_cookie; 270392241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 270492241e0bSTom Erickson ZPROP_SRC_LOCAL); 27054445fffbSMatthew Ahrens nvlist_t *errors; 2706e9dbad6fSeschrock int error; 2707e9dbad6fSeschrock 2708990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2709478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2710e9dbad6fSeschrock return (error); 2711e9dbad6fSeschrock 271292241e0bSTom Erickson if (received) { 2713bb0ade09Sahrens nvlist_t *origprops; 2714bb0ade09Sahrens 27153b2aab18SMatthew Ahrens if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) { 27163b2aab18SMatthew Ahrens (void) clear_received_props(zc->zc_name, 27173b2aab18SMatthew Ahrens origprops, nvl); 27183b2aab18SMatthew Ahrens nvlist_free(origprops); 2719bb0ade09Sahrens } 27203b2aab18SMatthew Ahrens 27213b2aab18SMatthew Ahrens error = dsl_prop_set_hasrecvd(zc->zc_name); 2722bb0ade09Sahrens } 2723bb0ade09Sahrens 27244445fffbSMatthew Ahrens errors = fnvlist_alloc(); 27253b2aab18SMatthew Ahrens if (error == 0) 27263b2aab18SMatthew Ahrens error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors); 272792241e0bSTom Erickson 272892241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 272992241e0bSTom Erickson (void) put_nvlist(zc, errors); 273092241e0bSTom Erickson } 2731ecd6cf80Smarks 273292241e0bSTom Erickson nvlist_free(errors); 2733e9dbad6fSeschrock nvlist_free(nvl); 2734e9dbad6fSeschrock return (error); 2735fa9e4066Sahrens } 2736fa9e4066Sahrens 27373cb34c60Sahrens /* 27383cb34c60Sahrens * inputs: 27393cb34c60Sahrens * zc_name name of filesystem 27403cb34c60Sahrens * zc_value name of property to inherit 274192241e0bSTom Erickson * zc_cookie revert to received value if TRUE 27423cb34c60Sahrens * 27433cb34c60Sahrens * outputs: none 27443cb34c60Sahrens */ 2745e45ce728Sahrens static int 2746e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2747e45ce728Sahrens { 274892241e0bSTom Erickson const char *propname = zc->zc_value; 274992241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 275092241e0bSTom Erickson boolean_t received = zc->zc_cookie; 275192241e0bSTom Erickson zprop_source_t source = (received 275292241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 275392241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 275492241e0bSTom Erickson 275592241e0bSTom Erickson if (received) { 275692241e0bSTom Erickson nvlist_t *dummy; 275792241e0bSTom Erickson nvpair_t *pair; 275892241e0bSTom Erickson zprop_type_t type; 275992241e0bSTom Erickson int err; 276092241e0bSTom Erickson 276192241e0bSTom Erickson /* 276292241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 276392241e0bSTom Erickson * nvpair with type info. 276492241e0bSTom Erickson */ 276592241e0bSTom Erickson if (prop == ZPROP_INVAL) { 276692241e0bSTom Erickson if (!zfs_prop_user(propname)) 2767be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 276892241e0bSTom Erickson 276992241e0bSTom Erickson type = PROP_TYPE_STRING; 2770a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2771a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2772be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 277392241e0bSTom Erickson } else { 277492241e0bSTom Erickson type = zfs_prop_get_type(prop); 277592241e0bSTom Erickson } 277692241e0bSTom Erickson 277792241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 277892241e0bSTom Erickson 277992241e0bSTom Erickson switch (type) { 278092241e0bSTom Erickson case PROP_TYPE_STRING: 278192241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 278292241e0bSTom Erickson break; 278392241e0bSTom Erickson case PROP_TYPE_NUMBER: 278492241e0bSTom Erickson case PROP_TYPE_INDEX: 278592241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 278692241e0bSTom Erickson break; 278792241e0bSTom Erickson default: 278892241e0bSTom Erickson nvlist_free(dummy); 2789be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 279092241e0bSTom Erickson } 279192241e0bSTom Erickson 279292241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 279392241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 279492241e0bSTom Erickson nvlist_free(dummy); 279592241e0bSTom Erickson if (err != -1) 279692241e0bSTom Erickson return (err); /* special property already handled */ 279792241e0bSTom Erickson } else { 279892241e0bSTom Erickson /* 279992241e0bSTom Erickson * Only check this in the non-received case. We want to allow 280092241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 280192241e0bSTom Erickson * and reservation to the received or default values even though 280292241e0bSTom Erickson * they are not considered inheritable. 280392241e0bSTom Erickson */ 280492241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 2805be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 280692241e0bSTom Erickson } 280792241e0bSTom Erickson 28084445fffbSMatthew Ahrens /* property name has been validated by zfs_secpolicy_inherit_prop() */ 28093b2aab18SMatthew Ahrens return (dsl_prop_inherit(zc->zc_name, zc->zc_value, source)); 2810e45ce728Sahrens } 2811e45ce728Sahrens 2812b1b8ab34Slling static int 281311a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2814b1b8ab34Slling { 2815990b4856Slling nvlist_t *props; 2816b1b8ab34Slling spa_t *spa; 2817990b4856Slling int error; 281892241e0bSTom Erickson nvpair_t *pair; 2819b1b8ab34Slling 282092241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 282192241e0bSTom Erickson zc->zc_iflags, &props)) 2822b1b8ab34Slling return (error); 2823b1b8ab34Slling 2824379c004dSEric Schrock /* 2825379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2826379c004dSEric Schrock * to handle the faulted case. 2827379c004dSEric Schrock */ 282892241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 282992241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2830379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 283192241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2832379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2833379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2834379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2835379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2836379c004dSEric Schrock } 2837379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2838b693757aSEric Schrock if (spa != NULL) { 2839b693757aSEric Schrock nvlist_free(props); 2840379c004dSEric Schrock return (0); 2841b693757aSEric Schrock } 2842379c004dSEric Schrock } 2843379c004dSEric Schrock 2844b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2845990b4856Slling nvlist_free(props); 2846b1b8ab34Slling return (error); 2847b1b8ab34Slling } 2848b1b8ab34Slling 2849990b4856Slling error = spa_prop_set(spa, props); 2850b1b8ab34Slling 2851990b4856Slling nvlist_free(props); 2852b1b8ab34Slling spa_close(spa, FTAG); 2853b1b8ab34Slling 2854b1b8ab34Slling return (error); 2855b1b8ab34Slling } 2856b1b8ab34Slling 2857b1b8ab34Slling static int 285811a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2859b1b8ab34Slling { 2860b1b8ab34Slling spa_t *spa; 2861b1b8ab34Slling int error; 2862b1b8ab34Slling nvlist_t *nvp = NULL; 2863b1b8ab34Slling 2864379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2865379c004dSEric Schrock /* 2866379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2867379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2868379c004dSEric Schrock * anyway. 2869379c004dSEric Schrock */ 2870379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2871379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2872379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2873379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2874379c004dSEric Schrock } else { 2875379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2876379c004dSEric Schrock spa_close(spa, FTAG); 2877379c004dSEric Schrock } 2878b1b8ab34Slling 2879b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2880b1b8ab34Slling error = put_nvlist(zc, nvp); 2881b1b8ab34Slling else 2882be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 2883b1b8ab34Slling 2884379c004dSEric Schrock nvlist_free(nvp); 2885b1b8ab34Slling return (error); 2886b1b8ab34Slling } 2887b1b8ab34Slling 28883cb34c60Sahrens /* 28893cb34c60Sahrens * inputs: 28903cb34c60Sahrens * zc_name name of filesystem 28913cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 28923cb34c60Sahrens * zc_perm_action allow/unallow flag 28933cb34c60Sahrens * 28943cb34c60Sahrens * outputs: none 28953cb34c60Sahrens */ 2896ecd6cf80Smarks static int 2897ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2898ecd6cf80Smarks { 2899ecd6cf80Smarks int error; 2900ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2901ecd6cf80Smarks 2902990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2903478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2904ecd6cf80Smarks return (error); 2905ecd6cf80Smarks 2906ecd6cf80Smarks /* 2907ecd6cf80Smarks * Verify nvlist is constructed correctly 2908ecd6cf80Smarks */ 2909ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2910ecd6cf80Smarks nvlist_free(fsaclnv); 2911be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2912ecd6cf80Smarks } 2913ecd6cf80Smarks 2914ecd6cf80Smarks /* 2915ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2916ecd6cf80Smarks * that user is allowed to hand out each permission in 2917ecd6cf80Smarks * the nvlist(s) 2918ecd6cf80Smarks */ 2919ecd6cf80Smarks 292091ebeef5Sahrens error = secpolicy_zfs(CRED()); 29213b2aab18SMatthew Ahrens if (error != 0) { 292291ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 292391ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 292491ebeef5Sahrens fsaclnv, CRED()); 292591ebeef5Sahrens } else { 292691ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 292791ebeef5Sahrens fsaclnv, CRED()); 292891ebeef5Sahrens } 2929ecd6cf80Smarks } 2930ecd6cf80Smarks 2931ecd6cf80Smarks if (error == 0) 2932ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2933ecd6cf80Smarks 2934ecd6cf80Smarks nvlist_free(fsaclnv); 2935ecd6cf80Smarks return (error); 2936ecd6cf80Smarks } 2937ecd6cf80Smarks 29383cb34c60Sahrens /* 29393cb34c60Sahrens * inputs: 29403cb34c60Sahrens * zc_name name of filesystem 29413cb34c60Sahrens * 29423cb34c60Sahrens * outputs: 29433cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 29443cb34c60Sahrens */ 2945ecd6cf80Smarks static int 2946ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2947ecd6cf80Smarks { 2948ecd6cf80Smarks nvlist_t *nvp; 2949ecd6cf80Smarks int error; 2950ecd6cf80Smarks 2951ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2952ecd6cf80Smarks error = put_nvlist(zc, nvp); 2953ecd6cf80Smarks nvlist_free(nvp); 2954ecd6cf80Smarks } 2955ecd6cf80Smarks 2956ecd6cf80Smarks return (error); 2957ecd6cf80Smarks } 2958ecd6cf80Smarks 2959fa9e4066Sahrens /* 2960fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2961fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2962fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2963fa9e4066Sahrens */ 2964fa9e4066Sahrens static vfs_t * 2965fa9e4066Sahrens zfs_get_vfs(const char *resource) 2966fa9e4066Sahrens { 2967fa9e4066Sahrens struct vfs *vfsp; 2968fa9e4066Sahrens struct vfs *vfs_found = NULL; 2969fa9e4066Sahrens 2970fa9e4066Sahrens vfs_list_read_lock(); 2971fa9e4066Sahrens vfsp = rootvfs; 2972fa9e4066Sahrens do { 2973fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2974fa9e4066Sahrens VFS_HOLD(vfsp); 2975fa9e4066Sahrens vfs_found = vfsp; 2976fa9e4066Sahrens break; 2977fa9e4066Sahrens } 2978fa9e4066Sahrens vfsp = vfsp->vfs_next; 2979fa9e4066Sahrens } while (vfsp != rootvfs); 2980fa9e4066Sahrens vfs_list_unlock(); 2981fa9e4066Sahrens return (vfs_found); 2982fa9e4066Sahrens } 2983fa9e4066Sahrens 2984ecd6cf80Smarks /* ARGSUSED */ 2985fa9e4066Sahrens static void 2986ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2987fa9e4066Sahrens { 2988da6c28aaSamw zfs_creat_t *zct = arg; 2989da6c28aaSamw 2990de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2991da6c28aaSamw } 2992da6c28aaSamw 2993de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2994da6c28aaSamw 2995da6c28aaSamw /* 2996de8267e0Stimh * inputs: 29970a48a24eStimh * os parent objset pointer (NULL if root fs) 2998f7170741SWill Andrews * fuids_ok fuids allowed in this version of the spa? 2999f7170741SWill Andrews * sa_ok SAs allowed in this version of the spa? 3000f7170741SWill Andrews * createprops list of properties requested by creator 3001de8267e0Stimh * 3002de8267e0Stimh * outputs: 3003de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 30040a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 3005da6c28aaSamw * 3006de8267e0Stimh * Determine the settings for utf8only, normalization and 3007de8267e0Stimh * casesensitivity. Specific values may have been requested by the 3008de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 3009de8267e0Stimh * the file system is of too early a vintage, a creator can not 3010de8267e0Stimh * request settings for these properties, even if the requested 3011de8267e0Stimh * setting is the default value. We don't actually want to create dsl 3012de8267e0Stimh * properties for these, so remove them from the source nvlist after 3013de8267e0Stimh * processing. 3014da6c28aaSamw */ 3015da6c28aaSamw static int 301614843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 30170a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 30180a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 3019da6c28aaSamw { 3020de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 3021de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 3022de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 3023da6c28aaSamw 3024de8267e0Stimh ASSERT(zplprops != NULL); 3025da6c28aaSamw 3026de8267e0Stimh /* 3027de8267e0Stimh * Pull out creator prop choices, if any. 3028de8267e0Stimh */ 3029de8267e0Stimh if (createprops) { 30300a48a24eStimh (void) nvlist_lookup_uint64(createprops, 30310a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 3032de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3033de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 3034de8267e0Stimh (void) nvlist_remove_all(createprops, 3035de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 3036de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3037de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 3038de8267e0Stimh (void) nvlist_remove_all(createprops, 3039de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 3040de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3041de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 3042de8267e0Stimh (void) nvlist_remove_all(createprops, 3043de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 3044de8267e0Stimh } 3045da6c28aaSamw 3046c2a93d44Stimh /* 30470a48a24eStimh * If the zpl version requested is whacky or the file system 30480a48a24eStimh * or pool is version is too "young" to support normalization 30490a48a24eStimh * and the creator tried to set a value for one of the props, 30500a48a24eStimh * error out. 3051c2a93d44Stimh */ 30520a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 30530a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 30540a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 30550a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 3056de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 30570a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 3058be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3059c2a93d44Stimh 3060de8267e0Stimh /* 3061de8267e0Stimh * Put the version in the zplprops 3062de8267e0Stimh */ 3063de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3064de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 3065da6c28aaSamw 3066de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 3067de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 3068de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3069de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 3070da6c28aaSamw 3071c2a93d44Stimh /* 3072de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 3073c2a93d44Stimh */ 3074de8267e0Stimh if (norm) 3075de8267e0Stimh u8 = 1; 3076de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 3077de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 3078de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3079de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 3080de8267e0Stimh 3081de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 3082de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 3083de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3084de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 3085c2a93d44Stimh 3086ab04eb8eStimh if (is_ci) 3087ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 3088ab04eb8eStimh 3089da6c28aaSamw return (0); 3090fa9e4066Sahrens } 3091fa9e4066Sahrens 30920a48a24eStimh static int 30930a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 30940a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 30950a48a24eStimh { 30960a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 30970a48a24eStimh uint64_t zplver = ZPL_VERSION; 30980a48a24eStimh objset_t *os = NULL; 30990a48a24eStimh char parentname[MAXNAMELEN]; 31000a48a24eStimh char *cp; 31010a586ceaSMark Shellenbaum spa_t *spa; 31020a586ceaSMark Shellenbaum uint64_t spa_vers; 31030a48a24eStimh int error; 31040a48a24eStimh 31050a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 31060a48a24eStimh cp = strrchr(parentname, '/'); 31070a48a24eStimh ASSERT(cp != NULL); 31080a48a24eStimh cp[0] = '\0'; 31090a48a24eStimh 31100a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 31110a586ceaSMark Shellenbaum return (error); 31120a586ceaSMark Shellenbaum 31130a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 31140a586ceaSMark Shellenbaum spa_close(spa, FTAG); 31150a586ceaSMark Shellenbaum 31160a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 31170a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 31180a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 31190a48a24eStimh 31200a48a24eStimh /* 31210a48a24eStimh * Open parent object set so we can inherit zplprop values. 31220a48a24eStimh */ 3123503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 31240a48a24eStimh return (error); 31250a48a24eStimh 31260a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 31270a48a24eStimh zplprops, is_ci); 3128503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 31290a48a24eStimh return (error); 31300a48a24eStimh } 31310a48a24eStimh 31320a48a24eStimh static int 31330a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 31340a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 31350a48a24eStimh { 31360a586ceaSMark Shellenbaum boolean_t fuids_ok; 31370a586ceaSMark Shellenbaum boolean_t sa_ok; 31380a48a24eStimh uint64_t zplver = ZPL_VERSION; 31390a48a24eStimh int error; 31400a48a24eStimh 31410a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 31420a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 31430a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 31440a48a24eStimh 31450a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 31460a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 31470a48a24eStimh return (error); 31480a48a24eStimh } 31490a48a24eStimh 31503cb34c60Sahrens /* 31514445fffbSMatthew Ahrens * innvl: { 31524445fffbSMatthew Ahrens * "type" -> dmu_objset_type_t (int32) 31534445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 31544445fffbSMatthew Ahrens * } 31553cb34c60Sahrens * 31564445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 31573cb34c60Sahrens */ 3158fa9e4066Sahrens static int 31594445fffbSMatthew Ahrens zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3160fa9e4066Sahrens { 3161fa9e4066Sahrens int error = 0; 31624445fffbSMatthew Ahrens zfs_creat_t zct = { 0 }; 3163ecd6cf80Smarks nvlist_t *nvprops = NULL; 3164ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 31654445fffbSMatthew Ahrens int32_t type32; 31664445fffbSMatthew Ahrens dmu_objset_type_t type; 31674445fffbSMatthew Ahrens boolean_t is_insensitive = B_FALSE; 3168fa9e4066Sahrens 31694445fffbSMatthew Ahrens if (nvlist_lookup_int32(innvl, "type", &type32) != 0) 3170be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 31714445fffbSMatthew Ahrens type = type32; 31724445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3173fa9e4066Sahrens 31744445fffbSMatthew Ahrens switch (type) { 3175fa9e4066Sahrens case DMU_OST_ZFS: 3176fa9e4066Sahrens cbfunc = zfs_create_cb; 3177fa9e4066Sahrens break; 3178fa9e4066Sahrens 3179fa9e4066Sahrens case DMU_OST_ZVOL: 3180fa9e4066Sahrens cbfunc = zvol_create_cb; 3181fa9e4066Sahrens break; 3182fa9e4066Sahrens 3183fa9e4066Sahrens default: 31841d452cf5Sahrens cbfunc = NULL; 3185e7cbe64fSgw break; 3186fa9e4066Sahrens } 31874445fffbSMatthew Ahrens if (strchr(fsname, '@') || 31884445fffbSMatthew Ahrens strchr(fsname, '%')) 3189be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3190fa9e4066Sahrens 3191da6c28aaSamw zct.zct_props = nvprops; 3192da6c28aaSamw 31934445fffbSMatthew Ahrens if (cbfunc == NULL) 3194be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 31954445fffbSMatthew Ahrens 31964445fffbSMatthew Ahrens if (type == DMU_OST_ZVOL) { 31974445fffbSMatthew Ahrens uint64_t volsize, volblocksize; 31984445fffbSMatthew Ahrens 31994445fffbSMatthew Ahrens if (nvprops == NULL) 3200be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32014445fffbSMatthew Ahrens if (nvlist_lookup_uint64(nvprops, 32024445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) != 0) 3203be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3204fa9e4066Sahrens 32054445fffbSMatthew Ahrens if ((error = nvlist_lookup_uint64(nvprops, 32064445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 32074445fffbSMatthew Ahrens &volblocksize)) != 0 && error != ENOENT) 3208be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32094445fffbSMatthew Ahrens 32104445fffbSMatthew Ahrens if (error != 0) 32114445fffbSMatthew Ahrens volblocksize = zfs_prop_default_numeric( 32124445fffbSMatthew Ahrens ZFS_PROP_VOLBLOCKSIZE); 32134445fffbSMatthew Ahrens 32144445fffbSMatthew Ahrens if ((error = zvol_check_volblocksize( 32154445fffbSMatthew Ahrens volblocksize)) != 0 || 32164445fffbSMatthew Ahrens (error = zvol_check_volsize(volsize, 32174445fffbSMatthew Ahrens volblocksize)) != 0) 3218fa9e4066Sahrens return (error); 32194445fffbSMatthew Ahrens } else if (type == DMU_OST_ZFS) { 32204445fffbSMatthew Ahrens int error; 3221ab04eb8eStimh 32224445fffbSMatthew Ahrens /* 32234445fffbSMatthew Ahrens * We have to have normalization and 32244445fffbSMatthew Ahrens * case-folding flags correct when we do the 32254445fffbSMatthew Ahrens * file system creation, so go figure them out 32264445fffbSMatthew Ahrens * now. 32274445fffbSMatthew Ahrens */ 32284445fffbSMatthew Ahrens VERIFY(nvlist_alloc(&zct.zct_zplprops, 32294445fffbSMatthew Ahrens NV_UNIQUE_NAME, KM_SLEEP) == 0); 32304445fffbSMatthew Ahrens error = zfs_fill_zplprops(fsname, nvprops, 32314445fffbSMatthew Ahrens zct.zct_zplprops, &is_insensitive); 32324445fffbSMatthew Ahrens if (error != 0) { 32334445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 3234da6c28aaSamw return (error); 3235da6c28aaSamw } 32364445fffbSMatthew Ahrens } 3237ab04eb8eStimh 32384445fffbSMatthew Ahrens error = dmu_objset_create(fsname, type, 32394445fffbSMatthew Ahrens is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 32404445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 32415c5460e9Seschrock 32424445fffbSMatthew Ahrens /* 32434445fffbSMatthew Ahrens * It would be nice to do this atomically. 32444445fffbSMatthew Ahrens */ 32454445fffbSMatthew Ahrens if (error == 0) { 32464445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 32474445fffbSMatthew Ahrens nvprops, outnvl); 32484445fffbSMatthew Ahrens if (error != 0) 32493b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 32504445fffbSMatthew Ahrens } 32514445fffbSMatthew Ahrens return (error); 32524445fffbSMatthew Ahrens } 3253e9dbad6fSeschrock 32544445fffbSMatthew Ahrens /* 32554445fffbSMatthew Ahrens * innvl: { 32564445fffbSMatthew Ahrens * "origin" -> name of origin snapshot 32574445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 32584445fffbSMatthew Ahrens * } 32594445fffbSMatthew Ahrens * 32604445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 32614445fffbSMatthew Ahrens */ 32624445fffbSMatthew Ahrens static int 32634445fffbSMatthew Ahrens zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 32644445fffbSMatthew Ahrens { 32654445fffbSMatthew Ahrens int error = 0; 32664445fffbSMatthew Ahrens nvlist_t *nvprops = NULL; 32674445fffbSMatthew Ahrens char *origin_name; 3268e9dbad6fSeschrock 32694445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) 3270be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32714445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3272e9dbad6fSeschrock 32734445fffbSMatthew Ahrens if (strchr(fsname, '@') || 32744445fffbSMatthew Ahrens strchr(fsname, '%')) 3275be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3276e9dbad6fSeschrock 32774445fffbSMatthew Ahrens if (dataset_namecheck(origin_name, NULL, NULL) != 0) 3278be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32793b2aab18SMatthew Ahrens error = dmu_objset_clone(fsname, origin_name); 32803b2aab18SMatthew Ahrens if (error != 0) 32814445fffbSMatthew Ahrens return (error); 3282e9dbad6fSeschrock 3283e9dbad6fSeschrock /* 3284e9dbad6fSeschrock * It would be nice to do this atomically. 3285e9dbad6fSeschrock */ 3286e9dbad6fSeschrock if (error == 0) { 32874445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 32884445fffbSMatthew Ahrens nvprops, outnvl); 328992241e0bSTom Erickson if (error != 0) 32903b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 3291e9dbad6fSeschrock } 3292fa9e4066Sahrens return (error); 3293fa9e4066Sahrens } 3294fa9e4066Sahrens 32953cb34c60Sahrens /* 32964445fffbSMatthew Ahrens * innvl: { 32974445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 32984445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value (string) } 32994445fffbSMatthew Ahrens * } 33004445fffbSMatthew Ahrens * 33014445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 33023cb34c60Sahrens */ 3303fa9e4066Sahrens static int 33044445fffbSMatthew Ahrens zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3305fa9e4066Sahrens { 33064445fffbSMatthew Ahrens nvlist_t *snaps; 33074445fffbSMatthew Ahrens nvlist_t *props = NULL; 33084445fffbSMatthew Ahrens int error, poollen; 33094445fffbSMatthew Ahrens nvpair_t *pair; 3310bb0ade09Sahrens 33114445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &props); 33124445fffbSMatthew Ahrens if ((error = zfs_check_userprops(poolname, props)) != 0) 33134445fffbSMatthew Ahrens return (error); 33144445fffbSMatthew Ahrens 33154445fffbSMatthew Ahrens if (!nvlist_empty(props) && 33164445fffbSMatthew Ahrens zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) 3317be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 33184445fffbSMatthew Ahrens 33194445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3320be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 33214445fffbSMatthew Ahrens poollen = strlen(poolname); 33224445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 33234445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 33244445fffbSMatthew Ahrens const char *name = nvpair_name(pair); 33254445fffbSMatthew Ahrens const char *cp = strchr(name, '@'); 3326bb0ade09Sahrens 33274445fffbSMatthew Ahrens /* 33284445fffbSMatthew Ahrens * The snap name must contain an @, and the part after it must 33294445fffbSMatthew Ahrens * contain only valid characters. 33304445fffbSMatthew Ahrens */ 333178f17100SMatthew Ahrens if (cp == NULL || 333278f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 3333be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3334bb0ade09Sahrens 33354445fffbSMatthew Ahrens /* 33364445fffbSMatthew Ahrens * The snap must be in the specified pool. 33374445fffbSMatthew Ahrens */ 33384445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 33394445fffbSMatthew Ahrens (name[poollen] != '/' && name[poollen] != '@')) 3340be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 33414445fffbSMatthew Ahrens 33424445fffbSMatthew Ahrens /* This must be the only snap of this fs. */ 33434445fffbSMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(snaps, pair); 33444445fffbSMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(snaps, pair2)) { 33454445fffbSMatthew Ahrens if (strncmp(name, nvpair_name(pair2), cp - name + 1) 33464445fffbSMatthew Ahrens == 0) { 3347be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 33484445fffbSMatthew Ahrens } 33494445fffbSMatthew Ahrens } 33504445fffbSMatthew Ahrens } 3351bb0ade09Sahrens 33523b2aab18SMatthew Ahrens error = dsl_dataset_snapshot(snaps, props, outnvl); 33534445fffbSMatthew Ahrens return (error); 33544445fffbSMatthew Ahrens } 33554445fffbSMatthew Ahrens 33564445fffbSMatthew Ahrens /* 33574445fffbSMatthew Ahrens * innvl: "message" -> string 33584445fffbSMatthew Ahrens */ 33594445fffbSMatthew Ahrens /* ARGSUSED */ 33604445fffbSMatthew Ahrens static int 33614445fffbSMatthew Ahrens zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 33624445fffbSMatthew Ahrens { 33634445fffbSMatthew Ahrens char *message; 33644445fffbSMatthew Ahrens spa_t *spa; 33654445fffbSMatthew Ahrens int error; 33664445fffbSMatthew Ahrens char *poolname; 33674445fffbSMatthew Ahrens 33684445fffbSMatthew Ahrens /* 33694445fffbSMatthew Ahrens * The poolname in the ioctl is not set, we get it from the TSD, 33704445fffbSMatthew Ahrens * which was set at the end of the last successful ioctl that allows 33714445fffbSMatthew Ahrens * logging. The secpolicy func already checked that it is set. 33724445fffbSMatthew Ahrens * Only one log ioctl is allowed after each successful ioctl, so 33734445fffbSMatthew Ahrens * we clear the TSD here. 33744445fffbSMatthew Ahrens */ 33754445fffbSMatthew Ahrens poolname = tsd_get(zfs_allow_log_key); 33764445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, NULL); 33774445fffbSMatthew Ahrens error = spa_open(poolname, &spa, FTAG); 33784445fffbSMatthew Ahrens strfree(poolname); 33794445fffbSMatthew Ahrens if (error != 0) 33804445fffbSMatthew Ahrens return (error); 33814445fffbSMatthew Ahrens 33824445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "message", &message) != 0) { 33834445fffbSMatthew Ahrens spa_close(spa, FTAG); 3384be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3385bb0ade09Sahrens } 3386ea2f5b9eSMatthew Ahrens 33874445fffbSMatthew Ahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 33884445fffbSMatthew Ahrens spa_close(spa, FTAG); 3389be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 33904445fffbSMatthew Ahrens } 3391ea2f5b9eSMatthew Ahrens 33924445fffbSMatthew Ahrens error = spa_history_log(spa, message); 33934445fffbSMatthew Ahrens spa_close(spa, FTAG); 3394bb0ade09Sahrens return (error); 33951d452cf5Sahrens } 3396fa9e4066Sahrens 33973b2aab18SMatthew Ahrens /* 33983b2aab18SMatthew Ahrens * The dp_config_rwlock must not be held when calling this, because the 33993b2aab18SMatthew Ahrens * unmount may need to write out data. 34003b2aab18SMatthew Ahrens * 34013b2aab18SMatthew Ahrens * This function is best-effort. Callers must deal gracefully if it 34023b2aab18SMatthew Ahrens * remains mounted (or is remounted after this call). 3403fc7a6e3fSWill Andrews * 3404fc7a6e3fSWill Andrews * Returns 0 if the argument is not a snapshot, or it is not currently a 3405fc7a6e3fSWill Andrews * filesystem, or we were able to unmount it. Returns error code otherwise. 34063b2aab18SMatthew Ahrens */ 3407fc7a6e3fSWill Andrews int 34083b2aab18SMatthew Ahrens zfs_unmount_snap(const char *snapname) 34091d452cf5Sahrens { 34104445fffbSMatthew Ahrens vfs_t *vfsp; 34113b2aab18SMatthew Ahrens zfsvfs_t *zfsvfs; 3412fc7a6e3fSWill Andrews int err; 34131d452cf5Sahrens 34143b2aab18SMatthew Ahrens if (strchr(snapname, '@') == NULL) 3415fc7a6e3fSWill Andrews return (0); 34161d452cf5Sahrens 34173b2aab18SMatthew Ahrens vfsp = zfs_get_vfs(snapname); 34184445fffbSMatthew Ahrens if (vfsp == NULL) 3419fc7a6e3fSWill Andrews return (0); 34203b2aab18SMatthew Ahrens 34213b2aab18SMatthew Ahrens zfsvfs = vfsp->vfs_data; 34223b2aab18SMatthew Ahrens ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os))); 34231d452cf5Sahrens 3424fc7a6e3fSWill Andrews err = vn_vfswlock(vfsp->vfs_vnodecovered); 34254445fffbSMatthew Ahrens VFS_RELE(vfsp); 3426fc7a6e3fSWill Andrews if (err != 0) 3427fc7a6e3fSWill Andrews return (SET_ERROR(err)); 34284445fffbSMatthew Ahrens 34294445fffbSMatthew Ahrens /* 34304445fffbSMatthew Ahrens * Always force the unmount for snapshots. 34314445fffbSMatthew Ahrens */ 34323b2aab18SMatthew Ahrens (void) dounmount(vfsp, MS_FORCE, kcred); 3433fc7a6e3fSWill Andrews return (0); 34343b2aab18SMatthew Ahrens } 34353b2aab18SMatthew Ahrens 34363b2aab18SMatthew Ahrens /* ARGSUSED */ 34373b2aab18SMatthew Ahrens static int 34383b2aab18SMatthew Ahrens zfs_unmount_snap_cb(const char *snapname, void *arg) 34393b2aab18SMatthew Ahrens { 3440fc7a6e3fSWill Andrews return (zfs_unmount_snap(snapname)); 34413b2aab18SMatthew Ahrens } 34423b2aab18SMatthew Ahrens 34433b2aab18SMatthew Ahrens /* 34443b2aab18SMatthew Ahrens * When a clone is destroyed, its origin may also need to be destroyed, 34453b2aab18SMatthew Ahrens * in which case it must be unmounted. This routine will do that unmount 34463b2aab18SMatthew Ahrens * if necessary. 34473b2aab18SMatthew Ahrens */ 34483b2aab18SMatthew Ahrens void 34493b2aab18SMatthew Ahrens zfs_destroy_unmount_origin(const char *fsname) 34503b2aab18SMatthew Ahrens { 34513b2aab18SMatthew Ahrens int error; 34523b2aab18SMatthew Ahrens objset_t *os; 34533b2aab18SMatthew Ahrens dsl_dataset_t *ds; 34543b2aab18SMatthew Ahrens 34553b2aab18SMatthew Ahrens error = dmu_objset_hold(fsname, FTAG, &os); 34563b2aab18SMatthew Ahrens if (error != 0) 34573b2aab18SMatthew Ahrens return; 34583b2aab18SMatthew Ahrens ds = dmu_objset_ds(os); 34593b2aab18SMatthew Ahrens if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev)) { 34603b2aab18SMatthew Ahrens char originname[MAXNAMELEN]; 34613b2aab18SMatthew Ahrens dsl_dataset_name(ds->ds_prev, originname); 34623b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 3463fc7a6e3fSWill Andrews (void) zfs_unmount_snap(originname); 34643b2aab18SMatthew Ahrens } else { 34653b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 34663b2aab18SMatthew Ahrens } 34671d452cf5Sahrens } 34681d452cf5Sahrens 34693cb34c60Sahrens /* 34704445fffbSMatthew Ahrens * innvl: { 34714445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 34724445fffbSMatthew Ahrens * (optional boolean) "defer" 34734445fffbSMatthew Ahrens * } 34744445fffbSMatthew Ahrens * 34754445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 34763cb34c60Sahrens * 34773cb34c60Sahrens */ 347878f17100SMatthew Ahrens /* ARGSUSED */ 34791d452cf5Sahrens static int 34804445fffbSMatthew Ahrens zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 34811d452cf5Sahrens { 34824445fffbSMatthew Ahrens nvlist_t *snaps; 348319b94df9SMatthew Ahrens nvpair_t *pair; 34844445fffbSMatthew Ahrens boolean_t defer; 34851d452cf5Sahrens 34864445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3487be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 34884445fffbSMatthew Ahrens defer = nvlist_exists(innvl, "defer"); 348919b94df9SMatthew Ahrens 34904445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 34914445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 349278f17100SMatthew Ahrens (void) zfs_unmount_snap(nvpair_name(pair)); 349378f17100SMatthew Ahrens } 349478f17100SMatthew Ahrens 349578f17100SMatthew Ahrens return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); 349678f17100SMatthew Ahrens } 349778f17100SMatthew Ahrens 349878f17100SMatthew Ahrens /* 349978f17100SMatthew Ahrens * Create bookmarks. Bookmark names are of the form <fs>#<bmark>. 350078f17100SMatthew Ahrens * All bookmarks must be in the same pool. 350178f17100SMatthew Ahrens * 350278f17100SMatthew Ahrens * innvl: { 350378f17100SMatthew Ahrens * bookmark1 -> snapshot1, bookmark2 -> snapshot2 350478f17100SMatthew Ahrens * } 350578f17100SMatthew Ahrens * 350678f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 350778f17100SMatthew Ahrens * 350878f17100SMatthew Ahrens */ 350978f17100SMatthew Ahrens /* ARGSUSED */ 351078f17100SMatthew Ahrens static int 351178f17100SMatthew Ahrens zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 351278f17100SMatthew Ahrens { 351378f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 351478f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 351578f17100SMatthew Ahrens char *snap_name; 351678f17100SMatthew Ahrens 351778f17100SMatthew Ahrens /* 351878f17100SMatthew Ahrens * Verify the snapshot argument. 351978f17100SMatthew Ahrens */ 352078f17100SMatthew Ahrens if (nvpair_value_string(pair, &snap_name) != 0) 352178f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 352278f17100SMatthew Ahrens 352378f17100SMatthew Ahrens 352478f17100SMatthew Ahrens /* Verify that the keys (bookmarks) are unique */ 352578f17100SMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(innvl, pair); 352678f17100SMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(innvl, pair2)) { 352778f17100SMatthew Ahrens if (strcmp(nvpair_name(pair), nvpair_name(pair2)) == 0) 352878f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 352978f17100SMatthew Ahrens } 353078f17100SMatthew Ahrens } 353178f17100SMatthew Ahrens 353278f17100SMatthew Ahrens return (dsl_bookmark_create(innvl, outnvl)); 353378f17100SMatthew Ahrens } 353478f17100SMatthew Ahrens 353578f17100SMatthew Ahrens /* 353678f17100SMatthew Ahrens * innvl: { 353778f17100SMatthew Ahrens * property 1, property 2, ... 353878f17100SMatthew Ahrens * } 353978f17100SMatthew Ahrens * 354078f17100SMatthew Ahrens * outnvl: { 354178f17100SMatthew Ahrens * bookmark name 1 -> { property 1, property 2, ... }, 354278f17100SMatthew Ahrens * bookmark name 2 -> { property 1, property 2, ... } 354378f17100SMatthew Ahrens * } 354478f17100SMatthew Ahrens * 354578f17100SMatthew Ahrens */ 354678f17100SMatthew Ahrens static int 354778f17100SMatthew Ahrens zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 354878f17100SMatthew Ahrens { 354978f17100SMatthew Ahrens return (dsl_get_bookmarks(fsname, innvl, outnvl)); 355078f17100SMatthew Ahrens } 355178f17100SMatthew Ahrens 355278f17100SMatthew Ahrens /* 355378f17100SMatthew Ahrens * innvl: { 355478f17100SMatthew Ahrens * bookmark name 1, bookmark name 2 355578f17100SMatthew Ahrens * } 355678f17100SMatthew Ahrens * 355778f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 355878f17100SMatthew Ahrens * 355978f17100SMatthew Ahrens */ 356078f17100SMatthew Ahrens static int 356178f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, 356278f17100SMatthew Ahrens nvlist_t *outnvl) 356378f17100SMatthew Ahrens { 356478f17100SMatthew Ahrens int error, poollen; 356578f17100SMatthew Ahrens 356678f17100SMatthew Ahrens poollen = strlen(poolname); 356778f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 356878f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 356919b94df9SMatthew Ahrens const char *name = nvpair_name(pair); 357078f17100SMatthew Ahrens const char *cp = strchr(name, '#'); 35714445fffbSMatthew Ahrens 357219b94df9SMatthew Ahrens /* 357378f17100SMatthew Ahrens * The bookmark name must contain an #, and the part after it 357478f17100SMatthew Ahrens * must contain only valid characters. 357578f17100SMatthew Ahrens */ 357678f17100SMatthew Ahrens if (cp == NULL || 357778f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 357878f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 357978f17100SMatthew Ahrens 358078f17100SMatthew Ahrens /* 358178f17100SMatthew Ahrens * The bookmark must be in the specified pool. 358219b94df9SMatthew Ahrens */ 35834445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 358478f17100SMatthew Ahrens (name[poollen] != '/' && name[poollen] != '#')) 3585be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 358619b94df9SMatthew Ahrens } 358719b94df9SMatthew Ahrens 358878f17100SMatthew Ahrens error = dsl_bookmark_destroy(innvl, outnvl); 358978f17100SMatthew Ahrens return (error); 35901d452cf5Sahrens } 35911d452cf5Sahrens 35923cb34c60Sahrens /* 35933cb34c60Sahrens * inputs: 35943cb34c60Sahrens * zc_name name of dataset to destroy 35953cb34c60Sahrens * zc_objset_type type of objset 3596842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 35973cb34c60Sahrens * 35983cb34c60Sahrens * outputs: none 35993cb34c60Sahrens */ 36001d452cf5Sahrens static int 36011d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 36021d452cf5Sahrens { 3603681d9761SEric Taylor int err; 3604fc7a6e3fSWill Andrews 3605fc7a6e3fSWill Andrews if (zc->zc_objset_type == DMU_OST_ZFS) { 3606fc7a6e3fSWill Andrews err = zfs_unmount_snap(zc->zc_name); 3607fc7a6e3fSWill Andrews if (err != 0) 3608fc7a6e3fSWill Andrews return (err); 3609fc7a6e3fSWill Andrews } 3610fa9e4066Sahrens 36113b2aab18SMatthew Ahrens if (strchr(zc->zc_name, '@')) 36123b2aab18SMatthew Ahrens err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); 36133b2aab18SMatthew Ahrens else 36143b2aab18SMatthew Ahrens err = dsl_destroy_head(zc->zc_name); 3615681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 36165c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 3617681d9761SEric Taylor return (err); 3618fa9e4066Sahrens } 3619fa9e4066Sahrens 36203cb34c60Sahrens /* 3621a7027df1SMatthew Ahrens * fsname is name of dataset to rollback (to most recent snapshot) 36223cb34c60Sahrens * 3623a7027df1SMatthew Ahrens * innvl is not used. 3624a7027df1SMatthew Ahrens * 3625a7027df1SMatthew Ahrens * outnvl: "target" -> name of most recent snapshot 3626a7027df1SMatthew Ahrens * } 36273cb34c60Sahrens */ 3628a7027df1SMatthew Ahrens /* ARGSUSED */ 3629fa9e4066Sahrens static int 3630a7027df1SMatthew Ahrens zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl) 3631fa9e4066Sahrens { 3632ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 36333b2aab18SMatthew Ahrens int error; 3634ae46e4c7SMatthew Ahrens 3635a7027df1SMatthew Ahrens if (getzfsvfs(fsname, &zfsvfs) == 0) { 3636503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 363747f263f4Sek if (error == 0) { 363847f263f4Sek int resume_err; 36394ccbb6e7Sahrens 3640a7027df1SMatthew Ahrens error = dsl_dataset_rollback(fsname, zfsvfs, outnvl); 3641a7027df1SMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, fsname); 364247f263f4Sek error = error ? error : resume_err; 364347f263f4Sek } 36444ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 36454ccbb6e7Sahrens } else { 3646a7027df1SMatthew Ahrens error = dsl_dataset_rollback(fsname, NULL, outnvl); 36474ccbb6e7Sahrens } 36483b2aab18SMatthew Ahrens return (error); 36493b2aab18SMatthew Ahrens } 36504ccbb6e7Sahrens 36513b2aab18SMatthew Ahrens static int 36523b2aab18SMatthew Ahrens recursive_unmount(const char *fsname, void *arg) 36533b2aab18SMatthew Ahrens { 36543b2aab18SMatthew Ahrens const char *snapname = arg; 36553b2aab18SMatthew Ahrens char fullname[MAXNAMELEN]; 3656ae46e4c7SMatthew Ahrens 36573b2aab18SMatthew Ahrens (void) snprintf(fullname, sizeof (fullname), "%s@%s", fsname, snapname); 3658fc7a6e3fSWill Andrews return (zfs_unmount_snap(fullname)); 3659fa9e4066Sahrens } 3660fa9e4066Sahrens 36613cb34c60Sahrens /* 36623cb34c60Sahrens * inputs: 36633cb34c60Sahrens * zc_name old name of dataset 36643cb34c60Sahrens * zc_value new name of dataset 36653cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 36663cb34c60Sahrens * 36673cb34c60Sahrens * outputs: none 36683cb34c60Sahrens */ 3669fa9e4066Sahrens static int 3670fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3671fa9e4066Sahrens { 36727f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 36733b2aab18SMatthew Ahrens char *at; 3674cdf5b4caSmmusante 3675e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3676f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3677f18faf3fSek strchr(zc->zc_value, '%')) 3678be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3679fa9e4066Sahrens 36803b2aab18SMatthew Ahrens at = strchr(zc->zc_name, '@'); 36813b2aab18SMatthew Ahrens if (at != NULL) { 36823b2aab18SMatthew Ahrens /* snaps must be in same fs */ 3683a0c1127bSSteven Hartland int error; 3684a0c1127bSSteven Hartland 36853b2aab18SMatthew Ahrens if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1)) 3686be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 36873b2aab18SMatthew Ahrens *at = '\0'; 36883b2aab18SMatthew Ahrens if (zc->zc_objset_type == DMU_OST_ZFS) { 3689a0c1127bSSteven Hartland error = dmu_objset_find(zc->zc_name, 36903b2aab18SMatthew Ahrens recursive_unmount, at + 1, 36913b2aab18SMatthew Ahrens recursive ? DS_FIND_CHILDREN : 0); 3692a0c1127bSSteven Hartland if (error != 0) { 3693a0c1127bSSteven Hartland *at = '@'; 36943b2aab18SMatthew Ahrens return (error); 3695a0c1127bSSteven Hartland } 36963b2aab18SMatthew Ahrens } 3697a0c1127bSSteven Hartland error = dsl_dataset_rename_snapshot(zc->zc_name, 3698a0c1127bSSteven Hartland at + 1, strchr(zc->zc_value, '@') + 1, recursive); 3699a0c1127bSSteven Hartland *at = '@'; 3700a0c1127bSSteven Hartland 3701a0c1127bSSteven Hartland return (error); 37023b2aab18SMatthew Ahrens } else { 37033b2aab18SMatthew Ahrens if (zc->zc_objset_type == DMU_OST_ZVOL) 37043b2aab18SMatthew Ahrens (void) zvol_remove_minor(zc->zc_name); 37053b2aab18SMatthew Ahrens return (dsl_dir_rename(zc->zc_name, zc->zc_value)); 3706fa9e4066Sahrens } 3707fa9e4066Sahrens } 3708fa9e4066Sahrens 370992241e0bSTom Erickson static int 371092241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 371192241e0bSTom Erickson { 371292241e0bSTom Erickson const char *propname = nvpair_name(pair); 371392241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 371492241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 371592241e0bSTom Erickson uint64_t intval; 371692241e0bSTom Erickson int err; 371792241e0bSTom Erickson 371892241e0bSTom Erickson if (prop == ZPROP_INVAL) { 371992241e0bSTom Erickson if (zfs_prop_user(propname)) { 372092241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 372192241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 372292241e0bSTom Erickson return (err); 372392241e0bSTom Erickson return (0); 372492241e0bSTom Erickson } 372592241e0bSTom Erickson 372692241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 372792241e0bSTom Erickson const char *perm = NULL; 372892241e0bSTom Erickson const char *uq_prefix = 372992241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 373092241e0bSTom Erickson const char *gq_prefix = 373192241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 373292241e0bSTom Erickson 373392241e0bSTom Erickson if (strncmp(propname, uq_prefix, 373492241e0bSTom Erickson strlen(uq_prefix)) == 0) { 373592241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 373692241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 373792241e0bSTom Erickson strlen(gq_prefix)) == 0) { 373892241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 373992241e0bSTom Erickson } else { 374092241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 3741be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 374292241e0bSTom Erickson } 374392241e0bSTom Erickson 374492241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 374592241e0bSTom Erickson return (err); 374692241e0bSTom Erickson return (0); 374792241e0bSTom Erickson } 374892241e0bSTom Erickson 3749be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 375092241e0bSTom Erickson } 375192241e0bSTom Erickson 375292241e0bSTom Erickson if (issnap) 3753be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 375492241e0bSTom Erickson 375592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 375692241e0bSTom Erickson /* 375792241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 375892241e0bSTom Erickson * format. 375992241e0bSTom Erickson */ 376092241e0bSTom Erickson nvlist_t *attrs; 376192241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 376292241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 376392241e0bSTom Erickson &pair) == 0); 376492241e0bSTom Erickson } 376592241e0bSTom Erickson 376692241e0bSTom Erickson /* 376792241e0bSTom Erickson * Check that this value is valid for this pool version 376892241e0bSTom Erickson */ 376992241e0bSTom Erickson switch (prop) { 377092241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 377192241e0bSTom Erickson /* 377292241e0bSTom Erickson * If the user specified gzip compression, make sure 377392241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 377492241e0bSTom Erickson * we'll catch them later. 377592241e0bSTom Erickson */ 3776b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0) { 377792241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 377892241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 377992241e0bSTom Erickson zfs_earlier_version(dsname, 378092241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 3781be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 378292241e0bSTom Erickson } 378392241e0bSTom Erickson 378492241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 378592241e0bSTom Erickson zfs_earlier_version(dsname, 378692241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 3787be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 378892241e0bSTom Erickson 3789a6f561b4SSašo Kiselkov if (intval == ZIO_COMPRESS_LZ4) { 3790a6f561b4SSašo Kiselkov spa_t *spa; 3791a6f561b4SSašo Kiselkov 3792a6f561b4SSašo Kiselkov if ((err = spa_open(dsname, &spa, FTAG)) != 0) 3793a6f561b4SSašo Kiselkov return (err); 3794a6f561b4SSašo Kiselkov 37952acef22dSMatthew Ahrens if (!spa_feature_is_enabled(spa, 37962acef22dSMatthew Ahrens SPA_FEATURE_LZ4_COMPRESS)) { 3797a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 3798be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3799a6f561b4SSašo Kiselkov } 3800a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 3801a6f561b4SSašo Kiselkov } 3802a6f561b4SSašo Kiselkov 380392241e0bSTom Erickson /* 380492241e0bSTom Erickson * If this is a bootable dataset then 380592241e0bSTom Erickson * verify that the compression algorithm 380692241e0bSTom Erickson * is supported for booting. We must return 380792241e0bSTom Erickson * something other than ENOTSUP since it 380892241e0bSTom Erickson * implies a downrev pool version. 380992241e0bSTom Erickson */ 381092241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 381192241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 3812be6fd75aSMatthew Ahrens return (SET_ERROR(ERANGE)); 381392241e0bSTom Erickson } 381492241e0bSTom Erickson } 381592241e0bSTom Erickson break; 381692241e0bSTom Erickson 381792241e0bSTom Erickson case ZFS_PROP_COPIES: 381892241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 3819be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 382092241e0bSTom Erickson break; 382192241e0bSTom Erickson 3822b5152584SMatthew Ahrens case ZFS_PROP_RECORDSIZE: 3823b5152584SMatthew Ahrens /* Record sizes above 128k need the feature to be enabled */ 3824b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0 && 3825b5152584SMatthew Ahrens intval > SPA_OLD_MAXBLOCKSIZE) { 3826b5152584SMatthew Ahrens spa_t *spa; 3827b5152584SMatthew Ahrens 3828b5152584SMatthew Ahrens /* 3829b5152584SMatthew Ahrens * If this is a bootable dataset then 3830b5152584SMatthew Ahrens * the we don't allow large (>128K) blocks, 3831b5152584SMatthew Ahrens * because GRUB doesn't support them. 3832b5152584SMatthew Ahrens */ 3833b5152584SMatthew Ahrens if (zfs_is_bootfs(dsname) && 3834b5152584SMatthew Ahrens intval > SPA_OLD_MAXBLOCKSIZE) { 38356de9bb56SMatthew Ahrens return (SET_ERROR(ERANGE)); 3836b5152584SMatthew Ahrens } 3837b5152584SMatthew Ahrens 3838b5152584SMatthew Ahrens /* 3839b5152584SMatthew Ahrens * We don't allow setting the property above 1MB, 3840b5152584SMatthew Ahrens * unless the tunable has been changed. 3841b5152584SMatthew Ahrens */ 3842b5152584SMatthew Ahrens if (intval > zfs_max_recordsize || 3843b5152584SMatthew Ahrens intval > SPA_MAXBLOCKSIZE) 38446de9bb56SMatthew Ahrens return (SET_ERROR(ERANGE)); 3845b5152584SMatthew Ahrens 3846b5152584SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 3847b5152584SMatthew Ahrens return (err); 3848b5152584SMatthew Ahrens 3849b5152584SMatthew Ahrens if (!spa_feature_is_enabled(spa, 3850b5152584SMatthew Ahrens SPA_FEATURE_LARGE_BLOCKS)) { 3851b5152584SMatthew Ahrens spa_close(spa, FTAG); 3852b5152584SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3853b5152584SMatthew Ahrens } 3854b5152584SMatthew Ahrens spa_close(spa, FTAG); 3855b5152584SMatthew Ahrens } 3856b5152584SMatthew Ahrens break; 3857b5152584SMatthew Ahrens 385892241e0bSTom Erickson case ZFS_PROP_SHARESMB: 385992241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 3860be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 386192241e0bSTom Erickson break; 386292241e0bSTom Erickson 386392241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 386492241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 386592241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 386692241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 386792241e0bSTom Erickson zfs_earlier_version(dsname, 386892241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 3869be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 387092241e0bSTom Erickson } 387192241e0bSTom Erickson break; 387245818ee1SMatthew Ahrens 387345818ee1SMatthew Ahrens case ZFS_PROP_CHECKSUM: 387445818ee1SMatthew Ahrens case ZFS_PROP_DEDUP: 387545818ee1SMatthew Ahrens { 387645818ee1SMatthew Ahrens spa_feature_t feature; 387745818ee1SMatthew Ahrens spa_t *spa; 387845818ee1SMatthew Ahrens 387945818ee1SMatthew Ahrens /* dedup feature version checks */ 388045818ee1SMatthew Ahrens if (prop == ZFS_PROP_DEDUP && 388145818ee1SMatthew Ahrens zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 388245818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 388345818ee1SMatthew Ahrens 388445818ee1SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) != 0) 388545818ee1SMatthew Ahrens return (SET_ERROR(EINVAL)); 388645818ee1SMatthew Ahrens 388745818ee1SMatthew Ahrens /* check prop value is enabled in features */ 388845818ee1SMatthew Ahrens feature = zio_checksum_to_feature(intval); 388945818ee1SMatthew Ahrens if (feature == SPA_FEATURE_NONE) 389045818ee1SMatthew Ahrens break; 389145818ee1SMatthew Ahrens 389245818ee1SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 389345818ee1SMatthew Ahrens return (err); 389445818ee1SMatthew Ahrens /* 389545818ee1SMatthew Ahrens * Salted checksums are not supported on root pools. 389645818ee1SMatthew Ahrens */ 389745818ee1SMatthew Ahrens if (spa_bootfs(spa) != 0 && 389845818ee1SMatthew Ahrens intval < ZIO_CHECKSUM_FUNCTIONS && 389945818ee1SMatthew Ahrens (zio_checksum_table[intval].ci_flags & 390045818ee1SMatthew Ahrens ZCHECKSUM_FLAG_SALTED)) { 390145818ee1SMatthew Ahrens spa_close(spa, FTAG); 390245818ee1SMatthew Ahrens return (SET_ERROR(ERANGE)); 390345818ee1SMatthew Ahrens } 390445818ee1SMatthew Ahrens if (!spa_feature_is_enabled(spa, feature)) { 390545818ee1SMatthew Ahrens spa_close(spa, FTAG); 390645818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 390745818ee1SMatthew Ahrens } 390845818ee1SMatthew Ahrens spa_close(spa, FTAG); 390945818ee1SMatthew Ahrens break; 391045818ee1SMatthew Ahrens } 391192241e0bSTom Erickson } 391292241e0bSTom Erickson 391392241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 391492241e0bSTom Erickson } 391592241e0bSTom Erickson 3916a6f561b4SSašo Kiselkov /* 3917a6f561b4SSašo Kiselkov * Checks for a race condition to make sure we don't increment a feature flag 3918a6f561b4SSašo Kiselkov * multiple times. 3919a6f561b4SSašo Kiselkov */ 3920a6f561b4SSašo Kiselkov static int 39213b2aab18SMatthew Ahrens zfs_prop_activate_feature_check(void *arg, dmu_tx_t *tx) 3922a6f561b4SSašo Kiselkov { 39233b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 39242acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 3925a6f561b4SSašo Kiselkov 39262acef22dSMatthew Ahrens if (!spa_feature_is_active(spa, *featurep)) 3927a6f561b4SSašo Kiselkov return (0); 3928a6f561b4SSašo Kiselkov else 3929be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 3930a6f561b4SSašo Kiselkov } 3931a6f561b4SSašo Kiselkov 3932a6f561b4SSašo Kiselkov /* 3933a6f561b4SSašo Kiselkov * The callback invoked on feature activation in the sync task caused by 3934a6f561b4SSašo Kiselkov * zfs_prop_activate_feature. 3935a6f561b4SSašo Kiselkov */ 3936a6f561b4SSašo Kiselkov static void 39373b2aab18SMatthew Ahrens zfs_prop_activate_feature_sync(void *arg, dmu_tx_t *tx) 3938a6f561b4SSašo Kiselkov { 39393b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 39402acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 3941a6f561b4SSašo Kiselkov 39422acef22dSMatthew Ahrens spa_feature_incr(spa, *featurep, tx); 3943a6f561b4SSašo Kiselkov } 3944a6f561b4SSašo Kiselkov 39453b2aab18SMatthew Ahrens /* 39463b2aab18SMatthew Ahrens * Activates a feature on a pool in response to a property setting. This 39473b2aab18SMatthew Ahrens * creates a new sync task which modifies the pool to reflect the feature 39483b2aab18SMatthew Ahrens * as being active. 39493b2aab18SMatthew Ahrens */ 39503b2aab18SMatthew Ahrens static int 39512acef22dSMatthew Ahrens zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature) 39523b2aab18SMatthew Ahrens { 39533b2aab18SMatthew Ahrens int err; 39543b2aab18SMatthew Ahrens 39553b2aab18SMatthew Ahrens /* EBUSY here indicates that the feature is already active */ 39563b2aab18SMatthew Ahrens err = dsl_sync_task(spa_name(spa), 39573b2aab18SMatthew Ahrens zfs_prop_activate_feature_check, zfs_prop_activate_feature_sync, 39587d46dc6cSMatthew Ahrens &feature, 2, ZFS_SPACE_CHECK_RESERVED); 39593b2aab18SMatthew Ahrens 39603b2aab18SMatthew Ahrens if (err != 0 && err != EBUSY) 39613b2aab18SMatthew Ahrens return (err); 39623b2aab18SMatthew Ahrens else 39633b2aab18SMatthew Ahrens return (0); 39643b2aab18SMatthew Ahrens } 39653b2aab18SMatthew Ahrens 396692241e0bSTom Erickson /* 396792241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 396892241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 396992241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 397092241e0bSTom Erickson * 397192241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 397292241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 397392241e0bSTom Erickson * of all the properties that failed a permission check along with the 397492241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 397592241e0bSTom Erickson * returned errlist. 397692241e0bSTom Erickson * 397792241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 397892241e0bSTom Erickson * pointed at by errlist is NULL. 397992241e0bSTom Erickson */ 398092241e0bSTom Erickson static int 398192241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 3982745cd3c5Smaybee { 3983745cd3c5Smaybee zfs_cmd_t *zc; 398492241e0bSTom Erickson nvpair_t *pair, *next_pair; 398592241e0bSTom Erickson nvlist_t *errors; 398692241e0bSTom Erickson int err, rv = 0; 3987745cd3c5Smaybee 3988745cd3c5Smaybee if (props == NULL) 398992241e0bSTom Erickson return (0); 399092241e0bSTom Erickson 399192241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 399292241e0bSTom Erickson 3993745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 3994745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 399592241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 399692241e0bSTom Erickson while (pair != NULL) { 399792241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 399892241e0bSTom Erickson 399992241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 400092241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 40014445fffbSMatthew Ahrens (err = zfs_secpolicy_inherit_prop(zc, NULL, CRED())) != 0) { 400292241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 400392241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 400492241e0bSTom Erickson zc->zc_value, err) == 0); 400592241e0bSTom Erickson } 400692241e0bSTom Erickson pair = next_pair; 4007745cd3c5Smaybee } 4008745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 400992241e0bSTom Erickson 401092241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 401192241e0bSTom Erickson nvlist_free(errors); 401292241e0bSTom Erickson errors = NULL; 401392241e0bSTom Erickson } else { 401492241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 401592241e0bSTom Erickson } 401692241e0bSTom Erickson 401792241e0bSTom Erickson if (errlist == NULL) 401892241e0bSTom Erickson nvlist_free(errors); 401992241e0bSTom Erickson else 402092241e0bSTom Erickson *errlist = errors; 402192241e0bSTom Erickson 402292241e0bSTom Erickson return (rv); 402392241e0bSTom Erickson } 402492241e0bSTom Erickson 402592241e0bSTom Erickson static boolean_t 402692241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 402792241e0bSTom Erickson { 402892241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 402992241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 403092241e0bSTom Erickson nvlist_t *attrs; 403192241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 403292241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 403392241e0bSTom Erickson &p1) == 0); 403492241e0bSTom Erickson } 403592241e0bSTom Erickson 403692241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 403792241e0bSTom Erickson nvlist_t *attrs; 403892241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 403992241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 404092241e0bSTom Erickson &p2) == 0); 404192241e0bSTom Erickson } 404292241e0bSTom Erickson 404392241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 404492241e0bSTom Erickson return (B_FALSE); 404592241e0bSTom Erickson 404692241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 404792241e0bSTom Erickson char *valstr1, *valstr2; 404892241e0bSTom Erickson 404992241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 405092241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 405192241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 405292241e0bSTom Erickson } else { 405392241e0bSTom Erickson uint64_t intval1, intval2; 405492241e0bSTom Erickson 405592241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 405692241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 405792241e0bSTom Erickson return (intval1 == intval2); 405892241e0bSTom Erickson } 4059745cd3c5Smaybee } 4060745cd3c5Smaybee 406192241e0bSTom Erickson /* 406292241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 406392241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 406492241e0bSTom Erickson * do not need to clear or restore properties that won't change. 406592241e0bSTom Erickson */ 406692241e0bSTom Erickson static void 406792241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 406892241e0bSTom Erickson { 406992241e0bSTom Erickson nvpair_t *pair, *next_pair; 407092241e0bSTom Erickson 407192241e0bSTom Erickson if (origprops == NULL) 407292241e0bSTom Erickson return; /* all props need to be received */ 407392241e0bSTom Erickson 407492241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 407592241e0bSTom Erickson while (pair != NULL) { 407692241e0bSTom Erickson const char *propname = nvpair_name(pair); 407792241e0bSTom Erickson nvpair_t *match; 407892241e0bSTom Erickson 407992241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 408092241e0bSTom Erickson 408192241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 408292241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 408392241e0bSTom Erickson goto next; /* need to set received value */ 408492241e0bSTom Erickson 408592241e0bSTom Erickson /* don't clear the existing received value */ 408692241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 408792241e0bSTom Erickson /* don't bother receiving the property */ 408892241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 408992241e0bSTom Erickson next: 409092241e0bSTom Erickson pair = next_pair; 409192241e0bSTom Erickson } 409292241e0bSTom Erickson } 409392241e0bSTom Erickson 4094*5878fad7SDan McDonald /* 4095*5878fad7SDan McDonald * Extract properties that cannot be set PRIOR to the receipt of a dataset. 4096*5878fad7SDan McDonald * For example, refquota cannot be set until after the receipt of a dataset, 4097*5878fad7SDan McDonald * because in replication streams, an older/earlier snapshot may exceed the 4098*5878fad7SDan McDonald * refquota. We want to receive the older/earlier snapshot, but setting 4099*5878fad7SDan McDonald * refquota pre-receipt will set the dsl's ACTUAL quota, which will prevent 4100*5878fad7SDan McDonald * the older/earlier snapshot from being received (with EDQUOT). 4101*5878fad7SDan McDonald * 4102*5878fad7SDan McDonald * The ZFS test "zfs_receive_011_pos" demonstrates such a scenario. 4103*5878fad7SDan McDonald * 4104*5878fad7SDan McDonald * libzfs will need to be judicious handling errors encountered by props 4105*5878fad7SDan McDonald * extracted by this function. 4106*5878fad7SDan McDonald */ 4107*5878fad7SDan McDonald static nvlist_t * 4108*5878fad7SDan McDonald extract_delay_props(nvlist_t *props) 4109*5878fad7SDan McDonald { 4110*5878fad7SDan McDonald nvlist_t *delayprops; 4111*5878fad7SDan McDonald nvpair_t *nvp, *tmp; 4112*5878fad7SDan McDonald static const zfs_prop_t delayable[] = { ZFS_PROP_REFQUOTA, 0 }; 4113*5878fad7SDan McDonald int i; 4114*5878fad7SDan McDonald 4115*5878fad7SDan McDonald VERIFY(nvlist_alloc(&delayprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 4116*5878fad7SDan McDonald 4117*5878fad7SDan McDonald for (nvp = nvlist_next_nvpair(props, NULL); nvp != NULL; 4118*5878fad7SDan McDonald nvp = nvlist_next_nvpair(props, nvp)) { 4119*5878fad7SDan McDonald /* 4120*5878fad7SDan McDonald * strcmp() is safe because zfs_prop_to_name() always returns 4121*5878fad7SDan McDonald * a bounded string. 4122*5878fad7SDan McDonald */ 4123*5878fad7SDan McDonald for (i = 0; delayable[i] != 0; i++) { 4124*5878fad7SDan McDonald if (strcmp(zfs_prop_to_name(delayable[i]), 4125*5878fad7SDan McDonald nvpair_name(nvp)) == 0) { 4126*5878fad7SDan McDonald break; 4127*5878fad7SDan McDonald } 4128*5878fad7SDan McDonald } 4129*5878fad7SDan McDonald if (delayable[i] != 0) { 4130*5878fad7SDan McDonald tmp = nvlist_prev_nvpair(props, nvp); 4131*5878fad7SDan McDonald VERIFY(nvlist_add_nvpair(delayprops, nvp) == 0); 4132*5878fad7SDan McDonald VERIFY(nvlist_remove_nvpair(props, nvp) == 0); 4133*5878fad7SDan McDonald nvp = tmp; 4134*5878fad7SDan McDonald } 4135*5878fad7SDan McDonald } 4136*5878fad7SDan McDonald 4137*5878fad7SDan McDonald if (nvlist_empty(delayprops)) { 4138*5878fad7SDan McDonald nvlist_free(delayprops); 4139*5878fad7SDan McDonald delayprops = NULL; 4140*5878fad7SDan McDonald } 4141*5878fad7SDan McDonald return (delayprops); 4142*5878fad7SDan McDonald } 4143*5878fad7SDan McDonald 414492241e0bSTom Erickson #ifdef DEBUG 414592241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 414692241e0bSTom Erickson #endif 414792241e0bSTom Erickson 41483cb34c60Sahrens /* 41493cb34c60Sahrens * inputs: 41503cb34c60Sahrens * zc_name name of containing filesystem 41513cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 41523cb34c60Sahrens * zc_value name of snapshot to create 41533cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 41543cb34c60Sahrens * zc_cookie file descriptor to recv from 41553cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 41563cb34c60Sahrens * zc_guid force flag 4157c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor 4158c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping (or zero on first call) 41599c3fd121SMatthew Ahrens * zc_resumable if data is incomplete assume sender will resume 41603cb34c60Sahrens * 41613cb34c60Sahrens * outputs: 41623cb34c60Sahrens * zc_cookie number of bytes read 416392241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 416492241e0bSTom Erickson * zc_obj zprop_errflags_t 4165c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping 41663cb34c60Sahrens */ 4167fa9e4066Sahrens static int 41683cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 4169fa9e4066Sahrens { 4170fa9e4066Sahrens file_t *fp; 41713cb34c60Sahrens dmu_recv_cookie_t drc; 4172f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 417392241e0bSTom Erickson int fd; 417492241e0bSTom Erickson int error = 0; 417592241e0bSTom Erickson int props_error = 0; 417692241e0bSTom Erickson nvlist_t *errors; 41773cb34c60Sahrens offset_t off; 417892241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 417992241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 4180*5878fad7SDan McDonald nvlist_t *delayprops = NULL; /* sent properties applied post-receive */ 41813b2aab18SMatthew Ahrens char *origin = NULL; 41823cb34c60Sahrens char *tosnap; 41833cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 418492241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 4185fa9e4066Sahrens 41863ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 4187f18faf3fSek strchr(zc->zc_value, '@') == NULL || 4188f18faf3fSek strchr(zc->zc_value, '%')) 4189be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 41903ccfa83cSahrens 41913cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 41923cb34c60Sahrens tosnap = strchr(tofs, '@'); 419392241e0bSTom Erickson *tosnap++ = '\0'; 41943cb34c60Sahrens 41953cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 41963cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 4197478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 41983cb34c60Sahrens return (error); 41993cb34c60Sahrens 4200fa9e4066Sahrens fd = zc->zc_cookie; 4201fa9e4066Sahrens fp = getf(fd); 42023cb34c60Sahrens if (fp == NULL) { 42033cb34c60Sahrens nvlist_free(props); 4204be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 42053cb34c60Sahrens } 4206f18faf3fSek 42079c3fd121SMatthew Ahrens errors = fnvlist_alloc(); 420892241e0bSTom Erickson 42093b2aab18SMatthew Ahrens if (zc->zc_string[0]) 42103b2aab18SMatthew Ahrens origin = zc->zc_string; 42113b2aab18SMatthew Ahrens 42123b2aab18SMatthew Ahrens error = dmu_recv_begin(tofs, tosnap, 42139c3fd121SMatthew Ahrens &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc); 42143b2aab18SMatthew Ahrens if (error != 0) 42153b2aab18SMatthew Ahrens goto out; 42163b2aab18SMatthew Ahrens 42173b2aab18SMatthew Ahrens /* 42183b2aab18SMatthew Ahrens * Set properties before we receive the stream so that they are applied 42193b2aab18SMatthew Ahrens * to the new data. Note that we must call dmu_recv_stream() if 42203b2aab18SMatthew Ahrens * dmu_recv_begin() succeeds. 42213b2aab18SMatthew Ahrens */ 42223b2aab18SMatthew Ahrens if (props != NULL && !drc.drc_newfs) { 42233b2aab18SMatthew Ahrens if (spa_version(dsl_dataset_get_spa(drc.drc_ds)) >= 42243b2aab18SMatthew Ahrens SPA_VERSION_RECVD_PROPS && 42253b2aab18SMatthew Ahrens !dsl_prop_get_hasrecvd(tofs)) 422692241e0bSTom Erickson first_recvd_props = B_TRUE; 422792241e0bSTom Erickson 4228745cd3c5Smaybee /* 422992241e0bSTom Erickson * If new received properties are supplied, they are to 423092241e0bSTom Erickson * completely replace the existing received properties, so stash 423192241e0bSTom Erickson * away the existing ones. 4232745cd3c5Smaybee */ 42333b2aab18SMatthew Ahrens if (dsl_prop_get_received(tofs, &origprops) == 0) { 423492241e0bSTom Erickson nvlist_t *errlist = NULL; 423592241e0bSTom Erickson /* 423692241e0bSTom Erickson * Don't bother writing a property if its value won't 423792241e0bSTom Erickson * change (and avoid the unnecessary security checks). 423892241e0bSTom Erickson * 423992241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 424092241e0bSTom Erickson * special case where we blow away all local properties 424192241e0bSTom Erickson * regardless. 424292241e0bSTom Erickson */ 424392241e0bSTom Erickson if (!first_recvd_props) 424492241e0bSTom Erickson props_reduce(props, origprops); 42453b2aab18SMatthew Ahrens if (zfs_check_clearable(tofs, origprops, &errlist) != 0) 424692241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 424792241e0bSTom Erickson nvlist_free(errlist); 42483cb34c60Sahrens 42493b2aab18SMatthew Ahrens if (clear_received_props(tofs, origprops, 42503b2aab18SMatthew Ahrens first_recvd_props ? NULL : props) != 0) 425192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 42523b2aab18SMatthew Ahrens } else { 425392241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 425492241e0bSTom Erickson } 42553b2aab18SMatthew Ahrens } 425692241e0bSTom Erickson 42573b2aab18SMatthew Ahrens if (props != NULL) { 42583b2aab18SMatthew Ahrens props_error = dsl_prop_set_hasrecvd(tofs); 42593b2aab18SMatthew Ahrens 42603b2aab18SMatthew Ahrens if (props_error == 0) { 4261*5878fad7SDan McDonald delayprops = extract_delay_props(props); 42623b2aab18SMatthew Ahrens (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 42633b2aab18SMatthew Ahrens props, errors); 42643b2aab18SMatthew Ahrens } 426592241e0bSTom Erickson } 426692241e0bSTom Erickson 42673cb34c60Sahrens off = fp->f_offset; 4268c99e4bdcSChris Kirby error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, 4269c99e4bdcSChris Kirby &zc->zc_action_handle); 4270a2eea2e1Sahrens 4271f4b94bdeSMatthew Ahrens if (error == 0) { 4272f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 4273745cd3c5Smaybee 4274f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 4275f4b94bdeSMatthew Ahrens /* online recv */ 4276f4b94bdeSMatthew Ahrens int end_err; 4277745cd3c5Smaybee 4278503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 4279f4b94bdeSMatthew Ahrens /* 4280f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 4281f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 4282f4b94bdeSMatthew Ahrens */ 428391948b51SKeith M Wesolowski end_err = dmu_recv_end(&drc, zfsvfs); 42845c703fceSGeorge Wilson if (error == 0) 42855c703fceSGeorge Wilson error = zfs_resume_fs(zfsvfs, tofs); 4286f4b94bdeSMatthew Ahrens error = error ? error : end_err; 4287f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 4288745cd3c5Smaybee } else { 428991948b51SKeith M Wesolowski error = dmu_recv_end(&drc, NULL); 42903cb34c60Sahrens } 4291*5878fad7SDan McDonald 4292*5878fad7SDan McDonald /* Set delayed properties now, after we're done receiving. */ 4293*5878fad7SDan McDonald if (delayprops != NULL && error == 0) { 4294*5878fad7SDan McDonald (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 4295*5878fad7SDan McDonald delayprops, errors); 4296*5878fad7SDan McDonald } 4297*5878fad7SDan McDonald } 4298*5878fad7SDan McDonald 4299*5878fad7SDan McDonald if (delayprops != NULL) { 4300*5878fad7SDan McDonald /* 4301*5878fad7SDan McDonald * Merge delayed props back in with initial props, in case 4302*5878fad7SDan McDonald * we're DEBUG and zfs_ioc_recv_inject_err is set (which means 4303*5878fad7SDan McDonald * we have to make sure clear_received_props() includes 4304*5878fad7SDan McDonald * the delayed properties). 4305*5878fad7SDan McDonald * 4306*5878fad7SDan McDonald * Since zfs_ioc_recv_inject_err is only in DEBUG kernels, 4307*5878fad7SDan McDonald * using ASSERT() will be just like a VERIFY. 4308*5878fad7SDan McDonald */ 4309*5878fad7SDan McDonald ASSERT(nvlist_merge(props, delayprops, 0) == 0); 4310*5878fad7SDan McDonald nvlist_free(delayprops); 4311*5878fad7SDan McDonald } 4312*5878fad7SDan McDonald 4313*5878fad7SDan McDonald /* 4314*5878fad7SDan McDonald * Now that all props, initial and delayed, are set, report the prop 4315*5878fad7SDan McDonald * errors to the caller. 4316*5878fad7SDan McDonald */ 4317*5878fad7SDan McDonald if (zc->zc_nvlist_dst_size != 0 && 4318*5878fad7SDan McDonald (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || 4319*5878fad7SDan McDonald put_nvlist(zc, errors) != 0)) { 4320*5878fad7SDan McDonald /* 4321*5878fad7SDan McDonald * Caller made zc->zc_nvlist_dst less than the minimum expected 4322*5878fad7SDan McDonald * size or supplied an invalid address. 4323*5878fad7SDan McDonald */ 4324*5878fad7SDan McDonald props_error = SET_ERROR(EINVAL); 432547f263f4Sek } 43263cb34c60Sahrens 43273cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 43283cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 43293cb34c60Sahrens fp->f_offset = off; 4330a2eea2e1Sahrens 433192241e0bSTom Erickson #ifdef DEBUG 433292241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 433392241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 433492241e0bSTom Erickson error = 1; 433592241e0bSTom Erickson } 433692241e0bSTom Erickson #endif 4337745cd3c5Smaybee /* 4338745cd3c5Smaybee * On error, restore the original props. 4339745cd3c5Smaybee */ 43403b2aab18SMatthew Ahrens if (error != 0 && props != NULL && !drc.drc_newfs) { 43413b2aab18SMatthew Ahrens if (clear_received_props(tofs, props, NULL) != 0) { 43423b2aab18SMatthew Ahrens /* 43433b2aab18SMatthew Ahrens * We failed to clear the received properties. 43443b2aab18SMatthew Ahrens * Since we may have left a $recvd value on the 43453b2aab18SMatthew Ahrens * system, we can't clear the $hasrecvd flag. 43463b2aab18SMatthew Ahrens */ 434792241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 43483b2aab18SMatthew Ahrens } else if (first_recvd_props) { 43493b2aab18SMatthew Ahrens dsl_prop_unset_hasrecvd(tofs); 435092241e0bSTom Erickson } 435192241e0bSTom Erickson 435292241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 435392241e0bSTom Erickson /* We failed to stash the original properties. */ 435492241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 435592241e0bSTom Erickson } 435692241e0bSTom Erickson 435792241e0bSTom Erickson /* 435892241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 435992241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 436092241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 436192241e0bSTom Erickson * first new-style receive. 436292241e0bSTom Erickson */ 436392241e0bSTom Erickson if (origprops != NULL && 436492241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 436592241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 436692241e0bSTom Erickson origprops, NULL) != 0) { 436792241e0bSTom Erickson /* 436892241e0bSTom Erickson * We stashed the original properties but failed to 436992241e0bSTom Erickson * restore them. 437092241e0bSTom Erickson */ 437192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 437292241e0bSTom Erickson } 4373745cd3c5Smaybee } 4374745cd3c5Smaybee out: 4375745cd3c5Smaybee nvlist_free(props); 4376745cd3c5Smaybee nvlist_free(origprops); 437792241e0bSTom Erickson nvlist_free(errors); 4378fa9e4066Sahrens releasef(fd); 437992241e0bSTom Erickson 438092241e0bSTom Erickson if (error == 0) 438192241e0bSTom Erickson error = props_error; 438292241e0bSTom Erickson 4383fa9e4066Sahrens return (error); 4384fa9e4066Sahrens } 4385fa9e4066Sahrens 43863cb34c60Sahrens /* 43873cb34c60Sahrens * inputs: 43883cb34c60Sahrens * zc_name name of snapshot to send 43893cb34c60Sahrens * zc_cookie file descriptor to send stream to 4390a7f53a56SChris Kirby * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) 4391a7f53a56SChris Kirby * zc_sendobj objsetid of snapshot to send 4392a7f53a56SChris Kirby * zc_fromobj objsetid of incremental fromsnap (may be zero) 439319b94df9SMatthew Ahrens * zc_guid if set, estimate size of stream only. zc_cookie is ignored. 439419b94df9SMatthew Ahrens * output size in zc_objset_type. 4395b5152584SMatthew Ahrens * zc_flags lzc_send_flags 43963cb34c60Sahrens * 439778f17100SMatthew Ahrens * outputs: 439878f17100SMatthew Ahrens * zc_objset_type estimated size, if zc_guid is set 43993cb34c60Sahrens */ 4400fa9e4066Sahrens static int 44013cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 4402fa9e4066Sahrens { 4403fa9e4066Sahrens int error; 44043cb34c60Sahrens offset_t off; 440519b94df9SMatthew Ahrens boolean_t estimate = (zc->zc_guid != 0); 44065d7b4d43SMatthew Ahrens boolean_t embedok = (zc->zc_flags & 0x1); 4407b5152584SMatthew Ahrens boolean_t large_block_ok = (zc->zc_flags & 0x2); 4408fa9e4066Sahrens 44093b2aab18SMatthew Ahrens if (zc->zc_obj != 0) { 44103b2aab18SMatthew Ahrens dsl_pool_t *dp; 44113b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 4412a7f53a56SChris Kirby 44133b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 44143b2aab18SMatthew Ahrens if (error != 0) 4415a7f53a56SChris Kirby return (error); 44163b2aab18SMatthew Ahrens 44173b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 44183b2aab18SMatthew Ahrens if (error != 0) { 44193b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 4420fa9e4066Sahrens return (error); 4421fa9e4066Sahrens } 44223b2aab18SMatthew Ahrens 44233b2aab18SMatthew Ahrens if (dsl_dir_is_clone(tosnap->ds_dir)) 4424c1379625SJustin T. Gibbs zc->zc_fromobj = 4425c1379625SJustin T. Gibbs dsl_dir_phys(tosnap->ds_dir)->dd_origin_obj; 44263b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 44273b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 44284445fffbSMatthew Ahrens } 44294445fffbSMatthew Ahrens 44303b2aab18SMatthew Ahrens if (estimate) { 44313b2aab18SMatthew Ahrens dsl_pool_t *dp; 44323b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 44333b2aab18SMatthew Ahrens dsl_dataset_t *fromsnap = NULL; 44344445fffbSMatthew Ahrens 44353b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 44363b2aab18SMatthew Ahrens if (error != 0) 44373b2aab18SMatthew Ahrens return (error); 44383b2aab18SMatthew Ahrens 44393b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 44403b2aab18SMatthew Ahrens if (error != 0) { 44413b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 44423b2aab18SMatthew Ahrens return (error); 44434445fffbSMatthew Ahrens } 44444445fffbSMatthew Ahrens 44453b2aab18SMatthew Ahrens if (zc->zc_fromobj != 0) { 44463b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, 44473b2aab18SMatthew Ahrens FTAG, &fromsnap); 44483b2aab18SMatthew Ahrens if (error != 0) { 44493b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 44503b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 44514445fffbSMatthew Ahrens return (error); 44524445fffbSMatthew Ahrens } 44534445fffbSMatthew Ahrens } 4454fa9e4066Sahrens 44554445fffbSMatthew Ahrens error = dmu_send_estimate(tosnap, fromsnap, 445619b94df9SMatthew Ahrens &zc->zc_objset_type); 44573b2aab18SMatthew Ahrens 44583b2aab18SMatthew Ahrens if (fromsnap != NULL) 44593b2aab18SMatthew Ahrens dsl_dataset_rele(fromsnap, FTAG); 44603b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 44613b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 446219b94df9SMatthew Ahrens } else { 446319b94df9SMatthew Ahrens file_t *fp = getf(zc->zc_cookie); 44643b2aab18SMatthew Ahrens if (fp == NULL) 4465be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 4466fa9e4066Sahrens 446719b94df9SMatthew Ahrens off = fp->f_offset; 44683b2aab18SMatthew Ahrens error = dmu_send_obj(zc->zc_name, zc->zc_sendobj, 4469b5152584SMatthew Ahrens zc->zc_fromobj, embedok, large_block_ok, 4470b5152584SMatthew Ahrens zc->zc_cookie, fp->f_vnode, &off); 4471fa9e4066Sahrens 447219b94df9SMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 447319b94df9SMatthew Ahrens fp->f_offset = off; 447419b94df9SMatthew Ahrens releasef(zc->zc_cookie); 447519b94df9SMatthew Ahrens } 4476fa9e4066Sahrens return (error); 4477fa9e4066Sahrens } 4478fa9e4066Sahrens 44794e3c9f44SBill Pijewski /* 44804e3c9f44SBill Pijewski * inputs: 44814e3c9f44SBill Pijewski * zc_name name of snapshot on which to report progress 44824e3c9f44SBill Pijewski * zc_cookie file descriptor of send stream 44834e3c9f44SBill Pijewski * 44844e3c9f44SBill Pijewski * outputs: 44854e3c9f44SBill Pijewski * zc_cookie number of bytes written in send stream thus far 44864e3c9f44SBill Pijewski */ 44874e3c9f44SBill Pijewski static int 44884e3c9f44SBill Pijewski zfs_ioc_send_progress(zfs_cmd_t *zc) 44894e3c9f44SBill Pijewski { 44903b2aab18SMatthew Ahrens dsl_pool_t *dp; 44914e3c9f44SBill Pijewski dsl_dataset_t *ds; 44924e3c9f44SBill Pijewski dmu_sendarg_t *dsp = NULL; 44934e3c9f44SBill Pijewski int error; 44944e3c9f44SBill Pijewski 44953b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 44963b2aab18SMatthew Ahrens if (error != 0) 44973b2aab18SMatthew Ahrens return (error); 44983b2aab18SMatthew Ahrens 44993b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); 45003b2aab18SMatthew Ahrens if (error != 0) { 45013b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 45024e3c9f44SBill Pijewski return (error); 45033b2aab18SMatthew Ahrens } 45044e3c9f44SBill Pijewski 45054e3c9f44SBill Pijewski mutex_enter(&ds->ds_sendstream_lock); 45064e3c9f44SBill Pijewski 45074e3c9f44SBill Pijewski /* 45084e3c9f44SBill Pijewski * Iterate over all the send streams currently active on this dataset. 45094e3c9f44SBill Pijewski * If there's one which matches the specified file descriptor _and_ the 45104e3c9f44SBill Pijewski * stream was started by the current process, return the progress of 45114e3c9f44SBill Pijewski * that stream. 45124e3c9f44SBill Pijewski */ 45134e3c9f44SBill Pijewski for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL; 45144e3c9f44SBill Pijewski dsp = list_next(&ds->ds_sendstreams, dsp)) { 45154e3c9f44SBill Pijewski if (dsp->dsa_outfd == zc->zc_cookie && 45164e3c9f44SBill Pijewski dsp->dsa_proc == curproc) 45174e3c9f44SBill Pijewski break; 45184e3c9f44SBill Pijewski } 45194e3c9f44SBill Pijewski 45204e3c9f44SBill Pijewski if (dsp != NULL) 45214e3c9f44SBill Pijewski zc->zc_cookie = *(dsp->dsa_off); 45224e3c9f44SBill Pijewski else 4523be6fd75aSMatthew Ahrens error = SET_ERROR(ENOENT); 45244e3c9f44SBill Pijewski 45254e3c9f44SBill Pijewski mutex_exit(&ds->ds_sendstream_lock); 45264e3c9f44SBill Pijewski dsl_dataset_rele(ds, FTAG); 45273b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 45284e3c9f44SBill Pijewski return (error); 45294e3c9f44SBill Pijewski } 45304e3c9f44SBill Pijewski 4531ea8dc4b6Seschrock static int 4532ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 4533ea8dc4b6Seschrock { 4534ea8dc4b6Seschrock int id, error; 4535ea8dc4b6Seschrock 4536ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 4537ea8dc4b6Seschrock &zc->zc_inject_record); 4538ea8dc4b6Seschrock 4539ea8dc4b6Seschrock if (error == 0) 4540ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 4541ea8dc4b6Seschrock 4542ea8dc4b6Seschrock return (error); 4543ea8dc4b6Seschrock } 4544ea8dc4b6Seschrock 4545ea8dc4b6Seschrock static int 4546ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 4547ea8dc4b6Seschrock { 4548ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 4549ea8dc4b6Seschrock } 4550ea8dc4b6Seschrock 4551ea8dc4b6Seschrock static int 4552ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 4553ea8dc4b6Seschrock { 4554ea8dc4b6Seschrock int id = (int)zc->zc_guid; 4555ea8dc4b6Seschrock int error; 4556ea8dc4b6Seschrock 4557ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 4558ea8dc4b6Seschrock &zc->zc_inject_record); 4559ea8dc4b6Seschrock 4560ea8dc4b6Seschrock zc->zc_guid = id; 4561ea8dc4b6Seschrock 4562ea8dc4b6Seschrock return (error); 4563ea8dc4b6Seschrock } 4564ea8dc4b6Seschrock 4565ea8dc4b6Seschrock static int 4566ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 4567ea8dc4b6Seschrock { 4568ea8dc4b6Seschrock spa_t *spa; 4569ea8dc4b6Seschrock int error; 4570e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 4571ea8dc4b6Seschrock 4572ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 4573ea8dc4b6Seschrock return (error); 4574ea8dc4b6Seschrock 4575e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 4576ea8dc4b6Seschrock &count); 4577ea8dc4b6Seschrock if (error == 0) 4578e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 4579ea8dc4b6Seschrock else 4580e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 4581ea8dc4b6Seschrock 4582ea8dc4b6Seschrock spa_close(spa, FTAG); 4583ea8dc4b6Seschrock 4584ea8dc4b6Seschrock return (error); 4585ea8dc4b6Seschrock } 4586ea8dc4b6Seschrock 4587ea8dc4b6Seschrock static int 4588ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 4589ea8dc4b6Seschrock { 4590ea8dc4b6Seschrock spa_t *spa; 4591ea8dc4b6Seschrock vdev_t *vd; 4592bb8b5132Sek int error; 4593ea8dc4b6Seschrock 4594b87f3af3Sperrin /* 4595b87f3af3Sperrin * On zpool clear we also fix up missing slogs 4596b87f3af3Sperrin */ 4597b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 4598b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 4599b87f3af3Sperrin if (spa == NULL) { 4600b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4601be6fd75aSMatthew Ahrens return (SET_ERROR(EIO)); 4602b87f3af3Sperrin } 4603b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 4604b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 4605b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 4606b87f3af3Sperrin } 4607468c413aSTim Haley spa->spa_last_open_failed = 0; 4608b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4609b87f3af3Sperrin 4610c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 4611468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 4612468c413aSTim Haley } else { 4613468c413aSTim Haley nvlist_t *policy; 4614468c413aSTim Haley nvlist_t *config = NULL; 4615468c413aSTim Haley 4616468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 4617be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 4618468c413aSTim Haley 4619468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 4620468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 4621468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 4622468c413aSTim Haley policy, &config); 4623468c413aSTim Haley if (config != NULL) { 46244b964adaSGeorge Wilson int err; 46254b964adaSGeorge Wilson 46264b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 46274b964adaSGeorge Wilson error = err; 4628468c413aSTim Haley nvlist_free(config); 4629468c413aSTim Haley } 4630468c413aSTim Haley nvlist_free(policy); 4631468c413aSTim Haley } 4632468c413aSTim Haley } 4633468c413aSTim Haley 46343b2aab18SMatthew Ahrens if (error != 0) 4635ea8dc4b6Seschrock return (error); 4636ea8dc4b6Seschrock 46378f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4638ea8dc4b6Seschrock 4639e9dbad6fSeschrock if (zc->zc_guid == 0) { 4640ea8dc4b6Seschrock vd = NULL; 4641c5904d13Seschrock } else { 4642c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 4643fa94a07fSbrendan if (vd == NULL) { 4644e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 4645fa94a07fSbrendan spa_close(spa, FTAG); 4646be6fd75aSMatthew Ahrens return (SET_ERROR(ENODEV)); 4647fa94a07fSbrendan } 4648ea8dc4b6Seschrock } 4649ea8dc4b6Seschrock 4650e14bb325SJeff Bonwick vdev_clear(spa, vd); 4651e14bb325SJeff Bonwick 4652e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 4653ea8dc4b6Seschrock 4654e14bb325SJeff Bonwick /* 4655e14bb325SJeff Bonwick * Resume any suspended I/Os. 4656e14bb325SJeff Bonwick */ 465754d692b7SGeorge Wilson if (zio_resume(spa) != 0) 4658be6fd75aSMatthew Ahrens error = SET_ERROR(EIO); 4659ea8dc4b6Seschrock 4660ea8dc4b6Seschrock spa_close(spa, FTAG); 4661ea8dc4b6Seschrock 466254d692b7SGeorge Wilson return (error); 4663ea8dc4b6Seschrock } 4664ea8dc4b6Seschrock 46654263d13fSGeorge Wilson static int 46664263d13fSGeorge Wilson zfs_ioc_pool_reopen(zfs_cmd_t *zc) 46674263d13fSGeorge Wilson { 46684263d13fSGeorge Wilson spa_t *spa; 46694263d13fSGeorge Wilson int error; 46704263d13fSGeorge Wilson 46714263d13fSGeorge Wilson error = spa_open(zc->zc_name, &spa, FTAG); 46723b2aab18SMatthew Ahrens if (error != 0) 46734263d13fSGeorge Wilson return (error); 46744263d13fSGeorge Wilson 46754263d13fSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4676d6afdce2SGeorge Wilson 4677d6afdce2SGeorge Wilson /* 4678d6afdce2SGeorge Wilson * If a resilver is already in progress then set the 4679d6afdce2SGeorge Wilson * spa_scrub_reopen flag to B_TRUE so that we don't restart 4680d6afdce2SGeorge Wilson * the scan as a side effect of the reopen. Otherwise, let 4681d6afdce2SGeorge Wilson * vdev_open() decided if a resilver is required. 4682d6afdce2SGeorge Wilson */ 4683d6afdce2SGeorge Wilson spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); 46844263d13fSGeorge Wilson vdev_reopen(spa->spa_root_vdev); 4685d6afdce2SGeorge Wilson spa->spa_scrub_reopen = B_FALSE; 4686d6afdce2SGeorge Wilson 46874263d13fSGeorge Wilson (void) spa_vdev_state_exit(spa, NULL, 0); 46884263d13fSGeorge Wilson spa_close(spa, FTAG); 46894263d13fSGeorge Wilson return (0); 46904263d13fSGeorge Wilson } 46913cb34c60Sahrens /* 46923cb34c60Sahrens * inputs: 46933cb34c60Sahrens * zc_name name of filesystem 46943cb34c60Sahrens * zc_value name of origin snapshot 46953cb34c60Sahrens * 4696681d9761SEric Taylor * outputs: 4697681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 46983cb34c60Sahrens */ 469999653d4eSeschrock static int 470099653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 470199653d4eSeschrock { 47020b69c2f0Sahrens char *cp; 47030b69c2f0Sahrens 47040b69c2f0Sahrens /* 47050b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 47060b69c2f0Sahrens * it's easier. 47070b69c2f0Sahrens */ 4708e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 47090b69c2f0Sahrens if (cp) 47100b69c2f0Sahrens *cp = '\0'; 4711e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 47123b2aab18SMatthew Ahrens zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS); 4713681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 471499653d4eSeschrock } 471599653d4eSeschrock 471614843421SMatthew Ahrens /* 471714843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 471814843421SMatthew Ahrens * 471914843421SMatthew Ahrens * inputs: 472014843421SMatthew Ahrens * zc_name name of filesystem 472114843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 472214843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 472314843421SMatthew Ahrens * zc_guid RID/UID/GID 472414843421SMatthew Ahrens * 472514843421SMatthew Ahrens * outputs: 472614843421SMatthew Ahrens * zc_cookie property value 472714843421SMatthew Ahrens */ 472814843421SMatthew Ahrens static int 472914843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 473014843421SMatthew Ahrens { 473114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 473214843421SMatthew Ahrens int error; 473314843421SMatthew Ahrens 473414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 4735be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 473614843421SMatthew Ahrens 47371412a1a2SMark Shellenbaum error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 47383b2aab18SMatthew Ahrens if (error != 0) 473914843421SMatthew Ahrens return (error); 474014843421SMatthew Ahrens 474114843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 474214843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 474314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 474414843421SMatthew Ahrens 474514843421SMatthew Ahrens return (error); 474614843421SMatthew Ahrens } 474714843421SMatthew Ahrens 474814843421SMatthew Ahrens /* 474914843421SMatthew Ahrens * inputs: 475014843421SMatthew Ahrens * zc_name name of filesystem 475114843421SMatthew Ahrens * zc_cookie zap cursor 475214843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 475314843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 475414843421SMatthew Ahrens * 475514843421SMatthew Ahrens * outputs: 475614843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 475714843421SMatthew Ahrens * zc_cookie zap cursor 475814843421SMatthew Ahrens */ 475914843421SMatthew Ahrens static int 476014843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 476114843421SMatthew Ahrens { 476214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 4763eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 476414843421SMatthew Ahrens 4765eeb85002STim Haley if (bufsize <= 0) 4766be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 4767eeb85002STim Haley 47681412a1a2SMark Shellenbaum int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 47693b2aab18SMatthew Ahrens if (error != 0) 477014843421SMatthew Ahrens return (error); 477114843421SMatthew Ahrens 477214843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 477314843421SMatthew Ahrens 477414843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 477514843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 477614843421SMatthew Ahrens 477714843421SMatthew Ahrens if (error == 0) { 477814843421SMatthew Ahrens error = xcopyout(buf, 477914843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 478014843421SMatthew Ahrens zc->zc_nvlist_dst_size); 478114843421SMatthew Ahrens } 478214843421SMatthew Ahrens kmem_free(buf, bufsize); 478314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 478414843421SMatthew Ahrens 478514843421SMatthew Ahrens return (error); 478614843421SMatthew Ahrens } 478714843421SMatthew Ahrens 478814843421SMatthew Ahrens /* 478914843421SMatthew Ahrens * inputs: 479014843421SMatthew Ahrens * zc_name name of filesystem 479114843421SMatthew Ahrens * 479214843421SMatthew Ahrens * outputs: 479314843421SMatthew Ahrens * none 479414843421SMatthew Ahrens */ 479514843421SMatthew Ahrens static int 479614843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 479714843421SMatthew Ahrens { 479814843421SMatthew Ahrens objset_t *os; 47991195e687SMark J Musante int error = 0; 480014843421SMatthew Ahrens zfsvfs_t *zfsvfs; 480114843421SMatthew Ahrens 480214843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 4803503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 480414843421SMatthew Ahrens /* 480514843421SMatthew Ahrens * If userused is not enabled, it may be because the 480614843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 480714843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 480814843421SMatthew Ahrens */ 4809503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 481091948b51SKeith M Wesolowski if (error == 0) { 481191948b51SKeith M Wesolowski dmu_objset_refresh_ownership(zfsvfs->z_os, 481291948b51SKeith M Wesolowski zfsvfs); 4813503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 481491948b51SKeith M Wesolowski } 481514843421SMatthew Ahrens } 481614843421SMatthew Ahrens if (error == 0) 481714843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 481814843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 481914843421SMatthew Ahrens } else { 4820503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 4821503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 48223b2aab18SMatthew Ahrens if (error != 0) 482314843421SMatthew Ahrens return (error); 482414843421SMatthew Ahrens 482514843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 4826503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 482714843421SMatthew Ahrens } 482814843421SMatthew Ahrens 482914843421SMatthew Ahrens return (error); 483014843421SMatthew Ahrens } 483114843421SMatthew Ahrens 4832ecd6cf80Smarks /* 4833ecd6cf80Smarks * We don't want to have a hard dependency 4834ecd6cf80Smarks * against some special symbols in sharefs 4835da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 4836ecd6cf80Smarks * the first file system is shared. 4837da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 4838ecd6cf80Smarks */ 4839da6c28aaSamw int (*znfsexport_fs)(void *arg); 4840ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 4841da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 4842da6c28aaSamw 4843da6c28aaSamw int zfs_nfsshare_inited; 4844da6c28aaSamw int zfs_smbshare_inited; 4845ecd6cf80Smarks 4846ecd6cf80Smarks ddi_modhandle_t nfs_mod; 4847ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 4848da6c28aaSamw ddi_modhandle_t smbsrv_mod; 4849ecd6cf80Smarks kmutex_t zfs_share_lock; 4850ecd6cf80Smarks 4851da6c28aaSamw static int 4852da6c28aaSamw zfs_init_sharefs() 4853da6c28aaSamw { 4854da6c28aaSamw int error; 4855da6c28aaSamw 4856da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 4857da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 4858da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 4859da6c28aaSamw ddi_modopen("fs/sharefs", 4860da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4861be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4862da6c28aaSamw } 4863da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 4864da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 4865da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 4866be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4867da6c28aaSamw } 4868da6c28aaSamw return (0); 4869da6c28aaSamw } 4870da6c28aaSamw 4871ecd6cf80Smarks static int 4872ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 4873ecd6cf80Smarks { 4874ecd6cf80Smarks int error; 4875ecd6cf80Smarks int opcode; 4876ecd6cf80Smarks 4877da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4878da6c28aaSamw case ZFS_SHARE_NFS: 4879da6c28aaSamw case ZFS_UNSHARE_NFS: 4880da6c28aaSamw if (zfs_nfsshare_inited == 0) { 4881da6c28aaSamw mutex_enter(&zfs_share_lock); 4882da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 4883da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4884da6c28aaSamw mutex_exit(&zfs_share_lock); 4885be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4886da6c28aaSamw } 4887da6c28aaSamw if (znfsexport_fs == NULL && 4888da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 4889da6c28aaSamw ddi_modsym(nfs_mod, 4890da6c28aaSamw "nfs_export", &error)) == NULL)) { 4891da6c28aaSamw mutex_exit(&zfs_share_lock); 4892be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4893da6c28aaSamw } 4894da6c28aaSamw error = zfs_init_sharefs(); 48953b2aab18SMatthew Ahrens if (error != 0) { 4896da6c28aaSamw mutex_exit(&zfs_share_lock); 4897be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4898da6c28aaSamw } 4899da6c28aaSamw zfs_nfsshare_inited = 1; 4900ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4901ecd6cf80Smarks } 4902da6c28aaSamw break; 4903da6c28aaSamw case ZFS_SHARE_SMB: 4904da6c28aaSamw case ZFS_UNSHARE_SMB: 4905da6c28aaSamw if (zfs_smbshare_inited == 0) { 4906da6c28aaSamw mutex_enter(&zfs_share_lock); 4907da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 4908da6c28aaSamw ddi_modopen("drv/smbsrv", 4909da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4910da6c28aaSamw mutex_exit(&zfs_share_lock); 4911be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4912da6c28aaSamw } 4913da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 4914da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 4915faa1795aSjb "smb_server_share", &error)) == NULL)) { 4916da6c28aaSamw mutex_exit(&zfs_share_lock); 4917be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4918da6c28aaSamw } 4919da6c28aaSamw error = zfs_init_sharefs(); 49203b2aab18SMatthew Ahrens if (error != 0) { 4921da6c28aaSamw mutex_exit(&zfs_share_lock); 4922be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4923da6c28aaSamw } 4924da6c28aaSamw zfs_smbshare_inited = 1; 4925ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4926ecd6cf80Smarks } 4927da6c28aaSamw break; 4928da6c28aaSamw default: 4929be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 4930da6c28aaSamw } 4931ecd6cf80Smarks 4932da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4933da6c28aaSamw case ZFS_SHARE_NFS: 4934da6c28aaSamw case ZFS_UNSHARE_NFS: 4935da6c28aaSamw if (error = 4936da6c28aaSamw znfsexport_fs((void *) 4937da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 4938da6c28aaSamw return (error); 4939da6c28aaSamw break; 4940da6c28aaSamw case ZFS_SHARE_SMB: 4941da6c28aaSamw case ZFS_UNSHARE_SMB: 4942da6c28aaSamw if (error = zsmbexport_fs((void *) 4943da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 4944da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 4945743a77edSAlan Wright B_TRUE: B_FALSE)) { 4946da6c28aaSamw return (error); 4947ecd6cf80Smarks } 4948da6c28aaSamw break; 4949ecd6cf80Smarks } 4950ecd6cf80Smarks 4951da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 4952da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 4953ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 4954ecd6cf80Smarks 4955da6c28aaSamw /* 4956da6c28aaSamw * Add or remove share from sharetab 4957da6c28aaSamw */ 4958ecd6cf80Smarks error = zshare_fs(opcode, 4959ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 4960ecd6cf80Smarks zc->zc_share.z_sharemax); 4961ecd6cf80Smarks 4962ecd6cf80Smarks return (error); 4963ecd6cf80Smarks 4964ecd6cf80Smarks } 4965ecd6cf80Smarks 4966743a77edSAlan Wright ace_t full_access[] = { 4967743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 4968743a77edSAlan Wright }; 4969743a77edSAlan Wright 497099d5e173STim Haley /* 497199d5e173STim Haley * inputs: 497299d5e173STim Haley * zc_name name of containing filesystem 497399d5e173STim Haley * zc_obj object # beyond which we want next in-use object # 497499d5e173STim Haley * 497599d5e173STim Haley * outputs: 497699d5e173STim Haley * zc_obj next in-use object # 497799d5e173STim Haley */ 497899d5e173STim Haley static int 497999d5e173STim Haley zfs_ioc_next_obj(zfs_cmd_t *zc) 498099d5e173STim Haley { 498199d5e173STim Haley objset_t *os = NULL; 498299d5e173STim Haley int error; 498399d5e173STim Haley 498499d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &os); 49853b2aab18SMatthew Ahrens if (error != 0) 498699d5e173STim Haley return (error); 498799d5e173STim Haley 498899d5e173STim Haley error = dmu_object_next(os, &zc->zc_obj, B_FALSE, 4989c1379625SJustin T. Gibbs dsl_dataset_phys(os->os_dsl_dataset)->ds_prev_snap_txg); 499099d5e173STim Haley 499199d5e173STim Haley dmu_objset_rele(os, FTAG); 499299d5e173STim Haley return (error); 499399d5e173STim Haley } 499499d5e173STim Haley 499599d5e173STim Haley /* 499699d5e173STim Haley * inputs: 499799d5e173STim Haley * zc_name name of filesystem 499899d5e173STim Haley * zc_value prefix name for snapshot 499999d5e173STim Haley * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 500099d5e173STim Haley * 500199d5e173STim Haley * outputs: 50024445fffbSMatthew Ahrens * zc_value short name of new snapshot 500399d5e173STim Haley */ 500499d5e173STim Haley static int 500599d5e173STim Haley zfs_ioc_tmp_snapshot(zfs_cmd_t *zc) 500699d5e173STim Haley { 500799d5e173STim Haley char *snap_name; 50083b2aab18SMatthew Ahrens char *hold_name; 500999d5e173STim Haley int error; 50103b2aab18SMatthew Ahrens minor_t minor; 501199d5e173STim Haley 50123b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor); 50133b2aab18SMatthew Ahrens if (error != 0) 501499d5e173STim Haley return (error); 501599d5e173STim Haley 50163b2aab18SMatthew Ahrens snap_name = kmem_asprintf("%s-%016llx", zc->zc_value, 50173b2aab18SMatthew Ahrens (u_longlong_t)ddi_get_lbolt64()); 50183b2aab18SMatthew Ahrens hold_name = kmem_asprintf("%%%s", zc->zc_value); 50193b2aab18SMatthew Ahrens 50203b2aab18SMatthew Ahrens error = dsl_dataset_snapshot_tmp(zc->zc_name, snap_name, minor, 50213b2aab18SMatthew Ahrens hold_name); 50223b2aab18SMatthew Ahrens if (error == 0) 50233b2aab18SMatthew Ahrens (void) strcpy(zc->zc_value, snap_name); 502499d5e173STim Haley strfree(snap_name); 50253b2aab18SMatthew Ahrens strfree(hold_name); 50263b2aab18SMatthew Ahrens zfs_onexit_fd_rele(zc->zc_cleanup_fd); 50273b2aab18SMatthew Ahrens return (error); 502899d5e173STim Haley } 502999d5e173STim Haley 503099d5e173STim Haley /* 503199d5e173STim Haley * inputs: 503299d5e173STim Haley * zc_name name of "to" snapshot 503399d5e173STim Haley * zc_value name of "from" snapshot 503499d5e173STim Haley * zc_cookie file descriptor to write diff data on 503599d5e173STim Haley * 503699d5e173STim Haley * outputs: 503799d5e173STim Haley * dmu_diff_record_t's to the file descriptor 503899d5e173STim Haley */ 503999d5e173STim Haley static int 504099d5e173STim Haley zfs_ioc_diff(zfs_cmd_t *zc) 504199d5e173STim Haley { 504299d5e173STim Haley file_t *fp; 504399d5e173STim Haley offset_t off; 504499d5e173STim Haley int error; 504599d5e173STim Haley 504699d5e173STim Haley fp = getf(zc->zc_cookie); 50473b2aab18SMatthew Ahrens if (fp == NULL) 5048be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 504999d5e173STim Haley 505099d5e173STim Haley off = fp->f_offset; 505199d5e173STim Haley 50523b2aab18SMatthew Ahrens error = dmu_diff(zc->zc_name, zc->zc_value, fp->f_vnode, &off); 505399d5e173STim Haley 505499d5e173STim Haley if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 505599d5e173STim Haley fp->f_offset = off; 505699d5e173STim Haley releasef(zc->zc_cookie); 505799d5e173STim Haley 505899d5e173STim Haley return (error); 505999d5e173STim Haley } 506099d5e173STim Haley 5061743a77edSAlan Wright /* 5062743a77edSAlan Wright * Remove all ACL files in shares dir 5063743a77edSAlan Wright */ 5064743a77edSAlan Wright static int 5065743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 5066743a77edSAlan Wright { 5067743a77edSAlan Wright zap_cursor_t zc; 5068743a77edSAlan Wright zap_attribute_t zap; 5069743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 5070743a77edSAlan Wright int error; 5071743a77edSAlan Wright 5072743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 5073743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 5074743a77edSAlan Wright zap_cursor_advance(&zc)) { 5075743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 5076743a77edSAlan Wright NULL, 0)) != 0) 5077743a77edSAlan Wright break; 5078743a77edSAlan Wright } 5079743a77edSAlan Wright zap_cursor_fini(&zc); 5080743a77edSAlan Wright return (error); 5081743a77edSAlan Wright } 5082743a77edSAlan Wright 5083743a77edSAlan Wright static int 5084743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 5085743a77edSAlan Wright { 5086743a77edSAlan Wright vnode_t *vp; 5087743a77edSAlan Wright znode_t *dzp; 5088743a77edSAlan Wright vnode_t *resourcevp = NULL; 5089743a77edSAlan Wright znode_t *sharedir; 5090743a77edSAlan Wright zfsvfs_t *zfsvfs; 5091743a77edSAlan Wright nvlist_t *nvlist; 5092743a77edSAlan Wright char *src, *target; 5093743a77edSAlan Wright vattr_t vattr; 5094743a77edSAlan Wright vsecattr_t vsec; 5095743a77edSAlan Wright int error = 0; 5096743a77edSAlan Wright 5097743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 5098743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 5099743a77edSAlan Wright return (error); 5100743a77edSAlan Wright 5101743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 5102743a77edSAlan Wright 5103743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 5104743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 5105743a77edSAlan Wright zc->zc_name) != 0)) { 5106743a77edSAlan Wright VN_RELE(vp); 5107be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5108743a77edSAlan Wright } 5109743a77edSAlan Wright 5110743a77edSAlan Wright dzp = VTOZ(vp); 5111743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 5112743a77edSAlan Wright ZFS_ENTER(zfsvfs); 5113743a77edSAlan Wright 51149e1320c0SMark Shellenbaum /* 51159e1320c0SMark Shellenbaum * Create share dir if its missing. 51169e1320c0SMark Shellenbaum */ 51179e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 51189e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 51199e1320c0SMark Shellenbaum dmu_tx_t *tx; 51209e1320c0SMark Shellenbaum 51219e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 51229e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 51239e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 51249e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 51259e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 51263b2aab18SMatthew Ahrens if (error != 0) { 51279e1320c0SMark Shellenbaum dmu_tx_abort(tx); 51289e1320c0SMark Shellenbaum } else { 51299e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 51309e1320c0SMark Shellenbaum dmu_tx_commit(tx); 51319e1320c0SMark Shellenbaum } 51323b2aab18SMatthew Ahrens if (error != 0) { 51339e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 51349e1320c0SMark Shellenbaum VN_RELE(vp); 51359e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 51369e1320c0SMark Shellenbaum return (error); 51379e1320c0SMark Shellenbaum } 51389e1320c0SMark Shellenbaum } 51399e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 51409e1320c0SMark Shellenbaum 51419e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 5142743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 51439e1320c0SMark Shellenbaum VN_RELE(vp); 5144743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5145743a77edSAlan Wright return (error); 5146743a77edSAlan Wright } 5147743a77edSAlan Wright 5148743a77edSAlan Wright switch (zc->zc_cookie) { 5149743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 5150743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 5151743a77edSAlan Wright vattr.va_type = VREG; 5152743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 5153743a77edSAlan Wright vattr.va_uid = 0; 5154743a77edSAlan Wright vattr.va_gid = 0; 5155743a77edSAlan Wright 5156743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 5157743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 5158743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 5159743a77edSAlan Wright vsec.vsa_aclcnt = 1; 5160743a77edSAlan Wright 5161743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 5162743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 5163743a77edSAlan Wright if (resourcevp) 5164743a77edSAlan Wright VN_RELE(resourcevp); 5165743a77edSAlan Wright break; 5166743a77edSAlan Wright 5167743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 5168743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 5169743a77edSAlan Wright NULL, 0); 5170743a77edSAlan Wright break; 5171743a77edSAlan Wright 5172743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 5173743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 5174478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 5175743a77edSAlan Wright VN_RELE(vp); 51768f5190a5SDan McDonald VN_RELE(ZTOV(sharedir)); 5177743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5178743a77edSAlan Wright return (error); 5179743a77edSAlan Wright } 5180743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 5181743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 5182743a77edSAlan Wright &target)) { 5183743a77edSAlan Wright VN_RELE(vp); 518489459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 5185743a77edSAlan Wright ZFS_EXIT(zfsvfs); 51861195e687SMark J Musante nvlist_free(nvlist); 5187743a77edSAlan Wright return (error); 5188743a77edSAlan Wright } 5189743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 5190743a77edSAlan Wright kcred, NULL, 0); 5191743a77edSAlan Wright nvlist_free(nvlist); 5192743a77edSAlan Wright break; 5193743a77edSAlan Wright 5194743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 5195743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 5196743a77edSAlan Wright break; 5197743a77edSAlan Wright 5198743a77edSAlan Wright default: 5199be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 5200743a77edSAlan Wright break; 5201743a77edSAlan Wright } 5202743a77edSAlan Wright 5203743a77edSAlan Wright VN_RELE(vp); 5204743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 5205743a77edSAlan Wright 5206743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5207743a77edSAlan Wright 5208743a77edSAlan Wright return (error); 5209743a77edSAlan Wright } 5210743a77edSAlan Wright 5211842727c2SChris Kirby /* 52123b2aab18SMatthew Ahrens * innvl: { 52133b2aab18SMatthew Ahrens * "holds" -> { snapname -> holdname (string), ... } 52143b2aab18SMatthew Ahrens * (optional) "cleanup_fd" -> fd (int32) 52153b2aab18SMatthew Ahrens * } 5216842727c2SChris Kirby * 52173b2aab18SMatthew Ahrens * outnvl: { 52183b2aab18SMatthew Ahrens * snapname -> error value (int32) 52193b2aab18SMatthew Ahrens * ... 52203b2aab18SMatthew Ahrens * } 5221842727c2SChris Kirby */ 52223b2aab18SMatthew Ahrens /* ARGSUSED */ 5223842727c2SChris Kirby static int 52243b2aab18SMatthew Ahrens zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) 5225842727c2SChris Kirby { 5226752fd8daSJosef 'Jeff' Sipek nvpair_t *pair; 52273b2aab18SMatthew Ahrens nvlist_t *holds; 52283b2aab18SMatthew Ahrens int cleanup_fd = -1; 5229a7f53a56SChris Kirby int error; 5230a7f53a56SChris Kirby minor_t minor = 0; 5231842727c2SChris Kirby 52323b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(args, "holds", &holds); 52333b2aab18SMatthew Ahrens if (error != 0) 5234be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5235a7f53a56SChris Kirby 5236752fd8daSJosef 'Jeff' Sipek /* make sure the user didn't pass us any invalid (empty) tags */ 5237752fd8daSJosef 'Jeff' Sipek for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 5238752fd8daSJosef 'Jeff' Sipek pair = nvlist_next_nvpair(holds, pair)) { 5239752fd8daSJosef 'Jeff' Sipek char *htag; 5240752fd8daSJosef 'Jeff' Sipek 5241752fd8daSJosef 'Jeff' Sipek error = nvpair_value_string(pair, &htag); 5242752fd8daSJosef 'Jeff' Sipek if (error != 0) 5243752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(error)); 5244752fd8daSJosef 'Jeff' Sipek 5245752fd8daSJosef 'Jeff' Sipek if (strlen(htag) == 0) 5246752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(EINVAL)); 5247752fd8daSJosef 'Jeff' Sipek } 5248752fd8daSJosef 'Jeff' Sipek 52493b2aab18SMatthew Ahrens if (nvlist_lookup_int32(args, "cleanup_fd", &cleanup_fd) == 0) { 52503b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(cleanup_fd, &minor); 52513b2aab18SMatthew Ahrens if (error != 0) 5252a7f53a56SChris Kirby return (error); 5253a7f53a56SChris Kirby } 5254a7f53a56SChris Kirby 52553b2aab18SMatthew Ahrens error = dsl_dataset_user_hold(holds, minor, errlist); 52563b2aab18SMatthew Ahrens if (minor != 0) 52573b2aab18SMatthew Ahrens zfs_onexit_fd_rele(cleanup_fd); 5258a7f53a56SChris Kirby return (error); 5259842727c2SChris Kirby } 5260842727c2SChris Kirby 5261842727c2SChris Kirby /* 52623b2aab18SMatthew Ahrens * innvl is not used. 5263842727c2SChris Kirby * 52643b2aab18SMatthew Ahrens * outnvl: { 52653b2aab18SMatthew Ahrens * holdname -> time added (uint64 seconds since epoch) 52663b2aab18SMatthew Ahrens * ... 52673b2aab18SMatthew Ahrens * } 5268842727c2SChris Kirby */ 52693b2aab18SMatthew Ahrens /* ARGSUSED */ 5270842727c2SChris Kirby static int 52713b2aab18SMatthew Ahrens zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) 5272842727c2SChris Kirby { 52733b2aab18SMatthew Ahrens return (dsl_dataset_get_holds(snapname, outnvl)); 5274842727c2SChris Kirby } 5275842727c2SChris Kirby 5276842727c2SChris Kirby /* 52773b2aab18SMatthew Ahrens * innvl: { 52783b2aab18SMatthew Ahrens * snapname -> { holdname, ... } 52793b2aab18SMatthew Ahrens * ... 52803b2aab18SMatthew Ahrens * } 5281842727c2SChris Kirby * 52823b2aab18SMatthew Ahrens * outnvl: { 52833b2aab18SMatthew Ahrens * snapname -> error value (int32) 52843b2aab18SMatthew Ahrens * ... 52853b2aab18SMatthew Ahrens * } 5286842727c2SChris Kirby */ 52873b2aab18SMatthew Ahrens /* ARGSUSED */ 5288842727c2SChris Kirby static int 52893b2aab18SMatthew Ahrens zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) 5290842727c2SChris Kirby { 52913b2aab18SMatthew Ahrens return (dsl_dataset_user_release(holds, errlist)); 5292842727c2SChris Kirby } 5293842727c2SChris Kirby 529419b94df9SMatthew Ahrens /* 529519b94df9SMatthew Ahrens * inputs: 529619b94df9SMatthew Ahrens * zc_name name of new filesystem or snapshot 529719b94df9SMatthew Ahrens * zc_value full name of old snapshot 529819b94df9SMatthew Ahrens * 529919b94df9SMatthew Ahrens * outputs: 530019b94df9SMatthew Ahrens * zc_cookie space in bytes 530119b94df9SMatthew Ahrens * zc_objset_type compressed space in bytes 530219b94df9SMatthew Ahrens * zc_perm_action uncompressed space in bytes 530319b94df9SMatthew Ahrens */ 530419b94df9SMatthew Ahrens static int 530519b94df9SMatthew Ahrens zfs_ioc_space_written(zfs_cmd_t *zc) 530619b94df9SMatthew Ahrens { 530719b94df9SMatthew Ahrens int error; 53083b2aab18SMatthew Ahrens dsl_pool_t *dp; 530919b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 531019b94df9SMatthew Ahrens 53113b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 531219b94df9SMatthew Ahrens if (error != 0) 531319b94df9SMatthew Ahrens return (error); 53143b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &new); 53153b2aab18SMatthew Ahrens if (error != 0) { 53163b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 53173b2aab18SMatthew Ahrens return (error); 53183b2aab18SMatthew Ahrens } 53193b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_value, FTAG, &old); 532019b94df9SMatthew Ahrens if (error != 0) { 532119b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53223b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 532319b94df9SMatthew Ahrens return (error); 532419b94df9SMatthew Ahrens } 532519b94df9SMatthew Ahrens 532619b94df9SMatthew Ahrens error = dsl_dataset_space_written(old, new, &zc->zc_cookie, 532719b94df9SMatthew Ahrens &zc->zc_objset_type, &zc->zc_perm_action); 532819b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 532919b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53303b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 533119b94df9SMatthew Ahrens return (error); 533219b94df9SMatthew Ahrens } 53333b2aab18SMatthew Ahrens 533419b94df9SMatthew Ahrens /* 53354445fffbSMatthew Ahrens * innvl: { 53364445fffbSMatthew Ahrens * "firstsnap" -> snapshot name 53374445fffbSMatthew Ahrens * } 533819b94df9SMatthew Ahrens * 53394445fffbSMatthew Ahrens * outnvl: { 53404445fffbSMatthew Ahrens * "used" -> space in bytes 53414445fffbSMatthew Ahrens * "compressed" -> compressed space in bytes 53424445fffbSMatthew Ahrens * "uncompressed" -> uncompressed space in bytes 53434445fffbSMatthew Ahrens * } 534419b94df9SMatthew Ahrens */ 534519b94df9SMatthew Ahrens static int 53464445fffbSMatthew Ahrens zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) 534719b94df9SMatthew Ahrens { 534819b94df9SMatthew Ahrens int error; 53493b2aab18SMatthew Ahrens dsl_pool_t *dp; 535019b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 53514445fffbSMatthew Ahrens char *firstsnap; 53524445fffbSMatthew Ahrens uint64_t used, comp, uncomp; 535319b94df9SMatthew Ahrens 53544445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) 5355be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 53564445fffbSMatthew Ahrens 53573b2aab18SMatthew Ahrens error = dsl_pool_hold(lastsnap, FTAG, &dp); 535819b94df9SMatthew Ahrens if (error != 0) 535919b94df9SMatthew Ahrens return (error); 53603b2aab18SMatthew Ahrens 53613b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, lastsnap, FTAG, &new); 536224218bebSAndriy Gapon if (error == 0 && !new->ds_is_snapshot) { 536324218bebSAndriy Gapon dsl_dataset_rele(new, FTAG); 536424218bebSAndriy Gapon error = SET_ERROR(EINVAL); 536524218bebSAndriy Gapon } 53663b2aab18SMatthew Ahrens if (error != 0) { 53673b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 53683b2aab18SMatthew Ahrens return (error); 53693b2aab18SMatthew Ahrens } 53703b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, firstsnap, FTAG, &old); 537124218bebSAndriy Gapon if (error == 0 && !old->ds_is_snapshot) { 537224218bebSAndriy Gapon dsl_dataset_rele(old, FTAG); 537324218bebSAndriy Gapon error = SET_ERROR(EINVAL); 537424218bebSAndriy Gapon } 537519b94df9SMatthew Ahrens if (error != 0) { 537619b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53773b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 537819b94df9SMatthew Ahrens return (error); 537919b94df9SMatthew Ahrens } 538019b94df9SMatthew Ahrens 53814445fffbSMatthew Ahrens error = dsl_dataset_space_wouldfree(old, new, &used, &comp, &uncomp); 538219b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 538319b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53843b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 53854445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "used", used); 53864445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "compressed", comp); 53874445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "uncompressed", uncomp); 538819b94df9SMatthew Ahrens return (error); 538919b94df9SMatthew Ahrens } 539019b94df9SMatthew Ahrens 5391ecd6cf80Smarks /* 53924445fffbSMatthew Ahrens * innvl: { 53934445fffbSMatthew Ahrens * "fd" -> file descriptor to write stream to (int32) 53944445fffbSMatthew Ahrens * (optional) "fromsnap" -> full snap name to send an incremental from 5395b5152584SMatthew Ahrens * (optional) "largeblockok" -> (value ignored) 5396b5152584SMatthew Ahrens * indicates that blocks > 128KB are permitted 53975d7b4d43SMatthew Ahrens * (optional) "embedok" -> (value ignored) 53985d7b4d43SMatthew Ahrens * presence indicates DRR_WRITE_EMBEDDED records are permitted 53999c3fd121SMatthew Ahrens * (optional) "resume_object" and "resume_offset" -> (uint64) 54009c3fd121SMatthew Ahrens * if present, resume send stream from specified object and offset. 54014445fffbSMatthew Ahrens * } 54024445fffbSMatthew Ahrens * 54034445fffbSMatthew Ahrens * outnvl is unused 5404ecd6cf80Smarks */ 54054445fffbSMatthew Ahrens /* ARGSUSED */ 54064445fffbSMatthew Ahrens static int 54074445fffbSMatthew Ahrens zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 54084445fffbSMatthew Ahrens { 54094445fffbSMatthew Ahrens int error; 54104445fffbSMatthew Ahrens offset_t off; 54113b2aab18SMatthew Ahrens char *fromname = NULL; 54124445fffbSMatthew Ahrens int fd; 5413b5152584SMatthew Ahrens boolean_t largeblockok; 54145d7b4d43SMatthew Ahrens boolean_t embedok; 54159c3fd121SMatthew Ahrens uint64_t resumeobj = 0; 54169c3fd121SMatthew Ahrens uint64_t resumeoff = 0; 54174445fffbSMatthew Ahrens 54184445fffbSMatthew Ahrens error = nvlist_lookup_int32(innvl, "fd", &fd); 54194445fffbSMatthew Ahrens if (error != 0) 5420be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 54214445fffbSMatthew Ahrens 54223b2aab18SMatthew Ahrens (void) nvlist_lookup_string(innvl, "fromsnap", &fromname); 54234445fffbSMatthew Ahrens 5424b5152584SMatthew Ahrens largeblockok = nvlist_exists(innvl, "largeblockok"); 54255d7b4d43SMatthew Ahrens embedok = nvlist_exists(innvl, "embedok"); 54265d7b4d43SMatthew Ahrens 54279c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); 54289c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff); 54299c3fd121SMatthew Ahrens 54304445fffbSMatthew Ahrens file_t *fp = getf(fd); 54313b2aab18SMatthew Ahrens if (fp == NULL) 5432be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 54334445fffbSMatthew Ahrens 54344445fffbSMatthew Ahrens off = fp->f_offset; 54359c3fd121SMatthew Ahrens error = dmu_send(snapname, fromname, embedok, largeblockok, fd, 54369c3fd121SMatthew Ahrens resumeobj, resumeoff, fp->f_vnode, &off); 54374445fffbSMatthew Ahrens 54384445fffbSMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 54394445fffbSMatthew Ahrens fp->f_offset = off; 54404445fffbSMatthew Ahrens releasef(fd); 54414445fffbSMatthew Ahrens return (error); 54424445fffbSMatthew Ahrens } 54434445fffbSMatthew Ahrens 54444445fffbSMatthew Ahrens /* 54454445fffbSMatthew Ahrens * Determine approximately how large a zfs send stream will be -- the number 54464445fffbSMatthew Ahrens * of bytes that will be written to the fd supplied to zfs_ioc_send_new(). 54474445fffbSMatthew Ahrens * 54484445fffbSMatthew Ahrens * innvl: { 5449643da460SMax Grossman * (optional) "from" -> full snap or bookmark name to send an incremental 5450643da460SMax Grossman * from 54514445fffbSMatthew Ahrens * } 54524445fffbSMatthew Ahrens * 54534445fffbSMatthew Ahrens * outnvl: { 54544445fffbSMatthew Ahrens * "space" -> bytes of space (uint64) 54554445fffbSMatthew Ahrens * } 54564445fffbSMatthew Ahrens */ 54574445fffbSMatthew Ahrens static int 54584445fffbSMatthew Ahrens zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 54594445fffbSMatthew Ahrens { 54603b2aab18SMatthew Ahrens dsl_pool_t *dp; 54613b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 54624445fffbSMatthew Ahrens int error; 54634445fffbSMatthew Ahrens char *fromname; 54644445fffbSMatthew Ahrens uint64_t space; 54654445fffbSMatthew Ahrens 54663b2aab18SMatthew Ahrens error = dsl_pool_hold(snapname, FTAG, &dp); 54673b2aab18SMatthew Ahrens if (error != 0) 54683b2aab18SMatthew Ahrens return (error); 54693b2aab18SMatthew Ahrens 54703b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, snapname, FTAG, &tosnap); 54713b2aab18SMatthew Ahrens if (error != 0) { 54723b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 54734445fffbSMatthew Ahrens return (error); 54743b2aab18SMatthew Ahrens } 54754445fffbSMatthew Ahrens 5476643da460SMax Grossman error = nvlist_lookup_string(innvl, "from", &fromname); 54774445fffbSMatthew Ahrens if (error == 0) { 5478643da460SMax Grossman if (strchr(fromname, '@') != NULL) { 5479643da460SMax Grossman /* 5480643da460SMax Grossman * If from is a snapshot, hold it and use the more 5481643da460SMax Grossman * efficient dmu_send_estimate to estimate send space 5482643da460SMax Grossman * size using deadlists. 5483643da460SMax Grossman */ 5484643da460SMax Grossman dsl_dataset_t *fromsnap; 5485643da460SMax Grossman error = dsl_dataset_hold(dp, fromname, FTAG, &fromsnap); 5486643da460SMax Grossman if (error != 0) 5487643da460SMax Grossman goto out; 5488643da460SMax Grossman error = dmu_send_estimate(tosnap, fromsnap, &space); 5489643da460SMax Grossman dsl_dataset_rele(fromsnap, FTAG); 5490643da460SMax Grossman } else if (strchr(fromname, '#') != NULL) { 5491643da460SMax Grossman /* 5492643da460SMax Grossman * If from is a bookmark, fetch the creation TXG of the 5493643da460SMax Grossman * snapshot it was created from and use that to find 5494643da460SMax Grossman * blocks that were born after it. 5495643da460SMax Grossman */ 5496643da460SMax Grossman zfs_bookmark_phys_t frombm; 5497643da460SMax Grossman 5498643da460SMax Grossman error = dsl_bookmark_lookup(dp, fromname, tosnap, 5499643da460SMax Grossman &frombm); 5500643da460SMax Grossman if (error != 0) 5501643da460SMax Grossman goto out; 5502643da460SMax Grossman error = dmu_send_estimate_from_txg(tosnap, 5503643da460SMax Grossman frombm.zbm_creation_txg, &space); 5504643da460SMax Grossman } else { 5505643da460SMax Grossman /* 5506643da460SMax Grossman * from is not properly formatted as a snapshot or 5507643da460SMax Grossman * bookmark 5508643da460SMax Grossman */ 5509643da460SMax Grossman error = SET_ERROR(EINVAL); 5510643da460SMax Grossman goto out; 55114445fffbSMatthew Ahrens } 5512643da460SMax Grossman } else { 5513643da460SMax Grossman // If estimating the size of a full send, use dmu_send_estimate 5514643da460SMax Grossman error = dmu_send_estimate(tosnap, NULL, &space); 55154445fffbSMatthew Ahrens } 55164445fffbSMatthew Ahrens 55174445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "space", space); 55184445fffbSMatthew Ahrens 5519643da460SMax Grossman out: 55203b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 55213b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 55224445fffbSMatthew Ahrens return (error); 55234445fffbSMatthew Ahrens } 55244445fffbSMatthew Ahrens 55254445fffbSMatthew Ahrens static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; 55264445fffbSMatthew Ahrens 55274445fffbSMatthew Ahrens static void 55284445fffbSMatthew Ahrens zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 55294445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 55304445fffbSMatthew Ahrens boolean_t log_history, zfs_ioc_poolcheck_t pool_check) 55314445fffbSMatthew Ahrens { 55324445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 55334445fffbSMatthew Ahrens 55344445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 55354445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 55364445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 55374445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 55384445fffbSMatthew Ahrens 55394445fffbSMatthew Ahrens vec->zvec_legacy_func = func; 55404445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 55414445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 55424445fffbSMatthew Ahrens vec->zvec_allow_log = log_history; 55434445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 55444445fffbSMatthew Ahrens } 55454445fffbSMatthew Ahrens 55464445fffbSMatthew Ahrens /* 55474445fffbSMatthew Ahrens * See the block comment at the beginning of this file for details on 55484445fffbSMatthew Ahrens * each argument to this function. 55494445fffbSMatthew Ahrens */ 55504445fffbSMatthew Ahrens static void 55514445fffbSMatthew Ahrens zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, 55524445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 55534445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, 55544445fffbSMatthew Ahrens boolean_t allow_log) 55554445fffbSMatthew Ahrens { 55564445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 55574445fffbSMatthew Ahrens 55584445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 55594445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 55604445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 55614445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 55624445fffbSMatthew Ahrens 55634445fffbSMatthew Ahrens /* if we are logging, the name must be valid */ 55644445fffbSMatthew Ahrens ASSERT(!allow_log || namecheck != NO_NAME); 55654445fffbSMatthew Ahrens 55664445fffbSMatthew Ahrens vec->zvec_name = name; 55674445fffbSMatthew Ahrens vec->zvec_func = func; 55684445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 55694445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 55704445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 55714445fffbSMatthew Ahrens vec->zvec_smush_outnvlist = smush_outnvlist; 55724445fffbSMatthew Ahrens vec->zvec_allow_log = allow_log; 55734445fffbSMatthew Ahrens } 55744445fffbSMatthew Ahrens 55754445fffbSMatthew Ahrens static void 55764445fffbSMatthew Ahrens zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 55774445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, boolean_t log_history, 55784445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check) 55794445fffbSMatthew Ahrens { 55804445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 55814445fffbSMatthew Ahrens POOL_NAME, log_history, pool_check); 55824445fffbSMatthew Ahrens } 55834445fffbSMatthew Ahrens 55844445fffbSMatthew Ahrens static void 55854445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 55864445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check) 55874445fffbSMatthew Ahrens { 55884445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 55894445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, pool_check); 55904445fffbSMatthew Ahrens } 55914445fffbSMatthew Ahrens 55924445fffbSMatthew Ahrens static void 55934445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 55944445fffbSMatthew Ahrens { 55954445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config, 55964445fffbSMatthew Ahrens POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 55974445fffbSMatthew Ahrens } 55984445fffbSMatthew Ahrens 55994445fffbSMatthew Ahrens static void 56004445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 56014445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy) 56024445fffbSMatthew Ahrens { 56034445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 56044445fffbSMatthew Ahrens NO_NAME, B_FALSE, POOL_CHECK_NONE); 56054445fffbSMatthew Ahrens } 56064445fffbSMatthew Ahrens 56074445fffbSMatthew Ahrens static void 56084445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc, 56094445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) 56104445fffbSMatthew Ahrens { 56114445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 56124445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED); 56134445fffbSMatthew Ahrens } 56144445fffbSMatthew Ahrens 56154445fffbSMatthew Ahrens static void 56164445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 56174445fffbSMatthew Ahrens { 56184445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ioc, func, 56194445fffbSMatthew Ahrens zfs_secpolicy_read); 56204445fffbSMatthew Ahrens } 56214445fffbSMatthew Ahrens 56224445fffbSMatthew Ahrens static void 56234445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 56249a686fbcSPaul Dagnelie zfs_secpolicy_func_t *secpolicy) 56254445fffbSMatthew Ahrens { 56264445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 56274445fffbSMatthew Ahrens DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 56284445fffbSMatthew Ahrens } 56294445fffbSMatthew Ahrens 56304445fffbSMatthew Ahrens static void 56314445fffbSMatthew Ahrens zfs_ioctl_init(void) 56324445fffbSMatthew Ahrens { 56334445fffbSMatthew Ahrens zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, 56344445fffbSMatthew Ahrens zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, 56354445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56364445fffbSMatthew Ahrens 56374445fffbSMatthew Ahrens zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, 56384445fffbSMatthew Ahrens zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, 56394445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); 56404445fffbSMatthew Ahrens 56414445fffbSMatthew Ahrens zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, 56424445fffbSMatthew Ahrens zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, 56434445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56444445fffbSMatthew Ahrens 56454445fffbSMatthew Ahrens zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, 56464445fffbSMatthew Ahrens zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, 56474445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56484445fffbSMatthew Ahrens 56494445fffbSMatthew Ahrens zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, 56504445fffbSMatthew Ahrens zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, 56514445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56524445fffbSMatthew Ahrens 56534445fffbSMatthew Ahrens zfs_ioctl_register("create", ZFS_IOC_CREATE, 56544445fffbSMatthew Ahrens zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, 56554445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56564445fffbSMatthew Ahrens 56574445fffbSMatthew Ahrens zfs_ioctl_register("clone", ZFS_IOC_CLONE, 56584445fffbSMatthew Ahrens zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, 56594445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56604445fffbSMatthew Ahrens 56614445fffbSMatthew Ahrens zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, 56624445fffbSMatthew Ahrens zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, 56634445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56644445fffbSMatthew Ahrens 56653b2aab18SMatthew Ahrens zfs_ioctl_register("hold", ZFS_IOC_HOLD, 56663b2aab18SMatthew Ahrens zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, 56673b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56683b2aab18SMatthew Ahrens zfs_ioctl_register("release", ZFS_IOC_RELEASE, 56693b2aab18SMatthew Ahrens zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, 56703b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56713b2aab18SMatthew Ahrens 56723b2aab18SMatthew Ahrens zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, 56733b2aab18SMatthew Ahrens zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, 56743b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56753b2aab18SMatthew Ahrens 5676a7027df1SMatthew Ahrens zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, 5677a7027df1SMatthew Ahrens zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, 5678a7027df1SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); 5679a7027df1SMatthew Ahrens 568078f17100SMatthew Ahrens zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, 568178f17100SMatthew Ahrens zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, 568278f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 568378f17100SMatthew Ahrens 568478f17100SMatthew Ahrens zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, 568578f17100SMatthew Ahrens zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, 568678f17100SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 568778f17100SMatthew Ahrens 568878f17100SMatthew Ahrens zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, 568978f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, 569078f17100SMatthew Ahrens POOL_NAME, 569178f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 569278f17100SMatthew Ahrens 56934445fffbSMatthew Ahrens /* IOCTLS that use the legacy function signature */ 56944445fffbSMatthew Ahrens 56954445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, 56964445fffbSMatthew Ahrens zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); 56974445fffbSMatthew Ahrens 56984445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, 56994445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 57004445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN, 57014445fffbSMatthew Ahrens zfs_ioc_pool_scan); 57024445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE, 57034445fffbSMatthew Ahrens zfs_ioc_pool_upgrade); 57044445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD, 57054445fffbSMatthew Ahrens zfs_ioc_vdev_add); 57064445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE, 57074445fffbSMatthew Ahrens zfs_ioc_vdev_remove); 57084445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SET_STATE, 57094445fffbSMatthew Ahrens zfs_ioc_vdev_set_state); 57104445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH, 57114445fffbSMatthew Ahrens zfs_ioc_vdev_attach); 57124445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH, 57134445fffbSMatthew Ahrens zfs_ioc_vdev_detach); 57144445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH, 57154445fffbSMatthew Ahrens zfs_ioc_vdev_setpath); 57164445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU, 57174445fffbSMatthew Ahrens zfs_ioc_vdev_setfru); 57184445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS, 57194445fffbSMatthew Ahrens zfs_ioc_pool_set_props); 57204445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SPLIT, 57214445fffbSMatthew Ahrens zfs_ioc_vdev_split); 57224445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_REGUID, 57234445fffbSMatthew Ahrens zfs_ioc_pool_reguid); 57244445fffbSMatthew Ahrens 57254445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_CONFIGS, 57264445fffbSMatthew Ahrens zfs_ioc_pool_configs, zfs_secpolicy_none); 57274445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_TRYIMPORT, 57284445fffbSMatthew Ahrens zfs_ioc_pool_tryimport, zfs_secpolicy_config); 57294445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_FAULT, 57304445fffbSMatthew Ahrens zfs_ioc_inject_fault, zfs_secpolicy_inject); 57314445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_CLEAR_FAULT, 57324445fffbSMatthew Ahrens zfs_ioc_clear_fault, zfs_secpolicy_inject); 57334445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_LIST_NEXT, 57344445fffbSMatthew Ahrens zfs_ioc_inject_list_next, zfs_secpolicy_inject); 57354445fffbSMatthew Ahrens 57364445fffbSMatthew Ahrens /* 57374445fffbSMatthew Ahrens * pool destroy, and export don't log the history as part of 57384445fffbSMatthew Ahrens * zfsdev_ioctl, but rather zfs_ioc_pool_export 57394445fffbSMatthew Ahrens * does the logging of those commands. 57404445fffbSMatthew Ahrens */ 57414445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_DESTROY, zfs_ioc_pool_destroy, 57424445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 57434445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_EXPORT, zfs_ioc_pool_export, 57444445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 57454445fffbSMatthew Ahrens 57464445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_STATS, zfs_ioc_pool_stats, 57474445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 57484445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_PROPS, zfs_ioc_pool_get_props, 57494445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 57504445fffbSMatthew Ahrens 57514445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_ERROR_LOG, zfs_ioc_error_log, 57524445fffbSMatthew Ahrens zfs_secpolicy_inject, B_FALSE, POOL_CHECK_SUSPENDED); 57534445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_DSOBJ_TO_DSNAME, 57544445fffbSMatthew Ahrens zfs_ioc_dsobj_to_dsname, 57554445fffbSMatthew Ahrens zfs_secpolicy_diff, B_FALSE, POOL_CHECK_SUSPENDED); 57564445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_HISTORY, 57574445fffbSMatthew Ahrens zfs_ioc_pool_get_history, 57584445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); 57594445fffbSMatthew Ahrens 57604445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, 57614445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 57624445fffbSMatthew Ahrens 57634445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, 576422e30981SGeorge Wilson zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 57654445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, 57664445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); 57674445fffbSMatthew Ahrens 57684445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, 57694445fffbSMatthew Ahrens zfs_ioc_space_written); 57704445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, 57714445fffbSMatthew Ahrens zfs_ioc_objset_recvd_props); 57724445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, 57734445fffbSMatthew Ahrens zfs_ioc_next_obj); 57744445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, 57754445fffbSMatthew Ahrens zfs_ioc_get_fsacl); 57764445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_STATS, 57774445fffbSMatthew Ahrens zfs_ioc_objset_stats); 57784445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_ZPLPROPS, 57794445fffbSMatthew Ahrens zfs_ioc_objset_zplprops); 57804445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_DATASET_LIST_NEXT, 57814445fffbSMatthew Ahrens zfs_ioc_dataset_list_next); 57824445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SNAPSHOT_LIST_NEXT, 57834445fffbSMatthew Ahrens zfs_ioc_snapshot_list_next); 57844445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SEND_PROGRESS, 57854445fffbSMatthew Ahrens zfs_ioc_send_progress); 57864445fffbSMatthew Ahrens 57874445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_DIFF, 57884445fffbSMatthew Ahrens zfs_ioc_diff, zfs_secpolicy_diff); 57894445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_STATS, 57904445fffbSMatthew Ahrens zfs_ioc_obj_to_stats, zfs_secpolicy_diff); 57914445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_PATH, 57924445fffbSMatthew Ahrens zfs_ioc_obj_to_path, zfs_secpolicy_diff); 57934445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_ONE, 57944445fffbSMatthew Ahrens zfs_ioc_userspace_one, zfs_secpolicy_userspace_one); 57954445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_MANY, 57964445fffbSMatthew Ahrens zfs_ioc_userspace_many, zfs_secpolicy_userspace_many); 57974445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND, 57984445fffbSMatthew Ahrens zfs_ioc_send, zfs_secpolicy_send); 57994445fffbSMatthew Ahrens 58004445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, 58014445fffbSMatthew Ahrens zfs_secpolicy_none); 58024445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, 58034445fffbSMatthew Ahrens zfs_secpolicy_destroy); 58044445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename, 58054445fffbSMatthew Ahrens zfs_secpolicy_rename); 58064445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv, 58074445fffbSMatthew Ahrens zfs_secpolicy_recv); 58084445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote, 58094445fffbSMatthew Ahrens zfs_secpolicy_promote); 58104445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP, 58114445fffbSMatthew Ahrens zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop); 58124445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl, 58134445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl); 58144445fffbSMatthew Ahrens 58154445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share, 58164445fffbSMatthew Ahrens zfs_secpolicy_share, POOL_CHECK_NONE); 58174445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl, 58184445fffbSMatthew Ahrens zfs_secpolicy_smb_acl, POOL_CHECK_NONE); 58194445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERSPACE_UPGRADE, 58204445fffbSMatthew Ahrens zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 58214445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 58224445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT, 58234445fffbSMatthew Ahrens zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, 58244445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 58254445fffbSMatthew Ahrens } 5826fa9e4066Sahrens 582754d692b7SGeorge Wilson int 5828f9af39baSGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type, 5829f9af39baSGeorge Wilson zfs_ioc_poolcheck_t check) 583054d692b7SGeorge Wilson { 583154d692b7SGeorge Wilson spa_t *spa; 583254d692b7SGeorge Wilson int error; 583354d692b7SGeorge Wilson 583454d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 583554d692b7SGeorge Wilson 5836f9af39baSGeorge Wilson if (check & POOL_CHECK_NONE) 5837f9af39baSGeorge Wilson return (0); 5838f9af39baSGeorge Wilson 583914843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 584054d692b7SGeorge Wilson if (error == 0) { 5841f9af39baSGeorge Wilson if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa)) 5842be6fd75aSMatthew Ahrens error = SET_ERROR(EAGAIN); 5843f9af39baSGeorge Wilson else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa)) 5844be6fd75aSMatthew Ahrens error = SET_ERROR(EROFS); 584554d692b7SGeorge Wilson spa_close(spa, FTAG); 584654d692b7SGeorge Wilson } 584754d692b7SGeorge Wilson return (error); 584854d692b7SGeorge Wilson } 584954d692b7SGeorge Wilson 5850c99e4bdcSChris Kirby /* 5851c99e4bdcSChris Kirby * Find a free minor number. 5852c99e4bdcSChris Kirby */ 5853c99e4bdcSChris Kirby minor_t 5854c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 5855c99e4bdcSChris Kirby { 5856c99e4bdcSChris Kirby static minor_t last_minor; 5857c99e4bdcSChris Kirby minor_t m; 5858c99e4bdcSChris Kirby 5859c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5860c99e4bdcSChris Kirby 5861c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 5862c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 5863c99e4bdcSChris Kirby m = 1; 5864c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 5865c99e4bdcSChris Kirby last_minor = m; 5866c99e4bdcSChris Kirby return (m); 5867c99e4bdcSChris Kirby } 5868c99e4bdcSChris Kirby } 5869c99e4bdcSChris Kirby 5870c99e4bdcSChris Kirby return (0); 5871c99e4bdcSChris Kirby } 5872c99e4bdcSChris Kirby 5873c99e4bdcSChris Kirby static int 5874c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 5875c99e4bdcSChris Kirby { 5876c99e4bdcSChris Kirby minor_t minor; 5877c99e4bdcSChris Kirby zfs_soft_state_t *zs; 5878c99e4bdcSChris Kirby 5879c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5880c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 5881c99e4bdcSChris Kirby 5882c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 5883c99e4bdcSChris Kirby if (minor == 0) 5884be6fd75aSMatthew Ahrens return (SET_ERROR(ENXIO)); 5885c99e4bdcSChris Kirby 5886c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 5887be6fd75aSMatthew Ahrens return (SET_ERROR(EAGAIN)); 5888c99e4bdcSChris Kirby 5889c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 5890c99e4bdcSChris Kirby 5891c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 5892c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 5893c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 5894c99e4bdcSChris Kirby 5895c99e4bdcSChris Kirby return (0); 5896c99e4bdcSChris Kirby } 5897c99e4bdcSChris Kirby 5898c99e4bdcSChris Kirby static void 5899c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 5900c99e4bdcSChris Kirby { 5901c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5902c99e4bdcSChris Kirby 5903c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 5904c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 5905c99e4bdcSChris Kirby } 5906c99e4bdcSChris Kirby 5907c99e4bdcSChris Kirby void * 5908c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 5909c99e4bdcSChris Kirby { 5910c99e4bdcSChris Kirby zfs_soft_state_t *zp; 5911c99e4bdcSChris Kirby 5912c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 5913c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 5914c99e4bdcSChris Kirby return (NULL); 5915c99e4bdcSChris Kirby 5916c99e4bdcSChris Kirby return (zp->zss_data); 5917c99e4bdcSChris Kirby } 5918c99e4bdcSChris Kirby 5919c99e4bdcSChris Kirby static int 5920c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 5921c99e4bdcSChris Kirby { 5922c99e4bdcSChris Kirby int error = 0; 5923c99e4bdcSChris Kirby 5924c99e4bdcSChris Kirby if (getminor(*devp) != 0) 5925c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 5926c99e4bdcSChris Kirby 5927c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 5928c99e4bdcSChris Kirby if (flag & FEXCL) { 5929c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 5930c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 5931c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5932c99e4bdcSChris Kirby } 5933c99e4bdcSChris Kirby 5934c99e4bdcSChris Kirby return (error); 5935c99e4bdcSChris Kirby } 5936c99e4bdcSChris Kirby 5937c99e4bdcSChris Kirby static int 5938c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 5939c99e4bdcSChris Kirby { 5940c99e4bdcSChris Kirby zfs_onexit_t *zo; 5941c99e4bdcSChris Kirby minor_t minor = getminor(dev); 5942c99e4bdcSChris Kirby 5943c99e4bdcSChris Kirby if (minor == 0) 5944c99e4bdcSChris Kirby return (0); 5945c99e4bdcSChris Kirby 5946c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 5947c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 5948c99e4bdcSChris Kirby if (zo == NULL) { 5949c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5950c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 5951c99e4bdcSChris Kirby } 5952c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 5953c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5954c99e4bdcSChris Kirby 5955c99e4bdcSChris Kirby return (0); 5956c99e4bdcSChris Kirby } 5957c99e4bdcSChris Kirby 5958fa9e4066Sahrens static int 5959fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 5960fa9e4066Sahrens { 5961fa9e4066Sahrens zfs_cmd_t *zc; 59624445fffbSMatthew Ahrens uint_t vecnum; 59634445fffbSMatthew Ahrens int error, rc, len; 5964c99e4bdcSChris Kirby minor_t minor = getminor(dev); 59654445fffbSMatthew Ahrens const zfs_ioc_vec_t *vec; 59664445fffbSMatthew Ahrens char *saved_poolname = NULL; 59674445fffbSMatthew Ahrens nvlist_t *innvl = NULL; 5968fa9e4066Sahrens 5969c99e4bdcSChris Kirby if (minor != 0 && 5970c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 5971fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 5972fa9e4066Sahrens 59734445fffbSMatthew Ahrens vecnum = cmd - ZFS_IOC_FIRST; 597491ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 5975fa9e4066Sahrens 59764445fffbSMatthew Ahrens if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 5977be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 59784445fffbSMatthew Ahrens vec = &zfs_ioc_vec[vecnum]; 5979fa9e4066Sahrens 5980fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 5981fa9e4066Sahrens 5982478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 59834445fffbSMatthew Ahrens if (error != 0) { 5984be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 59854445fffbSMatthew Ahrens goto out; 59864445fffbSMatthew Ahrens } 5987fa9e4066Sahrens 59884445fffbSMatthew Ahrens zc->zc_iflags = flag & FKIOCTL; 59894445fffbSMatthew Ahrens if (zc->zc_nvlist_src_size != 0) { 59904445fffbSMatthew Ahrens error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 59914445fffbSMatthew Ahrens zc->zc_iflags, &innvl); 59924445fffbSMatthew Ahrens if (error != 0) 59934445fffbSMatthew Ahrens goto out; 59944445fffbSMatthew Ahrens } 5995fa9e4066Sahrens 5996fa9e4066Sahrens /* 5997fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 5998fa9e4066Sahrens * the lower layers. 5999fa9e4066Sahrens */ 60004445fffbSMatthew Ahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 60014445fffbSMatthew Ahrens switch (vec->zvec_namecheck) { 60024445fffbSMatthew Ahrens case POOL_NAME: 60034445fffbSMatthew Ahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 6004be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 60054445fffbSMatthew Ahrens else 6006f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 60074445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 60084445fffbSMatthew Ahrens break; 6009fa9e4066Sahrens 60104445fffbSMatthew Ahrens case DATASET_NAME: 60114445fffbSMatthew Ahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 6012be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 60134445fffbSMatthew Ahrens else 6014f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 60154445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 60164445fffbSMatthew Ahrens break; 60175ad82045Snd 60184445fffbSMatthew Ahrens case NO_NAME: 60194445fffbSMatthew Ahrens break; 6020fa9e4066Sahrens } 6021fa9e4066Sahrens 6022fa9e4066Sahrens 60234445fffbSMatthew Ahrens if (error == 0 && !(flag & FKIOCTL)) 60244445fffbSMatthew Ahrens error = vec->zvec_secpolicy(zc, innvl, cr); 60254445fffbSMatthew Ahrens 60264445fffbSMatthew Ahrens if (error != 0) 60274445fffbSMatthew Ahrens goto out; 60284445fffbSMatthew Ahrens 60294445fffbSMatthew Ahrens /* legacy ioctls can modify zc_name */ 603078f17100SMatthew Ahrens len = strcspn(zc->zc_name, "/@#") + 1; 60314445fffbSMatthew Ahrens saved_poolname = kmem_alloc(len, KM_SLEEP); 60324445fffbSMatthew Ahrens (void) strlcpy(saved_poolname, zc->zc_name, len); 60334445fffbSMatthew Ahrens 60344445fffbSMatthew Ahrens if (vec->zvec_func != NULL) { 60354445fffbSMatthew Ahrens nvlist_t *outnvl; 60364445fffbSMatthew Ahrens int puterror = 0; 60374445fffbSMatthew Ahrens spa_t *spa; 60384445fffbSMatthew Ahrens nvlist_t *lognv = NULL; 60394445fffbSMatthew Ahrens 60404445fffbSMatthew Ahrens ASSERT(vec->zvec_legacy_func == NULL); 60414445fffbSMatthew Ahrens 60424445fffbSMatthew Ahrens /* 60434445fffbSMatthew Ahrens * Add the innvl to the lognv before calling the func, 60444445fffbSMatthew Ahrens * in case the func changes the innvl. 60454445fffbSMatthew Ahrens */ 60464445fffbSMatthew Ahrens if (vec->zvec_allow_log) { 60474445fffbSMatthew Ahrens lognv = fnvlist_alloc(); 60484445fffbSMatthew Ahrens fnvlist_add_string(lognv, ZPOOL_HIST_IOCTL, 60494445fffbSMatthew Ahrens vec->zvec_name); 60504445fffbSMatthew Ahrens if (!nvlist_empty(innvl)) { 60514445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_INPUT_NVL, 60524445fffbSMatthew Ahrens innvl); 60534445fffbSMatthew Ahrens } 60544445fffbSMatthew Ahrens } 60554445fffbSMatthew Ahrens 60564445fffbSMatthew Ahrens outnvl = fnvlist_alloc(); 60574445fffbSMatthew Ahrens error = vec->zvec_func(zc->zc_name, innvl, outnvl); 60584445fffbSMatthew Ahrens 60594445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log && 60604445fffbSMatthew Ahrens spa_open(zc->zc_name, &spa, FTAG) == 0) { 60614445fffbSMatthew Ahrens if (!nvlist_empty(outnvl)) { 60624445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, 60634445fffbSMatthew Ahrens outnvl); 60644445fffbSMatthew Ahrens } 60654445fffbSMatthew Ahrens (void) spa_history_log_nvl(spa, lognv); 60664445fffbSMatthew Ahrens spa_close(spa, FTAG); 60674445fffbSMatthew Ahrens } 60684445fffbSMatthew Ahrens fnvlist_free(lognv); 60694445fffbSMatthew Ahrens 60704445fffbSMatthew Ahrens if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) { 60714445fffbSMatthew Ahrens int smusherror = 0; 60724445fffbSMatthew Ahrens if (vec->zvec_smush_outnvlist) { 60734445fffbSMatthew Ahrens smusherror = nvlist_smush(outnvl, 60744445fffbSMatthew Ahrens zc->zc_nvlist_dst_size); 60754445fffbSMatthew Ahrens } 60764445fffbSMatthew Ahrens if (smusherror == 0) 60774445fffbSMatthew Ahrens puterror = put_nvlist(zc, outnvl); 60784445fffbSMatthew Ahrens } 60794445fffbSMatthew Ahrens 60804445fffbSMatthew Ahrens if (puterror != 0) 60814445fffbSMatthew Ahrens error = puterror; 60824445fffbSMatthew Ahrens 60834445fffbSMatthew Ahrens nvlist_free(outnvl); 60844445fffbSMatthew Ahrens } else { 60854445fffbSMatthew Ahrens error = vec->zvec_legacy_func(zc); 60864445fffbSMatthew Ahrens } 60874445fffbSMatthew Ahrens 60884445fffbSMatthew Ahrens out: 60894445fffbSMatthew Ahrens nvlist_free(innvl); 6090478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 60914445fffbSMatthew Ahrens if (error == 0 && rc != 0) 6092be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 60934445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log) { 60944445fffbSMatthew Ahrens char *s = tsd_get(zfs_allow_log_key); 60954445fffbSMatthew Ahrens if (s != NULL) 60964445fffbSMatthew Ahrens strfree(s); 60974445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, saved_poolname); 60984445fffbSMatthew Ahrens } else { 60994445fffbSMatthew Ahrens if (saved_poolname != NULL) 61004445fffbSMatthew Ahrens strfree(saved_poolname); 6101ecd6cf80Smarks } 6102fa9e4066Sahrens 6103fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 6104fa9e4066Sahrens return (error); 6105fa9e4066Sahrens } 6106fa9e4066Sahrens 6107fa9e4066Sahrens static int 6108fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 6109fa9e4066Sahrens { 6110fa9e4066Sahrens if (cmd != DDI_ATTACH) 6111fa9e4066Sahrens return (DDI_FAILURE); 6112fa9e4066Sahrens 6113fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 6114fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 6115fa9e4066Sahrens return (DDI_FAILURE); 6116fa9e4066Sahrens 6117fa9e4066Sahrens zfs_dip = dip; 6118fa9e4066Sahrens 6119fa9e4066Sahrens ddi_report_dev(dip); 6120fa9e4066Sahrens 6121fa9e4066Sahrens return (DDI_SUCCESS); 6122fa9e4066Sahrens } 6123fa9e4066Sahrens 6124fa9e4066Sahrens static int 6125fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 6126fa9e4066Sahrens { 6127fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 6128fa9e4066Sahrens return (DDI_FAILURE); 6129fa9e4066Sahrens 6130fa9e4066Sahrens if (cmd != DDI_DETACH) 6131fa9e4066Sahrens return (DDI_FAILURE); 6132fa9e4066Sahrens 6133fa9e4066Sahrens zfs_dip = NULL; 6134fa9e4066Sahrens 6135fa9e4066Sahrens ddi_prop_remove_all(dip); 6136fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 6137fa9e4066Sahrens 6138fa9e4066Sahrens return (DDI_SUCCESS); 6139fa9e4066Sahrens } 6140fa9e4066Sahrens 6141fa9e4066Sahrens /*ARGSUSED*/ 6142fa9e4066Sahrens static int 6143fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 6144fa9e4066Sahrens { 6145fa9e4066Sahrens switch (infocmd) { 6146fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 6147fa9e4066Sahrens *result = zfs_dip; 6148fa9e4066Sahrens return (DDI_SUCCESS); 6149fa9e4066Sahrens 6150fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 6151a0965f35Sbonwick *result = (void *)0; 6152fa9e4066Sahrens return (DDI_SUCCESS); 6153fa9e4066Sahrens } 6154fa9e4066Sahrens 6155fa9e4066Sahrens return (DDI_FAILURE); 6156fa9e4066Sahrens } 6157fa9e4066Sahrens 6158fa9e4066Sahrens /* 6159fa9e4066Sahrens * OK, so this is a little weird. 6160fa9e4066Sahrens * 6161fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 6162fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 6163fa9e4066Sahrens * 6164fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 6165fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 6166fa9e4066Sahrens */ 6167fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 6168c99e4bdcSChris Kirby zfsdev_open, /* open */ 6169c99e4bdcSChris Kirby zfsdev_close, /* close */ 6170fa9e4066Sahrens zvol_strategy, /* strategy */ 6171fa9e4066Sahrens nodev, /* print */ 6172e7cbe64fSgw zvol_dump, /* dump */ 6173fa9e4066Sahrens zvol_read, /* read */ 6174fa9e4066Sahrens zvol_write, /* write */ 6175fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 6176fa9e4066Sahrens nodev, /* devmap */ 6177fa9e4066Sahrens nodev, /* mmap */ 6178fa9e4066Sahrens nodev, /* segmap */ 6179fa9e4066Sahrens nochpoll, /* poll */ 6180fa9e4066Sahrens ddi_prop_op, /* prop_op */ 6181fa9e4066Sahrens NULL, /* streamtab */ 6182fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 6183fa9e4066Sahrens CB_REV, /* version */ 6184feb08c6bSbillm nodev, /* async read */ 6185feb08c6bSbillm nodev, /* async write */ 6186fa9e4066Sahrens }; 6187fa9e4066Sahrens 6188fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 6189fa9e4066Sahrens DEVO_REV, /* version */ 6190fa9e4066Sahrens 0, /* refcnt */ 6191fa9e4066Sahrens zfs_info, /* info */ 6192fa9e4066Sahrens nulldev, /* identify */ 6193fa9e4066Sahrens nulldev, /* probe */ 6194fa9e4066Sahrens zfs_attach, /* attach */ 6195fa9e4066Sahrens zfs_detach, /* detach */ 6196fa9e4066Sahrens nodev, /* reset */ 6197fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 619819397407SSherry Moore NULL, /* no bus operations */ 619919397407SSherry Moore NULL, /* power */ 620019397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 6201fa9e4066Sahrens }; 6202fa9e4066Sahrens 6203fa9e4066Sahrens static struct modldrv zfs_modldrv = { 620419397407SSherry Moore &mod_driverops, 620519397407SSherry Moore "ZFS storage pool", 620619397407SSherry Moore &zfs_dev_ops 6207fa9e4066Sahrens }; 6208fa9e4066Sahrens 6209fa9e4066Sahrens static struct modlinkage modlinkage = { 6210fa9e4066Sahrens MODREV_1, 6211fa9e4066Sahrens (void *)&zfs_modlfs, 6212fa9e4066Sahrens (void *)&zfs_modldrv, 6213fa9e4066Sahrens NULL 6214fa9e4066Sahrens }; 6215fa9e4066Sahrens 62164445fffbSMatthew Ahrens static void 62174445fffbSMatthew Ahrens zfs_allow_log_destroy(void *arg) 62184445fffbSMatthew Ahrens { 62194445fffbSMatthew Ahrens char *poolname = arg; 62204445fffbSMatthew Ahrens strfree(poolname); 62214445fffbSMatthew Ahrens } 6222ec533521Sfr 6223fa9e4066Sahrens int 6224fa9e4066Sahrens _init(void) 6225fa9e4066Sahrens { 6226fa9e4066Sahrens int error; 6227fa9e4066Sahrens 6228a0965f35Sbonwick spa_init(FREAD | FWRITE); 6229a0965f35Sbonwick zfs_init(); 6230a0965f35Sbonwick zvol_init(); 62314445fffbSMatthew Ahrens zfs_ioctl_init(); 6232a0965f35Sbonwick 6233a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 6234a0965f35Sbonwick zvol_fini(); 6235a0965f35Sbonwick zfs_fini(); 6236a0965f35Sbonwick spa_fini(); 6237fa9e4066Sahrens return (error); 6238a0965f35Sbonwick } 6239fa9e4066Sahrens 6240ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 62414445fffbSMatthew Ahrens tsd_create(&rrw_tsd_key, rrw_tsd_destroy); 62424445fffbSMatthew Ahrens tsd_create(&zfs_allow_log_key, zfs_allow_log_destroy); 6243ec533521Sfr 6244fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 6245fa9e4066Sahrens ASSERT(error == 0); 6246ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 6247fa9e4066Sahrens 6248fa9e4066Sahrens return (0); 6249fa9e4066Sahrens } 6250fa9e4066Sahrens 6251fa9e4066Sahrens int 6252fa9e4066Sahrens _fini(void) 6253fa9e4066Sahrens { 6254fa9e4066Sahrens int error; 6255fa9e4066Sahrens 6256ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 6257be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 6258fa9e4066Sahrens 6259fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 6260fa9e4066Sahrens return (error); 6261fa9e4066Sahrens 6262fa9e4066Sahrens zvol_fini(); 6263fa9e4066Sahrens zfs_fini(); 6264fa9e4066Sahrens spa_fini(); 6265da6c28aaSamw if (zfs_nfsshare_inited) 6266ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 6267da6c28aaSamw if (zfs_smbshare_inited) 6268da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 6269da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 6270ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 6271fa9e4066Sahrens 6272ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 6273fa9e4066Sahrens ldi_ident_release(zfs_li); 6274fa9e4066Sahrens zfs_li = NULL; 6275ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 6276fa9e4066Sahrens 6277fa9e4066Sahrens return (error); 6278fa9e4066Sahrens } 6279fa9e4066Sahrens 6280fa9e4066Sahrens int 6281fa9e4066Sahrens _info(struct modinfo *modinfop) 6282fa9e4066Sahrens { 6283fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 6284fa9e4066Sahrens } 6285