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 255878fad7SDan 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. 31*c3d26abcSMatthew Ahrens * Copyright (c) 2014 Integros [integros.com] 324445fffbSMatthew Ahrens */ 334445fffbSMatthew Ahrens 344445fffbSMatthew Ahrens /* 354445fffbSMatthew Ahrens * ZFS ioctls. 364445fffbSMatthew Ahrens * 374445fffbSMatthew Ahrens * This file handles the ioctls to /dev/zfs, used for configuring ZFS storage 384445fffbSMatthew Ahrens * pools and filesystems, e.g. with /sbin/zfs and /sbin/zpool. 394445fffbSMatthew Ahrens * 404445fffbSMatthew Ahrens * There are two ways that we handle ioctls: the legacy way where almost 414445fffbSMatthew Ahrens * all of the logic is in the ioctl callback, and the new way where most 424445fffbSMatthew Ahrens * of the marshalling is handled in the common entry point, zfsdev_ioctl(). 434445fffbSMatthew Ahrens * 444445fffbSMatthew Ahrens * Non-legacy ioctls should be registered by calling 454445fffbSMatthew Ahrens * zfs_ioctl_register() from zfs_ioctl_init(). The ioctl is invoked 464445fffbSMatthew Ahrens * from userland by lzc_ioctl(). 474445fffbSMatthew Ahrens * 484445fffbSMatthew Ahrens * The registration arguments are as follows: 494445fffbSMatthew Ahrens * 504445fffbSMatthew Ahrens * const char *name 514445fffbSMatthew Ahrens * The name of the ioctl. This is used for history logging. If the 524445fffbSMatthew Ahrens * ioctl returns successfully (the callback returns 0), and allow_log 534445fffbSMatthew Ahrens * is true, then a history log entry will be recorded with the input & 544445fffbSMatthew Ahrens * output nvlists. The log entry can be printed with "zpool history -i". 554445fffbSMatthew Ahrens * 564445fffbSMatthew Ahrens * zfs_ioc_t ioc 574445fffbSMatthew Ahrens * The ioctl request number, which userland will pass to ioctl(2). 584445fffbSMatthew Ahrens * The ioctl numbers can change from release to release, because 594445fffbSMatthew Ahrens * the caller (libzfs) must be matched to the kernel. 604445fffbSMatthew Ahrens * 614445fffbSMatthew Ahrens * zfs_secpolicy_func_t *secpolicy 624445fffbSMatthew Ahrens * This function will be called before the zfs_ioc_func_t, to 634445fffbSMatthew Ahrens * determine if this operation is permitted. It should return EPERM 644445fffbSMatthew Ahrens * on failure, and 0 on success. Checks include determining if the 654445fffbSMatthew Ahrens * dataset is visible in this zone, and if the user has either all 664445fffbSMatthew Ahrens * zfs privileges in the zone (SYS_MOUNT), or has been granted permission 674445fffbSMatthew Ahrens * to do this operation on this dataset with "zfs allow". 684445fffbSMatthew Ahrens * 694445fffbSMatthew Ahrens * zfs_ioc_namecheck_t namecheck 704445fffbSMatthew Ahrens * This specifies what to expect in the zfs_cmd_t:zc_name -- a pool 714445fffbSMatthew Ahrens * name, a dataset name, or nothing. If the name is not well-formed, 724445fffbSMatthew Ahrens * the ioctl will fail and the callback will not be called. 734445fffbSMatthew Ahrens * Therefore, the callback can assume that the name is well-formed 744445fffbSMatthew Ahrens * (e.g. is null-terminated, doesn't have more than one '@' character, 754445fffbSMatthew Ahrens * doesn't have invalid characters). 764445fffbSMatthew Ahrens * 774445fffbSMatthew Ahrens * zfs_ioc_poolcheck_t pool_check 784445fffbSMatthew Ahrens * This specifies requirements on the pool state. If the pool does 794445fffbSMatthew Ahrens * not meet them (is suspended or is readonly), the ioctl will fail 804445fffbSMatthew Ahrens * and the callback will not be called. If any checks are specified 814445fffbSMatthew Ahrens * (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. 824445fffbSMatthew Ahrens * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | 834445fffbSMatthew Ahrens * POOL_CHECK_READONLY). 844445fffbSMatthew Ahrens * 854445fffbSMatthew Ahrens * boolean_t smush_outnvlist 864445fffbSMatthew Ahrens * If smush_outnvlist is true, then the output is presumed to be a 874445fffbSMatthew Ahrens * list of errors, and it will be "smushed" down to fit into the 884445fffbSMatthew Ahrens * caller's buffer, by removing some entries and replacing them with a 894445fffbSMatthew Ahrens * single "N_MORE_ERRORS" entry indicating how many were removed. See 904445fffbSMatthew Ahrens * nvlist_smush() for details. If smush_outnvlist is false, and the 914445fffbSMatthew Ahrens * outnvlist does not fit into the userland-provided buffer, then the 924445fffbSMatthew Ahrens * ioctl will fail with ENOMEM. 934445fffbSMatthew Ahrens * 944445fffbSMatthew Ahrens * zfs_ioc_func_t *func 954445fffbSMatthew Ahrens * The callback function that will perform the operation. 964445fffbSMatthew Ahrens * 974445fffbSMatthew Ahrens * The callback should return 0 on success, or an error number on 984445fffbSMatthew Ahrens * failure. If the function fails, the userland ioctl will return -1, 994445fffbSMatthew Ahrens * and errno will be set to the callback's return value. The callback 1004445fffbSMatthew Ahrens * will be called with the following arguments: 1014445fffbSMatthew Ahrens * 1024445fffbSMatthew Ahrens * const char *name 1034445fffbSMatthew Ahrens * The name of the pool or dataset to operate on, from 1044445fffbSMatthew Ahrens * zfs_cmd_t:zc_name. The 'namecheck' argument specifies the 1054445fffbSMatthew Ahrens * expected type (pool, dataset, or none). 1064445fffbSMatthew Ahrens * 1074445fffbSMatthew Ahrens * nvlist_t *innvl 1084445fffbSMatthew Ahrens * The input nvlist, deserialized from zfs_cmd_t:zc_nvlist_src. Or 1094445fffbSMatthew Ahrens * NULL if no input nvlist was provided. Changes to this nvlist are 1104445fffbSMatthew Ahrens * ignored. If the input nvlist could not be deserialized, the 1114445fffbSMatthew Ahrens * ioctl will fail and the callback will not be called. 1124445fffbSMatthew Ahrens * 1134445fffbSMatthew Ahrens * nvlist_t *outnvl 1144445fffbSMatthew Ahrens * The output nvlist, initially empty. The callback can fill it in, 1154445fffbSMatthew Ahrens * and it will be returned to userland by serializing it into 1164445fffbSMatthew Ahrens * zfs_cmd_t:zc_nvlist_dst. If it is non-empty, and serialization 1174445fffbSMatthew Ahrens * fails (e.g. because the caller didn't supply a large enough 1184445fffbSMatthew Ahrens * buffer), then the overall ioctl will fail. See the 1194445fffbSMatthew Ahrens * 'smush_nvlist' argument above for additional behaviors. 1204445fffbSMatthew Ahrens * 1214445fffbSMatthew Ahrens * There are two typical uses of the output nvlist: 1224445fffbSMatthew Ahrens * - To return state, e.g. property values. In this case, 1234445fffbSMatthew Ahrens * smush_outnvlist should be false. If the buffer was not large 1244445fffbSMatthew Ahrens * enough, the caller will reallocate a larger buffer and try 1254445fffbSMatthew Ahrens * the ioctl again. 1264445fffbSMatthew Ahrens * 1274445fffbSMatthew Ahrens * - To return multiple errors from an ioctl which makes on-disk 1284445fffbSMatthew Ahrens * changes. In this case, smush_outnvlist should be true. 1294445fffbSMatthew Ahrens * Ioctls which make on-disk modifications should generally not 1304445fffbSMatthew Ahrens * use the outnvl if they succeed, because the caller can not 1314445fffbSMatthew Ahrens * distinguish between the operation failing, and 1324445fffbSMatthew Ahrens * deserialization failing. 133e9103aaeSGarrett D'Amore */ 134fa9e4066Sahrens 135fa9e4066Sahrens #include <sys/types.h> 136fa9e4066Sahrens #include <sys/param.h> 137fa9e4066Sahrens #include <sys/errno.h> 138fa9e4066Sahrens #include <sys/uio.h> 139fa9e4066Sahrens #include <sys/buf.h> 140fa9e4066Sahrens #include <sys/modctl.h> 141fa9e4066Sahrens #include <sys/open.h> 142fa9e4066Sahrens #include <sys/file.h> 143fa9e4066Sahrens #include <sys/kmem.h> 144fa9e4066Sahrens #include <sys/conf.h> 145fa9e4066Sahrens #include <sys/cmn_err.h> 146fa9e4066Sahrens #include <sys/stat.h> 147fa9e4066Sahrens #include <sys/zfs_ioctl.h> 1484201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 149da6c28aaSamw #include <sys/zfs_znode.h> 150fa9e4066Sahrens #include <sys/zap.h> 151fa9e4066Sahrens #include <sys/spa.h> 152b1b8ab34Slling #include <sys/spa_impl.h> 153fa9e4066Sahrens #include <sys/vdev.h> 1544201a95eSRic Aleshire #include <sys/priv_impl.h> 155fa9e4066Sahrens #include <sys/dmu.h> 156fa9e4066Sahrens #include <sys/dsl_dir.h> 157fa9e4066Sahrens #include <sys/dsl_dataset.h> 158fa9e4066Sahrens #include <sys/dsl_prop.h> 159ecd6cf80Smarks #include <sys/dsl_deleg.h> 160ecd6cf80Smarks #include <sys/dmu_objset.h> 1614e3c9f44SBill Pijewski #include <sys/dmu_impl.h> 1623b2aab18SMatthew Ahrens #include <sys/dmu_tx.h> 163fa9e4066Sahrens #include <sys/ddi.h> 164fa9e4066Sahrens #include <sys/sunddi.h> 165fa9e4066Sahrens #include <sys/sunldi.h> 166fa9e4066Sahrens #include <sys/policy.h> 167fa9e4066Sahrens #include <sys/zone.h> 168fa9e4066Sahrens #include <sys/nvpair.h> 169fa9e4066Sahrens #include <sys/pathname.h> 170fa9e4066Sahrens #include <sys/mount.h> 171fa9e4066Sahrens #include <sys/sdt.h> 172fa9e4066Sahrens #include <sys/fs/zfs.h> 173fa9e4066Sahrens #include <sys/zfs_ctldir.h> 174da6c28aaSamw #include <sys/zfs_dir.h> 175c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 176a2eea2e1Sahrens #include <sys/zvol.h> 1773f9d6ad7SLin Ling #include <sys/dsl_scan.h> 178ecd6cf80Smarks #include <sharefs/share.h> 179f18faf3fSek #include <sys/dmu_objset.h> 1803b2aab18SMatthew Ahrens #include <sys/dmu_send.h> 1813b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h> 18278f17100SMatthew Ahrens #include <sys/dsl_bookmark.h> 1833b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h> 184a6f561b4SSašo Kiselkov #include <sys/zfeature.h> 18545818ee1SMatthew Ahrens #include <sys/zio_checksum.h> 186fa9e4066Sahrens 187fa9e4066Sahrens #include "zfs_namecheck.h" 188e9dbad6fSeschrock #include "zfs_prop.h" 189ecd6cf80Smarks #include "zfs_deleg.h" 1900a586ceaSMark Shellenbaum #include "zfs_comutil.h" 191fa9e4066Sahrens 192fa9e4066Sahrens extern struct modlfs zfs_modlfs; 193fa9e4066Sahrens 194fa9e4066Sahrens extern void zfs_init(void); 195fa9e4066Sahrens extern void zfs_fini(void); 196fa9e4066Sahrens 197fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 198fa9e4066Sahrens dev_info_t *zfs_dip; 199fa9e4066Sahrens 2004445fffbSMatthew Ahrens uint_t zfs_fsyncer_key; 2014445fffbSMatthew Ahrens extern uint_t rrw_tsd_key; 2024445fffbSMatthew Ahrens static uint_t zfs_allow_log_key; 2034445fffbSMatthew Ahrens 2044445fffbSMatthew Ahrens typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); 2054445fffbSMatthew Ahrens typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); 2064445fffbSMatthew Ahrens typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); 207fa9e4066Sahrens 20854d692b7SGeorge Wilson typedef enum { 20954d692b7SGeorge Wilson NO_NAME, 21054d692b7SGeorge Wilson POOL_NAME, 21154d692b7SGeorge Wilson DATASET_NAME 21254d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 21354d692b7SGeorge Wilson 214f9af39baSGeorge Wilson typedef enum { 215f9af39baSGeorge Wilson POOL_CHECK_NONE = 1 << 0, 216f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED = 1 << 1, 2174445fffbSMatthew Ahrens POOL_CHECK_READONLY = 1 << 2, 218f9af39baSGeorge Wilson } zfs_ioc_poolcheck_t; 219f9af39baSGeorge Wilson 220fa9e4066Sahrens typedef struct zfs_ioc_vec { 2214445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *zvec_legacy_func; 222fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 223fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 22454d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 2254445fffbSMatthew Ahrens boolean_t zvec_allow_log; 226f9af39baSGeorge Wilson zfs_ioc_poolcheck_t zvec_pool_check; 2274445fffbSMatthew Ahrens boolean_t zvec_smush_outnvlist; 2284445fffbSMatthew Ahrens const char *zvec_name; 229fa9e4066Sahrens } zfs_ioc_vec_t; 230fa9e4066Sahrens 23114843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 23214843421SMatthew Ahrens static const char *userquota_perms[] = { 23314843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 23414843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 23514843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 23614843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 23714843421SMatthew Ahrens }; 23814843421SMatthew Ahrens 23914843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 24092241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 24192241e0bSTom Erickson cred_t *cr); 24292241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 24392241e0bSTom Erickson nvlist_t **errors); 2440a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 2450a48a24eStimh boolean_t *); 2464445fffbSMatthew Ahrens int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); 2474445fffbSMatthew Ahrens static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp); 2480a48a24eStimh 2492acef22dSMatthew Ahrens static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature); 250a6f561b4SSašo Kiselkov 251fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 252fa9e4066Sahrens void 253fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 254fa9e4066Sahrens { 255fa9e4066Sahrens const char *newfile; 2563f9d6ad7SLin Ling char buf[512]; 257fa9e4066Sahrens va_list adx; 258fa9e4066Sahrens 259fa9e4066Sahrens /* 260fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 261fa9e4066Sahrens */ 262fa9e4066Sahrens newfile = strrchr(file, '/'); 263fa9e4066Sahrens if (newfile != NULL) { 264fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 265fa9e4066Sahrens } else { 266fa9e4066Sahrens newfile = file; 267fa9e4066Sahrens } 268fa9e4066Sahrens 269fa9e4066Sahrens va_start(adx, fmt); 270fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 271fa9e4066Sahrens va_end(adx); 272fa9e4066Sahrens 273fa9e4066Sahrens /* 274fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 275fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 276fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 277fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 278fa9e4066Sahrens * arg0 = file name 279fa9e4066Sahrens * arg1 = function name 280fa9e4066Sahrens * arg2 = line number 281fa9e4066Sahrens * arg3 = message 282fa9e4066Sahrens */ 283fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 284fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 285fa9e4066Sahrens } 286fa9e4066Sahrens 287ecd6cf80Smarks static void 288228975ccSek history_str_free(char *buf) 289228975ccSek { 290228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 291228975ccSek } 292228975ccSek 293228975ccSek static char * 294228975ccSek history_str_get(zfs_cmd_t *zc) 295ecd6cf80Smarks { 29640feaa91Sahrens char *buf; 297ecd6cf80Smarks 298ecd6cf80Smarks if (zc->zc_history == NULL) 299228975ccSek return (NULL); 300e7437265Sahrens 301ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 302ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 303ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 304228975ccSek history_str_free(buf); 305228975ccSek return (NULL); 306ecd6cf80Smarks } 307ecd6cf80Smarks 308ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 309ecd6cf80Smarks 310228975ccSek return (buf); 311228975ccSek } 312ecd6cf80Smarks 31315e6edf1Sgw /* 31415e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 31515e6edf1Sgw */ 31615e6edf1Sgw static boolean_t 31715e6edf1Sgw zfs_is_bootfs(const char *name) 31815e6edf1Sgw { 319503ad85cSMatthew Ahrens objset_t *os; 32015e6edf1Sgw 321503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 322503ad85cSMatthew Ahrens boolean_t ret; 323b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 324503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 325503ad85cSMatthew Ahrens return (ret); 32615e6edf1Sgw } 327503ad85cSMatthew Ahrens return (B_FALSE); 32815e6edf1Sgw } 32915e6edf1Sgw 330c2a93d44Stimh /* 331f7170741SWill Andrews * Return non-zero if the spa version is less than requested version. 332c2a93d44Stimh */ 333da6c28aaSamw static int 3340a48a24eStimh zfs_earlier_version(const char *name, int version) 335da6c28aaSamw { 336da6c28aaSamw spa_t *spa; 337da6c28aaSamw 338da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 339da6c28aaSamw if (spa_version(spa) < version) { 340da6c28aaSamw spa_close(spa, FTAG); 341da6c28aaSamw return (1); 342da6c28aaSamw } 343da6c28aaSamw spa_close(spa, FTAG); 344da6c28aaSamw } 345da6c28aaSamw return (0); 346da6c28aaSamw } 347da6c28aaSamw 3489e6eda55Smarks /* 349745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 3509e6eda55Smarks */ 351745cd3c5Smaybee static boolean_t 352745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 3539e6eda55Smarks { 3549e6eda55Smarks objset_t *os; 355745cd3c5Smaybee boolean_t rc = B_TRUE; 3569e6eda55Smarks 357503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 358745cd3c5Smaybee uint64_t zplversion; 3599e6eda55Smarks 360503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 361503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 362503ad85cSMatthew Ahrens return (B_TRUE); 363503ad85cSMatthew Ahrens } 364503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 365745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 366745cd3c5Smaybee rc = zplversion < version; 367503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3689e6eda55Smarks } 3699e6eda55Smarks return (rc); 3709e6eda55Smarks } 3719e6eda55Smarks 372228975ccSek static void 373228975ccSek zfs_log_history(zfs_cmd_t *zc) 374228975ccSek { 375228975ccSek spa_t *spa; 376228975ccSek char *buf; 377ecd6cf80Smarks 378228975ccSek if ((buf = history_str_get(zc)) == NULL) 379228975ccSek return; 380228975ccSek 381228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 382228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 3834445fffbSMatthew Ahrens (void) spa_history_log(spa, buf); 384228975ccSek spa_close(spa, FTAG); 385228975ccSek } 386228975ccSek history_str_free(buf); 387ecd6cf80Smarks } 388ecd6cf80Smarks 389fa9e4066Sahrens /* 390fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 391fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 392fa9e4066Sahrens */ 393fa9e4066Sahrens /* ARGSUSED */ 394fa9e4066Sahrens static int 3954445fffbSMatthew Ahrens zfs_secpolicy_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 396fa9e4066Sahrens { 397fa9e4066Sahrens return (0); 398fa9e4066Sahrens } 399fa9e4066Sahrens 400fa9e4066Sahrens /* 401fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 402fa9e4066Sahrens * no privileges, but must be visible in the local zone. 403fa9e4066Sahrens */ 404fa9e4066Sahrens /* ARGSUSED */ 405fa9e4066Sahrens static int 4064445fffbSMatthew Ahrens zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 407fa9e4066Sahrens { 408fa9e4066Sahrens if (INGLOBALZONE(curproc) || 409ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 410fa9e4066Sahrens return (0); 411fa9e4066Sahrens 412be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 413fa9e4066Sahrens } 414fa9e4066Sahrens 415fa9e4066Sahrens static int 416a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) 417fa9e4066Sahrens { 418fa9e4066Sahrens int writable = 1; 419fa9e4066Sahrens 420fa9e4066Sahrens /* 421fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 422fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 423fa9e4066Sahrens */ 424fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 425fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 426be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 427fa9e4066Sahrens 428fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 429fa9e4066Sahrens /* 430fa9e4066Sahrens * If the fs is zoned, only root can access it from the 431fa9e4066Sahrens * global zone. 432fa9e4066Sahrens */ 433fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 434be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 435fa9e4066Sahrens } else { 436fa9e4066Sahrens /* 437fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 438fa9e4066Sahrens */ 439fa9e4066Sahrens if (!zoned) 440be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 441fa9e4066Sahrens 442fa9e4066Sahrens /* must be writable by this zone */ 443fa9e4066Sahrens if (!writable) 444be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 445fa9e4066Sahrens } 446fa9e4066Sahrens return (0); 447fa9e4066Sahrens } 448fa9e4066Sahrens 449a7f53a56SChris Kirby static int 450a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr) 451a7f53a56SChris Kirby { 452a7f53a56SChris Kirby uint64_t zoned; 453a7f53a56SChris Kirby 454a7f53a56SChris Kirby if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 455be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 456a7f53a56SChris Kirby 457a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 458a7f53a56SChris Kirby } 459a7f53a56SChris Kirby 460a7f53a56SChris Kirby static int 461a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) 462a7f53a56SChris Kirby { 463a7f53a56SChris Kirby uint64_t zoned; 464a7f53a56SChris Kirby 4653b2aab18SMatthew Ahrens if (dsl_prop_get_int_ds(ds, "zoned", &zoned)) 466be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 467a7f53a56SChris Kirby 468a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 469a7f53a56SChris Kirby } 470a7f53a56SChris Kirby 4714445fffbSMatthew Ahrens static int 4723b2aab18SMatthew Ahrens zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, 4733b2aab18SMatthew Ahrens const char *perm, cred_t *cr) 474fa9e4066Sahrens { 475fa9e4066Sahrens int error; 476fa9e4066Sahrens 47719b94df9SMatthew Ahrens error = zfs_dozonecheck_ds(name, ds, cr); 478ecd6cf80Smarks if (error == 0) { 479ecd6cf80Smarks error = secpolicy_zfs(cr); 4803b2aab18SMatthew Ahrens if (error != 0) 4814445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 482ecd6cf80Smarks } 483ecd6cf80Smarks return (error); 484ecd6cf80Smarks } 485ecd6cf80Smarks 4864445fffbSMatthew Ahrens static int 4873b2aab18SMatthew Ahrens zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 488a7f53a56SChris Kirby { 489a7f53a56SChris Kirby int error; 4903b2aab18SMatthew Ahrens dsl_dataset_t *ds; 4913b2aab18SMatthew Ahrens dsl_pool_t *dp; 492a7f53a56SChris Kirby 4933b2aab18SMatthew Ahrens error = dsl_pool_hold(name, FTAG, &dp); 4943b2aab18SMatthew Ahrens if (error != 0) 4953b2aab18SMatthew Ahrens return (error); 4963b2aab18SMatthew Ahrens 4973b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, name, FTAG, &ds); 4983b2aab18SMatthew Ahrens if (error != 0) { 4993b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 5003b2aab18SMatthew Ahrens return (error); 501a7f53a56SChris Kirby } 5023b2aab18SMatthew Ahrens 5033b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(name, ds, perm, cr); 5043b2aab18SMatthew Ahrens 5053b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 5063b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 507a7f53a56SChris Kirby return (error); 508a7f53a56SChris Kirby } 509a7f53a56SChris Kirby 5104201a95eSRic Aleshire /* 5114201a95eSRic Aleshire * Policy for setting the security label property. 5124201a95eSRic Aleshire * 5134201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 5144201a95eSRic Aleshire */ 5154201a95eSRic Aleshire static int 51692241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 5174201a95eSRic Aleshire { 5184201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 5194201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 5204201a95eSRic Aleshire boolean_t new_default = FALSE; 5214201a95eSRic Aleshire uint64_t zoned; 5224201a95eSRic Aleshire int needed_priv = -1; 5234201a95eSRic Aleshire int error; 5244201a95eSRic Aleshire 5254201a95eSRic Aleshire /* First get the existing dataset label. */ 5264201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 5274201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 5283b2aab18SMatthew Ahrens if (error != 0) 529be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5304201a95eSRic Aleshire 5314201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 5324201a95eSRic Aleshire new_default = TRUE; 5334201a95eSRic Aleshire 5344201a95eSRic Aleshire /* The label must be translatable */ 5354201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 536be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5374201a95eSRic Aleshire 5384201a95eSRic Aleshire /* 5394201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 5404201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 5414201a95eSRic Aleshire * are needed. 5424201a95eSRic Aleshire */ 5434201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 5444201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 545be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5464201a95eSRic Aleshire return (0); 5474201a95eSRic Aleshire } 5484201a95eSRic Aleshire 5494201a95eSRic Aleshire /* 5504201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 5514201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 5524201a95eSRic Aleshire * global zone. 5534201a95eSRic Aleshire */ 5544201a95eSRic Aleshire if (dsl_prop_get_integer(name, 5554201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 556be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5574201a95eSRic Aleshire if (!zoned) { 5584201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 559be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5604201a95eSRic Aleshire } 5614201a95eSRic Aleshire 5624201a95eSRic Aleshire /* 5634201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 5644201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 5654201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 5664201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 5674201a95eSRic Aleshire */ 5684201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 56992241e0bSTom Erickson objset_t *os; 57092241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 57192241e0bSTom Erickson 5724201a95eSRic Aleshire /* 5734201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 5744201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 5754201a95eSRic Aleshire */ 5764201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 57792241e0bSTom Erickson setsl_tag, &os); 5783b2aab18SMatthew Ahrens if (error != 0) 579be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5804201a95eSRic Aleshire 58192241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 58292241e0bSTom Erickson 5834201a95eSRic Aleshire if (new_default) { 5844201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 5854201a95eSRic Aleshire goto out_check; 5864201a95eSRic Aleshire } 5874201a95eSRic Aleshire 5884201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 589be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5904201a95eSRic Aleshire 5914201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 5924201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 5934201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 5944201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 5954201a95eSRic Aleshire } else { 5964201a95eSRic Aleshire /* dataset currently has a default label */ 5974201a95eSRic Aleshire if (!new_default) 5984201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 5994201a95eSRic Aleshire } 6004201a95eSRic Aleshire 6014201a95eSRic Aleshire out_check: 6024201a95eSRic Aleshire if (needed_priv != -1) 6034201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 6044201a95eSRic Aleshire return (0); 6054201a95eSRic Aleshire } 6064201a95eSRic Aleshire 607ecd6cf80Smarks static int 60892241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 60992241e0bSTom Erickson cred_t *cr) 610ecd6cf80Smarks { 61192241e0bSTom Erickson char *strval; 61292241e0bSTom Erickson 613ecd6cf80Smarks /* 614ecd6cf80Smarks * Check permissions for special properties. 615ecd6cf80Smarks */ 616ecd6cf80Smarks switch (prop) { 617ecd6cf80Smarks case ZFS_PROP_ZONED: 618ecd6cf80Smarks /* 619ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 620ecd6cf80Smarks */ 621ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 622be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 623ecd6cf80Smarks break; 624ecd6cf80Smarks 625ecd6cf80Smarks case ZFS_PROP_QUOTA: 626a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 627a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 628ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 629ecd6cf80Smarks uint64_t zoned; 630ecd6cf80Smarks char setpoint[MAXNAMELEN]; 631ecd6cf80Smarks /* 632ecd6cf80Smarks * Unprivileged users are allowed to modify the 633a2afb611SJerry Jelinek * limit on things *under* (ie. contained by) 634ecd6cf80Smarks * the thing they own. 635ecd6cf80Smarks */ 63692241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 637ecd6cf80Smarks setpoint)) 638be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 63992241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 640be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 641ecd6cf80Smarks } 642db870a07Sahrens break; 6434201a95eSRic Aleshire 6444201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 6454201a95eSRic Aleshire if (!is_system_labeled()) 646be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 64792241e0bSTom Erickson 64892241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 64992241e0bSTom Erickson int err; 65092241e0bSTom Erickson 65192241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 65292241e0bSTom Erickson if (err != 0) 65392241e0bSTom Erickson return (err); 65492241e0bSTom Erickson } 6554201a95eSRic Aleshire break; 656ecd6cf80Smarks } 657ecd6cf80Smarks 65892241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 659ecd6cf80Smarks } 660ecd6cf80Smarks 6614445fffbSMatthew Ahrens /* ARGSUSED */ 6624445fffbSMatthew Ahrens static int 6634445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 664ecd6cf80Smarks { 665ecd6cf80Smarks int error; 666ecd6cf80Smarks 667ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 6683b2aab18SMatthew Ahrens if (error != 0) 669fa9e4066Sahrens return (error); 670fa9e4066Sahrens 671ecd6cf80Smarks /* 672ecd6cf80Smarks * permission to set permissions will be evaluated later in 673ecd6cf80Smarks * dsl_deleg_can_allow() 674ecd6cf80Smarks */ 675ecd6cf80Smarks return (0); 676ecd6cf80Smarks } 677ecd6cf80Smarks 6784445fffbSMatthew Ahrens /* ARGSUSED */ 6794445fffbSMatthew Ahrens static int 6804445fffbSMatthew Ahrens zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 681ecd6cf80Smarks { 682681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 683681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 684ecd6cf80Smarks } 685ecd6cf80Smarks 6864445fffbSMatthew Ahrens /* ARGSUSED */ 6874445fffbSMatthew Ahrens static int 6884445fffbSMatthew Ahrens zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 689ecd6cf80Smarks { 690a7f53a56SChris Kirby dsl_pool_t *dp; 691a7f53a56SChris Kirby dsl_dataset_t *ds; 692a7f53a56SChris Kirby char *cp; 693a7f53a56SChris Kirby int error; 694a7f53a56SChris Kirby 695a7f53a56SChris Kirby /* 696a7f53a56SChris Kirby * Generate the current snapshot name from the given objsetid, then 697a7f53a56SChris Kirby * use that name for the secpolicy/zone checks. 698a7f53a56SChris Kirby */ 699a7f53a56SChris Kirby cp = strchr(zc->zc_name, '@'); 700a7f53a56SChris Kirby if (cp == NULL) 701be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 7023b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 7033b2aab18SMatthew Ahrens if (error != 0) 704a7f53a56SChris Kirby return (error); 705a7f53a56SChris Kirby 706a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 7073b2aab18SMatthew Ahrens if (error != 0) { 7083b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 709a7f53a56SChris Kirby return (error); 7103b2aab18SMatthew Ahrens } 711a7f53a56SChris Kirby 712a7f53a56SChris Kirby dsl_dataset_name(ds, zc->zc_name); 713a7f53a56SChris Kirby 714a7f53a56SChris Kirby error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, 715a7f53a56SChris Kirby ZFS_DELEG_PERM_SEND, cr); 716a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 7173b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 718a7f53a56SChris Kirby 719a7f53a56SChris Kirby return (error); 720ecd6cf80Smarks } 721ecd6cf80Smarks 7224445fffbSMatthew Ahrens /* ARGSUSED */ 723743a77edSAlan Wright static int 7244445fffbSMatthew Ahrens zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 7254445fffbSMatthew Ahrens { 7264445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 7274445fffbSMatthew Ahrens ZFS_DELEG_PERM_SEND, cr)); 7284445fffbSMatthew Ahrens } 7294445fffbSMatthew Ahrens 7304445fffbSMatthew Ahrens /* ARGSUSED */ 7314445fffbSMatthew Ahrens static int 7324445fffbSMatthew Ahrens zfs_secpolicy_deleg_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 733743a77edSAlan Wright { 734743a77edSAlan Wright vnode_t *vp; 735743a77edSAlan Wright int error; 736743a77edSAlan Wright 737743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 738743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 739743a77edSAlan Wright return (error); 740743a77edSAlan Wright 741743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 742743a77edSAlan Wright 743743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 744743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 745743a77edSAlan Wright zc->zc_name) != 0)) { 746743a77edSAlan Wright VN_RELE(vp); 747be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 748743a77edSAlan Wright } 749743a77edSAlan Wright 750743a77edSAlan Wright VN_RELE(vp); 751743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 752743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 753743a77edSAlan Wright } 754743a77edSAlan Wright 755ecd6cf80Smarks int 7564445fffbSMatthew Ahrens zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 757ecd6cf80Smarks { 758ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 759be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 760ecd6cf80Smarks 7613cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 762ecd6cf80Smarks return (0); 763ecd6cf80Smarks } else { 7644445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 765743a77edSAlan Wright } 766743a77edSAlan Wright } 767ecd6cf80Smarks 768743a77edSAlan Wright int 7694445fffbSMatthew Ahrens zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 770743a77edSAlan Wright { 771743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 772be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 773ecd6cf80Smarks 774743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 775743a77edSAlan Wright return (0); 776743a77edSAlan Wright } else { 7774445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 778ecd6cf80Smarks } 779fa9e4066Sahrens } 780fa9e4066Sahrens 781fa9e4066Sahrens static int 782ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 783fa9e4066Sahrens { 784fa9e4066Sahrens char *cp; 785fa9e4066Sahrens 786fa9e4066Sahrens /* 787fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 788fa9e4066Sahrens */ 789ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 790ecd6cf80Smarks cp = strrchr(parent, '@'); 791fa9e4066Sahrens if (cp != NULL) { 792fa9e4066Sahrens cp[0] = '\0'; 793fa9e4066Sahrens } else { 794ecd6cf80Smarks cp = strrchr(parent, '/'); 795fa9e4066Sahrens if (cp == NULL) 796be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 797fa9e4066Sahrens cp[0] = '\0'; 798ecd6cf80Smarks } 799ecd6cf80Smarks 800ecd6cf80Smarks return (0); 801ecd6cf80Smarks } 802ecd6cf80Smarks 803ecd6cf80Smarks int 804ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 805ecd6cf80Smarks { 806ecd6cf80Smarks int error; 807ecd6cf80Smarks 808ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 809ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 810ecd6cf80Smarks return (error); 811ecd6cf80Smarks 812ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 813ecd6cf80Smarks } 814ecd6cf80Smarks 8154445fffbSMatthew Ahrens /* ARGSUSED */ 816ecd6cf80Smarks static int 8174445fffbSMatthew Ahrens zfs_secpolicy_destroy(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 818ecd6cf80Smarks { 819ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 820ecd6cf80Smarks } 821ecd6cf80Smarks 822cbf6f6aaSWilliam Gorrell /* 823cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 8244445fffbSMatthew Ahrens * descendant mount and destroy permissions. 825cbf6f6aaSWilliam Gorrell */ 8264445fffbSMatthew Ahrens /* ARGSUSED */ 827cbf6f6aaSWilliam Gorrell static int 8284445fffbSMatthew Ahrens zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 829cbf6f6aaSWilliam Gorrell { 8304445fffbSMatthew Ahrens nvlist_t *snaps; 8314445fffbSMatthew Ahrens nvpair_t *pair, *nextpair; 8324445fffbSMatthew Ahrens int error = 0; 833cbf6f6aaSWilliam Gorrell 8344445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 835be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 8364445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 8374445fffbSMatthew Ahrens pair = nextpair) { 8384445fffbSMatthew Ahrens nextpair = nvlist_next_nvpair(snaps, pair); 83978f17100SMatthew Ahrens error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); 84078f17100SMatthew Ahrens if (error == ENOENT) { 8414445fffbSMatthew Ahrens /* 8424445fffbSMatthew Ahrens * Ignore any snapshots that don't exist (we consider 8434445fffbSMatthew Ahrens * them "already destroyed"). Remove the name from the 8444445fffbSMatthew Ahrens * nvl here in case the snapshot is created between 8454445fffbSMatthew Ahrens * now and when we try to destroy it (in which case 8464445fffbSMatthew Ahrens * we don't want to destroy it since we haven't 8474445fffbSMatthew Ahrens * checked for permission). 8484445fffbSMatthew Ahrens */ 8494445fffbSMatthew Ahrens fnvlist_remove_nvpair(snaps, pair); 8504445fffbSMatthew Ahrens error = 0; 8514445fffbSMatthew Ahrens } 8524445fffbSMatthew Ahrens if (error != 0) 8534445fffbSMatthew Ahrens break; 8544445fffbSMatthew Ahrens } 855cbf6f6aaSWilliam Gorrell 856cbf6f6aaSWilliam Gorrell return (error); 857cbf6f6aaSWilliam Gorrell } 858cbf6f6aaSWilliam Gorrell 859ecd6cf80Smarks int 860ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 861ecd6cf80Smarks { 86292241e0bSTom Erickson char parentname[MAXNAMELEN]; 863ecd6cf80Smarks int error; 864ecd6cf80Smarks 865ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 866ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 867ecd6cf80Smarks return (error); 868ecd6cf80Smarks 869ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 870ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 871ecd6cf80Smarks return (error); 872ecd6cf80Smarks 873ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 874ecd6cf80Smarks sizeof (parentname))) != 0) 875ecd6cf80Smarks return (error); 876ecd6cf80Smarks 877ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 878ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 879ecd6cf80Smarks return (error); 880ecd6cf80Smarks 881ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 882ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 883ecd6cf80Smarks return (error); 884ecd6cf80Smarks 885ecd6cf80Smarks return (error); 886ecd6cf80Smarks } 887ecd6cf80Smarks 8884445fffbSMatthew Ahrens /* ARGSUSED */ 889ecd6cf80Smarks static int 8904445fffbSMatthew Ahrens zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 891ecd6cf80Smarks { 892ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 893ecd6cf80Smarks } 894ecd6cf80Smarks 8954445fffbSMatthew Ahrens /* ARGSUSED */ 896ecd6cf80Smarks static int 8974445fffbSMatthew Ahrens zfs_secpolicy_promote(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 898ecd6cf80Smarks { 8993b2aab18SMatthew Ahrens dsl_pool_t *dp; 9003b2aab18SMatthew Ahrens dsl_dataset_t *clone; 901ecd6cf80Smarks int error; 902ecd6cf80Smarks 903ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 904ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9053b2aab18SMatthew Ahrens if (error != 0) 9063b2aab18SMatthew Ahrens return (error); 9073b2aab18SMatthew Ahrens 9083b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 9093b2aab18SMatthew Ahrens if (error != 0) 910ecd6cf80Smarks return (error); 911ecd6cf80Smarks 9123b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &clone); 913ecd6cf80Smarks 914ecd6cf80Smarks if (error == 0) { 9153b2aab18SMatthew Ahrens char parentname[MAXNAMELEN]; 9163b2aab18SMatthew Ahrens dsl_dataset_t *origin = NULL; 917ecd6cf80Smarks dsl_dir_t *dd; 9183b2aab18SMatthew Ahrens dd = clone->ds_dir; 919ecd6cf80Smarks 920745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 921c1379625SJustin T. Gibbs dsl_dir_phys(dd)->dd_origin_obj, FTAG, &origin); 9223b2aab18SMatthew Ahrens if (error != 0) { 9233b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9243b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 925ecd6cf80Smarks return (error); 926ecd6cf80Smarks } 927ecd6cf80Smarks 9283b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(zc->zc_name, clone, 929ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 930ecd6cf80Smarks 9313b2aab18SMatthew Ahrens dsl_dataset_name(origin, parentname); 9323b2aab18SMatthew Ahrens if (error == 0) { 9333b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(parentname, origin, 934ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9353b2aab18SMatthew Ahrens } 9363b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9373b2aab18SMatthew Ahrens dsl_dataset_rele(origin, FTAG); 938ecd6cf80Smarks } 9393b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 940ecd6cf80Smarks return (error); 941ecd6cf80Smarks } 942ecd6cf80Smarks 9434445fffbSMatthew Ahrens /* ARGSUSED */ 944ecd6cf80Smarks static int 9454445fffbSMatthew Ahrens zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 946ecd6cf80Smarks { 947ecd6cf80Smarks int error; 948ecd6cf80Smarks 949ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 950ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 951ecd6cf80Smarks return (error); 952ecd6cf80Smarks 953ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 954ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 955ecd6cf80Smarks return (error); 956ecd6cf80Smarks 957ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 958ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 959ecd6cf80Smarks } 960ecd6cf80Smarks 961ecd6cf80Smarks int 962ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 963ecd6cf80Smarks { 964681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 965681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 966ecd6cf80Smarks } 967ecd6cf80Smarks 9684445fffbSMatthew Ahrens /* 9694445fffbSMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 9704445fffbSMatthew Ahrens */ 9714445fffbSMatthew Ahrens /* ARGSUSED */ 972ecd6cf80Smarks static int 9734445fffbSMatthew Ahrens zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 974ecd6cf80Smarks { 9754445fffbSMatthew Ahrens nvlist_t *snaps; 976d5285caeSGeorge Wilson int error = 0; 9774445fffbSMatthew Ahrens nvpair_t *pair; 978ecd6cf80Smarks 9794445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 980be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 9814445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 9824445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 9834445fffbSMatthew Ahrens char *name = nvpair_name(pair); 9844445fffbSMatthew Ahrens char *atp = strchr(name, '@'); 9854445fffbSMatthew Ahrens 9864445fffbSMatthew Ahrens if (atp == NULL) { 987be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 9884445fffbSMatthew Ahrens break; 9894445fffbSMatthew Ahrens } 9904445fffbSMatthew Ahrens *atp = '\0'; 9914445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(name, cr); 9924445fffbSMatthew Ahrens *atp = '@'; 9934445fffbSMatthew Ahrens if (error != 0) 9944445fffbSMatthew Ahrens break; 9954445fffbSMatthew Ahrens } 9964445fffbSMatthew Ahrens return (error); 997ecd6cf80Smarks } 998ecd6cf80Smarks 99978f17100SMatthew Ahrens /* 100078f17100SMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 100178f17100SMatthew Ahrens */ 100278f17100SMatthew Ahrens /* ARGSUSED */ 100378f17100SMatthew Ahrens static int 100478f17100SMatthew Ahrens zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 100578f17100SMatthew Ahrens { 100678f17100SMatthew Ahrens int error = 0; 100778f17100SMatthew Ahrens 100878f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 100978f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 101078f17100SMatthew Ahrens char *name = nvpair_name(pair); 101178f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 101278f17100SMatthew Ahrens 101378f17100SMatthew Ahrens if (hashp == NULL) { 101478f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 101578f17100SMatthew Ahrens break; 101678f17100SMatthew Ahrens } 101778f17100SMatthew Ahrens *hashp = '\0'; 101878f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 101978f17100SMatthew Ahrens ZFS_DELEG_PERM_BOOKMARK, cr); 102078f17100SMatthew Ahrens *hashp = '#'; 102178f17100SMatthew Ahrens if (error != 0) 102278f17100SMatthew Ahrens break; 102378f17100SMatthew Ahrens } 102478f17100SMatthew Ahrens return (error); 102578f17100SMatthew Ahrens } 102678f17100SMatthew Ahrens 102778f17100SMatthew Ahrens /* ARGSUSED */ 102878f17100SMatthew Ahrens static int 102978f17100SMatthew Ahrens zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 103078f17100SMatthew Ahrens { 103178f17100SMatthew Ahrens nvpair_t *pair, *nextpair; 103278f17100SMatthew Ahrens int error = 0; 103378f17100SMatthew Ahrens 103478f17100SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 103578f17100SMatthew Ahrens pair = nextpair) { 103678f17100SMatthew Ahrens char *name = nvpair_name(pair); 103778f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 103878f17100SMatthew Ahrens nextpair = nvlist_next_nvpair(innvl, pair); 103978f17100SMatthew Ahrens 104078f17100SMatthew Ahrens if (hashp == NULL) { 104178f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 104278f17100SMatthew Ahrens break; 104378f17100SMatthew Ahrens } 104478f17100SMatthew Ahrens 104578f17100SMatthew Ahrens *hashp = '\0'; 104678f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 104778f17100SMatthew Ahrens ZFS_DELEG_PERM_DESTROY, cr); 104878f17100SMatthew Ahrens *hashp = '#'; 104978f17100SMatthew Ahrens if (error == ENOENT) { 105078f17100SMatthew Ahrens /* 105178f17100SMatthew Ahrens * Ignore any filesystems that don't exist (we consider 105278f17100SMatthew Ahrens * their bookmarks "already destroyed"). Remove 105378f17100SMatthew Ahrens * the name from the nvl here in case the filesystem 105478f17100SMatthew Ahrens * is created between now and when we try to destroy 105578f17100SMatthew Ahrens * the bookmark (in which case we don't want to 105678f17100SMatthew Ahrens * destroy it since we haven't checked for permission). 105778f17100SMatthew Ahrens */ 105878f17100SMatthew Ahrens fnvlist_remove_nvpair(innvl, pair); 105978f17100SMatthew Ahrens error = 0; 106078f17100SMatthew Ahrens } 106178f17100SMatthew Ahrens if (error != 0) 106278f17100SMatthew Ahrens break; 106378f17100SMatthew Ahrens } 106478f17100SMatthew Ahrens 106578f17100SMatthew Ahrens return (error); 106678f17100SMatthew Ahrens } 106778f17100SMatthew Ahrens 10684445fffbSMatthew Ahrens /* ARGSUSED */ 1069ecd6cf80Smarks static int 10704445fffbSMatthew Ahrens zfs_secpolicy_log_history(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 10714445fffbSMatthew Ahrens { 10724445fffbSMatthew Ahrens /* 10734445fffbSMatthew Ahrens * Even root must have a proper TSD so that we know what pool 10744445fffbSMatthew Ahrens * to log to. 10754445fffbSMatthew Ahrens */ 10764445fffbSMatthew Ahrens if (tsd_get(zfs_allow_log_key) == NULL) 1077be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 10784445fffbSMatthew Ahrens return (0); 10794445fffbSMatthew Ahrens } 10804445fffbSMatthew Ahrens 10814445fffbSMatthew Ahrens static int 10824445fffbSMatthew Ahrens zfs_secpolicy_create_clone(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1083ecd6cf80Smarks { 108492241e0bSTom Erickson char parentname[MAXNAMELEN]; 108592241e0bSTom Erickson int error; 10864445fffbSMatthew Ahrens char *origin; 1087ecd6cf80Smarks 1088ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 1089ecd6cf80Smarks sizeof (parentname))) != 0) 1090ecd6cf80Smarks return (error); 1091fa9e4066Sahrens 10924445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin) == 0 && 10934445fffbSMatthew Ahrens (error = zfs_secpolicy_write_perms(origin, 10944445fffbSMatthew Ahrens ZFS_DELEG_PERM_CLONE, cr)) != 0) 10954445fffbSMatthew Ahrens return (error); 1096fa9e4066Sahrens 1097ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 1098ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 1099ecd6cf80Smarks return (error); 1100ecd6cf80Smarks 11014445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(parentname, 11024445fffbSMatthew Ahrens ZFS_DELEG_PERM_MOUNT, cr)); 1103fa9e4066Sahrens } 1104fa9e4066Sahrens 1105fa9e4066Sahrens /* 1106fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 1107fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 1108fa9e4066Sahrens */ 1109fa9e4066Sahrens /* ARGSUSED */ 1110fa9e4066Sahrens static int 11114445fffbSMatthew Ahrens zfs_secpolicy_config(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1112fa9e4066Sahrens { 1113fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 1114be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 1115fa9e4066Sahrens 1116fa9e4066Sahrens return (0); 1117fa9e4066Sahrens } 1118fa9e4066Sahrens 111999d5e173STim Haley /* 112099d5e173STim Haley * Policy for object to name lookups. 112199d5e173STim Haley */ 112299d5e173STim Haley /* ARGSUSED */ 112399d5e173STim Haley static int 11244445fffbSMatthew Ahrens zfs_secpolicy_diff(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 112599d5e173STim Haley { 112699d5e173STim Haley int error; 112799d5e173STim Haley 112899d5e173STim Haley if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0) 112999d5e173STim Haley return (0); 113099d5e173STim Haley 113199d5e173STim Haley error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr); 113299d5e173STim Haley return (error); 113399d5e173STim Haley } 113499d5e173STim Haley 1135ea8dc4b6Seschrock /* 1136ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 1137ea8dc4b6Seschrock */ 1138ea8dc4b6Seschrock /* ARGSUSED */ 1139ea8dc4b6Seschrock static int 11404445fffbSMatthew Ahrens zfs_secpolicy_inject(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1141ea8dc4b6Seschrock { 1142ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 1143ea8dc4b6Seschrock } 1144ea8dc4b6Seschrock 11454445fffbSMatthew Ahrens /* ARGSUSED */ 1146e45ce728Sahrens static int 11474445fffbSMatthew Ahrens zfs_secpolicy_inherit_prop(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1148e45ce728Sahrens { 1149e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 1150e45ce728Sahrens 1151990b4856Slling if (prop == ZPROP_INVAL) { 1152e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 1153be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1154e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 1155e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 1156e45ce728Sahrens } else { 115792241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 115892241e0bSTom Erickson NULL, cr)); 1159e45ce728Sahrens } 1160e45ce728Sahrens } 1161e45ce728Sahrens 116214843421SMatthew Ahrens static int 11634445fffbSMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 116414843421SMatthew Ahrens { 11654445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 116614843421SMatthew Ahrens if (err) 116714843421SMatthew Ahrens return (err); 116814843421SMatthew Ahrens 116914843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1170be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 117114843421SMatthew Ahrens 117214843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 117314843421SMatthew Ahrens /* 117414843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 117514843421SMatthew Ahrens * themself, allow it. 117614843421SMatthew Ahrens */ 117714843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 117814843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 117914843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 118014843421SMatthew Ahrens return (0); 118114843421SMatthew Ahrens } else { 118214843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 118314843421SMatthew Ahrens return (0); 118414843421SMatthew Ahrens } 118514843421SMatthew Ahrens } 118614843421SMatthew Ahrens 118714843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 118814843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 118914843421SMatthew Ahrens } 119014843421SMatthew Ahrens 119114843421SMatthew Ahrens static int 11924445fffbSMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 119314843421SMatthew Ahrens { 11944445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 119514843421SMatthew Ahrens if (err) 119614843421SMatthew Ahrens return (err); 119714843421SMatthew Ahrens 119814843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1199be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 120014843421SMatthew Ahrens 120114843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 120214843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 120314843421SMatthew Ahrens } 120414843421SMatthew Ahrens 12054445fffbSMatthew Ahrens /* ARGSUSED */ 120614843421SMatthew Ahrens static int 12074445fffbSMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 120814843421SMatthew Ahrens { 120992241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 121092241e0bSTom Erickson NULL, cr)); 121114843421SMatthew Ahrens } 121214843421SMatthew Ahrens 12134445fffbSMatthew Ahrens /* ARGSUSED */ 1214842727c2SChris Kirby static int 12154445fffbSMatthew Ahrens zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1216842727c2SChris Kirby { 12173b2aab18SMatthew Ahrens nvpair_t *pair; 12183b2aab18SMatthew Ahrens nvlist_t *holds; 12193b2aab18SMatthew Ahrens int error; 12203b2aab18SMatthew Ahrens 12213b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(innvl, "holds", &holds); 12223b2aab18SMatthew Ahrens if (error != 0) 1223be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 12243b2aab18SMatthew Ahrens 12253b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 12263b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(holds, pair)) { 12273b2aab18SMatthew Ahrens char fsname[MAXNAMELEN]; 12283b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12293b2aab18SMatthew Ahrens if (error != 0) 12303b2aab18SMatthew Ahrens return (error); 12313b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12323b2aab18SMatthew Ahrens ZFS_DELEG_PERM_HOLD, cr); 12333b2aab18SMatthew Ahrens if (error != 0) 12343b2aab18SMatthew Ahrens return (error); 12353b2aab18SMatthew Ahrens } 12363b2aab18SMatthew Ahrens return (0); 1237842727c2SChris Kirby } 1238842727c2SChris Kirby 12394445fffbSMatthew Ahrens /* ARGSUSED */ 1240842727c2SChris Kirby static int 12414445fffbSMatthew Ahrens zfs_secpolicy_release(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1242842727c2SChris Kirby { 12433b2aab18SMatthew Ahrens nvpair_t *pair; 12443b2aab18SMatthew Ahrens int error; 12453b2aab18SMatthew Ahrens 12463b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 12473b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(innvl, pair)) { 12483b2aab18SMatthew Ahrens char fsname[MAXNAMELEN]; 12493b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12503b2aab18SMatthew Ahrens if (error != 0) 12513b2aab18SMatthew Ahrens return (error); 12523b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12533b2aab18SMatthew Ahrens ZFS_DELEG_PERM_RELEASE, cr); 12543b2aab18SMatthew Ahrens if (error != 0) 12553b2aab18SMatthew Ahrens return (error); 12563b2aab18SMatthew Ahrens } 12573b2aab18SMatthew Ahrens return (0); 1258842727c2SChris Kirby } 1259842727c2SChris Kirby 126099d5e173STim Haley /* 126199d5e173STim Haley * Policy for allowing temporary snapshots to be taken or released 126299d5e173STim Haley */ 126399d5e173STim Haley static int 12644445fffbSMatthew Ahrens zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 126599d5e173STim Haley { 126699d5e173STim Haley /* 126799d5e173STim Haley * A temporary snapshot is the same as a snapshot, 126899d5e173STim Haley * hold, destroy and release all rolled into one. 126999d5e173STim Haley * Delegated diff alone is sufficient that we allow this. 127099d5e173STim Haley */ 127199d5e173STim Haley int error; 127299d5e173STim Haley 127399d5e173STim Haley if ((error = zfs_secpolicy_write_perms(zc->zc_name, 127499d5e173STim Haley ZFS_DELEG_PERM_DIFF, cr)) == 0) 127599d5e173STim Haley return (0); 127699d5e173STim Haley 12774445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); 12783b2aab18SMatthew Ahrens if (error == 0) 12794445fffbSMatthew Ahrens error = zfs_secpolicy_hold(zc, innvl, cr); 12803b2aab18SMatthew Ahrens if (error == 0) 12814445fffbSMatthew Ahrens error = zfs_secpolicy_release(zc, innvl, cr); 12823b2aab18SMatthew Ahrens if (error == 0) 12834445fffbSMatthew Ahrens error = zfs_secpolicy_destroy(zc, innvl, cr); 128499d5e173STim Haley return (error); 128599d5e173STim Haley } 128699d5e173STim Haley 1287fa9e4066Sahrens /* 1288fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 1289fa9e4066Sahrens */ 1290fa9e4066Sahrens static int 1291478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 1292fa9e4066Sahrens { 1293fa9e4066Sahrens char *packed; 1294fa9e4066Sahrens int error; 1295990b4856Slling nvlist_t *list = NULL; 1296fa9e4066Sahrens 1297fa9e4066Sahrens /* 1298e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 1299fa9e4066Sahrens */ 1300990b4856Slling if (size == 0) 1301be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1302fa9e4066Sahrens 1303fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 1304fa9e4066Sahrens 1305478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 1306478ed9adSEric Taylor iflag)) != 0) { 1307fa9e4066Sahrens kmem_free(packed, size); 1308c71c00bbSRichard Yao return (SET_ERROR(EFAULT)); 1309fa9e4066Sahrens } 1310fa9e4066Sahrens 1311990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 1312fa9e4066Sahrens kmem_free(packed, size); 1313fa9e4066Sahrens return (error); 1314fa9e4066Sahrens } 1315fa9e4066Sahrens 1316fa9e4066Sahrens kmem_free(packed, size); 1317fa9e4066Sahrens 1318990b4856Slling *nvp = list; 1319fa9e4066Sahrens return (0); 1320fa9e4066Sahrens } 1321fa9e4066Sahrens 13224445fffbSMatthew Ahrens /* 13234445fffbSMatthew Ahrens * Reduce the size of this nvlist until it can be serialized in 'max' bytes. 13244445fffbSMatthew Ahrens * Entries will be removed from the end of the nvlist, and one int32 entry 13254445fffbSMatthew Ahrens * named "N_MORE_ERRORS" will be added indicating how many entries were 13264445fffbSMatthew Ahrens * removed. 13274445fffbSMatthew Ahrens */ 132892241e0bSTom Erickson static int 13294445fffbSMatthew Ahrens nvlist_smush(nvlist_t *errors, size_t max) 133092241e0bSTom Erickson { 133192241e0bSTom Erickson size_t size; 133292241e0bSTom Erickson 13334445fffbSMatthew Ahrens size = fnvlist_size(errors); 133492241e0bSTom Erickson 13354445fffbSMatthew Ahrens if (size > max) { 133692241e0bSTom Erickson nvpair_t *more_errors; 133792241e0bSTom Erickson int n = 0; 133892241e0bSTom Erickson 13394445fffbSMatthew Ahrens if (max < 1024) 1340be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 134192241e0bSTom Erickson 13424445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, 0); 13434445fffbSMatthew Ahrens more_errors = nvlist_prev_nvpair(errors, NULL); 134492241e0bSTom Erickson 134592241e0bSTom Erickson do { 13464445fffbSMatthew Ahrens nvpair_t *pair = nvlist_prev_nvpair(errors, 134792241e0bSTom Erickson more_errors); 13484445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, pair); 134992241e0bSTom Erickson n++; 13504445fffbSMatthew Ahrens size = fnvlist_size(errors); 13514445fffbSMatthew Ahrens } while (size > max); 135292241e0bSTom Erickson 13534445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, more_errors); 13544445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, n); 13554445fffbSMatthew Ahrens ASSERT3U(fnvlist_size(errors), <=, max); 135692241e0bSTom Erickson } 135792241e0bSTom Erickson 135892241e0bSTom Erickson return (0); 135992241e0bSTom Erickson } 136092241e0bSTom Erickson 1361e9dbad6fSeschrock static int 1362e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 1363e9dbad6fSeschrock { 1364e9dbad6fSeschrock char *packed = NULL; 13656e27f868SSam Falkner int error = 0; 1366e9dbad6fSeschrock size_t size; 1367e9dbad6fSeschrock 13684445fffbSMatthew Ahrens size = fnvlist_size(nvl); 1369e9dbad6fSeschrock 1370e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 1371be6fd75aSMatthew Ahrens error = SET_ERROR(ENOMEM); 1372e9dbad6fSeschrock } else { 13734445fffbSMatthew Ahrens packed = fnvlist_pack(nvl, &size); 13746e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 13756e27f868SSam Falkner size, zc->zc_iflags) != 0) 1376be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 13774445fffbSMatthew Ahrens fnvlist_pack_free(packed, size); 1378e9dbad6fSeschrock } 1379e9dbad6fSeschrock 1380e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 13814445fffbSMatthew Ahrens zc->zc_nvlist_dst_filled = B_TRUE; 1382e9dbad6fSeschrock return (error); 1383e9dbad6fSeschrock } 1384e9dbad6fSeschrock 138514843421SMatthew Ahrens static int 1386af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 138714843421SMatthew Ahrens { 138814843421SMatthew Ahrens objset_t *os; 138914843421SMatthew Ahrens int error; 139014843421SMatthew Ahrens 1391503ad85cSMatthew Ahrens error = dmu_objset_hold(dsname, FTAG, &os); 13923b2aab18SMatthew Ahrens if (error != 0) 139314843421SMatthew Ahrens return (error); 1394503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1395503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1396be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1397503ad85cSMatthew Ahrens } 139814843421SMatthew Ahrens 1399503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 1400af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 1401af4c679fSSean McEnroe if (*zfvp) { 1402af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 140314843421SMatthew Ahrens } else { 1404be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 140514843421SMatthew Ahrens } 1406503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 1407503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 140814843421SMatthew Ahrens return (error); 140914843421SMatthew Ahrens } 141014843421SMatthew Ahrens 141114843421SMatthew Ahrens /* 141214843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 141314843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 1414ad135b5dSChristopher Siden * If 'writer' is set, the z_teardown_lock will be held for RW_WRITER, 1415ad135b5dSChristopher Siden * which prevents all vnode ops from running. 141614843421SMatthew Ahrens */ 141714843421SMatthew Ahrens static int 14181412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) 141914843421SMatthew Ahrens { 142014843421SMatthew Ahrens int error = 0; 142114843421SMatthew Ahrens 1422af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1423af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 142414843421SMatthew Ahrens if (error == 0) { 1425c9030f6cSAlexander Motin rrm_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : 14261412a1a2SMark Shellenbaum RW_READER, tag); 1427af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 142814843421SMatthew Ahrens /* 142914843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 143014843421SMatthew Ahrens * thread should be just about to disassociate the 143114843421SMatthew Ahrens * objset from the zfsvfs. 143214843421SMatthew Ahrens */ 1433c9030f6cSAlexander Motin rrm_exit(&(*zfvp)->z_teardown_lock, tag); 1434be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 143514843421SMatthew Ahrens } 143614843421SMatthew Ahrens } 143714843421SMatthew Ahrens return (error); 143814843421SMatthew Ahrens } 143914843421SMatthew Ahrens 144014843421SMatthew Ahrens static void 144114843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 144214843421SMatthew Ahrens { 1443c9030f6cSAlexander Motin rrm_exit(&zfsvfs->z_teardown_lock, tag); 144414843421SMatthew Ahrens 144514843421SMatthew Ahrens if (zfsvfs->z_vfs) { 144614843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 144714843421SMatthew Ahrens } else { 1448503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 144914843421SMatthew Ahrens zfsvfs_free(zfsvfs); 145014843421SMatthew Ahrens } 145114843421SMatthew Ahrens } 145214843421SMatthew Ahrens 1453fa9e4066Sahrens static int 1454fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1455fa9e4066Sahrens { 1456fa9e4066Sahrens int error; 1457990b4856Slling nvlist_t *config, *props = NULL; 14580a48a24eStimh nvlist_t *rootprops = NULL; 14590a48a24eStimh nvlist_t *zplprops = NULL; 1460fa9e4066Sahrens 1461990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1462478ed9adSEric Taylor zc->zc_iflags, &config)) 1463fa9e4066Sahrens return (error); 14642a6b87f0Sek 1465990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1466478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1467478ed9adSEric Taylor zc->zc_iflags, &props))) { 1468990b4856Slling nvlist_free(config); 1469990b4856Slling return (error); 1470990b4856Slling } 1471990b4856Slling 14720a48a24eStimh if (props) { 14730a48a24eStimh nvlist_t *nvl = NULL; 14740a48a24eStimh uint64_t version = SPA_VERSION; 14750a48a24eStimh 14760a48a24eStimh (void) nvlist_lookup_uint64(props, 14770a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 1478ad135b5dSChristopher Siden if (!SPA_VERSION_IS_SUPPORTED(version)) { 1479be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 14800a48a24eStimh goto pool_props_bad; 14810a48a24eStimh } 14820a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 14830a48a24eStimh if (nvl) { 14840a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 14850a48a24eStimh if (error != 0) { 14860a48a24eStimh nvlist_free(config); 14870a48a24eStimh nvlist_free(props); 14880a48a24eStimh return (error); 14890a48a24eStimh } 14900a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 14910a48a24eStimh } 14920a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 14930a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 14940a48a24eStimh zplprops, NULL); 14953b2aab18SMatthew Ahrens if (error != 0) 14960a48a24eStimh goto pool_props_bad; 14970a48a24eStimh } 14980a48a24eStimh 14994445fffbSMatthew Ahrens error = spa_create(zc->zc_name, config, props, zplprops); 15000a48a24eStimh 15010a48a24eStimh /* 15020a48a24eStimh * Set the remaining root properties 15030a48a24eStimh */ 150492241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 150592241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 15060a48a24eStimh (void) spa_destroy(zc->zc_name); 1507fa9e4066Sahrens 15080a48a24eStimh pool_props_bad: 15090a48a24eStimh nvlist_free(rootprops); 15100a48a24eStimh nvlist_free(zplprops); 1511fa9e4066Sahrens nvlist_free(config); 15120a48a24eStimh nvlist_free(props); 1513990b4856Slling 1514fa9e4066Sahrens return (error); 1515fa9e4066Sahrens } 1516fa9e4066Sahrens 1517fa9e4066Sahrens static int 1518fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1519fa9e4066Sahrens { 1520ecd6cf80Smarks int error; 1521ecd6cf80Smarks zfs_log_history(zc); 1522ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1523681d9761SEric Taylor if (error == 0) 1524681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1525ecd6cf80Smarks return (error); 1526fa9e4066Sahrens } 1527fa9e4066Sahrens 1528fa9e4066Sahrens static int 1529fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1530fa9e4066Sahrens { 1531990b4856Slling nvlist_t *config, *props = NULL; 1532fa9e4066Sahrens uint64_t guid; 1533468c413aSTim Haley int error; 1534fa9e4066Sahrens 1535990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1536478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1537990b4856Slling return (error); 1538990b4856Slling 1539990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1540478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1541478ed9adSEric Taylor zc->zc_iflags, &props))) { 1542990b4856Slling nvlist_free(config); 1543fa9e4066Sahrens return (error); 1544990b4856Slling } 1545fa9e4066Sahrens 1546fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1547ea8dc4b6Seschrock guid != zc->zc_guid) 1548be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 1549fa9e4066Sahrens else 15504b964adaSGeorge Wilson error = spa_import(zc->zc_name, config, props, zc->zc_cookie); 1551fa9e4066Sahrens 15524b964adaSGeorge Wilson if (zc->zc_nvlist_dst != 0) { 15534b964adaSGeorge Wilson int err; 15544b964adaSGeorge Wilson 15554b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 15564b964adaSGeorge Wilson error = err; 15574b964adaSGeorge Wilson } 1558468c413aSTim Haley 1559fa9e4066Sahrens nvlist_free(config); 1560fa9e4066Sahrens 1561990b4856Slling if (props) 1562990b4856Slling nvlist_free(props); 1563990b4856Slling 1564fa9e4066Sahrens return (error); 1565fa9e4066Sahrens } 1566fa9e4066Sahrens 1567fa9e4066Sahrens static int 1568fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1569fa9e4066Sahrens { 1570ecd6cf80Smarks int error; 157189a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1572394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 157389a89ebfSlling 1574ecd6cf80Smarks zfs_log_history(zc); 1575394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1576681d9761SEric Taylor if (error == 0) 1577681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1578ecd6cf80Smarks return (error); 1579fa9e4066Sahrens } 1580fa9e4066Sahrens 1581fa9e4066Sahrens static int 1582fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1583fa9e4066Sahrens { 1584fa9e4066Sahrens nvlist_t *configs; 1585fa9e4066Sahrens int error; 1586fa9e4066Sahrens 1587fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1588be6fd75aSMatthew Ahrens return (SET_ERROR(EEXIST)); 1589fa9e4066Sahrens 1590e9dbad6fSeschrock error = put_nvlist(zc, configs); 1591fa9e4066Sahrens 1592fa9e4066Sahrens nvlist_free(configs); 1593fa9e4066Sahrens 1594fa9e4066Sahrens return (error); 1595fa9e4066Sahrens } 1596fa9e4066Sahrens 1597ad135b5dSChristopher Siden /* 1598ad135b5dSChristopher Siden * inputs: 1599ad135b5dSChristopher Siden * zc_name name of the pool 1600ad135b5dSChristopher Siden * 1601ad135b5dSChristopher Siden * outputs: 1602ad135b5dSChristopher Siden * zc_cookie real errno 1603ad135b5dSChristopher Siden * zc_nvlist_dst config nvlist 1604ad135b5dSChristopher Siden * zc_nvlist_dst_size size of config nvlist 1605ad135b5dSChristopher Siden */ 1606fa9e4066Sahrens static int 1607fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1608fa9e4066Sahrens { 1609fa9e4066Sahrens nvlist_t *config; 1610fa9e4066Sahrens int error; 1611ea8dc4b6Seschrock int ret = 0; 1612fa9e4066Sahrens 1613e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1614e9dbad6fSeschrock sizeof (zc->zc_value)); 1615fa9e4066Sahrens 1616fa9e4066Sahrens if (config != NULL) { 1617e9dbad6fSeschrock ret = put_nvlist(zc, config); 1618fa9e4066Sahrens nvlist_free(config); 1619ea8dc4b6Seschrock 1620ea8dc4b6Seschrock /* 1621ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1622ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1623ea8dc4b6Seschrock * in 'zc_cookie'. 1624ea8dc4b6Seschrock */ 1625ea8dc4b6Seschrock zc->zc_cookie = error; 1626fa9e4066Sahrens } else { 1627ea8dc4b6Seschrock ret = error; 1628fa9e4066Sahrens } 1629fa9e4066Sahrens 1630ea8dc4b6Seschrock return (ret); 1631fa9e4066Sahrens } 1632fa9e4066Sahrens 1633fa9e4066Sahrens /* 1634fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1635fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1636fa9e4066Sahrens */ 1637fa9e4066Sahrens static int 1638fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1639fa9e4066Sahrens { 1640fa9e4066Sahrens nvlist_t *tryconfig, *config; 1641fa9e4066Sahrens int error; 1642fa9e4066Sahrens 1643990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1644478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1645fa9e4066Sahrens return (error); 1646fa9e4066Sahrens 1647fa9e4066Sahrens config = spa_tryimport(tryconfig); 1648fa9e4066Sahrens 1649fa9e4066Sahrens nvlist_free(tryconfig); 1650fa9e4066Sahrens 1651fa9e4066Sahrens if (config == NULL) 1652be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1653fa9e4066Sahrens 1654e9dbad6fSeschrock error = put_nvlist(zc, config); 1655fa9e4066Sahrens nvlist_free(config); 1656fa9e4066Sahrens 1657fa9e4066Sahrens return (error); 1658fa9e4066Sahrens } 1659fa9e4066Sahrens 16603f9d6ad7SLin Ling /* 16613f9d6ad7SLin Ling * inputs: 16623f9d6ad7SLin Ling * zc_name name of the pool 16633f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 16643f9d6ad7SLin Ling */ 1665fa9e4066Sahrens static int 16663f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1667fa9e4066Sahrens { 1668fa9e4066Sahrens spa_t *spa; 1669fa9e4066Sahrens int error; 1670fa9e4066Sahrens 167106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 167206eeb2adSek return (error); 167306eeb2adSek 16743f9d6ad7SLin Ling if (zc->zc_cookie == POOL_SCAN_NONE) 16753f9d6ad7SLin Ling error = spa_scan_stop(spa); 16763f9d6ad7SLin Ling else 16773f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 167806eeb2adSek 167906eeb2adSek spa_close(spa, FTAG); 168006eeb2adSek 1681fa9e4066Sahrens return (error); 1682fa9e4066Sahrens } 1683fa9e4066Sahrens 1684fa9e4066Sahrens static int 1685fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1686fa9e4066Sahrens { 1687fa9e4066Sahrens spa_t *spa; 1688fa9e4066Sahrens int error; 1689fa9e4066Sahrens 1690fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1691fa9e4066Sahrens if (error == 0) { 1692fa9e4066Sahrens spa_freeze(spa); 1693fa9e4066Sahrens spa_close(spa, FTAG); 1694fa9e4066Sahrens } 1695fa9e4066Sahrens return (error); 1696fa9e4066Sahrens } 1697fa9e4066Sahrens 1698eaca9bbdSeschrock static int 1699eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1700eaca9bbdSeschrock { 1701eaca9bbdSeschrock spa_t *spa; 1702eaca9bbdSeschrock int error; 1703eaca9bbdSeschrock 170406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 170506eeb2adSek return (error); 170606eeb2adSek 1707ad135b5dSChristopher Siden if (zc->zc_cookie < spa_version(spa) || 1708ad135b5dSChristopher Siden !SPA_VERSION_IS_SUPPORTED(zc->zc_cookie)) { 1709558d2d50Slling spa_close(spa, FTAG); 1710be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1711558d2d50Slling } 1712558d2d50Slling 1713990b4856Slling spa_upgrade(spa, zc->zc_cookie); 171406eeb2adSek spa_close(spa, FTAG); 171506eeb2adSek 171606eeb2adSek return (error); 171706eeb2adSek } 171806eeb2adSek 171906eeb2adSek static int 172006eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 172106eeb2adSek { 172206eeb2adSek spa_t *spa; 172306eeb2adSek char *hist_buf; 172406eeb2adSek uint64_t size; 172506eeb2adSek int error; 172606eeb2adSek 172706eeb2adSek if ((size = zc->zc_history_len) == 0) 1728be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 172906eeb2adSek 173006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 173106eeb2adSek return (error); 173206eeb2adSek 1733e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1734d7306b64Sek spa_close(spa, FTAG); 1735be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 1736d7306b64Sek } 1737d7306b64Sek 173806eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 173906eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 174006eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1741478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1742478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1743478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 174406eeb2adSek } 174506eeb2adSek 174606eeb2adSek spa_close(spa, FTAG); 174706eeb2adSek kmem_free(hist_buf, size); 174806eeb2adSek return (error); 174906eeb2adSek } 175006eeb2adSek 1751e9103aaeSGarrett D'Amore static int 1752e9103aaeSGarrett D'Amore zfs_ioc_pool_reguid(zfs_cmd_t *zc) 1753e9103aaeSGarrett D'Amore { 1754e9103aaeSGarrett D'Amore spa_t *spa; 1755e9103aaeSGarrett D'Amore int error; 1756e9103aaeSGarrett D'Amore 1757e9103aaeSGarrett D'Amore error = spa_open(zc->zc_name, &spa, FTAG); 1758e9103aaeSGarrett D'Amore if (error == 0) { 1759e9103aaeSGarrett D'Amore error = spa_change_guid(spa); 1760e9103aaeSGarrett D'Amore spa_close(spa, FTAG); 1761e9103aaeSGarrett D'Amore } 1762e9103aaeSGarrett D'Amore return (error); 1763e9103aaeSGarrett D'Amore } 1764e9103aaeSGarrett D'Amore 176555434c77Sek static int 176655434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 176755434c77Sek { 17683b2aab18SMatthew Ahrens return (dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)); 176955434c77Sek } 177055434c77Sek 1771503ad85cSMatthew Ahrens /* 1772503ad85cSMatthew Ahrens * inputs: 1773503ad85cSMatthew Ahrens * zc_name name of filesystem 1774503ad85cSMatthew Ahrens * zc_obj object to find 1775503ad85cSMatthew Ahrens * 1776503ad85cSMatthew Ahrens * outputs: 1777503ad85cSMatthew Ahrens * zc_value name of object 1778503ad85cSMatthew Ahrens */ 177955434c77Sek static int 178055434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 178155434c77Sek { 1782503ad85cSMatthew Ahrens objset_t *os; 178355434c77Sek int error; 178455434c77Sek 1785503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1786503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 178755434c77Sek return (error); 1788503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1789503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1790be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1791503ad85cSMatthew Ahrens } 1792503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 179355434c77Sek sizeof (zc->zc_value)); 1794503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 179555434c77Sek 179655434c77Sek return (error); 179755434c77Sek } 179855434c77Sek 179999d5e173STim Haley /* 180099d5e173STim Haley * inputs: 180199d5e173STim Haley * zc_name name of filesystem 180299d5e173STim Haley * zc_obj object to find 180399d5e173STim Haley * 180499d5e173STim Haley * outputs: 180599d5e173STim Haley * zc_stat stats on object 180699d5e173STim Haley * zc_value path to object 180799d5e173STim Haley */ 180899d5e173STim Haley static int 180999d5e173STim Haley zfs_ioc_obj_to_stats(zfs_cmd_t *zc) 181099d5e173STim Haley { 181199d5e173STim Haley objset_t *os; 181299d5e173STim Haley int error; 181399d5e173STim Haley 181499d5e173STim Haley /* XXX reading from objset not owned */ 181599d5e173STim Haley if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 181699d5e173STim Haley return (error); 181799d5e173STim Haley if (dmu_objset_type(os) != DMU_OST_ZFS) { 181899d5e173STim Haley dmu_objset_rele(os, FTAG); 1819be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 182099d5e173STim Haley } 182199d5e173STim Haley error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value, 182299d5e173STim Haley sizeof (zc->zc_value)); 182399d5e173STim Haley dmu_objset_rele(os, FTAG); 182499d5e173STim Haley 182599d5e173STim Haley return (error); 182699d5e173STim Haley } 182799d5e173STim Haley 1828fa9e4066Sahrens static int 1829fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1830fa9e4066Sahrens { 1831fa9e4066Sahrens spa_t *spa; 1832fa9e4066Sahrens int error; 1833e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1834e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1835fa9e4066Sahrens 1836fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1837fa9e4066Sahrens if (error != 0) 1838fa9e4066Sahrens return (error); 1839fa9e4066Sahrens 1840fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1841478ed9adSEric Taylor zc->zc_iflags, &config); 1842fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1843fa94a07fSbrendan &l2cache, &nl2cache); 1844fa94a07fSbrendan 1845e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1846e7cbe64fSgw &spares, &nspares); 1847e7cbe64fSgw 1848b1b8ab34Slling /* 1849b1b8ab34Slling * A root pool with concatenated devices is not supported. 1850e7cbe64fSgw * Thus, can not add a device to a root pool. 1851e7cbe64fSgw * 1852e7cbe64fSgw * Intent log device can not be added to a rootpool because 1853e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1854e7cbe64fSgw * can not be accessed during the mountroot time. 1855e7cbe64fSgw * 1856e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1857b1b8ab34Slling */ 1858b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 18591195e687SMark J Musante nvlist_free(config); 1860b1b8ab34Slling spa_close(spa, FTAG); 1861be6fd75aSMatthew Ahrens return (SET_ERROR(EDOM)); 1862b1b8ab34Slling } 1863b1b8ab34Slling 1864fa94a07fSbrendan if (error == 0) { 1865fa9e4066Sahrens error = spa_vdev_add(spa, config); 1866fa9e4066Sahrens nvlist_free(config); 1867fa9e4066Sahrens } 1868fa9e4066Sahrens spa_close(spa, FTAG); 1869fa9e4066Sahrens return (error); 1870fa9e4066Sahrens } 1871fa9e4066Sahrens 18723f9d6ad7SLin Ling /* 18733f9d6ad7SLin Ling * inputs: 18743f9d6ad7SLin Ling * zc_name name of the pool 18753f9d6ad7SLin Ling * zc_nvlist_conf nvlist of devices to remove 18763f9d6ad7SLin Ling * zc_cookie to stop the remove? 18773f9d6ad7SLin Ling */ 1878fa9e4066Sahrens static int 1879fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1880fa9e4066Sahrens { 188199653d4eSeschrock spa_t *spa; 188299653d4eSeschrock int error; 188399653d4eSeschrock 188499653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 188599653d4eSeschrock if (error != 0) 188699653d4eSeschrock return (error); 188799653d4eSeschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 188899653d4eSeschrock spa_close(spa, FTAG); 188999653d4eSeschrock return (error); 1890fa9e4066Sahrens } 1891fa9e4066Sahrens 1892fa9e4066Sahrens static int 18933d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1894fa9e4066Sahrens { 1895fa9e4066Sahrens spa_t *spa; 1896fa9e4066Sahrens int error; 18973d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1898fa9e4066Sahrens 189906eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1900fa9e4066Sahrens return (error); 19013d7072f8Seschrock switch (zc->zc_cookie) { 19023d7072f8Seschrock case VDEV_STATE_ONLINE: 19033d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 19043d7072f8Seschrock break; 1905fa9e4066Sahrens 19063d7072f8Seschrock case VDEV_STATE_OFFLINE: 19073d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 19083d7072f8Seschrock break; 1909fa9e4066Sahrens 19103d7072f8Seschrock case VDEV_STATE_FAULTED: 1911069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1912069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1913069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1914069f55e2SEric Schrock 1915069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 19163d7072f8Seschrock break; 19173d7072f8Seschrock 19183d7072f8Seschrock case VDEV_STATE_DEGRADED: 1919069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1920069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1921069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1922069f55e2SEric Schrock 1923069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 19243d7072f8Seschrock break; 19253d7072f8Seschrock 19263d7072f8Seschrock default: 1927be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 19283d7072f8Seschrock } 19293d7072f8Seschrock zc->zc_cookie = newstate; 1930fa9e4066Sahrens spa_close(spa, FTAG); 1931fa9e4066Sahrens return (error); 1932fa9e4066Sahrens } 1933fa9e4066Sahrens 1934fa9e4066Sahrens static int 1935fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1936fa9e4066Sahrens { 1937fa9e4066Sahrens spa_t *spa; 1938fa9e4066Sahrens int replacing = zc->zc_cookie; 1939fa9e4066Sahrens nvlist_t *config; 1940fa9e4066Sahrens int error; 1941fa9e4066Sahrens 194206eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1943fa9e4066Sahrens return (error); 1944fa9e4066Sahrens 1945990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1946478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1947ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1948fa9e4066Sahrens nvlist_free(config); 1949fa9e4066Sahrens } 1950fa9e4066Sahrens 1951fa9e4066Sahrens spa_close(spa, FTAG); 1952fa9e4066Sahrens return (error); 1953fa9e4066Sahrens } 1954fa9e4066Sahrens 1955fa9e4066Sahrens static int 1956fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 1957fa9e4066Sahrens { 1958fa9e4066Sahrens spa_t *spa; 1959fa9e4066Sahrens int error; 1960fa9e4066Sahrens 196106eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1962fa9e4066Sahrens return (error); 1963fa9e4066Sahrens 19648ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 1965fa9e4066Sahrens 1966fa9e4066Sahrens spa_close(spa, FTAG); 1967fa9e4066Sahrens return (error); 1968fa9e4066Sahrens } 1969fa9e4066Sahrens 19701195e687SMark J Musante static int 19711195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 19721195e687SMark J Musante { 19731195e687SMark J Musante spa_t *spa; 19741195e687SMark J Musante nvlist_t *config, *props = NULL; 19751195e687SMark J Musante int error; 19761195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 19771195e687SMark J Musante 19781195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 19791195e687SMark J Musante return (error); 19801195e687SMark J Musante 19811195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 19821195e687SMark J Musante zc->zc_iflags, &config)) { 19831195e687SMark J Musante spa_close(spa, FTAG); 19841195e687SMark J Musante return (error); 19851195e687SMark J Musante } 19861195e687SMark J Musante 19871195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 19881195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 19891195e687SMark J Musante zc->zc_iflags, &props))) { 19901195e687SMark J Musante spa_close(spa, FTAG); 19911195e687SMark J Musante nvlist_free(config); 19921195e687SMark J Musante return (error); 19931195e687SMark J Musante } 19941195e687SMark J Musante 19951195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 19961195e687SMark J Musante 19971195e687SMark J Musante spa_close(spa, FTAG); 19981195e687SMark J Musante 19991195e687SMark J Musante nvlist_free(config); 20001195e687SMark J Musante nvlist_free(props); 20011195e687SMark J Musante 20021195e687SMark J Musante return (error); 20031195e687SMark J Musante } 20041195e687SMark J Musante 2005c67d9675Seschrock static int 2006c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 2007c67d9675Seschrock { 2008c67d9675Seschrock spa_t *spa; 2009e9dbad6fSeschrock char *path = zc->zc_value; 2010ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 2011c67d9675Seschrock int error; 2012c67d9675Seschrock 2013c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 2014c67d9675Seschrock if (error != 0) 2015c67d9675Seschrock return (error); 2016c67d9675Seschrock 2017c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 2018c67d9675Seschrock spa_close(spa, FTAG); 2019c67d9675Seschrock return (error); 2020c67d9675Seschrock } 2021c67d9675Seschrock 20226809eb4eSEric Schrock static int 20236809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 20246809eb4eSEric Schrock { 20256809eb4eSEric Schrock spa_t *spa; 20266809eb4eSEric Schrock char *fru = zc->zc_value; 20276809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 20286809eb4eSEric Schrock int error; 20296809eb4eSEric Schrock 20306809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 20316809eb4eSEric Schrock if (error != 0) 20326809eb4eSEric Schrock return (error); 20336809eb4eSEric Schrock 20346809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 20356809eb4eSEric Schrock spa_close(spa, FTAG); 20366809eb4eSEric Schrock return (error); 20376809eb4eSEric Schrock } 20386809eb4eSEric Schrock 2039fa9e4066Sahrens static int 2040a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) 2041fa9e4066Sahrens { 2042a7f53a56SChris Kirby int error = 0; 20437f7322feSeschrock nvlist_t *nv; 2044fa9e4066Sahrens 2045a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2046fa9e4066Sahrens 20475ad82045Snd if (zc->zc_nvlist_dst != 0 && 204892241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 2049a2eea2e1Sahrens dmu_objset_stats(os, nv); 2050432f72fdSahrens /* 2051bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 2052432f72fdSahrens * which we aren't supposed to do with a 2053745cd3c5Smaybee * DS_MODE_USER hold, because it could be 2054432f72fdSahrens * inconsistent. So this is a bit of a workaround... 2055503ad85cSMatthew Ahrens * XXX reading with out owning 2056432f72fdSahrens */ 205719b94df9SMatthew Ahrens if (!zc->zc_objset_stats.dds_inconsistent && 205819b94df9SMatthew Ahrens dmu_objset_type(os) == DMU_OST_ZVOL) { 205919b94df9SMatthew Ahrens error = zvol_get_stats(os, nv); 206019b94df9SMatthew Ahrens if (error == EIO) 206119b94df9SMatthew Ahrens return (error); 2062fb09f5aaSMadhav Suresh VERIFY0(error); 2063e7437265Sahrens } 2064e9dbad6fSeschrock error = put_nvlist(zc, nv); 20657f7322feSeschrock nvlist_free(nv); 20667f7322feSeschrock } 2067fa9e4066Sahrens 2068a7f53a56SChris Kirby return (error); 2069a7f53a56SChris Kirby } 2070a7f53a56SChris Kirby 2071a7f53a56SChris Kirby /* 2072a7f53a56SChris Kirby * inputs: 2073a7f53a56SChris Kirby * zc_name name of filesystem 2074a7f53a56SChris Kirby * zc_nvlist_dst_size size of buffer for property nvlist 2075a7f53a56SChris Kirby * 2076a7f53a56SChris Kirby * outputs: 2077a7f53a56SChris Kirby * zc_objset_stats stats 2078a7f53a56SChris Kirby * zc_nvlist_dst property nvlist 2079a7f53a56SChris Kirby * zc_nvlist_dst_size size of property nvlist 2080a7f53a56SChris Kirby */ 2081a7f53a56SChris Kirby static int 2082a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc) 2083a7f53a56SChris Kirby { 20843b2aab18SMatthew Ahrens objset_t *os; 2085a7f53a56SChris Kirby int error; 2086a7f53a56SChris Kirby 20873b2aab18SMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 20883b2aab18SMatthew Ahrens if (error == 0) { 20893b2aab18SMatthew Ahrens error = zfs_ioc_objset_stats_impl(zc, os); 20903b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 20913b2aab18SMatthew Ahrens } 2092a7f53a56SChris Kirby 2093fa9e4066Sahrens return (error); 2094fa9e4066Sahrens } 2095fa9e4066Sahrens 209692241e0bSTom Erickson /* 209792241e0bSTom Erickson * inputs: 209892241e0bSTom Erickson * zc_name name of filesystem 209992241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 210092241e0bSTom Erickson * 210192241e0bSTom Erickson * outputs: 210292241e0bSTom Erickson * zc_nvlist_dst received property nvlist 210392241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 210492241e0bSTom Erickson * 210592241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 210692241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 210792241e0bSTom Erickson * local property values. 210892241e0bSTom Erickson */ 210992241e0bSTom Erickson static int 211092241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 211192241e0bSTom Erickson { 21123b2aab18SMatthew Ahrens int error = 0; 211392241e0bSTom Erickson nvlist_t *nv; 211492241e0bSTom Erickson 211592241e0bSTom Erickson /* 211692241e0bSTom Erickson * Without this check, we would return local property values if the 211792241e0bSTom Erickson * caller has not already received properties on or after 211892241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 211992241e0bSTom Erickson */ 21203b2aab18SMatthew Ahrens if (!dsl_prop_get_hasrecvd(zc->zc_name)) 2121be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 212292241e0bSTom Erickson 212392241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 21243b2aab18SMatthew Ahrens (error = dsl_prop_get_received(zc->zc_name, &nv)) == 0) { 212592241e0bSTom Erickson error = put_nvlist(zc, nv); 212692241e0bSTom Erickson nvlist_free(nv); 212792241e0bSTom Erickson } 212892241e0bSTom Erickson 212992241e0bSTom Erickson return (error); 213092241e0bSTom Erickson } 213192241e0bSTom Erickson 2132de8267e0Stimh static int 2133de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 2134de8267e0Stimh { 2135de8267e0Stimh uint64_t value; 2136de8267e0Stimh int error; 2137de8267e0Stimh 2138de8267e0Stimh /* 2139de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 2140de8267e0Stimh * the default value (if there is one). 2141de8267e0Stimh */ 2142de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 2143de8267e0Stimh return (error); 2144de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 2145de8267e0Stimh return (0); 2146de8267e0Stimh } 2147de8267e0Stimh 21483cb34c60Sahrens /* 21493cb34c60Sahrens * inputs: 21503cb34c60Sahrens * zc_name name of filesystem 2151de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 21523cb34c60Sahrens * 21533cb34c60Sahrens * outputs: 2154de8267e0Stimh * zc_nvlist_dst zpl property nvlist 2155de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 21563cb34c60Sahrens */ 2157bd00f61bSrm static int 2158de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 2159bd00f61bSrm { 2160de8267e0Stimh objset_t *os; 2161de8267e0Stimh int err; 2162bd00f61bSrm 2163503ad85cSMatthew Ahrens /* XXX reading without owning */ 2164503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 2165de8267e0Stimh return (err); 2166bd00f61bSrm 2167bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2168bd00f61bSrm 2169bd00f61bSrm /* 2170de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 2171745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 2172745cd3c5Smaybee * hold, because it could be inconsistent. 2173bd00f61bSrm */ 2174de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 2175de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 2176de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 2177de8267e0Stimh nvlist_t *nv; 2178de8267e0Stimh 2179de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2180de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 2181de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 2182de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 2183de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 2184de8267e0Stimh err = put_nvlist(zc, nv); 2185de8267e0Stimh nvlist_free(nv); 2186de8267e0Stimh } else { 2187be6fd75aSMatthew Ahrens err = SET_ERROR(ENOENT); 2188de8267e0Stimh } 2189503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2190de8267e0Stimh return (err); 2191bd00f61bSrm } 2192bd00f61bSrm 219314843421SMatthew Ahrens static boolean_t 219414843421SMatthew Ahrens dataset_name_hidden(const char *name) 219514843421SMatthew Ahrens { 219614843421SMatthew Ahrens /* 219714843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 219814843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 219914843421SMatthew Ahrens * temporary datasets (which have a % in their name). 220014843421SMatthew Ahrens */ 220114843421SMatthew Ahrens if (strchr(name, '$') != NULL) 220214843421SMatthew Ahrens return (B_TRUE); 220314843421SMatthew Ahrens if (strchr(name, '%') != NULL) 220414843421SMatthew Ahrens return (B_TRUE); 220514843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 220614843421SMatthew Ahrens return (B_TRUE); 220714843421SMatthew Ahrens return (B_FALSE); 220814843421SMatthew Ahrens } 220914843421SMatthew Ahrens 2210de8267e0Stimh /* 2211de8267e0Stimh * inputs: 2212de8267e0Stimh * zc_name name of filesystem 2213de8267e0Stimh * zc_cookie zap cursor 2214de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 2215de8267e0Stimh * 2216de8267e0Stimh * outputs: 2217de8267e0Stimh * zc_name name of next filesystem 221814843421SMatthew Ahrens * zc_cookie zap cursor 2219de8267e0Stimh * zc_objset_stats stats 2220de8267e0Stimh * zc_nvlist_dst property nvlist 2221de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 2222de8267e0Stimh */ 2223fa9e4066Sahrens static int 2224fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 2225fa9e4066Sahrens { 222687e5029aSahrens objset_t *os; 2227fa9e4066Sahrens int error; 2228fa9e4066Sahrens char *p; 2229620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 2230fa9e4066Sahrens 2231620252bcSChris Kirby top: 2232503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 223387e5029aSahrens if (error == ENOENT) 2234be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 223587e5029aSahrens return (error); 2236fa9e4066Sahrens } 2237fa9e4066Sahrens 2238fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 2239fa9e4066Sahrens if (p == NULL || p[1] != '\0') 2240fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 2241fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 2242fa9e4066Sahrens 2243fa9e4066Sahrens do { 224487e5029aSahrens error = dmu_dir_list_next(os, 224587e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 224687e5029aSahrens NULL, &zc->zc_cookie); 2247fa9e4066Sahrens if (error == ENOENT) 2248be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 224919b94df9SMatthew Ahrens } while (error == 0 && dataset_name_hidden(zc->zc_name)); 2250503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2251fa9e4066Sahrens 2252681d9761SEric Taylor /* 2253681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 2254681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 2255681d9761SEric Taylor */ 2256620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 225787e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 2258620252bcSChris Kirby if (error == ENOENT) { 2259620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 2260620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 2261620252bcSChris Kirby goto top; 2262620252bcSChris Kirby } 2263620252bcSChris Kirby } 2264fa9e4066Sahrens return (error); 2265fa9e4066Sahrens } 2266fa9e4066Sahrens 22673cb34c60Sahrens /* 22683cb34c60Sahrens * inputs: 22693cb34c60Sahrens * zc_name name of filesystem 22703cb34c60Sahrens * zc_cookie zap cursor 22713cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 22723cb34c60Sahrens * 22733cb34c60Sahrens * outputs: 22743cb34c60Sahrens * zc_name name of next snapshot 22753cb34c60Sahrens * zc_objset_stats stats 22763cb34c60Sahrens * zc_nvlist_dst property nvlist 22773cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 22783cb34c60Sahrens */ 2279fa9e4066Sahrens static int 2280fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 2281fa9e4066Sahrens { 228287e5029aSahrens objset_t *os; 2283fa9e4066Sahrens int error; 2284fa9e4066Sahrens 2285503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 22863b2aab18SMatthew Ahrens if (error != 0) { 2287745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 22883b2aab18SMatthew Ahrens } 2289fa9e4066Sahrens 2290b81d61a6Slling /* 2291b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 2292b81d61a6Slling * so exit immediately. 2293b81d61a6Slling */ 2294b81d61a6Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) { 2295503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2296be6fd75aSMatthew Ahrens return (SET_ERROR(ESRCH)); 2297fa9e4066Sahrens } 2298fa9e4066Sahrens 229987e5029aSahrens error = dmu_snapshot_list_next(os, 230087e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 2301a7f53a56SChris Kirby zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, 2302a7f53a56SChris Kirby NULL); 2303a7f53a56SChris Kirby 2304620252bcSChris Kirby if (error == 0) { 2305a7f53a56SChris Kirby dsl_dataset_t *ds; 2306a7f53a56SChris Kirby dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; 2307a7f53a56SChris Kirby 2308a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); 23093b2aab18SMatthew Ahrens if (error == 0) { 2310a7f53a56SChris Kirby objset_t *ossnap; 2311a7f53a56SChris Kirby 2312a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &ossnap); 2313a7f53a56SChris Kirby if (error == 0) 2314a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, ossnap); 2315a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 2316620252bcSChris Kirby } 2317620252bcSChris Kirby } else if (error == ENOENT) { 2318be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 2319620252bcSChris Kirby } 2320fa9e4066Sahrens 2321a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 23223cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 23233b2aab18SMatthew Ahrens if (error != 0) 23243cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 2325fa9e4066Sahrens return (error); 2326fa9e4066Sahrens } 2327fa9e4066Sahrens 232892241e0bSTom Erickson static int 232992241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 2330fa9e4066Sahrens { 233192241e0bSTom Erickson const char *propname = nvpair_name(pair); 233292241e0bSTom Erickson uint64_t *valary; 233392241e0bSTom Erickson unsigned int vallen; 233492241e0bSTom Erickson const char *domain; 2335eeb85002STim Haley char *dash; 233692241e0bSTom Erickson zfs_userquota_prop_t type; 233792241e0bSTom Erickson uint64_t rid; 233892241e0bSTom Erickson uint64_t quota; 233992241e0bSTom Erickson zfsvfs_t *zfsvfs; 234092241e0bSTom Erickson int err; 234192241e0bSTom Erickson 234292241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 234392241e0bSTom Erickson nvlist_t *attrs; 234492241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2345eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2346eeb85002STim Haley &pair) != 0) 2347be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 234892241e0bSTom Erickson } 2349e9dbad6fSeschrock 2350ecd6cf80Smarks /* 2351eeb85002STim Haley * A correctly constructed propname is encoded as 235292241e0bSTom Erickson * userquota@<rid>-<domain>. 2353ecd6cf80Smarks */ 2354eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 2355eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 2356eeb85002STim Haley vallen != 3) 2357be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2358eeb85002STim Haley 2359eeb85002STim Haley domain = dash + 1; 2360eeb85002STim Haley type = valary[0]; 2361eeb85002STim Haley rid = valary[1]; 2362eeb85002STim Haley quota = valary[2]; 2363e9dbad6fSeschrock 23641412a1a2SMark Shellenbaum err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); 236592241e0bSTom Erickson if (err == 0) { 236692241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 236792241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 236892241e0bSTom Erickson } 2369e9dbad6fSeschrock 237092241e0bSTom Erickson return (err); 237192241e0bSTom Erickson } 237214843421SMatthew Ahrens 237392241e0bSTom Erickson /* 237492241e0bSTom Erickson * If the named property is one that has a special function to set its value, 237592241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 237692241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 237792241e0bSTom Erickson * 2378eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 237992241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 238092241e0bSTom Erickson */ 238192241e0bSTom Erickson static int 238292241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 238392241e0bSTom Erickson nvpair_t *pair) 238492241e0bSTom Erickson { 238592241e0bSTom Erickson const char *propname = nvpair_name(pair); 238692241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 238792241e0bSTom Erickson uint64_t intval; 2388b5152584SMatthew Ahrens int err = -1; 2389fa9e4066Sahrens 239092241e0bSTom Erickson if (prop == ZPROP_INVAL) { 239192241e0bSTom Erickson if (zfs_prop_userquota(propname)) 239292241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 239392241e0bSTom Erickson return (-1); 239492241e0bSTom Erickson } 239514843421SMatthew Ahrens 239692241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 239792241e0bSTom Erickson nvlist_t *attrs; 239892241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 239992241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 240092241e0bSTom Erickson &pair) == 0); 240192241e0bSTom Erickson } 2402db870a07Sahrens 240392241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 240492241e0bSTom Erickson return (-1); 2405b24ab676SJeff Bonwick 240692241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 240740feaa91Sahrens 240892241e0bSTom Erickson switch (prop) { 240992241e0bSTom Erickson case ZFS_PROP_QUOTA: 241092241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 241192241e0bSTom Erickson break; 241292241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 24133b2aab18SMatthew Ahrens err = dsl_dataset_set_refquota(dsname, source, intval); 241492241e0bSTom Erickson break; 2415a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 2416a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 2417a2afb611SJerry Jelinek if (intval == UINT64_MAX) { 2418a2afb611SJerry Jelinek /* clearing the limit, just do it */ 2419a2afb611SJerry Jelinek err = 0; 2420a2afb611SJerry Jelinek } else { 2421a2afb611SJerry Jelinek err = dsl_dir_activate_fs_ss_limit(dsname); 2422a2afb611SJerry Jelinek } 2423a2afb611SJerry Jelinek /* 2424a2afb611SJerry Jelinek * Set err to -1 to force the zfs_set_prop_nvlist code down the 2425a2afb611SJerry Jelinek * default path to set the value in the nvlist. 2426a2afb611SJerry Jelinek */ 2427a2afb611SJerry Jelinek if (err == 0) 2428a2afb611SJerry Jelinek err = -1; 2429a2afb611SJerry Jelinek break; 243092241e0bSTom Erickson case ZFS_PROP_RESERVATION: 243192241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 243292241e0bSTom Erickson break; 243392241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 24343b2aab18SMatthew Ahrens err = dsl_dataset_set_refreservation(dsname, source, intval); 243592241e0bSTom Erickson break; 243692241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 2437c61ea566SGeorge Wilson err = zvol_set_volsize(dsname, intval); 243892241e0bSTom Erickson break; 243992241e0bSTom Erickson case ZFS_PROP_VERSION: 244092241e0bSTom Erickson { 244192241e0bSTom Erickson zfsvfs_t *zfsvfs; 24429e6eda55Smarks 24431412a1a2SMark Shellenbaum if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) 2444b24ab676SJeff Bonwick break; 2445b24ab676SJeff Bonwick 244692241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 244792241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 2448d0f3f37eSMark Shellenbaum 244992241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 2450b16da2e2SGeorge Wilson zfs_cmd_t *zc; 2451b16da2e2SGeorge Wilson 2452b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 2453b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 2454b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 2455b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 245640feaa91Sahrens } 245792241e0bSTom Erickson break; 2458ecd6cf80Smarks } 245992241e0bSTom Erickson default: 246092241e0bSTom Erickson err = -1; 246192241e0bSTom Erickson } 2462e9dbad6fSeschrock 246392241e0bSTom Erickson return (err); 246492241e0bSTom Erickson } 2465a9799022Sck 246692241e0bSTom Erickson /* 246792241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 24684445fffbSMatthew Ahrens * it continues to set as many as it can and returns the last error 24694445fffbSMatthew Ahrens * encountered. If the caller provides a non-NULL errlist, it will be filled in 24704445fffbSMatthew Ahrens * with the list of names of all the properties that failed along with the 24714445fffbSMatthew Ahrens * corresponding error numbers. 247292241e0bSTom Erickson * 24734445fffbSMatthew Ahrens * If every property is set successfully, zero is returned and errlist is not 24744445fffbSMatthew Ahrens * modified. 247592241e0bSTom Erickson */ 247692241e0bSTom Erickson int 247792241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 24784445fffbSMatthew Ahrens nvlist_t *errlist) 247992241e0bSTom Erickson { 248092241e0bSTom Erickson nvpair_t *pair; 248192241e0bSTom Erickson nvpair_t *propval; 248202e383d1STom Erickson int rv = 0; 248392241e0bSTom Erickson uint64_t intval; 248492241e0bSTom Erickson char *strval; 24854445fffbSMatthew Ahrens nvlist_t *genericnvl = fnvlist_alloc(); 24864445fffbSMatthew Ahrens nvlist_t *retrynvl = fnvlist_alloc(); 2487a9799022Sck 248892241e0bSTom Erickson retry: 248992241e0bSTom Erickson pair = NULL; 249092241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 249192241e0bSTom Erickson const char *propname = nvpair_name(pair); 249292241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2493cfa69fd2STom Erickson int err = 0; 2494e9dbad6fSeschrock 249592241e0bSTom Erickson /* decode the property value */ 249692241e0bSTom Erickson propval = pair; 249792241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 249892241e0bSTom Erickson nvlist_t *attrs; 24994445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 2500eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2501eeb85002STim Haley &propval) != 0) 2502be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 250314843421SMatthew Ahrens } 2504e9dbad6fSeschrock 250592241e0bSTom Erickson /* Validate value type */ 2506eeb85002STim Haley if (err == 0 && prop == ZPROP_INVAL) { 250792241e0bSTom Erickson if (zfs_prop_user(propname)) { 250892241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 2509be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 251092241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 251192241e0bSTom Erickson if (nvpair_type(propval) != 251292241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 2513be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 251419b94df9SMatthew Ahrens } else { 2515be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 25164201a95eSRic Aleshire } 2517eeb85002STim Haley } else if (err == 0) { 251892241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 251992241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 2520be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 252192241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2522a2eea2e1Sahrens const char *unused; 2523a2eea2e1Sahrens 25244445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 2525e9dbad6fSeschrock 2526e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 252791ebeef5Sahrens case PROP_TYPE_NUMBER: 2528e9dbad6fSeschrock break; 252991ebeef5Sahrens case PROP_TYPE_STRING: 2530be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 253192241e0bSTom Erickson break; 253291ebeef5Sahrens case PROP_TYPE_INDEX: 2533acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 253492241e0bSTom Erickson intval, &unused) != 0) 2535be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2536e9dbad6fSeschrock break; 2537e9dbad6fSeschrock default: 2538e7437265Sahrens cmn_err(CE_PANIC, 2539e7437265Sahrens "unknown property type"); 2540e9dbad6fSeschrock } 2541e9dbad6fSeschrock } else { 2542be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2543e9dbad6fSeschrock } 2544e9dbad6fSeschrock } 254592241e0bSTom Erickson 254692241e0bSTom Erickson /* Validate permissions */ 254792241e0bSTom Erickson if (err == 0) 254892241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 254992241e0bSTom Erickson 255092241e0bSTom Erickson if (err == 0) { 255192241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 255292241e0bSTom Erickson if (err == -1) { 255392241e0bSTom Erickson /* 255492241e0bSTom Erickson * For better performance we build up a list of 255592241e0bSTom Erickson * properties to set in a single transaction. 255692241e0bSTom Erickson */ 255792241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 255892241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 255992241e0bSTom Erickson /* 256092241e0bSTom Erickson * This may be a spurious error caused by 256192241e0bSTom Erickson * receiving quota and reservation out of order. 256292241e0bSTom Erickson * Try again in a second pass. 256392241e0bSTom Erickson */ 256492241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 256592241e0bSTom Erickson } 256692241e0bSTom Erickson } 256792241e0bSTom Erickson 25684445fffbSMatthew Ahrens if (err != 0) { 25694445fffbSMatthew Ahrens if (errlist != NULL) 25704445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, err); 25714445fffbSMatthew Ahrens rv = err; 25724445fffbSMatthew Ahrens } 2573e9dbad6fSeschrock } 2574e9dbad6fSeschrock 257592241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 257692241e0bSTom Erickson nvl = retrynvl; 257792241e0bSTom Erickson goto retry; 257892241e0bSTom Erickson } 257992241e0bSTom Erickson 258092241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 258192241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 258292241e0bSTom Erickson /* 258392241e0bSTom Erickson * If this fails, we still want to set as many properties as we 258492241e0bSTom Erickson * can, so try setting them individually. 258592241e0bSTom Erickson */ 258692241e0bSTom Erickson pair = NULL; 258792241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 258892241e0bSTom Erickson const char *propname = nvpair_name(pair); 2589cfa69fd2STom Erickson int err = 0; 259092241e0bSTom Erickson 259192241e0bSTom Erickson propval = pair; 259292241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 259392241e0bSTom Erickson nvlist_t *attrs; 25944445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 25954445fffbSMatthew Ahrens propval = fnvlist_lookup_nvpair(attrs, 25964445fffbSMatthew Ahrens ZPROP_VALUE); 259792241e0bSTom Erickson } 259892241e0bSTom Erickson 259992241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 26004445fffbSMatthew Ahrens strval = fnvpair_value_string(propval); 26013b2aab18SMatthew Ahrens err = dsl_prop_set_string(dsname, propname, 26023b2aab18SMatthew Ahrens source, strval); 260392241e0bSTom Erickson } else { 26044445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 26053b2aab18SMatthew Ahrens err = dsl_prop_set_int(dsname, propname, source, 26063b2aab18SMatthew Ahrens intval); 260792241e0bSTom Erickson } 260892241e0bSTom Erickson 260992241e0bSTom Erickson if (err != 0) { 26104445fffbSMatthew Ahrens if (errlist != NULL) { 26114445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, 26124445fffbSMatthew Ahrens err); 26134445fffbSMatthew Ahrens } 26144445fffbSMatthew Ahrens rv = err; 261592241e0bSTom Erickson } 261692241e0bSTom Erickson } 26175c0b6a79SRich Morris } 26185c0b6a79SRich Morris nvlist_free(genericnvl); 261992241e0bSTom Erickson nvlist_free(retrynvl); 262092241e0bSTom Erickson 262192241e0bSTom Erickson return (rv); 2622fa9e4066Sahrens } 2623fa9e4066Sahrens 2624ea2f5b9eSMatthew Ahrens /* 2625ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2626ea2f5b9eSMatthew Ahrens */ 2627ea2f5b9eSMatthew Ahrens static int 26284445fffbSMatthew Ahrens zfs_check_userprops(const char *fsname, nvlist_t *nvl) 2629ea2f5b9eSMatthew Ahrens { 263092241e0bSTom Erickson nvpair_t *pair = NULL; 2631ea2f5b9eSMatthew Ahrens int error = 0; 2632ea2f5b9eSMatthew Ahrens 263392241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 263492241e0bSTom Erickson const char *propname = nvpair_name(pair); 2635ea2f5b9eSMatthew Ahrens 2636ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 263792241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2638be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2639ea2f5b9eSMatthew Ahrens 2640ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2641ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2642ea2f5b9eSMatthew Ahrens return (error); 2643ea2f5b9eSMatthew Ahrens 2644ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2645be6fd75aSMatthew Ahrens return (SET_ERROR(ENAMETOOLONG)); 2646ea2f5b9eSMatthew Ahrens 264778f17100SMatthew Ahrens if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN) 2648ea2f5b9eSMatthew Ahrens return (E2BIG); 2649ea2f5b9eSMatthew Ahrens } 2650ea2f5b9eSMatthew Ahrens return (0); 2651ea2f5b9eSMatthew Ahrens } 2652ea2f5b9eSMatthew Ahrens 265392241e0bSTom Erickson static void 265492241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 265592241e0bSTom Erickson { 265692241e0bSTom Erickson nvpair_t *pair; 265792241e0bSTom Erickson 265892241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 265992241e0bSTom Erickson 266092241e0bSTom Erickson pair = NULL; 266192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 266292241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 266392241e0bSTom Erickson continue; 266492241e0bSTom Erickson 266592241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 266692241e0bSTom Erickson } 266792241e0bSTom Erickson } 266892241e0bSTom Erickson 266992241e0bSTom Erickson static int 26703b2aab18SMatthew Ahrens clear_received_props(const char *dsname, nvlist_t *props, 267192241e0bSTom Erickson nvlist_t *skipped) 267292241e0bSTom Erickson { 267392241e0bSTom Erickson int err = 0; 267492241e0bSTom Erickson nvlist_t *cleared_props = NULL; 267592241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 267692241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 267792241e0bSTom Erickson /* 267892241e0bSTom Erickson * Acts on local properties until the dataset has received 267992241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 268092241e0bSTom Erickson */ 268192241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 26823b2aab18SMatthew Ahrens (dsl_prop_get_hasrecvd(dsname) ? ZPROP_SRC_RECEIVED : 0)); 26833b2aab18SMatthew Ahrens err = zfs_set_prop_nvlist(dsname, flags, cleared_props, NULL); 268492241e0bSTom Erickson } 268592241e0bSTom Erickson nvlist_free(cleared_props); 268692241e0bSTom Erickson return (err); 268792241e0bSTom Erickson } 268892241e0bSTom Erickson 26893cb34c60Sahrens /* 26903cb34c60Sahrens * inputs: 26913cb34c60Sahrens * zc_name name of filesystem 26925c0b6a79SRich Morris * zc_value name of property to set 26933cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 269492241e0bSTom Erickson * zc_cookie received properties flag 26953cb34c60Sahrens * 269692241e0bSTom Erickson * outputs: 269792241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 26983cb34c60Sahrens */ 2699fa9e4066Sahrens static int 2700e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2701fa9e4066Sahrens { 2702e9dbad6fSeschrock nvlist_t *nvl; 270392241e0bSTom Erickson boolean_t received = zc->zc_cookie; 270492241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 270592241e0bSTom Erickson ZPROP_SRC_LOCAL); 27064445fffbSMatthew Ahrens nvlist_t *errors; 2707e9dbad6fSeschrock int error; 2708e9dbad6fSeschrock 2709990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2710478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2711e9dbad6fSeschrock return (error); 2712e9dbad6fSeschrock 271392241e0bSTom Erickson if (received) { 2714bb0ade09Sahrens nvlist_t *origprops; 2715bb0ade09Sahrens 27163b2aab18SMatthew Ahrens if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) { 27173b2aab18SMatthew Ahrens (void) clear_received_props(zc->zc_name, 27183b2aab18SMatthew Ahrens origprops, nvl); 27193b2aab18SMatthew Ahrens nvlist_free(origprops); 2720bb0ade09Sahrens } 27213b2aab18SMatthew Ahrens 27223b2aab18SMatthew Ahrens error = dsl_prop_set_hasrecvd(zc->zc_name); 2723bb0ade09Sahrens } 2724bb0ade09Sahrens 27254445fffbSMatthew Ahrens errors = fnvlist_alloc(); 27263b2aab18SMatthew Ahrens if (error == 0) 27273b2aab18SMatthew Ahrens error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors); 272892241e0bSTom Erickson 272992241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 273092241e0bSTom Erickson (void) put_nvlist(zc, errors); 273192241e0bSTom Erickson } 2732ecd6cf80Smarks 273392241e0bSTom Erickson nvlist_free(errors); 2734e9dbad6fSeschrock nvlist_free(nvl); 2735e9dbad6fSeschrock return (error); 2736fa9e4066Sahrens } 2737fa9e4066Sahrens 27383cb34c60Sahrens /* 27393cb34c60Sahrens * inputs: 27403cb34c60Sahrens * zc_name name of filesystem 27413cb34c60Sahrens * zc_value name of property to inherit 274292241e0bSTom Erickson * zc_cookie revert to received value if TRUE 27433cb34c60Sahrens * 27443cb34c60Sahrens * outputs: none 27453cb34c60Sahrens */ 2746e45ce728Sahrens static int 2747e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2748e45ce728Sahrens { 274992241e0bSTom Erickson const char *propname = zc->zc_value; 275092241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 275192241e0bSTom Erickson boolean_t received = zc->zc_cookie; 275292241e0bSTom Erickson zprop_source_t source = (received 275392241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 275492241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 275592241e0bSTom Erickson 275692241e0bSTom Erickson if (received) { 275792241e0bSTom Erickson nvlist_t *dummy; 275892241e0bSTom Erickson nvpair_t *pair; 275992241e0bSTom Erickson zprop_type_t type; 276092241e0bSTom Erickson int err; 276192241e0bSTom Erickson 276292241e0bSTom Erickson /* 276392241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 276492241e0bSTom Erickson * nvpair with type info. 276592241e0bSTom Erickson */ 276692241e0bSTom Erickson if (prop == ZPROP_INVAL) { 276792241e0bSTom Erickson if (!zfs_prop_user(propname)) 2768be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 276992241e0bSTom Erickson 277092241e0bSTom Erickson type = PROP_TYPE_STRING; 2771a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2772a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2773be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 277492241e0bSTom Erickson } else { 277592241e0bSTom Erickson type = zfs_prop_get_type(prop); 277692241e0bSTom Erickson } 277792241e0bSTom Erickson 277892241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 277992241e0bSTom Erickson 278092241e0bSTom Erickson switch (type) { 278192241e0bSTom Erickson case PROP_TYPE_STRING: 278292241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 278392241e0bSTom Erickson break; 278492241e0bSTom Erickson case PROP_TYPE_NUMBER: 278592241e0bSTom Erickson case PROP_TYPE_INDEX: 278692241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 278792241e0bSTom Erickson break; 278892241e0bSTom Erickson default: 278992241e0bSTom Erickson nvlist_free(dummy); 2790be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 279192241e0bSTom Erickson } 279292241e0bSTom Erickson 279392241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 279492241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 279592241e0bSTom Erickson nvlist_free(dummy); 279692241e0bSTom Erickson if (err != -1) 279792241e0bSTom Erickson return (err); /* special property already handled */ 279892241e0bSTom Erickson } else { 279992241e0bSTom Erickson /* 280092241e0bSTom Erickson * Only check this in the non-received case. We want to allow 280192241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 280292241e0bSTom Erickson * and reservation to the received or default values even though 280392241e0bSTom Erickson * they are not considered inheritable. 280492241e0bSTom Erickson */ 280592241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 2806be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 280792241e0bSTom Erickson } 280892241e0bSTom Erickson 28094445fffbSMatthew Ahrens /* property name has been validated by zfs_secpolicy_inherit_prop() */ 28103b2aab18SMatthew Ahrens return (dsl_prop_inherit(zc->zc_name, zc->zc_value, source)); 2811e45ce728Sahrens } 2812e45ce728Sahrens 2813b1b8ab34Slling static int 281411a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2815b1b8ab34Slling { 2816990b4856Slling nvlist_t *props; 2817b1b8ab34Slling spa_t *spa; 2818990b4856Slling int error; 281992241e0bSTom Erickson nvpair_t *pair; 2820b1b8ab34Slling 282192241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 282292241e0bSTom Erickson zc->zc_iflags, &props)) 2823b1b8ab34Slling return (error); 2824b1b8ab34Slling 2825379c004dSEric Schrock /* 2826379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2827379c004dSEric Schrock * to handle the faulted case. 2828379c004dSEric Schrock */ 282992241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 283092241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2831379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 283292241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2833379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2834379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2835379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 2836379c004dSEric Schrock spa_config_sync(spa, B_FALSE, B_TRUE); 2837379c004dSEric Schrock } 2838379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2839b693757aSEric Schrock if (spa != NULL) { 2840b693757aSEric Schrock nvlist_free(props); 2841379c004dSEric Schrock return (0); 2842b693757aSEric Schrock } 2843379c004dSEric Schrock } 2844379c004dSEric Schrock 2845b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2846990b4856Slling nvlist_free(props); 2847b1b8ab34Slling return (error); 2848b1b8ab34Slling } 2849b1b8ab34Slling 2850990b4856Slling error = spa_prop_set(spa, props); 2851b1b8ab34Slling 2852990b4856Slling nvlist_free(props); 2853b1b8ab34Slling spa_close(spa, FTAG); 2854b1b8ab34Slling 2855b1b8ab34Slling return (error); 2856b1b8ab34Slling } 2857b1b8ab34Slling 2858b1b8ab34Slling static int 285911a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2860b1b8ab34Slling { 2861b1b8ab34Slling spa_t *spa; 2862b1b8ab34Slling int error; 2863b1b8ab34Slling nvlist_t *nvp = NULL; 2864b1b8ab34Slling 2865379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2866379c004dSEric Schrock /* 2867379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2868379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2869379c004dSEric Schrock * anyway. 2870379c004dSEric Schrock */ 2871379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2872379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2873379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2874379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2875379c004dSEric Schrock } else { 2876379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2877379c004dSEric Schrock spa_close(spa, FTAG); 2878379c004dSEric Schrock } 2879b1b8ab34Slling 2880b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2881b1b8ab34Slling error = put_nvlist(zc, nvp); 2882b1b8ab34Slling else 2883be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 2884b1b8ab34Slling 2885379c004dSEric Schrock nvlist_free(nvp); 2886b1b8ab34Slling return (error); 2887b1b8ab34Slling } 2888b1b8ab34Slling 28893cb34c60Sahrens /* 28903cb34c60Sahrens * inputs: 28913cb34c60Sahrens * zc_name name of filesystem 28923cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 28933cb34c60Sahrens * zc_perm_action allow/unallow flag 28943cb34c60Sahrens * 28953cb34c60Sahrens * outputs: none 28963cb34c60Sahrens */ 2897ecd6cf80Smarks static int 2898ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2899ecd6cf80Smarks { 2900ecd6cf80Smarks int error; 2901ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2902ecd6cf80Smarks 2903990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2904478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2905ecd6cf80Smarks return (error); 2906ecd6cf80Smarks 2907ecd6cf80Smarks /* 2908ecd6cf80Smarks * Verify nvlist is constructed correctly 2909ecd6cf80Smarks */ 2910ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2911ecd6cf80Smarks nvlist_free(fsaclnv); 2912be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2913ecd6cf80Smarks } 2914ecd6cf80Smarks 2915ecd6cf80Smarks /* 2916ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2917ecd6cf80Smarks * that user is allowed to hand out each permission in 2918ecd6cf80Smarks * the nvlist(s) 2919ecd6cf80Smarks */ 2920ecd6cf80Smarks 292191ebeef5Sahrens error = secpolicy_zfs(CRED()); 29223b2aab18SMatthew Ahrens if (error != 0) { 292391ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 292491ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 292591ebeef5Sahrens fsaclnv, CRED()); 292691ebeef5Sahrens } else { 292791ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 292891ebeef5Sahrens fsaclnv, CRED()); 292991ebeef5Sahrens } 2930ecd6cf80Smarks } 2931ecd6cf80Smarks 2932ecd6cf80Smarks if (error == 0) 2933ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2934ecd6cf80Smarks 2935ecd6cf80Smarks nvlist_free(fsaclnv); 2936ecd6cf80Smarks return (error); 2937ecd6cf80Smarks } 2938ecd6cf80Smarks 29393cb34c60Sahrens /* 29403cb34c60Sahrens * inputs: 29413cb34c60Sahrens * zc_name name of filesystem 29423cb34c60Sahrens * 29433cb34c60Sahrens * outputs: 29443cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 29453cb34c60Sahrens */ 2946ecd6cf80Smarks static int 2947ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2948ecd6cf80Smarks { 2949ecd6cf80Smarks nvlist_t *nvp; 2950ecd6cf80Smarks int error; 2951ecd6cf80Smarks 2952ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2953ecd6cf80Smarks error = put_nvlist(zc, nvp); 2954ecd6cf80Smarks nvlist_free(nvp); 2955ecd6cf80Smarks } 2956ecd6cf80Smarks 2957ecd6cf80Smarks return (error); 2958ecd6cf80Smarks } 2959ecd6cf80Smarks 2960fa9e4066Sahrens /* 2961fa9e4066Sahrens * Search the vfs list for a specified resource. Returns a pointer to it 2962fa9e4066Sahrens * or NULL if no suitable entry is found. The caller of this routine 2963fa9e4066Sahrens * is responsible for releasing the returned vfs pointer. 2964fa9e4066Sahrens */ 2965fa9e4066Sahrens static vfs_t * 2966fa9e4066Sahrens zfs_get_vfs(const char *resource) 2967fa9e4066Sahrens { 2968fa9e4066Sahrens struct vfs *vfsp; 2969fa9e4066Sahrens struct vfs *vfs_found = NULL; 2970fa9e4066Sahrens 2971fa9e4066Sahrens vfs_list_read_lock(); 2972fa9e4066Sahrens vfsp = rootvfs; 2973fa9e4066Sahrens do { 2974fa9e4066Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) { 2975fa9e4066Sahrens VFS_HOLD(vfsp); 2976fa9e4066Sahrens vfs_found = vfsp; 2977fa9e4066Sahrens break; 2978fa9e4066Sahrens } 2979fa9e4066Sahrens vfsp = vfsp->vfs_next; 2980fa9e4066Sahrens } while (vfsp != rootvfs); 2981fa9e4066Sahrens vfs_list_unlock(); 2982fa9e4066Sahrens return (vfs_found); 2983fa9e4066Sahrens } 2984fa9e4066Sahrens 2985ecd6cf80Smarks /* ARGSUSED */ 2986fa9e4066Sahrens static void 2987ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 2988fa9e4066Sahrens { 2989da6c28aaSamw zfs_creat_t *zct = arg; 2990da6c28aaSamw 2991de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 2992da6c28aaSamw } 2993da6c28aaSamw 2994de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 2995da6c28aaSamw 2996da6c28aaSamw /* 2997de8267e0Stimh * inputs: 29980a48a24eStimh * os parent objset pointer (NULL if root fs) 2999f7170741SWill Andrews * fuids_ok fuids allowed in this version of the spa? 3000f7170741SWill Andrews * sa_ok SAs allowed in this version of the spa? 3001f7170741SWill Andrews * createprops list of properties requested by creator 3002de8267e0Stimh * 3003de8267e0Stimh * outputs: 3004de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 30050a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 3006da6c28aaSamw * 3007de8267e0Stimh * Determine the settings for utf8only, normalization and 3008de8267e0Stimh * casesensitivity. Specific values may have been requested by the 3009de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 3010de8267e0Stimh * the file system is of too early a vintage, a creator can not 3011de8267e0Stimh * request settings for these properties, even if the requested 3012de8267e0Stimh * setting is the default value. We don't actually want to create dsl 3013de8267e0Stimh * properties for these, so remove them from the source nvlist after 3014de8267e0Stimh * processing. 3015da6c28aaSamw */ 3016da6c28aaSamw static int 301714843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 30180a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 30190a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 3020da6c28aaSamw { 3021de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 3022de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 3023de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 3024da6c28aaSamw 3025de8267e0Stimh ASSERT(zplprops != NULL); 3026da6c28aaSamw 3027de8267e0Stimh /* 3028de8267e0Stimh * Pull out creator prop choices, if any. 3029de8267e0Stimh */ 3030de8267e0Stimh if (createprops) { 30310a48a24eStimh (void) nvlist_lookup_uint64(createprops, 30320a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 3033de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3034de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 3035de8267e0Stimh (void) nvlist_remove_all(createprops, 3036de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 3037de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3038de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 3039de8267e0Stimh (void) nvlist_remove_all(createprops, 3040de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 3041de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3042de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 3043de8267e0Stimh (void) nvlist_remove_all(createprops, 3044de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 3045de8267e0Stimh } 3046da6c28aaSamw 3047c2a93d44Stimh /* 30480a48a24eStimh * If the zpl version requested is whacky or the file system 30490a48a24eStimh * or pool is version is too "young" to support normalization 30500a48a24eStimh * and the creator tried to set a value for one of the props, 30510a48a24eStimh * error out. 3052c2a93d44Stimh */ 30530a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 30540a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 30550a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 30560a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 3057de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 30580a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 3059be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3060c2a93d44Stimh 3061de8267e0Stimh /* 3062de8267e0Stimh * Put the version in the zplprops 3063de8267e0Stimh */ 3064de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3065de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 3066da6c28aaSamw 3067de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 3068de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 3069de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3070de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 3071da6c28aaSamw 3072c2a93d44Stimh /* 3073de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 3074c2a93d44Stimh */ 3075de8267e0Stimh if (norm) 3076de8267e0Stimh u8 = 1; 3077de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 3078de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 3079de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3080de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 3081de8267e0Stimh 3082de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 3083de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 3084de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3085de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 3086c2a93d44Stimh 3087ab04eb8eStimh if (is_ci) 3088ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 3089ab04eb8eStimh 3090da6c28aaSamw return (0); 3091fa9e4066Sahrens } 3092fa9e4066Sahrens 30930a48a24eStimh static int 30940a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 30950a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 30960a48a24eStimh { 30970a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 30980a48a24eStimh uint64_t zplver = ZPL_VERSION; 30990a48a24eStimh objset_t *os = NULL; 31000a48a24eStimh char parentname[MAXNAMELEN]; 31010a48a24eStimh char *cp; 31020a586ceaSMark Shellenbaum spa_t *spa; 31030a586ceaSMark Shellenbaum uint64_t spa_vers; 31040a48a24eStimh int error; 31050a48a24eStimh 31060a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 31070a48a24eStimh cp = strrchr(parentname, '/'); 31080a48a24eStimh ASSERT(cp != NULL); 31090a48a24eStimh cp[0] = '\0'; 31100a48a24eStimh 31110a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 31120a586ceaSMark Shellenbaum return (error); 31130a586ceaSMark Shellenbaum 31140a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 31150a586ceaSMark Shellenbaum spa_close(spa, FTAG); 31160a586ceaSMark Shellenbaum 31170a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 31180a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 31190a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 31200a48a24eStimh 31210a48a24eStimh /* 31220a48a24eStimh * Open parent object set so we can inherit zplprop values. 31230a48a24eStimh */ 3124503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 31250a48a24eStimh return (error); 31260a48a24eStimh 31270a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 31280a48a24eStimh zplprops, is_ci); 3129503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 31300a48a24eStimh return (error); 31310a48a24eStimh } 31320a48a24eStimh 31330a48a24eStimh static int 31340a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 31350a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 31360a48a24eStimh { 31370a586ceaSMark Shellenbaum boolean_t fuids_ok; 31380a586ceaSMark Shellenbaum boolean_t sa_ok; 31390a48a24eStimh uint64_t zplver = ZPL_VERSION; 31400a48a24eStimh int error; 31410a48a24eStimh 31420a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 31430a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 31440a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 31450a48a24eStimh 31460a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 31470a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 31480a48a24eStimh return (error); 31490a48a24eStimh } 31500a48a24eStimh 31513cb34c60Sahrens /* 31524445fffbSMatthew Ahrens * innvl: { 31534445fffbSMatthew Ahrens * "type" -> dmu_objset_type_t (int32) 31544445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 31554445fffbSMatthew Ahrens * } 31563cb34c60Sahrens * 31574445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 31583cb34c60Sahrens */ 3159fa9e4066Sahrens static int 31604445fffbSMatthew Ahrens zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3161fa9e4066Sahrens { 3162fa9e4066Sahrens int error = 0; 31634445fffbSMatthew Ahrens zfs_creat_t zct = { 0 }; 3164ecd6cf80Smarks nvlist_t *nvprops = NULL; 3165ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 31664445fffbSMatthew Ahrens int32_t type32; 31674445fffbSMatthew Ahrens dmu_objset_type_t type; 31684445fffbSMatthew Ahrens boolean_t is_insensitive = B_FALSE; 3169fa9e4066Sahrens 31704445fffbSMatthew Ahrens if (nvlist_lookup_int32(innvl, "type", &type32) != 0) 3171be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 31724445fffbSMatthew Ahrens type = type32; 31734445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3174fa9e4066Sahrens 31754445fffbSMatthew Ahrens switch (type) { 3176fa9e4066Sahrens case DMU_OST_ZFS: 3177fa9e4066Sahrens cbfunc = zfs_create_cb; 3178fa9e4066Sahrens break; 3179fa9e4066Sahrens 3180fa9e4066Sahrens case DMU_OST_ZVOL: 3181fa9e4066Sahrens cbfunc = zvol_create_cb; 3182fa9e4066Sahrens break; 3183fa9e4066Sahrens 3184fa9e4066Sahrens default: 31851d452cf5Sahrens cbfunc = NULL; 3186e7cbe64fSgw break; 3187fa9e4066Sahrens } 31884445fffbSMatthew Ahrens if (strchr(fsname, '@') || 31894445fffbSMatthew Ahrens strchr(fsname, '%')) 3190be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3191fa9e4066Sahrens 3192da6c28aaSamw zct.zct_props = nvprops; 3193da6c28aaSamw 31944445fffbSMatthew Ahrens if (cbfunc == NULL) 3195be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 31964445fffbSMatthew Ahrens 31974445fffbSMatthew Ahrens if (type == DMU_OST_ZVOL) { 31984445fffbSMatthew Ahrens uint64_t volsize, volblocksize; 31994445fffbSMatthew Ahrens 32004445fffbSMatthew Ahrens if (nvprops == NULL) 3201be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32024445fffbSMatthew Ahrens if (nvlist_lookup_uint64(nvprops, 32034445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) != 0) 3204be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3205fa9e4066Sahrens 32064445fffbSMatthew Ahrens if ((error = nvlist_lookup_uint64(nvprops, 32074445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 32084445fffbSMatthew Ahrens &volblocksize)) != 0 && error != ENOENT) 3209be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32104445fffbSMatthew Ahrens 32114445fffbSMatthew Ahrens if (error != 0) 32124445fffbSMatthew Ahrens volblocksize = zfs_prop_default_numeric( 32134445fffbSMatthew Ahrens ZFS_PROP_VOLBLOCKSIZE); 32144445fffbSMatthew Ahrens 32154445fffbSMatthew Ahrens if ((error = zvol_check_volblocksize( 32164445fffbSMatthew Ahrens volblocksize)) != 0 || 32174445fffbSMatthew Ahrens (error = zvol_check_volsize(volsize, 32184445fffbSMatthew Ahrens volblocksize)) != 0) 3219fa9e4066Sahrens return (error); 32204445fffbSMatthew Ahrens } else if (type == DMU_OST_ZFS) { 32214445fffbSMatthew Ahrens int error; 3222ab04eb8eStimh 32234445fffbSMatthew Ahrens /* 32244445fffbSMatthew Ahrens * We have to have normalization and 32254445fffbSMatthew Ahrens * case-folding flags correct when we do the 32264445fffbSMatthew Ahrens * file system creation, so go figure them out 32274445fffbSMatthew Ahrens * now. 32284445fffbSMatthew Ahrens */ 32294445fffbSMatthew Ahrens VERIFY(nvlist_alloc(&zct.zct_zplprops, 32304445fffbSMatthew Ahrens NV_UNIQUE_NAME, KM_SLEEP) == 0); 32314445fffbSMatthew Ahrens error = zfs_fill_zplprops(fsname, nvprops, 32324445fffbSMatthew Ahrens zct.zct_zplprops, &is_insensitive); 32334445fffbSMatthew Ahrens if (error != 0) { 32344445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 3235da6c28aaSamw return (error); 3236da6c28aaSamw } 32374445fffbSMatthew Ahrens } 3238ab04eb8eStimh 32394445fffbSMatthew Ahrens error = dmu_objset_create(fsname, type, 32404445fffbSMatthew Ahrens is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 32414445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 32425c5460e9Seschrock 32434445fffbSMatthew Ahrens /* 32444445fffbSMatthew Ahrens * It would be nice to do this atomically. 32454445fffbSMatthew Ahrens */ 32464445fffbSMatthew Ahrens if (error == 0) { 32474445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 32484445fffbSMatthew Ahrens nvprops, outnvl); 32494445fffbSMatthew Ahrens if (error != 0) 32503b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 32514445fffbSMatthew Ahrens } 32524445fffbSMatthew Ahrens return (error); 32534445fffbSMatthew Ahrens } 3254e9dbad6fSeschrock 32554445fffbSMatthew Ahrens /* 32564445fffbSMatthew Ahrens * innvl: { 32574445fffbSMatthew Ahrens * "origin" -> name of origin snapshot 32584445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 32594445fffbSMatthew Ahrens * } 32604445fffbSMatthew Ahrens * 32614445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 32624445fffbSMatthew Ahrens */ 32634445fffbSMatthew Ahrens static int 32644445fffbSMatthew Ahrens zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 32654445fffbSMatthew Ahrens { 32664445fffbSMatthew Ahrens int error = 0; 32674445fffbSMatthew Ahrens nvlist_t *nvprops = NULL; 32684445fffbSMatthew Ahrens char *origin_name; 3269e9dbad6fSeschrock 32704445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) 3271be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32724445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3273e9dbad6fSeschrock 32744445fffbSMatthew Ahrens if (strchr(fsname, '@') || 32754445fffbSMatthew Ahrens strchr(fsname, '%')) 3276be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3277e9dbad6fSeschrock 32784445fffbSMatthew Ahrens if (dataset_namecheck(origin_name, NULL, NULL) != 0) 3279be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32803b2aab18SMatthew Ahrens error = dmu_objset_clone(fsname, origin_name); 32813b2aab18SMatthew Ahrens if (error != 0) 32824445fffbSMatthew Ahrens return (error); 3283e9dbad6fSeschrock 3284e9dbad6fSeschrock /* 3285e9dbad6fSeschrock * It would be nice to do this atomically. 3286e9dbad6fSeschrock */ 3287e9dbad6fSeschrock if (error == 0) { 32884445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 32894445fffbSMatthew Ahrens nvprops, outnvl); 329092241e0bSTom Erickson if (error != 0) 32913b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 3292e9dbad6fSeschrock } 3293fa9e4066Sahrens return (error); 3294fa9e4066Sahrens } 3295fa9e4066Sahrens 32963cb34c60Sahrens /* 32974445fffbSMatthew Ahrens * innvl: { 32984445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 32994445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value (string) } 33004445fffbSMatthew Ahrens * } 33014445fffbSMatthew Ahrens * 33024445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 33033cb34c60Sahrens */ 3304fa9e4066Sahrens static int 33054445fffbSMatthew Ahrens zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3306fa9e4066Sahrens { 33074445fffbSMatthew Ahrens nvlist_t *snaps; 33084445fffbSMatthew Ahrens nvlist_t *props = NULL; 33094445fffbSMatthew Ahrens int error, poollen; 33104445fffbSMatthew Ahrens nvpair_t *pair; 3311bb0ade09Sahrens 33124445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &props); 33134445fffbSMatthew Ahrens if ((error = zfs_check_userprops(poolname, props)) != 0) 33144445fffbSMatthew Ahrens return (error); 33154445fffbSMatthew Ahrens 33164445fffbSMatthew Ahrens if (!nvlist_empty(props) && 33174445fffbSMatthew Ahrens zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) 3318be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 33194445fffbSMatthew Ahrens 33204445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3321be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 33224445fffbSMatthew Ahrens poollen = strlen(poolname); 33234445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 33244445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 33254445fffbSMatthew Ahrens const char *name = nvpair_name(pair); 33264445fffbSMatthew Ahrens const char *cp = strchr(name, '@'); 3327bb0ade09Sahrens 33284445fffbSMatthew Ahrens /* 33294445fffbSMatthew Ahrens * The snap name must contain an @, and the part after it must 33304445fffbSMatthew Ahrens * contain only valid characters. 33314445fffbSMatthew Ahrens */ 333278f17100SMatthew Ahrens if (cp == NULL || 333378f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 3334be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3335bb0ade09Sahrens 33364445fffbSMatthew Ahrens /* 33374445fffbSMatthew Ahrens * The snap must be in the specified pool. 33384445fffbSMatthew Ahrens */ 33394445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 33404445fffbSMatthew Ahrens (name[poollen] != '/' && name[poollen] != '@')) 3341be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 33424445fffbSMatthew Ahrens 33434445fffbSMatthew Ahrens /* This must be the only snap of this fs. */ 33444445fffbSMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(snaps, pair); 33454445fffbSMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(snaps, pair2)) { 33464445fffbSMatthew Ahrens if (strncmp(name, nvpair_name(pair2), cp - name + 1) 33474445fffbSMatthew Ahrens == 0) { 3348be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 33494445fffbSMatthew Ahrens } 33504445fffbSMatthew Ahrens } 33514445fffbSMatthew Ahrens } 3352bb0ade09Sahrens 33533b2aab18SMatthew Ahrens error = dsl_dataset_snapshot(snaps, props, outnvl); 33544445fffbSMatthew Ahrens return (error); 33554445fffbSMatthew Ahrens } 33564445fffbSMatthew Ahrens 33574445fffbSMatthew Ahrens /* 33584445fffbSMatthew Ahrens * innvl: "message" -> string 33594445fffbSMatthew Ahrens */ 33604445fffbSMatthew Ahrens /* ARGSUSED */ 33614445fffbSMatthew Ahrens static int 33624445fffbSMatthew Ahrens zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 33634445fffbSMatthew Ahrens { 33644445fffbSMatthew Ahrens char *message; 33654445fffbSMatthew Ahrens spa_t *spa; 33664445fffbSMatthew Ahrens int error; 33674445fffbSMatthew Ahrens char *poolname; 33684445fffbSMatthew Ahrens 33694445fffbSMatthew Ahrens /* 33704445fffbSMatthew Ahrens * The poolname in the ioctl is not set, we get it from the TSD, 33714445fffbSMatthew Ahrens * which was set at the end of the last successful ioctl that allows 33724445fffbSMatthew Ahrens * logging. The secpolicy func already checked that it is set. 33734445fffbSMatthew Ahrens * Only one log ioctl is allowed after each successful ioctl, so 33744445fffbSMatthew Ahrens * we clear the TSD here. 33754445fffbSMatthew Ahrens */ 33764445fffbSMatthew Ahrens poolname = tsd_get(zfs_allow_log_key); 33774445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, NULL); 33784445fffbSMatthew Ahrens error = spa_open(poolname, &spa, FTAG); 33794445fffbSMatthew Ahrens strfree(poolname); 33804445fffbSMatthew Ahrens if (error != 0) 33814445fffbSMatthew Ahrens return (error); 33824445fffbSMatthew Ahrens 33834445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "message", &message) != 0) { 33844445fffbSMatthew Ahrens spa_close(spa, FTAG); 3385be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3386bb0ade09Sahrens } 3387ea2f5b9eSMatthew Ahrens 33884445fffbSMatthew Ahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 33894445fffbSMatthew Ahrens spa_close(spa, FTAG); 3390be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 33914445fffbSMatthew Ahrens } 3392ea2f5b9eSMatthew Ahrens 33934445fffbSMatthew Ahrens error = spa_history_log(spa, message); 33944445fffbSMatthew Ahrens spa_close(spa, FTAG); 3395bb0ade09Sahrens return (error); 33961d452cf5Sahrens } 3397fa9e4066Sahrens 33983b2aab18SMatthew Ahrens /* 33993b2aab18SMatthew Ahrens * The dp_config_rwlock must not be held when calling this, because the 34003b2aab18SMatthew Ahrens * unmount may need to write out data. 34013b2aab18SMatthew Ahrens * 34023b2aab18SMatthew Ahrens * This function is best-effort. Callers must deal gracefully if it 34033b2aab18SMatthew Ahrens * remains mounted (or is remounted after this call). 3404fc7a6e3fSWill Andrews * 3405fc7a6e3fSWill Andrews * Returns 0 if the argument is not a snapshot, or it is not currently a 3406fc7a6e3fSWill Andrews * filesystem, or we were able to unmount it. Returns error code otherwise. 34073b2aab18SMatthew Ahrens */ 3408fc7a6e3fSWill Andrews int 34093b2aab18SMatthew Ahrens zfs_unmount_snap(const char *snapname) 34101d452cf5Sahrens { 34114445fffbSMatthew Ahrens vfs_t *vfsp; 34123b2aab18SMatthew Ahrens zfsvfs_t *zfsvfs; 3413fc7a6e3fSWill Andrews int err; 34141d452cf5Sahrens 34153b2aab18SMatthew Ahrens if (strchr(snapname, '@') == NULL) 3416fc7a6e3fSWill Andrews return (0); 34171d452cf5Sahrens 34183b2aab18SMatthew Ahrens vfsp = zfs_get_vfs(snapname); 34194445fffbSMatthew Ahrens if (vfsp == NULL) 3420fc7a6e3fSWill Andrews return (0); 34213b2aab18SMatthew Ahrens 34223b2aab18SMatthew Ahrens zfsvfs = vfsp->vfs_data; 34233b2aab18SMatthew Ahrens ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os))); 34241d452cf5Sahrens 3425fc7a6e3fSWill Andrews err = vn_vfswlock(vfsp->vfs_vnodecovered); 34264445fffbSMatthew Ahrens VFS_RELE(vfsp); 3427fc7a6e3fSWill Andrews if (err != 0) 3428fc7a6e3fSWill Andrews return (SET_ERROR(err)); 34294445fffbSMatthew Ahrens 34304445fffbSMatthew Ahrens /* 34314445fffbSMatthew Ahrens * Always force the unmount for snapshots. 34324445fffbSMatthew Ahrens */ 34333b2aab18SMatthew Ahrens (void) dounmount(vfsp, MS_FORCE, kcred); 3434fc7a6e3fSWill Andrews return (0); 34353b2aab18SMatthew Ahrens } 34363b2aab18SMatthew Ahrens 34373b2aab18SMatthew Ahrens /* ARGSUSED */ 34383b2aab18SMatthew Ahrens static int 34393b2aab18SMatthew Ahrens zfs_unmount_snap_cb(const char *snapname, void *arg) 34403b2aab18SMatthew Ahrens { 3441fc7a6e3fSWill Andrews return (zfs_unmount_snap(snapname)); 34423b2aab18SMatthew Ahrens } 34433b2aab18SMatthew Ahrens 34443b2aab18SMatthew Ahrens /* 34453b2aab18SMatthew Ahrens * When a clone is destroyed, its origin may also need to be destroyed, 34463b2aab18SMatthew Ahrens * in which case it must be unmounted. This routine will do that unmount 34473b2aab18SMatthew Ahrens * if necessary. 34483b2aab18SMatthew Ahrens */ 34493b2aab18SMatthew Ahrens void 34503b2aab18SMatthew Ahrens zfs_destroy_unmount_origin(const char *fsname) 34513b2aab18SMatthew Ahrens { 34523b2aab18SMatthew Ahrens int error; 34533b2aab18SMatthew Ahrens objset_t *os; 34543b2aab18SMatthew Ahrens dsl_dataset_t *ds; 34553b2aab18SMatthew Ahrens 34563b2aab18SMatthew Ahrens error = dmu_objset_hold(fsname, FTAG, &os); 34573b2aab18SMatthew Ahrens if (error != 0) 34583b2aab18SMatthew Ahrens return; 34593b2aab18SMatthew Ahrens ds = dmu_objset_ds(os); 34603b2aab18SMatthew Ahrens if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev)) { 34613b2aab18SMatthew Ahrens char originname[MAXNAMELEN]; 34623b2aab18SMatthew Ahrens dsl_dataset_name(ds->ds_prev, originname); 34633b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 3464fc7a6e3fSWill Andrews (void) zfs_unmount_snap(originname); 34653b2aab18SMatthew Ahrens } else { 34663b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 34673b2aab18SMatthew Ahrens } 34681d452cf5Sahrens } 34691d452cf5Sahrens 34703cb34c60Sahrens /* 34714445fffbSMatthew Ahrens * innvl: { 34724445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 34734445fffbSMatthew Ahrens * (optional boolean) "defer" 34744445fffbSMatthew Ahrens * } 34754445fffbSMatthew Ahrens * 34764445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 34773cb34c60Sahrens * 34783cb34c60Sahrens */ 347978f17100SMatthew Ahrens /* ARGSUSED */ 34801d452cf5Sahrens static int 34814445fffbSMatthew Ahrens zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 34821d452cf5Sahrens { 34834445fffbSMatthew Ahrens nvlist_t *snaps; 348419b94df9SMatthew Ahrens nvpair_t *pair; 34854445fffbSMatthew Ahrens boolean_t defer; 34861d452cf5Sahrens 34874445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3488be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 34894445fffbSMatthew Ahrens defer = nvlist_exists(innvl, "defer"); 349019b94df9SMatthew Ahrens 34914445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 34924445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 349378f17100SMatthew Ahrens (void) zfs_unmount_snap(nvpair_name(pair)); 349478f17100SMatthew Ahrens } 349578f17100SMatthew Ahrens 349678f17100SMatthew Ahrens return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); 349778f17100SMatthew Ahrens } 349878f17100SMatthew Ahrens 349978f17100SMatthew Ahrens /* 350078f17100SMatthew Ahrens * Create bookmarks. Bookmark names are of the form <fs>#<bmark>. 350178f17100SMatthew Ahrens * All bookmarks must be in the same pool. 350278f17100SMatthew Ahrens * 350378f17100SMatthew Ahrens * innvl: { 350478f17100SMatthew Ahrens * bookmark1 -> snapshot1, bookmark2 -> snapshot2 350578f17100SMatthew Ahrens * } 350678f17100SMatthew Ahrens * 350778f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 350878f17100SMatthew Ahrens * 350978f17100SMatthew Ahrens */ 351078f17100SMatthew Ahrens /* ARGSUSED */ 351178f17100SMatthew Ahrens static int 351278f17100SMatthew Ahrens zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 351378f17100SMatthew Ahrens { 351478f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 351578f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 351678f17100SMatthew Ahrens char *snap_name; 351778f17100SMatthew Ahrens 351878f17100SMatthew Ahrens /* 351978f17100SMatthew Ahrens * Verify the snapshot argument. 352078f17100SMatthew Ahrens */ 352178f17100SMatthew Ahrens if (nvpair_value_string(pair, &snap_name) != 0) 352278f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 352378f17100SMatthew Ahrens 352478f17100SMatthew Ahrens 352578f17100SMatthew Ahrens /* Verify that the keys (bookmarks) are unique */ 352678f17100SMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(innvl, pair); 352778f17100SMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(innvl, pair2)) { 352878f17100SMatthew Ahrens if (strcmp(nvpair_name(pair), nvpair_name(pair2)) == 0) 352978f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 353078f17100SMatthew Ahrens } 353178f17100SMatthew Ahrens } 353278f17100SMatthew Ahrens 353378f17100SMatthew Ahrens return (dsl_bookmark_create(innvl, outnvl)); 353478f17100SMatthew Ahrens } 353578f17100SMatthew Ahrens 353678f17100SMatthew Ahrens /* 353778f17100SMatthew Ahrens * innvl: { 353878f17100SMatthew Ahrens * property 1, property 2, ... 353978f17100SMatthew Ahrens * } 354078f17100SMatthew Ahrens * 354178f17100SMatthew Ahrens * outnvl: { 354278f17100SMatthew Ahrens * bookmark name 1 -> { property 1, property 2, ... }, 354378f17100SMatthew Ahrens * bookmark name 2 -> { property 1, property 2, ... } 354478f17100SMatthew Ahrens * } 354578f17100SMatthew Ahrens * 354678f17100SMatthew Ahrens */ 354778f17100SMatthew Ahrens static int 354878f17100SMatthew Ahrens zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 354978f17100SMatthew Ahrens { 355078f17100SMatthew Ahrens return (dsl_get_bookmarks(fsname, innvl, outnvl)); 355178f17100SMatthew Ahrens } 355278f17100SMatthew Ahrens 355378f17100SMatthew Ahrens /* 355478f17100SMatthew Ahrens * innvl: { 355578f17100SMatthew Ahrens * bookmark name 1, bookmark name 2 355678f17100SMatthew Ahrens * } 355778f17100SMatthew Ahrens * 355878f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 355978f17100SMatthew Ahrens * 356078f17100SMatthew Ahrens */ 356178f17100SMatthew Ahrens static int 356278f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, 356378f17100SMatthew Ahrens nvlist_t *outnvl) 356478f17100SMatthew Ahrens { 356578f17100SMatthew Ahrens int error, poollen; 356678f17100SMatthew Ahrens 356778f17100SMatthew Ahrens poollen = strlen(poolname); 356878f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 356978f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 357019b94df9SMatthew Ahrens const char *name = nvpair_name(pair); 357178f17100SMatthew Ahrens const char *cp = strchr(name, '#'); 35724445fffbSMatthew Ahrens 357319b94df9SMatthew Ahrens /* 357478f17100SMatthew Ahrens * The bookmark name must contain an #, and the part after it 357578f17100SMatthew Ahrens * must contain only valid characters. 357678f17100SMatthew Ahrens */ 357778f17100SMatthew Ahrens if (cp == NULL || 357878f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 357978f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 358078f17100SMatthew Ahrens 358178f17100SMatthew Ahrens /* 358278f17100SMatthew Ahrens * The bookmark must be in the specified pool. 358319b94df9SMatthew Ahrens */ 35844445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 358578f17100SMatthew Ahrens (name[poollen] != '/' && name[poollen] != '#')) 3586be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 358719b94df9SMatthew Ahrens } 358819b94df9SMatthew Ahrens 358978f17100SMatthew Ahrens error = dsl_bookmark_destroy(innvl, outnvl); 359078f17100SMatthew Ahrens return (error); 35911d452cf5Sahrens } 35921d452cf5Sahrens 35933cb34c60Sahrens /* 35943cb34c60Sahrens * inputs: 35953cb34c60Sahrens * zc_name name of dataset to destroy 35963cb34c60Sahrens * zc_objset_type type of objset 3597842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 35983cb34c60Sahrens * 35993cb34c60Sahrens * outputs: none 36003cb34c60Sahrens */ 36011d452cf5Sahrens static int 36021d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 36031d452cf5Sahrens { 3604681d9761SEric Taylor int err; 3605fc7a6e3fSWill Andrews 3606fc7a6e3fSWill Andrews if (zc->zc_objset_type == DMU_OST_ZFS) { 3607fc7a6e3fSWill Andrews err = zfs_unmount_snap(zc->zc_name); 3608fc7a6e3fSWill Andrews if (err != 0) 3609fc7a6e3fSWill Andrews return (err); 3610fc7a6e3fSWill Andrews } 3611fa9e4066Sahrens 36123b2aab18SMatthew Ahrens if (strchr(zc->zc_name, '@')) 36133b2aab18SMatthew Ahrens err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); 36143b2aab18SMatthew Ahrens else 36153b2aab18SMatthew Ahrens err = dsl_destroy_head(zc->zc_name); 3616681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 36175c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 3618681d9761SEric Taylor return (err); 3619fa9e4066Sahrens } 3620fa9e4066Sahrens 36213cb34c60Sahrens /* 3622a7027df1SMatthew Ahrens * fsname is name of dataset to rollback (to most recent snapshot) 36233cb34c60Sahrens * 3624a7027df1SMatthew Ahrens * innvl is not used. 3625a7027df1SMatthew Ahrens * 3626a7027df1SMatthew Ahrens * outnvl: "target" -> name of most recent snapshot 3627a7027df1SMatthew Ahrens * } 36283cb34c60Sahrens */ 3629a7027df1SMatthew Ahrens /* ARGSUSED */ 3630fa9e4066Sahrens static int 3631a7027df1SMatthew Ahrens zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl) 3632fa9e4066Sahrens { 3633ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 36343b2aab18SMatthew Ahrens int error; 3635ae46e4c7SMatthew Ahrens 3636a7027df1SMatthew Ahrens if (getzfsvfs(fsname, &zfsvfs) == 0) { 3637503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 363847f263f4Sek if (error == 0) { 363947f263f4Sek int resume_err; 36404ccbb6e7Sahrens 3641a7027df1SMatthew Ahrens error = dsl_dataset_rollback(fsname, zfsvfs, outnvl); 3642a7027df1SMatthew Ahrens resume_err = zfs_resume_fs(zfsvfs, fsname); 364347f263f4Sek error = error ? error : resume_err; 364447f263f4Sek } 36454ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 36464ccbb6e7Sahrens } else { 3647a7027df1SMatthew Ahrens error = dsl_dataset_rollback(fsname, NULL, outnvl); 36484ccbb6e7Sahrens } 36493b2aab18SMatthew Ahrens return (error); 36503b2aab18SMatthew Ahrens } 36514ccbb6e7Sahrens 36523b2aab18SMatthew Ahrens static int 36533b2aab18SMatthew Ahrens recursive_unmount(const char *fsname, void *arg) 36543b2aab18SMatthew Ahrens { 36553b2aab18SMatthew Ahrens const char *snapname = arg; 36563b2aab18SMatthew Ahrens char fullname[MAXNAMELEN]; 3657ae46e4c7SMatthew Ahrens 36583b2aab18SMatthew Ahrens (void) snprintf(fullname, sizeof (fullname), "%s@%s", fsname, snapname); 3659fc7a6e3fSWill Andrews return (zfs_unmount_snap(fullname)); 3660fa9e4066Sahrens } 3661fa9e4066Sahrens 36623cb34c60Sahrens /* 36633cb34c60Sahrens * inputs: 36643cb34c60Sahrens * zc_name old name of dataset 36653cb34c60Sahrens * zc_value new name of dataset 36663cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 36673cb34c60Sahrens * 36683cb34c60Sahrens * outputs: none 36693cb34c60Sahrens */ 3670fa9e4066Sahrens static int 3671fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3672fa9e4066Sahrens { 36737f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 36743b2aab18SMatthew Ahrens char *at; 3675cdf5b4caSmmusante 3676e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3677f18faf3fSek if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3678f18faf3fSek strchr(zc->zc_value, '%')) 3679be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3680fa9e4066Sahrens 36813b2aab18SMatthew Ahrens at = strchr(zc->zc_name, '@'); 36823b2aab18SMatthew Ahrens if (at != NULL) { 36833b2aab18SMatthew Ahrens /* snaps must be in same fs */ 3684a0c1127bSSteven Hartland int error; 3685a0c1127bSSteven Hartland 36863b2aab18SMatthew Ahrens if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1)) 3687be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 36883b2aab18SMatthew Ahrens *at = '\0'; 36893b2aab18SMatthew Ahrens if (zc->zc_objset_type == DMU_OST_ZFS) { 3690a0c1127bSSteven Hartland error = dmu_objset_find(zc->zc_name, 36913b2aab18SMatthew Ahrens recursive_unmount, at + 1, 36923b2aab18SMatthew Ahrens recursive ? DS_FIND_CHILDREN : 0); 3693a0c1127bSSteven Hartland if (error != 0) { 3694a0c1127bSSteven Hartland *at = '@'; 36953b2aab18SMatthew Ahrens return (error); 3696a0c1127bSSteven Hartland } 36973b2aab18SMatthew Ahrens } 3698a0c1127bSSteven Hartland error = dsl_dataset_rename_snapshot(zc->zc_name, 3699a0c1127bSSteven Hartland at + 1, strchr(zc->zc_value, '@') + 1, recursive); 3700a0c1127bSSteven Hartland *at = '@'; 3701a0c1127bSSteven Hartland 3702a0c1127bSSteven Hartland return (error); 37033b2aab18SMatthew Ahrens } else { 37043b2aab18SMatthew Ahrens if (zc->zc_objset_type == DMU_OST_ZVOL) 37053b2aab18SMatthew Ahrens (void) zvol_remove_minor(zc->zc_name); 37063b2aab18SMatthew Ahrens return (dsl_dir_rename(zc->zc_name, zc->zc_value)); 3707fa9e4066Sahrens } 3708fa9e4066Sahrens } 3709fa9e4066Sahrens 371092241e0bSTom Erickson static int 371192241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 371292241e0bSTom Erickson { 371392241e0bSTom Erickson const char *propname = nvpair_name(pair); 371492241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 371592241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 371692241e0bSTom Erickson uint64_t intval; 371792241e0bSTom Erickson int err; 371892241e0bSTom Erickson 371992241e0bSTom Erickson if (prop == ZPROP_INVAL) { 372092241e0bSTom Erickson if (zfs_prop_user(propname)) { 372192241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 372292241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 372392241e0bSTom Erickson return (err); 372492241e0bSTom Erickson return (0); 372592241e0bSTom Erickson } 372692241e0bSTom Erickson 372792241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 372892241e0bSTom Erickson const char *perm = NULL; 372992241e0bSTom Erickson const char *uq_prefix = 373092241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 373192241e0bSTom Erickson const char *gq_prefix = 373292241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 373392241e0bSTom Erickson 373492241e0bSTom Erickson if (strncmp(propname, uq_prefix, 373592241e0bSTom Erickson strlen(uq_prefix)) == 0) { 373692241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 373792241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 373892241e0bSTom Erickson strlen(gq_prefix)) == 0) { 373992241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 374092241e0bSTom Erickson } else { 374192241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 3742be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 374392241e0bSTom Erickson } 374492241e0bSTom Erickson 374592241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 374692241e0bSTom Erickson return (err); 374792241e0bSTom Erickson return (0); 374892241e0bSTom Erickson } 374992241e0bSTom Erickson 3750be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 375192241e0bSTom Erickson } 375292241e0bSTom Erickson 375392241e0bSTom Erickson if (issnap) 3754be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 375592241e0bSTom Erickson 375692241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 375792241e0bSTom Erickson /* 375892241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 375992241e0bSTom Erickson * format. 376092241e0bSTom Erickson */ 376192241e0bSTom Erickson nvlist_t *attrs; 376292241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 376392241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 376492241e0bSTom Erickson &pair) == 0); 376592241e0bSTom Erickson } 376692241e0bSTom Erickson 376792241e0bSTom Erickson /* 376892241e0bSTom Erickson * Check that this value is valid for this pool version 376992241e0bSTom Erickson */ 377092241e0bSTom Erickson switch (prop) { 377192241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 377292241e0bSTom Erickson /* 377392241e0bSTom Erickson * If the user specified gzip compression, make sure 377492241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 377592241e0bSTom Erickson * we'll catch them later. 377692241e0bSTom Erickson */ 3777b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0) { 377892241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 377992241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 378092241e0bSTom Erickson zfs_earlier_version(dsname, 378192241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 3782be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 378392241e0bSTom Erickson } 378492241e0bSTom Erickson 378592241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 378692241e0bSTom Erickson zfs_earlier_version(dsname, 378792241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 3788be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 378992241e0bSTom Erickson 3790a6f561b4SSašo Kiselkov if (intval == ZIO_COMPRESS_LZ4) { 3791a6f561b4SSašo Kiselkov spa_t *spa; 3792a6f561b4SSašo Kiselkov 3793a6f561b4SSašo Kiselkov if ((err = spa_open(dsname, &spa, FTAG)) != 0) 3794a6f561b4SSašo Kiselkov return (err); 3795a6f561b4SSašo Kiselkov 37962acef22dSMatthew Ahrens if (!spa_feature_is_enabled(spa, 37972acef22dSMatthew Ahrens SPA_FEATURE_LZ4_COMPRESS)) { 3798a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 3799be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3800a6f561b4SSašo Kiselkov } 3801a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 3802a6f561b4SSašo Kiselkov } 3803a6f561b4SSašo Kiselkov 380492241e0bSTom Erickson /* 380592241e0bSTom Erickson * If this is a bootable dataset then 380692241e0bSTom Erickson * verify that the compression algorithm 380792241e0bSTom Erickson * is supported for booting. We must return 380892241e0bSTom Erickson * something other than ENOTSUP since it 380992241e0bSTom Erickson * implies a downrev pool version. 381092241e0bSTom Erickson */ 381192241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 381292241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 3813be6fd75aSMatthew Ahrens return (SET_ERROR(ERANGE)); 381492241e0bSTom Erickson } 381592241e0bSTom Erickson } 381692241e0bSTom Erickson break; 381792241e0bSTom Erickson 381892241e0bSTom Erickson case ZFS_PROP_COPIES: 381992241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 3820be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 382192241e0bSTom Erickson break; 382292241e0bSTom Erickson 3823b5152584SMatthew Ahrens case ZFS_PROP_RECORDSIZE: 3824b5152584SMatthew Ahrens /* Record sizes above 128k need the feature to be enabled */ 3825b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0 && 3826b5152584SMatthew Ahrens intval > SPA_OLD_MAXBLOCKSIZE) { 3827b5152584SMatthew Ahrens spa_t *spa; 3828b5152584SMatthew Ahrens 3829b5152584SMatthew Ahrens /* 3830b5152584SMatthew Ahrens * If this is a bootable dataset then 3831b5152584SMatthew Ahrens * the we don't allow large (>128K) blocks, 3832b5152584SMatthew Ahrens * because GRUB doesn't support them. 3833b5152584SMatthew Ahrens */ 3834b5152584SMatthew Ahrens if (zfs_is_bootfs(dsname) && 3835b5152584SMatthew Ahrens intval > SPA_OLD_MAXBLOCKSIZE) { 38366de9bb56SMatthew Ahrens return (SET_ERROR(ERANGE)); 3837b5152584SMatthew Ahrens } 3838b5152584SMatthew Ahrens 3839b5152584SMatthew Ahrens /* 3840b5152584SMatthew Ahrens * We don't allow setting the property above 1MB, 3841b5152584SMatthew Ahrens * unless the tunable has been changed. 3842b5152584SMatthew Ahrens */ 3843b5152584SMatthew Ahrens if (intval > zfs_max_recordsize || 3844b5152584SMatthew Ahrens intval > SPA_MAXBLOCKSIZE) 38456de9bb56SMatthew Ahrens return (SET_ERROR(ERANGE)); 3846b5152584SMatthew Ahrens 3847b5152584SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 3848b5152584SMatthew Ahrens return (err); 3849b5152584SMatthew Ahrens 3850b5152584SMatthew Ahrens if (!spa_feature_is_enabled(spa, 3851b5152584SMatthew Ahrens SPA_FEATURE_LARGE_BLOCKS)) { 3852b5152584SMatthew Ahrens spa_close(spa, FTAG); 3853b5152584SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3854b5152584SMatthew Ahrens } 3855b5152584SMatthew Ahrens spa_close(spa, FTAG); 3856b5152584SMatthew Ahrens } 3857b5152584SMatthew Ahrens break; 3858b5152584SMatthew Ahrens 385992241e0bSTom Erickson case ZFS_PROP_SHARESMB: 386092241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 3861be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 386292241e0bSTom Erickson break; 386392241e0bSTom Erickson 386492241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 386592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 386692241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 386792241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 386892241e0bSTom Erickson zfs_earlier_version(dsname, 386992241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 3870be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 387192241e0bSTom Erickson } 387292241e0bSTom Erickson break; 387345818ee1SMatthew Ahrens 387445818ee1SMatthew Ahrens case ZFS_PROP_CHECKSUM: 387545818ee1SMatthew Ahrens case ZFS_PROP_DEDUP: 387645818ee1SMatthew Ahrens { 387745818ee1SMatthew Ahrens spa_feature_t feature; 387845818ee1SMatthew Ahrens spa_t *spa; 387945818ee1SMatthew Ahrens 388045818ee1SMatthew Ahrens /* dedup feature version checks */ 388145818ee1SMatthew Ahrens if (prop == ZFS_PROP_DEDUP && 388245818ee1SMatthew Ahrens zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 388345818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 388445818ee1SMatthew Ahrens 388545818ee1SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) != 0) 388645818ee1SMatthew Ahrens return (SET_ERROR(EINVAL)); 388745818ee1SMatthew Ahrens 388845818ee1SMatthew Ahrens /* check prop value is enabled in features */ 388945818ee1SMatthew Ahrens feature = zio_checksum_to_feature(intval); 389045818ee1SMatthew Ahrens if (feature == SPA_FEATURE_NONE) 389145818ee1SMatthew Ahrens break; 389245818ee1SMatthew Ahrens 389345818ee1SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 389445818ee1SMatthew Ahrens return (err); 389545818ee1SMatthew Ahrens /* 389645818ee1SMatthew Ahrens * Salted checksums are not supported on root pools. 389745818ee1SMatthew Ahrens */ 389845818ee1SMatthew Ahrens if (spa_bootfs(spa) != 0 && 389945818ee1SMatthew Ahrens intval < ZIO_CHECKSUM_FUNCTIONS && 390045818ee1SMatthew Ahrens (zio_checksum_table[intval].ci_flags & 390145818ee1SMatthew Ahrens ZCHECKSUM_FLAG_SALTED)) { 390245818ee1SMatthew Ahrens spa_close(spa, FTAG); 390345818ee1SMatthew Ahrens return (SET_ERROR(ERANGE)); 390445818ee1SMatthew Ahrens } 390545818ee1SMatthew Ahrens if (!spa_feature_is_enabled(spa, feature)) { 390645818ee1SMatthew Ahrens spa_close(spa, FTAG); 390745818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 390845818ee1SMatthew Ahrens } 390945818ee1SMatthew Ahrens spa_close(spa, FTAG); 391045818ee1SMatthew Ahrens break; 391145818ee1SMatthew Ahrens } 391292241e0bSTom Erickson } 391392241e0bSTom Erickson 391492241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 391592241e0bSTom Erickson } 391692241e0bSTom Erickson 3917a6f561b4SSašo Kiselkov /* 3918a6f561b4SSašo Kiselkov * Checks for a race condition to make sure we don't increment a feature flag 3919a6f561b4SSašo Kiselkov * multiple times. 3920a6f561b4SSašo Kiselkov */ 3921a6f561b4SSašo Kiselkov static int 39223b2aab18SMatthew Ahrens zfs_prop_activate_feature_check(void *arg, dmu_tx_t *tx) 3923a6f561b4SSašo Kiselkov { 39243b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 39252acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 3926a6f561b4SSašo Kiselkov 39272acef22dSMatthew Ahrens if (!spa_feature_is_active(spa, *featurep)) 3928a6f561b4SSašo Kiselkov return (0); 3929a6f561b4SSašo Kiselkov else 3930be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 3931a6f561b4SSašo Kiselkov } 3932a6f561b4SSašo Kiselkov 3933a6f561b4SSašo Kiselkov /* 3934a6f561b4SSašo Kiselkov * The callback invoked on feature activation in the sync task caused by 3935a6f561b4SSašo Kiselkov * zfs_prop_activate_feature. 3936a6f561b4SSašo Kiselkov */ 3937a6f561b4SSašo Kiselkov static void 39383b2aab18SMatthew Ahrens zfs_prop_activate_feature_sync(void *arg, dmu_tx_t *tx) 3939a6f561b4SSašo Kiselkov { 39403b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 39412acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 3942a6f561b4SSašo Kiselkov 39432acef22dSMatthew Ahrens spa_feature_incr(spa, *featurep, tx); 3944a6f561b4SSašo Kiselkov } 3945a6f561b4SSašo Kiselkov 39463b2aab18SMatthew Ahrens /* 39473b2aab18SMatthew Ahrens * Activates a feature on a pool in response to a property setting. This 39483b2aab18SMatthew Ahrens * creates a new sync task which modifies the pool to reflect the feature 39493b2aab18SMatthew Ahrens * as being active. 39503b2aab18SMatthew Ahrens */ 39513b2aab18SMatthew Ahrens static int 39522acef22dSMatthew Ahrens zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature) 39533b2aab18SMatthew Ahrens { 39543b2aab18SMatthew Ahrens int err; 39553b2aab18SMatthew Ahrens 39563b2aab18SMatthew Ahrens /* EBUSY here indicates that the feature is already active */ 39573b2aab18SMatthew Ahrens err = dsl_sync_task(spa_name(spa), 39583b2aab18SMatthew Ahrens zfs_prop_activate_feature_check, zfs_prop_activate_feature_sync, 39597d46dc6cSMatthew Ahrens &feature, 2, ZFS_SPACE_CHECK_RESERVED); 39603b2aab18SMatthew Ahrens 39613b2aab18SMatthew Ahrens if (err != 0 && err != EBUSY) 39623b2aab18SMatthew Ahrens return (err); 39633b2aab18SMatthew Ahrens else 39643b2aab18SMatthew Ahrens return (0); 39653b2aab18SMatthew Ahrens } 39663b2aab18SMatthew Ahrens 396792241e0bSTom Erickson /* 396892241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 396992241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 397092241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 397192241e0bSTom Erickson * 397292241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 397392241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 397492241e0bSTom Erickson * of all the properties that failed a permission check along with the 397592241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 397692241e0bSTom Erickson * returned errlist. 397792241e0bSTom Erickson * 397892241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 397992241e0bSTom Erickson * pointed at by errlist is NULL. 398092241e0bSTom Erickson */ 398192241e0bSTom Erickson static int 398292241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 3983745cd3c5Smaybee { 3984745cd3c5Smaybee zfs_cmd_t *zc; 398592241e0bSTom Erickson nvpair_t *pair, *next_pair; 398692241e0bSTom Erickson nvlist_t *errors; 398792241e0bSTom Erickson int err, rv = 0; 3988745cd3c5Smaybee 3989745cd3c5Smaybee if (props == NULL) 399092241e0bSTom Erickson return (0); 399192241e0bSTom Erickson 399292241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 399392241e0bSTom Erickson 3994745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 3995745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 399692241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 399792241e0bSTom Erickson while (pair != NULL) { 399892241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 399992241e0bSTom Erickson 400092241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 400192241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 40024445fffbSMatthew Ahrens (err = zfs_secpolicy_inherit_prop(zc, NULL, CRED())) != 0) { 400392241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 400492241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 400592241e0bSTom Erickson zc->zc_value, err) == 0); 400692241e0bSTom Erickson } 400792241e0bSTom Erickson pair = next_pair; 4008745cd3c5Smaybee } 4009745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 401092241e0bSTom Erickson 401192241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 401292241e0bSTom Erickson nvlist_free(errors); 401392241e0bSTom Erickson errors = NULL; 401492241e0bSTom Erickson } else { 401592241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 401692241e0bSTom Erickson } 401792241e0bSTom Erickson 401892241e0bSTom Erickson if (errlist == NULL) 401992241e0bSTom Erickson nvlist_free(errors); 402092241e0bSTom Erickson else 402192241e0bSTom Erickson *errlist = errors; 402292241e0bSTom Erickson 402392241e0bSTom Erickson return (rv); 402492241e0bSTom Erickson } 402592241e0bSTom Erickson 402692241e0bSTom Erickson static boolean_t 402792241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 402892241e0bSTom Erickson { 402992241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 403092241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 403192241e0bSTom Erickson nvlist_t *attrs; 403292241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 403392241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 403492241e0bSTom Erickson &p1) == 0); 403592241e0bSTom Erickson } 403692241e0bSTom Erickson 403792241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 403892241e0bSTom Erickson nvlist_t *attrs; 403992241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 404092241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 404192241e0bSTom Erickson &p2) == 0); 404292241e0bSTom Erickson } 404392241e0bSTom Erickson 404492241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 404592241e0bSTom Erickson return (B_FALSE); 404692241e0bSTom Erickson 404792241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 404892241e0bSTom Erickson char *valstr1, *valstr2; 404992241e0bSTom Erickson 405092241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 405192241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 405292241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 405392241e0bSTom Erickson } else { 405492241e0bSTom Erickson uint64_t intval1, intval2; 405592241e0bSTom Erickson 405692241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 405792241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 405892241e0bSTom Erickson return (intval1 == intval2); 405992241e0bSTom Erickson } 4060745cd3c5Smaybee } 4061745cd3c5Smaybee 406292241e0bSTom Erickson /* 406392241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 406492241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 406592241e0bSTom Erickson * do not need to clear or restore properties that won't change. 406692241e0bSTom Erickson */ 406792241e0bSTom Erickson static void 406892241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 406992241e0bSTom Erickson { 407092241e0bSTom Erickson nvpair_t *pair, *next_pair; 407192241e0bSTom Erickson 407292241e0bSTom Erickson if (origprops == NULL) 407392241e0bSTom Erickson return; /* all props need to be received */ 407492241e0bSTom Erickson 407592241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 407692241e0bSTom Erickson while (pair != NULL) { 407792241e0bSTom Erickson const char *propname = nvpair_name(pair); 407892241e0bSTom Erickson nvpair_t *match; 407992241e0bSTom Erickson 408092241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 408192241e0bSTom Erickson 408292241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 408392241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 408492241e0bSTom Erickson goto next; /* need to set received value */ 408592241e0bSTom Erickson 408692241e0bSTom Erickson /* don't clear the existing received value */ 408792241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 408892241e0bSTom Erickson /* don't bother receiving the property */ 408992241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 409092241e0bSTom Erickson next: 409192241e0bSTom Erickson pair = next_pair; 409292241e0bSTom Erickson } 409392241e0bSTom Erickson } 409492241e0bSTom Erickson 40955878fad7SDan McDonald /* 40965878fad7SDan McDonald * Extract properties that cannot be set PRIOR to the receipt of a dataset. 40975878fad7SDan McDonald * For example, refquota cannot be set until after the receipt of a dataset, 40985878fad7SDan McDonald * because in replication streams, an older/earlier snapshot may exceed the 40995878fad7SDan McDonald * refquota. We want to receive the older/earlier snapshot, but setting 41005878fad7SDan McDonald * refquota pre-receipt will set the dsl's ACTUAL quota, which will prevent 41015878fad7SDan McDonald * the older/earlier snapshot from being received (with EDQUOT). 41025878fad7SDan McDonald * 41035878fad7SDan McDonald * The ZFS test "zfs_receive_011_pos" demonstrates such a scenario. 41045878fad7SDan McDonald * 41055878fad7SDan McDonald * libzfs will need to be judicious handling errors encountered by props 41065878fad7SDan McDonald * extracted by this function. 41075878fad7SDan McDonald */ 41085878fad7SDan McDonald static nvlist_t * 41095878fad7SDan McDonald extract_delay_props(nvlist_t *props) 41105878fad7SDan McDonald { 41115878fad7SDan McDonald nvlist_t *delayprops; 41125878fad7SDan McDonald nvpair_t *nvp, *tmp; 41135878fad7SDan McDonald static const zfs_prop_t delayable[] = { ZFS_PROP_REFQUOTA, 0 }; 41145878fad7SDan McDonald int i; 41155878fad7SDan McDonald 41165878fad7SDan McDonald VERIFY(nvlist_alloc(&delayprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 41175878fad7SDan McDonald 41185878fad7SDan McDonald for (nvp = nvlist_next_nvpair(props, NULL); nvp != NULL; 41195878fad7SDan McDonald nvp = nvlist_next_nvpair(props, nvp)) { 41205878fad7SDan McDonald /* 41215878fad7SDan McDonald * strcmp() is safe because zfs_prop_to_name() always returns 41225878fad7SDan McDonald * a bounded string. 41235878fad7SDan McDonald */ 41245878fad7SDan McDonald for (i = 0; delayable[i] != 0; i++) { 41255878fad7SDan McDonald if (strcmp(zfs_prop_to_name(delayable[i]), 41265878fad7SDan McDonald nvpair_name(nvp)) == 0) { 41275878fad7SDan McDonald break; 41285878fad7SDan McDonald } 41295878fad7SDan McDonald } 41305878fad7SDan McDonald if (delayable[i] != 0) { 41315878fad7SDan McDonald tmp = nvlist_prev_nvpair(props, nvp); 41325878fad7SDan McDonald VERIFY(nvlist_add_nvpair(delayprops, nvp) == 0); 41335878fad7SDan McDonald VERIFY(nvlist_remove_nvpair(props, nvp) == 0); 41345878fad7SDan McDonald nvp = tmp; 41355878fad7SDan McDonald } 41365878fad7SDan McDonald } 41375878fad7SDan McDonald 41385878fad7SDan McDonald if (nvlist_empty(delayprops)) { 41395878fad7SDan McDonald nvlist_free(delayprops); 41405878fad7SDan McDonald delayprops = NULL; 41415878fad7SDan McDonald } 41425878fad7SDan McDonald return (delayprops); 41435878fad7SDan McDonald } 41445878fad7SDan McDonald 414592241e0bSTom Erickson #ifdef DEBUG 414692241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 414792241e0bSTom Erickson #endif 414892241e0bSTom Erickson 41493cb34c60Sahrens /* 41503cb34c60Sahrens * inputs: 41513cb34c60Sahrens * zc_name name of containing filesystem 41523cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 41533cb34c60Sahrens * zc_value name of snapshot to create 41543cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 41553cb34c60Sahrens * zc_cookie file descriptor to recv from 41563cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 41573cb34c60Sahrens * zc_guid force flag 4158c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor 4159c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping (or zero on first call) 41609c3fd121SMatthew Ahrens * zc_resumable if data is incomplete assume sender will resume 41613cb34c60Sahrens * 41623cb34c60Sahrens * outputs: 41633cb34c60Sahrens * zc_cookie number of bytes read 416492241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 416592241e0bSTom Erickson * zc_obj zprop_errflags_t 4166c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping 41673cb34c60Sahrens */ 4168fa9e4066Sahrens static int 41693cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 4170fa9e4066Sahrens { 4171fa9e4066Sahrens file_t *fp; 41723cb34c60Sahrens dmu_recv_cookie_t drc; 4173f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 417492241e0bSTom Erickson int fd; 417592241e0bSTom Erickson int error = 0; 417692241e0bSTom Erickson int props_error = 0; 417792241e0bSTom Erickson nvlist_t *errors; 41783cb34c60Sahrens offset_t off; 417992241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 418092241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 41815878fad7SDan McDonald nvlist_t *delayprops = NULL; /* sent properties applied post-receive */ 41823b2aab18SMatthew Ahrens char *origin = NULL; 41833cb34c60Sahrens char *tosnap; 41843cb34c60Sahrens char tofs[ZFS_MAXNAMELEN]; 418592241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 4186fa9e4066Sahrens 41873ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 4188f18faf3fSek strchr(zc->zc_value, '@') == NULL || 4189f18faf3fSek strchr(zc->zc_value, '%')) 4190be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 41913ccfa83cSahrens 41923cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 41933cb34c60Sahrens tosnap = strchr(tofs, '@'); 419492241e0bSTom Erickson *tosnap++ = '\0'; 41953cb34c60Sahrens 41963cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 41973cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 4198478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 41993cb34c60Sahrens return (error); 42003cb34c60Sahrens 4201fa9e4066Sahrens fd = zc->zc_cookie; 4202fa9e4066Sahrens fp = getf(fd); 42033cb34c60Sahrens if (fp == NULL) { 42043cb34c60Sahrens nvlist_free(props); 4205be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 42063cb34c60Sahrens } 4207f18faf3fSek 42089c3fd121SMatthew Ahrens errors = fnvlist_alloc(); 420992241e0bSTom Erickson 42103b2aab18SMatthew Ahrens if (zc->zc_string[0]) 42113b2aab18SMatthew Ahrens origin = zc->zc_string; 42123b2aab18SMatthew Ahrens 42133b2aab18SMatthew Ahrens error = dmu_recv_begin(tofs, tosnap, 42149c3fd121SMatthew Ahrens &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc); 42153b2aab18SMatthew Ahrens if (error != 0) 42163b2aab18SMatthew Ahrens goto out; 42173b2aab18SMatthew Ahrens 42183b2aab18SMatthew Ahrens /* 42193b2aab18SMatthew Ahrens * Set properties before we receive the stream so that they are applied 42203b2aab18SMatthew Ahrens * to the new data. Note that we must call dmu_recv_stream() if 42213b2aab18SMatthew Ahrens * dmu_recv_begin() succeeds. 42223b2aab18SMatthew Ahrens */ 42233b2aab18SMatthew Ahrens if (props != NULL && !drc.drc_newfs) { 42243b2aab18SMatthew Ahrens if (spa_version(dsl_dataset_get_spa(drc.drc_ds)) >= 42253b2aab18SMatthew Ahrens SPA_VERSION_RECVD_PROPS && 42263b2aab18SMatthew Ahrens !dsl_prop_get_hasrecvd(tofs)) 422792241e0bSTom Erickson first_recvd_props = B_TRUE; 422892241e0bSTom Erickson 4229745cd3c5Smaybee /* 423092241e0bSTom Erickson * If new received properties are supplied, they are to 423192241e0bSTom Erickson * completely replace the existing received properties, so stash 423292241e0bSTom Erickson * away the existing ones. 4233745cd3c5Smaybee */ 42343b2aab18SMatthew Ahrens if (dsl_prop_get_received(tofs, &origprops) == 0) { 423592241e0bSTom Erickson nvlist_t *errlist = NULL; 423692241e0bSTom Erickson /* 423792241e0bSTom Erickson * Don't bother writing a property if its value won't 423892241e0bSTom Erickson * change (and avoid the unnecessary security checks). 423992241e0bSTom Erickson * 424092241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 424192241e0bSTom Erickson * special case where we blow away all local properties 424292241e0bSTom Erickson * regardless. 424392241e0bSTom Erickson */ 424492241e0bSTom Erickson if (!first_recvd_props) 424592241e0bSTom Erickson props_reduce(props, origprops); 42463b2aab18SMatthew Ahrens if (zfs_check_clearable(tofs, origprops, &errlist) != 0) 424792241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 424892241e0bSTom Erickson nvlist_free(errlist); 42493cb34c60Sahrens 42503b2aab18SMatthew Ahrens if (clear_received_props(tofs, origprops, 42513b2aab18SMatthew Ahrens first_recvd_props ? NULL : props) != 0) 425292241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 42533b2aab18SMatthew Ahrens } else { 425492241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 425592241e0bSTom Erickson } 42563b2aab18SMatthew Ahrens } 425792241e0bSTom Erickson 42583b2aab18SMatthew Ahrens if (props != NULL) { 42593b2aab18SMatthew Ahrens props_error = dsl_prop_set_hasrecvd(tofs); 42603b2aab18SMatthew Ahrens 42613b2aab18SMatthew Ahrens if (props_error == 0) { 42625878fad7SDan McDonald delayprops = extract_delay_props(props); 42633b2aab18SMatthew Ahrens (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 42643b2aab18SMatthew Ahrens props, errors); 42653b2aab18SMatthew Ahrens } 426692241e0bSTom Erickson } 426792241e0bSTom Erickson 42683cb34c60Sahrens off = fp->f_offset; 4269c99e4bdcSChris Kirby error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, 4270c99e4bdcSChris Kirby &zc->zc_action_handle); 4271a2eea2e1Sahrens 4272f4b94bdeSMatthew Ahrens if (error == 0) { 4273f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 4274745cd3c5Smaybee 4275f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 4276f4b94bdeSMatthew Ahrens /* online recv */ 4277f4b94bdeSMatthew Ahrens int end_err; 4278745cd3c5Smaybee 4279503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 4280f4b94bdeSMatthew Ahrens /* 4281f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 4282f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 4283f4b94bdeSMatthew Ahrens */ 428491948b51SKeith M Wesolowski end_err = dmu_recv_end(&drc, zfsvfs); 42855c703fceSGeorge Wilson if (error == 0) 42865c703fceSGeorge Wilson error = zfs_resume_fs(zfsvfs, tofs); 4287f4b94bdeSMatthew Ahrens error = error ? error : end_err; 4288f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 4289745cd3c5Smaybee } else { 429091948b51SKeith M Wesolowski error = dmu_recv_end(&drc, NULL); 42913cb34c60Sahrens } 42925878fad7SDan McDonald 42935878fad7SDan McDonald /* Set delayed properties now, after we're done receiving. */ 42945878fad7SDan McDonald if (delayprops != NULL && error == 0) { 42955878fad7SDan McDonald (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 42965878fad7SDan McDonald delayprops, errors); 42975878fad7SDan McDonald } 42985878fad7SDan McDonald } 42995878fad7SDan McDonald 43005878fad7SDan McDonald if (delayprops != NULL) { 43015878fad7SDan McDonald /* 43025878fad7SDan McDonald * Merge delayed props back in with initial props, in case 43035878fad7SDan McDonald * we're DEBUG and zfs_ioc_recv_inject_err is set (which means 43045878fad7SDan McDonald * we have to make sure clear_received_props() includes 43055878fad7SDan McDonald * the delayed properties). 43065878fad7SDan McDonald * 43075878fad7SDan McDonald * Since zfs_ioc_recv_inject_err is only in DEBUG kernels, 43085878fad7SDan McDonald * using ASSERT() will be just like a VERIFY. 43095878fad7SDan McDonald */ 43105878fad7SDan McDonald ASSERT(nvlist_merge(props, delayprops, 0) == 0); 43115878fad7SDan McDonald nvlist_free(delayprops); 43125878fad7SDan McDonald } 43135878fad7SDan McDonald 43145878fad7SDan McDonald /* 43155878fad7SDan McDonald * Now that all props, initial and delayed, are set, report the prop 43165878fad7SDan McDonald * errors to the caller. 43175878fad7SDan McDonald */ 43185878fad7SDan McDonald if (zc->zc_nvlist_dst_size != 0 && 43195878fad7SDan McDonald (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || 43205878fad7SDan McDonald put_nvlist(zc, errors) != 0)) { 43215878fad7SDan McDonald /* 43225878fad7SDan McDonald * Caller made zc->zc_nvlist_dst less than the minimum expected 43235878fad7SDan McDonald * size or supplied an invalid address. 43245878fad7SDan McDonald */ 43255878fad7SDan McDonald props_error = SET_ERROR(EINVAL); 432647f263f4Sek } 43273cb34c60Sahrens 43283cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 43293cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 43303cb34c60Sahrens fp->f_offset = off; 4331a2eea2e1Sahrens 433292241e0bSTom Erickson #ifdef DEBUG 433392241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 433492241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 433592241e0bSTom Erickson error = 1; 433692241e0bSTom Erickson } 433792241e0bSTom Erickson #endif 4338745cd3c5Smaybee /* 4339745cd3c5Smaybee * On error, restore the original props. 4340745cd3c5Smaybee */ 43413b2aab18SMatthew Ahrens if (error != 0 && props != NULL && !drc.drc_newfs) { 43423b2aab18SMatthew Ahrens if (clear_received_props(tofs, props, NULL) != 0) { 43433b2aab18SMatthew Ahrens /* 43443b2aab18SMatthew Ahrens * We failed to clear the received properties. 43453b2aab18SMatthew Ahrens * Since we may have left a $recvd value on the 43463b2aab18SMatthew Ahrens * system, we can't clear the $hasrecvd flag. 43473b2aab18SMatthew Ahrens */ 434892241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 43493b2aab18SMatthew Ahrens } else if (first_recvd_props) { 43503b2aab18SMatthew Ahrens dsl_prop_unset_hasrecvd(tofs); 435192241e0bSTom Erickson } 435292241e0bSTom Erickson 435392241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 435492241e0bSTom Erickson /* We failed to stash the original properties. */ 435592241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 435692241e0bSTom Erickson } 435792241e0bSTom Erickson 435892241e0bSTom Erickson /* 435992241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 436092241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 436192241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 436292241e0bSTom Erickson * first new-style receive. 436392241e0bSTom Erickson */ 436492241e0bSTom Erickson if (origprops != NULL && 436592241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 436692241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 436792241e0bSTom Erickson origprops, NULL) != 0) { 436892241e0bSTom Erickson /* 436992241e0bSTom Erickson * We stashed the original properties but failed to 437092241e0bSTom Erickson * restore them. 437192241e0bSTom Erickson */ 437292241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 437392241e0bSTom Erickson } 4374745cd3c5Smaybee } 4375745cd3c5Smaybee out: 4376745cd3c5Smaybee nvlist_free(props); 4377745cd3c5Smaybee nvlist_free(origprops); 437892241e0bSTom Erickson nvlist_free(errors); 4379fa9e4066Sahrens releasef(fd); 438092241e0bSTom Erickson 438192241e0bSTom Erickson if (error == 0) 438292241e0bSTom Erickson error = props_error; 438392241e0bSTom Erickson 4384fa9e4066Sahrens return (error); 4385fa9e4066Sahrens } 4386fa9e4066Sahrens 43873cb34c60Sahrens /* 43883cb34c60Sahrens * inputs: 43893cb34c60Sahrens * zc_name name of snapshot to send 43903cb34c60Sahrens * zc_cookie file descriptor to send stream to 4391a7f53a56SChris Kirby * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) 4392a7f53a56SChris Kirby * zc_sendobj objsetid of snapshot to send 4393a7f53a56SChris Kirby * zc_fromobj objsetid of incremental fromsnap (may be zero) 439419b94df9SMatthew Ahrens * zc_guid if set, estimate size of stream only. zc_cookie is ignored. 439519b94df9SMatthew Ahrens * output size in zc_objset_type. 4396b5152584SMatthew Ahrens * zc_flags lzc_send_flags 43973cb34c60Sahrens * 439878f17100SMatthew Ahrens * outputs: 439978f17100SMatthew Ahrens * zc_objset_type estimated size, if zc_guid is set 44003cb34c60Sahrens */ 4401fa9e4066Sahrens static int 44023cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 4403fa9e4066Sahrens { 4404fa9e4066Sahrens int error; 44053cb34c60Sahrens offset_t off; 440619b94df9SMatthew Ahrens boolean_t estimate = (zc->zc_guid != 0); 44075d7b4d43SMatthew Ahrens boolean_t embedok = (zc->zc_flags & 0x1); 4408b5152584SMatthew Ahrens boolean_t large_block_ok = (zc->zc_flags & 0x2); 4409fa9e4066Sahrens 44103b2aab18SMatthew Ahrens if (zc->zc_obj != 0) { 44113b2aab18SMatthew Ahrens dsl_pool_t *dp; 44123b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 4413a7f53a56SChris Kirby 44143b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 44153b2aab18SMatthew Ahrens if (error != 0) 4416a7f53a56SChris Kirby return (error); 44173b2aab18SMatthew Ahrens 44183b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 44193b2aab18SMatthew Ahrens if (error != 0) { 44203b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 4421fa9e4066Sahrens return (error); 4422fa9e4066Sahrens } 44233b2aab18SMatthew Ahrens 44243b2aab18SMatthew Ahrens if (dsl_dir_is_clone(tosnap->ds_dir)) 4425c1379625SJustin T. Gibbs zc->zc_fromobj = 4426c1379625SJustin T. Gibbs dsl_dir_phys(tosnap->ds_dir)->dd_origin_obj; 44273b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 44283b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 44294445fffbSMatthew Ahrens } 44304445fffbSMatthew Ahrens 44313b2aab18SMatthew Ahrens if (estimate) { 44323b2aab18SMatthew Ahrens dsl_pool_t *dp; 44333b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 44343b2aab18SMatthew Ahrens dsl_dataset_t *fromsnap = NULL; 44354445fffbSMatthew Ahrens 44363b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 44373b2aab18SMatthew Ahrens if (error != 0) 44383b2aab18SMatthew Ahrens return (error); 44393b2aab18SMatthew Ahrens 44403b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 44413b2aab18SMatthew Ahrens if (error != 0) { 44423b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 44433b2aab18SMatthew Ahrens return (error); 44444445fffbSMatthew Ahrens } 44454445fffbSMatthew Ahrens 44463b2aab18SMatthew Ahrens if (zc->zc_fromobj != 0) { 44473b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, 44483b2aab18SMatthew Ahrens FTAG, &fromsnap); 44493b2aab18SMatthew Ahrens if (error != 0) { 44503b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 44513b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 44524445fffbSMatthew Ahrens return (error); 44534445fffbSMatthew Ahrens } 44544445fffbSMatthew Ahrens } 4455fa9e4066Sahrens 44564445fffbSMatthew Ahrens error = dmu_send_estimate(tosnap, fromsnap, 445719b94df9SMatthew Ahrens &zc->zc_objset_type); 44583b2aab18SMatthew Ahrens 44593b2aab18SMatthew Ahrens if (fromsnap != NULL) 44603b2aab18SMatthew Ahrens dsl_dataset_rele(fromsnap, FTAG); 44613b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 44623b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 446319b94df9SMatthew Ahrens } else { 446419b94df9SMatthew Ahrens file_t *fp = getf(zc->zc_cookie); 44653b2aab18SMatthew Ahrens if (fp == NULL) 4466be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 4467fa9e4066Sahrens 446819b94df9SMatthew Ahrens off = fp->f_offset; 44693b2aab18SMatthew Ahrens error = dmu_send_obj(zc->zc_name, zc->zc_sendobj, 4470b5152584SMatthew Ahrens zc->zc_fromobj, embedok, large_block_ok, 4471b5152584SMatthew Ahrens zc->zc_cookie, fp->f_vnode, &off); 4472fa9e4066Sahrens 447319b94df9SMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 447419b94df9SMatthew Ahrens fp->f_offset = off; 447519b94df9SMatthew Ahrens releasef(zc->zc_cookie); 447619b94df9SMatthew Ahrens } 4477fa9e4066Sahrens return (error); 4478fa9e4066Sahrens } 4479fa9e4066Sahrens 44804e3c9f44SBill Pijewski /* 44814e3c9f44SBill Pijewski * inputs: 44824e3c9f44SBill Pijewski * zc_name name of snapshot on which to report progress 44834e3c9f44SBill Pijewski * zc_cookie file descriptor of send stream 44844e3c9f44SBill Pijewski * 44854e3c9f44SBill Pijewski * outputs: 44864e3c9f44SBill Pijewski * zc_cookie number of bytes written in send stream thus far 44874e3c9f44SBill Pijewski */ 44884e3c9f44SBill Pijewski static int 44894e3c9f44SBill Pijewski zfs_ioc_send_progress(zfs_cmd_t *zc) 44904e3c9f44SBill Pijewski { 44913b2aab18SMatthew Ahrens dsl_pool_t *dp; 44924e3c9f44SBill Pijewski dsl_dataset_t *ds; 44934e3c9f44SBill Pijewski dmu_sendarg_t *dsp = NULL; 44944e3c9f44SBill Pijewski int error; 44954e3c9f44SBill Pijewski 44963b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 44973b2aab18SMatthew Ahrens if (error != 0) 44983b2aab18SMatthew Ahrens return (error); 44993b2aab18SMatthew Ahrens 45003b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); 45013b2aab18SMatthew Ahrens if (error != 0) { 45023b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 45034e3c9f44SBill Pijewski return (error); 45043b2aab18SMatthew Ahrens } 45054e3c9f44SBill Pijewski 45064e3c9f44SBill Pijewski mutex_enter(&ds->ds_sendstream_lock); 45074e3c9f44SBill Pijewski 45084e3c9f44SBill Pijewski /* 45094e3c9f44SBill Pijewski * Iterate over all the send streams currently active on this dataset. 45104e3c9f44SBill Pijewski * If there's one which matches the specified file descriptor _and_ the 45114e3c9f44SBill Pijewski * stream was started by the current process, return the progress of 45124e3c9f44SBill Pijewski * that stream. 45134e3c9f44SBill Pijewski */ 45144e3c9f44SBill Pijewski for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL; 45154e3c9f44SBill Pijewski dsp = list_next(&ds->ds_sendstreams, dsp)) { 45164e3c9f44SBill Pijewski if (dsp->dsa_outfd == zc->zc_cookie && 45174e3c9f44SBill Pijewski dsp->dsa_proc == curproc) 45184e3c9f44SBill Pijewski break; 45194e3c9f44SBill Pijewski } 45204e3c9f44SBill Pijewski 45214e3c9f44SBill Pijewski if (dsp != NULL) 45224e3c9f44SBill Pijewski zc->zc_cookie = *(dsp->dsa_off); 45234e3c9f44SBill Pijewski else 4524be6fd75aSMatthew Ahrens error = SET_ERROR(ENOENT); 45254e3c9f44SBill Pijewski 45264e3c9f44SBill Pijewski mutex_exit(&ds->ds_sendstream_lock); 45274e3c9f44SBill Pijewski dsl_dataset_rele(ds, FTAG); 45283b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 45294e3c9f44SBill Pijewski return (error); 45304e3c9f44SBill Pijewski } 45314e3c9f44SBill Pijewski 4532ea8dc4b6Seschrock static int 4533ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 4534ea8dc4b6Seschrock { 4535ea8dc4b6Seschrock int id, error; 4536ea8dc4b6Seschrock 4537ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 4538ea8dc4b6Seschrock &zc->zc_inject_record); 4539ea8dc4b6Seschrock 4540ea8dc4b6Seschrock if (error == 0) 4541ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 4542ea8dc4b6Seschrock 4543ea8dc4b6Seschrock return (error); 4544ea8dc4b6Seschrock } 4545ea8dc4b6Seschrock 4546ea8dc4b6Seschrock static int 4547ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 4548ea8dc4b6Seschrock { 4549ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 4550ea8dc4b6Seschrock } 4551ea8dc4b6Seschrock 4552ea8dc4b6Seschrock static int 4553ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 4554ea8dc4b6Seschrock { 4555ea8dc4b6Seschrock int id = (int)zc->zc_guid; 4556ea8dc4b6Seschrock int error; 4557ea8dc4b6Seschrock 4558ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 4559ea8dc4b6Seschrock &zc->zc_inject_record); 4560ea8dc4b6Seschrock 4561ea8dc4b6Seschrock zc->zc_guid = id; 4562ea8dc4b6Seschrock 4563ea8dc4b6Seschrock return (error); 4564ea8dc4b6Seschrock } 4565ea8dc4b6Seschrock 4566ea8dc4b6Seschrock static int 4567ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 4568ea8dc4b6Seschrock { 4569ea8dc4b6Seschrock spa_t *spa; 4570ea8dc4b6Seschrock int error; 4571e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 4572ea8dc4b6Seschrock 4573ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 4574ea8dc4b6Seschrock return (error); 4575ea8dc4b6Seschrock 4576e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 4577ea8dc4b6Seschrock &count); 4578ea8dc4b6Seschrock if (error == 0) 4579e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 4580ea8dc4b6Seschrock else 4581e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 4582ea8dc4b6Seschrock 4583ea8dc4b6Seschrock spa_close(spa, FTAG); 4584ea8dc4b6Seschrock 4585ea8dc4b6Seschrock return (error); 4586ea8dc4b6Seschrock } 4587ea8dc4b6Seschrock 4588ea8dc4b6Seschrock static int 4589ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 4590ea8dc4b6Seschrock { 4591ea8dc4b6Seschrock spa_t *spa; 4592ea8dc4b6Seschrock vdev_t *vd; 4593bb8b5132Sek int error; 4594ea8dc4b6Seschrock 4595b87f3af3Sperrin /* 4596b87f3af3Sperrin * On zpool clear we also fix up missing slogs 4597b87f3af3Sperrin */ 4598b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 4599b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 4600b87f3af3Sperrin if (spa == NULL) { 4601b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4602be6fd75aSMatthew Ahrens return (SET_ERROR(EIO)); 4603b87f3af3Sperrin } 4604b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 4605b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 4606b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 4607b87f3af3Sperrin } 4608468c413aSTim Haley spa->spa_last_open_failed = 0; 4609b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4610b87f3af3Sperrin 4611c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 4612468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 4613468c413aSTim Haley } else { 4614468c413aSTim Haley nvlist_t *policy; 4615468c413aSTim Haley nvlist_t *config = NULL; 4616468c413aSTim Haley 4617468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 4618be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 4619468c413aSTim Haley 4620468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 4621468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 4622468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 4623468c413aSTim Haley policy, &config); 4624468c413aSTim Haley if (config != NULL) { 46254b964adaSGeorge Wilson int err; 46264b964adaSGeorge Wilson 46274b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 46284b964adaSGeorge Wilson error = err; 4629468c413aSTim Haley nvlist_free(config); 4630468c413aSTim Haley } 4631468c413aSTim Haley nvlist_free(policy); 4632468c413aSTim Haley } 4633468c413aSTim Haley } 4634468c413aSTim Haley 46353b2aab18SMatthew Ahrens if (error != 0) 4636ea8dc4b6Seschrock return (error); 4637ea8dc4b6Seschrock 46388f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4639ea8dc4b6Seschrock 4640e9dbad6fSeschrock if (zc->zc_guid == 0) { 4641ea8dc4b6Seschrock vd = NULL; 4642c5904d13Seschrock } else { 4643c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 4644fa94a07fSbrendan if (vd == NULL) { 4645e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 4646fa94a07fSbrendan spa_close(spa, FTAG); 4647be6fd75aSMatthew Ahrens return (SET_ERROR(ENODEV)); 4648fa94a07fSbrendan } 4649ea8dc4b6Seschrock } 4650ea8dc4b6Seschrock 4651e14bb325SJeff Bonwick vdev_clear(spa, vd); 4652e14bb325SJeff Bonwick 4653e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 4654ea8dc4b6Seschrock 4655e14bb325SJeff Bonwick /* 4656e14bb325SJeff Bonwick * Resume any suspended I/Os. 4657e14bb325SJeff Bonwick */ 465854d692b7SGeorge Wilson if (zio_resume(spa) != 0) 4659be6fd75aSMatthew Ahrens error = SET_ERROR(EIO); 4660ea8dc4b6Seschrock 4661ea8dc4b6Seschrock spa_close(spa, FTAG); 4662ea8dc4b6Seschrock 466354d692b7SGeorge Wilson return (error); 4664ea8dc4b6Seschrock } 4665ea8dc4b6Seschrock 46664263d13fSGeorge Wilson static int 46674263d13fSGeorge Wilson zfs_ioc_pool_reopen(zfs_cmd_t *zc) 46684263d13fSGeorge Wilson { 46694263d13fSGeorge Wilson spa_t *spa; 46704263d13fSGeorge Wilson int error; 46714263d13fSGeorge Wilson 46724263d13fSGeorge Wilson error = spa_open(zc->zc_name, &spa, FTAG); 46733b2aab18SMatthew Ahrens if (error != 0) 46744263d13fSGeorge Wilson return (error); 46754263d13fSGeorge Wilson 46764263d13fSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4677d6afdce2SGeorge Wilson 4678d6afdce2SGeorge Wilson /* 4679d6afdce2SGeorge Wilson * If a resilver is already in progress then set the 4680d6afdce2SGeorge Wilson * spa_scrub_reopen flag to B_TRUE so that we don't restart 4681d6afdce2SGeorge Wilson * the scan as a side effect of the reopen. Otherwise, let 4682d6afdce2SGeorge Wilson * vdev_open() decided if a resilver is required. 4683d6afdce2SGeorge Wilson */ 4684d6afdce2SGeorge Wilson spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); 46854263d13fSGeorge Wilson vdev_reopen(spa->spa_root_vdev); 4686d6afdce2SGeorge Wilson spa->spa_scrub_reopen = B_FALSE; 4687d6afdce2SGeorge Wilson 46884263d13fSGeorge Wilson (void) spa_vdev_state_exit(spa, NULL, 0); 46894263d13fSGeorge Wilson spa_close(spa, FTAG); 46904263d13fSGeorge Wilson return (0); 46914263d13fSGeorge Wilson } 46923cb34c60Sahrens /* 46933cb34c60Sahrens * inputs: 46943cb34c60Sahrens * zc_name name of filesystem 46953cb34c60Sahrens * zc_value name of origin snapshot 46963cb34c60Sahrens * 4697681d9761SEric Taylor * outputs: 4698681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 46993cb34c60Sahrens */ 470099653d4eSeschrock static int 470199653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 470299653d4eSeschrock { 47030b69c2f0Sahrens char *cp; 47040b69c2f0Sahrens 47050b69c2f0Sahrens /* 47060b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 47070b69c2f0Sahrens * it's easier. 47080b69c2f0Sahrens */ 4709e9dbad6fSeschrock cp = strchr(zc->zc_value, '@'); 47100b69c2f0Sahrens if (cp) 47110b69c2f0Sahrens *cp = '\0'; 4712e9dbad6fSeschrock (void) dmu_objset_find(zc->zc_value, 47133b2aab18SMatthew Ahrens zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS); 4714681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 471599653d4eSeschrock } 471699653d4eSeschrock 471714843421SMatthew Ahrens /* 471814843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 471914843421SMatthew Ahrens * 472014843421SMatthew Ahrens * inputs: 472114843421SMatthew Ahrens * zc_name name of filesystem 472214843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 472314843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 472414843421SMatthew Ahrens * zc_guid RID/UID/GID 472514843421SMatthew Ahrens * 472614843421SMatthew Ahrens * outputs: 472714843421SMatthew Ahrens * zc_cookie property value 472814843421SMatthew Ahrens */ 472914843421SMatthew Ahrens static int 473014843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 473114843421SMatthew Ahrens { 473214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 473314843421SMatthew Ahrens int error; 473414843421SMatthew Ahrens 473514843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 4736be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 473714843421SMatthew Ahrens 47381412a1a2SMark Shellenbaum error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 47393b2aab18SMatthew Ahrens if (error != 0) 474014843421SMatthew Ahrens return (error); 474114843421SMatthew Ahrens 474214843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 474314843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 474414843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 474514843421SMatthew Ahrens 474614843421SMatthew Ahrens return (error); 474714843421SMatthew Ahrens } 474814843421SMatthew Ahrens 474914843421SMatthew Ahrens /* 475014843421SMatthew Ahrens * inputs: 475114843421SMatthew Ahrens * zc_name name of filesystem 475214843421SMatthew Ahrens * zc_cookie zap cursor 475314843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 475414843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 475514843421SMatthew Ahrens * 475614843421SMatthew Ahrens * outputs: 475714843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 475814843421SMatthew Ahrens * zc_cookie zap cursor 475914843421SMatthew Ahrens */ 476014843421SMatthew Ahrens static int 476114843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 476214843421SMatthew Ahrens { 476314843421SMatthew Ahrens zfsvfs_t *zfsvfs; 4764eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 476514843421SMatthew Ahrens 4766eeb85002STim Haley if (bufsize <= 0) 4767be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 4768eeb85002STim Haley 47691412a1a2SMark Shellenbaum int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 47703b2aab18SMatthew Ahrens if (error != 0) 477114843421SMatthew Ahrens return (error); 477214843421SMatthew Ahrens 477314843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 477414843421SMatthew Ahrens 477514843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 477614843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 477714843421SMatthew Ahrens 477814843421SMatthew Ahrens if (error == 0) { 477914843421SMatthew Ahrens error = xcopyout(buf, 478014843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 478114843421SMatthew Ahrens zc->zc_nvlist_dst_size); 478214843421SMatthew Ahrens } 478314843421SMatthew Ahrens kmem_free(buf, bufsize); 478414843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 478514843421SMatthew Ahrens 478614843421SMatthew Ahrens return (error); 478714843421SMatthew Ahrens } 478814843421SMatthew Ahrens 478914843421SMatthew Ahrens /* 479014843421SMatthew Ahrens * inputs: 479114843421SMatthew Ahrens * zc_name name of filesystem 479214843421SMatthew Ahrens * 479314843421SMatthew Ahrens * outputs: 479414843421SMatthew Ahrens * none 479514843421SMatthew Ahrens */ 479614843421SMatthew Ahrens static int 479714843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 479814843421SMatthew Ahrens { 479914843421SMatthew Ahrens objset_t *os; 48001195e687SMark J Musante int error = 0; 480114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 480214843421SMatthew Ahrens 480314843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 4804503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 480514843421SMatthew Ahrens /* 480614843421SMatthew Ahrens * If userused is not enabled, it may be because the 480714843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 480814843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 480914843421SMatthew Ahrens */ 4810503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 481191948b51SKeith M Wesolowski if (error == 0) { 481291948b51SKeith M Wesolowski dmu_objset_refresh_ownership(zfsvfs->z_os, 481391948b51SKeith M Wesolowski zfsvfs); 4814503ad85cSMatthew Ahrens error = zfs_resume_fs(zfsvfs, zc->zc_name); 481591948b51SKeith M Wesolowski } 481614843421SMatthew Ahrens } 481714843421SMatthew Ahrens if (error == 0) 481814843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 481914843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 482014843421SMatthew Ahrens } else { 4821503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 4822503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 48233b2aab18SMatthew Ahrens if (error != 0) 482414843421SMatthew Ahrens return (error); 482514843421SMatthew Ahrens 482614843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 4827503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 482814843421SMatthew Ahrens } 482914843421SMatthew Ahrens 483014843421SMatthew Ahrens return (error); 483114843421SMatthew Ahrens } 483214843421SMatthew Ahrens 4833ecd6cf80Smarks /* 4834ecd6cf80Smarks * We don't want to have a hard dependency 4835ecd6cf80Smarks * against some special symbols in sharefs 4836da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 4837ecd6cf80Smarks * the first file system is shared. 4838da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 4839ecd6cf80Smarks */ 4840da6c28aaSamw int (*znfsexport_fs)(void *arg); 4841ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 4842da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 4843da6c28aaSamw 4844da6c28aaSamw int zfs_nfsshare_inited; 4845da6c28aaSamw int zfs_smbshare_inited; 4846ecd6cf80Smarks 4847ecd6cf80Smarks ddi_modhandle_t nfs_mod; 4848ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 4849da6c28aaSamw ddi_modhandle_t smbsrv_mod; 4850ecd6cf80Smarks kmutex_t zfs_share_lock; 4851ecd6cf80Smarks 4852da6c28aaSamw static int 4853da6c28aaSamw zfs_init_sharefs() 4854da6c28aaSamw { 4855da6c28aaSamw int error; 4856da6c28aaSamw 4857da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 4858da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 4859da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 4860da6c28aaSamw ddi_modopen("fs/sharefs", 4861da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4862be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4863da6c28aaSamw } 4864da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 4865da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 4866da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 4867be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4868da6c28aaSamw } 4869da6c28aaSamw return (0); 4870da6c28aaSamw } 4871da6c28aaSamw 4872ecd6cf80Smarks static int 4873ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 4874ecd6cf80Smarks { 4875ecd6cf80Smarks int error; 4876ecd6cf80Smarks int opcode; 4877ecd6cf80Smarks 4878da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4879da6c28aaSamw case ZFS_SHARE_NFS: 4880da6c28aaSamw case ZFS_UNSHARE_NFS: 4881da6c28aaSamw if (zfs_nfsshare_inited == 0) { 4882da6c28aaSamw mutex_enter(&zfs_share_lock); 4883da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 4884da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4885da6c28aaSamw mutex_exit(&zfs_share_lock); 4886be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4887da6c28aaSamw } 4888da6c28aaSamw if (znfsexport_fs == NULL && 4889da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 4890da6c28aaSamw ddi_modsym(nfs_mod, 4891da6c28aaSamw "nfs_export", &error)) == NULL)) { 4892da6c28aaSamw mutex_exit(&zfs_share_lock); 4893be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4894da6c28aaSamw } 4895da6c28aaSamw error = zfs_init_sharefs(); 48963b2aab18SMatthew Ahrens if (error != 0) { 4897da6c28aaSamw mutex_exit(&zfs_share_lock); 4898be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4899da6c28aaSamw } 4900da6c28aaSamw zfs_nfsshare_inited = 1; 4901ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4902ecd6cf80Smarks } 4903da6c28aaSamw break; 4904da6c28aaSamw case ZFS_SHARE_SMB: 4905da6c28aaSamw case ZFS_UNSHARE_SMB: 4906da6c28aaSamw if (zfs_smbshare_inited == 0) { 4907da6c28aaSamw mutex_enter(&zfs_share_lock); 4908da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 4909da6c28aaSamw ddi_modopen("drv/smbsrv", 4910da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 4911da6c28aaSamw mutex_exit(&zfs_share_lock); 4912be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4913da6c28aaSamw } 4914da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 4915da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 4916faa1795aSjb "smb_server_share", &error)) == NULL)) { 4917da6c28aaSamw mutex_exit(&zfs_share_lock); 4918be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4919da6c28aaSamw } 4920da6c28aaSamw error = zfs_init_sharefs(); 49213b2aab18SMatthew Ahrens if (error != 0) { 4922da6c28aaSamw mutex_exit(&zfs_share_lock); 4923be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 4924da6c28aaSamw } 4925da6c28aaSamw zfs_smbshare_inited = 1; 4926ecd6cf80Smarks mutex_exit(&zfs_share_lock); 4927ecd6cf80Smarks } 4928da6c28aaSamw break; 4929da6c28aaSamw default: 4930be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 4931da6c28aaSamw } 4932ecd6cf80Smarks 4933da6c28aaSamw switch (zc->zc_share.z_sharetype) { 4934da6c28aaSamw case ZFS_SHARE_NFS: 4935da6c28aaSamw case ZFS_UNSHARE_NFS: 4936da6c28aaSamw if (error = 4937da6c28aaSamw znfsexport_fs((void *) 4938da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 4939da6c28aaSamw return (error); 4940da6c28aaSamw break; 4941da6c28aaSamw case ZFS_SHARE_SMB: 4942da6c28aaSamw case ZFS_UNSHARE_SMB: 4943da6c28aaSamw if (error = zsmbexport_fs((void *) 4944da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 4945da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 4946743a77edSAlan Wright B_TRUE: B_FALSE)) { 4947da6c28aaSamw return (error); 4948ecd6cf80Smarks } 4949da6c28aaSamw break; 4950ecd6cf80Smarks } 4951ecd6cf80Smarks 4952da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 4953da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 4954ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 4955ecd6cf80Smarks 4956da6c28aaSamw /* 4957da6c28aaSamw * Add or remove share from sharetab 4958da6c28aaSamw */ 4959ecd6cf80Smarks error = zshare_fs(opcode, 4960ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 4961ecd6cf80Smarks zc->zc_share.z_sharemax); 4962ecd6cf80Smarks 4963ecd6cf80Smarks return (error); 4964ecd6cf80Smarks 4965ecd6cf80Smarks } 4966ecd6cf80Smarks 4967743a77edSAlan Wright ace_t full_access[] = { 4968743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 4969743a77edSAlan Wright }; 4970743a77edSAlan Wright 497199d5e173STim Haley /* 497299d5e173STim Haley * inputs: 497399d5e173STim Haley * zc_name name of containing filesystem 497499d5e173STim Haley * zc_obj object # beyond which we want next in-use object # 497599d5e173STim Haley * 497699d5e173STim Haley * outputs: 497799d5e173STim Haley * zc_obj next in-use object # 497899d5e173STim Haley */ 497999d5e173STim Haley static int 498099d5e173STim Haley zfs_ioc_next_obj(zfs_cmd_t *zc) 498199d5e173STim Haley { 498299d5e173STim Haley objset_t *os = NULL; 498399d5e173STim Haley int error; 498499d5e173STim Haley 498599d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &os); 49863b2aab18SMatthew Ahrens if (error != 0) 498799d5e173STim Haley return (error); 498899d5e173STim Haley 498999d5e173STim Haley error = dmu_object_next(os, &zc->zc_obj, B_FALSE, 4990c1379625SJustin T. Gibbs dsl_dataset_phys(os->os_dsl_dataset)->ds_prev_snap_txg); 499199d5e173STim Haley 499299d5e173STim Haley dmu_objset_rele(os, FTAG); 499399d5e173STim Haley return (error); 499499d5e173STim Haley } 499599d5e173STim Haley 499699d5e173STim Haley /* 499799d5e173STim Haley * inputs: 499899d5e173STim Haley * zc_name name of filesystem 499999d5e173STim Haley * zc_value prefix name for snapshot 500099d5e173STim Haley * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 500199d5e173STim Haley * 500299d5e173STim Haley * outputs: 50034445fffbSMatthew Ahrens * zc_value short name of new snapshot 500499d5e173STim Haley */ 500599d5e173STim Haley static int 500699d5e173STim Haley zfs_ioc_tmp_snapshot(zfs_cmd_t *zc) 500799d5e173STim Haley { 500899d5e173STim Haley char *snap_name; 50093b2aab18SMatthew Ahrens char *hold_name; 501099d5e173STim Haley int error; 50113b2aab18SMatthew Ahrens minor_t minor; 501299d5e173STim Haley 50133b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor); 50143b2aab18SMatthew Ahrens if (error != 0) 501599d5e173STim Haley return (error); 501699d5e173STim Haley 50173b2aab18SMatthew Ahrens snap_name = kmem_asprintf("%s-%016llx", zc->zc_value, 50183b2aab18SMatthew Ahrens (u_longlong_t)ddi_get_lbolt64()); 50193b2aab18SMatthew Ahrens hold_name = kmem_asprintf("%%%s", zc->zc_value); 50203b2aab18SMatthew Ahrens 50213b2aab18SMatthew Ahrens error = dsl_dataset_snapshot_tmp(zc->zc_name, snap_name, minor, 50223b2aab18SMatthew Ahrens hold_name); 50233b2aab18SMatthew Ahrens if (error == 0) 50243b2aab18SMatthew Ahrens (void) strcpy(zc->zc_value, snap_name); 502599d5e173STim Haley strfree(snap_name); 50263b2aab18SMatthew Ahrens strfree(hold_name); 50273b2aab18SMatthew Ahrens zfs_onexit_fd_rele(zc->zc_cleanup_fd); 50283b2aab18SMatthew Ahrens return (error); 502999d5e173STim Haley } 503099d5e173STim Haley 503199d5e173STim Haley /* 503299d5e173STim Haley * inputs: 503399d5e173STim Haley * zc_name name of "to" snapshot 503499d5e173STim Haley * zc_value name of "from" snapshot 503599d5e173STim Haley * zc_cookie file descriptor to write diff data on 503699d5e173STim Haley * 503799d5e173STim Haley * outputs: 503899d5e173STim Haley * dmu_diff_record_t's to the file descriptor 503999d5e173STim Haley */ 504099d5e173STim Haley static int 504199d5e173STim Haley zfs_ioc_diff(zfs_cmd_t *zc) 504299d5e173STim Haley { 504399d5e173STim Haley file_t *fp; 504499d5e173STim Haley offset_t off; 504599d5e173STim Haley int error; 504699d5e173STim Haley 504799d5e173STim Haley fp = getf(zc->zc_cookie); 50483b2aab18SMatthew Ahrens if (fp == NULL) 5049be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 505099d5e173STim Haley 505199d5e173STim Haley off = fp->f_offset; 505299d5e173STim Haley 50533b2aab18SMatthew Ahrens error = dmu_diff(zc->zc_name, zc->zc_value, fp->f_vnode, &off); 505499d5e173STim Haley 505599d5e173STim Haley if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 505699d5e173STim Haley fp->f_offset = off; 505799d5e173STim Haley releasef(zc->zc_cookie); 505899d5e173STim Haley 505999d5e173STim Haley return (error); 506099d5e173STim Haley } 506199d5e173STim Haley 5062743a77edSAlan Wright /* 5063743a77edSAlan Wright * Remove all ACL files in shares dir 5064743a77edSAlan Wright */ 5065743a77edSAlan Wright static int 5066743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 5067743a77edSAlan Wright { 5068743a77edSAlan Wright zap_cursor_t zc; 5069743a77edSAlan Wright zap_attribute_t zap; 5070743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 5071743a77edSAlan Wright int error; 5072743a77edSAlan Wright 5073743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 5074743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 5075743a77edSAlan Wright zap_cursor_advance(&zc)) { 5076743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 5077743a77edSAlan Wright NULL, 0)) != 0) 5078743a77edSAlan Wright break; 5079743a77edSAlan Wright } 5080743a77edSAlan Wright zap_cursor_fini(&zc); 5081743a77edSAlan Wright return (error); 5082743a77edSAlan Wright } 5083743a77edSAlan Wright 5084743a77edSAlan Wright static int 5085743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 5086743a77edSAlan Wright { 5087743a77edSAlan Wright vnode_t *vp; 5088743a77edSAlan Wright znode_t *dzp; 5089743a77edSAlan Wright vnode_t *resourcevp = NULL; 5090743a77edSAlan Wright znode_t *sharedir; 5091743a77edSAlan Wright zfsvfs_t *zfsvfs; 5092743a77edSAlan Wright nvlist_t *nvlist; 5093743a77edSAlan Wright char *src, *target; 5094743a77edSAlan Wright vattr_t vattr; 5095743a77edSAlan Wright vsecattr_t vsec; 5096743a77edSAlan Wright int error = 0; 5097743a77edSAlan Wright 5098743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 5099743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 5100743a77edSAlan Wright return (error); 5101743a77edSAlan Wright 5102743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 5103743a77edSAlan Wright 5104743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 5105743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 5106743a77edSAlan Wright zc->zc_name) != 0)) { 5107743a77edSAlan Wright VN_RELE(vp); 5108be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5109743a77edSAlan Wright } 5110743a77edSAlan Wright 5111743a77edSAlan Wright dzp = VTOZ(vp); 5112743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 5113743a77edSAlan Wright ZFS_ENTER(zfsvfs); 5114743a77edSAlan Wright 51159e1320c0SMark Shellenbaum /* 51169e1320c0SMark Shellenbaum * Create share dir if its missing. 51179e1320c0SMark Shellenbaum */ 51189e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 51199e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 51209e1320c0SMark Shellenbaum dmu_tx_t *tx; 51219e1320c0SMark Shellenbaum 51229e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 51239e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 51249e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 51259e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 51269e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 51273b2aab18SMatthew Ahrens if (error != 0) { 51289e1320c0SMark Shellenbaum dmu_tx_abort(tx); 51299e1320c0SMark Shellenbaum } else { 51309e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 51319e1320c0SMark Shellenbaum dmu_tx_commit(tx); 51329e1320c0SMark Shellenbaum } 51333b2aab18SMatthew Ahrens if (error != 0) { 51349e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 51359e1320c0SMark Shellenbaum VN_RELE(vp); 51369e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 51379e1320c0SMark Shellenbaum return (error); 51389e1320c0SMark Shellenbaum } 51399e1320c0SMark Shellenbaum } 51409e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 51419e1320c0SMark Shellenbaum 51429e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 5143743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 51449e1320c0SMark Shellenbaum VN_RELE(vp); 5145743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5146743a77edSAlan Wright return (error); 5147743a77edSAlan Wright } 5148743a77edSAlan Wright 5149743a77edSAlan Wright switch (zc->zc_cookie) { 5150743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 5151743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 5152743a77edSAlan Wright vattr.va_type = VREG; 5153743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 5154743a77edSAlan Wright vattr.va_uid = 0; 5155743a77edSAlan Wright vattr.va_gid = 0; 5156743a77edSAlan Wright 5157743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 5158743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 5159743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 5160743a77edSAlan Wright vsec.vsa_aclcnt = 1; 5161743a77edSAlan Wright 5162743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 5163743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 5164743a77edSAlan Wright if (resourcevp) 5165743a77edSAlan Wright VN_RELE(resourcevp); 5166743a77edSAlan Wright break; 5167743a77edSAlan Wright 5168743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 5169743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 5170743a77edSAlan Wright NULL, 0); 5171743a77edSAlan Wright break; 5172743a77edSAlan Wright 5173743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 5174743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 5175478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 5176743a77edSAlan Wright VN_RELE(vp); 51778f5190a5SDan McDonald VN_RELE(ZTOV(sharedir)); 5178743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5179743a77edSAlan Wright return (error); 5180743a77edSAlan Wright } 5181743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 5182743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 5183743a77edSAlan Wright &target)) { 5184743a77edSAlan Wright VN_RELE(vp); 518589459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 5186743a77edSAlan Wright ZFS_EXIT(zfsvfs); 51871195e687SMark J Musante nvlist_free(nvlist); 5188743a77edSAlan Wright return (error); 5189743a77edSAlan Wright } 5190743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 5191743a77edSAlan Wright kcred, NULL, 0); 5192743a77edSAlan Wright nvlist_free(nvlist); 5193743a77edSAlan Wright break; 5194743a77edSAlan Wright 5195743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 5196743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 5197743a77edSAlan Wright break; 5198743a77edSAlan Wright 5199743a77edSAlan Wright default: 5200be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 5201743a77edSAlan Wright break; 5202743a77edSAlan Wright } 5203743a77edSAlan Wright 5204743a77edSAlan Wright VN_RELE(vp); 5205743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 5206743a77edSAlan Wright 5207743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5208743a77edSAlan Wright 5209743a77edSAlan Wright return (error); 5210743a77edSAlan Wright } 5211743a77edSAlan Wright 5212842727c2SChris Kirby /* 52133b2aab18SMatthew Ahrens * innvl: { 52143b2aab18SMatthew Ahrens * "holds" -> { snapname -> holdname (string), ... } 52153b2aab18SMatthew Ahrens * (optional) "cleanup_fd" -> fd (int32) 52163b2aab18SMatthew Ahrens * } 5217842727c2SChris Kirby * 52183b2aab18SMatthew Ahrens * outnvl: { 52193b2aab18SMatthew Ahrens * snapname -> error value (int32) 52203b2aab18SMatthew Ahrens * ... 52213b2aab18SMatthew Ahrens * } 5222842727c2SChris Kirby */ 52233b2aab18SMatthew Ahrens /* ARGSUSED */ 5224842727c2SChris Kirby static int 52253b2aab18SMatthew Ahrens zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) 5226842727c2SChris Kirby { 5227752fd8daSJosef 'Jeff' Sipek nvpair_t *pair; 52283b2aab18SMatthew Ahrens nvlist_t *holds; 52293b2aab18SMatthew Ahrens int cleanup_fd = -1; 5230a7f53a56SChris Kirby int error; 5231a7f53a56SChris Kirby minor_t minor = 0; 5232842727c2SChris Kirby 52333b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(args, "holds", &holds); 52343b2aab18SMatthew Ahrens if (error != 0) 5235be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5236a7f53a56SChris Kirby 5237752fd8daSJosef 'Jeff' Sipek /* make sure the user didn't pass us any invalid (empty) tags */ 5238752fd8daSJosef 'Jeff' Sipek for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 5239752fd8daSJosef 'Jeff' Sipek pair = nvlist_next_nvpair(holds, pair)) { 5240752fd8daSJosef 'Jeff' Sipek char *htag; 5241752fd8daSJosef 'Jeff' Sipek 5242752fd8daSJosef 'Jeff' Sipek error = nvpair_value_string(pair, &htag); 5243752fd8daSJosef 'Jeff' Sipek if (error != 0) 5244752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(error)); 5245752fd8daSJosef 'Jeff' Sipek 5246752fd8daSJosef 'Jeff' Sipek if (strlen(htag) == 0) 5247752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(EINVAL)); 5248752fd8daSJosef 'Jeff' Sipek } 5249752fd8daSJosef 'Jeff' Sipek 52503b2aab18SMatthew Ahrens if (nvlist_lookup_int32(args, "cleanup_fd", &cleanup_fd) == 0) { 52513b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(cleanup_fd, &minor); 52523b2aab18SMatthew Ahrens if (error != 0) 5253a7f53a56SChris Kirby return (error); 5254a7f53a56SChris Kirby } 5255a7f53a56SChris Kirby 52563b2aab18SMatthew Ahrens error = dsl_dataset_user_hold(holds, minor, errlist); 52573b2aab18SMatthew Ahrens if (minor != 0) 52583b2aab18SMatthew Ahrens zfs_onexit_fd_rele(cleanup_fd); 5259a7f53a56SChris Kirby return (error); 5260842727c2SChris Kirby } 5261842727c2SChris Kirby 5262842727c2SChris Kirby /* 52633b2aab18SMatthew Ahrens * innvl is not used. 5264842727c2SChris Kirby * 52653b2aab18SMatthew Ahrens * outnvl: { 52663b2aab18SMatthew Ahrens * holdname -> time added (uint64 seconds since epoch) 52673b2aab18SMatthew Ahrens * ... 52683b2aab18SMatthew Ahrens * } 5269842727c2SChris Kirby */ 52703b2aab18SMatthew Ahrens /* ARGSUSED */ 5271842727c2SChris Kirby static int 52723b2aab18SMatthew Ahrens zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) 5273842727c2SChris Kirby { 52743b2aab18SMatthew Ahrens return (dsl_dataset_get_holds(snapname, outnvl)); 5275842727c2SChris Kirby } 5276842727c2SChris Kirby 5277842727c2SChris Kirby /* 52783b2aab18SMatthew Ahrens * innvl: { 52793b2aab18SMatthew Ahrens * snapname -> { holdname, ... } 52803b2aab18SMatthew Ahrens * ... 52813b2aab18SMatthew Ahrens * } 5282842727c2SChris Kirby * 52833b2aab18SMatthew Ahrens * outnvl: { 52843b2aab18SMatthew Ahrens * snapname -> error value (int32) 52853b2aab18SMatthew Ahrens * ... 52863b2aab18SMatthew Ahrens * } 5287842727c2SChris Kirby */ 52883b2aab18SMatthew Ahrens /* ARGSUSED */ 5289842727c2SChris Kirby static int 52903b2aab18SMatthew Ahrens zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) 5291842727c2SChris Kirby { 52923b2aab18SMatthew Ahrens return (dsl_dataset_user_release(holds, errlist)); 5293842727c2SChris Kirby } 5294842727c2SChris Kirby 529519b94df9SMatthew Ahrens /* 529619b94df9SMatthew Ahrens * inputs: 529719b94df9SMatthew Ahrens * zc_name name of new filesystem or snapshot 529819b94df9SMatthew Ahrens * zc_value full name of old snapshot 529919b94df9SMatthew Ahrens * 530019b94df9SMatthew Ahrens * outputs: 530119b94df9SMatthew Ahrens * zc_cookie space in bytes 530219b94df9SMatthew Ahrens * zc_objset_type compressed space in bytes 530319b94df9SMatthew Ahrens * zc_perm_action uncompressed space in bytes 530419b94df9SMatthew Ahrens */ 530519b94df9SMatthew Ahrens static int 530619b94df9SMatthew Ahrens zfs_ioc_space_written(zfs_cmd_t *zc) 530719b94df9SMatthew Ahrens { 530819b94df9SMatthew Ahrens int error; 53093b2aab18SMatthew Ahrens dsl_pool_t *dp; 531019b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 531119b94df9SMatthew Ahrens 53123b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 531319b94df9SMatthew Ahrens if (error != 0) 531419b94df9SMatthew Ahrens return (error); 53153b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &new); 53163b2aab18SMatthew Ahrens if (error != 0) { 53173b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 53183b2aab18SMatthew Ahrens return (error); 53193b2aab18SMatthew Ahrens } 53203b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_value, FTAG, &old); 532119b94df9SMatthew Ahrens if (error != 0) { 532219b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53233b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 532419b94df9SMatthew Ahrens return (error); 532519b94df9SMatthew Ahrens } 532619b94df9SMatthew Ahrens 532719b94df9SMatthew Ahrens error = dsl_dataset_space_written(old, new, &zc->zc_cookie, 532819b94df9SMatthew Ahrens &zc->zc_objset_type, &zc->zc_perm_action); 532919b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 533019b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53313b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 533219b94df9SMatthew Ahrens return (error); 533319b94df9SMatthew Ahrens } 53343b2aab18SMatthew Ahrens 533519b94df9SMatthew Ahrens /* 53364445fffbSMatthew Ahrens * innvl: { 53374445fffbSMatthew Ahrens * "firstsnap" -> snapshot name 53384445fffbSMatthew Ahrens * } 533919b94df9SMatthew Ahrens * 53404445fffbSMatthew Ahrens * outnvl: { 53414445fffbSMatthew Ahrens * "used" -> space in bytes 53424445fffbSMatthew Ahrens * "compressed" -> compressed space in bytes 53434445fffbSMatthew Ahrens * "uncompressed" -> uncompressed space in bytes 53444445fffbSMatthew Ahrens * } 534519b94df9SMatthew Ahrens */ 534619b94df9SMatthew Ahrens static int 53474445fffbSMatthew Ahrens zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) 534819b94df9SMatthew Ahrens { 534919b94df9SMatthew Ahrens int error; 53503b2aab18SMatthew Ahrens dsl_pool_t *dp; 535119b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 53524445fffbSMatthew Ahrens char *firstsnap; 53534445fffbSMatthew Ahrens uint64_t used, comp, uncomp; 535419b94df9SMatthew Ahrens 53554445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) 5356be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 53574445fffbSMatthew Ahrens 53583b2aab18SMatthew Ahrens error = dsl_pool_hold(lastsnap, FTAG, &dp); 535919b94df9SMatthew Ahrens if (error != 0) 536019b94df9SMatthew Ahrens return (error); 53613b2aab18SMatthew Ahrens 53623b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, lastsnap, FTAG, &new); 536324218bebSAndriy Gapon if (error == 0 && !new->ds_is_snapshot) { 536424218bebSAndriy Gapon dsl_dataset_rele(new, FTAG); 536524218bebSAndriy Gapon error = SET_ERROR(EINVAL); 536624218bebSAndriy Gapon } 53673b2aab18SMatthew Ahrens if (error != 0) { 53683b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 53693b2aab18SMatthew Ahrens return (error); 53703b2aab18SMatthew Ahrens } 53713b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, firstsnap, FTAG, &old); 537224218bebSAndriy Gapon if (error == 0 && !old->ds_is_snapshot) { 537324218bebSAndriy Gapon dsl_dataset_rele(old, FTAG); 537424218bebSAndriy Gapon error = SET_ERROR(EINVAL); 537524218bebSAndriy Gapon } 537619b94df9SMatthew Ahrens if (error != 0) { 537719b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53783b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 537919b94df9SMatthew Ahrens return (error); 538019b94df9SMatthew Ahrens } 538119b94df9SMatthew Ahrens 53824445fffbSMatthew Ahrens error = dsl_dataset_space_wouldfree(old, new, &used, &comp, &uncomp); 538319b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 538419b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 53853b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 53864445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "used", used); 53874445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "compressed", comp); 53884445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "uncompressed", uncomp); 538919b94df9SMatthew Ahrens return (error); 539019b94df9SMatthew Ahrens } 539119b94df9SMatthew Ahrens 5392ecd6cf80Smarks /* 53934445fffbSMatthew Ahrens * innvl: { 53944445fffbSMatthew Ahrens * "fd" -> file descriptor to write stream to (int32) 53954445fffbSMatthew Ahrens * (optional) "fromsnap" -> full snap name to send an incremental from 5396b5152584SMatthew Ahrens * (optional) "largeblockok" -> (value ignored) 5397b5152584SMatthew Ahrens * indicates that blocks > 128KB are permitted 53985d7b4d43SMatthew Ahrens * (optional) "embedok" -> (value ignored) 53995d7b4d43SMatthew Ahrens * presence indicates DRR_WRITE_EMBEDDED records are permitted 54009c3fd121SMatthew Ahrens * (optional) "resume_object" and "resume_offset" -> (uint64) 54019c3fd121SMatthew Ahrens * if present, resume send stream from specified object and offset. 54024445fffbSMatthew Ahrens * } 54034445fffbSMatthew Ahrens * 54044445fffbSMatthew Ahrens * outnvl is unused 5405ecd6cf80Smarks */ 54064445fffbSMatthew Ahrens /* ARGSUSED */ 54074445fffbSMatthew Ahrens static int 54084445fffbSMatthew Ahrens zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 54094445fffbSMatthew Ahrens { 54104445fffbSMatthew Ahrens int error; 54114445fffbSMatthew Ahrens offset_t off; 54123b2aab18SMatthew Ahrens char *fromname = NULL; 54134445fffbSMatthew Ahrens int fd; 5414b5152584SMatthew Ahrens boolean_t largeblockok; 54155d7b4d43SMatthew Ahrens boolean_t embedok; 54169c3fd121SMatthew Ahrens uint64_t resumeobj = 0; 54179c3fd121SMatthew Ahrens uint64_t resumeoff = 0; 54184445fffbSMatthew Ahrens 54194445fffbSMatthew Ahrens error = nvlist_lookup_int32(innvl, "fd", &fd); 54204445fffbSMatthew Ahrens if (error != 0) 5421be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 54224445fffbSMatthew Ahrens 54233b2aab18SMatthew Ahrens (void) nvlist_lookup_string(innvl, "fromsnap", &fromname); 54244445fffbSMatthew Ahrens 5425b5152584SMatthew Ahrens largeblockok = nvlist_exists(innvl, "largeblockok"); 54265d7b4d43SMatthew Ahrens embedok = nvlist_exists(innvl, "embedok"); 54275d7b4d43SMatthew Ahrens 54289c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); 54299c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff); 54309c3fd121SMatthew Ahrens 54314445fffbSMatthew Ahrens file_t *fp = getf(fd); 54323b2aab18SMatthew Ahrens if (fp == NULL) 5433be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 54344445fffbSMatthew Ahrens 54354445fffbSMatthew Ahrens off = fp->f_offset; 54369c3fd121SMatthew Ahrens error = dmu_send(snapname, fromname, embedok, largeblockok, fd, 54379c3fd121SMatthew Ahrens resumeobj, resumeoff, fp->f_vnode, &off); 54384445fffbSMatthew Ahrens 54394445fffbSMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 54404445fffbSMatthew Ahrens fp->f_offset = off; 54414445fffbSMatthew Ahrens releasef(fd); 54424445fffbSMatthew Ahrens return (error); 54434445fffbSMatthew Ahrens } 54444445fffbSMatthew Ahrens 54454445fffbSMatthew Ahrens /* 54464445fffbSMatthew Ahrens * Determine approximately how large a zfs send stream will be -- the number 54474445fffbSMatthew Ahrens * of bytes that will be written to the fd supplied to zfs_ioc_send_new(). 54484445fffbSMatthew Ahrens * 54494445fffbSMatthew Ahrens * innvl: { 5450643da460SMax Grossman * (optional) "from" -> full snap or bookmark name to send an incremental 5451643da460SMax Grossman * from 54524445fffbSMatthew Ahrens * } 54534445fffbSMatthew Ahrens * 54544445fffbSMatthew Ahrens * outnvl: { 54554445fffbSMatthew Ahrens * "space" -> bytes of space (uint64) 54564445fffbSMatthew Ahrens * } 54574445fffbSMatthew Ahrens */ 54584445fffbSMatthew Ahrens static int 54594445fffbSMatthew Ahrens zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 54604445fffbSMatthew Ahrens { 54613b2aab18SMatthew Ahrens dsl_pool_t *dp; 54623b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 54634445fffbSMatthew Ahrens int error; 54644445fffbSMatthew Ahrens char *fromname; 54654445fffbSMatthew Ahrens uint64_t space; 54664445fffbSMatthew Ahrens 54673b2aab18SMatthew Ahrens error = dsl_pool_hold(snapname, FTAG, &dp); 54683b2aab18SMatthew Ahrens if (error != 0) 54693b2aab18SMatthew Ahrens return (error); 54703b2aab18SMatthew Ahrens 54713b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, snapname, FTAG, &tosnap); 54723b2aab18SMatthew Ahrens if (error != 0) { 54733b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 54744445fffbSMatthew Ahrens return (error); 54753b2aab18SMatthew Ahrens } 54764445fffbSMatthew Ahrens 5477643da460SMax Grossman error = nvlist_lookup_string(innvl, "from", &fromname); 54784445fffbSMatthew Ahrens if (error == 0) { 5479643da460SMax Grossman if (strchr(fromname, '@') != NULL) { 5480643da460SMax Grossman /* 5481643da460SMax Grossman * If from is a snapshot, hold it and use the more 5482643da460SMax Grossman * efficient dmu_send_estimate to estimate send space 5483643da460SMax Grossman * size using deadlists. 5484643da460SMax Grossman */ 5485643da460SMax Grossman dsl_dataset_t *fromsnap; 5486643da460SMax Grossman error = dsl_dataset_hold(dp, fromname, FTAG, &fromsnap); 5487643da460SMax Grossman if (error != 0) 5488643da460SMax Grossman goto out; 5489643da460SMax Grossman error = dmu_send_estimate(tosnap, fromsnap, &space); 5490643da460SMax Grossman dsl_dataset_rele(fromsnap, FTAG); 5491643da460SMax Grossman } else if (strchr(fromname, '#') != NULL) { 5492643da460SMax Grossman /* 5493643da460SMax Grossman * If from is a bookmark, fetch the creation TXG of the 5494643da460SMax Grossman * snapshot it was created from and use that to find 5495643da460SMax Grossman * blocks that were born after it. 5496643da460SMax Grossman */ 5497643da460SMax Grossman zfs_bookmark_phys_t frombm; 5498643da460SMax Grossman 5499643da460SMax Grossman error = dsl_bookmark_lookup(dp, fromname, tosnap, 5500643da460SMax Grossman &frombm); 5501643da460SMax Grossman if (error != 0) 5502643da460SMax Grossman goto out; 5503643da460SMax Grossman error = dmu_send_estimate_from_txg(tosnap, 5504643da460SMax Grossman frombm.zbm_creation_txg, &space); 5505643da460SMax Grossman } else { 5506643da460SMax Grossman /* 5507643da460SMax Grossman * from is not properly formatted as a snapshot or 5508643da460SMax Grossman * bookmark 5509643da460SMax Grossman */ 5510643da460SMax Grossman error = SET_ERROR(EINVAL); 5511643da460SMax Grossman goto out; 55124445fffbSMatthew Ahrens } 5513643da460SMax Grossman } else { 5514643da460SMax Grossman // If estimating the size of a full send, use dmu_send_estimate 5515643da460SMax Grossman error = dmu_send_estimate(tosnap, NULL, &space); 55164445fffbSMatthew Ahrens } 55174445fffbSMatthew Ahrens 55184445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "space", space); 55194445fffbSMatthew Ahrens 5520643da460SMax Grossman out: 55213b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 55223b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 55234445fffbSMatthew Ahrens return (error); 55244445fffbSMatthew Ahrens } 55254445fffbSMatthew Ahrens 55264445fffbSMatthew Ahrens static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; 55274445fffbSMatthew Ahrens 55284445fffbSMatthew Ahrens static void 55294445fffbSMatthew Ahrens zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 55304445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 55314445fffbSMatthew Ahrens boolean_t log_history, zfs_ioc_poolcheck_t pool_check) 55324445fffbSMatthew Ahrens { 55334445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 55344445fffbSMatthew Ahrens 55354445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 55364445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 55374445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 55384445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 55394445fffbSMatthew Ahrens 55404445fffbSMatthew Ahrens vec->zvec_legacy_func = func; 55414445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 55424445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 55434445fffbSMatthew Ahrens vec->zvec_allow_log = log_history; 55444445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 55454445fffbSMatthew Ahrens } 55464445fffbSMatthew Ahrens 55474445fffbSMatthew Ahrens /* 55484445fffbSMatthew Ahrens * See the block comment at the beginning of this file for details on 55494445fffbSMatthew Ahrens * each argument to this function. 55504445fffbSMatthew Ahrens */ 55514445fffbSMatthew Ahrens static void 55524445fffbSMatthew Ahrens zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, 55534445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 55544445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, 55554445fffbSMatthew Ahrens boolean_t allow_log) 55564445fffbSMatthew Ahrens { 55574445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 55584445fffbSMatthew Ahrens 55594445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 55604445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 55614445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 55624445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 55634445fffbSMatthew Ahrens 55644445fffbSMatthew Ahrens /* if we are logging, the name must be valid */ 55654445fffbSMatthew Ahrens ASSERT(!allow_log || namecheck != NO_NAME); 55664445fffbSMatthew Ahrens 55674445fffbSMatthew Ahrens vec->zvec_name = name; 55684445fffbSMatthew Ahrens vec->zvec_func = func; 55694445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 55704445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 55714445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 55724445fffbSMatthew Ahrens vec->zvec_smush_outnvlist = smush_outnvlist; 55734445fffbSMatthew Ahrens vec->zvec_allow_log = allow_log; 55744445fffbSMatthew Ahrens } 55754445fffbSMatthew Ahrens 55764445fffbSMatthew Ahrens static void 55774445fffbSMatthew Ahrens zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 55784445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, boolean_t log_history, 55794445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check) 55804445fffbSMatthew Ahrens { 55814445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 55824445fffbSMatthew Ahrens POOL_NAME, log_history, pool_check); 55834445fffbSMatthew Ahrens } 55844445fffbSMatthew Ahrens 55854445fffbSMatthew Ahrens static void 55864445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 55874445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check) 55884445fffbSMatthew Ahrens { 55894445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 55904445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, pool_check); 55914445fffbSMatthew Ahrens } 55924445fffbSMatthew Ahrens 55934445fffbSMatthew Ahrens static void 55944445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 55954445fffbSMatthew Ahrens { 55964445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config, 55974445fffbSMatthew Ahrens POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 55984445fffbSMatthew Ahrens } 55994445fffbSMatthew Ahrens 56004445fffbSMatthew Ahrens static void 56014445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 56024445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy) 56034445fffbSMatthew Ahrens { 56044445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 56054445fffbSMatthew Ahrens NO_NAME, B_FALSE, POOL_CHECK_NONE); 56064445fffbSMatthew Ahrens } 56074445fffbSMatthew Ahrens 56084445fffbSMatthew Ahrens static void 56094445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc, 56104445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) 56114445fffbSMatthew Ahrens { 56124445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 56134445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED); 56144445fffbSMatthew Ahrens } 56154445fffbSMatthew Ahrens 56164445fffbSMatthew Ahrens static void 56174445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 56184445fffbSMatthew Ahrens { 56194445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ioc, func, 56204445fffbSMatthew Ahrens zfs_secpolicy_read); 56214445fffbSMatthew Ahrens } 56224445fffbSMatthew Ahrens 56234445fffbSMatthew Ahrens static void 56244445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 56259a686fbcSPaul Dagnelie zfs_secpolicy_func_t *secpolicy) 56264445fffbSMatthew Ahrens { 56274445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 56284445fffbSMatthew Ahrens DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 56294445fffbSMatthew Ahrens } 56304445fffbSMatthew Ahrens 56314445fffbSMatthew Ahrens static void 56324445fffbSMatthew Ahrens zfs_ioctl_init(void) 56334445fffbSMatthew Ahrens { 56344445fffbSMatthew Ahrens zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, 56354445fffbSMatthew Ahrens zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, 56364445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56374445fffbSMatthew Ahrens 56384445fffbSMatthew Ahrens zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, 56394445fffbSMatthew Ahrens zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, 56404445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); 56414445fffbSMatthew Ahrens 56424445fffbSMatthew Ahrens zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, 56434445fffbSMatthew Ahrens zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, 56444445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56454445fffbSMatthew Ahrens 56464445fffbSMatthew Ahrens zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, 56474445fffbSMatthew Ahrens zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, 56484445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56494445fffbSMatthew Ahrens 56504445fffbSMatthew Ahrens zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, 56514445fffbSMatthew Ahrens zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, 56524445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56534445fffbSMatthew Ahrens 56544445fffbSMatthew Ahrens zfs_ioctl_register("create", ZFS_IOC_CREATE, 56554445fffbSMatthew Ahrens zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, 56564445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56574445fffbSMatthew Ahrens 56584445fffbSMatthew Ahrens zfs_ioctl_register("clone", ZFS_IOC_CLONE, 56594445fffbSMatthew Ahrens zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, 56604445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56614445fffbSMatthew Ahrens 56624445fffbSMatthew Ahrens zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, 56634445fffbSMatthew Ahrens zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, 56644445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56654445fffbSMatthew Ahrens 56663b2aab18SMatthew Ahrens zfs_ioctl_register("hold", ZFS_IOC_HOLD, 56673b2aab18SMatthew Ahrens zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, 56683b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56693b2aab18SMatthew Ahrens zfs_ioctl_register("release", ZFS_IOC_RELEASE, 56703b2aab18SMatthew Ahrens zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, 56713b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 56723b2aab18SMatthew Ahrens 56733b2aab18SMatthew Ahrens zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, 56743b2aab18SMatthew Ahrens zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, 56753b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 56763b2aab18SMatthew Ahrens 5677a7027df1SMatthew Ahrens zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, 5678a7027df1SMatthew Ahrens zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, 5679a7027df1SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); 5680a7027df1SMatthew Ahrens 568178f17100SMatthew Ahrens zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, 568278f17100SMatthew Ahrens zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, 568378f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 568478f17100SMatthew Ahrens 568578f17100SMatthew Ahrens zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, 568678f17100SMatthew Ahrens zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, 568778f17100SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 568878f17100SMatthew Ahrens 568978f17100SMatthew Ahrens zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, 569078f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, 569178f17100SMatthew Ahrens POOL_NAME, 569278f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 569378f17100SMatthew Ahrens 56944445fffbSMatthew Ahrens /* IOCTLS that use the legacy function signature */ 56954445fffbSMatthew Ahrens 56964445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, 56974445fffbSMatthew Ahrens zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); 56984445fffbSMatthew Ahrens 56994445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, 57004445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 57014445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN, 57024445fffbSMatthew Ahrens zfs_ioc_pool_scan); 57034445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE, 57044445fffbSMatthew Ahrens zfs_ioc_pool_upgrade); 57054445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD, 57064445fffbSMatthew Ahrens zfs_ioc_vdev_add); 57074445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE, 57084445fffbSMatthew Ahrens zfs_ioc_vdev_remove); 57094445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SET_STATE, 57104445fffbSMatthew Ahrens zfs_ioc_vdev_set_state); 57114445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH, 57124445fffbSMatthew Ahrens zfs_ioc_vdev_attach); 57134445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH, 57144445fffbSMatthew Ahrens zfs_ioc_vdev_detach); 57154445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH, 57164445fffbSMatthew Ahrens zfs_ioc_vdev_setpath); 57174445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU, 57184445fffbSMatthew Ahrens zfs_ioc_vdev_setfru); 57194445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS, 57204445fffbSMatthew Ahrens zfs_ioc_pool_set_props); 57214445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SPLIT, 57224445fffbSMatthew Ahrens zfs_ioc_vdev_split); 57234445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_REGUID, 57244445fffbSMatthew Ahrens zfs_ioc_pool_reguid); 57254445fffbSMatthew Ahrens 57264445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_CONFIGS, 57274445fffbSMatthew Ahrens zfs_ioc_pool_configs, zfs_secpolicy_none); 57284445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_TRYIMPORT, 57294445fffbSMatthew Ahrens zfs_ioc_pool_tryimport, zfs_secpolicy_config); 57304445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_FAULT, 57314445fffbSMatthew Ahrens zfs_ioc_inject_fault, zfs_secpolicy_inject); 57324445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_CLEAR_FAULT, 57334445fffbSMatthew Ahrens zfs_ioc_clear_fault, zfs_secpolicy_inject); 57344445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_LIST_NEXT, 57354445fffbSMatthew Ahrens zfs_ioc_inject_list_next, zfs_secpolicy_inject); 57364445fffbSMatthew Ahrens 57374445fffbSMatthew Ahrens /* 57384445fffbSMatthew Ahrens * pool destroy, and export don't log the history as part of 57394445fffbSMatthew Ahrens * zfsdev_ioctl, but rather zfs_ioc_pool_export 57404445fffbSMatthew Ahrens * does the logging of those commands. 57414445fffbSMatthew Ahrens */ 57424445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_DESTROY, zfs_ioc_pool_destroy, 57434445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 57444445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_EXPORT, zfs_ioc_pool_export, 57454445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 57464445fffbSMatthew Ahrens 57474445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_STATS, zfs_ioc_pool_stats, 57484445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 57494445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_PROPS, zfs_ioc_pool_get_props, 57504445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 57514445fffbSMatthew Ahrens 57524445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_ERROR_LOG, zfs_ioc_error_log, 57534445fffbSMatthew Ahrens zfs_secpolicy_inject, B_FALSE, POOL_CHECK_SUSPENDED); 57544445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_DSOBJ_TO_DSNAME, 57554445fffbSMatthew Ahrens zfs_ioc_dsobj_to_dsname, 57564445fffbSMatthew Ahrens zfs_secpolicy_diff, B_FALSE, POOL_CHECK_SUSPENDED); 57574445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_HISTORY, 57584445fffbSMatthew Ahrens zfs_ioc_pool_get_history, 57594445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); 57604445fffbSMatthew Ahrens 57614445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, 57624445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 57634445fffbSMatthew Ahrens 57644445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, 576522e30981SGeorge Wilson zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 57664445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, 57674445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); 57684445fffbSMatthew Ahrens 57694445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, 57704445fffbSMatthew Ahrens zfs_ioc_space_written); 57714445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, 57724445fffbSMatthew Ahrens zfs_ioc_objset_recvd_props); 57734445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, 57744445fffbSMatthew Ahrens zfs_ioc_next_obj); 57754445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, 57764445fffbSMatthew Ahrens zfs_ioc_get_fsacl); 57774445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_STATS, 57784445fffbSMatthew Ahrens zfs_ioc_objset_stats); 57794445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_ZPLPROPS, 57804445fffbSMatthew Ahrens zfs_ioc_objset_zplprops); 57814445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_DATASET_LIST_NEXT, 57824445fffbSMatthew Ahrens zfs_ioc_dataset_list_next); 57834445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SNAPSHOT_LIST_NEXT, 57844445fffbSMatthew Ahrens zfs_ioc_snapshot_list_next); 57854445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SEND_PROGRESS, 57864445fffbSMatthew Ahrens zfs_ioc_send_progress); 57874445fffbSMatthew Ahrens 57884445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_DIFF, 57894445fffbSMatthew Ahrens zfs_ioc_diff, zfs_secpolicy_diff); 57904445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_STATS, 57914445fffbSMatthew Ahrens zfs_ioc_obj_to_stats, zfs_secpolicy_diff); 57924445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_PATH, 57934445fffbSMatthew Ahrens zfs_ioc_obj_to_path, zfs_secpolicy_diff); 57944445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_ONE, 57954445fffbSMatthew Ahrens zfs_ioc_userspace_one, zfs_secpolicy_userspace_one); 57964445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_MANY, 57974445fffbSMatthew Ahrens zfs_ioc_userspace_many, zfs_secpolicy_userspace_many); 57984445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND, 57994445fffbSMatthew Ahrens zfs_ioc_send, zfs_secpolicy_send); 58004445fffbSMatthew Ahrens 58014445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, 58024445fffbSMatthew Ahrens zfs_secpolicy_none); 58034445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, 58044445fffbSMatthew Ahrens zfs_secpolicy_destroy); 58054445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename, 58064445fffbSMatthew Ahrens zfs_secpolicy_rename); 58074445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv, 58084445fffbSMatthew Ahrens zfs_secpolicy_recv); 58094445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote, 58104445fffbSMatthew Ahrens zfs_secpolicy_promote); 58114445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP, 58124445fffbSMatthew Ahrens zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop); 58134445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl, 58144445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl); 58154445fffbSMatthew Ahrens 58164445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share, 58174445fffbSMatthew Ahrens zfs_secpolicy_share, POOL_CHECK_NONE); 58184445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl, 58194445fffbSMatthew Ahrens zfs_secpolicy_smb_acl, POOL_CHECK_NONE); 58204445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERSPACE_UPGRADE, 58214445fffbSMatthew Ahrens zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 58224445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 58234445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT, 58244445fffbSMatthew Ahrens zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, 58254445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 58264445fffbSMatthew Ahrens } 5827fa9e4066Sahrens 582854d692b7SGeorge Wilson int 5829f9af39baSGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type, 5830f9af39baSGeorge Wilson zfs_ioc_poolcheck_t check) 583154d692b7SGeorge Wilson { 583254d692b7SGeorge Wilson spa_t *spa; 583354d692b7SGeorge Wilson int error; 583454d692b7SGeorge Wilson 583554d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 583654d692b7SGeorge Wilson 5837f9af39baSGeorge Wilson if (check & POOL_CHECK_NONE) 5838f9af39baSGeorge Wilson return (0); 5839f9af39baSGeorge Wilson 584014843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 584154d692b7SGeorge Wilson if (error == 0) { 5842f9af39baSGeorge Wilson if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa)) 5843be6fd75aSMatthew Ahrens error = SET_ERROR(EAGAIN); 5844f9af39baSGeorge Wilson else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa)) 5845be6fd75aSMatthew Ahrens error = SET_ERROR(EROFS); 584654d692b7SGeorge Wilson spa_close(spa, FTAG); 584754d692b7SGeorge Wilson } 584854d692b7SGeorge Wilson return (error); 584954d692b7SGeorge Wilson } 585054d692b7SGeorge Wilson 5851c99e4bdcSChris Kirby /* 5852c99e4bdcSChris Kirby * Find a free minor number. 5853c99e4bdcSChris Kirby */ 5854c99e4bdcSChris Kirby minor_t 5855c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 5856c99e4bdcSChris Kirby { 5857c99e4bdcSChris Kirby static minor_t last_minor; 5858c99e4bdcSChris Kirby minor_t m; 5859c99e4bdcSChris Kirby 5860c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5861c99e4bdcSChris Kirby 5862c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 5863c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 5864c99e4bdcSChris Kirby m = 1; 5865c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 5866c99e4bdcSChris Kirby last_minor = m; 5867c99e4bdcSChris Kirby return (m); 5868c99e4bdcSChris Kirby } 5869c99e4bdcSChris Kirby } 5870c99e4bdcSChris Kirby 5871c99e4bdcSChris Kirby return (0); 5872c99e4bdcSChris Kirby } 5873c99e4bdcSChris Kirby 5874c99e4bdcSChris Kirby static int 5875c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 5876c99e4bdcSChris Kirby { 5877c99e4bdcSChris Kirby minor_t minor; 5878c99e4bdcSChris Kirby zfs_soft_state_t *zs; 5879c99e4bdcSChris Kirby 5880c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5881c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 5882c99e4bdcSChris Kirby 5883c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 5884c99e4bdcSChris Kirby if (minor == 0) 5885be6fd75aSMatthew Ahrens return (SET_ERROR(ENXIO)); 5886c99e4bdcSChris Kirby 5887c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 5888be6fd75aSMatthew Ahrens return (SET_ERROR(EAGAIN)); 5889c99e4bdcSChris Kirby 5890c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 5891c99e4bdcSChris Kirby 5892c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 5893c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 5894c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 5895c99e4bdcSChris Kirby 5896c99e4bdcSChris Kirby return (0); 5897c99e4bdcSChris Kirby } 5898c99e4bdcSChris Kirby 5899c99e4bdcSChris Kirby static void 5900c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 5901c99e4bdcSChris Kirby { 5902c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 5903c99e4bdcSChris Kirby 5904c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 5905c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 5906c99e4bdcSChris Kirby } 5907c99e4bdcSChris Kirby 5908c99e4bdcSChris Kirby void * 5909c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 5910c99e4bdcSChris Kirby { 5911c99e4bdcSChris Kirby zfs_soft_state_t *zp; 5912c99e4bdcSChris Kirby 5913c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 5914c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 5915c99e4bdcSChris Kirby return (NULL); 5916c99e4bdcSChris Kirby 5917c99e4bdcSChris Kirby return (zp->zss_data); 5918c99e4bdcSChris Kirby } 5919c99e4bdcSChris Kirby 5920c99e4bdcSChris Kirby static int 5921c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 5922c99e4bdcSChris Kirby { 5923c99e4bdcSChris Kirby int error = 0; 5924c99e4bdcSChris Kirby 5925c99e4bdcSChris Kirby if (getminor(*devp) != 0) 5926c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 5927c99e4bdcSChris Kirby 5928c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 5929c99e4bdcSChris Kirby if (flag & FEXCL) { 5930c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 5931c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 5932c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5933c99e4bdcSChris Kirby } 5934c99e4bdcSChris Kirby 5935c99e4bdcSChris Kirby return (error); 5936c99e4bdcSChris Kirby } 5937c99e4bdcSChris Kirby 5938c99e4bdcSChris Kirby static int 5939c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 5940c99e4bdcSChris Kirby { 5941c99e4bdcSChris Kirby zfs_onexit_t *zo; 5942c99e4bdcSChris Kirby minor_t minor = getminor(dev); 5943c99e4bdcSChris Kirby 5944c99e4bdcSChris Kirby if (minor == 0) 5945c99e4bdcSChris Kirby return (0); 5946c99e4bdcSChris Kirby 5947c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 5948c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 5949c99e4bdcSChris Kirby if (zo == NULL) { 5950c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5951c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 5952c99e4bdcSChris Kirby } 5953c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 5954c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 5955c99e4bdcSChris Kirby 5956c99e4bdcSChris Kirby return (0); 5957c99e4bdcSChris Kirby } 5958c99e4bdcSChris Kirby 5959fa9e4066Sahrens static int 5960fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 5961fa9e4066Sahrens { 5962fa9e4066Sahrens zfs_cmd_t *zc; 59634445fffbSMatthew Ahrens uint_t vecnum; 59644445fffbSMatthew Ahrens int error, rc, len; 5965c99e4bdcSChris Kirby minor_t minor = getminor(dev); 59664445fffbSMatthew Ahrens const zfs_ioc_vec_t *vec; 59674445fffbSMatthew Ahrens char *saved_poolname = NULL; 59684445fffbSMatthew Ahrens nvlist_t *innvl = NULL; 5969fa9e4066Sahrens 5970c99e4bdcSChris Kirby if (minor != 0 && 5971c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 5972fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 5973fa9e4066Sahrens 59744445fffbSMatthew Ahrens vecnum = cmd - ZFS_IOC_FIRST; 597591ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 5976fa9e4066Sahrens 59774445fffbSMatthew Ahrens if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 5978be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 59794445fffbSMatthew Ahrens vec = &zfs_ioc_vec[vecnum]; 5980fa9e4066Sahrens 5981fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 5982fa9e4066Sahrens 5983478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 59844445fffbSMatthew Ahrens if (error != 0) { 5985be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 59864445fffbSMatthew Ahrens goto out; 59874445fffbSMatthew Ahrens } 5988fa9e4066Sahrens 59894445fffbSMatthew Ahrens zc->zc_iflags = flag & FKIOCTL; 59904445fffbSMatthew Ahrens if (zc->zc_nvlist_src_size != 0) { 59914445fffbSMatthew Ahrens error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 59924445fffbSMatthew Ahrens zc->zc_iflags, &innvl); 59934445fffbSMatthew Ahrens if (error != 0) 59944445fffbSMatthew Ahrens goto out; 59954445fffbSMatthew Ahrens } 5996fa9e4066Sahrens 5997fa9e4066Sahrens /* 5998fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 5999fa9e4066Sahrens * the lower layers. 6000fa9e4066Sahrens */ 60014445fffbSMatthew Ahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 60024445fffbSMatthew Ahrens switch (vec->zvec_namecheck) { 60034445fffbSMatthew Ahrens case POOL_NAME: 60044445fffbSMatthew Ahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 6005be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 60064445fffbSMatthew Ahrens else 6007f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 60084445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 60094445fffbSMatthew Ahrens break; 6010fa9e4066Sahrens 60114445fffbSMatthew Ahrens case DATASET_NAME: 60124445fffbSMatthew Ahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 6013be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 60144445fffbSMatthew Ahrens else 6015f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 60164445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 60174445fffbSMatthew Ahrens break; 60185ad82045Snd 60194445fffbSMatthew Ahrens case NO_NAME: 60204445fffbSMatthew Ahrens break; 6021fa9e4066Sahrens } 6022fa9e4066Sahrens 6023fa9e4066Sahrens 60244445fffbSMatthew Ahrens if (error == 0 && !(flag & FKIOCTL)) 60254445fffbSMatthew Ahrens error = vec->zvec_secpolicy(zc, innvl, cr); 60264445fffbSMatthew Ahrens 60274445fffbSMatthew Ahrens if (error != 0) 60284445fffbSMatthew Ahrens goto out; 60294445fffbSMatthew Ahrens 60304445fffbSMatthew Ahrens /* legacy ioctls can modify zc_name */ 603178f17100SMatthew Ahrens len = strcspn(zc->zc_name, "/@#") + 1; 60324445fffbSMatthew Ahrens saved_poolname = kmem_alloc(len, KM_SLEEP); 60334445fffbSMatthew Ahrens (void) strlcpy(saved_poolname, zc->zc_name, len); 60344445fffbSMatthew Ahrens 60354445fffbSMatthew Ahrens if (vec->zvec_func != NULL) { 60364445fffbSMatthew Ahrens nvlist_t *outnvl; 60374445fffbSMatthew Ahrens int puterror = 0; 60384445fffbSMatthew Ahrens spa_t *spa; 60394445fffbSMatthew Ahrens nvlist_t *lognv = NULL; 60404445fffbSMatthew Ahrens 60414445fffbSMatthew Ahrens ASSERT(vec->zvec_legacy_func == NULL); 60424445fffbSMatthew Ahrens 60434445fffbSMatthew Ahrens /* 60444445fffbSMatthew Ahrens * Add the innvl to the lognv before calling the func, 60454445fffbSMatthew Ahrens * in case the func changes the innvl. 60464445fffbSMatthew Ahrens */ 60474445fffbSMatthew Ahrens if (vec->zvec_allow_log) { 60484445fffbSMatthew Ahrens lognv = fnvlist_alloc(); 60494445fffbSMatthew Ahrens fnvlist_add_string(lognv, ZPOOL_HIST_IOCTL, 60504445fffbSMatthew Ahrens vec->zvec_name); 60514445fffbSMatthew Ahrens if (!nvlist_empty(innvl)) { 60524445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_INPUT_NVL, 60534445fffbSMatthew Ahrens innvl); 60544445fffbSMatthew Ahrens } 60554445fffbSMatthew Ahrens } 60564445fffbSMatthew Ahrens 60574445fffbSMatthew Ahrens outnvl = fnvlist_alloc(); 60584445fffbSMatthew Ahrens error = vec->zvec_func(zc->zc_name, innvl, outnvl); 60594445fffbSMatthew Ahrens 60604445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log && 60614445fffbSMatthew Ahrens spa_open(zc->zc_name, &spa, FTAG) == 0) { 60624445fffbSMatthew Ahrens if (!nvlist_empty(outnvl)) { 60634445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, 60644445fffbSMatthew Ahrens outnvl); 60654445fffbSMatthew Ahrens } 60664445fffbSMatthew Ahrens (void) spa_history_log_nvl(spa, lognv); 60674445fffbSMatthew Ahrens spa_close(spa, FTAG); 60684445fffbSMatthew Ahrens } 60694445fffbSMatthew Ahrens fnvlist_free(lognv); 60704445fffbSMatthew Ahrens 60714445fffbSMatthew Ahrens if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) { 60724445fffbSMatthew Ahrens int smusherror = 0; 60734445fffbSMatthew Ahrens if (vec->zvec_smush_outnvlist) { 60744445fffbSMatthew Ahrens smusherror = nvlist_smush(outnvl, 60754445fffbSMatthew Ahrens zc->zc_nvlist_dst_size); 60764445fffbSMatthew Ahrens } 60774445fffbSMatthew Ahrens if (smusherror == 0) 60784445fffbSMatthew Ahrens puterror = put_nvlist(zc, outnvl); 60794445fffbSMatthew Ahrens } 60804445fffbSMatthew Ahrens 60814445fffbSMatthew Ahrens if (puterror != 0) 60824445fffbSMatthew Ahrens error = puterror; 60834445fffbSMatthew Ahrens 60844445fffbSMatthew Ahrens nvlist_free(outnvl); 60854445fffbSMatthew Ahrens } else { 60864445fffbSMatthew Ahrens error = vec->zvec_legacy_func(zc); 60874445fffbSMatthew Ahrens } 60884445fffbSMatthew Ahrens 60894445fffbSMatthew Ahrens out: 60904445fffbSMatthew Ahrens nvlist_free(innvl); 6091478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 60924445fffbSMatthew Ahrens if (error == 0 && rc != 0) 6093be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 60944445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log) { 60954445fffbSMatthew Ahrens char *s = tsd_get(zfs_allow_log_key); 60964445fffbSMatthew Ahrens if (s != NULL) 60974445fffbSMatthew Ahrens strfree(s); 60984445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, saved_poolname); 60994445fffbSMatthew Ahrens } else { 61004445fffbSMatthew Ahrens if (saved_poolname != NULL) 61014445fffbSMatthew Ahrens strfree(saved_poolname); 6102ecd6cf80Smarks } 6103fa9e4066Sahrens 6104fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 6105fa9e4066Sahrens return (error); 6106fa9e4066Sahrens } 6107fa9e4066Sahrens 6108fa9e4066Sahrens static int 6109fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 6110fa9e4066Sahrens { 6111fa9e4066Sahrens if (cmd != DDI_ATTACH) 6112fa9e4066Sahrens return (DDI_FAILURE); 6113fa9e4066Sahrens 6114fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 6115fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 6116fa9e4066Sahrens return (DDI_FAILURE); 6117fa9e4066Sahrens 6118fa9e4066Sahrens zfs_dip = dip; 6119fa9e4066Sahrens 6120fa9e4066Sahrens ddi_report_dev(dip); 6121fa9e4066Sahrens 6122fa9e4066Sahrens return (DDI_SUCCESS); 6123fa9e4066Sahrens } 6124fa9e4066Sahrens 6125fa9e4066Sahrens static int 6126fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 6127fa9e4066Sahrens { 6128fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 6129fa9e4066Sahrens return (DDI_FAILURE); 6130fa9e4066Sahrens 6131fa9e4066Sahrens if (cmd != DDI_DETACH) 6132fa9e4066Sahrens return (DDI_FAILURE); 6133fa9e4066Sahrens 6134fa9e4066Sahrens zfs_dip = NULL; 6135fa9e4066Sahrens 6136fa9e4066Sahrens ddi_prop_remove_all(dip); 6137fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 6138fa9e4066Sahrens 6139fa9e4066Sahrens return (DDI_SUCCESS); 6140fa9e4066Sahrens } 6141fa9e4066Sahrens 6142fa9e4066Sahrens /*ARGSUSED*/ 6143fa9e4066Sahrens static int 6144fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 6145fa9e4066Sahrens { 6146fa9e4066Sahrens switch (infocmd) { 6147fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 6148fa9e4066Sahrens *result = zfs_dip; 6149fa9e4066Sahrens return (DDI_SUCCESS); 6150fa9e4066Sahrens 6151fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 6152a0965f35Sbonwick *result = (void *)0; 6153fa9e4066Sahrens return (DDI_SUCCESS); 6154fa9e4066Sahrens } 6155fa9e4066Sahrens 6156fa9e4066Sahrens return (DDI_FAILURE); 6157fa9e4066Sahrens } 6158fa9e4066Sahrens 6159fa9e4066Sahrens /* 6160fa9e4066Sahrens * OK, so this is a little weird. 6161fa9e4066Sahrens * 6162fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 6163fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 6164fa9e4066Sahrens * 6165fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 6166fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 6167fa9e4066Sahrens */ 6168fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 6169c99e4bdcSChris Kirby zfsdev_open, /* open */ 6170c99e4bdcSChris Kirby zfsdev_close, /* close */ 6171fa9e4066Sahrens zvol_strategy, /* strategy */ 6172fa9e4066Sahrens nodev, /* print */ 6173e7cbe64fSgw zvol_dump, /* dump */ 6174fa9e4066Sahrens zvol_read, /* read */ 6175fa9e4066Sahrens zvol_write, /* write */ 6176fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 6177fa9e4066Sahrens nodev, /* devmap */ 6178fa9e4066Sahrens nodev, /* mmap */ 6179fa9e4066Sahrens nodev, /* segmap */ 6180fa9e4066Sahrens nochpoll, /* poll */ 6181fa9e4066Sahrens ddi_prop_op, /* prop_op */ 6182fa9e4066Sahrens NULL, /* streamtab */ 6183fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 6184fa9e4066Sahrens CB_REV, /* version */ 6185feb08c6bSbillm nodev, /* async read */ 6186feb08c6bSbillm nodev, /* async write */ 6187fa9e4066Sahrens }; 6188fa9e4066Sahrens 6189fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 6190fa9e4066Sahrens DEVO_REV, /* version */ 6191fa9e4066Sahrens 0, /* refcnt */ 6192fa9e4066Sahrens zfs_info, /* info */ 6193fa9e4066Sahrens nulldev, /* identify */ 6194fa9e4066Sahrens nulldev, /* probe */ 6195fa9e4066Sahrens zfs_attach, /* attach */ 6196fa9e4066Sahrens zfs_detach, /* detach */ 6197fa9e4066Sahrens nodev, /* reset */ 6198fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 619919397407SSherry Moore NULL, /* no bus operations */ 620019397407SSherry Moore NULL, /* power */ 620119397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 6202fa9e4066Sahrens }; 6203fa9e4066Sahrens 6204fa9e4066Sahrens static struct modldrv zfs_modldrv = { 620519397407SSherry Moore &mod_driverops, 620619397407SSherry Moore "ZFS storage pool", 620719397407SSherry Moore &zfs_dev_ops 6208fa9e4066Sahrens }; 6209fa9e4066Sahrens 6210fa9e4066Sahrens static struct modlinkage modlinkage = { 6211fa9e4066Sahrens MODREV_1, 6212fa9e4066Sahrens (void *)&zfs_modlfs, 6213fa9e4066Sahrens (void *)&zfs_modldrv, 6214fa9e4066Sahrens NULL 6215fa9e4066Sahrens }; 6216fa9e4066Sahrens 62174445fffbSMatthew Ahrens static void 62184445fffbSMatthew Ahrens zfs_allow_log_destroy(void *arg) 62194445fffbSMatthew Ahrens { 62204445fffbSMatthew Ahrens char *poolname = arg; 62214445fffbSMatthew Ahrens strfree(poolname); 62224445fffbSMatthew Ahrens } 6223ec533521Sfr 6224fa9e4066Sahrens int 6225fa9e4066Sahrens _init(void) 6226fa9e4066Sahrens { 6227fa9e4066Sahrens int error; 6228fa9e4066Sahrens 6229a0965f35Sbonwick spa_init(FREAD | FWRITE); 6230a0965f35Sbonwick zfs_init(); 6231a0965f35Sbonwick zvol_init(); 62324445fffbSMatthew Ahrens zfs_ioctl_init(); 6233a0965f35Sbonwick 6234a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 6235a0965f35Sbonwick zvol_fini(); 6236a0965f35Sbonwick zfs_fini(); 6237a0965f35Sbonwick spa_fini(); 6238fa9e4066Sahrens return (error); 6239a0965f35Sbonwick } 6240fa9e4066Sahrens 6241ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 62424445fffbSMatthew Ahrens tsd_create(&rrw_tsd_key, rrw_tsd_destroy); 62434445fffbSMatthew Ahrens tsd_create(&zfs_allow_log_key, zfs_allow_log_destroy); 6244ec533521Sfr 6245fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 6246fa9e4066Sahrens ASSERT(error == 0); 6247ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 6248fa9e4066Sahrens 6249fa9e4066Sahrens return (0); 6250fa9e4066Sahrens } 6251fa9e4066Sahrens 6252fa9e4066Sahrens int 6253fa9e4066Sahrens _fini(void) 6254fa9e4066Sahrens { 6255fa9e4066Sahrens int error; 6256fa9e4066Sahrens 6257ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 6258be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 6259fa9e4066Sahrens 6260fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 6261fa9e4066Sahrens return (error); 6262fa9e4066Sahrens 6263fa9e4066Sahrens zvol_fini(); 6264fa9e4066Sahrens zfs_fini(); 6265fa9e4066Sahrens spa_fini(); 6266da6c28aaSamw if (zfs_nfsshare_inited) 6267ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 6268da6c28aaSamw if (zfs_smbshare_inited) 6269da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 6270da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 6271ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 6272fa9e4066Sahrens 6273ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 6274fa9e4066Sahrens ldi_ident_release(zfs_li); 6275fa9e4066Sahrens zfs_li = NULL; 6276ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 6277fa9e4066Sahrens 6278fa9e4066Sahrens return (error); 6279fa9e4066Sahrens } 6280fa9e4066Sahrens 6281fa9e4066Sahrens int 6282fa9e4066Sahrens _info(struct modinfo *modinfop) 6283fa9e4066Sahrens { 6284fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 6285fa9e4066Sahrens } 6286