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. 240d8fa8f8SMartin Matuska * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. 251df56adaSMartin Matuska * Portions Copyright 2011 Martin Matuska 265878fad7SDan McDonald * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. 27752fd8daSJosef 'Jeff' Sipek * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 2845b17475SAlex Wilson * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. 292840dce1SChris Williamson * Copyright (c) 2011, 2017 by Delphix. All rights reserved. 30a6f561b4SSašo Kiselkov * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. 31a7a845e4SSteven Hartland * Copyright (c) 2013 Steven Hartland. All rights reserved. 32c3d26abcSMatthew Ahrens * Copyright (c) 2014 Integros [integros.com] 33c8811bd3SToomas Soome * Copyright 2016 Toomas Soome <tsoome@me.com> 34a4b8c9aaSAndrew Stormont * Copyright 2017 RackTop Systems. 351702cce7SAlek Pinchuk * Copyright (c) 2017 Datto Inc. 364445fffbSMatthew Ahrens */ 374445fffbSMatthew Ahrens 384445fffbSMatthew Ahrens /* 394445fffbSMatthew Ahrens * ZFS ioctls. 404445fffbSMatthew Ahrens * 414445fffbSMatthew Ahrens * This file handles the ioctls to /dev/zfs, used for configuring ZFS storage 424445fffbSMatthew Ahrens * pools and filesystems, e.g. with /sbin/zfs and /sbin/zpool. 434445fffbSMatthew Ahrens * 444445fffbSMatthew Ahrens * There are two ways that we handle ioctls: the legacy way where almost 454445fffbSMatthew Ahrens * all of the logic is in the ioctl callback, and the new way where most 464445fffbSMatthew Ahrens * of the marshalling is handled in the common entry point, zfsdev_ioctl(). 474445fffbSMatthew Ahrens * 484445fffbSMatthew Ahrens * Non-legacy ioctls should be registered by calling 494445fffbSMatthew Ahrens * zfs_ioctl_register() from zfs_ioctl_init(). The ioctl is invoked 504445fffbSMatthew Ahrens * from userland by lzc_ioctl(). 514445fffbSMatthew Ahrens * 524445fffbSMatthew Ahrens * The registration arguments are as follows: 534445fffbSMatthew Ahrens * 544445fffbSMatthew Ahrens * const char *name 554445fffbSMatthew Ahrens * The name of the ioctl. This is used for history logging. If the 564445fffbSMatthew Ahrens * ioctl returns successfully (the callback returns 0), and allow_log 574445fffbSMatthew Ahrens * is true, then a history log entry will be recorded with the input & 584445fffbSMatthew Ahrens * output nvlists. The log entry can be printed with "zpool history -i". 594445fffbSMatthew Ahrens * 604445fffbSMatthew Ahrens * zfs_ioc_t ioc 614445fffbSMatthew Ahrens * The ioctl request number, which userland will pass to ioctl(2). 624445fffbSMatthew Ahrens * The ioctl numbers can change from release to release, because 634445fffbSMatthew Ahrens * the caller (libzfs) must be matched to the kernel. 644445fffbSMatthew Ahrens * 654445fffbSMatthew Ahrens * zfs_secpolicy_func_t *secpolicy 664445fffbSMatthew Ahrens * This function will be called before the zfs_ioc_func_t, to 674445fffbSMatthew Ahrens * determine if this operation is permitted. It should return EPERM 684445fffbSMatthew Ahrens * on failure, and 0 on success. Checks include determining if the 694445fffbSMatthew Ahrens * dataset is visible in this zone, and if the user has either all 704445fffbSMatthew Ahrens * zfs privileges in the zone (SYS_MOUNT), or has been granted permission 714445fffbSMatthew Ahrens * to do this operation on this dataset with "zfs allow". 724445fffbSMatthew Ahrens * 734445fffbSMatthew Ahrens * zfs_ioc_namecheck_t namecheck 744445fffbSMatthew Ahrens * This specifies what to expect in the zfs_cmd_t:zc_name -- a pool 754445fffbSMatthew Ahrens * name, a dataset name, or nothing. If the name is not well-formed, 764445fffbSMatthew Ahrens * the ioctl will fail and the callback will not be called. 774445fffbSMatthew Ahrens * Therefore, the callback can assume that the name is well-formed 784445fffbSMatthew Ahrens * (e.g. is null-terminated, doesn't have more than one '@' character, 794445fffbSMatthew Ahrens * doesn't have invalid characters). 804445fffbSMatthew Ahrens * 814445fffbSMatthew Ahrens * zfs_ioc_poolcheck_t pool_check 824445fffbSMatthew Ahrens * This specifies requirements on the pool state. If the pool does 834445fffbSMatthew Ahrens * not meet them (is suspended or is readonly), the ioctl will fail 844445fffbSMatthew Ahrens * and the callback will not be called. If any checks are specified 854445fffbSMatthew Ahrens * (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. 864445fffbSMatthew Ahrens * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | 874445fffbSMatthew Ahrens * POOL_CHECK_READONLY). 884445fffbSMatthew Ahrens * 894445fffbSMatthew Ahrens * boolean_t smush_outnvlist 904445fffbSMatthew Ahrens * If smush_outnvlist is true, then the output is presumed to be a 914445fffbSMatthew Ahrens * list of errors, and it will be "smushed" down to fit into the 924445fffbSMatthew Ahrens * caller's buffer, by removing some entries and replacing them with a 934445fffbSMatthew Ahrens * single "N_MORE_ERRORS" entry indicating how many were removed. See 944445fffbSMatthew Ahrens * nvlist_smush() for details. If smush_outnvlist is false, and the 954445fffbSMatthew Ahrens * outnvlist does not fit into the userland-provided buffer, then the 964445fffbSMatthew Ahrens * ioctl will fail with ENOMEM. 974445fffbSMatthew Ahrens * 984445fffbSMatthew Ahrens * zfs_ioc_func_t *func 994445fffbSMatthew Ahrens * The callback function that will perform the operation. 1004445fffbSMatthew Ahrens * 1014445fffbSMatthew Ahrens * The callback should return 0 on success, or an error number on 1024445fffbSMatthew Ahrens * failure. If the function fails, the userland ioctl will return -1, 1034445fffbSMatthew Ahrens * and errno will be set to the callback's return value. The callback 1044445fffbSMatthew Ahrens * will be called with the following arguments: 1054445fffbSMatthew Ahrens * 1064445fffbSMatthew Ahrens * const char *name 1074445fffbSMatthew Ahrens * The name of the pool or dataset to operate on, from 1084445fffbSMatthew Ahrens * zfs_cmd_t:zc_name. The 'namecheck' argument specifies the 1094445fffbSMatthew Ahrens * expected type (pool, dataset, or none). 1104445fffbSMatthew Ahrens * 1114445fffbSMatthew Ahrens * nvlist_t *innvl 1124445fffbSMatthew Ahrens * The input nvlist, deserialized from zfs_cmd_t:zc_nvlist_src. Or 1134445fffbSMatthew Ahrens * NULL if no input nvlist was provided. Changes to this nvlist are 1144445fffbSMatthew Ahrens * ignored. If the input nvlist could not be deserialized, the 1154445fffbSMatthew Ahrens * ioctl will fail and the callback will not be called. 1164445fffbSMatthew Ahrens * 1174445fffbSMatthew Ahrens * nvlist_t *outnvl 1184445fffbSMatthew Ahrens * The output nvlist, initially empty. The callback can fill it in, 1194445fffbSMatthew Ahrens * and it will be returned to userland by serializing it into 1204445fffbSMatthew Ahrens * zfs_cmd_t:zc_nvlist_dst. If it is non-empty, and serialization 1214445fffbSMatthew Ahrens * fails (e.g. because the caller didn't supply a large enough 1224445fffbSMatthew Ahrens * buffer), then the overall ioctl will fail. See the 1234445fffbSMatthew Ahrens * 'smush_nvlist' argument above for additional behaviors. 1244445fffbSMatthew Ahrens * 1254445fffbSMatthew Ahrens * There are two typical uses of the output nvlist: 1264445fffbSMatthew Ahrens * - To return state, e.g. property values. In this case, 1274445fffbSMatthew Ahrens * smush_outnvlist should be false. If the buffer was not large 1284445fffbSMatthew Ahrens * enough, the caller will reallocate a larger buffer and try 1294445fffbSMatthew Ahrens * the ioctl again. 1304445fffbSMatthew Ahrens * 1314445fffbSMatthew Ahrens * - To return multiple errors from an ioctl which makes on-disk 1324445fffbSMatthew Ahrens * changes. In this case, smush_outnvlist should be true. 1334445fffbSMatthew Ahrens * Ioctls which make on-disk modifications should generally not 1344445fffbSMatthew Ahrens * use the outnvl if they succeed, because the caller can not 1354445fffbSMatthew Ahrens * distinguish between the operation failing, and 1364445fffbSMatthew Ahrens * deserialization failing. 137e9103aaeSGarrett D'Amore */ 138fa9e4066Sahrens 139fa9e4066Sahrens #include <sys/types.h> 140fa9e4066Sahrens #include <sys/param.h> 141fa9e4066Sahrens #include <sys/errno.h> 142fa9e4066Sahrens #include <sys/uio.h> 143fa9e4066Sahrens #include <sys/buf.h> 144fa9e4066Sahrens #include <sys/modctl.h> 145fa9e4066Sahrens #include <sys/open.h> 146fa9e4066Sahrens #include <sys/file.h> 147fa9e4066Sahrens #include <sys/kmem.h> 148fa9e4066Sahrens #include <sys/conf.h> 149fa9e4066Sahrens #include <sys/cmn_err.h> 150fa9e4066Sahrens #include <sys/stat.h> 151fa9e4066Sahrens #include <sys/zfs_ioctl.h> 1524201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 153da6c28aaSamw #include <sys/zfs_znode.h> 154fa9e4066Sahrens #include <sys/zap.h> 155fa9e4066Sahrens #include <sys/spa.h> 156b1b8ab34Slling #include <sys/spa_impl.h> 157fa9e4066Sahrens #include <sys/vdev.h> 1584201a95eSRic Aleshire #include <sys/priv_impl.h> 159fa9e4066Sahrens #include <sys/dmu.h> 160fa9e4066Sahrens #include <sys/dsl_dir.h> 161fa9e4066Sahrens #include <sys/dsl_dataset.h> 162fa9e4066Sahrens #include <sys/dsl_prop.h> 163ecd6cf80Smarks #include <sys/dsl_deleg.h> 164ecd6cf80Smarks #include <sys/dmu_objset.h> 1654e3c9f44SBill Pijewski #include <sys/dmu_impl.h> 1663b2aab18SMatthew Ahrens #include <sys/dmu_tx.h> 167fa9e4066Sahrens #include <sys/ddi.h> 168fa9e4066Sahrens #include <sys/sunddi.h> 169fa9e4066Sahrens #include <sys/sunldi.h> 170fa9e4066Sahrens #include <sys/policy.h> 171fa9e4066Sahrens #include <sys/zone.h> 172fa9e4066Sahrens #include <sys/nvpair.h> 173fa9e4066Sahrens #include <sys/pathname.h> 174fa9e4066Sahrens #include <sys/mount.h> 175fa9e4066Sahrens #include <sys/sdt.h> 176fa9e4066Sahrens #include <sys/fs/zfs.h> 177fa9e4066Sahrens #include <sys/zfs_ctldir.h> 178da6c28aaSamw #include <sys/zfs_dir.h> 179c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 180a2eea2e1Sahrens #include <sys/zvol.h> 1813f9d6ad7SLin Ling #include <sys/dsl_scan.h> 182ecd6cf80Smarks #include <sharefs/share.h> 183f18faf3fSek #include <sys/dmu_objset.h> 1843b2aab18SMatthew Ahrens #include <sys/dmu_send.h> 1853b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h> 18678f17100SMatthew Ahrens #include <sys/dsl_bookmark.h> 1873b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h> 188a6f561b4SSašo Kiselkov #include <sys/zfeature.h> 189dfc11533SChris Williamson #include <sys/zcp.h> 19045818ee1SMatthew Ahrens #include <sys/zio_checksum.h> 1915cabbc6bSPrashanth Sreenivasa #include <sys/vdev_removal.h> 192*094e47e9SGeorge Wilson #include <sys/vdev_impl.h> 193*094e47e9SGeorge Wilson #include <sys/vdev_initialize.h> 194fa9e4066Sahrens 195fa9e4066Sahrens #include "zfs_namecheck.h" 196e9dbad6fSeschrock #include "zfs_prop.h" 197ecd6cf80Smarks #include "zfs_deleg.h" 1980a586ceaSMark Shellenbaum #include "zfs_comutil.h" 199fa9e4066Sahrens 200dfc11533SChris Williamson #include "lua.h" 201dfc11533SChris Williamson #include "lauxlib.h" 202dfc11533SChris Williamson 203fa9e4066Sahrens extern struct modlfs zfs_modlfs; 204fa9e4066Sahrens 205fa9e4066Sahrens extern void zfs_init(void); 206fa9e4066Sahrens extern void zfs_fini(void); 207fa9e4066Sahrens 208fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 209fa9e4066Sahrens dev_info_t *zfs_dip; 210fa9e4066Sahrens 2114445fffbSMatthew Ahrens uint_t zfs_fsyncer_key; 2124445fffbSMatthew Ahrens extern uint_t rrw_tsd_key; 2134445fffbSMatthew Ahrens static uint_t zfs_allow_log_key; 2144445fffbSMatthew Ahrens 2154445fffbSMatthew Ahrens typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); 2164445fffbSMatthew Ahrens typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); 2174445fffbSMatthew Ahrens typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); 218fa9e4066Sahrens 21954d692b7SGeorge Wilson typedef enum { 22054d692b7SGeorge Wilson NO_NAME, 22154d692b7SGeorge Wilson POOL_NAME, 22254d692b7SGeorge Wilson DATASET_NAME 22354d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 22454d692b7SGeorge Wilson 225f9af39baSGeorge Wilson typedef enum { 226f9af39baSGeorge Wilson POOL_CHECK_NONE = 1 << 0, 227f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED = 1 << 1, 2284445fffbSMatthew Ahrens POOL_CHECK_READONLY = 1 << 2, 229f9af39baSGeorge Wilson } zfs_ioc_poolcheck_t; 230f9af39baSGeorge Wilson 231fa9e4066Sahrens typedef struct zfs_ioc_vec { 2324445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *zvec_legacy_func; 233fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 234fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 23554d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 2364445fffbSMatthew Ahrens boolean_t zvec_allow_log; 237f9af39baSGeorge Wilson zfs_ioc_poolcheck_t zvec_pool_check; 2384445fffbSMatthew Ahrens boolean_t zvec_smush_outnvlist; 2394445fffbSMatthew Ahrens const char *zvec_name; 240fa9e4066Sahrens } zfs_ioc_vec_t; 241fa9e4066Sahrens 24214843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 24314843421SMatthew Ahrens static const char *userquota_perms[] = { 24414843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 24514843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 24614843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 24714843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 24814843421SMatthew Ahrens }; 24914843421SMatthew Ahrens 25014843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 25192241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 25292241e0bSTom Erickson cred_t *cr); 25392241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 25492241e0bSTom Erickson nvlist_t **errors); 2550a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 2560a48a24eStimh boolean_t *); 2574445fffbSMatthew Ahrens int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); 2584445fffbSMatthew Ahrens static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp); 2590a48a24eStimh 2602acef22dSMatthew Ahrens static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature); 261a6f561b4SSašo Kiselkov 262fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 263fa9e4066Sahrens void 264fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 265fa9e4066Sahrens { 266fa9e4066Sahrens const char *newfile; 2673f9d6ad7SLin Ling char buf[512]; 268fa9e4066Sahrens va_list adx; 269fa9e4066Sahrens 270fa9e4066Sahrens /* 271fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 272fa9e4066Sahrens */ 273fa9e4066Sahrens newfile = strrchr(file, '/'); 274fa9e4066Sahrens if (newfile != NULL) { 275fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 276fa9e4066Sahrens } else { 277fa9e4066Sahrens newfile = file; 278fa9e4066Sahrens } 279fa9e4066Sahrens 280fa9e4066Sahrens va_start(adx, fmt); 281fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 282fa9e4066Sahrens va_end(adx); 283fa9e4066Sahrens 284fa9e4066Sahrens /* 285fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 286fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 287fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 288fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 289fa9e4066Sahrens * arg0 = file name 290fa9e4066Sahrens * arg1 = function name 291fa9e4066Sahrens * arg2 = line number 292fa9e4066Sahrens * arg3 = message 293fa9e4066Sahrens */ 294fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 295fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 296fa9e4066Sahrens } 297fa9e4066Sahrens 298ecd6cf80Smarks static void 299228975ccSek history_str_free(char *buf) 300228975ccSek { 301228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 302228975ccSek } 303228975ccSek 304228975ccSek static char * 305228975ccSek history_str_get(zfs_cmd_t *zc) 306ecd6cf80Smarks { 30740feaa91Sahrens char *buf; 308ecd6cf80Smarks 309ecd6cf80Smarks if (zc->zc_history == NULL) 310228975ccSek return (NULL); 311e7437265Sahrens 312ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 313ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 314ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 315228975ccSek history_str_free(buf); 316228975ccSek return (NULL); 317ecd6cf80Smarks } 318ecd6cf80Smarks 319ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 320ecd6cf80Smarks 321228975ccSek return (buf); 322228975ccSek } 323ecd6cf80Smarks 32415e6edf1Sgw /* 32515e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 32615e6edf1Sgw */ 32715e6edf1Sgw static boolean_t 32815e6edf1Sgw zfs_is_bootfs(const char *name) 32915e6edf1Sgw { 330503ad85cSMatthew Ahrens objset_t *os; 33115e6edf1Sgw 332503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 333503ad85cSMatthew Ahrens boolean_t ret; 334b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 335503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 336503ad85cSMatthew Ahrens return (ret); 33715e6edf1Sgw } 338503ad85cSMatthew Ahrens return (B_FALSE); 33915e6edf1Sgw } 34015e6edf1Sgw 341c2a93d44Stimh /* 342f7170741SWill Andrews * Return non-zero if the spa version is less than requested version. 343c2a93d44Stimh */ 344da6c28aaSamw static int 3450a48a24eStimh zfs_earlier_version(const char *name, int version) 346da6c28aaSamw { 347da6c28aaSamw spa_t *spa; 348da6c28aaSamw 349da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 350da6c28aaSamw if (spa_version(spa) < version) { 351da6c28aaSamw spa_close(spa, FTAG); 352da6c28aaSamw return (1); 353da6c28aaSamw } 354da6c28aaSamw spa_close(spa, FTAG); 355da6c28aaSamw } 356da6c28aaSamw return (0); 357da6c28aaSamw } 358da6c28aaSamw 3599e6eda55Smarks /* 360745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 3619e6eda55Smarks */ 362745cd3c5Smaybee static boolean_t 363745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 3649e6eda55Smarks { 3659e6eda55Smarks objset_t *os; 366745cd3c5Smaybee boolean_t rc = B_TRUE; 3679e6eda55Smarks 368503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 369745cd3c5Smaybee uint64_t zplversion; 3709e6eda55Smarks 371503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 372503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 373503ad85cSMatthew Ahrens return (B_TRUE); 374503ad85cSMatthew Ahrens } 375503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 376745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 377745cd3c5Smaybee rc = zplversion < version; 378503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3799e6eda55Smarks } 3809e6eda55Smarks return (rc); 3819e6eda55Smarks } 3829e6eda55Smarks 383228975ccSek static void 384228975ccSek zfs_log_history(zfs_cmd_t *zc) 385228975ccSek { 386228975ccSek spa_t *spa; 387228975ccSek char *buf; 388ecd6cf80Smarks 389228975ccSek if ((buf = history_str_get(zc)) == NULL) 390228975ccSek return; 391228975ccSek 392228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 393228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 3944445fffbSMatthew Ahrens (void) spa_history_log(spa, buf); 395228975ccSek spa_close(spa, FTAG); 396228975ccSek } 397228975ccSek history_str_free(buf); 398ecd6cf80Smarks } 399ecd6cf80Smarks 400fa9e4066Sahrens /* 401fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 402fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 403fa9e4066Sahrens */ 404fa9e4066Sahrens /* ARGSUSED */ 405fa9e4066Sahrens static int 4064445fffbSMatthew Ahrens zfs_secpolicy_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 407fa9e4066Sahrens { 408fa9e4066Sahrens return (0); 409fa9e4066Sahrens } 410fa9e4066Sahrens 411fa9e4066Sahrens /* 412fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 413fa9e4066Sahrens * no privileges, but must be visible in the local zone. 414fa9e4066Sahrens */ 415fa9e4066Sahrens /* ARGSUSED */ 416fa9e4066Sahrens static int 4174445fffbSMatthew Ahrens zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 418fa9e4066Sahrens { 419fa9e4066Sahrens if (INGLOBALZONE(curproc) || 420ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 421fa9e4066Sahrens return (0); 422fa9e4066Sahrens 423be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 424fa9e4066Sahrens } 425fa9e4066Sahrens 426fa9e4066Sahrens static int 427a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) 428fa9e4066Sahrens { 429fa9e4066Sahrens int writable = 1; 430fa9e4066Sahrens 431fa9e4066Sahrens /* 432fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 433fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 434fa9e4066Sahrens */ 435fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 436fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 437be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 438fa9e4066Sahrens 439fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 440fa9e4066Sahrens /* 441fa9e4066Sahrens * If the fs is zoned, only root can access it from the 442fa9e4066Sahrens * global zone. 443fa9e4066Sahrens */ 444fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 445be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 446fa9e4066Sahrens } else { 447fa9e4066Sahrens /* 448fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 449fa9e4066Sahrens */ 450fa9e4066Sahrens if (!zoned) 451be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 452fa9e4066Sahrens 453fa9e4066Sahrens /* must be writable by this zone */ 454fa9e4066Sahrens if (!writable) 455be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 456fa9e4066Sahrens } 457fa9e4066Sahrens return (0); 458fa9e4066Sahrens } 459fa9e4066Sahrens 460a7f53a56SChris Kirby static int 461a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr) 462a7f53a56SChris Kirby { 463a7f53a56SChris Kirby uint64_t zoned; 464a7f53a56SChris Kirby 465a7f53a56SChris Kirby if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 466be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 467a7f53a56SChris Kirby 468a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 469a7f53a56SChris Kirby } 470a7f53a56SChris Kirby 471a7f53a56SChris Kirby static int 472a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) 473a7f53a56SChris Kirby { 474a7f53a56SChris Kirby uint64_t zoned; 475a7f53a56SChris Kirby 4763b2aab18SMatthew Ahrens if (dsl_prop_get_int_ds(ds, "zoned", &zoned)) 477be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 478a7f53a56SChris Kirby 479a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 480a7f53a56SChris Kirby } 481a7f53a56SChris Kirby 4824445fffbSMatthew Ahrens static int 4833b2aab18SMatthew Ahrens zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, 4843b2aab18SMatthew Ahrens const char *perm, cred_t *cr) 485fa9e4066Sahrens { 486fa9e4066Sahrens int error; 487fa9e4066Sahrens 48819b94df9SMatthew Ahrens error = zfs_dozonecheck_ds(name, ds, cr); 489ecd6cf80Smarks if (error == 0) { 490ecd6cf80Smarks error = secpolicy_zfs(cr); 4913b2aab18SMatthew Ahrens if (error != 0) 4924445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 493ecd6cf80Smarks } 494ecd6cf80Smarks return (error); 495ecd6cf80Smarks } 496ecd6cf80Smarks 4974445fffbSMatthew Ahrens static int 4983b2aab18SMatthew Ahrens zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 499a7f53a56SChris Kirby { 500a7f53a56SChris Kirby int error; 5013b2aab18SMatthew Ahrens dsl_dataset_t *ds; 5023b2aab18SMatthew Ahrens dsl_pool_t *dp; 503a7f53a56SChris Kirby 50425f7d993SMatthew Ahrens /* 50525f7d993SMatthew Ahrens * First do a quick check for root in the global zone, which 50625f7d993SMatthew Ahrens * is allowed to do all write_perms. This ensures that zfs_ioc_* 50725f7d993SMatthew Ahrens * will get to handle nonexistent datasets. 50825f7d993SMatthew Ahrens */ 50925f7d993SMatthew Ahrens if (INGLOBALZONE(curproc) && secpolicy_zfs(cr) == 0) 51025f7d993SMatthew Ahrens return (0); 51125f7d993SMatthew Ahrens 5123b2aab18SMatthew Ahrens error = dsl_pool_hold(name, FTAG, &dp); 5133b2aab18SMatthew Ahrens if (error != 0) 5143b2aab18SMatthew Ahrens return (error); 5153b2aab18SMatthew Ahrens 5163b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, name, FTAG, &ds); 5173b2aab18SMatthew Ahrens if (error != 0) { 5183b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 5193b2aab18SMatthew Ahrens return (error); 520a7f53a56SChris Kirby } 5213b2aab18SMatthew Ahrens 5223b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(name, ds, perm, cr); 5233b2aab18SMatthew Ahrens 5243b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 5253b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 526a7f53a56SChris Kirby return (error); 527a7f53a56SChris Kirby } 528a7f53a56SChris Kirby 5294201a95eSRic Aleshire /* 5304201a95eSRic Aleshire * Policy for setting the security label property. 5314201a95eSRic Aleshire * 5324201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 5334201a95eSRic Aleshire */ 5344201a95eSRic Aleshire static int 53592241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 5364201a95eSRic Aleshire { 5374201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 5384201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 5394201a95eSRic Aleshire boolean_t new_default = FALSE; 5404201a95eSRic Aleshire uint64_t zoned; 5414201a95eSRic Aleshire int needed_priv = -1; 5424201a95eSRic Aleshire int error; 5434201a95eSRic Aleshire 5444201a95eSRic Aleshire /* First get the existing dataset label. */ 5454201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 5464201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 5473b2aab18SMatthew Ahrens if (error != 0) 548be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5494201a95eSRic Aleshire 5504201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 5514201a95eSRic Aleshire new_default = TRUE; 5524201a95eSRic Aleshire 5534201a95eSRic Aleshire /* The label must be translatable */ 5544201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 555be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5564201a95eSRic Aleshire 5574201a95eSRic Aleshire /* 5584201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 5594201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 5604201a95eSRic Aleshire * are needed. 5614201a95eSRic Aleshire */ 5624201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 5634201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 564be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5654201a95eSRic Aleshire return (0); 5664201a95eSRic Aleshire } 5674201a95eSRic Aleshire 5684201a95eSRic Aleshire /* 5694201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 5704201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 5714201a95eSRic Aleshire * global zone. 5724201a95eSRic Aleshire */ 5734201a95eSRic Aleshire if (dsl_prop_get_integer(name, 5744201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 575be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5764201a95eSRic Aleshire if (!zoned) { 5774201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 578be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5794201a95eSRic Aleshire } 5804201a95eSRic Aleshire 5814201a95eSRic Aleshire /* 5824201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 5834201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 5844201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 5854201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 5864201a95eSRic Aleshire */ 5874201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 58892241e0bSTom Erickson objset_t *os; 58992241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 59092241e0bSTom Erickson 5914201a95eSRic Aleshire /* 5924201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 5934201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 5944201a95eSRic Aleshire */ 5954201a95eSRic Aleshire error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, 59692241e0bSTom Erickson setsl_tag, &os); 5973b2aab18SMatthew Ahrens if (error != 0) 598be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5994201a95eSRic Aleshire 60092241e0bSTom Erickson dmu_objset_disown(os, setsl_tag); 60192241e0bSTom Erickson 6024201a95eSRic Aleshire if (new_default) { 6034201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 6044201a95eSRic Aleshire goto out_check; 6054201a95eSRic Aleshire } 6064201a95eSRic Aleshire 6074201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 608be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 6094201a95eSRic Aleshire 6104201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 6114201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 6124201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 6134201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 6144201a95eSRic Aleshire } else { 6154201a95eSRic Aleshire /* dataset currently has a default label */ 6164201a95eSRic Aleshire if (!new_default) 6174201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 6184201a95eSRic Aleshire } 6194201a95eSRic Aleshire 6204201a95eSRic Aleshire out_check: 6214201a95eSRic Aleshire if (needed_priv != -1) 6224201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 6234201a95eSRic Aleshire return (0); 6244201a95eSRic Aleshire } 6254201a95eSRic Aleshire 626ecd6cf80Smarks static int 62792241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 62892241e0bSTom Erickson cred_t *cr) 629ecd6cf80Smarks { 63092241e0bSTom Erickson char *strval; 63192241e0bSTom Erickson 632ecd6cf80Smarks /* 633ecd6cf80Smarks * Check permissions for special properties. 634ecd6cf80Smarks */ 635ecd6cf80Smarks switch (prop) { 636ecd6cf80Smarks case ZFS_PROP_ZONED: 637ecd6cf80Smarks /* 638ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 639ecd6cf80Smarks */ 640ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 641be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 642ecd6cf80Smarks break; 643ecd6cf80Smarks 644ecd6cf80Smarks case ZFS_PROP_QUOTA: 645a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 646a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 647ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 648ecd6cf80Smarks uint64_t zoned; 6499adfa60dSMatthew Ahrens char setpoint[ZFS_MAX_DATASET_NAME_LEN]; 650ecd6cf80Smarks /* 651ecd6cf80Smarks * Unprivileged users are allowed to modify the 652a2afb611SJerry Jelinek * limit on things *under* (ie. contained by) 653ecd6cf80Smarks * the thing they own. 654ecd6cf80Smarks */ 65592241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 656ecd6cf80Smarks setpoint)) 657be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 65892241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 659be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 660ecd6cf80Smarks } 661db870a07Sahrens break; 6624201a95eSRic Aleshire 6634201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 6644201a95eSRic Aleshire if (!is_system_labeled()) 665be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 66692241e0bSTom Erickson 66792241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 66892241e0bSTom Erickson int err; 66992241e0bSTom Erickson 67092241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 67192241e0bSTom Erickson if (err != 0) 67292241e0bSTom Erickson return (err); 67392241e0bSTom Erickson } 6744201a95eSRic Aleshire break; 675ecd6cf80Smarks } 676ecd6cf80Smarks 67792241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 678ecd6cf80Smarks } 679ecd6cf80Smarks 6804445fffbSMatthew Ahrens /* ARGSUSED */ 6814445fffbSMatthew Ahrens static int 6824445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 683ecd6cf80Smarks { 684ecd6cf80Smarks int error; 685ecd6cf80Smarks 686ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 6873b2aab18SMatthew Ahrens if (error != 0) 688fa9e4066Sahrens return (error); 689fa9e4066Sahrens 690ecd6cf80Smarks /* 691ecd6cf80Smarks * permission to set permissions will be evaluated later in 692ecd6cf80Smarks * dsl_deleg_can_allow() 693ecd6cf80Smarks */ 694ecd6cf80Smarks return (0); 695ecd6cf80Smarks } 696ecd6cf80Smarks 6974445fffbSMatthew Ahrens /* ARGSUSED */ 6984445fffbSMatthew Ahrens static int 6994445fffbSMatthew Ahrens zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 700ecd6cf80Smarks { 701681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 702681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 703ecd6cf80Smarks } 704ecd6cf80Smarks 7054445fffbSMatthew Ahrens /* ARGSUSED */ 7064445fffbSMatthew Ahrens static int 7074445fffbSMatthew Ahrens zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 708ecd6cf80Smarks { 709a7f53a56SChris Kirby dsl_pool_t *dp; 710a7f53a56SChris Kirby dsl_dataset_t *ds; 711a7f53a56SChris Kirby char *cp; 712a7f53a56SChris Kirby int error; 713a7f53a56SChris Kirby 714a7f53a56SChris Kirby /* 715a7f53a56SChris Kirby * Generate the current snapshot name from the given objsetid, then 716a7f53a56SChris Kirby * use that name for the secpolicy/zone checks. 717a7f53a56SChris Kirby */ 718a7f53a56SChris Kirby cp = strchr(zc->zc_name, '@'); 719a7f53a56SChris Kirby if (cp == NULL) 720be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 7213b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 7223b2aab18SMatthew Ahrens if (error != 0) 723a7f53a56SChris Kirby return (error); 724a7f53a56SChris Kirby 725a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 7263b2aab18SMatthew Ahrens if (error != 0) { 7273b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 728a7f53a56SChris Kirby return (error); 7293b2aab18SMatthew Ahrens } 730a7f53a56SChris Kirby 731a7f53a56SChris Kirby dsl_dataset_name(ds, zc->zc_name); 732a7f53a56SChris Kirby 733a7f53a56SChris Kirby error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, 734a7f53a56SChris Kirby ZFS_DELEG_PERM_SEND, cr); 735a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 7363b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 737a7f53a56SChris Kirby 738a7f53a56SChris Kirby return (error); 739ecd6cf80Smarks } 740ecd6cf80Smarks 7414445fffbSMatthew Ahrens /* ARGSUSED */ 742743a77edSAlan Wright static int 7434445fffbSMatthew Ahrens zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 7444445fffbSMatthew Ahrens { 7454445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 7464445fffbSMatthew Ahrens ZFS_DELEG_PERM_SEND, cr)); 7474445fffbSMatthew Ahrens } 7484445fffbSMatthew Ahrens 7494445fffbSMatthew Ahrens /* ARGSUSED */ 7504445fffbSMatthew Ahrens static int 7514445fffbSMatthew Ahrens zfs_secpolicy_deleg_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 752743a77edSAlan Wright { 753743a77edSAlan Wright vnode_t *vp; 754743a77edSAlan Wright int error; 755743a77edSAlan Wright 756743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 757743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 758743a77edSAlan Wright return (error); 759743a77edSAlan Wright 760743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 761743a77edSAlan Wright 762743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 763743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 764743a77edSAlan Wright zc->zc_name) != 0)) { 765743a77edSAlan Wright VN_RELE(vp); 766be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 767743a77edSAlan Wright } 768743a77edSAlan Wright 769743a77edSAlan Wright VN_RELE(vp); 770743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 771743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 772743a77edSAlan Wright } 773743a77edSAlan Wright 774ecd6cf80Smarks int 7754445fffbSMatthew Ahrens zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 776ecd6cf80Smarks { 777ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 778be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 779ecd6cf80Smarks 7803cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 781ecd6cf80Smarks return (0); 782ecd6cf80Smarks } else { 7834445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 784743a77edSAlan Wright } 785743a77edSAlan Wright } 786ecd6cf80Smarks 787743a77edSAlan Wright int 7884445fffbSMatthew Ahrens zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 789743a77edSAlan Wright { 790743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 791be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 792ecd6cf80Smarks 793743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 794743a77edSAlan Wright return (0); 795743a77edSAlan Wright } else { 7964445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 797ecd6cf80Smarks } 798fa9e4066Sahrens } 799fa9e4066Sahrens 800fa9e4066Sahrens static int 801ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 802fa9e4066Sahrens { 803fa9e4066Sahrens char *cp; 804fa9e4066Sahrens 805fa9e4066Sahrens /* 806fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 807fa9e4066Sahrens */ 808ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 809ecd6cf80Smarks cp = strrchr(parent, '@'); 810fa9e4066Sahrens if (cp != NULL) { 811fa9e4066Sahrens cp[0] = '\0'; 812fa9e4066Sahrens } else { 813ecd6cf80Smarks cp = strrchr(parent, '/'); 814fa9e4066Sahrens if (cp == NULL) 815be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 816fa9e4066Sahrens cp[0] = '\0'; 817ecd6cf80Smarks } 818ecd6cf80Smarks 819ecd6cf80Smarks return (0); 820ecd6cf80Smarks } 821ecd6cf80Smarks 822ecd6cf80Smarks int 823ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 824ecd6cf80Smarks { 825ecd6cf80Smarks int error; 826ecd6cf80Smarks 827ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 828ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 829ecd6cf80Smarks return (error); 830ecd6cf80Smarks 831ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 832ecd6cf80Smarks } 833ecd6cf80Smarks 8344445fffbSMatthew Ahrens /* ARGSUSED */ 835ecd6cf80Smarks static int 8364445fffbSMatthew Ahrens zfs_secpolicy_destroy(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 837ecd6cf80Smarks { 838ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 839ecd6cf80Smarks } 840ecd6cf80Smarks 841cbf6f6aaSWilliam Gorrell /* 842cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 8434445fffbSMatthew Ahrens * descendant mount and destroy permissions. 844cbf6f6aaSWilliam Gorrell */ 8454445fffbSMatthew Ahrens /* ARGSUSED */ 846cbf6f6aaSWilliam Gorrell static int 8474445fffbSMatthew Ahrens zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 848cbf6f6aaSWilliam Gorrell { 8494445fffbSMatthew Ahrens nvlist_t *snaps; 8504445fffbSMatthew Ahrens nvpair_t *pair, *nextpair; 8514445fffbSMatthew Ahrens int error = 0; 852cbf6f6aaSWilliam Gorrell 8534445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 854be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 8554445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 8564445fffbSMatthew Ahrens pair = nextpair) { 8574445fffbSMatthew Ahrens nextpair = nvlist_next_nvpair(snaps, pair); 85878f17100SMatthew Ahrens error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); 85978f17100SMatthew Ahrens if (error == ENOENT) { 8604445fffbSMatthew Ahrens /* 8614445fffbSMatthew Ahrens * Ignore any snapshots that don't exist (we consider 8624445fffbSMatthew Ahrens * them "already destroyed"). Remove the name from the 8634445fffbSMatthew Ahrens * nvl here in case the snapshot is created between 8644445fffbSMatthew Ahrens * now and when we try to destroy it (in which case 8654445fffbSMatthew Ahrens * we don't want to destroy it since we haven't 8664445fffbSMatthew Ahrens * checked for permission). 8674445fffbSMatthew Ahrens */ 8684445fffbSMatthew Ahrens fnvlist_remove_nvpair(snaps, pair); 8694445fffbSMatthew Ahrens error = 0; 8704445fffbSMatthew Ahrens } 8714445fffbSMatthew Ahrens if (error != 0) 8724445fffbSMatthew Ahrens break; 8734445fffbSMatthew Ahrens } 874cbf6f6aaSWilliam Gorrell 875cbf6f6aaSWilliam Gorrell return (error); 876cbf6f6aaSWilliam Gorrell } 877cbf6f6aaSWilliam Gorrell 878ecd6cf80Smarks int 879ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 880ecd6cf80Smarks { 8819adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 882ecd6cf80Smarks int error; 883ecd6cf80Smarks 884ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 885ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 886ecd6cf80Smarks return (error); 887ecd6cf80Smarks 888ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 889ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 890ecd6cf80Smarks return (error); 891ecd6cf80Smarks 892ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 893ecd6cf80Smarks sizeof (parentname))) != 0) 894ecd6cf80Smarks return (error); 895ecd6cf80Smarks 896ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 897ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 898ecd6cf80Smarks return (error); 899ecd6cf80Smarks 900ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 901ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 902ecd6cf80Smarks return (error); 903ecd6cf80Smarks 904ecd6cf80Smarks return (error); 905ecd6cf80Smarks } 906ecd6cf80Smarks 9074445fffbSMatthew Ahrens /* ARGSUSED */ 908ecd6cf80Smarks static int 9094445fffbSMatthew Ahrens zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 910ecd6cf80Smarks { 911ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 912ecd6cf80Smarks } 913ecd6cf80Smarks 9144445fffbSMatthew Ahrens /* ARGSUSED */ 915ecd6cf80Smarks static int 9164445fffbSMatthew Ahrens zfs_secpolicy_promote(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 917ecd6cf80Smarks { 9183b2aab18SMatthew Ahrens dsl_pool_t *dp; 9193b2aab18SMatthew Ahrens dsl_dataset_t *clone; 920ecd6cf80Smarks int error; 921ecd6cf80Smarks 922ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 923ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9243b2aab18SMatthew Ahrens if (error != 0) 9253b2aab18SMatthew Ahrens return (error); 9263b2aab18SMatthew Ahrens 9273b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 9283b2aab18SMatthew Ahrens if (error != 0) 929ecd6cf80Smarks return (error); 930ecd6cf80Smarks 9313b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &clone); 932ecd6cf80Smarks 933ecd6cf80Smarks if (error == 0) { 9349adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 9353b2aab18SMatthew Ahrens dsl_dataset_t *origin = NULL; 936ecd6cf80Smarks dsl_dir_t *dd; 9373b2aab18SMatthew Ahrens dd = clone->ds_dir; 938ecd6cf80Smarks 939745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 940c1379625SJustin T. Gibbs dsl_dir_phys(dd)->dd_origin_obj, FTAG, &origin); 9413b2aab18SMatthew Ahrens if (error != 0) { 9423b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9433b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 944ecd6cf80Smarks return (error); 945ecd6cf80Smarks } 946ecd6cf80Smarks 9473b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(zc->zc_name, clone, 948ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 949ecd6cf80Smarks 9503b2aab18SMatthew Ahrens dsl_dataset_name(origin, parentname); 9513b2aab18SMatthew Ahrens if (error == 0) { 9523b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(parentname, origin, 953ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9543b2aab18SMatthew Ahrens } 9553b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9563b2aab18SMatthew Ahrens dsl_dataset_rele(origin, FTAG); 957ecd6cf80Smarks } 9583b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 959ecd6cf80Smarks return (error); 960ecd6cf80Smarks } 961ecd6cf80Smarks 9624445fffbSMatthew Ahrens /* ARGSUSED */ 963ecd6cf80Smarks static int 9644445fffbSMatthew Ahrens zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 965ecd6cf80Smarks { 966ecd6cf80Smarks int error; 967ecd6cf80Smarks 968ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 969ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 970ecd6cf80Smarks return (error); 971ecd6cf80Smarks 972ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 973ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 974ecd6cf80Smarks return (error); 975ecd6cf80Smarks 976ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 977ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 978ecd6cf80Smarks } 979ecd6cf80Smarks 980ecd6cf80Smarks int 981ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 982ecd6cf80Smarks { 983681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 984681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 985ecd6cf80Smarks } 986ecd6cf80Smarks 9874445fffbSMatthew Ahrens /* 9884445fffbSMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 9894445fffbSMatthew Ahrens */ 9904445fffbSMatthew Ahrens /* ARGSUSED */ 991ecd6cf80Smarks static int 9924445fffbSMatthew Ahrens zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 993ecd6cf80Smarks { 9944445fffbSMatthew Ahrens nvlist_t *snaps; 995d5285caeSGeorge Wilson int error = 0; 9964445fffbSMatthew Ahrens nvpair_t *pair; 997ecd6cf80Smarks 9984445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 999be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 10004445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 10014445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 10024445fffbSMatthew Ahrens char *name = nvpair_name(pair); 10034445fffbSMatthew Ahrens char *atp = strchr(name, '@'); 10044445fffbSMatthew Ahrens 10054445fffbSMatthew Ahrens if (atp == NULL) { 1006be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 10074445fffbSMatthew Ahrens break; 10084445fffbSMatthew Ahrens } 10094445fffbSMatthew Ahrens *atp = '\0'; 10104445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(name, cr); 10114445fffbSMatthew Ahrens *atp = '@'; 10124445fffbSMatthew Ahrens if (error != 0) 10134445fffbSMatthew Ahrens break; 10144445fffbSMatthew Ahrens } 10154445fffbSMatthew Ahrens return (error); 1016ecd6cf80Smarks } 1017ecd6cf80Smarks 101878f17100SMatthew Ahrens /* 101978f17100SMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 102078f17100SMatthew Ahrens */ 102178f17100SMatthew Ahrens /* ARGSUSED */ 102278f17100SMatthew Ahrens static int 102378f17100SMatthew Ahrens zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 102478f17100SMatthew Ahrens { 102578f17100SMatthew Ahrens int error = 0; 102678f17100SMatthew Ahrens 102778f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 102878f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 102978f17100SMatthew Ahrens char *name = nvpair_name(pair); 103078f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 103178f17100SMatthew Ahrens 103278f17100SMatthew Ahrens if (hashp == NULL) { 103378f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 103478f17100SMatthew Ahrens break; 103578f17100SMatthew Ahrens } 103678f17100SMatthew Ahrens *hashp = '\0'; 103778f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 103878f17100SMatthew Ahrens ZFS_DELEG_PERM_BOOKMARK, cr); 103978f17100SMatthew Ahrens *hashp = '#'; 104078f17100SMatthew Ahrens if (error != 0) 104178f17100SMatthew Ahrens break; 104278f17100SMatthew Ahrens } 104378f17100SMatthew Ahrens return (error); 104478f17100SMatthew Ahrens } 104578f17100SMatthew Ahrens 10465cabbc6bSPrashanth Sreenivasa /* ARGSUSED */ 10475cabbc6bSPrashanth Sreenivasa static int 10485cabbc6bSPrashanth Sreenivasa zfs_secpolicy_remap(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 10495cabbc6bSPrashanth Sreenivasa { 10505cabbc6bSPrashanth Sreenivasa return (zfs_secpolicy_write_perms(zc->zc_name, 10515cabbc6bSPrashanth Sreenivasa ZFS_DELEG_PERM_REMAP, cr)); 10525cabbc6bSPrashanth Sreenivasa } 10535cabbc6bSPrashanth Sreenivasa 105478f17100SMatthew Ahrens /* ARGSUSED */ 105578f17100SMatthew Ahrens static int 105678f17100SMatthew Ahrens zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 105778f17100SMatthew Ahrens { 105878f17100SMatthew Ahrens nvpair_t *pair, *nextpair; 105978f17100SMatthew Ahrens int error = 0; 106078f17100SMatthew Ahrens 106178f17100SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 106278f17100SMatthew Ahrens pair = nextpair) { 106378f17100SMatthew Ahrens char *name = nvpair_name(pair); 106478f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 106578f17100SMatthew Ahrens nextpair = nvlist_next_nvpair(innvl, pair); 106678f17100SMatthew Ahrens 106778f17100SMatthew Ahrens if (hashp == NULL) { 106878f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 106978f17100SMatthew Ahrens break; 107078f17100SMatthew Ahrens } 107178f17100SMatthew Ahrens 107278f17100SMatthew Ahrens *hashp = '\0'; 107378f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 107478f17100SMatthew Ahrens ZFS_DELEG_PERM_DESTROY, cr); 107578f17100SMatthew Ahrens *hashp = '#'; 107678f17100SMatthew Ahrens if (error == ENOENT) { 107778f17100SMatthew Ahrens /* 107878f17100SMatthew Ahrens * Ignore any filesystems that don't exist (we consider 107978f17100SMatthew Ahrens * their bookmarks "already destroyed"). Remove 108078f17100SMatthew Ahrens * the name from the nvl here in case the filesystem 108178f17100SMatthew Ahrens * is created between now and when we try to destroy 108278f17100SMatthew Ahrens * the bookmark (in which case we don't want to 108378f17100SMatthew Ahrens * destroy it since we haven't checked for permission). 108478f17100SMatthew Ahrens */ 108578f17100SMatthew Ahrens fnvlist_remove_nvpair(innvl, pair); 108678f17100SMatthew Ahrens error = 0; 108778f17100SMatthew Ahrens } 108878f17100SMatthew Ahrens if (error != 0) 108978f17100SMatthew Ahrens break; 109078f17100SMatthew Ahrens } 109178f17100SMatthew Ahrens 109278f17100SMatthew Ahrens return (error); 109378f17100SMatthew Ahrens } 109478f17100SMatthew Ahrens 10954445fffbSMatthew Ahrens /* ARGSUSED */ 1096ecd6cf80Smarks static int 10974445fffbSMatthew Ahrens zfs_secpolicy_log_history(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 10984445fffbSMatthew Ahrens { 10994445fffbSMatthew Ahrens /* 11004445fffbSMatthew Ahrens * Even root must have a proper TSD so that we know what pool 11014445fffbSMatthew Ahrens * to log to. 11024445fffbSMatthew Ahrens */ 11034445fffbSMatthew Ahrens if (tsd_get(zfs_allow_log_key) == NULL) 1104be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 11054445fffbSMatthew Ahrens return (0); 11064445fffbSMatthew Ahrens } 11074445fffbSMatthew Ahrens 11084445fffbSMatthew Ahrens static int 11094445fffbSMatthew Ahrens zfs_secpolicy_create_clone(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1110ecd6cf80Smarks { 11119adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 111292241e0bSTom Erickson int error; 11134445fffbSMatthew Ahrens char *origin; 1114ecd6cf80Smarks 1115ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 1116ecd6cf80Smarks sizeof (parentname))) != 0) 1117ecd6cf80Smarks return (error); 1118fa9e4066Sahrens 11194445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin) == 0 && 11204445fffbSMatthew Ahrens (error = zfs_secpolicy_write_perms(origin, 11214445fffbSMatthew Ahrens ZFS_DELEG_PERM_CLONE, cr)) != 0) 11224445fffbSMatthew Ahrens return (error); 1123fa9e4066Sahrens 1124ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 1125ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 1126ecd6cf80Smarks return (error); 1127ecd6cf80Smarks 11284445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(parentname, 11294445fffbSMatthew Ahrens ZFS_DELEG_PERM_MOUNT, cr)); 1130fa9e4066Sahrens } 1131fa9e4066Sahrens 1132fa9e4066Sahrens /* 1133fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 1134fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 1135fa9e4066Sahrens */ 1136fa9e4066Sahrens /* ARGSUSED */ 1137fa9e4066Sahrens static int 11384445fffbSMatthew Ahrens zfs_secpolicy_config(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1139fa9e4066Sahrens { 1140fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 1141be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 1142fa9e4066Sahrens 1143fa9e4066Sahrens return (0); 1144fa9e4066Sahrens } 1145fa9e4066Sahrens 114699d5e173STim Haley /* 114799d5e173STim Haley * Policy for object to name lookups. 114899d5e173STim Haley */ 114999d5e173STim Haley /* ARGSUSED */ 115099d5e173STim Haley static int 11514445fffbSMatthew Ahrens zfs_secpolicy_diff(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 115299d5e173STim Haley { 115399d5e173STim Haley int error; 115499d5e173STim Haley 115599d5e173STim Haley if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0) 115699d5e173STim Haley return (0); 115799d5e173STim Haley 115899d5e173STim Haley error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr); 115999d5e173STim Haley return (error); 116099d5e173STim Haley } 116199d5e173STim Haley 1162ea8dc4b6Seschrock /* 1163ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 1164ea8dc4b6Seschrock */ 1165ea8dc4b6Seschrock /* ARGSUSED */ 1166ea8dc4b6Seschrock static int 11674445fffbSMatthew Ahrens zfs_secpolicy_inject(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1168ea8dc4b6Seschrock { 1169ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 1170ea8dc4b6Seschrock } 1171ea8dc4b6Seschrock 11724445fffbSMatthew Ahrens /* ARGSUSED */ 1173e45ce728Sahrens static int 11744445fffbSMatthew Ahrens zfs_secpolicy_inherit_prop(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1175e45ce728Sahrens { 1176e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 1177e45ce728Sahrens 1178990b4856Slling if (prop == ZPROP_INVAL) { 1179e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 1180be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1181e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 1182e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 1183e45ce728Sahrens } else { 118492241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 118592241e0bSTom Erickson NULL, cr)); 1186e45ce728Sahrens } 1187e45ce728Sahrens } 1188e45ce728Sahrens 118914843421SMatthew Ahrens static int 11904445fffbSMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 119114843421SMatthew Ahrens { 11924445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 119314843421SMatthew Ahrens if (err) 119414843421SMatthew Ahrens return (err); 119514843421SMatthew Ahrens 119614843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1197be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 119814843421SMatthew Ahrens 119914843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 120014843421SMatthew Ahrens /* 120114843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 120214843421SMatthew Ahrens * themself, allow it. 120314843421SMatthew Ahrens */ 120414843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 120514843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 120614843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 120714843421SMatthew Ahrens return (0); 120814843421SMatthew Ahrens } else { 120914843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 121014843421SMatthew Ahrens return (0); 121114843421SMatthew Ahrens } 121214843421SMatthew Ahrens } 121314843421SMatthew Ahrens 121414843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 121514843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 121614843421SMatthew Ahrens } 121714843421SMatthew Ahrens 121814843421SMatthew Ahrens static int 12194445fffbSMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 122014843421SMatthew Ahrens { 12214445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 122214843421SMatthew Ahrens if (err) 122314843421SMatthew Ahrens return (err); 122414843421SMatthew Ahrens 122514843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1226be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 122714843421SMatthew Ahrens 122814843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 122914843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 123014843421SMatthew Ahrens } 123114843421SMatthew Ahrens 12324445fffbSMatthew Ahrens /* ARGSUSED */ 123314843421SMatthew Ahrens static int 12344445fffbSMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 123514843421SMatthew Ahrens { 123692241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 123792241e0bSTom Erickson NULL, cr)); 123814843421SMatthew Ahrens } 123914843421SMatthew Ahrens 12404445fffbSMatthew Ahrens /* ARGSUSED */ 1241842727c2SChris Kirby static int 12424445fffbSMatthew Ahrens zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1243842727c2SChris Kirby { 12443b2aab18SMatthew Ahrens nvpair_t *pair; 12453b2aab18SMatthew Ahrens nvlist_t *holds; 12463b2aab18SMatthew Ahrens int error; 12473b2aab18SMatthew Ahrens 12483b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(innvl, "holds", &holds); 12493b2aab18SMatthew Ahrens if (error != 0) 1250be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 12513b2aab18SMatthew Ahrens 12523b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 12533b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(holds, pair)) { 12549adfa60dSMatthew Ahrens char fsname[ZFS_MAX_DATASET_NAME_LEN]; 12553b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12563b2aab18SMatthew Ahrens if (error != 0) 12573b2aab18SMatthew Ahrens return (error); 12583b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12593b2aab18SMatthew Ahrens ZFS_DELEG_PERM_HOLD, cr); 12603b2aab18SMatthew Ahrens if (error != 0) 12613b2aab18SMatthew Ahrens return (error); 12623b2aab18SMatthew Ahrens } 12633b2aab18SMatthew Ahrens return (0); 1264842727c2SChris Kirby } 1265842727c2SChris Kirby 12664445fffbSMatthew Ahrens /* ARGSUSED */ 1267842727c2SChris Kirby static int 12684445fffbSMatthew Ahrens zfs_secpolicy_release(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1269842727c2SChris Kirby { 12703b2aab18SMatthew Ahrens nvpair_t *pair; 12713b2aab18SMatthew Ahrens int error; 12723b2aab18SMatthew Ahrens 12733b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 12743b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(innvl, pair)) { 12759adfa60dSMatthew Ahrens char fsname[ZFS_MAX_DATASET_NAME_LEN]; 12763b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12773b2aab18SMatthew Ahrens if (error != 0) 12783b2aab18SMatthew Ahrens return (error); 12793b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12803b2aab18SMatthew Ahrens ZFS_DELEG_PERM_RELEASE, cr); 12813b2aab18SMatthew Ahrens if (error != 0) 12823b2aab18SMatthew Ahrens return (error); 12833b2aab18SMatthew Ahrens } 12843b2aab18SMatthew Ahrens return (0); 1285842727c2SChris Kirby } 1286842727c2SChris Kirby 128799d5e173STim Haley /* 128899d5e173STim Haley * Policy for allowing temporary snapshots to be taken or released 128999d5e173STim Haley */ 129099d5e173STim Haley static int 12914445fffbSMatthew Ahrens zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 129299d5e173STim Haley { 129399d5e173STim Haley /* 129499d5e173STim Haley * A temporary snapshot is the same as a snapshot, 129599d5e173STim Haley * hold, destroy and release all rolled into one. 129699d5e173STim Haley * Delegated diff alone is sufficient that we allow this. 129799d5e173STim Haley */ 129899d5e173STim Haley int error; 129999d5e173STim Haley 130099d5e173STim Haley if ((error = zfs_secpolicy_write_perms(zc->zc_name, 130199d5e173STim Haley ZFS_DELEG_PERM_DIFF, cr)) == 0) 130299d5e173STim Haley return (0); 130399d5e173STim Haley 13044445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); 13053b2aab18SMatthew Ahrens if (error == 0) 13064445fffbSMatthew Ahrens error = zfs_secpolicy_hold(zc, innvl, cr); 13073b2aab18SMatthew Ahrens if (error == 0) 13084445fffbSMatthew Ahrens error = zfs_secpolicy_release(zc, innvl, cr); 13093b2aab18SMatthew Ahrens if (error == 0) 13104445fffbSMatthew Ahrens error = zfs_secpolicy_destroy(zc, innvl, cr); 131199d5e173STim Haley return (error); 131299d5e173STim Haley } 131399d5e173STim Haley 1314fa9e4066Sahrens /* 1315fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 1316fa9e4066Sahrens */ 1317fa9e4066Sahrens static int 1318478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 1319fa9e4066Sahrens { 1320fa9e4066Sahrens char *packed; 1321fa9e4066Sahrens int error; 1322990b4856Slling nvlist_t *list = NULL; 1323fa9e4066Sahrens 1324fa9e4066Sahrens /* 1325e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 1326fa9e4066Sahrens */ 1327990b4856Slling if (size == 0) 1328be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1329fa9e4066Sahrens 1330fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 1331fa9e4066Sahrens 1332478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 1333478ed9adSEric Taylor iflag)) != 0) { 1334fa9e4066Sahrens kmem_free(packed, size); 1335c71c00bbSRichard Yao return (SET_ERROR(EFAULT)); 1336fa9e4066Sahrens } 1337fa9e4066Sahrens 1338990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 1339fa9e4066Sahrens kmem_free(packed, size); 1340fa9e4066Sahrens return (error); 1341fa9e4066Sahrens } 1342fa9e4066Sahrens 1343fa9e4066Sahrens kmem_free(packed, size); 1344fa9e4066Sahrens 1345990b4856Slling *nvp = list; 1346fa9e4066Sahrens return (0); 1347fa9e4066Sahrens } 1348fa9e4066Sahrens 13494445fffbSMatthew Ahrens /* 13504445fffbSMatthew Ahrens * Reduce the size of this nvlist until it can be serialized in 'max' bytes. 13514445fffbSMatthew Ahrens * Entries will be removed from the end of the nvlist, and one int32 entry 13524445fffbSMatthew Ahrens * named "N_MORE_ERRORS" will be added indicating how many entries were 13534445fffbSMatthew Ahrens * removed. 13544445fffbSMatthew Ahrens */ 135592241e0bSTom Erickson static int 13564445fffbSMatthew Ahrens nvlist_smush(nvlist_t *errors, size_t max) 135792241e0bSTom Erickson { 135892241e0bSTom Erickson size_t size; 135992241e0bSTom Erickson 13604445fffbSMatthew Ahrens size = fnvlist_size(errors); 136192241e0bSTom Erickson 13624445fffbSMatthew Ahrens if (size > max) { 136392241e0bSTom Erickson nvpair_t *more_errors; 136492241e0bSTom Erickson int n = 0; 136592241e0bSTom Erickson 13664445fffbSMatthew Ahrens if (max < 1024) 1367be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 136892241e0bSTom Erickson 13694445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, 0); 13704445fffbSMatthew Ahrens more_errors = nvlist_prev_nvpair(errors, NULL); 137192241e0bSTom Erickson 137292241e0bSTom Erickson do { 13734445fffbSMatthew Ahrens nvpair_t *pair = nvlist_prev_nvpair(errors, 137492241e0bSTom Erickson more_errors); 13754445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, pair); 137692241e0bSTom Erickson n++; 13774445fffbSMatthew Ahrens size = fnvlist_size(errors); 13784445fffbSMatthew Ahrens } while (size > max); 137992241e0bSTom Erickson 13804445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, more_errors); 13814445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, n); 13824445fffbSMatthew Ahrens ASSERT3U(fnvlist_size(errors), <=, max); 138392241e0bSTom Erickson } 138492241e0bSTom Erickson 138592241e0bSTom Erickson return (0); 138692241e0bSTom Erickson } 138792241e0bSTom Erickson 1388e9dbad6fSeschrock static int 1389e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 1390e9dbad6fSeschrock { 1391e9dbad6fSeschrock char *packed = NULL; 13926e27f868SSam Falkner int error = 0; 1393e9dbad6fSeschrock size_t size; 1394e9dbad6fSeschrock 13954445fffbSMatthew Ahrens size = fnvlist_size(nvl); 1396e9dbad6fSeschrock 1397e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 1398be6fd75aSMatthew Ahrens error = SET_ERROR(ENOMEM); 1399e9dbad6fSeschrock } else { 14004445fffbSMatthew Ahrens packed = fnvlist_pack(nvl, &size); 14016e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 14026e27f868SSam Falkner size, zc->zc_iflags) != 0) 1403be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 14044445fffbSMatthew Ahrens fnvlist_pack_free(packed, size); 1405e9dbad6fSeschrock } 1406e9dbad6fSeschrock 1407e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 14084445fffbSMatthew Ahrens zc->zc_nvlist_dst_filled = B_TRUE; 1409e9dbad6fSeschrock return (error); 1410e9dbad6fSeschrock } 1411e9dbad6fSeschrock 1412dfc11533SChris Williamson int 1413dfc11533SChris Williamson getzfsvfs_impl(objset_t *os, zfsvfs_t **zfvp) 141414843421SMatthew Ahrens { 1415dfc11533SChris Williamson int error = 0; 1416503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1417be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1418503ad85cSMatthew Ahrens } 141914843421SMatthew Ahrens 1420503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 1421af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 1422af4c679fSSean McEnroe if (*zfvp) { 1423af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 142414843421SMatthew Ahrens } else { 1425be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 142614843421SMatthew Ahrens } 1427503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 1428dfc11533SChris Williamson return (error); 1429dfc11533SChris Williamson } 1430dfc11533SChris Williamson 1431ed992b0aSSerapheim Dimitropoulos int 1432dfc11533SChris Williamson getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 1433dfc11533SChris Williamson { 1434dfc11533SChris Williamson objset_t *os; 1435dfc11533SChris Williamson int error; 1436dfc11533SChris Williamson 1437dfc11533SChris Williamson error = dmu_objset_hold(dsname, FTAG, &os); 1438dfc11533SChris Williamson if (error != 0) 1439dfc11533SChris Williamson return (error); 1440dfc11533SChris Williamson 1441dfc11533SChris Williamson error = getzfsvfs_impl(os, zfvp); 1442503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 144314843421SMatthew Ahrens return (error); 144414843421SMatthew Ahrens } 144514843421SMatthew Ahrens 144614843421SMatthew Ahrens /* 144714843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 144814843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 1449ad135b5dSChristopher Siden * If 'writer' is set, the z_teardown_lock will be held for RW_WRITER, 1450ad135b5dSChristopher Siden * which prevents all vnode ops from running. 145114843421SMatthew Ahrens */ 145214843421SMatthew Ahrens static int 14531412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) 145414843421SMatthew Ahrens { 145514843421SMatthew Ahrens int error = 0; 145614843421SMatthew Ahrens 1457af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1458af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 145914843421SMatthew Ahrens if (error == 0) { 1460c9030f6cSAlexander Motin rrm_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : 14611412a1a2SMark Shellenbaum RW_READER, tag); 1462af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 146314843421SMatthew Ahrens /* 146414843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 146514843421SMatthew Ahrens * thread should be just about to disassociate the 146614843421SMatthew Ahrens * objset from the zfsvfs. 146714843421SMatthew Ahrens */ 1468c9030f6cSAlexander Motin rrm_exit(&(*zfvp)->z_teardown_lock, tag); 1469be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 147014843421SMatthew Ahrens } 147114843421SMatthew Ahrens } 147214843421SMatthew Ahrens return (error); 147314843421SMatthew Ahrens } 147414843421SMatthew Ahrens 147514843421SMatthew Ahrens static void 147614843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 147714843421SMatthew Ahrens { 1478c9030f6cSAlexander Motin rrm_exit(&zfsvfs->z_teardown_lock, tag); 147914843421SMatthew Ahrens 148014843421SMatthew Ahrens if (zfsvfs->z_vfs) { 148114843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 148214843421SMatthew Ahrens } else { 1483503ad85cSMatthew Ahrens dmu_objset_disown(zfsvfs->z_os, zfsvfs); 148414843421SMatthew Ahrens zfsvfs_free(zfsvfs); 148514843421SMatthew Ahrens } 148614843421SMatthew Ahrens } 148714843421SMatthew Ahrens 1488fa9e4066Sahrens static int 1489fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1490fa9e4066Sahrens { 1491fa9e4066Sahrens int error; 1492990b4856Slling nvlist_t *config, *props = NULL; 14930a48a24eStimh nvlist_t *rootprops = NULL; 14940a48a24eStimh nvlist_t *zplprops = NULL; 1495fa9e4066Sahrens 1496990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1497478ed9adSEric Taylor zc->zc_iflags, &config)) 1498fa9e4066Sahrens return (error); 14992a6b87f0Sek 1500990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1501478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1502478ed9adSEric Taylor zc->zc_iflags, &props))) { 1503990b4856Slling nvlist_free(config); 1504990b4856Slling return (error); 1505990b4856Slling } 1506990b4856Slling 15070a48a24eStimh if (props) { 15080a48a24eStimh nvlist_t *nvl = NULL; 15090a48a24eStimh uint64_t version = SPA_VERSION; 15100a48a24eStimh 15110a48a24eStimh (void) nvlist_lookup_uint64(props, 15120a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 1513ad135b5dSChristopher Siden if (!SPA_VERSION_IS_SUPPORTED(version)) { 1514be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 15150a48a24eStimh goto pool_props_bad; 15160a48a24eStimh } 15170a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 15180a48a24eStimh if (nvl) { 15190a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 15200a48a24eStimh if (error != 0) { 15210a48a24eStimh nvlist_free(config); 15220a48a24eStimh nvlist_free(props); 15230a48a24eStimh return (error); 15240a48a24eStimh } 15250a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 15260a48a24eStimh } 15270a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 15280a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 15290a48a24eStimh zplprops, NULL); 15303b2aab18SMatthew Ahrens if (error != 0) 15310a48a24eStimh goto pool_props_bad; 15320a48a24eStimh } 15330a48a24eStimh 15344445fffbSMatthew Ahrens error = spa_create(zc->zc_name, config, props, zplprops); 15350a48a24eStimh 15360a48a24eStimh /* 15370a48a24eStimh * Set the remaining root properties 15380a48a24eStimh */ 153992241e0bSTom Erickson if (!error && (error = zfs_set_prop_nvlist(zc->zc_name, 154092241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 15410a48a24eStimh (void) spa_destroy(zc->zc_name); 1542fa9e4066Sahrens 15430a48a24eStimh pool_props_bad: 15440a48a24eStimh nvlist_free(rootprops); 15450a48a24eStimh nvlist_free(zplprops); 1546fa9e4066Sahrens nvlist_free(config); 15470a48a24eStimh nvlist_free(props); 1548990b4856Slling 1549fa9e4066Sahrens return (error); 1550fa9e4066Sahrens } 1551fa9e4066Sahrens 1552fa9e4066Sahrens static int 1553fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1554fa9e4066Sahrens { 1555ecd6cf80Smarks int error; 1556ecd6cf80Smarks zfs_log_history(zc); 1557ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1558681d9761SEric Taylor if (error == 0) 1559681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1560ecd6cf80Smarks return (error); 1561fa9e4066Sahrens } 1562fa9e4066Sahrens 1563fa9e4066Sahrens static int 1564fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1565fa9e4066Sahrens { 1566990b4856Slling nvlist_t *config, *props = NULL; 1567fa9e4066Sahrens uint64_t guid; 1568468c413aSTim Haley int error; 1569fa9e4066Sahrens 1570990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1571478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1572990b4856Slling return (error); 1573990b4856Slling 1574990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1575478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1576478ed9adSEric Taylor zc->zc_iflags, &props))) { 1577990b4856Slling nvlist_free(config); 1578fa9e4066Sahrens return (error); 1579990b4856Slling } 1580fa9e4066Sahrens 1581fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1582ea8dc4b6Seschrock guid != zc->zc_guid) 1583be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 1584fa9e4066Sahrens else 15854b964adaSGeorge Wilson error = spa_import(zc->zc_name, config, props, zc->zc_cookie); 1586fa9e4066Sahrens 15874b964adaSGeorge Wilson if (zc->zc_nvlist_dst != 0) { 15884b964adaSGeorge Wilson int err; 15894b964adaSGeorge Wilson 15904b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 15914b964adaSGeorge Wilson error = err; 15924b964adaSGeorge Wilson } 1593468c413aSTim Haley 1594fa9e4066Sahrens nvlist_free(config); 1595fa9e4066Sahrens 1596aab83bb8SJosef 'Jeff' Sipek nvlist_free(props); 1597990b4856Slling 1598fa9e4066Sahrens return (error); 1599fa9e4066Sahrens } 1600fa9e4066Sahrens 1601fa9e4066Sahrens static int 1602fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1603fa9e4066Sahrens { 1604ecd6cf80Smarks int error; 160589a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1606394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 160789a89ebfSlling 1608ecd6cf80Smarks zfs_log_history(zc); 1609394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1610681d9761SEric Taylor if (error == 0) 1611681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1612ecd6cf80Smarks return (error); 1613fa9e4066Sahrens } 1614fa9e4066Sahrens 1615fa9e4066Sahrens static int 1616fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1617fa9e4066Sahrens { 1618fa9e4066Sahrens nvlist_t *configs; 1619fa9e4066Sahrens int error; 1620fa9e4066Sahrens 1621fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1622be6fd75aSMatthew Ahrens return (SET_ERROR(EEXIST)); 1623fa9e4066Sahrens 1624e9dbad6fSeschrock error = put_nvlist(zc, configs); 1625fa9e4066Sahrens 1626fa9e4066Sahrens nvlist_free(configs); 1627fa9e4066Sahrens 1628fa9e4066Sahrens return (error); 1629fa9e4066Sahrens } 1630fa9e4066Sahrens 1631ad135b5dSChristopher Siden /* 1632ad135b5dSChristopher Siden * inputs: 1633ad135b5dSChristopher Siden * zc_name name of the pool 1634ad135b5dSChristopher Siden * 1635ad135b5dSChristopher Siden * outputs: 1636ad135b5dSChristopher Siden * zc_cookie real errno 1637ad135b5dSChristopher Siden * zc_nvlist_dst config nvlist 1638ad135b5dSChristopher Siden * zc_nvlist_dst_size size of config nvlist 1639ad135b5dSChristopher Siden */ 1640fa9e4066Sahrens static int 1641fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1642fa9e4066Sahrens { 1643fa9e4066Sahrens nvlist_t *config; 1644fa9e4066Sahrens int error; 1645ea8dc4b6Seschrock int ret = 0; 1646fa9e4066Sahrens 1647e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1648e9dbad6fSeschrock sizeof (zc->zc_value)); 1649fa9e4066Sahrens 1650fa9e4066Sahrens if (config != NULL) { 1651e9dbad6fSeschrock ret = put_nvlist(zc, config); 1652fa9e4066Sahrens nvlist_free(config); 1653ea8dc4b6Seschrock 1654ea8dc4b6Seschrock /* 1655ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1656ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1657ea8dc4b6Seschrock * in 'zc_cookie'. 1658ea8dc4b6Seschrock */ 1659ea8dc4b6Seschrock zc->zc_cookie = error; 1660fa9e4066Sahrens } else { 1661ea8dc4b6Seschrock ret = error; 1662fa9e4066Sahrens } 1663fa9e4066Sahrens 1664ea8dc4b6Seschrock return (ret); 1665fa9e4066Sahrens } 1666fa9e4066Sahrens 1667fa9e4066Sahrens /* 1668fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1669fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1670fa9e4066Sahrens */ 1671fa9e4066Sahrens static int 1672fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1673fa9e4066Sahrens { 1674fa9e4066Sahrens nvlist_t *tryconfig, *config; 1675fa9e4066Sahrens int error; 1676fa9e4066Sahrens 1677990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1678478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1679fa9e4066Sahrens return (error); 1680fa9e4066Sahrens 1681fa9e4066Sahrens config = spa_tryimport(tryconfig); 1682fa9e4066Sahrens 1683fa9e4066Sahrens nvlist_free(tryconfig); 1684fa9e4066Sahrens 1685fa9e4066Sahrens if (config == NULL) 1686be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1687fa9e4066Sahrens 1688e9dbad6fSeschrock error = put_nvlist(zc, config); 1689fa9e4066Sahrens nvlist_free(config); 1690fa9e4066Sahrens 1691fa9e4066Sahrens return (error); 1692fa9e4066Sahrens } 1693fa9e4066Sahrens 16943f9d6ad7SLin Ling /* 16953f9d6ad7SLin Ling * inputs: 16963f9d6ad7SLin Ling * zc_name name of the pool 16973f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 16981702cce7SAlek Pinchuk * zc_flags scrub pause/resume flag (pool_scrub_cmd_t) 16993f9d6ad7SLin Ling */ 1700fa9e4066Sahrens static int 17013f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1702fa9e4066Sahrens { 1703fa9e4066Sahrens spa_t *spa; 1704fa9e4066Sahrens int error; 1705fa9e4066Sahrens 170606eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 170706eeb2adSek return (error); 170806eeb2adSek 17091702cce7SAlek Pinchuk if (zc->zc_flags >= POOL_SCRUB_FLAGS_END) 17101702cce7SAlek Pinchuk return (SET_ERROR(EINVAL)); 17111702cce7SAlek Pinchuk 17121702cce7SAlek Pinchuk if (zc->zc_flags == POOL_SCRUB_PAUSE) 17131702cce7SAlek Pinchuk error = spa_scrub_pause_resume(spa, POOL_SCRUB_PAUSE); 17141702cce7SAlek Pinchuk else if (zc->zc_cookie == POOL_SCAN_NONE) 17153f9d6ad7SLin Ling error = spa_scan_stop(spa); 17163f9d6ad7SLin Ling else 17173f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 171806eeb2adSek 171906eeb2adSek spa_close(spa, FTAG); 172006eeb2adSek 1721fa9e4066Sahrens return (error); 1722fa9e4066Sahrens } 1723fa9e4066Sahrens 1724fa9e4066Sahrens static int 1725fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1726fa9e4066Sahrens { 1727fa9e4066Sahrens spa_t *spa; 1728fa9e4066Sahrens int error; 1729fa9e4066Sahrens 1730fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1731fa9e4066Sahrens if (error == 0) { 1732fa9e4066Sahrens spa_freeze(spa); 1733fa9e4066Sahrens spa_close(spa, FTAG); 1734fa9e4066Sahrens } 1735fa9e4066Sahrens return (error); 1736fa9e4066Sahrens } 1737fa9e4066Sahrens 1738eaca9bbdSeschrock static int 1739eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1740eaca9bbdSeschrock { 1741eaca9bbdSeschrock spa_t *spa; 1742eaca9bbdSeschrock int error; 1743eaca9bbdSeschrock 174406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 174506eeb2adSek return (error); 174606eeb2adSek 1747ad135b5dSChristopher Siden if (zc->zc_cookie < spa_version(spa) || 1748ad135b5dSChristopher Siden !SPA_VERSION_IS_SUPPORTED(zc->zc_cookie)) { 1749558d2d50Slling spa_close(spa, FTAG); 1750be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1751558d2d50Slling } 1752558d2d50Slling 1753990b4856Slling spa_upgrade(spa, zc->zc_cookie); 175406eeb2adSek spa_close(spa, FTAG); 175506eeb2adSek 175606eeb2adSek return (error); 175706eeb2adSek } 175806eeb2adSek 175906eeb2adSek static int 176006eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 176106eeb2adSek { 176206eeb2adSek spa_t *spa; 176306eeb2adSek char *hist_buf; 176406eeb2adSek uint64_t size; 176506eeb2adSek int error; 176606eeb2adSek 176706eeb2adSek if ((size = zc->zc_history_len) == 0) 1768be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 176906eeb2adSek 177006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 177106eeb2adSek return (error); 177206eeb2adSek 1773e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1774d7306b64Sek spa_close(spa, FTAG); 1775be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 1776d7306b64Sek } 1777d7306b64Sek 177806eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 177906eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 178006eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1781478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1782478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1783478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 178406eeb2adSek } 178506eeb2adSek 178606eeb2adSek spa_close(spa, FTAG); 178706eeb2adSek kmem_free(hist_buf, size); 178806eeb2adSek return (error); 178906eeb2adSek } 179006eeb2adSek 1791e9103aaeSGarrett D'Amore static int 1792e9103aaeSGarrett D'Amore zfs_ioc_pool_reguid(zfs_cmd_t *zc) 1793e9103aaeSGarrett D'Amore { 1794e9103aaeSGarrett D'Amore spa_t *spa; 1795e9103aaeSGarrett D'Amore int error; 1796e9103aaeSGarrett D'Amore 1797e9103aaeSGarrett D'Amore error = spa_open(zc->zc_name, &spa, FTAG); 1798e9103aaeSGarrett D'Amore if (error == 0) { 1799e9103aaeSGarrett D'Amore error = spa_change_guid(spa); 1800e9103aaeSGarrett D'Amore spa_close(spa, FTAG); 1801e9103aaeSGarrett D'Amore } 1802e9103aaeSGarrett D'Amore return (error); 1803e9103aaeSGarrett D'Amore } 1804e9103aaeSGarrett D'Amore 180555434c77Sek static int 180655434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 180755434c77Sek { 18083b2aab18SMatthew Ahrens return (dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)); 180955434c77Sek } 181055434c77Sek 1811503ad85cSMatthew Ahrens /* 1812503ad85cSMatthew Ahrens * inputs: 1813503ad85cSMatthew Ahrens * zc_name name of filesystem 1814503ad85cSMatthew Ahrens * zc_obj object to find 1815503ad85cSMatthew Ahrens * 1816503ad85cSMatthew Ahrens * outputs: 1817503ad85cSMatthew Ahrens * zc_value name of object 1818503ad85cSMatthew Ahrens */ 181955434c77Sek static int 182055434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 182155434c77Sek { 1822503ad85cSMatthew Ahrens objset_t *os; 182355434c77Sek int error; 182455434c77Sek 1825503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1826503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 182755434c77Sek return (error); 1828503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1829503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 1830be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1831503ad85cSMatthew Ahrens } 1832503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 183355434c77Sek sizeof (zc->zc_value)); 1834503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 183555434c77Sek 183655434c77Sek return (error); 183755434c77Sek } 183855434c77Sek 183999d5e173STim Haley /* 184099d5e173STim Haley * inputs: 184199d5e173STim Haley * zc_name name of filesystem 184299d5e173STim Haley * zc_obj object to find 184399d5e173STim Haley * 184499d5e173STim Haley * outputs: 184599d5e173STim Haley * zc_stat stats on object 184699d5e173STim Haley * zc_value path to object 184799d5e173STim Haley */ 184899d5e173STim Haley static int 184999d5e173STim Haley zfs_ioc_obj_to_stats(zfs_cmd_t *zc) 185099d5e173STim Haley { 185199d5e173STim Haley objset_t *os; 185299d5e173STim Haley int error; 185399d5e173STim Haley 185499d5e173STim Haley /* XXX reading from objset not owned */ 185599d5e173STim Haley if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0) 185699d5e173STim Haley return (error); 185799d5e173STim Haley if (dmu_objset_type(os) != DMU_OST_ZFS) { 185899d5e173STim Haley dmu_objset_rele(os, FTAG); 1859be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 186099d5e173STim Haley } 186199d5e173STim Haley error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value, 186299d5e173STim Haley sizeof (zc->zc_value)); 186399d5e173STim Haley dmu_objset_rele(os, FTAG); 186499d5e173STim Haley 186599d5e173STim Haley return (error); 186699d5e173STim Haley } 186799d5e173STim Haley 1868fa9e4066Sahrens static int 1869fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1870fa9e4066Sahrens { 1871fa9e4066Sahrens spa_t *spa; 1872fa9e4066Sahrens int error; 1873e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1874e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1875fa9e4066Sahrens 1876fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1877fa9e4066Sahrens if (error != 0) 1878fa9e4066Sahrens return (error); 1879fa9e4066Sahrens 1880fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1881478ed9adSEric Taylor zc->zc_iflags, &config); 1882fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1883fa94a07fSbrendan &l2cache, &nl2cache); 1884fa94a07fSbrendan 1885e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1886e7cbe64fSgw &spares, &nspares); 1887e7cbe64fSgw 1888b1b8ab34Slling /* 1889b1b8ab34Slling * A root pool with concatenated devices is not supported. 1890e7cbe64fSgw * Thus, can not add a device to a root pool. 1891e7cbe64fSgw * 1892e7cbe64fSgw * Intent log device can not be added to a rootpool because 1893e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1894e7cbe64fSgw * can not be accessed during the mountroot time. 1895e7cbe64fSgw * 1896e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1897b1b8ab34Slling */ 1898b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 18991195e687SMark J Musante nvlist_free(config); 1900b1b8ab34Slling spa_close(spa, FTAG); 1901be6fd75aSMatthew Ahrens return (SET_ERROR(EDOM)); 1902b1b8ab34Slling } 1903b1b8ab34Slling 1904fa94a07fSbrendan if (error == 0) { 1905fa9e4066Sahrens error = spa_vdev_add(spa, config); 1906fa9e4066Sahrens nvlist_free(config); 1907fa9e4066Sahrens } 1908fa9e4066Sahrens spa_close(spa, FTAG); 1909fa9e4066Sahrens return (error); 1910fa9e4066Sahrens } 1911fa9e4066Sahrens 19123f9d6ad7SLin Ling /* 19133f9d6ad7SLin Ling * inputs: 19143f9d6ad7SLin Ling * zc_name name of the pool 19155cabbc6bSPrashanth Sreenivasa * zc_guid guid of vdev to remove 19165cabbc6bSPrashanth Sreenivasa * zc_cookie cancel removal 19173f9d6ad7SLin Ling */ 1918fa9e4066Sahrens static int 1919fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1920fa9e4066Sahrens { 192199653d4eSeschrock spa_t *spa; 192299653d4eSeschrock int error; 192399653d4eSeschrock 192499653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 192599653d4eSeschrock if (error != 0) 192699653d4eSeschrock return (error); 19275cabbc6bSPrashanth Sreenivasa if (zc->zc_cookie != 0) { 19285cabbc6bSPrashanth Sreenivasa error = spa_vdev_remove_cancel(spa); 19295cabbc6bSPrashanth Sreenivasa } else { 19305cabbc6bSPrashanth Sreenivasa error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 19315cabbc6bSPrashanth Sreenivasa } 193299653d4eSeschrock spa_close(spa, FTAG); 193399653d4eSeschrock return (error); 1934fa9e4066Sahrens } 1935fa9e4066Sahrens 1936fa9e4066Sahrens static int 19373d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1938fa9e4066Sahrens { 1939fa9e4066Sahrens spa_t *spa; 1940fa9e4066Sahrens int error; 19413d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1942fa9e4066Sahrens 194306eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1944fa9e4066Sahrens return (error); 19453d7072f8Seschrock switch (zc->zc_cookie) { 19463d7072f8Seschrock case VDEV_STATE_ONLINE: 19473d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 19483d7072f8Seschrock break; 1949fa9e4066Sahrens 19503d7072f8Seschrock case VDEV_STATE_OFFLINE: 19513d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 19523d7072f8Seschrock break; 1953fa9e4066Sahrens 19543d7072f8Seschrock case VDEV_STATE_FAULTED: 1955069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1956069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1957069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1958069f55e2SEric Schrock 1959069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 19603d7072f8Seschrock break; 19613d7072f8Seschrock 19623d7072f8Seschrock case VDEV_STATE_DEGRADED: 1963069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1964069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1965069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 1966069f55e2SEric Schrock 1967069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 19683d7072f8Seschrock break; 19693d7072f8Seschrock 19703d7072f8Seschrock default: 1971be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 19723d7072f8Seschrock } 19733d7072f8Seschrock zc->zc_cookie = newstate; 1974fa9e4066Sahrens spa_close(spa, FTAG); 1975fa9e4066Sahrens return (error); 1976fa9e4066Sahrens } 1977fa9e4066Sahrens 1978fa9e4066Sahrens static int 1979fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 1980fa9e4066Sahrens { 1981fa9e4066Sahrens spa_t *spa; 1982fa9e4066Sahrens int replacing = zc->zc_cookie; 1983fa9e4066Sahrens nvlist_t *config; 1984fa9e4066Sahrens int error; 1985fa9e4066Sahrens 198606eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1987fa9e4066Sahrens return (error); 1988fa9e4066Sahrens 1989990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1990478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 1991ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 1992fa9e4066Sahrens nvlist_free(config); 1993fa9e4066Sahrens } 1994fa9e4066Sahrens 1995fa9e4066Sahrens spa_close(spa, FTAG); 1996fa9e4066Sahrens return (error); 1997fa9e4066Sahrens } 1998fa9e4066Sahrens 1999fa9e4066Sahrens static int 2000fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 2001fa9e4066Sahrens { 2002fa9e4066Sahrens spa_t *spa; 2003fa9e4066Sahrens int error; 2004fa9e4066Sahrens 200506eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 2006fa9e4066Sahrens return (error); 2007fa9e4066Sahrens 20088ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 2009fa9e4066Sahrens 2010fa9e4066Sahrens spa_close(spa, FTAG); 2011fa9e4066Sahrens return (error); 2012fa9e4066Sahrens } 2013fa9e4066Sahrens 20141195e687SMark J Musante static int 20151195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 20161195e687SMark J Musante { 20171195e687SMark J Musante spa_t *spa; 20181195e687SMark J Musante nvlist_t *config, *props = NULL; 20191195e687SMark J Musante int error; 20201195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 20211195e687SMark J Musante 20221195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 20231195e687SMark J Musante return (error); 20241195e687SMark J Musante 20251195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 20261195e687SMark J Musante zc->zc_iflags, &config)) { 20271195e687SMark J Musante spa_close(spa, FTAG); 20281195e687SMark J Musante return (error); 20291195e687SMark J Musante } 20301195e687SMark J Musante 20311195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 20321195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 20331195e687SMark J Musante zc->zc_iflags, &props))) { 20341195e687SMark J Musante spa_close(spa, FTAG); 20351195e687SMark J Musante nvlist_free(config); 20361195e687SMark J Musante return (error); 20371195e687SMark J Musante } 20381195e687SMark J Musante 20391195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 20401195e687SMark J Musante 20411195e687SMark J Musante spa_close(spa, FTAG); 20421195e687SMark J Musante 20431195e687SMark J Musante nvlist_free(config); 20441195e687SMark J Musante nvlist_free(props); 20451195e687SMark J Musante 20461195e687SMark J Musante return (error); 20471195e687SMark J Musante } 20481195e687SMark J Musante 2049c67d9675Seschrock static int 2050c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 2051c67d9675Seschrock { 2052c67d9675Seschrock spa_t *spa; 2053e9dbad6fSeschrock char *path = zc->zc_value; 2054ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 2055c67d9675Seschrock int error; 2056c67d9675Seschrock 2057c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 2058c67d9675Seschrock if (error != 0) 2059c67d9675Seschrock return (error); 2060c67d9675Seschrock 2061c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 2062c67d9675Seschrock spa_close(spa, FTAG); 2063c67d9675Seschrock return (error); 2064c67d9675Seschrock } 2065c67d9675Seschrock 20666809eb4eSEric Schrock static int 20676809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 20686809eb4eSEric Schrock { 20696809eb4eSEric Schrock spa_t *spa; 20706809eb4eSEric Schrock char *fru = zc->zc_value; 20716809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 20726809eb4eSEric Schrock int error; 20736809eb4eSEric Schrock 20746809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 20756809eb4eSEric Schrock if (error != 0) 20766809eb4eSEric Schrock return (error); 20776809eb4eSEric Schrock 20786809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 20796809eb4eSEric Schrock spa_close(spa, FTAG); 20806809eb4eSEric Schrock return (error); 20816809eb4eSEric Schrock } 20826809eb4eSEric Schrock 2083fa9e4066Sahrens static int 2084a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) 2085fa9e4066Sahrens { 2086a7f53a56SChris Kirby int error = 0; 20877f7322feSeschrock nvlist_t *nv; 2088fa9e4066Sahrens 2089a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2090fa9e4066Sahrens 20915ad82045Snd if (zc->zc_nvlist_dst != 0 && 209292241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 2093a2eea2e1Sahrens dmu_objset_stats(os, nv); 2094432f72fdSahrens /* 2095bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 2096432f72fdSahrens * which we aren't supposed to do with a 2097745cd3c5Smaybee * DS_MODE_USER hold, because it could be 2098432f72fdSahrens * inconsistent. So this is a bit of a workaround... 2099503ad85cSMatthew Ahrens * XXX reading with out owning 2100432f72fdSahrens */ 210119b94df9SMatthew Ahrens if (!zc->zc_objset_stats.dds_inconsistent && 210219b94df9SMatthew Ahrens dmu_objset_type(os) == DMU_OST_ZVOL) { 210319b94df9SMatthew Ahrens error = zvol_get_stats(os, nv); 210419b94df9SMatthew Ahrens if (error == EIO) 210519b94df9SMatthew Ahrens return (error); 2106fb09f5aaSMadhav Suresh VERIFY0(error); 2107e7437265Sahrens } 2108e9dbad6fSeschrock error = put_nvlist(zc, nv); 21097f7322feSeschrock nvlist_free(nv); 21107f7322feSeschrock } 2111fa9e4066Sahrens 2112a7f53a56SChris Kirby return (error); 2113a7f53a56SChris Kirby } 2114a7f53a56SChris Kirby 2115a7f53a56SChris Kirby /* 2116a7f53a56SChris Kirby * inputs: 2117a7f53a56SChris Kirby * zc_name name of filesystem 2118a7f53a56SChris Kirby * zc_nvlist_dst_size size of buffer for property nvlist 2119a7f53a56SChris Kirby * 2120a7f53a56SChris Kirby * outputs: 2121a7f53a56SChris Kirby * zc_objset_stats stats 2122a7f53a56SChris Kirby * zc_nvlist_dst property nvlist 2123a7f53a56SChris Kirby * zc_nvlist_dst_size size of property nvlist 2124a7f53a56SChris Kirby */ 2125a7f53a56SChris Kirby static int 2126a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc) 2127a7f53a56SChris Kirby { 21283b2aab18SMatthew Ahrens objset_t *os; 2129a7f53a56SChris Kirby int error; 2130a7f53a56SChris Kirby 21313b2aab18SMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 21323b2aab18SMatthew Ahrens if (error == 0) { 21333b2aab18SMatthew Ahrens error = zfs_ioc_objset_stats_impl(zc, os); 21343b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 21353b2aab18SMatthew Ahrens } 2136a7f53a56SChris Kirby 2137fa9e4066Sahrens return (error); 2138fa9e4066Sahrens } 2139fa9e4066Sahrens 214092241e0bSTom Erickson /* 214192241e0bSTom Erickson * inputs: 214292241e0bSTom Erickson * zc_name name of filesystem 214392241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 214492241e0bSTom Erickson * 214592241e0bSTom Erickson * outputs: 214692241e0bSTom Erickson * zc_nvlist_dst received property nvlist 214792241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 214892241e0bSTom Erickson * 214992241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 215092241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 215192241e0bSTom Erickson * local property values. 215292241e0bSTom Erickson */ 215392241e0bSTom Erickson static int 215492241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 215592241e0bSTom Erickson { 21563b2aab18SMatthew Ahrens int error = 0; 215792241e0bSTom Erickson nvlist_t *nv; 215892241e0bSTom Erickson 215992241e0bSTom Erickson /* 216092241e0bSTom Erickson * Without this check, we would return local property values if the 216192241e0bSTom Erickson * caller has not already received properties on or after 216292241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 216392241e0bSTom Erickson */ 21643b2aab18SMatthew Ahrens if (!dsl_prop_get_hasrecvd(zc->zc_name)) 2165be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 216692241e0bSTom Erickson 216792241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 21683b2aab18SMatthew Ahrens (error = dsl_prop_get_received(zc->zc_name, &nv)) == 0) { 216992241e0bSTom Erickson error = put_nvlist(zc, nv); 217092241e0bSTom Erickson nvlist_free(nv); 217192241e0bSTom Erickson } 217292241e0bSTom Erickson 217392241e0bSTom Erickson return (error); 217492241e0bSTom Erickson } 217592241e0bSTom Erickson 2176de8267e0Stimh static int 2177de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 2178de8267e0Stimh { 2179de8267e0Stimh uint64_t value; 2180de8267e0Stimh int error; 2181de8267e0Stimh 2182de8267e0Stimh /* 2183de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 2184de8267e0Stimh * the default value (if there is one). 2185de8267e0Stimh */ 2186de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 2187de8267e0Stimh return (error); 2188de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 2189de8267e0Stimh return (0); 2190de8267e0Stimh } 2191de8267e0Stimh 21923cb34c60Sahrens /* 21933cb34c60Sahrens * inputs: 21943cb34c60Sahrens * zc_name name of filesystem 2195de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 21963cb34c60Sahrens * 21973cb34c60Sahrens * outputs: 2198de8267e0Stimh * zc_nvlist_dst zpl property nvlist 2199de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 22003cb34c60Sahrens */ 2201bd00f61bSrm static int 2202de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 2203bd00f61bSrm { 2204de8267e0Stimh objset_t *os; 2205de8267e0Stimh int err; 2206bd00f61bSrm 2207503ad85cSMatthew Ahrens /* XXX reading without owning */ 2208503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 2209de8267e0Stimh return (err); 2210bd00f61bSrm 2211bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2212bd00f61bSrm 2213bd00f61bSrm /* 2214de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 2215745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 2216745cd3c5Smaybee * hold, because it could be inconsistent. 2217bd00f61bSrm */ 2218de8267e0Stimh if (zc->zc_nvlist_dst != NULL && 2219de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 2220de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 2221de8267e0Stimh nvlist_t *nv; 2222de8267e0Stimh 2223de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2224de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 2225de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 2226de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 2227de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 2228de8267e0Stimh err = put_nvlist(zc, nv); 2229de8267e0Stimh nvlist_free(nv); 2230de8267e0Stimh } else { 2231be6fd75aSMatthew Ahrens err = SET_ERROR(ENOENT); 2232de8267e0Stimh } 2233503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2234de8267e0Stimh return (err); 2235bd00f61bSrm } 2236bd00f61bSrm 223714843421SMatthew Ahrens static boolean_t 223814843421SMatthew Ahrens dataset_name_hidden(const char *name) 223914843421SMatthew Ahrens { 224014843421SMatthew Ahrens /* 224114843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 224214843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 224314843421SMatthew Ahrens * temporary datasets (which have a % in their name). 224414843421SMatthew Ahrens */ 224514843421SMatthew Ahrens if (strchr(name, '$') != NULL) 224614843421SMatthew Ahrens return (B_TRUE); 224714843421SMatthew Ahrens if (strchr(name, '%') != NULL) 224814843421SMatthew Ahrens return (B_TRUE); 224914843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 225014843421SMatthew Ahrens return (B_TRUE); 225114843421SMatthew Ahrens return (B_FALSE); 225214843421SMatthew Ahrens } 225314843421SMatthew Ahrens 2254de8267e0Stimh /* 2255de8267e0Stimh * inputs: 2256de8267e0Stimh * zc_name name of filesystem 2257de8267e0Stimh * zc_cookie zap cursor 2258de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 2259de8267e0Stimh * 2260de8267e0Stimh * outputs: 2261de8267e0Stimh * zc_name name of next filesystem 226214843421SMatthew Ahrens * zc_cookie zap cursor 2263de8267e0Stimh * zc_objset_stats stats 2264de8267e0Stimh * zc_nvlist_dst property nvlist 2265de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 2266de8267e0Stimh */ 2267fa9e4066Sahrens static int 2268fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 2269fa9e4066Sahrens { 227087e5029aSahrens objset_t *os; 2271fa9e4066Sahrens int error; 2272fa9e4066Sahrens char *p; 2273620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 2274fa9e4066Sahrens 2275620252bcSChris Kirby top: 2276503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 227787e5029aSahrens if (error == ENOENT) 2278be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 227987e5029aSahrens return (error); 2280fa9e4066Sahrens } 2281fa9e4066Sahrens 2282fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 2283fa9e4066Sahrens if (p == NULL || p[1] != '\0') 2284fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 2285fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 2286fa9e4066Sahrens 2287fa9e4066Sahrens do { 228887e5029aSahrens error = dmu_dir_list_next(os, 228987e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 229087e5029aSahrens NULL, &zc->zc_cookie); 2291fa9e4066Sahrens if (error == ENOENT) 2292be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 229319b94df9SMatthew Ahrens } while (error == 0 && dataset_name_hidden(zc->zc_name)); 2294503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2295fa9e4066Sahrens 2296681d9761SEric Taylor /* 2297681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 2298681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 2299681d9761SEric Taylor */ 2300620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 230187e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 2302620252bcSChris Kirby if (error == ENOENT) { 2303620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 2304620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 2305620252bcSChris Kirby goto top; 2306620252bcSChris Kirby } 2307620252bcSChris Kirby } 2308fa9e4066Sahrens return (error); 2309fa9e4066Sahrens } 2310fa9e4066Sahrens 23113cb34c60Sahrens /* 23123cb34c60Sahrens * inputs: 23133cb34c60Sahrens * zc_name name of filesystem 23143cb34c60Sahrens * zc_cookie zap cursor 23153cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 23160d8fa8f8SMartin Matuska * zc_simple when set, only name is requested 23173cb34c60Sahrens * 23183cb34c60Sahrens * outputs: 23193cb34c60Sahrens * zc_name name of next snapshot 23203cb34c60Sahrens * zc_objset_stats stats 23213cb34c60Sahrens * zc_nvlist_dst property nvlist 23223cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 23233cb34c60Sahrens */ 2324fa9e4066Sahrens static int 2325fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 2326fa9e4066Sahrens { 232787e5029aSahrens objset_t *os; 2328fa9e4066Sahrens int error; 2329fa9e4066Sahrens 2330503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 23313b2aab18SMatthew Ahrens if (error != 0) { 2332745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 23333b2aab18SMatthew Ahrens } 2334fa9e4066Sahrens 2335b81d61a6Slling /* 2336b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 2337b81d61a6Slling * so exit immediately. 2338b81d61a6Slling */ 23399adfa60dSMatthew Ahrens if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= 23409adfa60dSMatthew Ahrens ZFS_MAX_DATASET_NAME_LEN) { 2341503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2342be6fd75aSMatthew Ahrens return (SET_ERROR(ESRCH)); 2343fa9e4066Sahrens } 2344fa9e4066Sahrens 234587e5029aSahrens error = dmu_snapshot_list_next(os, 234687e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 2347a7f53a56SChris Kirby zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, 2348a7f53a56SChris Kirby NULL); 2349a7f53a56SChris Kirby 23500d8fa8f8SMartin Matuska if (error == 0 && !zc->zc_simple) { 2351a7f53a56SChris Kirby dsl_dataset_t *ds; 2352a7f53a56SChris Kirby dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; 2353a7f53a56SChris Kirby 2354a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); 23553b2aab18SMatthew Ahrens if (error == 0) { 2356a7f53a56SChris Kirby objset_t *ossnap; 2357a7f53a56SChris Kirby 2358a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &ossnap); 2359a7f53a56SChris Kirby if (error == 0) 2360a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, ossnap); 2361a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 2362620252bcSChris Kirby } 2363620252bcSChris Kirby } else if (error == ENOENT) { 2364be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 2365620252bcSChris Kirby } 2366fa9e4066Sahrens 2367a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 23683cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 23693b2aab18SMatthew Ahrens if (error != 0) 23703cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 2371fa9e4066Sahrens return (error); 2372fa9e4066Sahrens } 2373fa9e4066Sahrens 237492241e0bSTom Erickson static int 237592241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 2376fa9e4066Sahrens { 237792241e0bSTom Erickson const char *propname = nvpair_name(pair); 237892241e0bSTom Erickson uint64_t *valary; 237992241e0bSTom Erickson unsigned int vallen; 238092241e0bSTom Erickson const char *domain; 2381eeb85002STim Haley char *dash; 238292241e0bSTom Erickson zfs_userquota_prop_t type; 238392241e0bSTom Erickson uint64_t rid; 238492241e0bSTom Erickson uint64_t quota; 238592241e0bSTom Erickson zfsvfs_t *zfsvfs; 238692241e0bSTom Erickson int err; 238792241e0bSTom Erickson 238892241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 238992241e0bSTom Erickson nvlist_t *attrs; 239092241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2391eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2392eeb85002STim Haley &pair) != 0) 2393be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 239492241e0bSTom Erickson } 2395e9dbad6fSeschrock 2396ecd6cf80Smarks /* 2397eeb85002STim Haley * A correctly constructed propname is encoded as 239892241e0bSTom Erickson * userquota@<rid>-<domain>. 2399ecd6cf80Smarks */ 2400eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 2401eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 2402eeb85002STim Haley vallen != 3) 2403be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2404eeb85002STim Haley 2405eeb85002STim Haley domain = dash + 1; 2406eeb85002STim Haley type = valary[0]; 2407eeb85002STim Haley rid = valary[1]; 2408eeb85002STim Haley quota = valary[2]; 2409e9dbad6fSeschrock 24101412a1a2SMark Shellenbaum err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); 241192241e0bSTom Erickson if (err == 0) { 241292241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 241392241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 241492241e0bSTom Erickson } 2415e9dbad6fSeschrock 241692241e0bSTom Erickson return (err); 241792241e0bSTom Erickson } 241814843421SMatthew Ahrens 241992241e0bSTom Erickson /* 242092241e0bSTom Erickson * If the named property is one that has a special function to set its value, 242192241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 242292241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 242392241e0bSTom Erickson * 2424eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 242592241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 242692241e0bSTom Erickson */ 242792241e0bSTom Erickson static int 242892241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 242992241e0bSTom Erickson nvpair_t *pair) 243092241e0bSTom Erickson { 243192241e0bSTom Erickson const char *propname = nvpair_name(pair); 243292241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 243392241e0bSTom Erickson uint64_t intval; 2434b5152584SMatthew Ahrens int err = -1; 2435fa9e4066Sahrens 243692241e0bSTom Erickson if (prop == ZPROP_INVAL) { 243792241e0bSTom Erickson if (zfs_prop_userquota(propname)) 243892241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 243992241e0bSTom Erickson return (-1); 244092241e0bSTom Erickson } 244114843421SMatthew Ahrens 244292241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 244392241e0bSTom Erickson nvlist_t *attrs; 244492241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 244592241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 244692241e0bSTom Erickson &pair) == 0); 244792241e0bSTom Erickson } 2448db870a07Sahrens 244992241e0bSTom Erickson if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) 245092241e0bSTom Erickson return (-1); 2451b24ab676SJeff Bonwick 245292241e0bSTom Erickson VERIFY(0 == nvpair_value_uint64(pair, &intval)); 245340feaa91Sahrens 245492241e0bSTom Erickson switch (prop) { 245592241e0bSTom Erickson case ZFS_PROP_QUOTA: 245692241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 245792241e0bSTom Erickson break; 245892241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 24593b2aab18SMatthew Ahrens err = dsl_dataset_set_refquota(dsname, source, intval); 246092241e0bSTom Erickson break; 2461a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 2462a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 2463a2afb611SJerry Jelinek if (intval == UINT64_MAX) { 2464a2afb611SJerry Jelinek /* clearing the limit, just do it */ 2465a2afb611SJerry Jelinek err = 0; 2466a2afb611SJerry Jelinek } else { 2467a2afb611SJerry Jelinek err = dsl_dir_activate_fs_ss_limit(dsname); 2468a2afb611SJerry Jelinek } 2469a2afb611SJerry Jelinek /* 2470a2afb611SJerry Jelinek * Set err to -1 to force the zfs_set_prop_nvlist code down the 2471a2afb611SJerry Jelinek * default path to set the value in the nvlist. 2472a2afb611SJerry Jelinek */ 2473a2afb611SJerry Jelinek if (err == 0) 2474a2afb611SJerry Jelinek err = -1; 2475a2afb611SJerry Jelinek break; 247692241e0bSTom Erickson case ZFS_PROP_RESERVATION: 247792241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 247892241e0bSTom Erickson break; 247992241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 24803b2aab18SMatthew Ahrens err = dsl_dataset_set_refreservation(dsname, source, intval); 248192241e0bSTom Erickson break; 248292241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 2483c61ea566SGeorge Wilson err = zvol_set_volsize(dsname, intval); 248492241e0bSTom Erickson break; 248592241e0bSTom Erickson case ZFS_PROP_VERSION: 248692241e0bSTom Erickson { 248792241e0bSTom Erickson zfsvfs_t *zfsvfs; 24889e6eda55Smarks 24891412a1a2SMark Shellenbaum if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) 2490b24ab676SJeff Bonwick break; 2491b24ab676SJeff Bonwick 249292241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 249392241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 2494d0f3f37eSMark Shellenbaum 249592241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 2496b16da2e2SGeorge Wilson zfs_cmd_t *zc; 2497b16da2e2SGeorge Wilson 2498b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 2499b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 2500b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 2501b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 250240feaa91Sahrens } 250392241e0bSTom Erickson break; 2504ecd6cf80Smarks } 250592241e0bSTom Erickson default: 250692241e0bSTom Erickson err = -1; 250792241e0bSTom Erickson } 2508e9dbad6fSeschrock 250992241e0bSTom Erickson return (err); 251092241e0bSTom Erickson } 2511a9799022Sck 251292241e0bSTom Erickson /* 251392241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 25144445fffbSMatthew Ahrens * it continues to set as many as it can and returns the last error 25154445fffbSMatthew Ahrens * encountered. If the caller provides a non-NULL errlist, it will be filled in 25164445fffbSMatthew Ahrens * with the list of names of all the properties that failed along with the 25174445fffbSMatthew Ahrens * corresponding error numbers. 251892241e0bSTom Erickson * 25194445fffbSMatthew Ahrens * If every property is set successfully, zero is returned and errlist is not 25204445fffbSMatthew Ahrens * modified. 252192241e0bSTom Erickson */ 252292241e0bSTom Erickson int 252392241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 25244445fffbSMatthew Ahrens nvlist_t *errlist) 252592241e0bSTom Erickson { 252692241e0bSTom Erickson nvpair_t *pair; 252792241e0bSTom Erickson nvpair_t *propval; 252802e383d1STom Erickson int rv = 0; 252992241e0bSTom Erickson uint64_t intval; 253092241e0bSTom Erickson char *strval; 25314445fffbSMatthew Ahrens nvlist_t *genericnvl = fnvlist_alloc(); 25324445fffbSMatthew Ahrens nvlist_t *retrynvl = fnvlist_alloc(); 2533a9799022Sck 253492241e0bSTom Erickson retry: 253592241e0bSTom Erickson pair = NULL; 253692241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 253792241e0bSTom Erickson const char *propname = nvpair_name(pair); 253892241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2539cfa69fd2STom Erickson int err = 0; 2540e9dbad6fSeschrock 254192241e0bSTom Erickson /* decode the property value */ 254292241e0bSTom Erickson propval = pair; 254392241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 254492241e0bSTom Erickson nvlist_t *attrs; 25454445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 2546eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2547eeb85002STim Haley &propval) != 0) 2548be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 254914843421SMatthew Ahrens } 2550e9dbad6fSeschrock 255192241e0bSTom Erickson /* Validate value type */ 2552eeb85002STim Haley if (err == 0 && prop == ZPROP_INVAL) { 255392241e0bSTom Erickson if (zfs_prop_user(propname)) { 255492241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 2555be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 255692241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 255792241e0bSTom Erickson if (nvpair_type(propval) != 255892241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 2559be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 256019b94df9SMatthew Ahrens } else { 2561be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 25624201a95eSRic Aleshire } 2563eeb85002STim Haley } else if (err == 0) { 256492241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 256592241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 2566be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 256792241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2568a2eea2e1Sahrens const char *unused; 2569a2eea2e1Sahrens 25704445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 2571e9dbad6fSeschrock 2572e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 257391ebeef5Sahrens case PROP_TYPE_NUMBER: 2574e9dbad6fSeschrock break; 257591ebeef5Sahrens case PROP_TYPE_STRING: 2576be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 257792241e0bSTom Erickson break; 257891ebeef5Sahrens case PROP_TYPE_INDEX: 2579acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 258092241e0bSTom Erickson intval, &unused) != 0) 2581be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2582e9dbad6fSeschrock break; 2583e9dbad6fSeschrock default: 2584e7437265Sahrens cmn_err(CE_PANIC, 2585e7437265Sahrens "unknown property type"); 2586e9dbad6fSeschrock } 2587e9dbad6fSeschrock } else { 2588be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2589e9dbad6fSeschrock } 2590e9dbad6fSeschrock } 259192241e0bSTom Erickson 259292241e0bSTom Erickson /* Validate permissions */ 259392241e0bSTom Erickson if (err == 0) 259492241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 259592241e0bSTom Erickson 259692241e0bSTom Erickson if (err == 0) { 259792241e0bSTom Erickson err = zfs_prop_set_special(dsname, source, pair); 259892241e0bSTom Erickson if (err == -1) { 259992241e0bSTom Erickson /* 260092241e0bSTom Erickson * For better performance we build up a list of 260192241e0bSTom Erickson * properties to set in a single transaction. 260292241e0bSTom Erickson */ 260392241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 260492241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 260592241e0bSTom Erickson /* 260692241e0bSTom Erickson * This may be a spurious error caused by 260792241e0bSTom Erickson * receiving quota and reservation out of order. 260892241e0bSTom Erickson * Try again in a second pass. 260992241e0bSTom Erickson */ 261092241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 261192241e0bSTom Erickson } 261292241e0bSTom Erickson } 261392241e0bSTom Erickson 26144445fffbSMatthew Ahrens if (err != 0) { 26154445fffbSMatthew Ahrens if (errlist != NULL) 26164445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, err); 26174445fffbSMatthew Ahrens rv = err; 26184445fffbSMatthew Ahrens } 2619e9dbad6fSeschrock } 2620e9dbad6fSeschrock 262192241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 262292241e0bSTom Erickson nvl = retrynvl; 262392241e0bSTom Erickson goto retry; 262492241e0bSTom Erickson } 262592241e0bSTom Erickson 262692241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 262792241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 262892241e0bSTom Erickson /* 262992241e0bSTom Erickson * If this fails, we still want to set as many properties as we 263092241e0bSTom Erickson * can, so try setting them individually. 263192241e0bSTom Erickson */ 263292241e0bSTom Erickson pair = NULL; 263392241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 263492241e0bSTom Erickson const char *propname = nvpair_name(pair); 2635cfa69fd2STom Erickson int err = 0; 263692241e0bSTom Erickson 263792241e0bSTom Erickson propval = pair; 263892241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 263992241e0bSTom Erickson nvlist_t *attrs; 26404445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 26414445fffbSMatthew Ahrens propval = fnvlist_lookup_nvpair(attrs, 26424445fffbSMatthew Ahrens ZPROP_VALUE); 264392241e0bSTom Erickson } 264492241e0bSTom Erickson 264592241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 26464445fffbSMatthew Ahrens strval = fnvpair_value_string(propval); 26473b2aab18SMatthew Ahrens err = dsl_prop_set_string(dsname, propname, 26483b2aab18SMatthew Ahrens source, strval); 264992241e0bSTom Erickson } else { 26504445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 26513b2aab18SMatthew Ahrens err = dsl_prop_set_int(dsname, propname, source, 26523b2aab18SMatthew Ahrens intval); 265392241e0bSTom Erickson } 265492241e0bSTom Erickson 265592241e0bSTom Erickson if (err != 0) { 26564445fffbSMatthew Ahrens if (errlist != NULL) { 26574445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, 26584445fffbSMatthew Ahrens err); 26594445fffbSMatthew Ahrens } 26604445fffbSMatthew Ahrens rv = err; 266192241e0bSTom Erickson } 266292241e0bSTom Erickson } 26635c0b6a79SRich Morris } 26645c0b6a79SRich Morris nvlist_free(genericnvl); 266592241e0bSTom Erickson nvlist_free(retrynvl); 266692241e0bSTom Erickson 266792241e0bSTom Erickson return (rv); 2668fa9e4066Sahrens } 2669fa9e4066Sahrens 2670ea2f5b9eSMatthew Ahrens /* 2671ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2672ea2f5b9eSMatthew Ahrens */ 2673ea2f5b9eSMatthew Ahrens static int 26744445fffbSMatthew Ahrens zfs_check_userprops(const char *fsname, nvlist_t *nvl) 2675ea2f5b9eSMatthew Ahrens { 267692241e0bSTom Erickson nvpair_t *pair = NULL; 2677ea2f5b9eSMatthew Ahrens int error = 0; 2678ea2f5b9eSMatthew Ahrens 267992241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 268092241e0bSTom Erickson const char *propname = nvpair_name(pair); 2681ea2f5b9eSMatthew Ahrens 2682ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 268392241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2684be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2685ea2f5b9eSMatthew Ahrens 2686ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2687ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2688ea2f5b9eSMatthew Ahrens return (error); 2689ea2f5b9eSMatthew Ahrens 2690ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2691be6fd75aSMatthew Ahrens return (SET_ERROR(ENAMETOOLONG)); 2692ea2f5b9eSMatthew Ahrens 269378f17100SMatthew Ahrens if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN) 2694ea2f5b9eSMatthew Ahrens return (E2BIG); 2695ea2f5b9eSMatthew Ahrens } 2696ea2f5b9eSMatthew Ahrens return (0); 2697ea2f5b9eSMatthew Ahrens } 2698ea2f5b9eSMatthew Ahrens 269992241e0bSTom Erickson static void 270092241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 270192241e0bSTom Erickson { 270292241e0bSTom Erickson nvpair_t *pair; 270392241e0bSTom Erickson 270492241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 270592241e0bSTom Erickson 270692241e0bSTom Erickson pair = NULL; 270792241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 270892241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 270992241e0bSTom Erickson continue; 271092241e0bSTom Erickson 271192241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 271292241e0bSTom Erickson } 271392241e0bSTom Erickson } 271492241e0bSTom Erickson 271592241e0bSTom Erickson static int 27163b2aab18SMatthew Ahrens clear_received_props(const char *dsname, nvlist_t *props, 271792241e0bSTom Erickson nvlist_t *skipped) 271892241e0bSTom Erickson { 271992241e0bSTom Erickson int err = 0; 272092241e0bSTom Erickson nvlist_t *cleared_props = NULL; 272192241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 272292241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 272392241e0bSTom Erickson /* 272492241e0bSTom Erickson * Acts on local properties until the dataset has received 272592241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 272692241e0bSTom Erickson */ 272792241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 27283b2aab18SMatthew Ahrens (dsl_prop_get_hasrecvd(dsname) ? ZPROP_SRC_RECEIVED : 0)); 27293b2aab18SMatthew Ahrens err = zfs_set_prop_nvlist(dsname, flags, cleared_props, NULL); 273092241e0bSTom Erickson } 273192241e0bSTom Erickson nvlist_free(cleared_props); 273292241e0bSTom Erickson return (err); 273392241e0bSTom Erickson } 273492241e0bSTom Erickson 27353cb34c60Sahrens /* 27363cb34c60Sahrens * inputs: 27373cb34c60Sahrens * zc_name name of filesystem 27385c0b6a79SRich Morris * zc_value name of property to set 27393cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 274092241e0bSTom Erickson * zc_cookie received properties flag 27413cb34c60Sahrens * 274292241e0bSTom Erickson * outputs: 274392241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 27443cb34c60Sahrens */ 2745fa9e4066Sahrens static int 2746e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2747fa9e4066Sahrens { 2748e9dbad6fSeschrock nvlist_t *nvl; 274992241e0bSTom Erickson boolean_t received = zc->zc_cookie; 275092241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 275192241e0bSTom Erickson ZPROP_SRC_LOCAL); 27524445fffbSMatthew Ahrens nvlist_t *errors; 2753e9dbad6fSeschrock int error; 2754e9dbad6fSeschrock 2755990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2756478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2757e9dbad6fSeschrock return (error); 2758e9dbad6fSeschrock 275992241e0bSTom Erickson if (received) { 2760bb0ade09Sahrens nvlist_t *origprops; 2761bb0ade09Sahrens 27623b2aab18SMatthew Ahrens if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) { 27633b2aab18SMatthew Ahrens (void) clear_received_props(zc->zc_name, 27643b2aab18SMatthew Ahrens origprops, nvl); 27653b2aab18SMatthew Ahrens nvlist_free(origprops); 2766bb0ade09Sahrens } 27673b2aab18SMatthew Ahrens 27683b2aab18SMatthew Ahrens error = dsl_prop_set_hasrecvd(zc->zc_name); 2769bb0ade09Sahrens } 2770bb0ade09Sahrens 27714445fffbSMatthew Ahrens errors = fnvlist_alloc(); 27723b2aab18SMatthew Ahrens if (error == 0) 27733b2aab18SMatthew Ahrens error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors); 277492241e0bSTom Erickson 277592241e0bSTom Erickson if (zc->zc_nvlist_dst != NULL && errors != NULL) { 277692241e0bSTom Erickson (void) put_nvlist(zc, errors); 277792241e0bSTom Erickson } 2778ecd6cf80Smarks 277992241e0bSTom Erickson nvlist_free(errors); 2780e9dbad6fSeschrock nvlist_free(nvl); 2781e9dbad6fSeschrock return (error); 2782fa9e4066Sahrens } 2783fa9e4066Sahrens 27843cb34c60Sahrens /* 27853cb34c60Sahrens * inputs: 27863cb34c60Sahrens * zc_name name of filesystem 27873cb34c60Sahrens * zc_value name of property to inherit 278892241e0bSTom Erickson * zc_cookie revert to received value if TRUE 27893cb34c60Sahrens * 27903cb34c60Sahrens * outputs: none 27913cb34c60Sahrens */ 2792e45ce728Sahrens static int 2793e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2794e45ce728Sahrens { 279592241e0bSTom Erickson const char *propname = zc->zc_value; 279692241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 279792241e0bSTom Erickson boolean_t received = zc->zc_cookie; 279892241e0bSTom Erickson zprop_source_t source = (received 279992241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 280092241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 280192241e0bSTom Erickson 280292241e0bSTom Erickson if (received) { 280392241e0bSTom Erickson nvlist_t *dummy; 280492241e0bSTom Erickson nvpair_t *pair; 280592241e0bSTom Erickson zprop_type_t type; 280692241e0bSTom Erickson int err; 280792241e0bSTom Erickson 280892241e0bSTom Erickson /* 280992241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 281092241e0bSTom Erickson * nvpair with type info. 281192241e0bSTom Erickson */ 281292241e0bSTom Erickson if (prop == ZPROP_INVAL) { 281392241e0bSTom Erickson if (!zfs_prop_user(propname)) 2814be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 281592241e0bSTom Erickson 281692241e0bSTom Erickson type = PROP_TYPE_STRING; 2817a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2818a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2819be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 282092241e0bSTom Erickson } else { 282192241e0bSTom Erickson type = zfs_prop_get_type(prop); 282292241e0bSTom Erickson } 282392241e0bSTom Erickson 282492241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 282592241e0bSTom Erickson 282692241e0bSTom Erickson switch (type) { 282792241e0bSTom Erickson case PROP_TYPE_STRING: 282892241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 282992241e0bSTom Erickson break; 283092241e0bSTom Erickson case PROP_TYPE_NUMBER: 283192241e0bSTom Erickson case PROP_TYPE_INDEX: 283292241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 283392241e0bSTom Erickson break; 283492241e0bSTom Erickson default: 283592241e0bSTom Erickson nvlist_free(dummy); 2836be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 283792241e0bSTom Erickson } 283892241e0bSTom Erickson 283992241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 284092241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 284192241e0bSTom Erickson nvlist_free(dummy); 284292241e0bSTom Erickson if (err != -1) 284392241e0bSTom Erickson return (err); /* special property already handled */ 284492241e0bSTom Erickson } else { 284592241e0bSTom Erickson /* 284692241e0bSTom Erickson * Only check this in the non-received case. We want to allow 284792241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 284892241e0bSTom Erickson * and reservation to the received or default values even though 284992241e0bSTom Erickson * they are not considered inheritable. 285092241e0bSTom Erickson */ 285192241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 2852be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 285392241e0bSTom Erickson } 285492241e0bSTom Erickson 28554445fffbSMatthew Ahrens /* property name has been validated by zfs_secpolicy_inherit_prop() */ 28563b2aab18SMatthew Ahrens return (dsl_prop_inherit(zc->zc_name, zc->zc_value, source)); 2857e45ce728Sahrens } 2858e45ce728Sahrens 2859b1b8ab34Slling static int 286011a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2861b1b8ab34Slling { 2862990b4856Slling nvlist_t *props; 2863b1b8ab34Slling spa_t *spa; 2864990b4856Slling int error; 286592241e0bSTom Erickson nvpair_t *pair; 2866b1b8ab34Slling 286792241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 286892241e0bSTom Erickson zc->zc_iflags, &props)) 2869b1b8ab34Slling return (error); 2870b1b8ab34Slling 2871379c004dSEric Schrock /* 2872379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2873379c004dSEric Schrock * to handle the faulted case. 2874379c004dSEric Schrock */ 287592241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 287692241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2877379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 287892241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2879379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2880379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2881379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 28825cabbc6bSPrashanth Sreenivasa spa_write_cachefile(spa, B_FALSE, B_TRUE); 2883379c004dSEric Schrock } 2884379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2885b693757aSEric Schrock if (spa != NULL) { 2886b693757aSEric Schrock nvlist_free(props); 2887379c004dSEric Schrock return (0); 2888b693757aSEric Schrock } 2889379c004dSEric Schrock } 2890379c004dSEric Schrock 2891b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2892990b4856Slling nvlist_free(props); 2893b1b8ab34Slling return (error); 2894b1b8ab34Slling } 2895b1b8ab34Slling 2896990b4856Slling error = spa_prop_set(spa, props); 2897b1b8ab34Slling 2898990b4856Slling nvlist_free(props); 2899b1b8ab34Slling spa_close(spa, FTAG); 2900b1b8ab34Slling 2901b1b8ab34Slling return (error); 2902b1b8ab34Slling } 2903b1b8ab34Slling 2904b1b8ab34Slling static int 290511a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2906b1b8ab34Slling { 2907b1b8ab34Slling spa_t *spa; 2908b1b8ab34Slling int error; 2909b1b8ab34Slling nvlist_t *nvp = NULL; 2910b1b8ab34Slling 2911379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2912379c004dSEric Schrock /* 2913379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2914379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2915379c004dSEric Schrock * anyway. 2916379c004dSEric Schrock */ 2917379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2918379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2919379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2920379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2921379c004dSEric Schrock } else { 2922379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2923379c004dSEric Schrock spa_close(spa, FTAG); 2924379c004dSEric Schrock } 2925b1b8ab34Slling 2926b1b8ab34Slling if (error == 0 && zc->zc_nvlist_dst != NULL) 2927b1b8ab34Slling error = put_nvlist(zc, nvp); 2928b1b8ab34Slling else 2929be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 2930b1b8ab34Slling 2931379c004dSEric Schrock nvlist_free(nvp); 2932b1b8ab34Slling return (error); 2933b1b8ab34Slling } 2934b1b8ab34Slling 29353cb34c60Sahrens /* 29363cb34c60Sahrens * inputs: 29373cb34c60Sahrens * zc_name name of filesystem 29383cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 29393cb34c60Sahrens * zc_perm_action allow/unallow flag 29403cb34c60Sahrens * 29413cb34c60Sahrens * outputs: none 29423cb34c60Sahrens */ 2943ecd6cf80Smarks static int 2944ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 2945ecd6cf80Smarks { 2946ecd6cf80Smarks int error; 2947ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 2948ecd6cf80Smarks 2949990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2950478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 2951ecd6cf80Smarks return (error); 2952ecd6cf80Smarks 2953ecd6cf80Smarks /* 2954ecd6cf80Smarks * Verify nvlist is constructed correctly 2955ecd6cf80Smarks */ 2956ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 2957ecd6cf80Smarks nvlist_free(fsaclnv); 2958be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2959ecd6cf80Smarks } 2960ecd6cf80Smarks 2961ecd6cf80Smarks /* 2962ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 2963ecd6cf80Smarks * that user is allowed to hand out each permission in 2964ecd6cf80Smarks * the nvlist(s) 2965ecd6cf80Smarks */ 2966ecd6cf80Smarks 296791ebeef5Sahrens error = secpolicy_zfs(CRED()); 29683b2aab18SMatthew Ahrens if (error != 0) { 296991ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 297091ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 297191ebeef5Sahrens fsaclnv, CRED()); 297291ebeef5Sahrens } else { 297391ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 297491ebeef5Sahrens fsaclnv, CRED()); 297591ebeef5Sahrens } 2976ecd6cf80Smarks } 2977ecd6cf80Smarks 2978ecd6cf80Smarks if (error == 0) 2979ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 2980ecd6cf80Smarks 2981ecd6cf80Smarks nvlist_free(fsaclnv); 2982ecd6cf80Smarks return (error); 2983ecd6cf80Smarks } 2984ecd6cf80Smarks 29853cb34c60Sahrens /* 29863cb34c60Sahrens * inputs: 29873cb34c60Sahrens * zc_name name of filesystem 29883cb34c60Sahrens * 29893cb34c60Sahrens * outputs: 29903cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 29913cb34c60Sahrens */ 2992ecd6cf80Smarks static int 2993ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 2994ecd6cf80Smarks { 2995ecd6cf80Smarks nvlist_t *nvp; 2996ecd6cf80Smarks int error; 2997ecd6cf80Smarks 2998ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 2999ecd6cf80Smarks error = put_nvlist(zc, nvp); 3000ecd6cf80Smarks nvlist_free(nvp); 3001ecd6cf80Smarks } 3002ecd6cf80Smarks 3003ecd6cf80Smarks return (error); 3004ecd6cf80Smarks } 3005ecd6cf80Smarks 3006ecd6cf80Smarks /* ARGSUSED */ 3007fa9e4066Sahrens static void 3008ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 3009fa9e4066Sahrens { 3010da6c28aaSamw zfs_creat_t *zct = arg; 3011da6c28aaSamw 3012de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 3013da6c28aaSamw } 3014da6c28aaSamw 3015de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 3016da6c28aaSamw 3017da6c28aaSamw /* 3018de8267e0Stimh * inputs: 30190a48a24eStimh * os parent objset pointer (NULL if root fs) 3020f7170741SWill Andrews * fuids_ok fuids allowed in this version of the spa? 3021f7170741SWill Andrews * sa_ok SAs allowed in this version of the spa? 3022f7170741SWill Andrews * createprops list of properties requested by creator 3023de8267e0Stimh * 3024de8267e0Stimh * outputs: 3025de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 30260a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 3027da6c28aaSamw * 3028de8267e0Stimh * Determine the settings for utf8only, normalization and 3029de8267e0Stimh * casesensitivity. Specific values may have been requested by the 3030de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 3031de8267e0Stimh * the file system is of too early a vintage, a creator can not 3032de8267e0Stimh * request settings for these properties, even if the requested 3033de8267e0Stimh * setting is the default value. We don't actually want to create dsl 3034de8267e0Stimh * properties for these, so remove them from the source nvlist after 3035de8267e0Stimh * processing. 3036da6c28aaSamw */ 3037da6c28aaSamw static int 303814843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 30390a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 30400a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 3041da6c28aaSamw { 3042de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 3043de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 3044de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 3045da6c28aaSamw 3046de8267e0Stimh ASSERT(zplprops != NULL); 3047da6c28aaSamw 3048b127fe3cSAndriy Gapon if (os != NULL && os->os_phys->os_type != DMU_OST_ZFS) 3049b127fe3cSAndriy Gapon return (SET_ERROR(EINVAL)); 3050b127fe3cSAndriy Gapon 3051de8267e0Stimh /* 3052de8267e0Stimh * Pull out creator prop choices, if any. 3053de8267e0Stimh */ 3054de8267e0Stimh if (createprops) { 30550a48a24eStimh (void) nvlist_lookup_uint64(createprops, 30560a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 3057de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3058de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 3059de8267e0Stimh (void) nvlist_remove_all(createprops, 3060de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 3061de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3062de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 3063de8267e0Stimh (void) nvlist_remove_all(createprops, 3064de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 3065de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3066de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 3067de8267e0Stimh (void) nvlist_remove_all(createprops, 3068de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 3069de8267e0Stimh } 3070da6c28aaSamw 3071c2a93d44Stimh /* 30720a48a24eStimh * If the zpl version requested is whacky or the file system 30730a48a24eStimh * or pool is version is too "young" to support normalization 30740a48a24eStimh * and the creator tried to set a value for one of the props, 30750a48a24eStimh * error out. 3076c2a93d44Stimh */ 30770a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 30780a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 30790a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 30800a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 3081de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 30820a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 3083be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3084c2a93d44Stimh 3085de8267e0Stimh /* 3086de8267e0Stimh * Put the version in the zplprops 3087de8267e0Stimh */ 3088de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3089de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 3090da6c28aaSamw 3091de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 3092de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 3093de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3094de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 3095da6c28aaSamw 3096c2a93d44Stimh /* 3097de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 3098c2a93d44Stimh */ 3099de8267e0Stimh if (norm) 3100de8267e0Stimh u8 = 1; 3101de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 3102de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 3103de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3104de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 3105de8267e0Stimh 3106de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 3107de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 3108de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3109de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 3110c2a93d44Stimh 3111ab04eb8eStimh if (is_ci) 3112ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 3113ab04eb8eStimh 3114da6c28aaSamw return (0); 3115fa9e4066Sahrens } 3116fa9e4066Sahrens 31170a48a24eStimh static int 31180a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 31190a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 31200a48a24eStimh { 31210a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 31220a48a24eStimh uint64_t zplver = ZPL_VERSION; 31230a48a24eStimh objset_t *os = NULL; 31249adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 31250a48a24eStimh char *cp; 31260a586ceaSMark Shellenbaum spa_t *spa; 31270a586ceaSMark Shellenbaum uint64_t spa_vers; 31280a48a24eStimh int error; 31290a48a24eStimh 31300a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 31310a48a24eStimh cp = strrchr(parentname, '/'); 31320a48a24eStimh ASSERT(cp != NULL); 31330a48a24eStimh cp[0] = '\0'; 31340a48a24eStimh 31350a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 31360a586ceaSMark Shellenbaum return (error); 31370a586ceaSMark Shellenbaum 31380a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 31390a586ceaSMark Shellenbaum spa_close(spa, FTAG); 31400a586ceaSMark Shellenbaum 31410a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 31420a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 31430a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 31440a48a24eStimh 31450a48a24eStimh /* 31460a48a24eStimh * Open parent object set so we can inherit zplprop values. 31470a48a24eStimh */ 3148503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 31490a48a24eStimh return (error); 31500a48a24eStimh 31510a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 31520a48a24eStimh zplprops, is_ci); 3153503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 31540a48a24eStimh return (error); 31550a48a24eStimh } 31560a48a24eStimh 31570a48a24eStimh static int 31580a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 31590a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 31600a48a24eStimh { 31610a586ceaSMark Shellenbaum boolean_t fuids_ok; 31620a586ceaSMark Shellenbaum boolean_t sa_ok; 31630a48a24eStimh uint64_t zplver = ZPL_VERSION; 31640a48a24eStimh int error; 31650a48a24eStimh 31660a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 31670a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 31680a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 31690a48a24eStimh 31700a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 31710a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 31720a48a24eStimh return (error); 31730a48a24eStimh } 31740a48a24eStimh 31753cb34c60Sahrens /* 31764445fffbSMatthew Ahrens * innvl: { 31774445fffbSMatthew Ahrens * "type" -> dmu_objset_type_t (int32) 31784445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 31794445fffbSMatthew Ahrens * } 31803cb34c60Sahrens * 31814445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 31823cb34c60Sahrens */ 3183fa9e4066Sahrens static int 31844445fffbSMatthew Ahrens zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3185fa9e4066Sahrens { 3186fa9e4066Sahrens int error = 0; 31874445fffbSMatthew Ahrens zfs_creat_t zct = { 0 }; 3188ecd6cf80Smarks nvlist_t *nvprops = NULL; 3189ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 31904445fffbSMatthew Ahrens int32_t type32; 31914445fffbSMatthew Ahrens dmu_objset_type_t type; 31924445fffbSMatthew Ahrens boolean_t is_insensitive = B_FALSE; 3193fa9e4066Sahrens 31944445fffbSMatthew Ahrens if (nvlist_lookup_int32(innvl, "type", &type32) != 0) 3195be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 31964445fffbSMatthew Ahrens type = type32; 31974445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3198fa9e4066Sahrens 31994445fffbSMatthew Ahrens switch (type) { 3200fa9e4066Sahrens case DMU_OST_ZFS: 3201fa9e4066Sahrens cbfunc = zfs_create_cb; 3202fa9e4066Sahrens break; 3203fa9e4066Sahrens 3204fa9e4066Sahrens case DMU_OST_ZVOL: 3205fa9e4066Sahrens cbfunc = zvol_create_cb; 3206fa9e4066Sahrens break; 3207fa9e4066Sahrens 3208fa9e4066Sahrens default: 32091d452cf5Sahrens cbfunc = NULL; 3210e7cbe64fSgw break; 3211fa9e4066Sahrens } 32124445fffbSMatthew Ahrens if (strchr(fsname, '@') || 32134445fffbSMatthew Ahrens strchr(fsname, '%')) 3214be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3215fa9e4066Sahrens 3216da6c28aaSamw zct.zct_props = nvprops; 3217da6c28aaSamw 32184445fffbSMatthew Ahrens if (cbfunc == NULL) 3219be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32204445fffbSMatthew Ahrens 32214445fffbSMatthew Ahrens if (type == DMU_OST_ZVOL) { 32224445fffbSMatthew Ahrens uint64_t volsize, volblocksize; 32234445fffbSMatthew Ahrens 32244445fffbSMatthew Ahrens if (nvprops == NULL) 3225be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32264445fffbSMatthew Ahrens if (nvlist_lookup_uint64(nvprops, 32274445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) != 0) 3228be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3229fa9e4066Sahrens 32304445fffbSMatthew Ahrens if ((error = nvlist_lookup_uint64(nvprops, 32314445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 32324445fffbSMatthew Ahrens &volblocksize)) != 0 && error != ENOENT) 3233be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32344445fffbSMatthew Ahrens 32354445fffbSMatthew Ahrens if (error != 0) 32364445fffbSMatthew Ahrens volblocksize = zfs_prop_default_numeric( 32374445fffbSMatthew Ahrens ZFS_PROP_VOLBLOCKSIZE); 32384445fffbSMatthew Ahrens 32394445fffbSMatthew Ahrens if ((error = zvol_check_volblocksize( 32404445fffbSMatthew Ahrens volblocksize)) != 0 || 32414445fffbSMatthew Ahrens (error = zvol_check_volsize(volsize, 32424445fffbSMatthew Ahrens volblocksize)) != 0) 3243fa9e4066Sahrens return (error); 32444445fffbSMatthew Ahrens } else if (type == DMU_OST_ZFS) { 32454445fffbSMatthew Ahrens int error; 3246ab04eb8eStimh 32474445fffbSMatthew Ahrens /* 32484445fffbSMatthew Ahrens * We have to have normalization and 32494445fffbSMatthew Ahrens * case-folding flags correct when we do the 32504445fffbSMatthew Ahrens * file system creation, so go figure them out 32514445fffbSMatthew Ahrens * now. 32524445fffbSMatthew Ahrens */ 32534445fffbSMatthew Ahrens VERIFY(nvlist_alloc(&zct.zct_zplprops, 32544445fffbSMatthew Ahrens NV_UNIQUE_NAME, KM_SLEEP) == 0); 32554445fffbSMatthew Ahrens error = zfs_fill_zplprops(fsname, nvprops, 32564445fffbSMatthew Ahrens zct.zct_zplprops, &is_insensitive); 32574445fffbSMatthew Ahrens if (error != 0) { 32584445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 3259da6c28aaSamw return (error); 3260da6c28aaSamw } 32614445fffbSMatthew Ahrens } 3262ab04eb8eStimh 32634445fffbSMatthew Ahrens error = dmu_objset_create(fsname, type, 32644445fffbSMatthew Ahrens is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); 32654445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 32665c5460e9Seschrock 32674445fffbSMatthew Ahrens /* 32684445fffbSMatthew Ahrens * It would be nice to do this atomically. 32694445fffbSMatthew Ahrens */ 32704445fffbSMatthew Ahrens if (error == 0) { 32714445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 32724445fffbSMatthew Ahrens nvprops, outnvl); 32734445fffbSMatthew Ahrens if (error != 0) 32743b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 32754445fffbSMatthew Ahrens } 32764445fffbSMatthew Ahrens return (error); 32774445fffbSMatthew Ahrens } 3278e9dbad6fSeschrock 32794445fffbSMatthew Ahrens /* 32804445fffbSMatthew Ahrens * innvl: { 32814445fffbSMatthew Ahrens * "origin" -> name of origin snapshot 32824445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 32834445fffbSMatthew Ahrens * } 32844445fffbSMatthew Ahrens * 32854445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 32864445fffbSMatthew Ahrens */ 32874445fffbSMatthew Ahrens static int 32884445fffbSMatthew Ahrens zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 32894445fffbSMatthew Ahrens { 32904445fffbSMatthew Ahrens int error = 0; 32914445fffbSMatthew Ahrens nvlist_t *nvprops = NULL; 32924445fffbSMatthew Ahrens char *origin_name; 3293e9dbad6fSeschrock 32944445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) 3295be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32964445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3297e9dbad6fSeschrock 32984445fffbSMatthew Ahrens if (strchr(fsname, '@') || 32994445fffbSMatthew Ahrens strchr(fsname, '%')) 3300be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3301e9dbad6fSeschrock 33024445fffbSMatthew Ahrens if (dataset_namecheck(origin_name, NULL, NULL) != 0) 3303be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 33043b2aab18SMatthew Ahrens error = dmu_objset_clone(fsname, origin_name); 33053b2aab18SMatthew Ahrens if (error != 0) 33064445fffbSMatthew Ahrens return (error); 3307e9dbad6fSeschrock 3308e9dbad6fSeschrock /* 3309e9dbad6fSeschrock * It would be nice to do this atomically. 3310e9dbad6fSeschrock */ 3311e9dbad6fSeschrock if (error == 0) { 33124445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 33134445fffbSMatthew Ahrens nvprops, outnvl); 331492241e0bSTom Erickson if (error != 0) 33153b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 3316e9dbad6fSeschrock } 3317fa9e4066Sahrens return (error); 3318fa9e4066Sahrens } 3319fa9e4066Sahrens 33205cabbc6bSPrashanth Sreenivasa /* ARGSUSED */ 33215cabbc6bSPrashanth Sreenivasa static int 33225cabbc6bSPrashanth Sreenivasa zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 33235cabbc6bSPrashanth Sreenivasa { 33245cabbc6bSPrashanth Sreenivasa if (strchr(fsname, '@') || 33255cabbc6bSPrashanth Sreenivasa strchr(fsname, '%')) 33265cabbc6bSPrashanth Sreenivasa return (SET_ERROR(EINVAL)); 33275cabbc6bSPrashanth Sreenivasa 33285cabbc6bSPrashanth Sreenivasa return (dmu_objset_remap_indirects(fsname)); 33295cabbc6bSPrashanth Sreenivasa } 33305cabbc6bSPrashanth Sreenivasa 33313cb34c60Sahrens /* 33324445fffbSMatthew Ahrens * innvl: { 33334445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 33344445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value (string) } 33354445fffbSMatthew Ahrens * } 33364445fffbSMatthew Ahrens * 33374445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 33383cb34c60Sahrens */ 3339fa9e4066Sahrens static int 33404445fffbSMatthew Ahrens zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3341fa9e4066Sahrens { 33424445fffbSMatthew Ahrens nvlist_t *snaps; 33434445fffbSMatthew Ahrens nvlist_t *props = NULL; 33444445fffbSMatthew Ahrens int error, poollen; 33454445fffbSMatthew Ahrens nvpair_t *pair; 3346bb0ade09Sahrens 33474445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &props); 33484445fffbSMatthew Ahrens if ((error = zfs_check_userprops(poolname, props)) != 0) 33494445fffbSMatthew Ahrens return (error); 33504445fffbSMatthew Ahrens 33514445fffbSMatthew Ahrens if (!nvlist_empty(props) && 33524445fffbSMatthew Ahrens zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) 3353be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 33544445fffbSMatthew Ahrens 33554445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3356be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 33574445fffbSMatthew Ahrens poollen = strlen(poolname); 33584445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 33594445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 33604445fffbSMatthew Ahrens const char *name = nvpair_name(pair); 33614445fffbSMatthew Ahrens const char *cp = strchr(name, '@'); 3362bb0ade09Sahrens 33634445fffbSMatthew Ahrens /* 33644445fffbSMatthew Ahrens * The snap name must contain an @, and the part after it must 33654445fffbSMatthew Ahrens * contain only valid characters. 33664445fffbSMatthew Ahrens */ 336778f17100SMatthew Ahrens if (cp == NULL || 336878f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 3369be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3370bb0ade09Sahrens 33714445fffbSMatthew Ahrens /* 33724445fffbSMatthew Ahrens * The snap must be in the specified pool. 33734445fffbSMatthew Ahrens */ 33744445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 33754445fffbSMatthew Ahrens (name[poollen] != '/' && name[poollen] != '@')) 3376be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 33774445fffbSMatthew Ahrens 33784445fffbSMatthew Ahrens /* This must be the only snap of this fs. */ 33794445fffbSMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(snaps, pair); 33804445fffbSMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(snaps, pair2)) { 33814445fffbSMatthew Ahrens if (strncmp(name, nvpair_name(pair2), cp - name + 1) 33824445fffbSMatthew Ahrens == 0) { 3383be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 33844445fffbSMatthew Ahrens } 33854445fffbSMatthew Ahrens } 33864445fffbSMatthew Ahrens } 3387bb0ade09Sahrens 33883b2aab18SMatthew Ahrens error = dsl_dataset_snapshot(snaps, props, outnvl); 33894445fffbSMatthew Ahrens return (error); 33904445fffbSMatthew Ahrens } 33914445fffbSMatthew Ahrens 33924445fffbSMatthew Ahrens /* 33934445fffbSMatthew Ahrens * innvl: "message" -> string 33944445fffbSMatthew Ahrens */ 33954445fffbSMatthew Ahrens /* ARGSUSED */ 33964445fffbSMatthew Ahrens static int 33974445fffbSMatthew Ahrens zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 33984445fffbSMatthew Ahrens { 33994445fffbSMatthew Ahrens char *message; 34004445fffbSMatthew Ahrens spa_t *spa; 34014445fffbSMatthew Ahrens int error; 34024445fffbSMatthew Ahrens char *poolname; 34034445fffbSMatthew Ahrens 34044445fffbSMatthew Ahrens /* 34054445fffbSMatthew Ahrens * The poolname in the ioctl is not set, we get it from the TSD, 34064445fffbSMatthew Ahrens * which was set at the end of the last successful ioctl that allows 34074445fffbSMatthew Ahrens * logging. The secpolicy func already checked that it is set. 34084445fffbSMatthew Ahrens * Only one log ioctl is allowed after each successful ioctl, so 34094445fffbSMatthew Ahrens * we clear the TSD here. 34104445fffbSMatthew Ahrens */ 34114445fffbSMatthew Ahrens poolname = tsd_get(zfs_allow_log_key); 34124445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, NULL); 34134445fffbSMatthew Ahrens error = spa_open(poolname, &spa, FTAG); 34144445fffbSMatthew Ahrens strfree(poolname); 34154445fffbSMatthew Ahrens if (error != 0) 34164445fffbSMatthew Ahrens return (error); 34174445fffbSMatthew Ahrens 34184445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "message", &message) != 0) { 34194445fffbSMatthew Ahrens spa_close(spa, FTAG); 3420be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3421bb0ade09Sahrens } 3422ea2f5b9eSMatthew Ahrens 34234445fffbSMatthew Ahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 34244445fffbSMatthew Ahrens spa_close(spa, FTAG); 3425be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 34264445fffbSMatthew Ahrens } 3427ea2f5b9eSMatthew Ahrens 34284445fffbSMatthew Ahrens error = spa_history_log(spa, message); 34294445fffbSMatthew Ahrens spa_close(spa, FTAG); 3430bb0ade09Sahrens return (error); 34311d452cf5Sahrens } 3432fa9e4066Sahrens 34333b2aab18SMatthew Ahrens /* 34343b2aab18SMatthew Ahrens * The dp_config_rwlock must not be held when calling this, because the 34353b2aab18SMatthew Ahrens * unmount may need to write out data. 34363b2aab18SMatthew Ahrens * 34373b2aab18SMatthew Ahrens * This function is best-effort. Callers must deal gracefully if it 34383b2aab18SMatthew Ahrens * remains mounted (or is remounted after this call). 3439fc7a6e3fSWill Andrews * 3440fc7a6e3fSWill Andrews * Returns 0 if the argument is not a snapshot, or it is not currently a 3441fc7a6e3fSWill Andrews * filesystem, or we were able to unmount it. Returns error code otherwise. 34423b2aab18SMatthew Ahrens */ 3443ed992b0aSSerapheim Dimitropoulos void 34443b2aab18SMatthew Ahrens zfs_unmount_snap(const char *snapname) 34451d452cf5Sahrens { 3446ed992b0aSSerapheim Dimitropoulos vfs_t *vfsp = NULL; 3447ed992b0aSSerapheim Dimitropoulos zfsvfs_t *zfsvfs = NULL; 34481d452cf5Sahrens 34493b2aab18SMatthew Ahrens if (strchr(snapname, '@') == NULL) 3450ed992b0aSSerapheim Dimitropoulos return; 34511d452cf5Sahrens 3452ed992b0aSSerapheim Dimitropoulos int err = getzfsvfs(snapname, &zfsvfs); 3453ed992b0aSSerapheim Dimitropoulos if (err != 0) { 3454ed992b0aSSerapheim Dimitropoulos ASSERT3P(zfsvfs, ==, NULL); 3455ed992b0aSSerapheim Dimitropoulos return; 3456ed992b0aSSerapheim Dimitropoulos } 3457ed992b0aSSerapheim Dimitropoulos vfsp = zfsvfs->z_vfs; 34583b2aab18SMatthew Ahrens 34593b2aab18SMatthew Ahrens ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os))); 34601d452cf5Sahrens 3461fc7a6e3fSWill Andrews err = vn_vfswlock(vfsp->vfs_vnodecovered); 34624445fffbSMatthew Ahrens VFS_RELE(vfsp); 3463fc7a6e3fSWill Andrews if (err != 0) 3464ed992b0aSSerapheim Dimitropoulos return; 34654445fffbSMatthew Ahrens 34664445fffbSMatthew Ahrens /* 34674445fffbSMatthew Ahrens * Always force the unmount for snapshots. 34684445fffbSMatthew Ahrens */ 34693b2aab18SMatthew Ahrens (void) dounmount(vfsp, MS_FORCE, kcred); 34703b2aab18SMatthew Ahrens } 34713b2aab18SMatthew Ahrens 34723b2aab18SMatthew Ahrens /* ARGSUSED */ 34733b2aab18SMatthew Ahrens static int 34743b2aab18SMatthew Ahrens zfs_unmount_snap_cb(const char *snapname, void *arg) 34753b2aab18SMatthew Ahrens { 3476ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(snapname); 3477ed992b0aSSerapheim Dimitropoulos return (0); 34783b2aab18SMatthew Ahrens } 34793b2aab18SMatthew Ahrens 34803b2aab18SMatthew Ahrens /* 34813b2aab18SMatthew Ahrens * When a clone is destroyed, its origin may also need to be destroyed, 34823b2aab18SMatthew Ahrens * in which case it must be unmounted. This routine will do that unmount 34833b2aab18SMatthew Ahrens * if necessary. 34843b2aab18SMatthew Ahrens */ 34853b2aab18SMatthew Ahrens void 34863b2aab18SMatthew Ahrens zfs_destroy_unmount_origin(const char *fsname) 34873b2aab18SMatthew Ahrens { 34883b2aab18SMatthew Ahrens int error; 34893b2aab18SMatthew Ahrens objset_t *os; 34903b2aab18SMatthew Ahrens dsl_dataset_t *ds; 34913b2aab18SMatthew Ahrens 34923b2aab18SMatthew Ahrens error = dmu_objset_hold(fsname, FTAG, &os); 34933b2aab18SMatthew Ahrens if (error != 0) 34943b2aab18SMatthew Ahrens return; 34953b2aab18SMatthew Ahrens ds = dmu_objset_ds(os); 34963b2aab18SMatthew Ahrens if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev)) { 34979adfa60dSMatthew Ahrens char originname[ZFS_MAX_DATASET_NAME_LEN]; 34983b2aab18SMatthew Ahrens dsl_dataset_name(ds->ds_prev, originname); 34993b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 3500ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(originname); 35013b2aab18SMatthew Ahrens } else { 35023b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 35033b2aab18SMatthew Ahrens } 35041d452cf5Sahrens } 35051d452cf5Sahrens 35063cb34c60Sahrens /* 35074445fffbSMatthew Ahrens * innvl: { 35084445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 35094445fffbSMatthew Ahrens * (optional boolean) "defer" 35104445fffbSMatthew Ahrens * } 35114445fffbSMatthew Ahrens * 35124445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 35133cb34c60Sahrens * 35143cb34c60Sahrens */ 351578f17100SMatthew Ahrens /* ARGSUSED */ 35161d452cf5Sahrens static int 35174445fffbSMatthew Ahrens zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 35181d452cf5Sahrens { 35194445fffbSMatthew Ahrens nvlist_t *snaps; 352019b94df9SMatthew Ahrens nvpair_t *pair; 35214445fffbSMatthew Ahrens boolean_t defer; 35221d452cf5Sahrens 35234445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3524be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 35254445fffbSMatthew Ahrens defer = nvlist_exists(innvl, "defer"); 352619b94df9SMatthew Ahrens 35274445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 35284445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 3529ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(nvpair_name(pair)); 353078f17100SMatthew Ahrens } 353178f17100SMatthew Ahrens 353278f17100SMatthew Ahrens return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); 353378f17100SMatthew Ahrens } 353478f17100SMatthew Ahrens 353578f17100SMatthew Ahrens /* 353678f17100SMatthew Ahrens * Create bookmarks. Bookmark names are of the form <fs>#<bmark>. 353778f17100SMatthew Ahrens * All bookmarks must be in the same pool. 353878f17100SMatthew Ahrens * 353978f17100SMatthew Ahrens * innvl: { 354078f17100SMatthew Ahrens * bookmark1 -> snapshot1, bookmark2 -> snapshot2 354178f17100SMatthew Ahrens * } 354278f17100SMatthew Ahrens * 354378f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 354478f17100SMatthew Ahrens * 354578f17100SMatthew Ahrens */ 354678f17100SMatthew Ahrens /* ARGSUSED */ 354778f17100SMatthew Ahrens static int 354878f17100SMatthew Ahrens zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 354978f17100SMatthew Ahrens { 355078f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 355178f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 355278f17100SMatthew Ahrens char *snap_name; 355378f17100SMatthew Ahrens 355478f17100SMatthew Ahrens /* 355578f17100SMatthew Ahrens * Verify the snapshot argument. 355678f17100SMatthew Ahrens */ 355778f17100SMatthew Ahrens if (nvpair_value_string(pair, &snap_name) != 0) 355878f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 355978f17100SMatthew Ahrens 356078f17100SMatthew Ahrens 356178f17100SMatthew Ahrens /* Verify that the keys (bookmarks) are unique */ 356278f17100SMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(innvl, pair); 356378f17100SMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(innvl, pair2)) { 356478f17100SMatthew Ahrens if (strcmp(nvpair_name(pair), nvpair_name(pair2)) == 0) 356578f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 356678f17100SMatthew Ahrens } 356778f17100SMatthew Ahrens } 356878f17100SMatthew Ahrens 356978f17100SMatthew Ahrens return (dsl_bookmark_create(innvl, outnvl)); 357078f17100SMatthew Ahrens } 357178f17100SMatthew Ahrens 357278f17100SMatthew Ahrens /* 357378f17100SMatthew Ahrens * innvl: { 357478f17100SMatthew Ahrens * property 1, property 2, ... 357578f17100SMatthew Ahrens * } 357678f17100SMatthew Ahrens * 357778f17100SMatthew Ahrens * outnvl: { 357878f17100SMatthew Ahrens * bookmark name 1 -> { property 1, property 2, ... }, 357978f17100SMatthew Ahrens * bookmark name 2 -> { property 1, property 2, ... } 358078f17100SMatthew Ahrens * } 358178f17100SMatthew Ahrens * 358278f17100SMatthew Ahrens */ 358378f17100SMatthew Ahrens static int 358478f17100SMatthew Ahrens zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 358578f17100SMatthew Ahrens { 358678f17100SMatthew Ahrens return (dsl_get_bookmarks(fsname, innvl, outnvl)); 358778f17100SMatthew Ahrens } 358878f17100SMatthew Ahrens 358978f17100SMatthew Ahrens /* 359078f17100SMatthew Ahrens * innvl: { 359178f17100SMatthew Ahrens * bookmark name 1, bookmark name 2 359278f17100SMatthew Ahrens * } 359378f17100SMatthew Ahrens * 359478f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 359578f17100SMatthew Ahrens * 359678f17100SMatthew Ahrens */ 359778f17100SMatthew Ahrens static int 359878f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, 359978f17100SMatthew Ahrens nvlist_t *outnvl) 360078f17100SMatthew Ahrens { 360178f17100SMatthew Ahrens int error, poollen; 360278f17100SMatthew Ahrens 360378f17100SMatthew Ahrens poollen = strlen(poolname); 360478f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 360578f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 360619b94df9SMatthew Ahrens const char *name = nvpair_name(pair); 360778f17100SMatthew Ahrens const char *cp = strchr(name, '#'); 36084445fffbSMatthew Ahrens 360919b94df9SMatthew Ahrens /* 361078f17100SMatthew Ahrens * The bookmark name must contain an #, and the part after it 361178f17100SMatthew Ahrens * must contain only valid characters. 361278f17100SMatthew Ahrens */ 361378f17100SMatthew Ahrens if (cp == NULL || 361478f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 361578f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 361678f17100SMatthew Ahrens 361778f17100SMatthew Ahrens /* 361878f17100SMatthew Ahrens * The bookmark must be in the specified pool. 361919b94df9SMatthew Ahrens */ 36204445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 362178f17100SMatthew Ahrens (name[poollen] != '/' && name[poollen] != '#')) 3622be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 362319b94df9SMatthew Ahrens } 362419b94df9SMatthew Ahrens 362578f17100SMatthew Ahrens error = dsl_bookmark_destroy(innvl, outnvl); 362678f17100SMatthew Ahrens return (error); 36271d452cf5Sahrens } 36281d452cf5Sahrens 3629dfc11533SChris Williamson static int 3630dfc11533SChris Williamson zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, 3631dfc11533SChris Williamson nvlist_t *outnvl) 3632dfc11533SChris Williamson { 3633dfc11533SChris Williamson char *program; 3634dfc11533SChris Williamson uint64_t instrlimit, memlimit; 3635a3b28680SSerapheim Dimitropoulos boolean_t sync_flag; 3636dfc11533SChris Williamson nvpair_t *nvarg = NULL; 3637dfc11533SChris Williamson 3638dfc11533SChris Williamson if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) { 3639dfc11533SChris Williamson return (EINVAL); 3640dfc11533SChris Williamson } 3641a3b28680SSerapheim Dimitropoulos if (0 != nvlist_lookup_boolean_value(innvl, ZCP_ARG_SYNC, &sync_flag)) { 3642a3b28680SSerapheim Dimitropoulos sync_flag = B_TRUE; 3643a3b28680SSerapheim Dimitropoulos } 3644dfc11533SChris Williamson if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) { 3645dfc11533SChris Williamson instrlimit = ZCP_DEFAULT_INSTRLIMIT; 3646dfc11533SChris Williamson } 3647dfc11533SChris Williamson if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { 3648dfc11533SChris Williamson memlimit = ZCP_DEFAULT_MEMLIMIT; 3649dfc11533SChris Williamson } 3650dfc11533SChris Williamson if (0 != nvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST, &nvarg)) { 3651dfc11533SChris Williamson return (EINVAL); 3652dfc11533SChris Williamson } 3653dfc11533SChris Williamson 3654dfc11533SChris Williamson if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) 3655dfc11533SChris Williamson return (EINVAL); 36562840dce1SChris Williamson if (memlimit == 0 || memlimit > zfs_lua_max_memlimit) 3657dfc11533SChris Williamson return (EINVAL); 3658dfc11533SChris Williamson 3659a3b28680SSerapheim Dimitropoulos return (zcp_eval(poolname, program, sync_flag, instrlimit, memlimit, 3660dfc11533SChris Williamson nvarg, outnvl)); 3661dfc11533SChris Williamson } 3662dfc11533SChris Williamson 366386714001SSerapheim Dimitropoulos /* 366486714001SSerapheim Dimitropoulos * innvl: unused 366586714001SSerapheim Dimitropoulos * outnvl: empty 366686714001SSerapheim Dimitropoulos */ 366786714001SSerapheim Dimitropoulos /* ARGSUSED */ 366886714001SSerapheim Dimitropoulos static int 366986714001SSerapheim Dimitropoulos zfs_ioc_pool_checkpoint(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 367086714001SSerapheim Dimitropoulos { 367186714001SSerapheim Dimitropoulos return (spa_checkpoint(poolname)); 367286714001SSerapheim Dimitropoulos } 367386714001SSerapheim Dimitropoulos 367486714001SSerapheim Dimitropoulos /* 367586714001SSerapheim Dimitropoulos * innvl: unused 367686714001SSerapheim Dimitropoulos * outnvl: empty 367786714001SSerapheim Dimitropoulos */ 367886714001SSerapheim Dimitropoulos /* ARGSUSED */ 367986714001SSerapheim Dimitropoulos static int 368086714001SSerapheim Dimitropoulos zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, 368186714001SSerapheim Dimitropoulos nvlist_t *outnvl) 368286714001SSerapheim Dimitropoulos { 368386714001SSerapheim Dimitropoulos return (spa_checkpoint_discard(poolname)); 368486714001SSerapheim Dimitropoulos } 368586714001SSerapheim Dimitropoulos 36863cb34c60Sahrens /* 36873cb34c60Sahrens * inputs: 36883cb34c60Sahrens * zc_name name of dataset to destroy 36893cb34c60Sahrens * zc_objset_type type of objset 3690842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 36913cb34c60Sahrens * 36923cb34c60Sahrens * outputs: none 36933cb34c60Sahrens */ 36941d452cf5Sahrens static int 36951d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 36961d452cf5Sahrens { 3697681d9761SEric Taylor int err; 3698fc7a6e3fSWill Andrews 3699ed992b0aSSerapheim Dimitropoulos if (zc->zc_objset_type == DMU_OST_ZFS) 3700ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(zc->zc_name); 3701fa9e4066Sahrens 37023b2aab18SMatthew Ahrens if (strchr(zc->zc_name, '@')) 37033b2aab18SMatthew Ahrens err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); 37043b2aab18SMatthew Ahrens else 37053b2aab18SMatthew Ahrens err = dsl_destroy_head(zc->zc_name); 3706681d9761SEric Taylor if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) 37075c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 3708681d9761SEric Taylor return (err); 3709fa9e4066Sahrens } 3710fa9e4066Sahrens 3711*094e47e9SGeorge Wilson /* 3712*094e47e9SGeorge Wilson * innvl: { 3713*094e47e9SGeorge Wilson * vdevs: { 3714*094e47e9SGeorge Wilson * guid 1, guid 2, ... 3715*094e47e9SGeorge Wilson * }, 3716*094e47e9SGeorge Wilson * func: POOL_INITIALIZE_{CANCEL|DO|SUSPEND} 3717*094e47e9SGeorge Wilson * } 3718*094e47e9SGeorge Wilson * 3719*094e47e9SGeorge Wilson * outnvl: { 3720*094e47e9SGeorge Wilson * [func: EINVAL (if provided command type didn't make sense)], 3721*094e47e9SGeorge Wilson * [vdevs: { 3722*094e47e9SGeorge Wilson * guid1: errno, (see function body for possible errnos) 3723*094e47e9SGeorge Wilson * ... 3724*094e47e9SGeorge Wilson * }] 3725*094e47e9SGeorge Wilson * } 3726*094e47e9SGeorge Wilson * 3727*094e47e9SGeorge Wilson */ 3728*094e47e9SGeorge Wilson static int 3729*094e47e9SGeorge Wilson zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3730*094e47e9SGeorge Wilson { 3731*094e47e9SGeorge Wilson spa_t *spa; 3732*094e47e9SGeorge Wilson int error; 3733*094e47e9SGeorge Wilson 3734*094e47e9SGeorge Wilson error = spa_open(poolname, &spa, FTAG); 3735*094e47e9SGeorge Wilson if (error != 0) 3736*094e47e9SGeorge Wilson return (error); 3737*094e47e9SGeorge Wilson 3738*094e47e9SGeorge Wilson uint64_t cmd_type; 3739*094e47e9SGeorge Wilson if (nvlist_lookup_uint64(innvl, ZPOOL_INITIALIZE_COMMAND, 3740*094e47e9SGeorge Wilson &cmd_type) != 0) { 3741*094e47e9SGeorge Wilson spa_close(spa, FTAG); 3742*094e47e9SGeorge Wilson return (SET_ERROR(EINVAL)); 3743*094e47e9SGeorge Wilson } 3744*094e47e9SGeorge Wilson if (!(cmd_type == POOL_INITIALIZE_CANCEL || 3745*094e47e9SGeorge Wilson cmd_type == POOL_INITIALIZE_DO || 3746*094e47e9SGeorge Wilson cmd_type == POOL_INITIALIZE_SUSPEND)) { 3747*094e47e9SGeorge Wilson spa_close(spa, FTAG); 3748*094e47e9SGeorge Wilson return (SET_ERROR(EINVAL)); 3749*094e47e9SGeorge Wilson } 3750*094e47e9SGeorge Wilson 3751*094e47e9SGeorge Wilson nvlist_t *vdev_guids; 3752*094e47e9SGeorge Wilson if (nvlist_lookup_nvlist(innvl, ZPOOL_INITIALIZE_VDEVS, 3753*094e47e9SGeorge Wilson &vdev_guids) != 0) { 3754*094e47e9SGeorge Wilson spa_close(spa, FTAG); 3755*094e47e9SGeorge Wilson return (SET_ERROR(EINVAL)); 3756*094e47e9SGeorge Wilson } 3757*094e47e9SGeorge Wilson 3758*094e47e9SGeorge Wilson nvlist_t *vdev_errlist = fnvlist_alloc(); 3759*094e47e9SGeorge Wilson int total_errors = 0; 3760*094e47e9SGeorge Wilson 3761*094e47e9SGeorge Wilson for (nvpair_t *pair = nvlist_next_nvpair(vdev_guids, NULL); 3762*094e47e9SGeorge Wilson pair != NULL; pair = nvlist_next_nvpair(vdev_guids, pair)) { 3763*094e47e9SGeorge Wilson uint64_t vdev_guid = fnvpair_value_uint64(pair); 3764*094e47e9SGeorge Wilson 3765*094e47e9SGeorge Wilson error = spa_vdev_initialize(spa, vdev_guid, cmd_type); 3766*094e47e9SGeorge Wilson if (error != 0) { 3767*094e47e9SGeorge Wilson char guid_as_str[MAXNAMELEN]; 3768*094e47e9SGeorge Wilson 3769*094e47e9SGeorge Wilson (void) snprintf(guid_as_str, sizeof (guid_as_str), 3770*094e47e9SGeorge Wilson "%llu", (unsigned long long)vdev_guid); 3771*094e47e9SGeorge Wilson fnvlist_add_int64(vdev_errlist, guid_as_str, error); 3772*094e47e9SGeorge Wilson total_errors++; 3773*094e47e9SGeorge Wilson } 3774*094e47e9SGeorge Wilson } 3775*094e47e9SGeorge Wilson if (fnvlist_size(vdev_errlist) > 0) { 3776*094e47e9SGeorge Wilson fnvlist_add_nvlist(outnvl, ZPOOL_INITIALIZE_VDEVS, 3777*094e47e9SGeorge Wilson vdev_errlist); 3778*094e47e9SGeorge Wilson } 3779*094e47e9SGeorge Wilson fnvlist_free(vdev_errlist); 3780*094e47e9SGeorge Wilson 3781*094e47e9SGeorge Wilson spa_close(spa, FTAG); 3782*094e47e9SGeorge Wilson return (total_errors > 0 ? EINVAL : 0); 3783*094e47e9SGeorge Wilson } 3784*094e47e9SGeorge Wilson 37853cb34c60Sahrens /* 3786a7027df1SMatthew Ahrens * fsname is name of dataset to rollback (to most recent snapshot) 37873cb34c60Sahrens * 378877b17137SAndriy Gapon * innvl may contain name of expected target snapshot 3789a7027df1SMatthew Ahrens * 3790a7027df1SMatthew Ahrens * outnvl: "target" -> name of most recent snapshot 3791a7027df1SMatthew Ahrens * } 37923cb34c60Sahrens */ 3793a7027df1SMatthew Ahrens /* ARGSUSED */ 3794fa9e4066Sahrens static int 379577b17137SAndriy Gapon zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3796fa9e4066Sahrens { 3797ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 379877b17137SAndriy Gapon char *target = NULL; 37993b2aab18SMatthew Ahrens int error; 3800ae46e4c7SMatthew Ahrens 380177b17137SAndriy Gapon (void) nvlist_lookup_string(innvl, "target", &target); 380277b17137SAndriy Gapon if (target != NULL) { 380395643f75SAndriy Gapon const char *cp = strchr(target, '@'); 380477b17137SAndriy Gapon 380595643f75SAndriy Gapon /* 380695643f75SAndriy Gapon * The snap name must contain an @, and the part after it must 380795643f75SAndriy Gapon * contain only valid characters. 380895643f75SAndriy Gapon */ 380995643f75SAndriy Gapon if (cp == NULL || 381095643f75SAndriy Gapon zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 381177b17137SAndriy Gapon return (SET_ERROR(EINVAL)); 381277b17137SAndriy Gapon } 381377b17137SAndriy Gapon 3814a7027df1SMatthew Ahrens if (getzfsvfs(fsname, &zfsvfs) == 0) { 3815690041b9SAndriy Gapon dsl_dataset_t *ds; 3816690041b9SAndriy Gapon 3817690041b9SAndriy Gapon ds = dmu_objset_ds(zfsvfs->z_os); 3818503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 381947f263f4Sek if (error == 0) { 382047f263f4Sek int resume_err; 38214ccbb6e7Sahrens 382277b17137SAndriy Gapon error = dsl_dataset_rollback(fsname, target, zfsvfs, 382377b17137SAndriy Gapon outnvl); 3824690041b9SAndriy Gapon resume_err = zfs_resume_fs(zfsvfs, ds); 382547f263f4Sek error = error ? error : resume_err; 382647f263f4Sek } 38274ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 38284ccbb6e7Sahrens } else { 382977b17137SAndriy Gapon error = dsl_dataset_rollback(fsname, target, NULL, outnvl); 38304ccbb6e7Sahrens } 38313b2aab18SMatthew Ahrens return (error); 38323b2aab18SMatthew Ahrens } 38334ccbb6e7Sahrens 38343b2aab18SMatthew Ahrens static int 38353b2aab18SMatthew Ahrens recursive_unmount(const char *fsname, void *arg) 38363b2aab18SMatthew Ahrens { 38373b2aab18SMatthew Ahrens const char *snapname = arg; 38389adfa60dSMatthew Ahrens char fullname[ZFS_MAX_DATASET_NAME_LEN]; 3839ae46e4c7SMatthew Ahrens 38403b2aab18SMatthew Ahrens (void) snprintf(fullname, sizeof (fullname), "%s@%s", fsname, snapname); 3841ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(fullname); 3842ed992b0aSSerapheim Dimitropoulos 3843ed992b0aSSerapheim Dimitropoulos return (0); 3844fa9e4066Sahrens } 3845fa9e4066Sahrens 38463cb34c60Sahrens /* 38473cb34c60Sahrens * inputs: 38483cb34c60Sahrens * zc_name old name of dataset 38493cb34c60Sahrens * zc_value new name of dataset 38503cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 38513cb34c60Sahrens * 38523cb34c60Sahrens * outputs: none 38533cb34c60Sahrens */ 3854fa9e4066Sahrens static int 3855fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3856fa9e4066Sahrens { 38577f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 38583b2aab18SMatthew Ahrens char *at; 3859cdf5b4caSmmusante 3860add927f8Sloli /* "zfs rename" from and to ...%recv datasets should both fail */ 3861add927f8Sloli zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 3862e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3863add927f8Sloli if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 || 3864add927f8Sloli dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3865add927f8Sloli strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%')) 3866be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3867fa9e4066Sahrens 38683b2aab18SMatthew Ahrens at = strchr(zc->zc_name, '@'); 38693b2aab18SMatthew Ahrens if (at != NULL) { 38703b2aab18SMatthew Ahrens /* snaps must be in same fs */ 3871a0c1127bSSteven Hartland int error; 3872a0c1127bSSteven Hartland 38733b2aab18SMatthew Ahrens if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1)) 3874be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 38753b2aab18SMatthew Ahrens *at = '\0'; 38763b2aab18SMatthew Ahrens if (zc->zc_objset_type == DMU_OST_ZFS) { 3877a0c1127bSSteven Hartland error = dmu_objset_find(zc->zc_name, 38783b2aab18SMatthew Ahrens recursive_unmount, at + 1, 38793b2aab18SMatthew Ahrens recursive ? DS_FIND_CHILDREN : 0); 3880a0c1127bSSteven Hartland if (error != 0) { 3881a0c1127bSSteven Hartland *at = '@'; 38823b2aab18SMatthew Ahrens return (error); 3883a0c1127bSSteven Hartland } 38843b2aab18SMatthew Ahrens } 3885a0c1127bSSteven Hartland error = dsl_dataset_rename_snapshot(zc->zc_name, 3886a0c1127bSSteven Hartland at + 1, strchr(zc->zc_value, '@') + 1, recursive); 3887a0c1127bSSteven Hartland *at = '@'; 3888a0c1127bSSteven Hartland 3889a0c1127bSSteven Hartland return (error); 38903b2aab18SMatthew Ahrens } else { 38913b2aab18SMatthew Ahrens if (zc->zc_objset_type == DMU_OST_ZVOL) 38923b2aab18SMatthew Ahrens (void) zvol_remove_minor(zc->zc_name); 38933b2aab18SMatthew Ahrens return (dsl_dir_rename(zc->zc_name, zc->zc_value)); 3894fa9e4066Sahrens } 3895fa9e4066Sahrens } 3896fa9e4066Sahrens 389792241e0bSTom Erickson static int 389892241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 389992241e0bSTom Erickson { 390092241e0bSTom Erickson const char *propname = nvpair_name(pair); 390192241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 390292241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 390392241e0bSTom Erickson uint64_t intval; 390492241e0bSTom Erickson int err; 390592241e0bSTom Erickson 390692241e0bSTom Erickson if (prop == ZPROP_INVAL) { 390792241e0bSTom Erickson if (zfs_prop_user(propname)) { 390892241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 390992241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 391092241e0bSTom Erickson return (err); 391192241e0bSTom Erickson return (0); 391292241e0bSTom Erickson } 391392241e0bSTom Erickson 391492241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 391592241e0bSTom Erickson const char *perm = NULL; 391692241e0bSTom Erickson const char *uq_prefix = 391792241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 391892241e0bSTom Erickson const char *gq_prefix = 391992241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 392092241e0bSTom Erickson 392192241e0bSTom Erickson if (strncmp(propname, uq_prefix, 392292241e0bSTom Erickson strlen(uq_prefix)) == 0) { 392392241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 392492241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 392592241e0bSTom Erickson strlen(gq_prefix)) == 0) { 392692241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 392792241e0bSTom Erickson } else { 392892241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 3929be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 393092241e0bSTom Erickson } 393192241e0bSTom Erickson 393292241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 393392241e0bSTom Erickson return (err); 393492241e0bSTom Erickson return (0); 393592241e0bSTom Erickson } 393692241e0bSTom Erickson 3937be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 393892241e0bSTom Erickson } 393992241e0bSTom Erickson 394092241e0bSTom Erickson if (issnap) 3941be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 394292241e0bSTom Erickson 394392241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 394492241e0bSTom Erickson /* 394592241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 394692241e0bSTom Erickson * format. 394792241e0bSTom Erickson */ 394892241e0bSTom Erickson nvlist_t *attrs; 394992241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 395092241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 395192241e0bSTom Erickson &pair) == 0); 395292241e0bSTom Erickson } 395392241e0bSTom Erickson 395492241e0bSTom Erickson /* 395592241e0bSTom Erickson * Check that this value is valid for this pool version 395692241e0bSTom Erickson */ 395792241e0bSTom Erickson switch (prop) { 395892241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 395992241e0bSTom Erickson /* 396092241e0bSTom Erickson * If the user specified gzip compression, make sure 396192241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 396292241e0bSTom Erickson * we'll catch them later. 396392241e0bSTom Erickson */ 3964b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0) { 396592241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 396692241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 396792241e0bSTom Erickson zfs_earlier_version(dsname, 396892241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 3969be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 397092241e0bSTom Erickson } 397192241e0bSTom Erickson 397292241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 397392241e0bSTom Erickson zfs_earlier_version(dsname, 397492241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 3975be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 397692241e0bSTom Erickson 3977a6f561b4SSašo Kiselkov if (intval == ZIO_COMPRESS_LZ4) { 3978a6f561b4SSašo Kiselkov spa_t *spa; 3979a6f561b4SSašo Kiselkov 3980a6f561b4SSašo Kiselkov if ((err = spa_open(dsname, &spa, FTAG)) != 0) 3981a6f561b4SSašo Kiselkov return (err); 3982a6f561b4SSašo Kiselkov 39832acef22dSMatthew Ahrens if (!spa_feature_is_enabled(spa, 39842acef22dSMatthew Ahrens SPA_FEATURE_LZ4_COMPRESS)) { 3985a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 3986be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3987a6f561b4SSašo Kiselkov } 3988a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 3989a6f561b4SSašo Kiselkov } 3990a6f561b4SSašo Kiselkov 399192241e0bSTom Erickson /* 399292241e0bSTom Erickson * If this is a bootable dataset then 399392241e0bSTom Erickson * verify that the compression algorithm 399492241e0bSTom Erickson * is supported for booting. We must return 399592241e0bSTom Erickson * something other than ENOTSUP since it 399692241e0bSTom Erickson * implies a downrev pool version. 399792241e0bSTom Erickson */ 399892241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 399992241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 4000be6fd75aSMatthew Ahrens return (SET_ERROR(ERANGE)); 400192241e0bSTom Erickson } 400292241e0bSTom Erickson } 400392241e0bSTom Erickson break; 400492241e0bSTom Erickson 400592241e0bSTom Erickson case ZFS_PROP_COPIES: 400692241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 4007be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 400892241e0bSTom Erickson break; 400992241e0bSTom Erickson 4010b5152584SMatthew Ahrens case ZFS_PROP_RECORDSIZE: 4011b5152584SMatthew Ahrens /* Record sizes above 128k need the feature to be enabled */ 4012b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0 && 4013b5152584SMatthew Ahrens intval > SPA_OLD_MAXBLOCKSIZE) { 4014b5152584SMatthew Ahrens spa_t *spa; 4015b5152584SMatthew Ahrens 4016b5152584SMatthew Ahrens /* 4017b5152584SMatthew Ahrens * We don't allow setting the property above 1MB, 4018b5152584SMatthew Ahrens * unless the tunable has been changed. 4019b5152584SMatthew Ahrens */ 4020b5152584SMatthew Ahrens if (intval > zfs_max_recordsize || 4021b5152584SMatthew Ahrens intval > SPA_MAXBLOCKSIZE) 40226de9bb56SMatthew Ahrens return (SET_ERROR(ERANGE)); 4023b5152584SMatthew Ahrens 4024b5152584SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 4025b5152584SMatthew Ahrens return (err); 4026b5152584SMatthew Ahrens 4027b5152584SMatthew Ahrens if (!spa_feature_is_enabled(spa, 4028b5152584SMatthew Ahrens SPA_FEATURE_LARGE_BLOCKS)) { 4029b5152584SMatthew Ahrens spa_close(spa, FTAG); 4030b5152584SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 4031b5152584SMatthew Ahrens } 4032b5152584SMatthew Ahrens spa_close(spa, FTAG); 4033b5152584SMatthew Ahrens } 4034b5152584SMatthew Ahrens break; 4035b5152584SMatthew Ahrens 403692241e0bSTom Erickson case ZFS_PROP_SHARESMB: 403792241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 4038be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 403992241e0bSTom Erickson break; 404092241e0bSTom Erickson 404192241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 404292241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 404392241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 404492241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 404592241e0bSTom Erickson zfs_earlier_version(dsname, 404692241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 4047be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 404892241e0bSTom Erickson } 404992241e0bSTom Erickson break; 405045818ee1SMatthew Ahrens 405145818ee1SMatthew Ahrens case ZFS_PROP_CHECKSUM: 405245818ee1SMatthew Ahrens case ZFS_PROP_DEDUP: 405345818ee1SMatthew Ahrens { 405445818ee1SMatthew Ahrens spa_feature_t feature; 405545818ee1SMatthew Ahrens spa_t *spa; 405645818ee1SMatthew Ahrens 405745818ee1SMatthew Ahrens /* dedup feature version checks */ 405845818ee1SMatthew Ahrens if (prop == ZFS_PROP_DEDUP && 405945818ee1SMatthew Ahrens zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 406045818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 406145818ee1SMatthew Ahrens 406245818ee1SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) != 0) 406345818ee1SMatthew Ahrens return (SET_ERROR(EINVAL)); 406445818ee1SMatthew Ahrens 406545818ee1SMatthew Ahrens /* check prop value is enabled in features */ 4066971640e6Silovezfs feature = zio_checksum_to_feature(intval & ZIO_CHECKSUM_MASK); 406745818ee1SMatthew Ahrens if (feature == SPA_FEATURE_NONE) 406845818ee1SMatthew Ahrens break; 406945818ee1SMatthew Ahrens 407045818ee1SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 407145818ee1SMatthew Ahrens return (err); 407245818ee1SMatthew Ahrens /* 407345818ee1SMatthew Ahrens * Salted checksums are not supported on root pools. 407445818ee1SMatthew Ahrens */ 407545818ee1SMatthew Ahrens if (spa_bootfs(spa) != 0 && 407645818ee1SMatthew Ahrens intval < ZIO_CHECKSUM_FUNCTIONS && 407745818ee1SMatthew Ahrens (zio_checksum_table[intval].ci_flags & 407845818ee1SMatthew Ahrens ZCHECKSUM_FLAG_SALTED)) { 407945818ee1SMatthew Ahrens spa_close(spa, FTAG); 408045818ee1SMatthew Ahrens return (SET_ERROR(ERANGE)); 408145818ee1SMatthew Ahrens } 408245818ee1SMatthew Ahrens if (!spa_feature_is_enabled(spa, feature)) { 408345818ee1SMatthew Ahrens spa_close(spa, FTAG); 408445818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 408545818ee1SMatthew Ahrens } 408645818ee1SMatthew Ahrens spa_close(spa, FTAG); 408745818ee1SMatthew Ahrens break; 408845818ee1SMatthew Ahrens } 408992241e0bSTom Erickson } 409092241e0bSTom Erickson 409192241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 409292241e0bSTom Erickson } 409392241e0bSTom Erickson 4094a6f561b4SSašo Kiselkov /* 4095a6f561b4SSašo Kiselkov * Checks for a race condition to make sure we don't increment a feature flag 4096a6f561b4SSašo Kiselkov * multiple times. 4097a6f561b4SSašo Kiselkov */ 4098a6f561b4SSašo Kiselkov static int 40993b2aab18SMatthew Ahrens zfs_prop_activate_feature_check(void *arg, dmu_tx_t *tx) 4100a6f561b4SSašo Kiselkov { 41013b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 41022acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 4103a6f561b4SSašo Kiselkov 41042acef22dSMatthew Ahrens if (!spa_feature_is_active(spa, *featurep)) 4105a6f561b4SSašo Kiselkov return (0); 4106a6f561b4SSašo Kiselkov else 4107be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 4108a6f561b4SSašo Kiselkov } 4109a6f561b4SSašo Kiselkov 4110a6f561b4SSašo Kiselkov /* 4111a6f561b4SSašo Kiselkov * The callback invoked on feature activation in the sync task caused by 4112a6f561b4SSašo Kiselkov * zfs_prop_activate_feature. 4113a6f561b4SSašo Kiselkov */ 4114a6f561b4SSašo Kiselkov static void 41153b2aab18SMatthew Ahrens zfs_prop_activate_feature_sync(void *arg, dmu_tx_t *tx) 4116a6f561b4SSašo Kiselkov { 41173b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 41182acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 4119a6f561b4SSašo Kiselkov 41202acef22dSMatthew Ahrens spa_feature_incr(spa, *featurep, tx); 4121a6f561b4SSašo Kiselkov } 4122a6f561b4SSašo Kiselkov 41233b2aab18SMatthew Ahrens /* 41243b2aab18SMatthew Ahrens * Activates a feature on a pool in response to a property setting. This 41253b2aab18SMatthew Ahrens * creates a new sync task which modifies the pool to reflect the feature 41263b2aab18SMatthew Ahrens * as being active. 41273b2aab18SMatthew Ahrens */ 41283b2aab18SMatthew Ahrens static int 41292acef22dSMatthew Ahrens zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature) 41303b2aab18SMatthew Ahrens { 41313b2aab18SMatthew Ahrens int err; 41323b2aab18SMatthew Ahrens 41333b2aab18SMatthew Ahrens /* EBUSY here indicates that the feature is already active */ 41343b2aab18SMatthew Ahrens err = dsl_sync_task(spa_name(spa), 41353b2aab18SMatthew Ahrens zfs_prop_activate_feature_check, zfs_prop_activate_feature_sync, 41367d46dc6cSMatthew Ahrens &feature, 2, ZFS_SPACE_CHECK_RESERVED); 41373b2aab18SMatthew Ahrens 41383b2aab18SMatthew Ahrens if (err != 0 && err != EBUSY) 41393b2aab18SMatthew Ahrens return (err); 41403b2aab18SMatthew Ahrens else 41413b2aab18SMatthew Ahrens return (0); 41423b2aab18SMatthew Ahrens } 41433b2aab18SMatthew Ahrens 414492241e0bSTom Erickson /* 414592241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 414692241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 414792241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 414892241e0bSTom Erickson * 414992241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 415092241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 415192241e0bSTom Erickson * of all the properties that failed a permission check along with the 415292241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 415392241e0bSTom Erickson * returned errlist. 415492241e0bSTom Erickson * 415592241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 415692241e0bSTom Erickson * pointed at by errlist is NULL. 415792241e0bSTom Erickson */ 415892241e0bSTom Erickson static int 415992241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 4160745cd3c5Smaybee { 4161745cd3c5Smaybee zfs_cmd_t *zc; 416292241e0bSTom Erickson nvpair_t *pair, *next_pair; 416392241e0bSTom Erickson nvlist_t *errors; 416492241e0bSTom Erickson int err, rv = 0; 4165745cd3c5Smaybee 4166745cd3c5Smaybee if (props == NULL) 416792241e0bSTom Erickson return (0); 416892241e0bSTom Erickson 416992241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 417092241e0bSTom Erickson 4171745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 4172745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 417392241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 417492241e0bSTom Erickson while (pair != NULL) { 417592241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 417692241e0bSTom Erickson 417792241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 417892241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 41794445fffbSMatthew Ahrens (err = zfs_secpolicy_inherit_prop(zc, NULL, CRED())) != 0) { 418092241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 418192241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 418292241e0bSTom Erickson zc->zc_value, err) == 0); 418392241e0bSTom Erickson } 418492241e0bSTom Erickson pair = next_pair; 4185745cd3c5Smaybee } 4186745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 418792241e0bSTom Erickson 418892241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 418992241e0bSTom Erickson nvlist_free(errors); 419092241e0bSTom Erickson errors = NULL; 419192241e0bSTom Erickson } else { 419292241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 419392241e0bSTom Erickson } 419492241e0bSTom Erickson 419592241e0bSTom Erickson if (errlist == NULL) 419692241e0bSTom Erickson nvlist_free(errors); 419792241e0bSTom Erickson else 419892241e0bSTom Erickson *errlist = errors; 419992241e0bSTom Erickson 420092241e0bSTom Erickson return (rv); 420192241e0bSTom Erickson } 420292241e0bSTom Erickson 420392241e0bSTom Erickson static boolean_t 420492241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 420592241e0bSTom Erickson { 420692241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 420792241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 420892241e0bSTom Erickson nvlist_t *attrs; 420992241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 421092241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 421192241e0bSTom Erickson &p1) == 0); 421292241e0bSTom Erickson } 421392241e0bSTom Erickson 421492241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 421592241e0bSTom Erickson nvlist_t *attrs; 421692241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 421792241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 421892241e0bSTom Erickson &p2) == 0); 421992241e0bSTom Erickson } 422092241e0bSTom Erickson 422192241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 422292241e0bSTom Erickson return (B_FALSE); 422392241e0bSTom Erickson 422492241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 422592241e0bSTom Erickson char *valstr1, *valstr2; 422692241e0bSTom Erickson 422792241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 422892241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 422992241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 423092241e0bSTom Erickson } else { 423192241e0bSTom Erickson uint64_t intval1, intval2; 423292241e0bSTom Erickson 423392241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 423492241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 423592241e0bSTom Erickson return (intval1 == intval2); 423692241e0bSTom Erickson } 4237745cd3c5Smaybee } 4238745cd3c5Smaybee 423992241e0bSTom Erickson /* 424092241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 424192241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 424292241e0bSTom Erickson * do not need to clear or restore properties that won't change. 424392241e0bSTom Erickson */ 424492241e0bSTom Erickson static void 424592241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 424692241e0bSTom Erickson { 424792241e0bSTom Erickson nvpair_t *pair, *next_pair; 424892241e0bSTom Erickson 424992241e0bSTom Erickson if (origprops == NULL) 425092241e0bSTom Erickson return; /* all props need to be received */ 425192241e0bSTom Erickson 425292241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 425392241e0bSTom Erickson while (pair != NULL) { 425492241e0bSTom Erickson const char *propname = nvpair_name(pair); 425592241e0bSTom Erickson nvpair_t *match; 425692241e0bSTom Erickson 425792241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 425892241e0bSTom Erickson 425992241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 426092241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 426192241e0bSTom Erickson goto next; /* need to set received value */ 426292241e0bSTom Erickson 426392241e0bSTom Erickson /* don't clear the existing received value */ 426492241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 426592241e0bSTom Erickson /* don't bother receiving the property */ 426692241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 426792241e0bSTom Erickson next: 426892241e0bSTom Erickson pair = next_pair; 426992241e0bSTom Erickson } 427092241e0bSTom Erickson } 427192241e0bSTom Erickson 42725878fad7SDan McDonald /* 42735878fad7SDan McDonald * Extract properties that cannot be set PRIOR to the receipt of a dataset. 42745878fad7SDan McDonald * For example, refquota cannot be set until after the receipt of a dataset, 42755878fad7SDan McDonald * because in replication streams, an older/earlier snapshot may exceed the 42765878fad7SDan McDonald * refquota. We want to receive the older/earlier snapshot, but setting 42775878fad7SDan McDonald * refquota pre-receipt will set the dsl's ACTUAL quota, which will prevent 42785878fad7SDan McDonald * the older/earlier snapshot from being received (with EDQUOT). 42795878fad7SDan McDonald * 42805878fad7SDan McDonald * The ZFS test "zfs_receive_011_pos" demonstrates such a scenario. 42815878fad7SDan McDonald * 42825878fad7SDan McDonald * libzfs will need to be judicious handling errors encountered by props 42835878fad7SDan McDonald * extracted by this function. 42845878fad7SDan McDonald */ 42855878fad7SDan McDonald static nvlist_t * 42865878fad7SDan McDonald extract_delay_props(nvlist_t *props) 42875878fad7SDan McDonald { 42885878fad7SDan McDonald nvlist_t *delayprops; 42895878fad7SDan McDonald nvpair_t *nvp, *tmp; 42905878fad7SDan McDonald static const zfs_prop_t delayable[] = { ZFS_PROP_REFQUOTA, 0 }; 42915878fad7SDan McDonald int i; 42925878fad7SDan McDonald 42935878fad7SDan McDonald VERIFY(nvlist_alloc(&delayprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 42945878fad7SDan McDonald 42955878fad7SDan McDonald for (nvp = nvlist_next_nvpair(props, NULL); nvp != NULL; 42965878fad7SDan McDonald nvp = nvlist_next_nvpair(props, nvp)) { 42975878fad7SDan McDonald /* 42985878fad7SDan McDonald * strcmp() is safe because zfs_prop_to_name() always returns 42995878fad7SDan McDonald * a bounded string. 43005878fad7SDan McDonald */ 43015878fad7SDan McDonald for (i = 0; delayable[i] != 0; i++) { 43025878fad7SDan McDonald if (strcmp(zfs_prop_to_name(delayable[i]), 43035878fad7SDan McDonald nvpair_name(nvp)) == 0) { 43045878fad7SDan McDonald break; 43055878fad7SDan McDonald } 43065878fad7SDan McDonald } 43075878fad7SDan McDonald if (delayable[i] != 0) { 43085878fad7SDan McDonald tmp = nvlist_prev_nvpair(props, nvp); 43095878fad7SDan McDonald VERIFY(nvlist_add_nvpair(delayprops, nvp) == 0); 43105878fad7SDan McDonald VERIFY(nvlist_remove_nvpair(props, nvp) == 0); 43115878fad7SDan McDonald nvp = tmp; 43125878fad7SDan McDonald } 43135878fad7SDan McDonald } 43145878fad7SDan McDonald 43155878fad7SDan McDonald if (nvlist_empty(delayprops)) { 43165878fad7SDan McDonald nvlist_free(delayprops); 43175878fad7SDan McDonald delayprops = NULL; 43185878fad7SDan McDonald } 43195878fad7SDan McDonald return (delayprops); 43205878fad7SDan McDonald } 43215878fad7SDan McDonald 432292241e0bSTom Erickson #ifdef DEBUG 432392241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 432492241e0bSTom Erickson #endif 432592241e0bSTom Erickson 43263cb34c60Sahrens /* 43273cb34c60Sahrens * inputs: 43283cb34c60Sahrens * zc_name name of containing filesystem 43293cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 43303cb34c60Sahrens * zc_value name of snapshot to create 43313cb34c60Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE) 43323cb34c60Sahrens * zc_cookie file descriptor to recv from 43333cb34c60Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped) 43343cb34c60Sahrens * zc_guid force flag 4335c99e4bdcSChris Kirby * zc_cleanup_fd cleanup-on-exit file descriptor 4336c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping (or zero on first call) 43379c3fd121SMatthew Ahrens * zc_resumable if data is incomplete assume sender will resume 43383cb34c60Sahrens * 43393cb34c60Sahrens * outputs: 43403cb34c60Sahrens * zc_cookie number of bytes read 434192241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 434292241e0bSTom Erickson * zc_obj zprop_errflags_t 4343c99e4bdcSChris Kirby * zc_action_handle handle for this guid/ds mapping 43443cb34c60Sahrens */ 4345fa9e4066Sahrens static int 43463cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc) 4347fa9e4066Sahrens { 4348fa9e4066Sahrens file_t *fp; 43493cb34c60Sahrens dmu_recv_cookie_t drc; 4350f18faf3fSek boolean_t force = (boolean_t)zc->zc_guid; 435192241e0bSTom Erickson int fd; 435292241e0bSTom Erickson int error = 0; 435392241e0bSTom Erickson int props_error = 0; 435492241e0bSTom Erickson nvlist_t *errors; 43553cb34c60Sahrens offset_t off; 435692241e0bSTom Erickson nvlist_t *props = NULL; /* sent properties */ 435792241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 43585878fad7SDan McDonald nvlist_t *delayprops = NULL; /* sent properties applied post-receive */ 43593b2aab18SMatthew Ahrens char *origin = NULL; 43603cb34c60Sahrens char *tosnap; 43619adfa60dSMatthew Ahrens char tofs[ZFS_MAX_DATASET_NAME_LEN]; 436292241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 4363fa9e4066Sahrens 43643ccfa83cSahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 4365f18faf3fSek strchr(zc->zc_value, '@') == NULL || 4366f18faf3fSek strchr(zc->zc_value, '%')) 4367be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 43683ccfa83cSahrens 43693cb34c60Sahrens (void) strcpy(tofs, zc->zc_value); 43703cb34c60Sahrens tosnap = strchr(tofs, '@'); 437192241e0bSTom Erickson *tosnap++ = '\0'; 43723cb34c60Sahrens 43733cb34c60Sahrens if (zc->zc_nvlist_src != NULL && 43743cb34c60Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 4375478ed9adSEric Taylor zc->zc_iflags, &props)) != 0) 43763cb34c60Sahrens return (error); 43773cb34c60Sahrens 4378fa9e4066Sahrens fd = zc->zc_cookie; 4379fa9e4066Sahrens fp = getf(fd); 43803cb34c60Sahrens if (fp == NULL) { 43813cb34c60Sahrens nvlist_free(props); 4382be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 43833cb34c60Sahrens } 4384f18faf3fSek 43859c3fd121SMatthew Ahrens errors = fnvlist_alloc(); 438692241e0bSTom Erickson 43873b2aab18SMatthew Ahrens if (zc->zc_string[0]) 43883b2aab18SMatthew Ahrens origin = zc->zc_string; 43893b2aab18SMatthew Ahrens 43903b2aab18SMatthew Ahrens error = dmu_recv_begin(tofs, tosnap, 43919c3fd121SMatthew Ahrens &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc); 43923b2aab18SMatthew Ahrens if (error != 0) 43933b2aab18SMatthew Ahrens goto out; 43943b2aab18SMatthew Ahrens 43953b2aab18SMatthew Ahrens /* 43963b2aab18SMatthew Ahrens * Set properties before we receive the stream so that they are applied 43973b2aab18SMatthew Ahrens * to the new data. Note that we must call dmu_recv_stream() if 43983b2aab18SMatthew Ahrens * dmu_recv_begin() succeeds. 43993b2aab18SMatthew Ahrens */ 44003b2aab18SMatthew Ahrens if (props != NULL && !drc.drc_newfs) { 44013b2aab18SMatthew Ahrens if (spa_version(dsl_dataset_get_spa(drc.drc_ds)) >= 44023b2aab18SMatthew Ahrens SPA_VERSION_RECVD_PROPS && 44033b2aab18SMatthew Ahrens !dsl_prop_get_hasrecvd(tofs)) 440492241e0bSTom Erickson first_recvd_props = B_TRUE; 440592241e0bSTom Erickson 4406745cd3c5Smaybee /* 440792241e0bSTom Erickson * If new received properties are supplied, they are to 440892241e0bSTom Erickson * completely replace the existing received properties, so stash 440992241e0bSTom Erickson * away the existing ones. 4410745cd3c5Smaybee */ 44113b2aab18SMatthew Ahrens if (dsl_prop_get_received(tofs, &origprops) == 0) { 441292241e0bSTom Erickson nvlist_t *errlist = NULL; 441392241e0bSTom Erickson /* 441492241e0bSTom Erickson * Don't bother writing a property if its value won't 441592241e0bSTom Erickson * change (and avoid the unnecessary security checks). 441692241e0bSTom Erickson * 441792241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 441892241e0bSTom Erickson * special case where we blow away all local properties 441992241e0bSTom Erickson * regardless. 442092241e0bSTom Erickson */ 442192241e0bSTom Erickson if (!first_recvd_props) 442292241e0bSTom Erickson props_reduce(props, origprops); 44233b2aab18SMatthew Ahrens if (zfs_check_clearable(tofs, origprops, &errlist) != 0) 442492241e0bSTom Erickson (void) nvlist_merge(errors, errlist, 0); 442592241e0bSTom Erickson nvlist_free(errlist); 44263cb34c60Sahrens 44273b2aab18SMatthew Ahrens if (clear_received_props(tofs, origprops, 44283b2aab18SMatthew Ahrens first_recvd_props ? NULL : props) != 0) 442992241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 44303b2aab18SMatthew Ahrens } else { 443192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NOCLEAR; 443292241e0bSTom Erickson } 44333b2aab18SMatthew Ahrens } 443492241e0bSTom Erickson 44353b2aab18SMatthew Ahrens if (props != NULL) { 44363b2aab18SMatthew Ahrens props_error = dsl_prop_set_hasrecvd(tofs); 44373b2aab18SMatthew Ahrens 44383b2aab18SMatthew Ahrens if (props_error == 0) { 44395878fad7SDan McDonald delayprops = extract_delay_props(props); 44403b2aab18SMatthew Ahrens (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 44413b2aab18SMatthew Ahrens props, errors); 44423b2aab18SMatthew Ahrens } 444392241e0bSTom Erickson } 444492241e0bSTom Erickson 44453cb34c60Sahrens off = fp->f_offset; 4446c99e4bdcSChris Kirby error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd, 4447c99e4bdcSChris Kirby &zc->zc_action_handle); 4448a2eea2e1Sahrens 4449f4b94bdeSMatthew Ahrens if (error == 0) { 4450f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 4451745cd3c5Smaybee 4452f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 4453f4b94bdeSMatthew Ahrens /* online recv */ 4454690041b9SAndriy Gapon dsl_dataset_t *ds; 4455f4b94bdeSMatthew Ahrens int end_err; 4456745cd3c5Smaybee 4457690041b9SAndriy Gapon ds = dmu_objset_ds(zfsvfs->z_os); 4458503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 4459f4b94bdeSMatthew Ahrens /* 4460f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 4461f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 4462f4b94bdeSMatthew Ahrens */ 446391948b51SKeith M Wesolowski end_err = dmu_recv_end(&drc, zfsvfs); 44645c703fceSGeorge Wilson if (error == 0) 4465690041b9SAndriy Gapon error = zfs_resume_fs(zfsvfs, ds); 4466f4b94bdeSMatthew Ahrens error = error ? error : end_err; 4467f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 4468745cd3c5Smaybee } else { 446991948b51SKeith M Wesolowski error = dmu_recv_end(&drc, NULL); 44703cb34c60Sahrens } 44715878fad7SDan McDonald 44725878fad7SDan McDonald /* Set delayed properties now, after we're done receiving. */ 44735878fad7SDan McDonald if (delayprops != NULL && error == 0) { 44745878fad7SDan McDonald (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 44755878fad7SDan McDonald delayprops, errors); 44765878fad7SDan McDonald } 44775878fad7SDan McDonald } 44785878fad7SDan McDonald 44795878fad7SDan McDonald if (delayprops != NULL) { 44805878fad7SDan McDonald /* 44815878fad7SDan McDonald * Merge delayed props back in with initial props, in case 44825878fad7SDan McDonald * we're DEBUG and zfs_ioc_recv_inject_err is set (which means 44835878fad7SDan McDonald * we have to make sure clear_received_props() includes 44845878fad7SDan McDonald * the delayed properties). 44855878fad7SDan McDonald * 44865878fad7SDan McDonald * Since zfs_ioc_recv_inject_err is only in DEBUG kernels, 44875878fad7SDan McDonald * using ASSERT() will be just like a VERIFY. 44885878fad7SDan McDonald */ 44895878fad7SDan McDonald ASSERT(nvlist_merge(props, delayprops, 0) == 0); 44905878fad7SDan McDonald nvlist_free(delayprops); 44915878fad7SDan McDonald } 44925878fad7SDan McDonald 44935878fad7SDan McDonald /* 44945878fad7SDan McDonald * Now that all props, initial and delayed, are set, report the prop 44955878fad7SDan McDonald * errors to the caller. 44965878fad7SDan McDonald */ 44975878fad7SDan McDonald if (zc->zc_nvlist_dst_size != 0 && 44985878fad7SDan McDonald (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || 44995878fad7SDan McDonald put_nvlist(zc, errors) != 0)) { 45005878fad7SDan McDonald /* 45015878fad7SDan McDonald * Caller made zc->zc_nvlist_dst less than the minimum expected 45025878fad7SDan McDonald * size or supplied an invalid address. 45035878fad7SDan McDonald */ 45045878fad7SDan McDonald props_error = SET_ERROR(EINVAL); 450547f263f4Sek } 45063cb34c60Sahrens 45073cb34c60Sahrens zc->zc_cookie = off - fp->f_offset; 45083cb34c60Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 45093cb34c60Sahrens fp->f_offset = off; 4510a2eea2e1Sahrens 451192241e0bSTom Erickson #ifdef DEBUG 451292241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 451392241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 451492241e0bSTom Erickson error = 1; 451592241e0bSTom Erickson } 451692241e0bSTom Erickson #endif 4517745cd3c5Smaybee /* 4518745cd3c5Smaybee * On error, restore the original props. 4519745cd3c5Smaybee */ 45203b2aab18SMatthew Ahrens if (error != 0 && props != NULL && !drc.drc_newfs) { 45213b2aab18SMatthew Ahrens if (clear_received_props(tofs, props, NULL) != 0) { 45223b2aab18SMatthew Ahrens /* 45233b2aab18SMatthew Ahrens * We failed to clear the received properties. 45243b2aab18SMatthew Ahrens * Since we may have left a $recvd value on the 45253b2aab18SMatthew Ahrens * system, we can't clear the $hasrecvd flag. 45263b2aab18SMatthew Ahrens */ 452792241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 45283b2aab18SMatthew Ahrens } else if (first_recvd_props) { 45293b2aab18SMatthew Ahrens dsl_prop_unset_hasrecvd(tofs); 453092241e0bSTom Erickson } 453192241e0bSTom Erickson 453292241e0bSTom Erickson if (origprops == NULL && !drc.drc_newfs) { 453392241e0bSTom Erickson /* We failed to stash the original properties. */ 453492241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 453592241e0bSTom Erickson } 453692241e0bSTom Erickson 453792241e0bSTom Erickson /* 453892241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 453992241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 454092241e0bSTom Erickson * explictly if we're restoring local properties cleared in the 454192241e0bSTom Erickson * first new-style receive. 454292241e0bSTom Erickson */ 454392241e0bSTom Erickson if (origprops != NULL && 454492241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 454592241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 454692241e0bSTom Erickson origprops, NULL) != 0) { 454792241e0bSTom Erickson /* 454892241e0bSTom Erickson * We stashed the original properties but failed to 454992241e0bSTom Erickson * restore them. 455092241e0bSTom Erickson */ 455192241e0bSTom Erickson zc->zc_obj |= ZPROP_ERR_NORESTORE; 455292241e0bSTom Erickson } 4553745cd3c5Smaybee } 4554745cd3c5Smaybee out: 4555745cd3c5Smaybee nvlist_free(props); 4556745cd3c5Smaybee nvlist_free(origprops); 455792241e0bSTom Erickson nvlist_free(errors); 4558fa9e4066Sahrens releasef(fd); 455992241e0bSTom Erickson 456092241e0bSTom Erickson if (error == 0) 456192241e0bSTom Erickson error = props_error; 456292241e0bSTom Erickson 4563fa9e4066Sahrens return (error); 4564fa9e4066Sahrens } 4565fa9e4066Sahrens 45663cb34c60Sahrens /* 45673cb34c60Sahrens * inputs: 45683cb34c60Sahrens * zc_name name of snapshot to send 45693cb34c60Sahrens * zc_cookie file descriptor to send stream to 4570a7f53a56SChris Kirby * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) 4571a7f53a56SChris Kirby * zc_sendobj objsetid of snapshot to send 4572a7f53a56SChris Kirby * zc_fromobj objsetid of incremental fromsnap (may be zero) 457319b94df9SMatthew Ahrens * zc_guid if set, estimate size of stream only. zc_cookie is ignored. 457419b94df9SMatthew Ahrens * output size in zc_objset_type. 4575b5152584SMatthew Ahrens * zc_flags lzc_send_flags 45763cb34c60Sahrens * 457778f17100SMatthew Ahrens * outputs: 457878f17100SMatthew Ahrens * zc_objset_type estimated size, if zc_guid is set 45793cb34c60Sahrens */ 4580fa9e4066Sahrens static int 45813cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 4582fa9e4066Sahrens { 4583fa9e4066Sahrens int error; 45843cb34c60Sahrens offset_t off; 458519b94df9SMatthew Ahrens boolean_t estimate = (zc->zc_guid != 0); 45865d7b4d43SMatthew Ahrens boolean_t embedok = (zc->zc_flags & 0x1); 4587b5152584SMatthew Ahrens boolean_t large_block_ok = (zc->zc_flags & 0x2); 45885602294fSDan Kimmel boolean_t compressok = (zc->zc_flags & 0x4); 4589fa9e4066Sahrens 45903b2aab18SMatthew Ahrens if (zc->zc_obj != 0) { 45913b2aab18SMatthew Ahrens dsl_pool_t *dp; 45923b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 4593a7f53a56SChris Kirby 45943b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 45953b2aab18SMatthew Ahrens if (error != 0) 4596a7f53a56SChris Kirby return (error); 45973b2aab18SMatthew Ahrens 45983b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 45993b2aab18SMatthew Ahrens if (error != 0) { 46003b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 4601fa9e4066Sahrens return (error); 4602fa9e4066Sahrens } 46033b2aab18SMatthew Ahrens 46043b2aab18SMatthew Ahrens if (dsl_dir_is_clone(tosnap->ds_dir)) 4605c1379625SJustin T. Gibbs zc->zc_fromobj = 4606c1379625SJustin T. Gibbs dsl_dir_phys(tosnap->ds_dir)->dd_origin_obj; 46073b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 46083b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 46094445fffbSMatthew Ahrens } 46104445fffbSMatthew Ahrens 46113b2aab18SMatthew Ahrens if (estimate) { 46123b2aab18SMatthew Ahrens dsl_pool_t *dp; 46133b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 46143b2aab18SMatthew Ahrens dsl_dataset_t *fromsnap = NULL; 46154445fffbSMatthew Ahrens 46163b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 46173b2aab18SMatthew Ahrens if (error != 0) 46183b2aab18SMatthew Ahrens return (error); 46193b2aab18SMatthew Ahrens 46203b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 46213b2aab18SMatthew Ahrens if (error != 0) { 46223b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 46233b2aab18SMatthew Ahrens return (error); 46244445fffbSMatthew Ahrens } 46254445fffbSMatthew Ahrens 46263b2aab18SMatthew Ahrens if (zc->zc_fromobj != 0) { 46273b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, 46283b2aab18SMatthew Ahrens FTAG, &fromsnap); 46293b2aab18SMatthew Ahrens if (error != 0) { 46303b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 46313b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 46324445fffbSMatthew Ahrens return (error); 46334445fffbSMatthew Ahrens } 46344445fffbSMatthew Ahrens } 4635fa9e4066Sahrens 46365602294fSDan Kimmel error = dmu_send_estimate(tosnap, fromsnap, compressok, 463719b94df9SMatthew Ahrens &zc->zc_objset_type); 46383b2aab18SMatthew Ahrens 46393b2aab18SMatthew Ahrens if (fromsnap != NULL) 46403b2aab18SMatthew Ahrens dsl_dataset_rele(fromsnap, FTAG); 46413b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 46423b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 464319b94df9SMatthew Ahrens } else { 464419b94df9SMatthew Ahrens file_t *fp = getf(zc->zc_cookie); 46453b2aab18SMatthew Ahrens if (fp == NULL) 4646be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 4647fa9e4066Sahrens 464819b94df9SMatthew Ahrens off = fp->f_offset; 46493b2aab18SMatthew Ahrens error = dmu_send_obj(zc->zc_name, zc->zc_sendobj, 46505602294fSDan Kimmel zc->zc_fromobj, embedok, large_block_ok, compressok, 4651b5152584SMatthew Ahrens zc->zc_cookie, fp->f_vnode, &off); 4652fa9e4066Sahrens 465319b94df9SMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 465419b94df9SMatthew Ahrens fp->f_offset = off; 465519b94df9SMatthew Ahrens releasef(zc->zc_cookie); 465619b94df9SMatthew Ahrens } 4657fa9e4066Sahrens return (error); 4658fa9e4066Sahrens } 4659fa9e4066Sahrens 46604e3c9f44SBill Pijewski /* 46614e3c9f44SBill Pijewski * inputs: 46624e3c9f44SBill Pijewski * zc_name name of snapshot on which to report progress 46634e3c9f44SBill Pijewski * zc_cookie file descriptor of send stream 46644e3c9f44SBill Pijewski * 46654e3c9f44SBill Pijewski * outputs: 46664e3c9f44SBill Pijewski * zc_cookie number of bytes written in send stream thus far 46674e3c9f44SBill Pijewski */ 46684e3c9f44SBill Pijewski static int 46694e3c9f44SBill Pijewski zfs_ioc_send_progress(zfs_cmd_t *zc) 46704e3c9f44SBill Pijewski { 46713b2aab18SMatthew Ahrens dsl_pool_t *dp; 46724e3c9f44SBill Pijewski dsl_dataset_t *ds; 46734e3c9f44SBill Pijewski dmu_sendarg_t *dsp = NULL; 46744e3c9f44SBill Pijewski int error; 46754e3c9f44SBill Pijewski 46763b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 46773b2aab18SMatthew Ahrens if (error != 0) 46783b2aab18SMatthew Ahrens return (error); 46793b2aab18SMatthew Ahrens 46803b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); 46813b2aab18SMatthew Ahrens if (error != 0) { 46823b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 46834e3c9f44SBill Pijewski return (error); 46843b2aab18SMatthew Ahrens } 46854e3c9f44SBill Pijewski 46864e3c9f44SBill Pijewski mutex_enter(&ds->ds_sendstream_lock); 46874e3c9f44SBill Pijewski 46884e3c9f44SBill Pijewski /* 46894e3c9f44SBill Pijewski * Iterate over all the send streams currently active on this dataset. 46904e3c9f44SBill Pijewski * If there's one which matches the specified file descriptor _and_ the 46914e3c9f44SBill Pijewski * stream was started by the current process, return the progress of 46924e3c9f44SBill Pijewski * that stream. 46934e3c9f44SBill Pijewski */ 46944e3c9f44SBill Pijewski for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL; 46954e3c9f44SBill Pijewski dsp = list_next(&ds->ds_sendstreams, dsp)) { 46964e3c9f44SBill Pijewski if (dsp->dsa_outfd == zc->zc_cookie && 46974e3c9f44SBill Pijewski dsp->dsa_proc == curproc) 46984e3c9f44SBill Pijewski break; 46994e3c9f44SBill Pijewski } 47004e3c9f44SBill Pijewski 47014e3c9f44SBill Pijewski if (dsp != NULL) 47024e3c9f44SBill Pijewski zc->zc_cookie = *(dsp->dsa_off); 47034e3c9f44SBill Pijewski else 4704be6fd75aSMatthew Ahrens error = SET_ERROR(ENOENT); 47054e3c9f44SBill Pijewski 47064e3c9f44SBill Pijewski mutex_exit(&ds->ds_sendstream_lock); 47074e3c9f44SBill Pijewski dsl_dataset_rele(ds, FTAG); 47083b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 47094e3c9f44SBill Pijewski return (error); 47104e3c9f44SBill Pijewski } 47114e3c9f44SBill Pijewski 4712ea8dc4b6Seschrock static int 4713ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 4714ea8dc4b6Seschrock { 4715ea8dc4b6Seschrock int id, error; 4716ea8dc4b6Seschrock 4717ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 4718ea8dc4b6Seschrock &zc->zc_inject_record); 4719ea8dc4b6Seschrock 4720ea8dc4b6Seschrock if (error == 0) 4721ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 4722ea8dc4b6Seschrock 4723ea8dc4b6Seschrock return (error); 4724ea8dc4b6Seschrock } 4725ea8dc4b6Seschrock 4726ea8dc4b6Seschrock static int 4727ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 4728ea8dc4b6Seschrock { 4729ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 4730ea8dc4b6Seschrock } 4731ea8dc4b6Seschrock 4732ea8dc4b6Seschrock static int 4733ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 4734ea8dc4b6Seschrock { 4735ea8dc4b6Seschrock int id = (int)zc->zc_guid; 4736ea8dc4b6Seschrock int error; 4737ea8dc4b6Seschrock 4738ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 4739ea8dc4b6Seschrock &zc->zc_inject_record); 4740ea8dc4b6Seschrock 4741ea8dc4b6Seschrock zc->zc_guid = id; 4742ea8dc4b6Seschrock 4743ea8dc4b6Seschrock return (error); 4744ea8dc4b6Seschrock } 4745ea8dc4b6Seschrock 4746ea8dc4b6Seschrock static int 4747ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 4748ea8dc4b6Seschrock { 4749ea8dc4b6Seschrock spa_t *spa; 4750ea8dc4b6Seschrock int error; 4751e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 4752ea8dc4b6Seschrock 4753ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 4754ea8dc4b6Seschrock return (error); 4755ea8dc4b6Seschrock 4756e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 4757ea8dc4b6Seschrock &count); 4758ea8dc4b6Seschrock if (error == 0) 4759e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 4760ea8dc4b6Seschrock else 4761e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 4762ea8dc4b6Seschrock 4763ea8dc4b6Seschrock spa_close(spa, FTAG); 4764ea8dc4b6Seschrock 4765ea8dc4b6Seschrock return (error); 4766ea8dc4b6Seschrock } 4767ea8dc4b6Seschrock 4768ea8dc4b6Seschrock static int 4769ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 4770ea8dc4b6Seschrock { 4771ea8dc4b6Seschrock spa_t *spa; 4772ea8dc4b6Seschrock vdev_t *vd; 4773bb8b5132Sek int error; 4774ea8dc4b6Seschrock 4775b87f3af3Sperrin /* 4776b87f3af3Sperrin * On zpool clear we also fix up missing slogs 4777b87f3af3Sperrin */ 4778b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 4779b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 4780b87f3af3Sperrin if (spa == NULL) { 4781b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4782be6fd75aSMatthew Ahrens return (SET_ERROR(EIO)); 4783b87f3af3Sperrin } 4784b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 4785b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 4786b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 4787b87f3af3Sperrin } 4788468c413aSTim Haley spa->spa_last_open_failed = 0; 4789b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 4790b87f3af3Sperrin 4791c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 4792468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 4793468c413aSTim Haley } else { 4794468c413aSTim Haley nvlist_t *policy; 4795468c413aSTim Haley nvlist_t *config = NULL; 4796468c413aSTim Haley 4797468c413aSTim Haley if (zc->zc_nvlist_src == NULL) 4798be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 4799468c413aSTim Haley 4800468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 4801468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 4802468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 4803468c413aSTim Haley policy, &config); 4804468c413aSTim Haley if (config != NULL) { 48054b964adaSGeorge Wilson int err; 48064b964adaSGeorge Wilson 48074b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 48084b964adaSGeorge Wilson error = err; 4809468c413aSTim Haley nvlist_free(config); 4810468c413aSTim Haley } 4811468c413aSTim Haley nvlist_free(policy); 4812468c413aSTim Haley } 4813468c413aSTim Haley } 4814468c413aSTim Haley 48153b2aab18SMatthew Ahrens if (error != 0) 4816ea8dc4b6Seschrock return (error); 4817ea8dc4b6Seschrock 48188f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4819ea8dc4b6Seschrock 4820e9dbad6fSeschrock if (zc->zc_guid == 0) { 4821ea8dc4b6Seschrock vd = NULL; 4822c5904d13Seschrock } else { 4823c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 4824fa94a07fSbrendan if (vd == NULL) { 4825e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 4826fa94a07fSbrendan spa_close(spa, FTAG); 4827be6fd75aSMatthew Ahrens return (SET_ERROR(ENODEV)); 4828fa94a07fSbrendan } 4829ea8dc4b6Seschrock } 4830ea8dc4b6Seschrock 4831e14bb325SJeff Bonwick vdev_clear(spa, vd); 4832e14bb325SJeff Bonwick 4833e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 4834ea8dc4b6Seschrock 4835e14bb325SJeff Bonwick /* 4836e14bb325SJeff Bonwick * Resume any suspended I/Os. 4837e14bb325SJeff Bonwick */ 483854d692b7SGeorge Wilson if (zio_resume(spa) != 0) 4839be6fd75aSMatthew Ahrens error = SET_ERROR(EIO); 4840ea8dc4b6Seschrock 4841ea8dc4b6Seschrock spa_close(spa, FTAG); 4842ea8dc4b6Seschrock 484354d692b7SGeorge Wilson return (error); 4844ea8dc4b6Seschrock } 4845ea8dc4b6Seschrock 48464263d13fSGeorge Wilson static int 48474263d13fSGeorge Wilson zfs_ioc_pool_reopen(zfs_cmd_t *zc) 48484263d13fSGeorge Wilson { 48494263d13fSGeorge Wilson spa_t *spa; 48504263d13fSGeorge Wilson int error; 48514263d13fSGeorge Wilson 48524263d13fSGeorge Wilson error = spa_open(zc->zc_name, &spa, FTAG); 48533b2aab18SMatthew Ahrens if (error != 0) 48544263d13fSGeorge Wilson return (error); 48554263d13fSGeorge Wilson 48564263d13fSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 4857d6afdce2SGeorge Wilson 4858d6afdce2SGeorge Wilson /* 4859d6afdce2SGeorge Wilson * If a resilver is already in progress then set the 4860d6afdce2SGeorge Wilson * spa_scrub_reopen flag to B_TRUE so that we don't restart 4861d6afdce2SGeorge Wilson * the scan as a side effect of the reopen. Otherwise, let 4862d6afdce2SGeorge Wilson * vdev_open() decided if a resilver is required. 4863d6afdce2SGeorge Wilson */ 4864d6afdce2SGeorge Wilson spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); 48654263d13fSGeorge Wilson vdev_reopen(spa->spa_root_vdev); 4866d6afdce2SGeorge Wilson spa->spa_scrub_reopen = B_FALSE; 4867d6afdce2SGeorge Wilson 48684263d13fSGeorge Wilson (void) spa_vdev_state_exit(spa, NULL, 0); 48694263d13fSGeorge Wilson spa_close(spa, FTAG); 48704263d13fSGeorge Wilson return (0); 48714263d13fSGeorge Wilson } 48723cb34c60Sahrens /* 48733cb34c60Sahrens * inputs: 48743cb34c60Sahrens * zc_name name of filesystem 48753cb34c60Sahrens * 4876681d9761SEric Taylor * outputs: 4877681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 48783cb34c60Sahrens */ 487999653d4eSeschrock static int 488099653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 488199653d4eSeschrock { 4882a4b8c9aaSAndrew Stormont dsl_pool_t *dp; 4883a4b8c9aaSAndrew Stormont dsl_dataset_t *ds, *ods; 4884a4b8c9aaSAndrew Stormont char origin[ZFS_MAX_DATASET_NAME_LEN]; 48850b69c2f0Sahrens char *cp; 4886a4b8c9aaSAndrew Stormont int error; 4887a4b8c9aaSAndrew Stormont 4888add927f8Sloli zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 4889add927f8Sloli if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 || 4890add927f8Sloli strchr(zc->zc_name, '%')) 4891add927f8Sloli return (SET_ERROR(EINVAL)); 4892add927f8Sloli 4893a4b8c9aaSAndrew Stormont error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 4894a4b8c9aaSAndrew Stormont if (error != 0) 4895a4b8c9aaSAndrew Stormont return (error); 4896a4b8c9aaSAndrew Stormont 4897a4b8c9aaSAndrew Stormont error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); 4898a4b8c9aaSAndrew Stormont if (error != 0) { 4899a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 4900a4b8c9aaSAndrew Stormont return (error); 4901a4b8c9aaSAndrew Stormont } 4902a4b8c9aaSAndrew Stormont 4903a4b8c9aaSAndrew Stormont if (!dsl_dir_is_clone(ds->ds_dir)) { 4904a4b8c9aaSAndrew Stormont dsl_dataset_rele(ds, FTAG); 4905a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 4906a4b8c9aaSAndrew Stormont return (SET_ERROR(EINVAL)); 4907a4b8c9aaSAndrew Stormont } 4908a4b8c9aaSAndrew Stormont 4909a4b8c9aaSAndrew Stormont error = dsl_dataset_hold_obj(dp, 4910a4b8c9aaSAndrew Stormont dsl_dir_phys(ds->ds_dir)->dd_origin_obj, FTAG, &ods); 4911a4b8c9aaSAndrew Stormont if (error != 0) { 4912a4b8c9aaSAndrew Stormont dsl_dataset_rele(ds, FTAG); 4913a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 4914a4b8c9aaSAndrew Stormont return (error); 4915a4b8c9aaSAndrew Stormont } 4916a4b8c9aaSAndrew Stormont 4917a4b8c9aaSAndrew Stormont dsl_dataset_name(ods, origin); 4918a4b8c9aaSAndrew Stormont dsl_dataset_rele(ods, FTAG); 4919a4b8c9aaSAndrew Stormont dsl_dataset_rele(ds, FTAG); 4920a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 49210b69c2f0Sahrens 49220b69c2f0Sahrens /* 49230b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 49240b69c2f0Sahrens * it's easier. 49250b69c2f0Sahrens */ 4926a4b8c9aaSAndrew Stormont cp = strchr(origin, '@'); 49270b69c2f0Sahrens if (cp) 49280b69c2f0Sahrens *cp = '\0'; 4929a4b8c9aaSAndrew Stormont (void) dmu_objset_find(origin, 49303b2aab18SMatthew Ahrens zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS); 4931681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 493299653d4eSeschrock } 493399653d4eSeschrock 493414843421SMatthew Ahrens /* 493514843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 493614843421SMatthew Ahrens * 493714843421SMatthew Ahrens * inputs: 493814843421SMatthew Ahrens * zc_name name of filesystem 493914843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 494014843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 494114843421SMatthew Ahrens * zc_guid RID/UID/GID 494214843421SMatthew Ahrens * 494314843421SMatthew Ahrens * outputs: 494414843421SMatthew Ahrens * zc_cookie property value 494514843421SMatthew Ahrens */ 494614843421SMatthew Ahrens static int 494714843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 494814843421SMatthew Ahrens { 494914843421SMatthew Ahrens zfsvfs_t *zfsvfs; 495014843421SMatthew Ahrens int error; 495114843421SMatthew Ahrens 495214843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 4953be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 495414843421SMatthew Ahrens 49551412a1a2SMark Shellenbaum error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 49563b2aab18SMatthew Ahrens if (error != 0) 495714843421SMatthew Ahrens return (error); 495814843421SMatthew Ahrens 495914843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 496014843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 496114843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 496214843421SMatthew Ahrens 496314843421SMatthew Ahrens return (error); 496414843421SMatthew Ahrens } 496514843421SMatthew Ahrens 496614843421SMatthew Ahrens /* 496714843421SMatthew Ahrens * inputs: 496814843421SMatthew Ahrens * zc_name name of filesystem 496914843421SMatthew Ahrens * zc_cookie zap cursor 497014843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 497114843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 497214843421SMatthew Ahrens * 497314843421SMatthew Ahrens * outputs: 497414843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 497514843421SMatthew Ahrens * zc_cookie zap cursor 497614843421SMatthew Ahrens */ 497714843421SMatthew Ahrens static int 497814843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 497914843421SMatthew Ahrens { 498014843421SMatthew Ahrens zfsvfs_t *zfsvfs; 4981eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 498214843421SMatthew Ahrens 4983eeb85002STim Haley if (bufsize <= 0) 4984be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 4985eeb85002STim Haley 49861412a1a2SMark Shellenbaum int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 49873b2aab18SMatthew Ahrens if (error != 0) 498814843421SMatthew Ahrens return (error); 498914843421SMatthew Ahrens 499014843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 499114843421SMatthew Ahrens 499214843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 499314843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 499414843421SMatthew Ahrens 499514843421SMatthew Ahrens if (error == 0) { 499614843421SMatthew Ahrens error = xcopyout(buf, 499714843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 499814843421SMatthew Ahrens zc->zc_nvlist_dst_size); 499914843421SMatthew Ahrens } 500014843421SMatthew Ahrens kmem_free(buf, bufsize); 500114843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 500214843421SMatthew Ahrens 500314843421SMatthew Ahrens return (error); 500414843421SMatthew Ahrens } 500514843421SMatthew Ahrens 500614843421SMatthew Ahrens /* 500714843421SMatthew Ahrens * inputs: 500814843421SMatthew Ahrens * zc_name name of filesystem 500914843421SMatthew Ahrens * 501014843421SMatthew Ahrens * outputs: 501114843421SMatthew Ahrens * none 501214843421SMatthew Ahrens */ 501314843421SMatthew Ahrens static int 501414843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 501514843421SMatthew Ahrens { 501614843421SMatthew Ahrens objset_t *os; 50171195e687SMark J Musante int error = 0; 501814843421SMatthew Ahrens zfsvfs_t *zfsvfs; 501914843421SMatthew Ahrens 502014843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 5021503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 502214843421SMatthew Ahrens /* 502314843421SMatthew Ahrens * If userused is not enabled, it may be because the 502414843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 502514843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 502614843421SMatthew Ahrens */ 50275f5913bbSAndriy Gapon dsl_dataset_t *ds, *newds; 5028690041b9SAndriy Gapon 5029690041b9SAndriy Gapon ds = dmu_objset_ds(zfsvfs->z_os); 5030503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 503191948b51SKeith M Wesolowski if (error == 0) { 50325f5913bbSAndriy Gapon dmu_objset_refresh_ownership(ds, &newds, 503391948b51SKeith M Wesolowski zfsvfs); 50345f5913bbSAndriy Gapon error = zfs_resume_fs(zfsvfs, newds); 503591948b51SKeith M Wesolowski } 503614843421SMatthew Ahrens } 503714843421SMatthew Ahrens if (error == 0) 503814843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 503914843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 504014843421SMatthew Ahrens } else { 5041503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 5042503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 50433b2aab18SMatthew Ahrens if (error != 0) 504414843421SMatthew Ahrens return (error); 504514843421SMatthew Ahrens 504614843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 5047503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 504814843421SMatthew Ahrens } 504914843421SMatthew Ahrens 505014843421SMatthew Ahrens return (error); 505114843421SMatthew Ahrens } 505214843421SMatthew Ahrens 5053ecd6cf80Smarks /* 5054ecd6cf80Smarks * We don't want to have a hard dependency 5055ecd6cf80Smarks * against some special symbols in sharefs 5056da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 5057ecd6cf80Smarks * the first file system is shared. 5058da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 5059ecd6cf80Smarks */ 5060da6c28aaSamw int (*znfsexport_fs)(void *arg); 5061ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 5062da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 5063da6c28aaSamw 5064da6c28aaSamw int zfs_nfsshare_inited; 5065da6c28aaSamw int zfs_smbshare_inited; 5066ecd6cf80Smarks 5067ecd6cf80Smarks ddi_modhandle_t nfs_mod; 5068ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 5069da6c28aaSamw ddi_modhandle_t smbsrv_mod; 5070ecd6cf80Smarks kmutex_t zfs_share_lock; 5071ecd6cf80Smarks 5072da6c28aaSamw static int 5073da6c28aaSamw zfs_init_sharefs() 5074da6c28aaSamw { 5075da6c28aaSamw int error; 5076da6c28aaSamw 5077da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 5078da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 5079da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 5080da6c28aaSamw ddi_modopen("fs/sharefs", 5081da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 5082be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5083da6c28aaSamw } 5084da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 5085da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 5086da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 5087be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5088da6c28aaSamw } 5089da6c28aaSamw return (0); 5090da6c28aaSamw } 5091da6c28aaSamw 5092ecd6cf80Smarks static int 5093ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 5094ecd6cf80Smarks { 5095ecd6cf80Smarks int error; 5096ecd6cf80Smarks int opcode; 5097ecd6cf80Smarks 5098da6c28aaSamw switch (zc->zc_share.z_sharetype) { 5099da6c28aaSamw case ZFS_SHARE_NFS: 5100da6c28aaSamw case ZFS_UNSHARE_NFS: 5101da6c28aaSamw if (zfs_nfsshare_inited == 0) { 5102da6c28aaSamw mutex_enter(&zfs_share_lock); 5103da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 5104da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 5105da6c28aaSamw mutex_exit(&zfs_share_lock); 5106be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5107da6c28aaSamw } 5108da6c28aaSamw if (znfsexport_fs == NULL && 5109da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 5110da6c28aaSamw ddi_modsym(nfs_mod, 5111da6c28aaSamw "nfs_export", &error)) == NULL)) { 5112da6c28aaSamw mutex_exit(&zfs_share_lock); 5113be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5114da6c28aaSamw } 5115da6c28aaSamw error = zfs_init_sharefs(); 51163b2aab18SMatthew Ahrens if (error != 0) { 5117da6c28aaSamw mutex_exit(&zfs_share_lock); 5118be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5119da6c28aaSamw } 5120da6c28aaSamw zfs_nfsshare_inited = 1; 5121ecd6cf80Smarks mutex_exit(&zfs_share_lock); 5122ecd6cf80Smarks } 5123da6c28aaSamw break; 5124da6c28aaSamw case ZFS_SHARE_SMB: 5125da6c28aaSamw case ZFS_UNSHARE_SMB: 5126da6c28aaSamw if (zfs_smbshare_inited == 0) { 5127da6c28aaSamw mutex_enter(&zfs_share_lock); 5128da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 5129da6c28aaSamw ddi_modopen("drv/smbsrv", 5130da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 5131da6c28aaSamw mutex_exit(&zfs_share_lock); 5132be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5133da6c28aaSamw } 5134da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 5135da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 5136faa1795aSjb "smb_server_share", &error)) == NULL)) { 5137da6c28aaSamw mutex_exit(&zfs_share_lock); 5138be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5139da6c28aaSamw } 5140da6c28aaSamw error = zfs_init_sharefs(); 51413b2aab18SMatthew Ahrens if (error != 0) { 5142da6c28aaSamw mutex_exit(&zfs_share_lock); 5143be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5144da6c28aaSamw } 5145da6c28aaSamw zfs_smbshare_inited = 1; 5146ecd6cf80Smarks mutex_exit(&zfs_share_lock); 5147ecd6cf80Smarks } 5148da6c28aaSamw break; 5149da6c28aaSamw default: 5150be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5151da6c28aaSamw } 5152ecd6cf80Smarks 5153da6c28aaSamw switch (zc->zc_share.z_sharetype) { 5154da6c28aaSamw case ZFS_SHARE_NFS: 5155da6c28aaSamw case ZFS_UNSHARE_NFS: 5156da6c28aaSamw if (error = 5157da6c28aaSamw znfsexport_fs((void *) 5158da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 5159da6c28aaSamw return (error); 5160da6c28aaSamw break; 5161da6c28aaSamw case ZFS_SHARE_SMB: 5162da6c28aaSamw case ZFS_UNSHARE_SMB: 5163da6c28aaSamw if (error = zsmbexport_fs((void *) 5164da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 5165da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 5166743a77edSAlan Wright B_TRUE: B_FALSE)) { 5167da6c28aaSamw return (error); 5168ecd6cf80Smarks } 5169da6c28aaSamw break; 5170ecd6cf80Smarks } 5171ecd6cf80Smarks 5172da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 5173da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 5174ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 5175ecd6cf80Smarks 5176da6c28aaSamw /* 5177da6c28aaSamw * Add or remove share from sharetab 5178da6c28aaSamw */ 5179ecd6cf80Smarks error = zshare_fs(opcode, 5180ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 5181ecd6cf80Smarks zc->zc_share.z_sharemax); 5182ecd6cf80Smarks 5183ecd6cf80Smarks return (error); 5184ecd6cf80Smarks 5185ecd6cf80Smarks } 5186ecd6cf80Smarks 5187743a77edSAlan Wright ace_t full_access[] = { 5188743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 5189743a77edSAlan Wright }; 5190743a77edSAlan Wright 519199d5e173STim Haley /* 519299d5e173STim Haley * inputs: 519399d5e173STim Haley * zc_name name of containing filesystem 519499d5e173STim Haley * zc_obj object # beyond which we want next in-use object # 519599d5e173STim Haley * 519699d5e173STim Haley * outputs: 519799d5e173STim Haley * zc_obj next in-use object # 519899d5e173STim Haley */ 519999d5e173STim Haley static int 520099d5e173STim Haley zfs_ioc_next_obj(zfs_cmd_t *zc) 520199d5e173STim Haley { 520299d5e173STim Haley objset_t *os = NULL; 520399d5e173STim Haley int error; 520499d5e173STim Haley 520599d5e173STim Haley error = dmu_objset_hold(zc->zc_name, FTAG, &os); 52063b2aab18SMatthew Ahrens if (error != 0) 520799d5e173STim Haley return (error); 520899d5e173STim Haley 520999d5e173STim Haley error = dmu_object_next(os, &zc->zc_obj, B_FALSE, 5210c1379625SJustin T. Gibbs dsl_dataset_phys(os->os_dsl_dataset)->ds_prev_snap_txg); 521199d5e173STim Haley 521299d5e173STim Haley dmu_objset_rele(os, FTAG); 521399d5e173STim Haley return (error); 521499d5e173STim Haley } 521599d5e173STim Haley 521699d5e173STim Haley /* 521799d5e173STim Haley * inputs: 521899d5e173STim Haley * zc_name name of filesystem 521999d5e173STim Haley * zc_value prefix name for snapshot 522099d5e173STim Haley * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 522199d5e173STim Haley * 522299d5e173STim Haley * outputs: 52234445fffbSMatthew Ahrens * zc_value short name of new snapshot 522499d5e173STim Haley */ 522599d5e173STim Haley static int 522699d5e173STim Haley zfs_ioc_tmp_snapshot(zfs_cmd_t *zc) 522799d5e173STim Haley { 522899d5e173STim Haley char *snap_name; 52293b2aab18SMatthew Ahrens char *hold_name; 523099d5e173STim Haley int error; 52313b2aab18SMatthew Ahrens minor_t minor; 523299d5e173STim Haley 52333b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor); 52343b2aab18SMatthew Ahrens if (error != 0) 523599d5e173STim Haley return (error); 523699d5e173STim Haley 52373b2aab18SMatthew Ahrens snap_name = kmem_asprintf("%s-%016llx", zc->zc_value, 52383b2aab18SMatthew Ahrens (u_longlong_t)ddi_get_lbolt64()); 52393b2aab18SMatthew Ahrens hold_name = kmem_asprintf("%%%s", zc->zc_value); 52403b2aab18SMatthew Ahrens 52413b2aab18SMatthew Ahrens error = dsl_dataset_snapshot_tmp(zc->zc_name, snap_name, minor, 52423b2aab18SMatthew Ahrens hold_name); 52433b2aab18SMatthew Ahrens if (error == 0) 52443b2aab18SMatthew Ahrens (void) strcpy(zc->zc_value, snap_name); 524599d5e173STim Haley strfree(snap_name); 52463b2aab18SMatthew Ahrens strfree(hold_name); 52473b2aab18SMatthew Ahrens zfs_onexit_fd_rele(zc->zc_cleanup_fd); 52483b2aab18SMatthew Ahrens return (error); 524999d5e173STim Haley } 525099d5e173STim Haley 525199d5e173STim Haley /* 525299d5e173STim Haley * inputs: 525399d5e173STim Haley * zc_name name of "to" snapshot 525499d5e173STim Haley * zc_value name of "from" snapshot 525599d5e173STim Haley * zc_cookie file descriptor to write diff data on 525699d5e173STim Haley * 525799d5e173STim Haley * outputs: 525899d5e173STim Haley * dmu_diff_record_t's to the file descriptor 525999d5e173STim Haley */ 526099d5e173STim Haley static int 526199d5e173STim Haley zfs_ioc_diff(zfs_cmd_t *zc) 526299d5e173STim Haley { 526399d5e173STim Haley file_t *fp; 526499d5e173STim Haley offset_t off; 526599d5e173STim Haley int error; 526699d5e173STim Haley 526799d5e173STim Haley fp = getf(zc->zc_cookie); 52683b2aab18SMatthew Ahrens if (fp == NULL) 5269be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 527099d5e173STim Haley 527199d5e173STim Haley off = fp->f_offset; 527299d5e173STim Haley 52733b2aab18SMatthew Ahrens error = dmu_diff(zc->zc_name, zc->zc_value, fp->f_vnode, &off); 527499d5e173STim Haley 527599d5e173STim Haley if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 527699d5e173STim Haley fp->f_offset = off; 527799d5e173STim Haley releasef(zc->zc_cookie); 527899d5e173STim Haley 527999d5e173STim Haley return (error); 528099d5e173STim Haley } 528199d5e173STim Haley 5282743a77edSAlan Wright /* 5283743a77edSAlan Wright * Remove all ACL files in shares dir 5284743a77edSAlan Wright */ 5285743a77edSAlan Wright static int 5286743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 5287743a77edSAlan Wright { 5288743a77edSAlan Wright zap_cursor_t zc; 5289743a77edSAlan Wright zap_attribute_t zap; 5290743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 5291743a77edSAlan Wright int error; 5292743a77edSAlan Wright 5293743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 5294743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 5295743a77edSAlan Wright zap_cursor_advance(&zc)) { 5296743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 5297743a77edSAlan Wright NULL, 0)) != 0) 5298743a77edSAlan Wright break; 5299743a77edSAlan Wright } 5300743a77edSAlan Wright zap_cursor_fini(&zc); 5301743a77edSAlan Wright return (error); 5302743a77edSAlan Wright } 5303743a77edSAlan Wright 5304743a77edSAlan Wright static int 5305743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 5306743a77edSAlan Wright { 5307743a77edSAlan Wright vnode_t *vp; 5308743a77edSAlan Wright znode_t *dzp; 5309743a77edSAlan Wright vnode_t *resourcevp = NULL; 5310743a77edSAlan Wright znode_t *sharedir; 5311743a77edSAlan Wright zfsvfs_t *zfsvfs; 5312743a77edSAlan Wright nvlist_t *nvlist; 5313743a77edSAlan Wright char *src, *target; 5314743a77edSAlan Wright vattr_t vattr; 5315743a77edSAlan Wright vsecattr_t vsec; 5316743a77edSAlan Wright int error = 0; 5317743a77edSAlan Wright 5318743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 5319743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 5320743a77edSAlan Wright return (error); 5321743a77edSAlan Wright 5322743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 5323743a77edSAlan Wright 5324743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 5325743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 5326743a77edSAlan Wright zc->zc_name) != 0)) { 5327743a77edSAlan Wright VN_RELE(vp); 5328be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5329743a77edSAlan Wright } 5330743a77edSAlan Wright 5331743a77edSAlan Wright dzp = VTOZ(vp); 5332743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 5333743a77edSAlan Wright ZFS_ENTER(zfsvfs); 5334743a77edSAlan Wright 53359e1320c0SMark Shellenbaum /* 53369e1320c0SMark Shellenbaum * Create share dir if its missing. 53379e1320c0SMark Shellenbaum */ 53389e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 53399e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 53409e1320c0SMark Shellenbaum dmu_tx_t *tx; 53419e1320c0SMark Shellenbaum 53429e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 53439e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 53449e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 53459e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 53469e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 53473b2aab18SMatthew Ahrens if (error != 0) { 53489e1320c0SMark Shellenbaum dmu_tx_abort(tx); 53499e1320c0SMark Shellenbaum } else { 53509e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 53519e1320c0SMark Shellenbaum dmu_tx_commit(tx); 53529e1320c0SMark Shellenbaum } 53533b2aab18SMatthew Ahrens if (error != 0) { 53549e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 53559e1320c0SMark Shellenbaum VN_RELE(vp); 53569e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 53579e1320c0SMark Shellenbaum return (error); 53589e1320c0SMark Shellenbaum } 53599e1320c0SMark Shellenbaum } 53609e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 53619e1320c0SMark Shellenbaum 53629e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 5363743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 53649e1320c0SMark Shellenbaum VN_RELE(vp); 5365743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5366743a77edSAlan Wright return (error); 5367743a77edSAlan Wright } 5368743a77edSAlan Wright 5369743a77edSAlan Wright switch (zc->zc_cookie) { 5370743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 5371743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 5372743a77edSAlan Wright vattr.va_type = VREG; 5373743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 5374743a77edSAlan Wright vattr.va_uid = 0; 5375743a77edSAlan Wright vattr.va_gid = 0; 5376743a77edSAlan Wright 5377743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 5378743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 5379743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 5380743a77edSAlan Wright vsec.vsa_aclcnt = 1; 5381743a77edSAlan Wright 5382743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 5383743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 5384743a77edSAlan Wright if (resourcevp) 5385743a77edSAlan Wright VN_RELE(resourcevp); 5386743a77edSAlan Wright break; 5387743a77edSAlan Wright 5388743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 5389743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 5390743a77edSAlan Wright NULL, 0); 5391743a77edSAlan Wright break; 5392743a77edSAlan Wright 5393743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 5394743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 5395478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 5396743a77edSAlan Wright VN_RELE(vp); 53978f5190a5SDan McDonald VN_RELE(ZTOV(sharedir)); 5398743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5399743a77edSAlan Wright return (error); 5400743a77edSAlan Wright } 5401743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 5402743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 5403743a77edSAlan Wright &target)) { 5404743a77edSAlan Wright VN_RELE(vp); 540589459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 5406743a77edSAlan Wright ZFS_EXIT(zfsvfs); 54071195e687SMark J Musante nvlist_free(nvlist); 5408743a77edSAlan Wright return (error); 5409743a77edSAlan Wright } 5410743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 5411743a77edSAlan Wright kcred, NULL, 0); 5412743a77edSAlan Wright nvlist_free(nvlist); 5413743a77edSAlan Wright break; 5414743a77edSAlan Wright 5415743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 5416743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 5417743a77edSAlan Wright break; 5418743a77edSAlan Wright 5419743a77edSAlan Wright default: 5420be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 5421743a77edSAlan Wright break; 5422743a77edSAlan Wright } 5423743a77edSAlan Wright 5424743a77edSAlan Wright VN_RELE(vp); 5425743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 5426743a77edSAlan Wright 5427743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5428743a77edSAlan Wright 5429743a77edSAlan Wright return (error); 5430743a77edSAlan Wright } 5431743a77edSAlan Wright 5432842727c2SChris Kirby /* 54333b2aab18SMatthew Ahrens * innvl: { 54343b2aab18SMatthew Ahrens * "holds" -> { snapname -> holdname (string), ... } 54353b2aab18SMatthew Ahrens * (optional) "cleanup_fd" -> fd (int32) 54363b2aab18SMatthew Ahrens * } 5437842727c2SChris Kirby * 54383b2aab18SMatthew Ahrens * outnvl: { 54393b2aab18SMatthew Ahrens * snapname -> error value (int32) 54403b2aab18SMatthew Ahrens * ... 54413b2aab18SMatthew Ahrens * } 5442842727c2SChris Kirby */ 54433b2aab18SMatthew Ahrens /* ARGSUSED */ 5444842727c2SChris Kirby static int 54453b2aab18SMatthew Ahrens zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) 5446842727c2SChris Kirby { 5447752fd8daSJosef 'Jeff' Sipek nvpair_t *pair; 54483b2aab18SMatthew Ahrens nvlist_t *holds; 54493b2aab18SMatthew Ahrens int cleanup_fd = -1; 5450a7f53a56SChris Kirby int error; 5451a7f53a56SChris Kirby minor_t minor = 0; 5452842727c2SChris Kirby 54533b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(args, "holds", &holds); 54543b2aab18SMatthew Ahrens if (error != 0) 5455be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5456a7f53a56SChris Kirby 5457752fd8daSJosef 'Jeff' Sipek /* make sure the user didn't pass us any invalid (empty) tags */ 5458752fd8daSJosef 'Jeff' Sipek for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 5459752fd8daSJosef 'Jeff' Sipek pair = nvlist_next_nvpair(holds, pair)) { 5460752fd8daSJosef 'Jeff' Sipek char *htag; 5461752fd8daSJosef 'Jeff' Sipek 5462752fd8daSJosef 'Jeff' Sipek error = nvpair_value_string(pair, &htag); 5463752fd8daSJosef 'Jeff' Sipek if (error != 0) 5464752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(error)); 5465752fd8daSJosef 'Jeff' Sipek 5466752fd8daSJosef 'Jeff' Sipek if (strlen(htag) == 0) 5467752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(EINVAL)); 5468752fd8daSJosef 'Jeff' Sipek } 5469752fd8daSJosef 'Jeff' Sipek 54703b2aab18SMatthew Ahrens if (nvlist_lookup_int32(args, "cleanup_fd", &cleanup_fd) == 0) { 54713b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(cleanup_fd, &minor); 54723b2aab18SMatthew Ahrens if (error != 0) 5473a7f53a56SChris Kirby return (error); 5474a7f53a56SChris Kirby } 5475a7f53a56SChris Kirby 54763b2aab18SMatthew Ahrens error = dsl_dataset_user_hold(holds, minor, errlist); 54773b2aab18SMatthew Ahrens if (minor != 0) 54783b2aab18SMatthew Ahrens zfs_onexit_fd_rele(cleanup_fd); 5479a7f53a56SChris Kirby return (error); 5480842727c2SChris Kirby } 5481842727c2SChris Kirby 5482842727c2SChris Kirby /* 54833b2aab18SMatthew Ahrens * innvl is not used. 5484842727c2SChris Kirby * 54853b2aab18SMatthew Ahrens * outnvl: { 54863b2aab18SMatthew Ahrens * holdname -> time added (uint64 seconds since epoch) 54873b2aab18SMatthew Ahrens * ... 54883b2aab18SMatthew Ahrens * } 5489842727c2SChris Kirby */ 54903b2aab18SMatthew Ahrens /* ARGSUSED */ 5491842727c2SChris Kirby static int 54923b2aab18SMatthew Ahrens zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) 5493842727c2SChris Kirby { 54943b2aab18SMatthew Ahrens return (dsl_dataset_get_holds(snapname, outnvl)); 5495842727c2SChris Kirby } 5496842727c2SChris Kirby 5497842727c2SChris Kirby /* 54983b2aab18SMatthew Ahrens * innvl: { 54993b2aab18SMatthew Ahrens * snapname -> { holdname, ... } 55003b2aab18SMatthew Ahrens * ... 55013b2aab18SMatthew Ahrens * } 5502842727c2SChris Kirby * 55033b2aab18SMatthew Ahrens * outnvl: { 55043b2aab18SMatthew Ahrens * snapname -> error value (int32) 55053b2aab18SMatthew Ahrens * ... 55063b2aab18SMatthew Ahrens * } 5507842727c2SChris Kirby */ 55083b2aab18SMatthew Ahrens /* ARGSUSED */ 5509842727c2SChris Kirby static int 55103b2aab18SMatthew Ahrens zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) 5511842727c2SChris Kirby { 55123b2aab18SMatthew Ahrens return (dsl_dataset_user_release(holds, errlist)); 5513842727c2SChris Kirby } 5514842727c2SChris Kirby 551519b94df9SMatthew Ahrens /* 551619b94df9SMatthew Ahrens * inputs: 551719b94df9SMatthew Ahrens * zc_name name of new filesystem or snapshot 551819b94df9SMatthew Ahrens * zc_value full name of old snapshot 551919b94df9SMatthew Ahrens * 552019b94df9SMatthew Ahrens * outputs: 552119b94df9SMatthew Ahrens * zc_cookie space in bytes 552219b94df9SMatthew Ahrens * zc_objset_type compressed space in bytes 552319b94df9SMatthew Ahrens * zc_perm_action uncompressed space in bytes 552419b94df9SMatthew Ahrens */ 552519b94df9SMatthew Ahrens static int 552619b94df9SMatthew Ahrens zfs_ioc_space_written(zfs_cmd_t *zc) 552719b94df9SMatthew Ahrens { 552819b94df9SMatthew Ahrens int error; 55293b2aab18SMatthew Ahrens dsl_pool_t *dp; 553019b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 553119b94df9SMatthew Ahrens 55323b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 553319b94df9SMatthew Ahrens if (error != 0) 553419b94df9SMatthew Ahrens return (error); 55353b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &new); 55363b2aab18SMatthew Ahrens if (error != 0) { 55373b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 55383b2aab18SMatthew Ahrens return (error); 55393b2aab18SMatthew Ahrens } 55403b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_value, FTAG, &old); 554119b94df9SMatthew Ahrens if (error != 0) { 554219b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 55433b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 554419b94df9SMatthew Ahrens return (error); 554519b94df9SMatthew Ahrens } 554619b94df9SMatthew Ahrens 554719b94df9SMatthew Ahrens error = dsl_dataset_space_written(old, new, &zc->zc_cookie, 554819b94df9SMatthew Ahrens &zc->zc_objset_type, &zc->zc_perm_action); 554919b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 555019b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 55513b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 555219b94df9SMatthew Ahrens return (error); 555319b94df9SMatthew Ahrens } 55543b2aab18SMatthew Ahrens 555519b94df9SMatthew Ahrens /* 55564445fffbSMatthew Ahrens * innvl: { 55574445fffbSMatthew Ahrens * "firstsnap" -> snapshot name 55584445fffbSMatthew Ahrens * } 555919b94df9SMatthew Ahrens * 55604445fffbSMatthew Ahrens * outnvl: { 55614445fffbSMatthew Ahrens * "used" -> space in bytes 55624445fffbSMatthew Ahrens * "compressed" -> compressed space in bytes 55634445fffbSMatthew Ahrens * "uncompressed" -> uncompressed space in bytes 55644445fffbSMatthew Ahrens * } 556519b94df9SMatthew Ahrens */ 556619b94df9SMatthew Ahrens static int 55674445fffbSMatthew Ahrens zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) 556819b94df9SMatthew Ahrens { 556919b94df9SMatthew Ahrens int error; 55703b2aab18SMatthew Ahrens dsl_pool_t *dp; 557119b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 55724445fffbSMatthew Ahrens char *firstsnap; 55734445fffbSMatthew Ahrens uint64_t used, comp, uncomp; 557419b94df9SMatthew Ahrens 55754445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) 5576be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 55774445fffbSMatthew Ahrens 55783b2aab18SMatthew Ahrens error = dsl_pool_hold(lastsnap, FTAG, &dp); 557919b94df9SMatthew Ahrens if (error != 0) 558019b94df9SMatthew Ahrens return (error); 55813b2aab18SMatthew Ahrens 55823b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, lastsnap, FTAG, &new); 558324218bebSAndriy Gapon if (error == 0 && !new->ds_is_snapshot) { 558424218bebSAndriy Gapon dsl_dataset_rele(new, FTAG); 558524218bebSAndriy Gapon error = SET_ERROR(EINVAL); 558624218bebSAndriy Gapon } 55873b2aab18SMatthew Ahrens if (error != 0) { 55883b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 55893b2aab18SMatthew Ahrens return (error); 55903b2aab18SMatthew Ahrens } 55913b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, firstsnap, FTAG, &old); 559224218bebSAndriy Gapon if (error == 0 && !old->ds_is_snapshot) { 559324218bebSAndriy Gapon dsl_dataset_rele(old, FTAG); 559424218bebSAndriy Gapon error = SET_ERROR(EINVAL); 559524218bebSAndriy Gapon } 559619b94df9SMatthew Ahrens if (error != 0) { 559719b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 55983b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 559919b94df9SMatthew Ahrens return (error); 560019b94df9SMatthew Ahrens } 560119b94df9SMatthew Ahrens 56024445fffbSMatthew Ahrens error = dsl_dataset_space_wouldfree(old, new, &used, &comp, &uncomp); 560319b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 560419b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 56053b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 56064445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "used", used); 56074445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "compressed", comp); 56084445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "uncompressed", uncomp); 560919b94df9SMatthew Ahrens return (error); 561019b94df9SMatthew Ahrens } 561119b94df9SMatthew Ahrens 5612ecd6cf80Smarks /* 56134445fffbSMatthew Ahrens * innvl: { 56144445fffbSMatthew Ahrens * "fd" -> file descriptor to write stream to (int32) 56154445fffbSMatthew Ahrens * (optional) "fromsnap" -> full snap name to send an incremental from 5616b5152584SMatthew Ahrens * (optional) "largeblockok" -> (value ignored) 5617b5152584SMatthew Ahrens * indicates that blocks > 128KB are permitted 56185d7b4d43SMatthew Ahrens * (optional) "embedok" -> (value ignored) 56195d7b4d43SMatthew Ahrens * presence indicates DRR_WRITE_EMBEDDED records are permitted 56205602294fSDan Kimmel * (optional) "compressok" -> (value ignored) 56215602294fSDan Kimmel * presence indicates compressed DRR_WRITE records are permitted 56229c3fd121SMatthew Ahrens * (optional) "resume_object" and "resume_offset" -> (uint64) 56239c3fd121SMatthew Ahrens * if present, resume send stream from specified object and offset. 56244445fffbSMatthew Ahrens * } 56254445fffbSMatthew Ahrens * 56264445fffbSMatthew Ahrens * outnvl is unused 5627ecd6cf80Smarks */ 56284445fffbSMatthew Ahrens /* ARGSUSED */ 56294445fffbSMatthew Ahrens static int 56304445fffbSMatthew Ahrens zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 56314445fffbSMatthew Ahrens { 56324445fffbSMatthew Ahrens int error; 56334445fffbSMatthew Ahrens offset_t off; 56343b2aab18SMatthew Ahrens char *fromname = NULL; 56354445fffbSMatthew Ahrens int fd; 5636b5152584SMatthew Ahrens boolean_t largeblockok; 56375d7b4d43SMatthew Ahrens boolean_t embedok; 56385602294fSDan Kimmel boolean_t compressok; 56399c3fd121SMatthew Ahrens uint64_t resumeobj = 0; 56409c3fd121SMatthew Ahrens uint64_t resumeoff = 0; 56414445fffbSMatthew Ahrens 56424445fffbSMatthew Ahrens error = nvlist_lookup_int32(innvl, "fd", &fd); 56434445fffbSMatthew Ahrens if (error != 0) 5644be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 56454445fffbSMatthew Ahrens 56463b2aab18SMatthew Ahrens (void) nvlist_lookup_string(innvl, "fromsnap", &fromname); 56474445fffbSMatthew Ahrens 5648b5152584SMatthew Ahrens largeblockok = nvlist_exists(innvl, "largeblockok"); 56495d7b4d43SMatthew Ahrens embedok = nvlist_exists(innvl, "embedok"); 56505602294fSDan Kimmel compressok = nvlist_exists(innvl, "compressok"); 56515d7b4d43SMatthew Ahrens 56529c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); 56539c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff); 56549c3fd121SMatthew Ahrens 56554445fffbSMatthew Ahrens file_t *fp = getf(fd); 56563b2aab18SMatthew Ahrens if (fp == NULL) 5657be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 56584445fffbSMatthew Ahrens 56594445fffbSMatthew Ahrens off = fp->f_offset; 56605602294fSDan Kimmel error = dmu_send(snapname, fromname, embedok, largeblockok, compressok, 56615602294fSDan Kimmel fd, resumeobj, resumeoff, fp->f_vnode, &off); 56624445fffbSMatthew Ahrens 56634445fffbSMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 56644445fffbSMatthew Ahrens fp->f_offset = off; 56654445fffbSMatthew Ahrens releasef(fd); 56664445fffbSMatthew Ahrens return (error); 56674445fffbSMatthew Ahrens } 56684445fffbSMatthew Ahrens 56694445fffbSMatthew Ahrens /* 56704445fffbSMatthew Ahrens * Determine approximately how large a zfs send stream will be -- the number 56714445fffbSMatthew Ahrens * of bytes that will be written to the fd supplied to zfs_ioc_send_new(). 56724445fffbSMatthew Ahrens * 56734445fffbSMatthew Ahrens * innvl: { 5674643da460SMax Grossman * (optional) "from" -> full snap or bookmark name to send an incremental 5675643da460SMax Grossman * from 56765602294fSDan Kimmel * (optional) "largeblockok" -> (value ignored) 56775602294fSDan Kimmel * indicates that blocks > 128KB are permitted 56785602294fSDan Kimmel * (optional) "embedok" -> (value ignored) 56795602294fSDan Kimmel * presence indicates DRR_WRITE_EMBEDDED records are permitted 56805602294fSDan Kimmel * (optional) "compressok" -> (value ignored) 56815602294fSDan Kimmel * presence indicates compressed DRR_WRITE records are permitted 56824445fffbSMatthew Ahrens * } 56834445fffbSMatthew Ahrens * 56844445fffbSMatthew Ahrens * outnvl: { 56854445fffbSMatthew Ahrens * "space" -> bytes of space (uint64) 56864445fffbSMatthew Ahrens * } 56874445fffbSMatthew Ahrens */ 56884445fffbSMatthew Ahrens static int 56894445fffbSMatthew Ahrens zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 56904445fffbSMatthew Ahrens { 56913b2aab18SMatthew Ahrens dsl_pool_t *dp; 56923b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 56934445fffbSMatthew Ahrens int error; 56944445fffbSMatthew Ahrens char *fromname; 56955602294fSDan Kimmel boolean_t compressok; 56964445fffbSMatthew Ahrens uint64_t space; 56974445fffbSMatthew Ahrens 56983b2aab18SMatthew Ahrens error = dsl_pool_hold(snapname, FTAG, &dp); 56993b2aab18SMatthew Ahrens if (error != 0) 57003b2aab18SMatthew Ahrens return (error); 57013b2aab18SMatthew Ahrens 57023b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, snapname, FTAG, &tosnap); 57033b2aab18SMatthew Ahrens if (error != 0) { 57043b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 57054445fffbSMatthew Ahrens return (error); 57063b2aab18SMatthew Ahrens } 57074445fffbSMatthew Ahrens 57085602294fSDan Kimmel compressok = nvlist_exists(innvl, "compressok"); 57095602294fSDan Kimmel 5710643da460SMax Grossman error = nvlist_lookup_string(innvl, "from", &fromname); 57114445fffbSMatthew Ahrens if (error == 0) { 5712643da460SMax Grossman if (strchr(fromname, '@') != NULL) { 5713643da460SMax Grossman /* 5714643da460SMax Grossman * If from is a snapshot, hold it and use the more 5715643da460SMax Grossman * efficient dmu_send_estimate to estimate send space 5716643da460SMax Grossman * size using deadlists. 5717643da460SMax Grossman */ 5718643da460SMax Grossman dsl_dataset_t *fromsnap; 5719643da460SMax Grossman error = dsl_dataset_hold(dp, fromname, FTAG, &fromsnap); 5720643da460SMax Grossman if (error != 0) 5721643da460SMax Grossman goto out; 57225602294fSDan Kimmel error = dmu_send_estimate(tosnap, fromsnap, compressok, 57235602294fSDan Kimmel &space); 5724643da460SMax Grossman dsl_dataset_rele(fromsnap, FTAG); 5725643da460SMax Grossman } else if (strchr(fromname, '#') != NULL) { 5726643da460SMax Grossman /* 5727643da460SMax Grossman * If from is a bookmark, fetch the creation TXG of the 5728643da460SMax Grossman * snapshot it was created from and use that to find 5729643da460SMax Grossman * blocks that were born after it. 5730643da460SMax Grossman */ 5731643da460SMax Grossman zfs_bookmark_phys_t frombm; 5732643da460SMax Grossman 5733643da460SMax Grossman error = dsl_bookmark_lookup(dp, fromname, tosnap, 5734643da460SMax Grossman &frombm); 5735643da460SMax Grossman if (error != 0) 5736643da460SMax Grossman goto out; 5737643da460SMax Grossman error = dmu_send_estimate_from_txg(tosnap, 57385602294fSDan Kimmel frombm.zbm_creation_txg, compressok, &space); 5739643da460SMax Grossman } else { 5740643da460SMax Grossman /* 5741643da460SMax Grossman * from is not properly formatted as a snapshot or 5742643da460SMax Grossman * bookmark 5743643da460SMax Grossman */ 5744643da460SMax Grossman error = SET_ERROR(EINVAL); 5745643da460SMax Grossman goto out; 57464445fffbSMatthew Ahrens } 5747643da460SMax Grossman } else { 5748b852c2f5SToomas Soome /* 5749b852c2f5SToomas Soome * If estimating the size of a full send, use dmu_send_estimate. 5750b852c2f5SToomas Soome */ 57515602294fSDan Kimmel error = dmu_send_estimate(tosnap, NULL, compressok, &space); 57524445fffbSMatthew Ahrens } 57534445fffbSMatthew Ahrens 57544445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "space", space); 57554445fffbSMatthew Ahrens 5756643da460SMax Grossman out: 57573b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 57583b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 57594445fffbSMatthew Ahrens return (error); 57604445fffbSMatthew Ahrens } 57614445fffbSMatthew Ahrens 57624445fffbSMatthew Ahrens static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; 57634445fffbSMatthew Ahrens 57644445fffbSMatthew Ahrens static void 57654445fffbSMatthew Ahrens zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 57664445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 57674445fffbSMatthew Ahrens boolean_t log_history, zfs_ioc_poolcheck_t pool_check) 57684445fffbSMatthew Ahrens { 57694445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 57704445fffbSMatthew Ahrens 57714445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 57724445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 57734445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 57744445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 57754445fffbSMatthew Ahrens 57764445fffbSMatthew Ahrens vec->zvec_legacy_func = func; 57774445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 57784445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 57794445fffbSMatthew Ahrens vec->zvec_allow_log = log_history; 57804445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 57814445fffbSMatthew Ahrens } 57824445fffbSMatthew Ahrens 57834445fffbSMatthew Ahrens /* 57844445fffbSMatthew Ahrens * See the block comment at the beginning of this file for details on 57854445fffbSMatthew Ahrens * each argument to this function. 57864445fffbSMatthew Ahrens */ 57874445fffbSMatthew Ahrens static void 57884445fffbSMatthew Ahrens zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, 57894445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 57904445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, 57914445fffbSMatthew Ahrens boolean_t allow_log) 57924445fffbSMatthew Ahrens { 57934445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 57944445fffbSMatthew Ahrens 57954445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 57964445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 57974445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 57984445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 57994445fffbSMatthew Ahrens 58004445fffbSMatthew Ahrens /* if we are logging, the name must be valid */ 58014445fffbSMatthew Ahrens ASSERT(!allow_log || namecheck != NO_NAME); 58024445fffbSMatthew Ahrens 58034445fffbSMatthew Ahrens vec->zvec_name = name; 58044445fffbSMatthew Ahrens vec->zvec_func = func; 58054445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 58064445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 58074445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 58084445fffbSMatthew Ahrens vec->zvec_smush_outnvlist = smush_outnvlist; 58094445fffbSMatthew Ahrens vec->zvec_allow_log = allow_log; 58104445fffbSMatthew Ahrens } 58114445fffbSMatthew Ahrens 58124445fffbSMatthew Ahrens static void 58134445fffbSMatthew Ahrens zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 58144445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, boolean_t log_history, 58154445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check) 58164445fffbSMatthew Ahrens { 58174445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 58184445fffbSMatthew Ahrens POOL_NAME, log_history, pool_check); 58194445fffbSMatthew Ahrens } 58204445fffbSMatthew Ahrens 58214445fffbSMatthew Ahrens static void 58224445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 58234445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check) 58244445fffbSMatthew Ahrens { 58254445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 58264445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, pool_check); 58274445fffbSMatthew Ahrens } 58284445fffbSMatthew Ahrens 58294445fffbSMatthew Ahrens static void 58304445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 58314445fffbSMatthew Ahrens { 58324445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config, 58334445fffbSMatthew Ahrens POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 58344445fffbSMatthew Ahrens } 58354445fffbSMatthew Ahrens 58364445fffbSMatthew Ahrens static void 58374445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 58384445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy) 58394445fffbSMatthew Ahrens { 58404445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 58414445fffbSMatthew Ahrens NO_NAME, B_FALSE, POOL_CHECK_NONE); 58424445fffbSMatthew Ahrens } 58434445fffbSMatthew Ahrens 58444445fffbSMatthew Ahrens static void 58454445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc, 58464445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) 58474445fffbSMatthew Ahrens { 58484445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 58494445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED); 58504445fffbSMatthew Ahrens } 58514445fffbSMatthew Ahrens 58524445fffbSMatthew Ahrens static void 58534445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 58544445fffbSMatthew Ahrens { 58554445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ioc, func, 58564445fffbSMatthew Ahrens zfs_secpolicy_read); 58574445fffbSMatthew Ahrens } 58584445fffbSMatthew Ahrens 58594445fffbSMatthew Ahrens static void 58604445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 58619a686fbcSPaul Dagnelie zfs_secpolicy_func_t *secpolicy) 58624445fffbSMatthew Ahrens { 58634445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 58644445fffbSMatthew Ahrens DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 58654445fffbSMatthew Ahrens } 58664445fffbSMatthew Ahrens 58674445fffbSMatthew Ahrens static void 58684445fffbSMatthew Ahrens zfs_ioctl_init(void) 58694445fffbSMatthew Ahrens { 58704445fffbSMatthew Ahrens zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, 58714445fffbSMatthew Ahrens zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, 58724445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 58734445fffbSMatthew Ahrens 58744445fffbSMatthew Ahrens zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, 58754445fffbSMatthew Ahrens zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, 58764445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); 58774445fffbSMatthew Ahrens 58784445fffbSMatthew Ahrens zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, 58794445fffbSMatthew Ahrens zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, 58804445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 58814445fffbSMatthew Ahrens 58824445fffbSMatthew Ahrens zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, 58834445fffbSMatthew Ahrens zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, 58844445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 58854445fffbSMatthew Ahrens 58864445fffbSMatthew Ahrens zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, 58874445fffbSMatthew Ahrens zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, 58884445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 58894445fffbSMatthew Ahrens 58904445fffbSMatthew Ahrens zfs_ioctl_register("create", ZFS_IOC_CREATE, 58914445fffbSMatthew Ahrens zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, 58924445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 58934445fffbSMatthew Ahrens 58944445fffbSMatthew Ahrens zfs_ioctl_register("clone", ZFS_IOC_CLONE, 58954445fffbSMatthew Ahrens zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, 58964445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 58974445fffbSMatthew Ahrens 58985cabbc6bSPrashanth Sreenivasa zfs_ioctl_register("remap", ZFS_IOC_REMAP, 58995cabbc6bSPrashanth Sreenivasa zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME, 59005cabbc6bSPrashanth Sreenivasa POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); 59015cabbc6bSPrashanth Sreenivasa 59024445fffbSMatthew Ahrens zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, 59034445fffbSMatthew Ahrens zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, 59044445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 59054445fffbSMatthew Ahrens 59063b2aab18SMatthew Ahrens zfs_ioctl_register("hold", ZFS_IOC_HOLD, 59073b2aab18SMatthew Ahrens zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, 59083b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 59093b2aab18SMatthew Ahrens zfs_ioctl_register("release", ZFS_IOC_RELEASE, 59103b2aab18SMatthew Ahrens zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, 59113b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 59123b2aab18SMatthew Ahrens 59133b2aab18SMatthew Ahrens zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, 59143b2aab18SMatthew Ahrens zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, 59153b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 59163b2aab18SMatthew Ahrens 5917a7027df1SMatthew Ahrens zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, 5918a7027df1SMatthew Ahrens zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, 5919a7027df1SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); 5920a7027df1SMatthew Ahrens 592178f17100SMatthew Ahrens zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, 592278f17100SMatthew Ahrens zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, 592378f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 592478f17100SMatthew Ahrens 592578f17100SMatthew Ahrens zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, 592678f17100SMatthew Ahrens zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, 592778f17100SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 592878f17100SMatthew Ahrens 592978f17100SMatthew Ahrens zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, 593078f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, 593178f17100SMatthew Ahrens POOL_NAME, 593278f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 593378f17100SMatthew Ahrens 5934dfc11533SChris Williamson zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, 5935dfc11533SChris Williamson zfs_ioc_channel_program, zfs_secpolicy_config, 5936dfc11533SChris Williamson POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, 5937dfc11533SChris Williamson B_TRUE); 5938dfc11533SChris Williamson 593986714001SSerapheim Dimitropoulos zfs_ioctl_register("zpool_checkpoint", ZFS_IOC_POOL_CHECKPOINT, 594086714001SSerapheim Dimitropoulos zfs_ioc_pool_checkpoint, zfs_secpolicy_config, POOL_NAME, 594186714001SSerapheim Dimitropoulos POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 594286714001SSerapheim Dimitropoulos 594386714001SSerapheim Dimitropoulos zfs_ioctl_register("zpool_discard_checkpoint", 594486714001SSerapheim Dimitropoulos ZFS_IOC_POOL_DISCARD_CHECKPOINT, zfs_ioc_pool_discard_checkpoint, 594586714001SSerapheim Dimitropoulos zfs_secpolicy_config, POOL_NAME, 594686714001SSerapheim Dimitropoulos POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 594786714001SSerapheim Dimitropoulos 5948*094e47e9SGeorge Wilson zfs_ioctl_register("initialize", ZFS_IOC_POOL_INITIALIZE, 5949*094e47e9SGeorge Wilson zfs_ioc_pool_initialize, zfs_secpolicy_config, POOL_NAME, 5950*094e47e9SGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 5951*094e47e9SGeorge Wilson 59524445fffbSMatthew Ahrens /* IOCTLS that use the legacy function signature */ 59534445fffbSMatthew Ahrens 59544445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, 59554445fffbSMatthew Ahrens zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); 59564445fffbSMatthew Ahrens 59574445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, 59584445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 59594445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN, 59604445fffbSMatthew Ahrens zfs_ioc_pool_scan); 59614445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE, 59624445fffbSMatthew Ahrens zfs_ioc_pool_upgrade); 59634445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD, 59644445fffbSMatthew Ahrens zfs_ioc_vdev_add); 59654445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE, 59664445fffbSMatthew Ahrens zfs_ioc_vdev_remove); 59674445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SET_STATE, 59684445fffbSMatthew Ahrens zfs_ioc_vdev_set_state); 59694445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH, 59704445fffbSMatthew Ahrens zfs_ioc_vdev_attach); 59714445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH, 59724445fffbSMatthew Ahrens zfs_ioc_vdev_detach); 59734445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH, 59744445fffbSMatthew Ahrens zfs_ioc_vdev_setpath); 59754445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU, 59764445fffbSMatthew Ahrens zfs_ioc_vdev_setfru); 59774445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS, 59784445fffbSMatthew Ahrens zfs_ioc_pool_set_props); 59794445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SPLIT, 59804445fffbSMatthew Ahrens zfs_ioc_vdev_split); 59814445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_REGUID, 59824445fffbSMatthew Ahrens zfs_ioc_pool_reguid); 59834445fffbSMatthew Ahrens 59844445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_CONFIGS, 59854445fffbSMatthew Ahrens zfs_ioc_pool_configs, zfs_secpolicy_none); 59864445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_TRYIMPORT, 59874445fffbSMatthew Ahrens zfs_ioc_pool_tryimport, zfs_secpolicy_config); 59884445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_FAULT, 59894445fffbSMatthew Ahrens zfs_ioc_inject_fault, zfs_secpolicy_inject); 59904445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_CLEAR_FAULT, 59914445fffbSMatthew Ahrens zfs_ioc_clear_fault, zfs_secpolicy_inject); 59924445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_LIST_NEXT, 59934445fffbSMatthew Ahrens zfs_ioc_inject_list_next, zfs_secpolicy_inject); 59944445fffbSMatthew Ahrens 59954445fffbSMatthew Ahrens /* 59964445fffbSMatthew Ahrens * pool destroy, and export don't log the history as part of 59974445fffbSMatthew Ahrens * zfsdev_ioctl, but rather zfs_ioc_pool_export 59984445fffbSMatthew Ahrens * does the logging of those commands. 59994445fffbSMatthew Ahrens */ 60004445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_DESTROY, zfs_ioc_pool_destroy, 60014445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 60024445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_EXPORT, zfs_ioc_pool_export, 60034445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 60044445fffbSMatthew Ahrens 60054445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_STATS, zfs_ioc_pool_stats, 60064445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 60074445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_PROPS, zfs_ioc_pool_get_props, 60084445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 60094445fffbSMatthew Ahrens 60104445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_ERROR_LOG, zfs_ioc_error_log, 60114445fffbSMatthew Ahrens zfs_secpolicy_inject, B_FALSE, POOL_CHECK_SUSPENDED); 60124445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_DSOBJ_TO_DSNAME, 60134445fffbSMatthew Ahrens zfs_ioc_dsobj_to_dsname, 60144445fffbSMatthew Ahrens zfs_secpolicy_diff, B_FALSE, POOL_CHECK_SUSPENDED); 60154445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_HISTORY, 60164445fffbSMatthew Ahrens zfs_ioc_pool_get_history, 60174445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); 60184445fffbSMatthew Ahrens 60194445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, 60204445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 60214445fffbSMatthew Ahrens 60224445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, 6023f4c1745bSloli zfs_secpolicy_config, B_TRUE, POOL_CHECK_READONLY); 60244445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, 60254445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); 60264445fffbSMatthew Ahrens 60274445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, 60284445fffbSMatthew Ahrens zfs_ioc_space_written); 60294445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, 60304445fffbSMatthew Ahrens zfs_ioc_objset_recvd_props); 60314445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, 60324445fffbSMatthew Ahrens zfs_ioc_next_obj); 60334445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, 60344445fffbSMatthew Ahrens zfs_ioc_get_fsacl); 60354445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_STATS, 60364445fffbSMatthew Ahrens zfs_ioc_objset_stats); 60374445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_ZPLPROPS, 60384445fffbSMatthew Ahrens zfs_ioc_objset_zplprops); 60394445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_DATASET_LIST_NEXT, 60404445fffbSMatthew Ahrens zfs_ioc_dataset_list_next); 60414445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SNAPSHOT_LIST_NEXT, 60424445fffbSMatthew Ahrens zfs_ioc_snapshot_list_next); 60434445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SEND_PROGRESS, 60444445fffbSMatthew Ahrens zfs_ioc_send_progress); 60454445fffbSMatthew Ahrens 60464445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_DIFF, 60474445fffbSMatthew Ahrens zfs_ioc_diff, zfs_secpolicy_diff); 60484445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_STATS, 60494445fffbSMatthew Ahrens zfs_ioc_obj_to_stats, zfs_secpolicy_diff); 60504445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_PATH, 60514445fffbSMatthew Ahrens zfs_ioc_obj_to_path, zfs_secpolicy_diff); 60524445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_ONE, 60534445fffbSMatthew Ahrens zfs_ioc_userspace_one, zfs_secpolicy_userspace_one); 60544445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_MANY, 60554445fffbSMatthew Ahrens zfs_ioc_userspace_many, zfs_secpolicy_userspace_many); 60564445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND, 60574445fffbSMatthew Ahrens zfs_ioc_send, zfs_secpolicy_send); 60584445fffbSMatthew Ahrens 60594445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, 60604445fffbSMatthew Ahrens zfs_secpolicy_none); 60614445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, 60624445fffbSMatthew Ahrens zfs_secpolicy_destroy); 60634445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename, 60644445fffbSMatthew Ahrens zfs_secpolicy_rename); 60654445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv, 60664445fffbSMatthew Ahrens zfs_secpolicy_recv); 60674445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote, 60684445fffbSMatthew Ahrens zfs_secpolicy_promote); 60694445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP, 60704445fffbSMatthew Ahrens zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop); 60714445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl, 60724445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl); 60734445fffbSMatthew Ahrens 60744445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share, 60754445fffbSMatthew Ahrens zfs_secpolicy_share, POOL_CHECK_NONE); 60764445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl, 60774445fffbSMatthew Ahrens zfs_secpolicy_smb_acl, POOL_CHECK_NONE); 60784445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERSPACE_UPGRADE, 60794445fffbSMatthew Ahrens zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 60804445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 60814445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT, 60824445fffbSMatthew Ahrens zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, 60834445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 60844445fffbSMatthew Ahrens } 6085fa9e4066Sahrens 608654d692b7SGeorge Wilson int 6087f9af39baSGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type, 6088f9af39baSGeorge Wilson zfs_ioc_poolcheck_t check) 608954d692b7SGeorge Wilson { 609054d692b7SGeorge Wilson spa_t *spa; 609154d692b7SGeorge Wilson int error; 609254d692b7SGeorge Wilson 609354d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 609454d692b7SGeorge Wilson 6095f9af39baSGeorge Wilson if (check & POOL_CHECK_NONE) 6096f9af39baSGeorge Wilson return (0); 6097f9af39baSGeorge Wilson 609814843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 609954d692b7SGeorge Wilson if (error == 0) { 6100f9af39baSGeorge Wilson if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa)) 6101be6fd75aSMatthew Ahrens error = SET_ERROR(EAGAIN); 6102f9af39baSGeorge Wilson else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa)) 6103be6fd75aSMatthew Ahrens error = SET_ERROR(EROFS); 610454d692b7SGeorge Wilson spa_close(spa, FTAG); 610554d692b7SGeorge Wilson } 610654d692b7SGeorge Wilson return (error); 610754d692b7SGeorge Wilson } 610854d692b7SGeorge Wilson 6109c99e4bdcSChris Kirby /* 6110c99e4bdcSChris Kirby * Find a free minor number. 6111c99e4bdcSChris Kirby */ 6112c99e4bdcSChris Kirby minor_t 6113c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 6114c99e4bdcSChris Kirby { 6115c99e4bdcSChris Kirby static minor_t last_minor; 6116c99e4bdcSChris Kirby minor_t m; 6117c99e4bdcSChris Kirby 6118c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 6119c99e4bdcSChris Kirby 6120c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 6121c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 6122c99e4bdcSChris Kirby m = 1; 6123c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 6124c99e4bdcSChris Kirby last_minor = m; 6125c99e4bdcSChris Kirby return (m); 6126c99e4bdcSChris Kirby } 6127c99e4bdcSChris Kirby } 6128c99e4bdcSChris Kirby 6129c99e4bdcSChris Kirby return (0); 6130c99e4bdcSChris Kirby } 6131c99e4bdcSChris Kirby 6132c99e4bdcSChris Kirby static int 6133c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 6134c99e4bdcSChris Kirby { 6135c99e4bdcSChris Kirby minor_t minor; 6136c99e4bdcSChris Kirby zfs_soft_state_t *zs; 6137c99e4bdcSChris Kirby 6138c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 6139c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 6140c99e4bdcSChris Kirby 6141c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 6142c99e4bdcSChris Kirby if (minor == 0) 6143be6fd75aSMatthew Ahrens return (SET_ERROR(ENXIO)); 6144c99e4bdcSChris Kirby 6145c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 6146be6fd75aSMatthew Ahrens return (SET_ERROR(EAGAIN)); 6147c99e4bdcSChris Kirby 6148c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 6149c99e4bdcSChris Kirby 6150c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 6151c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 6152c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 6153c99e4bdcSChris Kirby 6154c99e4bdcSChris Kirby return (0); 6155c99e4bdcSChris Kirby } 6156c99e4bdcSChris Kirby 6157c99e4bdcSChris Kirby static void 6158c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 6159c99e4bdcSChris Kirby { 6160c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 6161c99e4bdcSChris Kirby 6162c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 6163c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 6164c99e4bdcSChris Kirby } 6165c99e4bdcSChris Kirby 6166c99e4bdcSChris Kirby void * 6167c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 6168c99e4bdcSChris Kirby { 6169c99e4bdcSChris Kirby zfs_soft_state_t *zp; 6170c99e4bdcSChris Kirby 6171c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 6172c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 6173c99e4bdcSChris Kirby return (NULL); 6174c99e4bdcSChris Kirby 6175c99e4bdcSChris Kirby return (zp->zss_data); 6176c99e4bdcSChris Kirby } 6177c99e4bdcSChris Kirby 6178c99e4bdcSChris Kirby static int 6179c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 6180c99e4bdcSChris Kirby { 6181c99e4bdcSChris Kirby int error = 0; 6182c99e4bdcSChris Kirby 6183c99e4bdcSChris Kirby if (getminor(*devp) != 0) 6184c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 6185c99e4bdcSChris Kirby 6186c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 6187c99e4bdcSChris Kirby if (flag & FEXCL) { 6188c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 6189c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 6190c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 6191c99e4bdcSChris Kirby } 6192c99e4bdcSChris Kirby 6193c99e4bdcSChris Kirby return (error); 6194c99e4bdcSChris Kirby } 6195c99e4bdcSChris Kirby 6196c99e4bdcSChris Kirby static int 6197c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 6198c99e4bdcSChris Kirby { 6199c99e4bdcSChris Kirby zfs_onexit_t *zo; 6200c99e4bdcSChris Kirby minor_t minor = getminor(dev); 6201c99e4bdcSChris Kirby 6202c99e4bdcSChris Kirby if (minor == 0) 6203c99e4bdcSChris Kirby return (0); 6204c99e4bdcSChris Kirby 6205c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 6206c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 6207c99e4bdcSChris Kirby if (zo == NULL) { 6208c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 6209c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 6210c99e4bdcSChris Kirby } 6211c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 6212c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 6213c99e4bdcSChris Kirby 6214c99e4bdcSChris Kirby return (0); 6215c99e4bdcSChris Kirby } 6216c99e4bdcSChris Kirby 6217fa9e4066Sahrens static int 6218fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 6219fa9e4066Sahrens { 6220fa9e4066Sahrens zfs_cmd_t *zc; 62214445fffbSMatthew Ahrens uint_t vecnum; 62224445fffbSMatthew Ahrens int error, rc, len; 6223c99e4bdcSChris Kirby minor_t minor = getminor(dev); 62244445fffbSMatthew Ahrens const zfs_ioc_vec_t *vec; 62254445fffbSMatthew Ahrens char *saved_poolname = NULL; 62264445fffbSMatthew Ahrens nvlist_t *innvl = NULL; 6227fa9e4066Sahrens 6228c99e4bdcSChris Kirby if (minor != 0 && 6229c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 6230fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 6231fa9e4066Sahrens 62324445fffbSMatthew Ahrens vecnum = cmd - ZFS_IOC_FIRST; 623391ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 6234fa9e4066Sahrens 62354445fffbSMatthew Ahrens if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 6236be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 62374445fffbSMatthew Ahrens vec = &zfs_ioc_vec[vecnum]; 6238fa9e4066Sahrens 6239fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 6240fa9e4066Sahrens 6241478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 62424445fffbSMatthew Ahrens if (error != 0) { 6243be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 62444445fffbSMatthew Ahrens goto out; 62454445fffbSMatthew Ahrens } 6246fa9e4066Sahrens 62474445fffbSMatthew Ahrens zc->zc_iflags = flag & FKIOCTL; 62484445fffbSMatthew Ahrens if (zc->zc_nvlist_src_size != 0) { 62494445fffbSMatthew Ahrens error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 62504445fffbSMatthew Ahrens zc->zc_iflags, &innvl); 62514445fffbSMatthew Ahrens if (error != 0) 62524445fffbSMatthew Ahrens goto out; 62534445fffbSMatthew Ahrens } 6254fa9e4066Sahrens 6255fa9e4066Sahrens /* 6256fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 6257fa9e4066Sahrens * the lower layers. 6258fa9e4066Sahrens */ 62594445fffbSMatthew Ahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 62604445fffbSMatthew Ahrens switch (vec->zvec_namecheck) { 62614445fffbSMatthew Ahrens case POOL_NAME: 62624445fffbSMatthew Ahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 6263be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 62644445fffbSMatthew Ahrens else 6265f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 62664445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 62674445fffbSMatthew Ahrens break; 6268fa9e4066Sahrens 62694445fffbSMatthew Ahrens case DATASET_NAME: 62704445fffbSMatthew Ahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 6271be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 62724445fffbSMatthew Ahrens else 6273f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 62744445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 62754445fffbSMatthew Ahrens break; 62765ad82045Snd 62774445fffbSMatthew Ahrens case NO_NAME: 62784445fffbSMatthew Ahrens break; 6279fa9e4066Sahrens } 6280fa9e4066Sahrens 6281fa9e4066Sahrens 628245b17475SAlex Wilson if (error == 0) 62834445fffbSMatthew Ahrens error = vec->zvec_secpolicy(zc, innvl, cr); 62844445fffbSMatthew Ahrens 62854445fffbSMatthew Ahrens if (error != 0) 62864445fffbSMatthew Ahrens goto out; 62874445fffbSMatthew Ahrens 62884445fffbSMatthew Ahrens /* legacy ioctls can modify zc_name */ 628978f17100SMatthew Ahrens len = strcspn(zc->zc_name, "/@#") + 1; 62904445fffbSMatthew Ahrens saved_poolname = kmem_alloc(len, KM_SLEEP); 62914445fffbSMatthew Ahrens (void) strlcpy(saved_poolname, zc->zc_name, len); 62924445fffbSMatthew Ahrens 62934445fffbSMatthew Ahrens if (vec->zvec_func != NULL) { 62944445fffbSMatthew Ahrens nvlist_t *outnvl; 62954445fffbSMatthew Ahrens int puterror = 0; 62964445fffbSMatthew Ahrens spa_t *spa; 62974445fffbSMatthew Ahrens nvlist_t *lognv = NULL; 62984445fffbSMatthew Ahrens 62994445fffbSMatthew Ahrens ASSERT(vec->zvec_legacy_func == NULL); 63004445fffbSMatthew Ahrens 63014445fffbSMatthew Ahrens /* 63024445fffbSMatthew Ahrens * Add the innvl to the lognv before calling the func, 63034445fffbSMatthew Ahrens * in case the func changes the innvl. 63044445fffbSMatthew Ahrens */ 63054445fffbSMatthew Ahrens if (vec->zvec_allow_log) { 63064445fffbSMatthew Ahrens lognv = fnvlist_alloc(); 63074445fffbSMatthew Ahrens fnvlist_add_string(lognv, ZPOOL_HIST_IOCTL, 63084445fffbSMatthew Ahrens vec->zvec_name); 63094445fffbSMatthew Ahrens if (!nvlist_empty(innvl)) { 63104445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_INPUT_NVL, 63114445fffbSMatthew Ahrens innvl); 63124445fffbSMatthew Ahrens } 63134445fffbSMatthew Ahrens } 63144445fffbSMatthew Ahrens 63154445fffbSMatthew Ahrens outnvl = fnvlist_alloc(); 63164445fffbSMatthew Ahrens error = vec->zvec_func(zc->zc_name, innvl, outnvl); 63174445fffbSMatthew Ahrens 6318dfc11533SChris Williamson /* 6319dfc11533SChris Williamson * Some commands can partially execute, modfiy state, and still 6320dfc11533SChris Williamson * return an error. In these cases, attempt to record what 6321dfc11533SChris Williamson * was modified. 6322dfc11533SChris Williamson */ 6323dfc11533SChris Williamson if ((error == 0 || 6324dfc11533SChris Williamson (cmd == ZFS_IOC_CHANNEL_PROGRAM && error != EINVAL)) && 6325dfc11533SChris Williamson vec->zvec_allow_log && 63264445fffbSMatthew Ahrens spa_open(zc->zc_name, &spa, FTAG) == 0) { 63274445fffbSMatthew Ahrens if (!nvlist_empty(outnvl)) { 63284445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, 63294445fffbSMatthew Ahrens outnvl); 63304445fffbSMatthew Ahrens } 6331dfc11533SChris Williamson if (error != 0) { 6332dfc11533SChris Williamson fnvlist_add_int64(lognv, ZPOOL_HIST_ERRNO, 6333dfc11533SChris Williamson error); 6334dfc11533SChris Williamson } 63354445fffbSMatthew Ahrens (void) spa_history_log_nvl(spa, lognv); 63364445fffbSMatthew Ahrens spa_close(spa, FTAG); 63374445fffbSMatthew Ahrens } 63384445fffbSMatthew Ahrens fnvlist_free(lognv); 63394445fffbSMatthew Ahrens 63404445fffbSMatthew Ahrens if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) { 63414445fffbSMatthew Ahrens int smusherror = 0; 63424445fffbSMatthew Ahrens if (vec->zvec_smush_outnvlist) { 63434445fffbSMatthew Ahrens smusherror = nvlist_smush(outnvl, 63444445fffbSMatthew Ahrens zc->zc_nvlist_dst_size); 63454445fffbSMatthew Ahrens } 63464445fffbSMatthew Ahrens if (smusherror == 0) 63474445fffbSMatthew Ahrens puterror = put_nvlist(zc, outnvl); 63484445fffbSMatthew Ahrens } 63494445fffbSMatthew Ahrens 63504445fffbSMatthew Ahrens if (puterror != 0) 63514445fffbSMatthew Ahrens error = puterror; 63524445fffbSMatthew Ahrens 63534445fffbSMatthew Ahrens nvlist_free(outnvl); 63544445fffbSMatthew Ahrens } else { 63554445fffbSMatthew Ahrens error = vec->zvec_legacy_func(zc); 63564445fffbSMatthew Ahrens } 63574445fffbSMatthew Ahrens 63584445fffbSMatthew Ahrens out: 63594445fffbSMatthew Ahrens nvlist_free(innvl); 6360478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 63614445fffbSMatthew Ahrens if (error == 0 && rc != 0) 6362be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 63634445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log) { 63644445fffbSMatthew Ahrens char *s = tsd_get(zfs_allow_log_key); 63654445fffbSMatthew Ahrens if (s != NULL) 63664445fffbSMatthew Ahrens strfree(s); 63674445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, saved_poolname); 63684445fffbSMatthew Ahrens } else { 63694445fffbSMatthew Ahrens if (saved_poolname != NULL) 63704445fffbSMatthew Ahrens strfree(saved_poolname); 6371ecd6cf80Smarks } 6372fa9e4066Sahrens 6373fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 6374fa9e4066Sahrens return (error); 6375fa9e4066Sahrens } 6376fa9e4066Sahrens 6377fa9e4066Sahrens static int 6378fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 6379fa9e4066Sahrens { 6380fa9e4066Sahrens if (cmd != DDI_ATTACH) 6381fa9e4066Sahrens return (DDI_FAILURE); 6382fa9e4066Sahrens 6383fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 6384fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 6385fa9e4066Sahrens return (DDI_FAILURE); 6386fa9e4066Sahrens 6387fa9e4066Sahrens zfs_dip = dip; 6388fa9e4066Sahrens 6389fa9e4066Sahrens ddi_report_dev(dip); 6390fa9e4066Sahrens 6391fa9e4066Sahrens return (DDI_SUCCESS); 6392fa9e4066Sahrens } 6393fa9e4066Sahrens 6394fa9e4066Sahrens static int 6395fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 6396fa9e4066Sahrens { 6397fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 6398fa9e4066Sahrens return (DDI_FAILURE); 6399fa9e4066Sahrens 6400fa9e4066Sahrens if (cmd != DDI_DETACH) 6401fa9e4066Sahrens return (DDI_FAILURE); 6402fa9e4066Sahrens 6403fa9e4066Sahrens zfs_dip = NULL; 6404fa9e4066Sahrens 6405fa9e4066Sahrens ddi_prop_remove_all(dip); 6406fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 6407fa9e4066Sahrens 6408fa9e4066Sahrens return (DDI_SUCCESS); 6409fa9e4066Sahrens } 6410fa9e4066Sahrens 6411fa9e4066Sahrens /*ARGSUSED*/ 6412fa9e4066Sahrens static int 6413fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 6414fa9e4066Sahrens { 6415fa9e4066Sahrens switch (infocmd) { 6416fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 6417fa9e4066Sahrens *result = zfs_dip; 6418fa9e4066Sahrens return (DDI_SUCCESS); 6419fa9e4066Sahrens 6420fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 6421a0965f35Sbonwick *result = (void *)0; 6422fa9e4066Sahrens return (DDI_SUCCESS); 6423fa9e4066Sahrens } 6424fa9e4066Sahrens 6425fa9e4066Sahrens return (DDI_FAILURE); 6426fa9e4066Sahrens } 6427fa9e4066Sahrens 6428fa9e4066Sahrens /* 6429fa9e4066Sahrens * OK, so this is a little weird. 6430fa9e4066Sahrens * 6431fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 6432fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 6433fa9e4066Sahrens * 6434fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 6435fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 6436fa9e4066Sahrens */ 6437fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 6438c99e4bdcSChris Kirby zfsdev_open, /* open */ 6439c99e4bdcSChris Kirby zfsdev_close, /* close */ 6440fa9e4066Sahrens zvol_strategy, /* strategy */ 6441fa9e4066Sahrens nodev, /* print */ 6442e7cbe64fSgw zvol_dump, /* dump */ 6443fa9e4066Sahrens zvol_read, /* read */ 6444fa9e4066Sahrens zvol_write, /* write */ 6445fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 6446fa9e4066Sahrens nodev, /* devmap */ 6447fa9e4066Sahrens nodev, /* mmap */ 6448fa9e4066Sahrens nodev, /* segmap */ 6449fa9e4066Sahrens nochpoll, /* poll */ 6450fa9e4066Sahrens ddi_prop_op, /* prop_op */ 6451fa9e4066Sahrens NULL, /* streamtab */ 6452fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 6453fa9e4066Sahrens CB_REV, /* version */ 6454feb08c6bSbillm nodev, /* async read */ 6455feb08c6bSbillm nodev, /* async write */ 6456fa9e4066Sahrens }; 6457fa9e4066Sahrens 6458fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 6459fa9e4066Sahrens DEVO_REV, /* version */ 6460fa9e4066Sahrens 0, /* refcnt */ 6461fa9e4066Sahrens zfs_info, /* info */ 6462fa9e4066Sahrens nulldev, /* identify */ 6463fa9e4066Sahrens nulldev, /* probe */ 6464fa9e4066Sahrens zfs_attach, /* attach */ 6465fa9e4066Sahrens zfs_detach, /* detach */ 6466fa9e4066Sahrens nodev, /* reset */ 6467fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 646819397407SSherry Moore NULL, /* no bus operations */ 646919397407SSherry Moore NULL, /* power */ 647019397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 6471fa9e4066Sahrens }; 6472fa9e4066Sahrens 6473fa9e4066Sahrens static struct modldrv zfs_modldrv = { 647419397407SSherry Moore &mod_driverops, 647519397407SSherry Moore "ZFS storage pool", 647619397407SSherry Moore &zfs_dev_ops 6477fa9e4066Sahrens }; 6478fa9e4066Sahrens 6479fa9e4066Sahrens static struct modlinkage modlinkage = { 6480fa9e4066Sahrens MODREV_1, 6481fa9e4066Sahrens (void *)&zfs_modlfs, 6482fa9e4066Sahrens (void *)&zfs_modldrv, 6483fa9e4066Sahrens NULL 6484fa9e4066Sahrens }; 6485fa9e4066Sahrens 64864445fffbSMatthew Ahrens static void 64874445fffbSMatthew Ahrens zfs_allow_log_destroy(void *arg) 64884445fffbSMatthew Ahrens { 64894445fffbSMatthew Ahrens char *poolname = arg; 64904445fffbSMatthew Ahrens strfree(poolname); 64914445fffbSMatthew Ahrens } 6492ec533521Sfr 6493fa9e4066Sahrens int 6494fa9e4066Sahrens _init(void) 6495fa9e4066Sahrens { 6496fa9e4066Sahrens int error; 6497fa9e4066Sahrens 6498a0965f35Sbonwick spa_init(FREAD | FWRITE); 6499a0965f35Sbonwick zfs_init(); 6500a0965f35Sbonwick zvol_init(); 65014445fffbSMatthew Ahrens zfs_ioctl_init(); 6502a0965f35Sbonwick 6503a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 6504a0965f35Sbonwick zvol_fini(); 6505a0965f35Sbonwick zfs_fini(); 6506a0965f35Sbonwick spa_fini(); 6507fa9e4066Sahrens return (error); 6508a0965f35Sbonwick } 6509fa9e4066Sahrens 6510ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 65114445fffbSMatthew Ahrens tsd_create(&rrw_tsd_key, rrw_tsd_destroy); 65124445fffbSMatthew Ahrens tsd_create(&zfs_allow_log_key, zfs_allow_log_destroy); 6513ec533521Sfr 6514fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 6515fa9e4066Sahrens ASSERT(error == 0); 6516ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 6517fa9e4066Sahrens 6518fa9e4066Sahrens return (0); 6519fa9e4066Sahrens } 6520fa9e4066Sahrens 6521fa9e4066Sahrens int 6522fa9e4066Sahrens _fini(void) 6523fa9e4066Sahrens { 6524fa9e4066Sahrens int error; 6525fa9e4066Sahrens 6526ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 6527be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 6528fa9e4066Sahrens 6529fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 6530fa9e4066Sahrens return (error); 6531fa9e4066Sahrens 6532fa9e4066Sahrens zvol_fini(); 6533fa9e4066Sahrens zfs_fini(); 6534fa9e4066Sahrens spa_fini(); 6535da6c28aaSamw if (zfs_nfsshare_inited) 6536ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 6537da6c28aaSamw if (zfs_smbshare_inited) 6538da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 6539da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 6540ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 6541fa9e4066Sahrens 6542ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 6543fa9e4066Sahrens ldi_ident_release(zfs_li); 6544fa9e4066Sahrens zfs_li = NULL; 6545ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 6546fa9e4066Sahrens 6547fa9e4066Sahrens return (error); 6548fa9e4066Sahrens } 6549fa9e4066Sahrens 6550fa9e4066Sahrens int 6551fa9e4066Sahrens _info(struct modinfo *modinfop) 6552fa9e4066Sahrens { 6553fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 6554fa9e4066Sahrens } 6555