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. 27*6ccda740Sloli * Copyright 2016 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> 34*6ccda740Sloli * Copyright (c) 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved. 35a4b8c9aaSAndrew Stormont * Copyright 2017 RackTop Systems. 36eb633035STom Caputi * Copyright (c) 2017, Datto, Inc. All rights reserved. 374445fffbSMatthew Ahrens */ 384445fffbSMatthew Ahrens 394445fffbSMatthew Ahrens /* 404445fffbSMatthew Ahrens * ZFS ioctls. 414445fffbSMatthew Ahrens * 424445fffbSMatthew Ahrens * This file handles the ioctls to /dev/zfs, used for configuring ZFS storage 434445fffbSMatthew Ahrens * pools and filesystems, e.g. with /sbin/zfs and /sbin/zpool. 444445fffbSMatthew Ahrens * 454445fffbSMatthew Ahrens * There are two ways that we handle ioctls: the legacy way where almost 464445fffbSMatthew Ahrens * all of the logic is in the ioctl callback, and the new way where most 474445fffbSMatthew Ahrens * of the marshalling is handled in the common entry point, zfsdev_ioctl(). 484445fffbSMatthew Ahrens * 494445fffbSMatthew Ahrens * Non-legacy ioctls should be registered by calling 504445fffbSMatthew Ahrens * zfs_ioctl_register() from zfs_ioctl_init(). The ioctl is invoked 514445fffbSMatthew Ahrens * from userland by lzc_ioctl(). 524445fffbSMatthew Ahrens * 534445fffbSMatthew Ahrens * The registration arguments are as follows: 544445fffbSMatthew Ahrens * 554445fffbSMatthew Ahrens * const char *name 564445fffbSMatthew Ahrens * The name of the ioctl. This is used for history logging. If the 574445fffbSMatthew Ahrens * ioctl returns successfully (the callback returns 0), and allow_log 584445fffbSMatthew Ahrens * is true, then a history log entry will be recorded with the input & 594445fffbSMatthew Ahrens * output nvlists. The log entry can be printed with "zpool history -i". 604445fffbSMatthew Ahrens * 614445fffbSMatthew Ahrens * zfs_ioc_t ioc 624445fffbSMatthew Ahrens * The ioctl request number, which userland will pass to ioctl(2). 634445fffbSMatthew Ahrens * The ioctl numbers can change from release to release, because 644445fffbSMatthew Ahrens * the caller (libzfs) must be matched to the kernel. 654445fffbSMatthew Ahrens * 664445fffbSMatthew Ahrens * zfs_secpolicy_func_t *secpolicy 674445fffbSMatthew Ahrens * This function will be called before the zfs_ioc_func_t, to 684445fffbSMatthew Ahrens * determine if this operation is permitted. It should return EPERM 694445fffbSMatthew Ahrens * on failure, and 0 on success. Checks include determining if the 704445fffbSMatthew Ahrens * dataset is visible in this zone, and if the user has either all 714445fffbSMatthew Ahrens * zfs privileges in the zone (SYS_MOUNT), or has been granted permission 724445fffbSMatthew Ahrens * to do this operation on this dataset with "zfs allow". 734445fffbSMatthew Ahrens * 744445fffbSMatthew Ahrens * zfs_ioc_namecheck_t namecheck 754445fffbSMatthew Ahrens * This specifies what to expect in the zfs_cmd_t:zc_name -- a pool 764445fffbSMatthew Ahrens * name, a dataset name, or nothing. If the name is not well-formed, 774445fffbSMatthew Ahrens * the ioctl will fail and the callback will not be called. 784445fffbSMatthew Ahrens * Therefore, the callback can assume that the name is well-formed 794445fffbSMatthew Ahrens * (e.g. is null-terminated, doesn't have more than one '@' character, 804445fffbSMatthew Ahrens * doesn't have invalid characters). 814445fffbSMatthew Ahrens * 824445fffbSMatthew Ahrens * zfs_ioc_poolcheck_t pool_check 834445fffbSMatthew Ahrens * This specifies requirements on the pool state. If the pool does 844445fffbSMatthew Ahrens * not meet them (is suspended or is readonly), the ioctl will fail 854445fffbSMatthew Ahrens * and the callback will not be called. If any checks are specified 864445fffbSMatthew Ahrens * (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. 874445fffbSMatthew Ahrens * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | 884445fffbSMatthew Ahrens * POOL_CHECK_READONLY). 894445fffbSMatthew Ahrens * 904445fffbSMatthew Ahrens * boolean_t smush_outnvlist 914445fffbSMatthew Ahrens * If smush_outnvlist is true, then the output is presumed to be a 924445fffbSMatthew Ahrens * list of errors, and it will be "smushed" down to fit into the 934445fffbSMatthew Ahrens * caller's buffer, by removing some entries and replacing them with a 944445fffbSMatthew Ahrens * single "N_MORE_ERRORS" entry indicating how many were removed. See 954445fffbSMatthew Ahrens * nvlist_smush() for details. If smush_outnvlist is false, and the 964445fffbSMatthew Ahrens * outnvlist does not fit into the userland-provided buffer, then the 974445fffbSMatthew Ahrens * ioctl will fail with ENOMEM. 984445fffbSMatthew Ahrens * 994445fffbSMatthew Ahrens * zfs_ioc_func_t *func 1004445fffbSMatthew Ahrens * The callback function that will perform the operation. 1014445fffbSMatthew Ahrens * 1024445fffbSMatthew Ahrens * The callback should return 0 on success, or an error number on 1034445fffbSMatthew Ahrens * failure. If the function fails, the userland ioctl will return -1, 1044445fffbSMatthew Ahrens * and errno will be set to the callback's return value. The callback 1054445fffbSMatthew Ahrens * will be called with the following arguments: 1064445fffbSMatthew Ahrens * 1074445fffbSMatthew Ahrens * const char *name 1084445fffbSMatthew Ahrens * The name of the pool or dataset to operate on, from 1094445fffbSMatthew Ahrens * zfs_cmd_t:zc_name. The 'namecheck' argument specifies the 1104445fffbSMatthew Ahrens * expected type (pool, dataset, or none). 1114445fffbSMatthew Ahrens * 1124445fffbSMatthew Ahrens * nvlist_t *innvl 1134445fffbSMatthew Ahrens * The input nvlist, deserialized from zfs_cmd_t:zc_nvlist_src. Or 1144445fffbSMatthew Ahrens * NULL if no input nvlist was provided. Changes to this nvlist are 1154445fffbSMatthew Ahrens * ignored. If the input nvlist could not be deserialized, the 1164445fffbSMatthew Ahrens * ioctl will fail and the callback will not be called. 1174445fffbSMatthew Ahrens * 1184445fffbSMatthew Ahrens * nvlist_t *outnvl 1194445fffbSMatthew Ahrens * The output nvlist, initially empty. The callback can fill it in, 1204445fffbSMatthew Ahrens * and it will be returned to userland by serializing it into 1214445fffbSMatthew Ahrens * zfs_cmd_t:zc_nvlist_dst. If it is non-empty, and serialization 1224445fffbSMatthew Ahrens * fails (e.g. because the caller didn't supply a large enough 1234445fffbSMatthew Ahrens * buffer), then the overall ioctl will fail. See the 1244445fffbSMatthew Ahrens * 'smush_nvlist' argument above for additional behaviors. 1254445fffbSMatthew Ahrens * 1264445fffbSMatthew Ahrens * There are two typical uses of the output nvlist: 1274445fffbSMatthew Ahrens * - To return state, e.g. property values. In this case, 1284445fffbSMatthew Ahrens * smush_outnvlist should be false. If the buffer was not large 1294445fffbSMatthew Ahrens * enough, the caller will reallocate a larger buffer and try 1304445fffbSMatthew Ahrens * the ioctl again. 1314445fffbSMatthew Ahrens * 1324445fffbSMatthew Ahrens * - To return multiple errors from an ioctl which makes on-disk 1334445fffbSMatthew Ahrens * changes. In this case, smush_outnvlist should be true. 1344445fffbSMatthew Ahrens * Ioctls which make on-disk modifications should generally not 1354445fffbSMatthew Ahrens * use the outnvl if they succeed, because the caller can not 1364445fffbSMatthew Ahrens * distinguish between the operation failing, and 1374445fffbSMatthew Ahrens * deserialization failing. 138e9103aaeSGarrett D'Amore */ 139fa9e4066Sahrens 140fa9e4066Sahrens #include <sys/types.h> 141fa9e4066Sahrens #include <sys/param.h> 142fa9e4066Sahrens #include <sys/errno.h> 143fa9e4066Sahrens #include <sys/uio.h> 144fa9e4066Sahrens #include <sys/buf.h> 145fa9e4066Sahrens #include <sys/modctl.h> 146fa9e4066Sahrens #include <sys/open.h> 147fa9e4066Sahrens #include <sys/file.h> 148fa9e4066Sahrens #include <sys/kmem.h> 149fa9e4066Sahrens #include <sys/conf.h> 150fa9e4066Sahrens #include <sys/cmn_err.h> 151fa9e4066Sahrens #include <sys/stat.h> 152fa9e4066Sahrens #include <sys/zfs_ioctl.h> 1534201a95eSRic Aleshire #include <sys/zfs_vfsops.h> 154da6c28aaSamw #include <sys/zfs_znode.h> 155fa9e4066Sahrens #include <sys/zap.h> 156fa9e4066Sahrens #include <sys/spa.h> 157b1b8ab34Slling #include <sys/spa_impl.h> 158fa9e4066Sahrens #include <sys/vdev.h> 1594201a95eSRic Aleshire #include <sys/priv_impl.h> 160fa9e4066Sahrens #include <sys/dmu.h> 161fa9e4066Sahrens #include <sys/dsl_dir.h> 162fa9e4066Sahrens #include <sys/dsl_dataset.h> 163fa9e4066Sahrens #include <sys/dsl_prop.h> 164ecd6cf80Smarks #include <sys/dsl_deleg.h> 165ecd6cf80Smarks #include <sys/dmu_objset.h> 1664e3c9f44SBill Pijewski #include <sys/dmu_impl.h> 1673b2aab18SMatthew Ahrens #include <sys/dmu_tx.h> 168fa9e4066Sahrens #include <sys/ddi.h> 169fa9e4066Sahrens #include <sys/sunddi.h> 170fa9e4066Sahrens #include <sys/sunldi.h> 171fa9e4066Sahrens #include <sys/policy.h> 172fa9e4066Sahrens #include <sys/zone.h> 173fa9e4066Sahrens #include <sys/nvpair.h> 174fa9e4066Sahrens #include <sys/pathname.h> 175fa9e4066Sahrens #include <sys/mount.h> 176fa9e4066Sahrens #include <sys/sdt.h> 177fa9e4066Sahrens #include <sys/fs/zfs.h> 178fa9e4066Sahrens #include <sys/zfs_ctldir.h> 179da6c28aaSamw #include <sys/zfs_dir.h> 180c99e4bdcSChris Kirby #include <sys/zfs_onexit.h> 181a2eea2e1Sahrens #include <sys/zvol.h> 1823f9d6ad7SLin Ling #include <sys/dsl_scan.h> 183ecd6cf80Smarks #include <sharefs/share.h> 184f18faf3fSek #include <sys/dmu_objset.h> 1850fa1b3ccSPaul Dagnelie #include <sys/dmu_recv.h> 1863b2aab18SMatthew Ahrens #include <sys/dmu_send.h> 1873b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h> 18878f17100SMatthew Ahrens #include <sys/dsl_bookmark.h> 1893b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h> 190a6f561b4SSašo Kiselkov #include <sys/zfeature.h> 191dfc11533SChris Williamson #include <sys/zcp.h> 19245818ee1SMatthew Ahrens #include <sys/zio_checksum.h> 1935cabbc6bSPrashanth Sreenivasa #include <sys/vdev_removal.h> 194094e47e9SGeorge Wilson #include <sys/vdev_impl.h> 195094e47e9SGeorge Wilson #include <sys/vdev_initialize.h> 196eb633035STom Caputi #include <sys/dsl_crypt.h> 197fa9e4066Sahrens 198fa9e4066Sahrens #include "zfs_namecheck.h" 199e9dbad6fSeschrock #include "zfs_prop.h" 200ecd6cf80Smarks #include "zfs_deleg.h" 2010a586ceaSMark Shellenbaum #include "zfs_comutil.h" 202fa9e4066Sahrens 203dfc11533SChris Williamson #include "lua.h" 204dfc11533SChris Williamson #include "lauxlib.h" 205dfc11533SChris Williamson 206fa9e4066Sahrens extern struct modlfs zfs_modlfs; 207fa9e4066Sahrens 208fa9e4066Sahrens extern void zfs_init(void); 209fa9e4066Sahrens extern void zfs_fini(void); 210fa9e4066Sahrens 211fa9e4066Sahrens ldi_ident_t zfs_li = NULL; 212fa9e4066Sahrens dev_info_t *zfs_dip; 213fa9e4066Sahrens 2144445fffbSMatthew Ahrens uint_t zfs_fsyncer_key; 2154445fffbSMatthew Ahrens extern uint_t rrw_tsd_key; 2164445fffbSMatthew Ahrens static uint_t zfs_allow_log_key; 2174445fffbSMatthew Ahrens 2184445fffbSMatthew Ahrens typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); 2194445fffbSMatthew Ahrens typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); 2204445fffbSMatthew Ahrens typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); 221fa9e4066Sahrens 22254d692b7SGeorge Wilson typedef enum { 22354d692b7SGeorge Wilson NO_NAME, 22454d692b7SGeorge Wilson POOL_NAME, 22554d692b7SGeorge Wilson DATASET_NAME 22654d692b7SGeorge Wilson } zfs_ioc_namecheck_t; 22754d692b7SGeorge Wilson 228f9af39baSGeorge Wilson typedef enum { 229f9af39baSGeorge Wilson POOL_CHECK_NONE = 1 << 0, 230f9af39baSGeorge Wilson POOL_CHECK_SUSPENDED = 1 << 1, 2314445fffbSMatthew Ahrens POOL_CHECK_READONLY = 1 << 2, 232f9af39baSGeorge Wilson } zfs_ioc_poolcheck_t; 233f9af39baSGeorge Wilson 234fa9e4066Sahrens typedef struct zfs_ioc_vec { 2354445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *zvec_legacy_func; 236fa9e4066Sahrens zfs_ioc_func_t *zvec_func; 237fa9e4066Sahrens zfs_secpolicy_func_t *zvec_secpolicy; 23854d692b7SGeorge Wilson zfs_ioc_namecheck_t zvec_namecheck; 2394445fffbSMatthew Ahrens boolean_t zvec_allow_log; 240f9af39baSGeorge Wilson zfs_ioc_poolcheck_t zvec_pool_check; 2414445fffbSMatthew Ahrens boolean_t zvec_smush_outnvlist; 2424445fffbSMatthew Ahrens const char *zvec_name; 243fa9e4066Sahrens } zfs_ioc_vec_t; 244fa9e4066Sahrens 24514843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */ 24614843421SMatthew Ahrens static const char *userquota_perms[] = { 24714843421SMatthew Ahrens ZFS_DELEG_PERM_USERUSED, 24814843421SMatthew Ahrens ZFS_DELEG_PERM_USERQUOTA, 24914843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPUSED, 25014843421SMatthew Ahrens ZFS_DELEG_PERM_GROUPQUOTA, 25114843421SMatthew Ahrens }; 25214843421SMatthew Ahrens 25314843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); 25492241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property, 25592241e0bSTom Erickson cred_t *cr); 25692241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props, 25792241e0bSTom Erickson nvlist_t **errors); 2580a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *, 2590a48a24eStimh boolean_t *); 2604445fffbSMatthew Ahrens int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); 2614445fffbSMatthew Ahrens static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp); 2620a48a24eStimh 2632acef22dSMatthew Ahrens static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature); 264a6f561b4SSašo Kiselkov 265fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */ 266fa9e4066Sahrens void 267fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...) 268fa9e4066Sahrens { 269fa9e4066Sahrens const char *newfile; 2703f9d6ad7SLin Ling char buf[512]; 271fa9e4066Sahrens va_list adx; 272fa9e4066Sahrens 273fa9e4066Sahrens /* 274fa9e4066Sahrens * Get rid of annoying "../common/" prefix to filename. 275fa9e4066Sahrens */ 276fa9e4066Sahrens newfile = strrchr(file, '/'); 277fa9e4066Sahrens if (newfile != NULL) { 278fa9e4066Sahrens newfile = newfile + 1; /* Get rid of leading / */ 279fa9e4066Sahrens } else { 280fa9e4066Sahrens newfile = file; 281fa9e4066Sahrens } 282fa9e4066Sahrens 283fa9e4066Sahrens va_start(adx, fmt); 284fa9e4066Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx); 285fa9e4066Sahrens va_end(adx); 286fa9e4066Sahrens 287fa9e4066Sahrens /* 288fa9e4066Sahrens * To get this data, use the zfs-dprintf probe as so: 289fa9e4066Sahrens * dtrace -q -n 'zfs-dprintf \ 290fa9e4066Sahrens * /stringof(arg0) == "dbuf.c"/ \ 291fa9e4066Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}' 292fa9e4066Sahrens * arg0 = file name 293fa9e4066Sahrens * arg1 = function name 294fa9e4066Sahrens * arg2 = line number 295fa9e4066Sahrens * arg3 = message 296fa9e4066Sahrens */ 297fa9e4066Sahrens DTRACE_PROBE4(zfs__dprintf, 298fa9e4066Sahrens char *, newfile, char *, func, int, line, char *, buf); 299fa9e4066Sahrens } 300fa9e4066Sahrens 301ecd6cf80Smarks static void 302228975ccSek history_str_free(char *buf) 303228975ccSek { 304228975ccSek kmem_free(buf, HIS_MAX_RECORD_LEN); 305228975ccSek } 306228975ccSek 307228975ccSek static char * 308228975ccSek history_str_get(zfs_cmd_t *zc) 309ecd6cf80Smarks { 31040feaa91Sahrens char *buf; 311ecd6cf80Smarks 312dd328bf6SToomas Soome if (zc->zc_history == 0) 313228975ccSek return (NULL); 314e7437265Sahrens 315ecd6cf80Smarks buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP); 316ecd6cf80Smarks if (copyinstr((void *)(uintptr_t)zc->zc_history, 317ecd6cf80Smarks buf, HIS_MAX_RECORD_LEN, NULL) != 0) { 318228975ccSek history_str_free(buf); 319228975ccSek return (NULL); 320ecd6cf80Smarks } 321ecd6cf80Smarks 322ecd6cf80Smarks buf[HIS_MAX_RECORD_LEN -1] = '\0'; 323ecd6cf80Smarks 324228975ccSek return (buf); 325228975ccSek } 326ecd6cf80Smarks 32715e6edf1Sgw /* 32815e6edf1Sgw * Check to see if the named dataset is currently defined as bootable 32915e6edf1Sgw */ 33015e6edf1Sgw static boolean_t 33115e6edf1Sgw zfs_is_bootfs(const char *name) 33215e6edf1Sgw { 333503ad85cSMatthew Ahrens objset_t *os; 33415e6edf1Sgw 335503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 336503ad85cSMatthew Ahrens boolean_t ret; 337b24ab676SJeff Bonwick ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os))); 338503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 339503ad85cSMatthew Ahrens return (ret); 34015e6edf1Sgw } 341503ad85cSMatthew Ahrens return (B_FALSE); 34215e6edf1Sgw } 34315e6edf1Sgw 344c2a93d44Stimh /* 345f7170741SWill Andrews * Return non-zero if the spa version is less than requested version. 346c2a93d44Stimh */ 347da6c28aaSamw static int 3480a48a24eStimh zfs_earlier_version(const char *name, int version) 349da6c28aaSamw { 350da6c28aaSamw spa_t *spa; 351da6c28aaSamw 352da6c28aaSamw if (spa_open(name, &spa, FTAG) == 0) { 353da6c28aaSamw if (spa_version(spa) < version) { 354da6c28aaSamw spa_close(spa, FTAG); 355da6c28aaSamw return (1); 356da6c28aaSamw } 357da6c28aaSamw spa_close(spa, FTAG); 358da6c28aaSamw } 359da6c28aaSamw return (0); 360da6c28aaSamw } 361da6c28aaSamw 3629e6eda55Smarks /* 363745cd3c5Smaybee * Return TRUE if the ZPL version is less than requested version. 3649e6eda55Smarks */ 365745cd3c5Smaybee static boolean_t 366745cd3c5Smaybee zpl_earlier_version(const char *name, int version) 3679e6eda55Smarks { 3689e6eda55Smarks objset_t *os; 369745cd3c5Smaybee boolean_t rc = B_TRUE; 3709e6eda55Smarks 371503ad85cSMatthew Ahrens if (dmu_objset_hold(name, FTAG, &os) == 0) { 372745cd3c5Smaybee uint64_t zplversion; 3739e6eda55Smarks 374503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 375503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 376503ad85cSMatthew Ahrens return (B_TRUE); 377503ad85cSMatthew Ahrens } 378503ad85cSMatthew Ahrens /* XXX reading from non-owned objset */ 379745cd3c5Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0) 380745cd3c5Smaybee rc = zplversion < version; 381503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 3829e6eda55Smarks } 3839e6eda55Smarks return (rc); 3849e6eda55Smarks } 3859e6eda55Smarks 386228975ccSek static void 387228975ccSek zfs_log_history(zfs_cmd_t *zc) 388228975ccSek { 389228975ccSek spa_t *spa; 390228975ccSek char *buf; 391ecd6cf80Smarks 392228975ccSek if ((buf = history_str_get(zc)) == NULL) 393228975ccSek return; 394228975ccSek 395228975ccSek if (spa_open(zc->zc_name, &spa, FTAG) == 0) { 396228975ccSek if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY) 3974445fffbSMatthew Ahrens (void) spa_history_log(spa, buf); 398228975ccSek spa_close(spa, FTAG); 399228975ccSek } 400228975ccSek history_str_free(buf); 401ecd6cf80Smarks } 402ecd6cf80Smarks 403fa9e4066Sahrens /* 404fa9e4066Sahrens * Policy for top-level read operations (list pools). Requires no privileges, 405fa9e4066Sahrens * and can be used in the local zone, as there is no associated dataset. 406fa9e4066Sahrens */ 407fa9e4066Sahrens /* ARGSUSED */ 408fa9e4066Sahrens static int 4094445fffbSMatthew Ahrens zfs_secpolicy_none(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 410fa9e4066Sahrens { 411fa9e4066Sahrens return (0); 412fa9e4066Sahrens } 413fa9e4066Sahrens 414fa9e4066Sahrens /* 415fa9e4066Sahrens * Policy for dataset read operations (list children, get statistics). Requires 416fa9e4066Sahrens * no privileges, but must be visible in the local zone. 417fa9e4066Sahrens */ 418fa9e4066Sahrens /* ARGSUSED */ 419fa9e4066Sahrens static int 4204445fffbSMatthew Ahrens zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 421fa9e4066Sahrens { 422fa9e4066Sahrens if (INGLOBALZONE(curproc) || 423ecd6cf80Smarks zone_dataset_visible(zc->zc_name, NULL)) 424fa9e4066Sahrens return (0); 425fa9e4066Sahrens 426be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 427fa9e4066Sahrens } 428fa9e4066Sahrens 429fa9e4066Sahrens static int 430a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) 431fa9e4066Sahrens { 432fa9e4066Sahrens int writable = 1; 433fa9e4066Sahrens 434fa9e4066Sahrens /* 435fa9e4066Sahrens * The dataset must be visible by this zone -- check this first 436fa9e4066Sahrens * so they don't see EPERM on something they shouldn't know about. 437fa9e4066Sahrens */ 438fa9e4066Sahrens if (!INGLOBALZONE(curproc) && 439fa9e4066Sahrens !zone_dataset_visible(dataset, &writable)) 440be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 441fa9e4066Sahrens 442fa9e4066Sahrens if (INGLOBALZONE(curproc)) { 443fa9e4066Sahrens /* 444fa9e4066Sahrens * If the fs is zoned, only root can access it from the 445fa9e4066Sahrens * global zone. 446fa9e4066Sahrens */ 447fa9e4066Sahrens if (secpolicy_zfs(cr) && zoned) 448be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 449fa9e4066Sahrens } else { 450fa9e4066Sahrens /* 451fa9e4066Sahrens * If we are in a local zone, the 'zoned' property must be set. 452fa9e4066Sahrens */ 453fa9e4066Sahrens if (!zoned) 454be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 455fa9e4066Sahrens 456fa9e4066Sahrens /* must be writable by this zone */ 457fa9e4066Sahrens if (!writable) 458be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 459fa9e4066Sahrens } 460fa9e4066Sahrens return (0); 461fa9e4066Sahrens } 462fa9e4066Sahrens 463a7f53a56SChris Kirby static int 464a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr) 465a7f53a56SChris Kirby { 466a7f53a56SChris Kirby uint64_t zoned; 467a7f53a56SChris Kirby 468a7f53a56SChris Kirby if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL)) 469be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 470a7f53a56SChris Kirby 471a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 472a7f53a56SChris Kirby } 473a7f53a56SChris Kirby 474a7f53a56SChris Kirby static int 475a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr) 476a7f53a56SChris Kirby { 477a7f53a56SChris Kirby uint64_t zoned; 478a7f53a56SChris Kirby 4793b2aab18SMatthew Ahrens if (dsl_prop_get_int_ds(ds, "zoned", &zoned)) 480be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 481a7f53a56SChris Kirby 482a7f53a56SChris Kirby return (zfs_dozonecheck_impl(dataset, zoned, cr)); 483a7f53a56SChris Kirby } 484a7f53a56SChris Kirby 4854445fffbSMatthew Ahrens static int 4863b2aab18SMatthew Ahrens zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds, 4873b2aab18SMatthew Ahrens const char *perm, cred_t *cr) 488fa9e4066Sahrens { 489fa9e4066Sahrens int error; 490fa9e4066Sahrens 49119b94df9SMatthew Ahrens error = zfs_dozonecheck_ds(name, ds, cr); 492ecd6cf80Smarks if (error == 0) { 493ecd6cf80Smarks error = secpolicy_zfs(cr); 4943b2aab18SMatthew Ahrens if (error != 0) 4954445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 496ecd6cf80Smarks } 497ecd6cf80Smarks return (error); 498ecd6cf80Smarks } 499ecd6cf80Smarks 5004445fffbSMatthew Ahrens static int 5013b2aab18SMatthew Ahrens zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr) 502a7f53a56SChris Kirby { 503a7f53a56SChris Kirby int error; 5043b2aab18SMatthew Ahrens dsl_dataset_t *ds; 5053b2aab18SMatthew Ahrens dsl_pool_t *dp; 506a7f53a56SChris Kirby 50725f7d993SMatthew Ahrens /* 50825f7d993SMatthew Ahrens * First do a quick check for root in the global zone, which 50925f7d993SMatthew Ahrens * is allowed to do all write_perms. This ensures that zfs_ioc_* 51025f7d993SMatthew Ahrens * will get to handle nonexistent datasets. 51125f7d993SMatthew Ahrens */ 51225f7d993SMatthew Ahrens if (INGLOBALZONE(curproc) && secpolicy_zfs(cr) == 0) 51325f7d993SMatthew Ahrens return (0); 51425f7d993SMatthew Ahrens 5153b2aab18SMatthew Ahrens error = dsl_pool_hold(name, FTAG, &dp); 5163b2aab18SMatthew Ahrens if (error != 0) 5173b2aab18SMatthew Ahrens return (error); 5183b2aab18SMatthew Ahrens 5193b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, name, FTAG, &ds); 5203b2aab18SMatthew Ahrens if (error != 0) { 5213b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 5223b2aab18SMatthew Ahrens return (error); 523a7f53a56SChris Kirby } 5243b2aab18SMatthew Ahrens 5253b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(name, ds, perm, cr); 5263b2aab18SMatthew Ahrens 5273b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 5283b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 529a7f53a56SChris Kirby return (error); 530a7f53a56SChris Kirby } 531a7f53a56SChris Kirby 5324201a95eSRic Aleshire /* 5334201a95eSRic Aleshire * Policy for setting the security label property. 5344201a95eSRic Aleshire * 5354201a95eSRic Aleshire * Returns 0 for success, non-zero for access and other errors. 5364201a95eSRic Aleshire */ 5374201a95eSRic Aleshire static int 53892241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) 5394201a95eSRic Aleshire { 5404201a95eSRic Aleshire char ds_hexsl[MAXNAMELEN]; 5414201a95eSRic Aleshire bslabel_t ds_sl, new_sl; 5424201a95eSRic Aleshire boolean_t new_default = FALSE; 5434201a95eSRic Aleshire uint64_t zoned; 5444201a95eSRic Aleshire int needed_priv = -1; 5454201a95eSRic Aleshire int error; 5464201a95eSRic Aleshire 5474201a95eSRic Aleshire /* First get the existing dataset label. */ 5484201a95eSRic Aleshire error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL), 5494201a95eSRic Aleshire 1, sizeof (ds_hexsl), &ds_hexsl, NULL); 5503b2aab18SMatthew Ahrens if (error != 0) 551be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5524201a95eSRic Aleshire 5534201a95eSRic Aleshire if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0) 5544201a95eSRic Aleshire new_default = TRUE; 5554201a95eSRic Aleshire 5564201a95eSRic Aleshire /* The label must be translatable */ 5574201a95eSRic Aleshire if (!new_default && (hexstr_to_label(strval, &new_sl) != 0)) 558be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5594201a95eSRic Aleshire 5604201a95eSRic Aleshire /* 5614201a95eSRic Aleshire * In a non-global zone, disallow attempts to set a label that 5624201a95eSRic Aleshire * doesn't match that of the zone; otherwise no other checks 5634201a95eSRic Aleshire * are needed. 5644201a95eSRic Aleshire */ 5654201a95eSRic Aleshire if (!INGLOBALZONE(curproc)) { 5664201a95eSRic Aleshire if (new_default || !blequal(&new_sl, CR_SL(CRED()))) 567be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5684201a95eSRic Aleshire return (0); 5694201a95eSRic Aleshire } 5704201a95eSRic Aleshire 5714201a95eSRic Aleshire /* 5724201a95eSRic Aleshire * For global-zone datasets (i.e., those whose zoned property is 5734201a95eSRic Aleshire * "off", verify that the specified new label is valid for the 5744201a95eSRic Aleshire * global zone. 5754201a95eSRic Aleshire */ 5764201a95eSRic Aleshire if (dsl_prop_get_integer(name, 5774201a95eSRic Aleshire zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL)) 578be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5794201a95eSRic Aleshire if (!zoned) { 5804201a95eSRic Aleshire if (zfs_check_global_label(name, strval) != 0) 581be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 5824201a95eSRic Aleshire } 5834201a95eSRic Aleshire 5844201a95eSRic Aleshire /* 5854201a95eSRic Aleshire * If the existing dataset label is nondefault, check if the 5864201a95eSRic Aleshire * dataset is mounted (label cannot be changed while mounted). 5874201a95eSRic Aleshire * Get the zfsvfs; if there isn't one, then the dataset isn't 5884201a95eSRic Aleshire * mounted (or isn't a dataset, doesn't exist, ...). 5894201a95eSRic Aleshire */ 5904201a95eSRic Aleshire if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) { 59192241e0bSTom Erickson objset_t *os; 59292241e0bSTom Erickson static char *setsl_tag = "setsl_tag"; 59392241e0bSTom Erickson 5944201a95eSRic Aleshire /* 5954201a95eSRic Aleshire * Try to own the dataset; abort if there is any error, 5964201a95eSRic Aleshire * (e.g., already mounted, in use, or other error). 5974201a95eSRic Aleshire */ 598eb633035STom Caputi error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, B_TRUE, 59992241e0bSTom Erickson setsl_tag, &os); 6003b2aab18SMatthew Ahrens if (error != 0) 601be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 6024201a95eSRic Aleshire 603eb633035STom Caputi dmu_objset_disown(os, B_TRUE, setsl_tag); 60492241e0bSTom Erickson 6054201a95eSRic Aleshire if (new_default) { 6064201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 6074201a95eSRic Aleshire goto out_check; 6084201a95eSRic Aleshire } 6094201a95eSRic Aleshire 6104201a95eSRic Aleshire if (hexstr_to_label(strval, &new_sl) != 0) 611be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 6124201a95eSRic Aleshire 6134201a95eSRic Aleshire if (blstrictdom(&ds_sl, &new_sl)) 6144201a95eSRic Aleshire needed_priv = PRIV_FILE_DOWNGRADE_SL; 6154201a95eSRic Aleshire else if (blstrictdom(&new_sl, &ds_sl)) 6164201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 6174201a95eSRic Aleshire } else { 6184201a95eSRic Aleshire /* dataset currently has a default label */ 6194201a95eSRic Aleshire if (!new_default) 6204201a95eSRic Aleshire needed_priv = PRIV_FILE_UPGRADE_SL; 6214201a95eSRic Aleshire } 6224201a95eSRic Aleshire 6234201a95eSRic Aleshire out_check: 6244201a95eSRic Aleshire if (needed_priv != -1) 6254201a95eSRic Aleshire return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL)); 6264201a95eSRic Aleshire return (0); 6274201a95eSRic Aleshire } 6284201a95eSRic Aleshire 629ecd6cf80Smarks static int 63092241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval, 63192241e0bSTom Erickson cred_t *cr) 632ecd6cf80Smarks { 63392241e0bSTom Erickson char *strval; 63492241e0bSTom Erickson 635ecd6cf80Smarks /* 636ecd6cf80Smarks * Check permissions for special properties. 637ecd6cf80Smarks */ 638ecd6cf80Smarks switch (prop) { 639ecd6cf80Smarks case ZFS_PROP_ZONED: 640ecd6cf80Smarks /* 641ecd6cf80Smarks * Disallow setting of 'zoned' from within a local zone. 642ecd6cf80Smarks */ 643ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 644be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 645ecd6cf80Smarks break; 646ecd6cf80Smarks 647ecd6cf80Smarks case ZFS_PROP_QUOTA: 648a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 649a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 650ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 651ecd6cf80Smarks uint64_t zoned; 6529adfa60dSMatthew Ahrens char setpoint[ZFS_MAX_DATASET_NAME_LEN]; 653ecd6cf80Smarks /* 654ecd6cf80Smarks * Unprivileged users are allowed to modify the 655a2afb611SJerry Jelinek * limit on things *under* (ie. contained by) 656ecd6cf80Smarks * the thing they own. 657ecd6cf80Smarks */ 65892241e0bSTom Erickson if (dsl_prop_get_integer(dsname, "zoned", &zoned, 659ecd6cf80Smarks setpoint)) 660be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 66192241e0bSTom Erickson if (!zoned || strlen(dsname) <= strlen(setpoint)) 662be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 663ecd6cf80Smarks } 664db870a07Sahrens break; 6654201a95eSRic Aleshire 6664201a95eSRic Aleshire case ZFS_PROP_MLSLABEL: 6674201a95eSRic Aleshire if (!is_system_labeled()) 668be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 66992241e0bSTom Erickson 67092241e0bSTom Erickson if (nvpair_value_string(propval, &strval) == 0) { 67192241e0bSTom Erickson int err; 67292241e0bSTom Erickson 67392241e0bSTom Erickson err = zfs_set_slabel_policy(dsname, strval, CRED()); 67492241e0bSTom Erickson if (err != 0) 67592241e0bSTom Erickson return (err); 67692241e0bSTom Erickson } 6774201a95eSRic Aleshire break; 678ecd6cf80Smarks } 679ecd6cf80Smarks 68092241e0bSTom Erickson return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr)); 681ecd6cf80Smarks } 682ecd6cf80Smarks 6834445fffbSMatthew Ahrens /* ARGSUSED */ 6844445fffbSMatthew Ahrens static int 6854445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 686ecd6cf80Smarks { 687ecd6cf80Smarks int error; 688ecd6cf80Smarks 689ecd6cf80Smarks error = zfs_dozonecheck(zc->zc_name, cr); 6903b2aab18SMatthew Ahrens if (error != 0) 691fa9e4066Sahrens return (error); 692fa9e4066Sahrens 693ecd6cf80Smarks /* 694ecd6cf80Smarks * permission to set permissions will be evaluated later in 695ecd6cf80Smarks * dsl_deleg_can_allow() 696ecd6cf80Smarks */ 697ecd6cf80Smarks return (0); 698ecd6cf80Smarks } 699ecd6cf80Smarks 7004445fffbSMatthew Ahrens /* ARGSUSED */ 7014445fffbSMatthew Ahrens static int 7024445fffbSMatthew Ahrens zfs_secpolicy_rollback(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 703ecd6cf80Smarks { 704681d9761SEric Taylor return (zfs_secpolicy_write_perms(zc->zc_name, 705681d9761SEric Taylor ZFS_DELEG_PERM_ROLLBACK, cr)); 706ecd6cf80Smarks } 707ecd6cf80Smarks 7084445fffbSMatthew Ahrens /* ARGSUSED */ 7094445fffbSMatthew Ahrens static int 7104445fffbSMatthew Ahrens zfs_secpolicy_send(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 711ecd6cf80Smarks { 712a7f53a56SChris Kirby dsl_pool_t *dp; 713a7f53a56SChris Kirby dsl_dataset_t *ds; 714a7f53a56SChris Kirby char *cp; 715a7f53a56SChris Kirby int error; 716a7f53a56SChris Kirby 717a7f53a56SChris Kirby /* 718a7f53a56SChris Kirby * Generate the current snapshot name from the given objsetid, then 719a7f53a56SChris Kirby * use that name for the secpolicy/zone checks. 720a7f53a56SChris Kirby */ 721a7f53a56SChris Kirby cp = strchr(zc->zc_name, '@'); 722a7f53a56SChris Kirby if (cp == NULL) 723be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 7243b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 7253b2aab18SMatthew Ahrens if (error != 0) 726a7f53a56SChris Kirby return (error); 727a7f53a56SChris Kirby 728a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds); 7293b2aab18SMatthew Ahrens if (error != 0) { 7303b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 731a7f53a56SChris Kirby return (error); 7323b2aab18SMatthew Ahrens } 733a7f53a56SChris Kirby 734a7f53a56SChris Kirby dsl_dataset_name(ds, zc->zc_name); 735a7f53a56SChris Kirby 736a7f53a56SChris Kirby error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds, 737a7f53a56SChris Kirby ZFS_DELEG_PERM_SEND, cr); 738a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 7393b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 740a7f53a56SChris Kirby 741a7f53a56SChris Kirby return (error); 742ecd6cf80Smarks } 743ecd6cf80Smarks 7444445fffbSMatthew Ahrens /* ARGSUSED */ 745743a77edSAlan Wright static int 7464445fffbSMatthew Ahrens zfs_secpolicy_send_new(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 7474445fffbSMatthew Ahrens { 7484445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 7494445fffbSMatthew Ahrens ZFS_DELEG_PERM_SEND, cr)); 7504445fffbSMatthew Ahrens } 7514445fffbSMatthew Ahrens 7524445fffbSMatthew Ahrens /* ARGSUSED */ 7534445fffbSMatthew Ahrens static int 7544445fffbSMatthew Ahrens zfs_secpolicy_deleg_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 755743a77edSAlan Wright { 756743a77edSAlan Wright vnode_t *vp; 757743a77edSAlan Wright int error; 758743a77edSAlan Wright 759743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 760743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 761743a77edSAlan Wright return (error); 762743a77edSAlan Wright 763743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 764743a77edSAlan Wright 765743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 766743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 767743a77edSAlan Wright zc->zc_name) != 0)) { 768743a77edSAlan Wright VN_RELE(vp); 769be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 770743a77edSAlan Wright } 771743a77edSAlan Wright 772743a77edSAlan Wright VN_RELE(vp); 773743a77edSAlan Wright return (dsl_deleg_access(zc->zc_name, 774743a77edSAlan Wright ZFS_DELEG_PERM_SHARE, cr)); 775743a77edSAlan Wright } 776743a77edSAlan Wright 777ecd6cf80Smarks int 7784445fffbSMatthew Ahrens zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 779ecd6cf80Smarks { 780ecd6cf80Smarks if (!INGLOBALZONE(curproc)) 781be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 782ecd6cf80Smarks 7833cb34c60Sahrens if (secpolicy_nfs(cr) == 0) { 784ecd6cf80Smarks return (0); 785ecd6cf80Smarks } else { 7864445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 787743a77edSAlan Wright } 788743a77edSAlan Wright } 789ecd6cf80Smarks 790743a77edSAlan Wright int 7914445fffbSMatthew Ahrens zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 792743a77edSAlan Wright { 793743a77edSAlan Wright if (!INGLOBALZONE(curproc)) 794be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 795ecd6cf80Smarks 796743a77edSAlan Wright if (secpolicy_smb(cr) == 0) { 797743a77edSAlan Wright return (0); 798743a77edSAlan Wright } else { 7994445fffbSMatthew Ahrens return (zfs_secpolicy_deleg_share(zc, innvl, cr)); 800ecd6cf80Smarks } 801fa9e4066Sahrens } 802fa9e4066Sahrens 803fa9e4066Sahrens static int 804ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize) 805fa9e4066Sahrens { 806fa9e4066Sahrens char *cp; 807fa9e4066Sahrens 808fa9e4066Sahrens /* 809fa9e4066Sahrens * Remove the @bla or /bla from the end of the name to get the parent. 810fa9e4066Sahrens */ 811ecd6cf80Smarks (void) strncpy(parent, datasetname, parentsize); 812ecd6cf80Smarks cp = strrchr(parent, '@'); 813fa9e4066Sahrens if (cp != NULL) { 814fa9e4066Sahrens cp[0] = '\0'; 815fa9e4066Sahrens } else { 816ecd6cf80Smarks cp = strrchr(parent, '/'); 817fa9e4066Sahrens if (cp == NULL) 818be6fd75aSMatthew Ahrens return (SET_ERROR(ENOENT)); 819fa9e4066Sahrens cp[0] = '\0'; 820ecd6cf80Smarks } 821ecd6cf80Smarks 822ecd6cf80Smarks return (0); 823ecd6cf80Smarks } 824ecd6cf80Smarks 825ecd6cf80Smarks int 826ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr) 827ecd6cf80Smarks { 828ecd6cf80Smarks int error; 829ecd6cf80Smarks 830ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(name, 831ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 832ecd6cf80Smarks return (error); 833ecd6cf80Smarks 834ecd6cf80Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr)); 835ecd6cf80Smarks } 836ecd6cf80Smarks 8374445fffbSMatthew Ahrens /* ARGSUSED */ 838ecd6cf80Smarks static int 8394445fffbSMatthew Ahrens zfs_secpolicy_destroy(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 840ecd6cf80Smarks { 841ecd6cf80Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr)); 842ecd6cf80Smarks } 843ecd6cf80Smarks 844cbf6f6aaSWilliam Gorrell /* 845cbf6f6aaSWilliam Gorrell * Destroying snapshots with delegated permissions requires 8464445fffbSMatthew Ahrens * descendant mount and destroy permissions. 847cbf6f6aaSWilliam Gorrell */ 8484445fffbSMatthew Ahrens /* ARGSUSED */ 849cbf6f6aaSWilliam Gorrell static int 8504445fffbSMatthew Ahrens zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 851cbf6f6aaSWilliam Gorrell { 8524445fffbSMatthew Ahrens nvlist_t *snaps; 8534445fffbSMatthew Ahrens nvpair_t *pair, *nextpair; 8544445fffbSMatthew Ahrens int error = 0; 855cbf6f6aaSWilliam Gorrell 8564445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 857be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 8584445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 8594445fffbSMatthew Ahrens pair = nextpair) { 8604445fffbSMatthew Ahrens nextpair = nvlist_next_nvpair(snaps, pair); 86178f17100SMatthew Ahrens error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); 86278f17100SMatthew Ahrens if (error == ENOENT) { 8634445fffbSMatthew Ahrens /* 8644445fffbSMatthew Ahrens * Ignore any snapshots that don't exist (we consider 8654445fffbSMatthew Ahrens * them "already destroyed"). Remove the name from the 8664445fffbSMatthew Ahrens * nvl here in case the snapshot is created between 8674445fffbSMatthew Ahrens * now and when we try to destroy it (in which case 8684445fffbSMatthew Ahrens * we don't want to destroy it since we haven't 8694445fffbSMatthew Ahrens * checked for permission). 8704445fffbSMatthew Ahrens */ 8714445fffbSMatthew Ahrens fnvlist_remove_nvpair(snaps, pair); 8724445fffbSMatthew Ahrens error = 0; 8734445fffbSMatthew Ahrens } 8744445fffbSMatthew Ahrens if (error != 0) 8754445fffbSMatthew Ahrens break; 8764445fffbSMatthew Ahrens } 877cbf6f6aaSWilliam Gorrell 878cbf6f6aaSWilliam Gorrell return (error); 879cbf6f6aaSWilliam Gorrell } 880cbf6f6aaSWilliam Gorrell 881ecd6cf80Smarks int 882ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr) 883ecd6cf80Smarks { 8849adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 885ecd6cf80Smarks int error; 886ecd6cf80Smarks 887ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 888ecd6cf80Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0) 889ecd6cf80Smarks return (error); 890ecd6cf80Smarks 891ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(from, 892ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 893ecd6cf80Smarks return (error); 894ecd6cf80Smarks 895ecd6cf80Smarks if ((error = zfs_get_parent(to, parentname, 896ecd6cf80Smarks sizeof (parentname))) != 0) 897ecd6cf80Smarks return (error); 898ecd6cf80Smarks 899ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 900ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 901ecd6cf80Smarks return (error); 902ecd6cf80Smarks 903ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 904ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 905ecd6cf80Smarks return (error); 906ecd6cf80Smarks 907ecd6cf80Smarks return (error); 908ecd6cf80Smarks } 909ecd6cf80Smarks 9104445fffbSMatthew Ahrens /* ARGSUSED */ 911ecd6cf80Smarks static int 9124445fffbSMatthew Ahrens zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 913ecd6cf80Smarks { 914ecd6cf80Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); 915ecd6cf80Smarks } 916ecd6cf80Smarks 9174445fffbSMatthew Ahrens /* ARGSUSED */ 918ecd6cf80Smarks static int 9194445fffbSMatthew Ahrens zfs_secpolicy_promote(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 920ecd6cf80Smarks { 9213b2aab18SMatthew Ahrens dsl_pool_t *dp; 9223b2aab18SMatthew Ahrens dsl_dataset_t *clone; 923ecd6cf80Smarks int error; 924ecd6cf80Smarks 925ecd6cf80Smarks error = zfs_secpolicy_write_perms(zc->zc_name, 926ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9273b2aab18SMatthew Ahrens if (error != 0) 9283b2aab18SMatthew Ahrens return (error); 9293b2aab18SMatthew Ahrens 9303b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 9313b2aab18SMatthew Ahrens if (error != 0) 932ecd6cf80Smarks return (error); 933ecd6cf80Smarks 9343b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &clone); 935ecd6cf80Smarks 936ecd6cf80Smarks if (error == 0) { 9379adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 9383b2aab18SMatthew Ahrens dsl_dataset_t *origin = NULL; 939ecd6cf80Smarks dsl_dir_t *dd; 9403b2aab18SMatthew Ahrens dd = clone->ds_dir; 941ecd6cf80Smarks 942745cd3c5Smaybee error = dsl_dataset_hold_obj(dd->dd_pool, 943c1379625SJustin T. Gibbs dsl_dir_phys(dd)->dd_origin_obj, FTAG, &origin); 9443b2aab18SMatthew Ahrens if (error != 0) { 9453b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9463b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 947ecd6cf80Smarks return (error); 948ecd6cf80Smarks } 949ecd6cf80Smarks 9503b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(zc->zc_name, clone, 951ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr); 952ecd6cf80Smarks 9533b2aab18SMatthew Ahrens dsl_dataset_name(origin, parentname); 9543b2aab18SMatthew Ahrens if (error == 0) { 9553b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms_ds(parentname, origin, 956ecd6cf80Smarks ZFS_DELEG_PERM_PROMOTE, cr); 9573b2aab18SMatthew Ahrens } 9583b2aab18SMatthew Ahrens dsl_dataset_rele(clone, FTAG); 9593b2aab18SMatthew Ahrens dsl_dataset_rele(origin, FTAG); 960ecd6cf80Smarks } 9613b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 962ecd6cf80Smarks return (error); 963ecd6cf80Smarks } 964ecd6cf80Smarks 9654445fffbSMatthew Ahrens /* ARGSUSED */ 966ecd6cf80Smarks static int 9674445fffbSMatthew Ahrens zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 968ecd6cf80Smarks { 969ecd6cf80Smarks int error; 970ecd6cf80Smarks 971ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 972ecd6cf80Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0) 973ecd6cf80Smarks return (error); 974ecd6cf80Smarks 975ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name, 976ecd6cf80Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0) 977ecd6cf80Smarks return (error); 978ecd6cf80Smarks 979ecd6cf80Smarks return (zfs_secpolicy_write_perms(zc->zc_name, 980ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)); 981ecd6cf80Smarks } 982ecd6cf80Smarks 983ecd6cf80Smarks int 984ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) 985ecd6cf80Smarks { 986681d9761SEric Taylor return (zfs_secpolicy_write_perms(name, 987681d9761SEric Taylor ZFS_DELEG_PERM_SNAPSHOT, cr)); 988ecd6cf80Smarks } 989ecd6cf80Smarks 9904445fffbSMatthew Ahrens /* 9914445fffbSMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 9924445fffbSMatthew Ahrens */ 9934445fffbSMatthew Ahrens /* ARGSUSED */ 994ecd6cf80Smarks static int 9954445fffbSMatthew Ahrens zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 996ecd6cf80Smarks { 9974445fffbSMatthew Ahrens nvlist_t *snaps; 998d5285caeSGeorge Wilson int error = 0; 9994445fffbSMatthew Ahrens nvpair_t *pair; 1000ecd6cf80Smarks 10014445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 1002be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 10034445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 10044445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 10054445fffbSMatthew Ahrens char *name = nvpair_name(pair); 10064445fffbSMatthew Ahrens char *atp = strchr(name, '@'); 10074445fffbSMatthew Ahrens 10084445fffbSMatthew Ahrens if (atp == NULL) { 1009be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 10104445fffbSMatthew Ahrens break; 10114445fffbSMatthew Ahrens } 10124445fffbSMatthew Ahrens *atp = '\0'; 10134445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(name, cr); 10144445fffbSMatthew Ahrens *atp = '@'; 10154445fffbSMatthew Ahrens if (error != 0) 10164445fffbSMatthew Ahrens break; 10174445fffbSMatthew Ahrens } 10184445fffbSMatthew Ahrens return (error); 1019ecd6cf80Smarks } 1020ecd6cf80Smarks 102178f17100SMatthew Ahrens /* 102278f17100SMatthew Ahrens * Check for permission to create each snapshot in the nvlist. 102378f17100SMatthew Ahrens */ 102478f17100SMatthew Ahrens /* ARGSUSED */ 102578f17100SMatthew Ahrens static int 102678f17100SMatthew Ahrens zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 102778f17100SMatthew Ahrens { 102878f17100SMatthew Ahrens int error = 0; 102978f17100SMatthew Ahrens 103078f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 103178f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 103278f17100SMatthew Ahrens char *name = nvpair_name(pair); 103378f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 103478f17100SMatthew Ahrens 103578f17100SMatthew Ahrens if (hashp == NULL) { 103678f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 103778f17100SMatthew Ahrens break; 103878f17100SMatthew Ahrens } 103978f17100SMatthew Ahrens *hashp = '\0'; 104078f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 104178f17100SMatthew Ahrens ZFS_DELEG_PERM_BOOKMARK, cr); 104278f17100SMatthew Ahrens *hashp = '#'; 104378f17100SMatthew Ahrens if (error != 0) 104478f17100SMatthew Ahrens break; 104578f17100SMatthew Ahrens } 104678f17100SMatthew Ahrens return (error); 104778f17100SMatthew Ahrens } 104878f17100SMatthew Ahrens 10495cabbc6bSPrashanth Sreenivasa /* ARGSUSED */ 10505cabbc6bSPrashanth Sreenivasa static int 10515cabbc6bSPrashanth Sreenivasa zfs_secpolicy_remap(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 10525cabbc6bSPrashanth Sreenivasa { 10535cabbc6bSPrashanth Sreenivasa return (zfs_secpolicy_write_perms(zc->zc_name, 10545cabbc6bSPrashanth Sreenivasa ZFS_DELEG_PERM_REMAP, cr)); 10555cabbc6bSPrashanth Sreenivasa } 10565cabbc6bSPrashanth Sreenivasa 105778f17100SMatthew Ahrens /* ARGSUSED */ 105878f17100SMatthew Ahrens static int 105978f17100SMatthew Ahrens zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 106078f17100SMatthew Ahrens { 106178f17100SMatthew Ahrens nvpair_t *pair, *nextpair; 106278f17100SMatthew Ahrens int error = 0; 106378f17100SMatthew Ahrens 106478f17100SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 106578f17100SMatthew Ahrens pair = nextpair) { 106678f17100SMatthew Ahrens char *name = nvpair_name(pair); 106778f17100SMatthew Ahrens char *hashp = strchr(name, '#'); 106878f17100SMatthew Ahrens nextpair = nvlist_next_nvpair(innvl, pair); 106978f17100SMatthew Ahrens 107078f17100SMatthew Ahrens if (hashp == NULL) { 107178f17100SMatthew Ahrens error = SET_ERROR(EINVAL); 107278f17100SMatthew Ahrens break; 107378f17100SMatthew Ahrens } 107478f17100SMatthew Ahrens 107578f17100SMatthew Ahrens *hashp = '\0'; 107678f17100SMatthew Ahrens error = zfs_secpolicy_write_perms(name, 107778f17100SMatthew Ahrens ZFS_DELEG_PERM_DESTROY, cr); 107878f17100SMatthew Ahrens *hashp = '#'; 107978f17100SMatthew Ahrens if (error == ENOENT) { 108078f17100SMatthew Ahrens /* 108178f17100SMatthew Ahrens * Ignore any filesystems that don't exist (we consider 108278f17100SMatthew Ahrens * their bookmarks "already destroyed"). Remove 108378f17100SMatthew Ahrens * the name from the nvl here in case the filesystem 108478f17100SMatthew Ahrens * is created between now and when we try to destroy 108578f17100SMatthew Ahrens * the bookmark (in which case we don't want to 108678f17100SMatthew Ahrens * destroy it since we haven't checked for permission). 108778f17100SMatthew Ahrens */ 108878f17100SMatthew Ahrens fnvlist_remove_nvpair(innvl, pair); 108978f17100SMatthew Ahrens error = 0; 109078f17100SMatthew Ahrens } 109178f17100SMatthew Ahrens if (error != 0) 109278f17100SMatthew Ahrens break; 109378f17100SMatthew Ahrens } 109478f17100SMatthew Ahrens 109578f17100SMatthew Ahrens return (error); 109678f17100SMatthew Ahrens } 109778f17100SMatthew Ahrens 10984445fffbSMatthew Ahrens /* ARGSUSED */ 1099ecd6cf80Smarks static int 11004445fffbSMatthew Ahrens zfs_secpolicy_log_history(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 11014445fffbSMatthew Ahrens { 11024445fffbSMatthew Ahrens /* 11034445fffbSMatthew Ahrens * Even root must have a proper TSD so that we know what pool 11044445fffbSMatthew Ahrens * to log to. 11054445fffbSMatthew Ahrens */ 11064445fffbSMatthew Ahrens if (tsd_get(zfs_allow_log_key) == NULL) 1107be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 11084445fffbSMatthew Ahrens return (0); 11094445fffbSMatthew Ahrens } 11104445fffbSMatthew Ahrens 11114445fffbSMatthew Ahrens static int 11124445fffbSMatthew Ahrens zfs_secpolicy_create_clone(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1113ecd6cf80Smarks { 11149adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 111592241e0bSTom Erickson int error; 11164445fffbSMatthew Ahrens char *origin; 1117ecd6cf80Smarks 1118ecd6cf80Smarks if ((error = zfs_get_parent(zc->zc_name, parentname, 1119ecd6cf80Smarks sizeof (parentname))) != 0) 1120ecd6cf80Smarks return (error); 1121fa9e4066Sahrens 11224445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin) == 0 && 11234445fffbSMatthew Ahrens (error = zfs_secpolicy_write_perms(origin, 11244445fffbSMatthew Ahrens ZFS_DELEG_PERM_CLONE, cr)) != 0) 11254445fffbSMatthew Ahrens return (error); 1126fa9e4066Sahrens 1127ecd6cf80Smarks if ((error = zfs_secpolicy_write_perms(parentname, 1128ecd6cf80Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0) 1129ecd6cf80Smarks return (error); 1130ecd6cf80Smarks 11314445fffbSMatthew Ahrens return (zfs_secpolicy_write_perms(parentname, 11324445fffbSMatthew Ahrens ZFS_DELEG_PERM_MOUNT, cr)); 1133fa9e4066Sahrens } 1134fa9e4066Sahrens 1135fa9e4066Sahrens /* 1136fa9e4066Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires 1137fa9e4066Sahrens * SYS_CONFIG privilege, which is not available in a local zone. 1138fa9e4066Sahrens */ 1139fa9e4066Sahrens /* ARGSUSED */ 1140fa9e4066Sahrens static int 11414445fffbSMatthew Ahrens zfs_secpolicy_config(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1142fa9e4066Sahrens { 1143fa9e4066Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0) 1144be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 1145fa9e4066Sahrens 1146fa9e4066Sahrens return (0); 1147fa9e4066Sahrens } 1148fa9e4066Sahrens 114999d5e173STim Haley /* 115099d5e173STim Haley * Policy for object to name lookups. 115199d5e173STim Haley */ 115299d5e173STim Haley /* ARGSUSED */ 115399d5e173STim Haley static int 11544445fffbSMatthew Ahrens zfs_secpolicy_diff(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 115599d5e173STim Haley { 115699d5e173STim Haley int error; 115799d5e173STim Haley 115899d5e173STim Haley if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0) 115999d5e173STim Haley return (0); 116099d5e173STim Haley 116199d5e173STim Haley error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr); 116299d5e173STim Haley return (error); 116399d5e173STim Haley } 116499d5e173STim Haley 1165ea8dc4b6Seschrock /* 1166ea8dc4b6Seschrock * Policy for fault injection. Requires all privileges. 1167ea8dc4b6Seschrock */ 1168ea8dc4b6Seschrock /* ARGSUSED */ 1169ea8dc4b6Seschrock static int 11704445fffbSMatthew Ahrens zfs_secpolicy_inject(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1171ea8dc4b6Seschrock { 1172ea8dc4b6Seschrock return (secpolicy_zinject(cr)); 1173ea8dc4b6Seschrock } 1174ea8dc4b6Seschrock 11754445fffbSMatthew Ahrens /* ARGSUSED */ 1176e45ce728Sahrens static int 11774445fffbSMatthew Ahrens zfs_secpolicy_inherit_prop(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1178e45ce728Sahrens { 1179e45ce728Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value); 1180e45ce728Sahrens 1181990b4856Slling if (prop == ZPROP_INVAL) { 1182e45ce728Sahrens if (!zfs_prop_user(zc->zc_value)) 1183be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1184e45ce728Sahrens return (zfs_secpolicy_write_perms(zc->zc_name, 1185e45ce728Sahrens ZFS_DELEG_PERM_USERPROP, cr)); 1186e45ce728Sahrens } else { 118792241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, prop, 118892241e0bSTom Erickson NULL, cr)); 1189e45ce728Sahrens } 1190e45ce728Sahrens } 1191e45ce728Sahrens 119214843421SMatthew Ahrens static int 11934445fffbSMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 119414843421SMatthew Ahrens { 11954445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 119614843421SMatthew Ahrens if (err) 119714843421SMatthew Ahrens return (err); 119814843421SMatthew Ahrens 119914843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1200be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 120114843421SMatthew Ahrens 120214843421SMatthew Ahrens if (zc->zc_value[0] == 0) { 120314843421SMatthew Ahrens /* 120414843421SMatthew Ahrens * They are asking about a posix uid/gid. If it's 120514843421SMatthew Ahrens * themself, allow it. 120614843421SMatthew Ahrens */ 120714843421SMatthew Ahrens if (zc->zc_objset_type == ZFS_PROP_USERUSED || 120814843421SMatthew Ahrens zc->zc_objset_type == ZFS_PROP_USERQUOTA) { 120914843421SMatthew Ahrens if (zc->zc_guid == crgetuid(cr)) 121014843421SMatthew Ahrens return (0); 121114843421SMatthew Ahrens } else { 121214843421SMatthew Ahrens if (groupmember(zc->zc_guid, cr)) 121314843421SMatthew Ahrens return (0); 121414843421SMatthew Ahrens } 121514843421SMatthew Ahrens } 121614843421SMatthew Ahrens 121714843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 121814843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 121914843421SMatthew Ahrens } 122014843421SMatthew Ahrens 122114843421SMatthew Ahrens static int 12224445fffbSMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 122314843421SMatthew Ahrens { 12244445fffbSMatthew Ahrens int err = zfs_secpolicy_read(zc, innvl, cr); 122514843421SMatthew Ahrens if (err) 122614843421SMatthew Ahrens return (err); 122714843421SMatthew Ahrens 122814843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 1229be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 123014843421SMatthew Ahrens 123114843421SMatthew Ahrens return (zfs_secpolicy_write_perms(zc->zc_name, 123214843421SMatthew Ahrens userquota_perms[zc->zc_objset_type], cr)); 123314843421SMatthew Ahrens } 123414843421SMatthew Ahrens 12354445fffbSMatthew Ahrens /* ARGSUSED */ 123614843421SMatthew Ahrens static int 12374445fffbSMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 123814843421SMatthew Ahrens { 123992241e0bSTom Erickson return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, 124092241e0bSTom Erickson NULL, cr)); 124114843421SMatthew Ahrens } 124214843421SMatthew Ahrens 12434445fffbSMatthew Ahrens /* ARGSUSED */ 1244842727c2SChris Kirby static int 12454445fffbSMatthew Ahrens zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1246842727c2SChris Kirby { 12473b2aab18SMatthew Ahrens nvpair_t *pair; 12483b2aab18SMatthew Ahrens nvlist_t *holds; 12493b2aab18SMatthew Ahrens int error; 12503b2aab18SMatthew Ahrens 12513b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(innvl, "holds", &holds); 12523b2aab18SMatthew Ahrens if (error != 0) 1253be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 12543b2aab18SMatthew Ahrens 12553b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 12563b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(holds, pair)) { 12579adfa60dSMatthew Ahrens char fsname[ZFS_MAX_DATASET_NAME_LEN]; 12583b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12593b2aab18SMatthew Ahrens if (error != 0) 12603b2aab18SMatthew Ahrens return (error); 12613b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12623b2aab18SMatthew Ahrens ZFS_DELEG_PERM_HOLD, cr); 12633b2aab18SMatthew Ahrens if (error != 0) 12643b2aab18SMatthew Ahrens return (error); 12653b2aab18SMatthew Ahrens } 12663b2aab18SMatthew Ahrens return (0); 1267842727c2SChris Kirby } 1268842727c2SChris Kirby 12694445fffbSMatthew Ahrens /* ARGSUSED */ 1270842727c2SChris Kirby static int 12714445fffbSMatthew Ahrens zfs_secpolicy_release(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1272842727c2SChris Kirby { 12733b2aab18SMatthew Ahrens nvpair_t *pair; 12743b2aab18SMatthew Ahrens int error; 12753b2aab18SMatthew Ahrens 12763b2aab18SMatthew Ahrens for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; 12773b2aab18SMatthew Ahrens pair = nvlist_next_nvpair(innvl, pair)) { 12789adfa60dSMatthew Ahrens char fsname[ZFS_MAX_DATASET_NAME_LEN]; 12793b2aab18SMatthew Ahrens error = dmu_fsname(nvpair_name(pair), fsname); 12803b2aab18SMatthew Ahrens if (error != 0) 12813b2aab18SMatthew Ahrens return (error); 12823b2aab18SMatthew Ahrens error = zfs_secpolicy_write_perms(fsname, 12833b2aab18SMatthew Ahrens ZFS_DELEG_PERM_RELEASE, cr); 12843b2aab18SMatthew Ahrens if (error != 0) 12853b2aab18SMatthew Ahrens return (error); 12863b2aab18SMatthew Ahrens } 12873b2aab18SMatthew Ahrens return (0); 1288842727c2SChris Kirby } 1289842727c2SChris Kirby 1290eb633035STom Caputi /* ARGSUSED */ 1291eb633035STom Caputi static int 1292eb633035STom Caputi zfs_secpolicy_load_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1293eb633035STom Caputi { 1294eb633035STom Caputi return (zfs_secpolicy_write_perms(zc->zc_name, 1295eb633035STom Caputi ZFS_DELEG_PERM_LOAD_KEY, cr)); 1296eb633035STom Caputi } 1297eb633035STom Caputi 1298eb633035STom Caputi /* ARGSUSED */ 1299eb633035STom Caputi static int 1300eb633035STom Caputi zfs_secpolicy_change_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 1301eb633035STom Caputi { 1302eb633035STom Caputi return (zfs_secpolicy_write_perms(zc->zc_name, 1303eb633035STom Caputi ZFS_DELEG_PERM_CHANGE_KEY, cr)); 1304eb633035STom Caputi } 1305eb633035STom Caputi 130699d5e173STim Haley /* 130799d5e173STim Haley * Policy for allowing temporary snapshots to be taken or released 130899d5e173STim Haley */ 130999d5e173STim Haley static int 13104445fffbSMatthew Ahrens zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 131199d5e173STim Haley { 131299d5e173STim Haley /* 131399d5e173STim Haley * A temporary snapshot is the same as a snapshot, 131499d5e173STim Haley * hold, destroy and release all rolled into one. 131599d5e173STim Haley * Delegated diff alone is sufficient that we allow this. 131699d5e173STim Haley */ 131799d5e173STim Haley int error; 131899d5e173STim Haley 131999d5e173STim Haley if ((error = zfs_secpolicy_write_perms(zc->zc_name, 132099d5e173STim Haley ZFS_DELEG_PERM_DIFF, cr)) == 0) 132199d5e173STim Haley return (0); 132299d5e173STim Haley 13234445fffbSMatthew Ahrens error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); 13243b2aab18SMatthew Ahrens if (error == 0) 13254445fffbSMatthew Ahrens error = zfs_secpolicy_hold(zc, innvl, cr); 13263b2aab18SMatthew Ahrens if (error == 0) 13274445fffbSMatthew Ahrens error = zfs_secpolicy_release(zc, innvl, cr); 13283b2aab18SMatthew Ahrens if (error == 0) 13294445fffbSMatthew Ahrens error = zfs_secpolicy_destroy(zc, innvl, cr); 133099d5e173STim Haley return (error); 133199d5e173STim Haley } 133299d5e173STim Haley 1333fa9e4066Sahrens /* 1334fa9e4066Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t. 1335fa9e4066Sahrens */ 1336fa9e4066Sahrens static int 1337478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) 1338fa9e4066Sahrens { 1339fa9e4066Sahrens char *packed; 1340fa9e4066Sahrens int error; 1341990b4856Slling nvlist_t *list = NULL; 1342fa9e4066Sahrens 1343fa9e4066Sahrens /* 1344e9dbad6fSeschrock * Read in and unpack the user-supplied nvlist. 1345fa9e4066Sahrens */ 1346990b4856Slling if (size == 0) 1347be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1348fa9e4066Sahrens 1349fa9e4066Sahrens packed = kmem_alloc(size, KM_SLEEP); 1350fa9e4066Sahrens 1351478ed9adSEric Taylor if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, 1352478ed9adSEric Taylor iflag)) != 0) { 1353fa9e4066Sahrens kmem_free(packed, size); 1354c71c00bbSRichard Yao return (SET_ERROR(EFAULT)); 1355fa9e4066Sahrens } 1356fa9e4066Sahrens 1357990b4856Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) { 1358fa9e4066Sahrens kmem_free(packed, size); 1359fa9e4066Sahrens return (error); 1360fa9e4066Sahrens } 1361fa9e4066Sahrens 1362fa9e4066Sahrens kmem_free(packed, size); 1363fa9e4066Sahrens 1364990b4856Slling *nvp = list; 1365fa9e4066Sahrens return (0); 1366fa9e4066Sahrens } 1367fa9e4066Sahrens 13684445fffbSMatthew Ahrens /* 13694445fffbSMatthew Ahrens * Reduce the size of this nvlist until it can be serialized in 'max' bytes. 13704445fffbSMatthew Ahrens * Entries will be removed from the end of the nvlist, and one int32 entry 13714445fffbSMatthew Ahrens * named "N_MORE_ERRORS" will be added indicating how many entries were 13724445fffbSMatthew Ahrens * removed. 13734445fffbSMatthew Ahrens */ 137492241e0bSTom Erickson static int 13754445fffbSMatthew Ahrens nvlist_smush(nvlist_t *errors, size_t max) 137692241e0bSTom Erickson { 137792241e0bSTom Erickson size_t size; 137892241e0bSTom Erickson 13794445fffbSMatthew Ahrens size = fnvlist_size(errors); 138092241e0bSTom Erickson 13814445fffbSMatthew Ahrens if (size > max) { 138292241e0bSTom Erickson nvpair_t *more_errors; 138392241e0bSTom Erickson int n = 0; 138492241e0bSTom Erickson 13854445fffbSMatthew Ahrens if (max < 1024) 1386be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 138792241e0bSTom Erickson 13884445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, 0); 13894445fffbSMatthew Ahrens more_errors = nvlist_prev_nvpair(errors, NULL); 139092241e0bSTom Erickson 139192241e0bSTom Erickson do { 13924445fffbSMatthew Ahrens nvpair_t *pair = nvlist_prev_nvpair(errors, 139392241e0bSTom Erickson more_errors); 13944445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, pair); 139592241e0bSTom Erickson n++; 13964445fffbSMatthew Ahrens size = fnvlist_size(errors); 13974445fffbSMatthew Ahrens } while (size > max); 139892241e0bSTom Erickson 13994445fffbSMatthew Ahrens fnvlist_remove_nvpair(errors, more_errors); 14004445fffbSMatthew Ahrens fnvlist_add_int32(errors, ZPROP_N_MORE_ERRORS, n); 14014445fffbSMatthew Ahrens ASSERT3U(fnvlist_size(errors), <=, max); 140292241e0bSTom Erickson } 140392241e0bSTom Erickson 140492241e0bSTom Erickson return (0); 140592241e0bSTom Erickson } 140692241e0bSTom Erickson 1407e9dbad6fSeschrock static int 1408e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) 1409e9dbad6fSeschrock { 1410e9dbad6fSeschrock char *packed = NULL; 14116e27f868SSam Falkner int error = 0; 1412e9dbad6fSeschrock size_t size; 1413e9dbad6fSeschrock 14144445fffbSMatthew Ahrens size = fnvlist_size(nvl); 1415e9dbad6fSeschrock 1416e9dbad6fSeschrock if (size > zc->zc_nvlist_dst_size) { 1417be6fd75aSMatthew Ahrens error = SET_ERROR(ENOMEM); 1418e9dbad6fSeschrock } else { 14194445fffbSMatthew Ahrens packed = fnvlist_pack(nvl, &size); 14206e27f868SSam Falkner if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, 14216e27f868SSam Falkner size, zc->zc_iflags) != 0) 1422be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 14234445fffbSMatthew Ahrens fnvlist_pack_free(packed, size); 1424e9dbad6fSeschrock } 1425e9dbad6fSeschrock 1426e9dbad6fSeschrock zc->zc_nvlist_dst_size = size; 14274445fffbSMatthew Ahrens zc->zc_nvlist_dst_filled = B_TRUE; 1428e9dbad6fSeschrock return (error); 1429e9dbad6fSeschrock } 1430e9dbad6fSeschrock 1431dfc11533SChris Williamson int 1432dfc11533SChris Williamson getzfsvfs_impl(objset_t *os, zfsvfs_t **zfvp) 143314843421SMatthew Ahrens { 1434dfc11533SChris Williamson int error = 0; 1435503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1436be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1437503ad85cSMatthew Ahrens } 143814843421SMatthew Ahrens 1439503ad85cSMatthew Ahrens mutex_enter(&os->os_user_ptr_lock); 1440af4c679fSSean McEnroe *zfvp = dmu_objset_get_user(os); 1441af4c679fSSean McEnroe if (*zfvp) { 1442af4c679fSSean McEnroe VFS_HOLD((*zfvp)->z_vfs); 144314843421SMatthew Ahrens } else { 1444be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 144514843421SMatthew Ahrens } 1446503ad85cSMatthew Ahrens mutex_exit(&os->os_user_ptr_lock); 1447dfc11533SChris Williamson return (error); 1448dfc11533SChris Williamson } 1449dfc11533SChris Williamson 1450ed992b0aSSerapheim Dimitropoulos int 1451dfc11533SChris Williamson getzfsvfs(const char *dsname, zfsvfs_t **zfvp) 1452dfc11533SChris Williamson { 1453dfc11533SChris Williamson objset_t *os; 1454dfc11533SChris Williamson int error; 1455dfc11533SChris Williamson 1456dfc11533SChris Williamson error = dmu_objset_hold(dsname, FTAG, &os); 1457dfc11533SChris Williamson if (error != 0) 1458dfc11533SChris Williamson return (error); 1459dfc11533SChris Williamson 1460dfc11533SChris Williamson error = getzfsvfs_impl(os, zfvp); 1461503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 146214843421SMatthew Ahrens return (error); 146314843421SMatthew Ahrens } 146414843421SMatthew Ahrens 146514843421SMatthew Ahrens /* 146614843421SMatthew Ahrens * Find a zfsvfs_t for a mounted filesystem, or create our own, in which 146714843421SMatthew Ahrens * case its z_vfs will be NULL, and it will be opened as the owner. 1468ad135b5dSChristopher Siden * If 'writer' is set, the z_teardown_lock will be held for RW_WRITER, 1469ad135b5dSChristopher Siden * which prevents all vnode ops from running. 147014843421SMatthew Ahrens */ 147114843421SMatthew Ahrens static int 14721412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer) 147314843421SMatthew Ahrens { 147414843421SMatthew Ahrens int error = 0; 147514843421SMatthew Ahrens 1476af4c679fSSean McEnroe if (getzfsvfs(name, zfvp) != 0) 1477af4c679fSSean McEnroe error = zfsvfs_create(name, zfvp); 147814843421SMatthew Ahrens if (error == 0) { 1479c9030f6cSAlexander Motin rrm_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER : 14801412a1a2SMark Shellenbaum RW_READER, tag); 1481af4c679fSSean McEnroe if ((*zfvp)->z_unmounted) { 148214843421SMatthew Ahrens /* 148314843421SMatthew Ahrens * XXX we could probably try again, since the unmounting 148414843421SMatthew Ahrens * thread should be just about to disassociate the 148514843421SMatthew Ahrens * objset from the zfsvfs. 148614843421SMatthew Ahrens */ 1487c9030f6cSAlexander Motin rrm_exit(&(*zfvp)->z_teardown_lock, tag); 1488be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 148914843421SMatthew Ahrens } 149014843421SMatthew Ahrens } 149114843421SMatthew Ahrens return (error); 149214843421SMatthew Ahrens } 149314843421SMatthew Ahrens 149414843421SMatthew Ahrens static void 149514843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag) 149614843421SMatthew Ahrens { 1497c9030f6cSAlexander Motin rrm_exit(&zfsvfs->z_teardown_lock, tag); 149814843421SMatthew Ahrens 149914843421SMatthew Ahrens if (zfsvfs->z_vfs) { 150014843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 150114843421SMatthew Ahrens } else { 1502eb633035STom Caputi dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs); 150314843421SMatthew Ahrens zfsvfs_free(zfsvfs); 150414843421SMatthew Ahrens } 150514843421SMatthew Ahrens } 150614843421SMatthew Ahrens 1507fa9e4066Sahrens static int 1508fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc) 1509fa9e4066Sahrens { 1510fa9e4066Sahrens int error; 1511990b4856Slling nvlist_t *config, *props = NULL; 15120a48a24eStimh nvlist_t *rootprops = NULL; 15130a48a24eStimh nvlist_t *zplprops = NULL; 151404e56356SAndriy Gapon char *spa_name = zc->zc_name; 1515eb633035STom Caputi dsl_crypto_params_t *dcp = NULL; 1516fa9e4066Sahrens 1517990b4856Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1518478ed9adSEric Taylor zc->zc_iflags, &config)) 1519fa9e4066Sahrens return (error); 15202a6b87f0Sek 1521990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1522478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1523478ed9adSEric Taylor zc->zc_iflags, &props))) { 1524990b4856Slling nvlist_free(config); 1525990b4856Slling return (error); 1526990b4856Slling } 1527990b4856Slling 15280a48a24eStimh if (props) { 15290a48a24eStimh nvlist_t *nvl = NULL; 1530eb633035STom Caputi nvlist_t *hidden_args = NULL; 15310a48a24eStimh uint64_t version = SPA_VERSION; 153204e56356SAndriy Gapon char *tname; 15330a48a24eStimh 15340a48a24eStimh (void) nvlist_lookup_uint64(props, 15350a48a24eStimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version); 1536ad135b5dSChristopher Siden if (!SPA_VERSION_IS_SUPPORTED(version)) { 1537be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 15380a48a24eStimh goto pool_props_bad; 15390a48a24eStimh } 15400a48a24eStimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl); 15410a48a24eStimh if (nvl) { 15420a48a24eStimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP); 15430a48a24eStimh if (error != 0) { 15440a48a24eStimh nvlist_free(config); 15450a48a24eStimh nvlist_free(props); 15460a48a24eStimh return (error); 15470a48a24eStimh } 15480a48a24eStimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); 15490a48a24eStimh } 1550eb633035STom Caputi 1551eb633035STom Caputi (void) nvlist_lookup_nvlist(props, ZPOOL_HIDDEN_ARGS, 1552eb633035STom Caputi &hidden_args); 1553eb633035STom Caputi error = dsl_crypto_params_create_nvlist(DCP_CMD_NONE, 1554eb633035STom Caputi rootprops, hidden_args, &dcp); 1555eb633035STom Caputi if (error != 0) { 1556eb633035STom Caputi nvlist_free(config); 1557eb633035STom Caputi nvlist_free(props); 1558eb633035STom Caputi return (error); 1559eb633035STom Caputi } 1560eb633035STom Caputi (void) nvlist_remove_all(props, ZPOOL_HIDDEN_ARGS); 1561eb633035STom Caputi 15620a48a24eStimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 15630a48a24eStimh error = zfs_fill_zplprops_root(version, rootprops, 15640a48a24eStimh zplprops, NULL); 15653b2aab18SMatthew Ahrens if (error != 0) 15660a48a24eStimh goto pool_props_bad; 156704e56356SAndriy Gapon 156804e56356SAndriy Gapon if (nvlist_lookup_string(props, 156904e56356SAndriy Gapon zpool_prop_to_name(ZPOOL_PROP_TNAME), &tname) == 0) 157004e56356SAndriy Gapon spa_name = tname; 15710a48a24eStimh } 15720a48a24eStimh 1573eb633035STom Caputi error = spa_create(zc->zc_name, config, props, zplprops, dcp); 15740a48a24eStimh 15750a48a24eStimh /* 15760a48a24eStimh * Set the remaining root properties 15770a48a24eStimh */ 157804e56356SAndriy Gapon if (!error && (error = zfs_set_prop_nvlist(spa_name, 157992241e0bSTom Erickson ZPROP_SRC_LOCAL, rootprops, NULL)) != 0) 158004e56356SAndriy Gapon (void) spa_destroy(spa_name); 1581fa9e4066Sahrens 15820a48a24eStimh pool_props_bad: 15830a48a24eStimh nvlist_free(rootprops); 15840a48a24eStimh nvlist_free(zplprops); 1585fa9e4066Sahrens nvlist_free(config); 15860a48a24eStimh nvlist_free(props); 1587eb633035STom Caputi dsl_crypto_params_free(dcp, !!error); 1588990b4856Slling 1589fa9e4066Sahrens return (error); 1590fa9e4066Sahrens } 1591fa9e4066Sahrens 1592fa9e4066Sahrens static int 1593fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc) 1594fa9e4066Sahrens { 1595ecd6cf80Smarks int error; 1596ecd6cf80Smarks zfs_log_history(zc); 1597ecd6cf80Smarks error = spa_destroy(zc->zc_name); 1598681d9761SEric Taylor if (error == 0) 1599681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1600ecd6cf80Smarks return (error); 1601fa9e4066Sahrens } 1602fa9e4066Sahrens 1603fa9e4066Sahrens static int 1604fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc) 1605fa9e4066Sahrens { 1606990b4856Slling nvlist_t *config, *props = NULL; 1607fa9e4066Sahrens uint64_t guid; 1608468c413aSTim Haley int error; 1609fa9e4066Sahrens 1610990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1611478ed9adSEric Taylor zc->zc_iflags, &config)) != 0) 1612990b4856Slling return (error); 1613990b4856Slling 1614990b4856Slling if (zc->zc_nvlist_src_size != 0 && (error = 1615478ed9adSEric Taylor get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 1616478ed9adSEric Taylor zc->zc_iflags, &props))) { 1617990b4856Slling nvlist_free(config); 1618fa9e4066Sahrens return (error); 1619990b4856Slling } 1620fa9e4066Sahrens 1621fa9e4066Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 || 1622ea8dc4b6Seschrock guid != zc->zc_guid) 1623be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 1624fa9e4066Sahrens else 16254b964adaSGeorge Wilson error = spa_import(zc->zc_name, config, props, zc->zc_cookie); 1626fa9e4066Sahrens 16274b964adaSGeorge Wilson if (zc->zc_nvlist_dst != 0) { 16284b964adaSGeorge Wilson int err; 16294b964adaSGeorge Wilson 16304b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 16314b964adaSGeorge Wilson error = err; 16324b964adaSGeorge Wilson } 1633468c413aSTim Haley 1634fa9e4066Sahrens nvlist_free(config); 1635fa9e4066Sahrens 1636aab83bb8SJosef 'Jeff' Sipek nvlist_free(props); 1637990b4856Slling 1638fa9e4066Sahrens return (error); 1639fa9e4066Sahrens } 1640fa9e4066Sahrens 1641fa9e4066Sahrens static int 1642fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc) 1643fa9e4066Sahrens { 1644ecd6cf80Smarks int error; 164589a89ebfSlling boolean_t force = (boolean_t)zc->zc_cookie; 1646394ab0cbSGeorge Wilson boolean_t hardforce = (boolean_t)zc->zc_guid; 164789a89ebfSlling 1648ecd6cf80Smarks zfs_log_history(zc); 1649394ab0cbSGeorge Wilson error = spa_export(zc->zc_name, NULL, force, hardforce); 1650681d9761SEric Taylor if (error == 0) 1651681d9761SEric Taylor zvol_remove_minors(zc->zc_name); 1652ecd6cf80Smarks return (error); 1653fa9e4066Sahrens } 1654fa9e4066Sahrens 1655fa9e4066Sahrens static int 1656fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc) 1657fa9e4066Sahrens { 1658fa9e4066Sahrens nvlist_t *configs; 1659fa9e4066Sahrens int error; 1660fa9e4066Sahrens 1661fa9e4066Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) 1662be6fd75aSMatthew Ahrens return (SET_ERROR(EEXIST)); 1663fa9e4066Sahrens 1664e9dbad6fSeschrock error = put_nvlist(zc, configs); 1665fa9e4066Sahrens 1666fa9e4066Sahrens nvlist_free(configs); 1667fa9e4066Sahrens 1668fa9e4066Sahrens return (error); 1669fa9e4066Sahrens } 1670fa9e4066Sahrens 1671ad135b5dSChristopher Siden /* 1672ad135b5dSChristopher Siden * inputs: 1673ad135b5dSChristopher Siden * zc_name name of the pool 1674ad135b5dSChristopher Siden * 1675ad135b5dSChristopher Siden * outputs: 1676ad135b5dSChristopher Siden * zc_cookie real errno 1677ad135b5dSChristopher Siden * zc_nvlist_dst config nvlist 1678ad135b5dSChristopher Siden * zc_nvlist_dst_size size of config nvlist 1679ad135b5dSChristopher Siden */ 1680fa9e4066Sahrens static int 1681fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc) 1682fa9e4066Sahrens { 1683fa9e4066Sahrens nvlist_t *config; 1684fa9e4066Sahrens int error; 1685ea8dc4b6Seschrock int ret = 0; 1686fa9e4066Sahrens 1687e9dbad6fSeschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value, 1688e9dbad6fSeschrock sizeof (zc->zc_value)); 1689fa9e4066Sahrens 1690fa9e4066Sahrens if (config != NULL) { 1691e9dbad6fSeschrock ret = put_nvlist(zc, config); 1692fa9e4066Sahrens nvlist_free(config); 1693ea8dc4b6Seschrock 1694ea8dc4b6Seschrock /* 1695ea8dc4b6Seschrock * The config may be present even if 'error' is non-zero. 1696ea8dc4b6Seschrock * In this case we return success, and preserve the real errno 1697ea8dc4b6Seschrock * in 'zc_cookie'. 1698ea8dc4b6Seschrock */ 1699ea8dc4b6Seschrock zc->zc_cookie = error; 1700fa9e4066Sahrens } else { 1701ea8dc4b6Seschrock ret = error; 1702fa9e4066Sahrens } 1703fa9e4066Sahrens 1704ea8dc4b6Seschrock return (ret); 1705fa9e4066Sahrens } 1706fa9e4066Sahrens 1707fa9e4066Sahrens /* 1708fa9e4066Sahrens * Try to import the given pool, returning pool stats as appropriate so that 1709fa9e4066Sahrens * user land knows which devices are available and overall pool health. 1710fa9e4066Sahrens */ 1711fa9e4066Sahrens static int 1712fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc) 1713fa9e4066Sahrens { 1714fa9e4066Sahrens nvlist_t *tryconfig, *config; 1715fa9e4066Sahrens int error; 1716fa9e4066Sahrens 1717990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1718478ed9adSEric Taylor zc->zc_iflags, &tryconfig)) != 0) 1719fa9e4066Sahrens return (error); 1720fa9e4066Sahrens 1721fa9e4066Sahrens config = spa_tryimport(tryconfig); 1722fa9e4066Sahrens 1723fa9e4066Sahrens nvlist_free(tryconfig); 1724fa9e4066Sahrens 1725fa9e4066Sahrens if (config == NULL) 1726be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1727fa9e4066Sahrens 1728e9dbad6fSeschrock error = put_nvlist(zc, config); 1729fa9e4066Sahrens nvlist_free(config); 1730fa9e4066Sahrens 1731fa9e4066Sahrens return (error); 1732fa9e4066Sahrens } 1733fa9e4066Sahrens 17343f9d6ad7SLin Ling /* 17353f9d6ad7SLin Ling * inputs: 17363f9d6ad7SLin Ling * zc_name name of the pool 17373f9d6ad7SLin Ling * zc_cookie scan func (pool_scan_func_t) 17381702cce7SAlek Pinchuk * zc_flags scrub pause/resume flag (pool_scrub_cmd_t) 17393f9d6ad7SLin Ling */ 1740fa9e4066Sahrens static int 17413f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc) 1742fa9e4066Sahrens { 1743fa9e4066Sahrens spa_t *spa; 1744fa9e4066Sahrens int error; 1745fa9e4066Sahrens 174606eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 174706eeb2adSek return (error); 174806eeb2adSek 17491702cce7SAlek Pinchuk if (zc->zc_flags >= POOL_SCRUB_FLAGS_END) 17501702cce7SAlek Pinchuk return (SET_ERROR(EINVAL)); 17511702cce7SAlek Pinchuk 17521702cce7SAlek Pinchuk if (zc->zc_flags == POOL_SCRUB_PAUSE) 17531702cce7SAlek Pinchuk error = spa_scrub_pause_resume(spa, POOL_SCRUB_PAUSE); 17541702cce7SAlek Pinchuk else if (zc->zc_cookie == POOL_SCAN_NONE) 17553f9d6ad7SLin Ling error = spa_scan_stop(spa); 17563f9d6ad7SLin Ling else 17573f9d6ad7SLin Ling error = spa_scan(spa, zc->zc_cookie); 175806eeb2adSek 175906eeb2adSek spa_close(spa, FTAG); 176006eeb2adSek 1761fa9e4066Sahrens return (error); 1762fa9e4066Sahrens } 1763fa9e4066Sahrens 1764fa9e4066Sahrens static int 1765fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc) 1766fa9e4066Sahrens { 1767fa9e4066Sahrens spa_t *spa; 1768fa9e4066Sahrens int error; 1769fa9e4066Sahrens 1770fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1771fa9e4066Sahrens if (error == 0) { 1772fa9e4066Sahrens spa_freeze(spa); 1773fa9e4066Sahrens spa_close(spa, FTAG); 1774fa9e4066Sahrens } 1775fa9e4066Sahrens return (error); 1776fa9e4066Sahrens } 1777fa9e4066Sahrens 1778eaca9bbdSeschrock static int 1779eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc) 1780eaca9bbdSeschrock { 1781eaca9bbdSeschrock spa_t *spa; 1782eaca9bbdSeschrock int error; 1783eaca9bbdSeschrock 178406eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 178506eeb2adSek return (error); 178606eeb2adSek 1787ad135b5dSChristopher Siden if (zc->zc_cookie < spa_version(spa) || 1788ad135b5dSChristopher Siden !SPA_VERSION_IS_SUPPORTED(zc->zc_cookie)) { 1789558d2d50Slling spa_close(spa, FTAG); 1790be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1791558d2d50Slling } 1792558d2d50Slling 1793990b4856Slling spa_upgrade(spa, zc->zc_cookie); 179406eeb2adSek spa_close(spa, FTAG); 179506eeb2adSek 179606eeb2adSek return (error); 179706eeb2adSek } 179806eeb2adSek 179906eeb2adSek static int 180006eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc) 180106eeb2adSek { 180206eeb2adSek spa_t *spa; 180306eeb2adSek char *hist_buf; 180406eeb2adSek uint64_t size; 180506eeb2adSek int error; 180606eeb2adSek 180706eeb2adSek if ((size = zc->zc_history_len) == 0) 1808be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 180906eeb2adSek 181006eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 181106eeb2adSek return (error); 181206eeb2adSek 1813e7437265Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 1814d7306b64Sek spa_close(spa, FTAG); 1815be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 1816d7306b64Sek } 1817d7306b64Sek 181806eeb2adSek hist_buf = kmem_alloc(size, KM_SLEEP); 181906eeb2adSek if ((error = spa_history_get(spa, &zc->zc_history_offset, 182006eeb2adSek &zc->zc_history_len, hist_buf)) == 0) { 1821478ed9adSEric Taylor error = ddi_copyout(hist_buf, 1822478ed9adSEric Taylor (void *)(uintptr_t)zc->zc_history, 1823478ed9adSEric Taylor zc->zc_history_len, zc->zc_iflags); 182406eeb2adSek } 182506eeb2adSek 182606eeb2adSek spa_close(spa, FTAG); 182706eeb2adSek kmem_free(hist_buf, size); 182806eeb2adSek return (error); 182906eeb2adSek } 183006eeb2adSek 1831e9103aaeSGarrett D'Amore static int 1832e9103aaeSGarrett D'Amore zfs_ioc_pool_reguid(zfs_cmd_t *zc) 1833e9103aaeSGarrett D'Amore { 1834e9103aaeSGarrett D'Amore spa_t *spa; 1835e9103aaeSGarrett D'Amore int error; 1836e9103aaeSGarrett D'Amore 1837e9103aaeSGarrett D'Amore error = spa_open(zc->zc_name, &spa, FTAG); 1838e9103aaeSGarrett D'Amore if (error == 0) { 1839e9103aaeSGarrett D'Amore error = spa_change_guid(spa); 1840e9103aaeSGarrett D'Amore spa_close(spa, FTAG); 1841e9103aaeSGarrett D'Amore } 1842e9103aaeSGarrett D'Amore return (error); 1843e9103aaeSGarrett D'Amore } 1844e9103aaeSGarrett D'Amore 184555434c77Sek static int 184655434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc) 184755434c77Sek { 18483b2aab18SMatthew Ahrens return (dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value)); 184955434c77Sek } 185055434c77Sek 1851503ad85cSMatthew Ahrens /* 1852503ad85cSMatthew Ahrens * inputs: 1853503ad85cSMatthew Ahrens * zc_name name of filesystem 1854503ad85cSMatthew Ahrens * zc_obj object to find 1855503ad85cSMatthew Ahrens * 1856503ad85cSMatthew Ahrens * outputs: 1857503ad85cSMatthew Ahrens * zc_value name of object 1858503ad85cSMatthew Ahrens */ 185955434c77Sek static int 186055434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc) 186155434c77Sek { 1862503ad85cSMatthew Ahrens objset_t *os; 186355434c77Sek int error; 186455434c77Sek 1865503ad85cSMatthew Ahrens /* XXX reading from objset not owned */ 1866eb633035STom Caputi if ((error = dmu_objset_hold_flags(zc->zc_name, B_TRUE, 1867eb633035STom Caputi FTAG, &os)) != 0) 186855434c77Sek return (error); 1869503ad85cSMatthew Ahrens if (dmu_objset_type(os) != DMU_OST_ZFS) { 1870eb633035STom Caputi dmu_objset_rele_flags(os, B_TRUE, FTAG); 1871be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 1872503ad85cSMatthew Ahrens } 1873503ad85cSMatthew Ahrens error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value, 187455434c77Sek sizeof (zc->zc_value)); 1875eb633035STom Caputi dmu_objset_rele_flags(os, B_TRUE, FTAG); 187655434c77Sek 187755434c77Sek return (error); 187855434c77Sek } 187955434c77Sek 188099d5e173STim Haley /* 188199d5e173STim Haley * inputs: 188299d5e173STim Haley * zc_name name of filesystem 188399d5e173STim Haley * zc_obj object to find 188499d5e173STim Haley * 188599d5e173STim Haley * outputs: 188699d5e173STim Haley * zc_stat stats on object 188799d5e173STim Haley * zc_value path to object 188899d5e173STim Haley */ 188999d5e173STim Haley static int 189099d5e173STim Haley zfs_ioc_obj_to_stats(zfs_cmd_t *zc) 189199d5e173STim Haley { 189299d5e173STim Haley objset_t *os; 189399d5e173STim Haley int error; 189499d5e173STim Haley 189599d5e173STim Haley /* XXX reading from objset not owned */ 1896eb633035STom Caputi if ((error = dmu_objset_hold_flags(zc->zc_name, B_TRUE, 1897eb633035STom Caputi FTAG, &os)) != 0) 189899d5e173STim Haley return (error); 189999d5e173STim Haley if (dmu_objset_type(os) != DMU_OST_ZFS) { 1900eb633035STom Caputi dmu_objset_rele_flags(os, B_TRUE, FTAG); 1901be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 190299d5e173STim Haley } 190399d5e173STim Haley error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value, 190499d5e173STim Haley sizeof (zc->zc_value)); 1905eb633035STom Caputi dmu_objset_rele_flags(os, B_TRUE, FTAG); 190699d5e173STim Haley 190799d5e173STim Haley return (error); 190899d5e173STim Haley } 190999d5e173STim Haley 1910fa9e4066Sahrens static int 1911fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc) 1912fa9e4066Sahrens { 1913fa9e4066Sahrens spa_t *spa; 1914fa9e4066Sahrens int error; 1915e7cbe64fSgw nvlist_t *config, **l2cache, **spares; 1916e7cbe64fSgw uint_t nl2cache = 0, nspares = 0; 1917fa9e4066Sahrens 1918fa9e4066Sahrens error = spa_open(zc->zc_name, &spa, FTAG); 1919fa9e4066Sahrens if (error != 0) 1920fa9e4066Sahrens return (error); 1921fa9e4066Sahrens 1922fa94a07fSbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 1923478ed9adSEric Taylor zc->zc_iflags, &config); 1924fa94a07fSbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, 1925fa94a07fSbrendan &l2cache, &nl2cache); 1926fa94a07fSbrendan 1927e7cbe64fSgw (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, 1928e7cbe64fSgw &spares, &nspares); 1929e7cbe64fSgw 1930b1b8ab34Slling /* 1931b1b8ab34Slling * A root pool with concatenated devices is not supported. 1932e7cbe64fSgw * Thus, can not add a device to a root pool. 1933e7cbe64fSgw * 1934e7cbe64fSgw * Intent log device can not be added to a rootpool because 1935e7cbe64fSgw * during mountroot, zil is replayed, a seperated log device 1936e7cbe64fSgw * can not be accessed during the mountroot time. 1937e7cbe64fSgw * 1938e7cbe64fSgw * l2cache and spare devices are ok to be added to a rootpool. 1939b1b8ab34Slling */ 1940b24ab676SJeff Bonwick if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) { 19411195e687SMark J Musante nvlist_free(config); 1942b1b8ab34Slling spa_close(spa, FTAG); 1943be6fd75aSMatthew Ahrens return (SET_ERROR(EDOM)); 1944b1b8ab34Slling } 1945b1b8ab34Slling 1946fa94a07fSbrendan if (error == 0) { 1947fa9e4066Sahrens error = spa_vdev_add(spa, config); 1948fa9e4066Sahrens nvlist_free(config); 1949fa9e4066Sahrens } 1950fa9e4066Sahrens spa_close(spa, FTAG); 1951fa9e4066Sahrens return (error); 1952fa9e4066Sahrens } 1953fa9e4066Sahrens 19543f9d6ad7SLin Ling /* 19553f9d6ad7SLin Ling * inputs: 19563f9d6ad7SLin Ling * zc_name name of the pool 19575cabbc6bSPrashanth Sreenivasa * zc_guid guid of vdev to remove 19585cabbc6bSPrashanth Sreenivasa * zc_cookie cancel removal 19593f9d6ad7SLin Ling */ 1960fa9e4066Sahrens static int 1961fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc) 1962fa9e4066Sahrens { 196399653d4eSeschrock spa_t *spa; 196499653d4eSeschrock int error; 196599653d4eSeschrock 196699653d4eSeschrock error = spa_open(zc->zc_name, &spa, FTAG); 196799653d4eSeschrock if (error != 0) 196899653d4eSeschrock return (error); 19695cabbc6bSPrashanth Sreenivasa if (zc->zc_cookie != 0) { 19705cabbc6bSPrashanth Sreenivasa error = spa_vdev_remove_cancel(spa); 19715cabbc6bSPrashanth Sreenivasa } else { 19725cabbc6bSPrashanth Sreenivasa error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE); 19735cabbc6bSPrashanth Sreenivasa } 197499653d4eSeschrock spa_close(spa, FTAG); 197599653d4eSeschrock return (error); 1976fa9e4066Sahrens } 1977fa9e4066Sahrens 1978fa9e4066Sahrens static int 19793d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc) 1980fa9e4066Sahrens { 1981fa9e4066Sahrens spa_t *spa; 1982fa9e4066Sahrens int error; 19833d7072f8Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN; 1984fa9e4066Sahrens 198506eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 1986fa9e4066Sahrens return (error); 19873d7072f8Seschrock switch (zc->zc_cookie) { 19883d7072f8Seschrock case VDEV_STATE_ONLINE: 19893d7072f8Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate); 19903d7072f8Seschrock break; 1991fa9e4066Sahrens 19923d7072f8Seschrock case VDEV_STATE_OFFLINE: 19933d7072f8Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj); 19943d7072f8Seschrock break; 1995fa9e4066Sahrens 19963d7072f8Seschrock case VDEV_STATE_FAULTED: 1997069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 1998069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 1999069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 2000069f55e2SEric Schrock 2001069f55e2SEric Schrock error = vdev_fault(spa, zc->zc_guid, zc->zc_obj); 20023d7072f8Seschrock break; 20033d7072f8Seschrock 20043d7072f8Seschrock case VDEV_STATE_DEGRADED: 2005069f55e2SEric Schrock if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED && 2006069f55e2SEric Schrock zc->zc_obj != VDEV_AUX_EXTERNAL) 2007069f55e2SEric Schrock zc->zc_obj = VDEV_AUX_ERR_EXCEEDED; 2008069f55e2SEric Schrock 2009069f55e2SEric Schrock error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj); 20103d7072f8Seschrock break; 20113d7072f8Seschrock 20123d7072f8Seschrock default: 2013be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 20143d7072f8Seschrock } 20153d7072f8Seschrock zc->zc_cookie = newstate; 2016fa9e4066Sahrens spa_close(spa, FTAG); 2017fa9e4066Sahrens return (error); 2018fa9e4066Sahrens } 2019fa9e4066Sahrens 2020fa9e4066Sahrens static int 2021fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc) 2022fa9e4066Sahrens { 2023fa9e4066Sahrens spa_t *spa; 2024fa9e4066Sahrens int replacing = zc->zc_cookie; 2025fa9e4066Sahrens nvlist_t *config; 2026fa9e4066Sahrens int error; 2027fa9e4066Sahrens 202806eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 2029fa9e4066Sahrens return (error); 2030fa9e4066Sahrens 2031990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 2032478ed9adSEric Taylor zc->zc_iflags, &config)) == 0) { 2033ea8dc4b6Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); 2034fa9e4066Sahrens nvlist_free(config); 2035fa9e4066Sahrens } 2036fa9e4066Sahrens 2037fa9e4066Sahrens spa_close(spa, FTAG); 2038fa9e4066Sahrens return (error); 2039fa9e4066Sahrens } 2040fa9e4066Sahrens 2041fa9e4066Sahrens static int 2042fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc) 2043fa9e4066Sahrens { 2044fa9e4066Sahrens spa_t *spa; 2045fa9e4066Sahrens int error; 2046fa9e4066Sahrens 204706eeb2adSek if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 2048fa9e4066Sahrens return (error); 2049fa9e4066Sahrens 20508ad4d6ddSJeff Bonwick error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); 2051fa9e4066Sahrens 2052fa9e4066Sahrens spa_close(spa, FTAG); 2053fa9e4066Sahrens return (error); 2054fa9e4066Sahrens } 2055fa9e4066Sahrens 20561195e687SMark J Musante static int 20571195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc) 20581195e687SMark J Musante { 20591195e687SMark J Musante spa_t *spa; 20601195e687SMark J Musante nvlist_t *config, *props = NULL; 20611195e687SMark J Musante int error; 20621195e687SMark J Musante boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT); 20631195e687SMark J Musante 20641195e687SMark J Musante if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 20651195e687SMark J Musante return (error); 20661195e687SMark J Musante 20671195e687SMark J Musante if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 20681195e687SMark J Musante zc->zc_iflags, &config)) { 20691195e687SMark J Musante spa_close(spa, FTAG); 20701195e687SMark J Musante return (error); 20711195e687SMark J Musante } 20721195e687SMark J Musante 20731195e687SMark J Musante if (zc->zc_nvlist_src_size != 0 && (error = 20741195e687SMark J Musante get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 20751195e687SMark J Musante zc->zc_iflags, &props))) { 20761195e687SMark J Musante spa_close(spa, FTAG); 20771195e687SMark J Musante nvlist_free(config); 20781195e687SMark J Musante return (error); 20791195e687SMark J Musante } 20801195e687SMark J Musante 20811195e687SMark J Musante error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp); 20821195e687SMark J Musante 20831195e687SMark J Musante spa_close(spa, FTAG); 20841195e687SMark J Musante 20851195e687SMark J Musante nvlist_free(config); 20861195e687SMark J Musante nvlist_free(props); 20871195e687SMark J Musante 20881195e687SMark J Musante return (error); 20891195e687SMark J Musante } 20901195e687SMark J Musante 2091c67d9675Seschrock static int 2092c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc) 2093c67d9675Seschrock { 2094c67d9675Seschrock spa_t *spa; 2095e9dbad6fSeschrock char *path = zc->zc_value; 2096ea8dc4b6Seschrock uint64_t guid = zc->zc_guid; 2097c67d9675Seschrock int error; 2098c67d9675Seschrock 2099c67d9675Seschrock error = spa_open(zc->zc_name, &spa, FTAG); 2100c67d9675Seschrock if (error != 0) 2101c67d9675Seschrock return (error); 2102c67d9675Seschrock 2103c67d9675Seschrock error = spa_vdev_setpath(spa, guid, path); 2104c67d9675Seschrock spa_close(spa, FTAG); 2105c67d9675Seschrock return (error); 2106c67d9675Seschrock } 2107c67d9675Seschrock 21086809eb4eSEric Schrock static int 21096809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc) 21106809eb4eSEric Schrock { 21116809eb4eSEric Schrock spa_t *spa; 21126809eb4eSEric Schrock char *fru = zc->zc_value; 21136809eb4eSEric Schrock uint64_t guid = zc->zc_guid; 21146809eb4eSEric Schrock int error; 21156809eb4eSEric Schrock 21166809eb4eSEric Schrock error = spa_open(zc->zc_name, &spa, FTAG); 21176809eb4eSEric Schrock if (error != 0) 21186809eb4eSEric Schrock return (error); 21196809eb4eSEric Schrock 21206809eb4eSEric Schrock error = spa_vdev_setfru(spa, guid, fru); 21216809eb4eSEric Schrock spa_close(spa, FTAG); 21226809eb4eSEric Schrock return (error); 21236809eb4eSEric Schrock } 21246809eb4eSEric Schrock 2125fa9e4066Sahrens static int 2126a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os) 2127fa9e4066Sahrens { 2128a7f53a56SChris Kirby int error = 0; 21297f7322feSeschrock nvlist_t *nv; 2130fa9e4066Sahrens 2131a2eea2e1Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2132fa9e4066Sahrens 21335ad82045Snd if (zc->zc_nvlist_dst != 0 && 213492241e0bSTom Erickson (error = dsl_prop_get_all(os, &nv)) == 0) { 2135a2eea2e1Sahrens dmu_objset_stats(os, nv); 2136432f72fdSahrens /* 2137bd00f61bSrm * NB: zvol_get_stats() will read the objset contents, 2138432f72fdSahrens * which we aren't supposed to do with a 2139745cd3c5Smaybee * DS_MODE_USER hold, because it could be 2140432f72fdSahrens * inconsistent. So this is a bit of a workaround... 2141503ad85cSMatthew Ahrens * XXX reading with out owning 2142432f72fdSahrens */ 214319b94df9SMatthew Ahrens if (!zc->zc_objset_stats.dds_inconsistent && 214419b94df9SMatthew Ahrens dmu_objset_type(os) == DMU_OST_ZVOL) { 214519b94df9SMatthew Ahrens error = zvol_get_stats(os, nv); 214619b94df9SMatthew Ahrens if (error == EIO) 214719b94df9SMatthew Ahrens return (error); 2148fb09f5aaSMadhav Suresh VERIFY0(error); 2149e7437265Sahrens } 2150e9dbad6fSeschrock error = put_nvlist(zc, nv); 21517f7322feSeschrock nvlist_free(nv); 21527f7322feSeschrock } 2153fa9e4066Sahrens 2154a7f53a56SChris Kirby return (error); 2155a7f53a56SChris Kirby } 2156a7f53a56SChris Kirby 2157a7f53a56SChris Kirby /* 2158a7f53a56SChris Kirby * inputs: 2159a7f53a56SChris Kirby * zc_name name of filesystem 2160a7f53a56SChris Kirby * zc_nvlist_dst_size size of buffer for property nvlist 2161a7f53a56SChris Kirby * 2162a7f53a56SChris Kirby * outputs: 2163a7f53a56SChris Kirby * zc_objset_stats stats 2164a7f53a56SChris Kirby * zc_nvlist_dst property nvlist 2165a7f53a56SChris Kirby * zc_nvlist_dst_size size of property nvlist 2166a7f53a56SChris Kirby */ 2167a7f53a56SChris Kirby static int 2168a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc) 2169a7f53a56SChris Kirby { 21703b2aab18SMatthew Ahrens objset_t *os; 2171a7f53a56SChris Kirby int error; 2172a7f53a56SChris Kirby 21733b2aab18SMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 21743b2aab18SMatthew Ahrens if (error == 0) { 21753b2aab18SMatthew Ahrens error = zfs_ioc_objset_stats_impl(zc, os); 21763b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 21773b2aab18SMatthew Ahrens } 2178a7f53a56SChris Kirby 2179fa9e4066Sahrens return (error); 2180fa9e4066Sahrens } 2181fa9e4066Sahrens 218292241e0bSTom Erickson /* 218392241e0bSTom Erickson * inputs: 218492241e0bSTom Erickson * zc_name name of filesystem 218592241e0bSTom Erickson * zc_nvlist_dst_size size of buffer for property nvlist 218692241e0bSTom Erickson * 218792241e0bSTom Erickson * outputs: 218892241e0bSTom Erickson * zc_nvlist_dst received property nvlist 218992241e0bSTom Erickson * zc_nvlist_dst_size size of received property nvlist 219092241e0bSTom Erickson * 219192241e0bSTom Erickson * Gets received properties (distinct from local properties on or after 219292241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from 219392241e0bSTom Erickson * local property values. 219492241e0bSTom Erickson */ 219592241e0bSTom Erickson static int 219692241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc) 219792241e0bSTom Erickson { 21983b2aab18SMatthew Ahrens int error = 0; 219992241e0bSTom Erickson nvlist_t *nv; 220092241e0bSTom Erickson 220192241e0bSTom Erickson /* 220292241e0bSTom Erickson * Without this check, we would return local property values if the 220392241e0bSTom Erickson * caller has not already received properties on or after 220492241e0bSTom Erickson * SPA_VERSION_RECVD_PROPS. 220592241e0bSTom Erickson */ 22063b2aab18SMatthew Ahrens if (!dsl_prop_get_hasrecvd(zc->zc_name)) 2207be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 220892241e0bSTom Erickson 220992241e0bSTom Erickson if (zc->zc_nvlist_dst != 0 && 22103b2aab18SMatthew Ahrens (error = dsl_prop_get_received(zc->zc_name, &nv)) == 0) { 221192241e0bSTom Erickson error = put_nvlist(zc, nv); 221292241e0bSTom Erickson nvlist_free(nv); 221392241e0bSTom Erickson } 221492241e0bSTom Erickson 221592241e0bSTom Erickson return (error); 221692241e0bSTom Erickson } 221792241e0bSTom Erickson 2218de8267e0Stimh static int 2219de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop) 2220de8267e0Stimh { 2221de8267e0Stimh uint64_t value; 2222de8267e0Stimh int error; 2223de8267e0Stimh 2224de8267e0Stimh /* 2225de8267e0Stimh * zfs_get_zplprop() will either find a value or give us 2226de8267e0Stimh * the default value (if there is one). 2227de8267e0Stimh */ 2228de8267e0Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0) 2229de8267e0Stimh return (error); 2230de8267e0Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0); 2231de8267e0Stimh return (0); 2232de8267e0Stimh } 2233de8267e0Stimh 22343cb34c60Sahrens /* 22353cb34c60Sahrens * inputs: 22363cb34c60Sahrens * zc_name name of filesystem 2237de8267e0Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist 22383cb34c60Sahrens * 22393cb34c60Sahrens * outputs: 2240de8267e0Stimh * zc_nvlist_dst zpl property nvlist 2241de8267e0Stimh * zc_nvlist_dst_size size of zpl property nvlist 22423cb34c60Sahrens */ 2243bd00f61bSrm static int 2244de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc) 2245bd00f61bSrm { 2246de8267e0Stimh objset_t *os; 2247de8267e0Stimh int err; 2248bd00f61bSrm 2249503ad85cSMatthew Ahrens /* XXX reading without owning */ 2250503ad85cSMatthew Ahrens if (err = dmu_objset_hold(zc->zc_name, FTAG, &os)) 2251de8267e0Stimh return (err); 2252bd00f61bSrm 2253bd00f61bSrm dmu_objset_fast_stat(os, &zc->zc_objset_stats); 2254bd00f61bSrm 2255bd00f61bSrm /* 2256de8267e0Stimh * NB: nvl_add_zplprop() will read the objset contents, 2257745cd3c5Smaybee * which we aren't supposed to do with a DS_MODE_USER 2258745cd3c5Smaybee * hold, because it could be inconsistent. 2259bd00f61bSrm */ 2260dd328bf6SToomas Soome if (zc->zc_nvlist_dst != 0 && 2261de8267e0Stimh !zc->zc_objset_stats.dds_inconsistent && 2262de8267e0Stimh dmu_objset_type(os) == DMU_OST_ZFS) { 2263de8267e0Stimh nvlist_t *nv; 2264de8267e0Stimh 2265de8267e0Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); 2266de8267e0Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 && 2267de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 && 2268de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 && 2269de8267e0Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0) 2270de8267e0Stimh err = put_nvlist(zc, nv); 2271de8267e0Stimh nvlist_free(nv); 2272de8267e0Stimh } else { 2273be6fd75aSMatthew Ahrens err = SET_ERROR(ENOENT); 2274de8267e0Stimh } 2275503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2276de8267e0Stimh return (err); 2277bd00f61bSrm } 2278bd00f61bSrm 227914843421SMatthew Ahrens static boolean_t 228014843421SMatthew Ahrens dataset_name_hidden(const char *name) 228114843421SMatthew Ahrens { 228214843421SMatthew Ahrens /* 228314843421SMatthew Ahrens * Skip over datasets that are not visible in this zone, 228414843421SMatthew Ahrens * internal datasets (which have a $ in their name), and 228514843421SMatthew Ahrens * temporary datasets (which have a % in their name). 228614843421SMatthew Ahrens */ 228714843421SMatthew Ahrens if (strchr(name, '$') != NULL) 228814843421SMatthew Ahrens return (B_TRUE); 228914843421SMatthew Ahrens if (strchr(name, '%') != NULL) 229014843421SMatthew Ahrens return (B_TRUE); 229114843421SMatthew Ahrens if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) 229214843421SMatthew Ahrens return (B_TRUE); 229314843421SMatthew Ahrens return (B_FALSE); 229414843421SMatthew Ahrens } 229514843421SMatthew Ahrens 2296de8267e0Stimh /* 2297de8267e0Stimh * inputs: 2298de8267e0Stimh * zc_name name of filesystem 2299de8267e0Stimh * zc_cookie zap cursor 2300de8267e0Stimh * zc_nvlist_dst_size size of buffer for property nvlist 2301de8267e0Stimh * 2302de8267e0Stimh * outputs: 2303de8267e0Stimh * zc_name name of next filesystem 230414843421SMatthew Ahrens * zc_cookie zap cursor 2305de8267e0Stimh * zc_objset_stats stats 2306de8267e0Stimh * zc_nvlist_dst property nvlist 2307de8267e0Stimh * zc_nvlist_dst_size size of property nvlist 2308de8267e0Stimh */ 2309fa9e4066Sahrens static int 2310fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc) 2311fa9e4066Sahrens { 231287e5029aSahrens objset_t *os; 2313fa9e4066Sahrens int error; 2314fa9e4066Sahrens char *p; 2315620252bcSChris Kirby size_t orig_len = strlen(zc->zc_name); 2316fa9e4066Sahrens 2317620252bcSChris Kirby top: 2318503ad85cSMatthew Ahrens if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) { 231987e5029aSahrens if (error == ENOENT) 2320be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 232187e5029aSahrens return (error); 2322fa9e4066Sahrens } 2323fa9e4066Sahrens 2324fa9e4066Sahrens p = strrchr(zc->zc_name, '/'); 2325fa9e4066Sahrens if (p == NULL || p[1] != '\0') 2326fa9e4066Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name)); 2327fa9e4066Sahrens p = zc->zc_name + strlen(zc->zc_name); 2328fa9e4066Sahrens 2329fa9e4066Sahrens do { 233087e5029aSahrens error = dmu_dir_list_next(os, 233187e5029aSahrens sizeof (zc->zc_name) - (p - zc->zc_name), p, 233287e5029aSahrens NULL, &zc->zc_cookie); 2333fa9e4066Sahrens if (error == ENOENT) 2334be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 233519b94df9SMatthew Ahrens } while (error == 0 && dataset_name_hidden(zc->zc_name)); 2336503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2337fa9e4066Sahrens 2338681d9761SEric Taylor /* 2339681d9761SEric Taylor * If it's an internal dataset (ie. with a '$' in its name), 2340681d9761SEric Taylor * don't try to get stats for it, otherwise we'll return ENOENT. 2341681d9761SEric Taylor */ 2342620252bcSChris Kirby if (error == 0 && strchr(zc->zc_name, '$') == NULL) { 234387e5029aSahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */ 2344620252bcSChris Kirby if (error == ENOENT) { 2345620252bcSChris Kirby /* We lost a race with destroy, get the next one. */ 2346620252bcSChris Kirby zc->zc_name[orig_len] = '\0'; 2347620252bcSChris Kirby goto top; 2348620252bcSChris Kirby } 2349620252bcSChris Kirby } 2350fa9e4066Sahrens return (error); 2351fa9e4066Sahrens } 2352fa9e4066Sahrens 23533cb34c60Sahrens /* 23543cb34c60Sahrens * inputs: 23553cb34c60Sahrens * zc_name name of filesystem 23563cb34c60Sahrens * zc_cookie zap cursor 23573cb34c60Sahrens * zc_nvlist_dst_size size of buffer for property nvlist 23580d8fa8f8SMartin Matuska * zc_simple when set, only name is requested 23593cb34c60Sahrens * 23603cb34c60Sahrens * outputs: 23613cb34c60Sahrens * zc_name name of next snapshot 23623cb34c60Sahrens * zc_objset_stats stats 23633cb34c60Sahrens * zc_nvlist_dst property nvlist 23643cb34c60Sahrens * zc_nvlist_dst_size size of property nvlist 23653cb34c60Sahrens */ 2366fa9e4066Sahrens static int 2367fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) 2368fa9e4066Sahrens { 236987e5029aSahrens objset_t *os; 2370fa9e4066Sahrens int error; 2371fa9e4066Sahrens 2372503ad85cSMatthew Ahrens error = dmu_objset_hold(zc->zc_name, FTAG, &os); 23733b2aab18SMatthew Ahrens if (error != 0) { 2374745cd3c5Smaybee return (error == ENOENT ? ESRCH : error); 23753b2aab18SMatthew Ahrens } 2376fa9e4066Sahrens 2377b81d61a6Slling /* 2378b81d61a6Slling * A dataset name of maximum length cannot have any snapshots, 2379b81d61a6Slling * so exit immediately. 2380b81d61a6Slling */ 23819adfa60dSMatthew Ahrens if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= 23829adfa60dSMatthew Ahrens ZFS_MAX_DATASET_NAME_LEN) { 2383503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 2384be6fd75aSMatthew Ahrens return (SET_ERROR(ESRCH)); 2385fa9e4066Sahrens } 2386fa9e4066Sahrens 238787e5029aSahrens error = dmu_snapshot_list_next(os, 238887e5029aSahrens sizeof (zc->zc_name) - strlen(zc->zc_name), 2389a7f53a56SChris Kirby zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie, 2390a7f53a56SChris Kirby NULL); 2391a7f53a56SChris Kirby 23920d8fa8f8SMartin Matuska if (error == 0 && !zc->zc_simple) { 2393a7f53a56SChris Kirby dsl_dataset_t *ds; 2394a7f53a56SChris Kirby dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool; 2395a7f53a56SChris Kirby 2396a7f53a56SChris Kirby error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds); 23973b2aab18SMatthew Ahrens if (error == 0) { 2398a7f53a56SChris Kirby objset_t *ossnap; 2399a7f53a56SChris Kirby 2400a7f53a56SChris Kirby error = dmu_objset_from_ds(ds, &ossnap); 2401a7f53a56SChris Kirby if (error == 0) 2402a7f53a56SChris Kirby error = zfs_ioc_objset_stats_impl(zc, ossnap); 2403a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 2404620252bcSChris Kirby } 2405620252bcSChris Kirby } else if (error == ENOENT) { 2406be6fd75aSMatthew Ahrens error = SET_ERROR(ESRCH); 2407620252bcSChris Kirby } 2408fa9e4066Sahrens 2409a7f53a56SChris Kirby dmu_objset_rele(os, FTAG); 24103cb34c60Sahrens /* if we failed, undo the @ that we tacked on to zc_name */ 24113b2aab18SMatthew Ahrens if (error != 0) 24123cb34c60Sahrens *strchr(zc->zc_name, '@') = '\0'; 2413fa9e4066Sahrens return (error); 2414fa9e4066Sahrens } 2415fa9e4066Sahrens 241692241e0bSTom Erickson static int 241792241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair) 2418fa9e4066Sahrens { 241992241e0bSTom Erickson const char *propname = nvpair_name(pair); 242092241e0bSTom Erickson uint64_t *valary; 242192241e0bSTom Erickson unsigned int vallen; 242292241e0bSTom Erickson const char *domain; 2423eeb85002STim Haley char *dash; 242492241e0bSTom Erickson zfs_userquota_prop_t type; 242592241e0bSTom Erickson uint64_t rid; 242692241e0bSTom Erickson uint64_t quota; 242792241e0bSTom Erickson zfsvfs_t *zfsvfs; 242892241e0bSTom Erickson int err; 242992241e0bSTom Erickson 243092241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 243192241e0bSTom Erickson nvlist_t *attrs; 243292241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 2433eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2434eeb85002STim Haley &pair) != 0) 2435be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 243692241e0bSTom Erickson } 2437e9dbad6fSeschrock 2438ecd6cf80Smarks /* 2439eeb85002STim Haley * A correctly constructed propname is encoded as 244092241e0bSTom Erickson * userquota@<rid>-<domain>. 2441ecd6cf80Smarks */ 2442eeb85002STim Haley if ((dash = strchr(propname, '-')) == NULL || 2443eeb85002STim Haley nvpair_value_uint64_array(pair, &valary, &vallen) != 0 || 2444eeb85002STim Haley vallen != 3) 2445be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2446eeb85002STim Haley 2447eeb85002STim Haley domain = dash + 1; 2448eeb85002STim Haley type = valary[0]; 2449eeb85002STim Haley rid = valary[1]; 2450eeb85002STim Haley quota = valary[2]; 2451e9dbad6fSeschrock 24521412a1a2SMark Shellenbaum err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE); 245392241e0bSTom Erickson if (err == 0) { 245492241e0bSTom Erickson err = zfs_set_userquota(zfsvfs, type, domain, rid, quota); 245592241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 245692241e0bSTom Erickson } 2457e9dbad6fSeschrock 245892241e0bSTom Erickson return (err); 245992241e0bSTom Erickson } 246014843421SMatthew Ahrens 246192241e0bSTom Erickson /* 246292241e0bSTom Erickson * If the named property is one that has a special function to set its value, 246392241e0bSTom Erickson * return 0 on success and a positive error code on failure; otherwise if it is 246492241e0bSTom Erickson * not one of the special properties handled by this function, return -1. 246592241e0bSTom Erickson * 2466eeb85002STim Haley * XXX: It would be better for callers of the property interface if we handled 246792241e0bSTom Erickson * these special cases in dsl_prop.c (in the dsl layer). 246892241e0bSTom Erickson */ 246992241e0bSTom Erickson static int 247092241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source, 247192241e0bSTom Erickson nvpair_t *pair) 247292241e0bSTom Erickson { 247392241e0bSTom Erickson const char *propname = nvpair_name(pair); 247492241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2475eb633035STom Caputi uint64_t intval = 0; 2476eb633035STom Caputi char *strval = NULL; 2477b5152584SMatthew Ahrens int err = -1; 2478fa9e4066Sahrens 247992241e0bSTom Erickson if (prop == ZPROP_INVAL) { 248092241e0bSTom Erickson if (zfs_prop_userquota(propname)) 248192241e0bSTom Erickson return (zfs_prop_set_userquota(dsname, pair)); 248292241e0bSTom Erickson return (-1); 248392241e0bSTom Erickson } 248414843421SMatthew Ahrens 248592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 248692241e0bSTom Erickson nvlist_t *attrs; 248792241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 248892241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 248992241e0bSTom Erickson &pair) == 0); 249092241e0bSTom Erickson } 2491db870a07Sahrens 2492eb633035STom Caputi /* all special properties are numeric except for keylocation */ 2493eb633035STom Caputi if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { 2494eb633035STom Caputi strval = fnvpair_value_string(pair); 2495eb633035STom Caputi } else { 2496eb633035STom Caputi intval = fnvpair_value_uint64(pair); 2497eb633035STom Caputi } 249840feaa91Sahrens 249992241e0bSTom Erickson switch (prop) { 250092241e0bSTom Erickson case ZFS_PROP_QUOTA: 250192241e0bSTom Erickson err = dsl_dir_set_quota(dsname, source, intval); 250292241e0bSTom Erickson break; 250392241e0bSTom Erickson case ZFS_PROP_REFQUOTA: 25043b2aab18SMatthew Ahrens err = dsl_dataset_set_refquota(dsname, source, intval); 250592241e0bSTom Erickson break; 2506a2afb611SJerry Jelinek case ZFS_PROP_FILESYSTEM_LIMIT: 2507a2afb611SJerry Jelinek case ZFS_PROP_SNAPSHOT_LIMIT: 2508a2afb611SJerry Jelinek if (intval == UINT64_MAX) { 2509a2afb611SJerry Jelinek /* clearing the limit, just do it */ 2510a2afb611SJerry Jelinek err = 0; 2511a2afb611SJerry Jelinek } else { 2512a2afb611SJerry Jelinek err = dsl_dir_activate_fs_ss_limit(dsname); 2513a2afb611SJerry Jelinek } 2514eb633035STom Caputi /* 2515eb633035STom Caputi * Set err to -1 to force the zfs_set_prop_nvlist code down the 2516eb633035STom Caputi * default path to set the value in the nvlist. 2517eb633035STom Caputi */ 2518eb633035STom Caputi if (err == 0) 2519eb633035STom Caputi err = -1; 2520eb633035STom Caputi break; 2521eb633035STom Caputi case ZFS_PROP_KEYLOCATION: 2522eb633035STom Caputi err = dsl_crypto_can_set_keylocation(dsname, strval); 2523eb633035STom Caputi 2524a2afb611SJerry Jelinek /* 2525a2afb611SJerry Jelinek * Set err to -1 to force the zfs_set_prop_nvlist code down the 2526a2afb611SJerry Jelinek * default path to set the value in the nvlist. 2527a2afb611SJerry Jelinek */ 2528a2afb611SJerry Jelinek if (err == 0) 2529a2afb611SJerry Jelinek err = -1; 2530a2afb611SJerry Jelinek break; 253192241e0bSTom Erickson case ZFS_PROP_RESERVATION: 253292241e0bSTom Erickson err = dsl_dir_set_reservation(dsname, source, intval); 253392241e0bSTom Erickson break; 253492241e0bSTom Erickson case ZFS_PROP_REFRESERVATION: 25353b2aab18SMatthew Ahrens err = dsl_dataset_set_refreservation(dsname, source, intval); 253692241e0bSTom Erickson break; 253792241e0bSTom Erickson case ZFS_PROP_VOLSIZE: 2538c61ea566SGeorge Wilson err = zvol_set_volsize(dsname, intval); 253992241e0bSTom Erickson break; 254092241e0bSTom Erickson case ZFS_PROP_VERSION: 254192241e0bSTom Erickson { 254292241e0bSTom Erickson zfsvfs_t *zfsvfs; 25439e6eda55Smarks 25441412a1a2SMark Shellenbaum if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0) 2545b24ab676SJeff Bonwick break; 2546b24ab676SJeff Bonwick 254792241e0bSTom Erickson err = zfs_set_version(zfsvfs, intval); 254892241e0bSTom Erickson zfsvfs_rele(zfsvfs, FTAG); 2549d0f3f37eSMark Shellenbaum 255092241e0bSTom Erickson if (err == 0 && intval >= ZPL_VERSION_USERSPACE) { 2551b16da2e2SGeorge Wilson zfs_cmd_t *zc; 2552b16da2e2SGeorge Wilson 2553b16da2e2SGeorge Wilson zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 2554b16da2e2SGeorge Wilson (void) strcpy(zc->zc_name, dsname); 2555b16da2e2SGeorge Wilson (void) zfs_ioc_userspace_upgrade(zc); 2556b16da2e2SGeorge Wilson kmem_free(zc, sizeof (zfs_cmd_t)); 255740feaa91Sahrens } 255892241e0bSTom Erickson break; 2559ecd6cf80Smarks } 256092241e0bSTom Erickson default: 256192241e0bSTom Erickson err = -1; 256292241e0bSTom Erickson } 2563e9dbad6fSeschrock 256492241e0bSTom Erickson return (err); 256592241e0bSTom Erickson } 2566a9799022Sck 256792241e0bSTom Erickson /* 256892241e0bSTom Erickson * This function is best effort. If it fails to set any of the given properties, 25694445fffbSMatthew Ahrens * it continues to set as many as it can and returns the last error 25704445fffbSMatthew Ahrens * encountered. If the caller provides a non-NULL errlist, it will be filled in 25714445fffbSMatthew Ahrens * with the list of names of all the properties that failed along with the 25724445fffbSMatthew Ahrens * corresponding error numbers. 257392241e0bSTom Erickson * 25744445fffbSMatthew Ahrens * If every property is set successfully, zero is returned and errlist is not 25754445fffbSMatthew Ahrens * modified. 257692241e0bSTom Erickson */ 257792241e0bSTom Erickson int 257892241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl, 25794445fffbSMatthew Ahrens nvlist_t *errlist) 258092241e0bSTom Erickson { 258192241e0bSTom Erickson nvpair_t *pair; 258292241e0bSTom Erickson nvpair_t *propval; 258302e383d1STom Erickson int rv = 0; 258492241e0bSTom Erickson uint64_t intval; 258592241e0bSTom Erickson char *strval; 25864445fffbSMatthew Ahrens nvlist_t *genericnvl = fnvlist_alloc(); 25874445fffbSMatthew Ahrens nvlist_t *retrynvl = fnvlist_alloc(); 2588a9799022Sck 258992241e0bSTom Erickson retry: 259092241e0bSTom Erickson pair = NULL; 259192241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 259292241e0bSTom Erickson const char *propname = nvpair_name(pair); 259392241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 2594cfa69fd2STom Erickson int err = 0; 2595e9dbad6fSeschrock 259692241e0bSTom Erickson /* decode the property value */ 259792241e0bSTom Erickson propval = pair; 259892241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 259992241e0bSTom Erickson nvlist_t *attrs; 26004445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 2601eeb85002STim Haley if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 2602eeb85002STim Haley &propval) != 0) 2603be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 260414843421SMatthew Ahrens } 2605e9dbad6fSeschrock 260692241e0bSTom Erickson /* Validate value type */ 2607*6ccda740Sloli if (err == 0 && source == ZPROP_SRC_INHERITED) { 2608*6ccda740Sloli /* inherited properties are expected to be booleans */ 2609*6ccda740Sloli if (nvpair_type(propval) != DATA_TYPE_BOOLEAN) 2610*6ccda740Sloli err = SET_ERROR(EINVAL); 2611*6ccda740Sloli } else if (err == 0 && prop == ZPROP_INVAL) { 261292241e0bSTom Erickson if (zfs_prop_user(propname)) { 261392241e0bSTom Erickson if (nvpair_type(propval) != DATA_TYPE_STRING) 2614be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 261592241e0bSTom Erickson } else if (zfs_prop_userquota(propname)) { 261692241e0bSTom Erickson if (nvpair_type(propval) != 261792241e0bSTom Erickson DATA_TYPE_UINT64_ARRAY) 2618be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 261919b94df9SMatthew Ahrens } else { 2620be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 26214201a95eSRic Aleshire } 2622eeb85002STim Haley } else if (err == 0) { 262392241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 262492241e0bSTom Erickson if (zfs_prop_get_type(prop) != PROP_TYPE_STRING) 2625be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 262692241e0bSTom Erickson } else if (nvpair_type(propval) == DATA_TYPE_UINT64) { 2627a2eea2e1Sahrens const char *unused; 2628a2eea2e1Sahrens 26294445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 2630e9dbad6fSeschrock 2631e9dbad6fSeschrock switch (zfs_prop_get_type(prop)) { 263291ebeef5Sahrens case PROP_TYPE_NUMBER: 2633e9dbad6fSeschrock break; 263491ebeef5Sahrens case PROP_TYPE_STRING: 2635be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 263692241e0bSTom Erickson break; 263791ebeef5Sahrens case PROP_TYPE_INDEX: 2638acd76fe5Seschrock if (zfs_prop_index_to_string(prop, 263992241e0bSTom Erickson intval, &unused) != 0) 2640be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2641e9dbad6fSeschrock break; 2642e9dbad6fSeschrock default: 2643e7437265Sahrens cmn_err(CE_PANIC, 2644e7437265Sahrens "unknown property type"); 2645e9dbad6fSeschrock } 2646e9dbad6fSeschrock } else { 2647be6fd75aSMatthew Ahrens err = SET_ERROR(EINVAL); 2648e9dbad6fSeschrock } 2649e9dbad6fSeschrock } 265092241e0bSTom Erickson 265192241e0bSTom Erickson /* Validate permissions */ 265292241e0bSTom Erickson if (err == 0) 265392241e0bSTom Erickson err = zfs_check_settable(dsname, pair, CRED()); 265492241e0bSTom Erickson 265592241e0bSTom Erickson if (err == 0) { 2656*6ccda740Sloli if (source == ZPROP_SRC_INHERITED) 2657*6ccda740Sloli err = -1; /* does not need special handling */ 2658*6ccda740Sloli else 2659*6ccda740Sloli err = zfs_prop_set_special(dsname, source, 2660*6ccda740Sloli pair); 266192241e0bSTom Erickson if (err == -1) { 266292241e0bSTom Erickson /* 266392241e0bSTom Erickson * For better performance we build up a list of 266492241e0bSTom Erickson * properties to set in a single transaction. 266592241e0bSTom Erickson */ 266692241e0bSTom Erickson err = nvlist_add_nvpair(genericnvl, pair); 266792241e0bSTom Erickson } else if (err != 0 && nvl != retrynvl) { 266892241e0bSTom Erickson /* 266992241e0bSTom Erickson * This may be a spurious error caused by 267092241e0bSTom Erickson * receiving quota and reservation out of order. 267192241e0bSTom Erickson * Try again in a second pass. 267292241e0bSTom Erickson */ 267392241e0bSTom Erickson err = nvlist_add_nvpair(retrynvl, pair); 267492241e0bSTom Erickson } 267592241e0bSTom Erickson } 267692241e0bSTom Erickson 26774445fffbSMatthew Ahrens if (err != 0) { 26784445fffbSMatthew Ahrens if (errlist != NULL) 26794445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, err); 26804445fffbSMatthew Ahrens rv = err; 26814445fffbSMatthew Ahrens } 2682e9dbad6fSeschrock } 2683e9dbad6fSeschrock 268492241e0bSTom Erickson if (nvl != retrynvl && !nvlist_empty(retrynvl)) { 268592241e0bSTom Erickson nvl = retrynvl; 268692241e0bSTom Erickson goto retry; 268792241e0bSTom Erickson } 268892241e0bSTom Erickson 268992241e0bSTom Erickson if (!nvlist_empty(genericnvl) && 269092241e0bSTom Erickson dsl_props_set(dsname, source, genericnvl) != 0) { 269192241e0bSTom Erickson /* 269292241e0bSTom Erickson * If this fails, we still want to set as many properties as we 269392241e0bSTom Erickson * can, so try setting them individually. 269492241e0bSTom Erickson */ 269592241e0bSTom Erickson pair = NULL; 269692241e0bSTom Erickson while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) { 269792241e0bSTom Erickson const char *propname = nvpair_name(pair); 2698cfa69fd2STom Erickson int err = 0; 269992241e0bSTom Erickson 270092241e0bSTom Erickson propval = pair; 270192241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 270292241e0bSTom Erickson nvlist_t *attrs; 27034445fffbSMatthew Ahrens attrs = fnvpair_value_nvlist(pair); 27044445fffbSMatthew Ahrens propval = fnvlist_lookup_nvpair(attrs, 27054445fffbSMatthew Ahrens ZPROP_VALUE); 270692241e0bSTom Erickson } 270792241e0bSTom Erickson 270892241e0bSTom Erickson if (nvpair_type(propval) == DATA_TYPE_STRING) { 27094445fffbSMatthew Ahrens strval = fnvpair_value_string(propval); 27103b2aab18SMatthew Ahrens err = dsl_prop_set_string(dsname, propname, 27113b2aab18SMatthew Ahrens source, strval); 2712*6ccda740Sloli } else if (nvpair_type(propval) == DATA_TYPE_BOOLEAN) { 2713*6ccda740Sloli err = dsl_prop_inherit(dsname, propname, 2714*6ccda740Sloli source); 271592241e0bSTom Erickson } else { 27164445fffbSMatthew Ahrens intval = fnvpair_value_uint64(propval); 27173b2aab18SMatthew Ahrens err = dsl_prop_set_int(dsname, propname, source, 27183b2aab18SMatthew Ahrens intval); 271992241e0bSTom Erickson } 272092241e0bSTom Erickson 272192241e0bSTom Erickson if (err != 0) { 27224445fffbSMatthew Ahrens if (errlist != NULL) { 27234445fffbSMatthew Ahrens fnvlist_add_int32(errlist, propname, 27244445fffbSMatthew Ahrens err); 27254445fffbSMatthew Ahrens } 27264445fffbSMatthew Ahrens rv = err; 272792241e0bSTom Erickson } 272892241e0bSTom Erickson } 27295c0b6a79SRich Morris } 27305c0b6a79SRich Morris nvlist_free(genericnvl); 273192241e0bSTom Erickson nvlist_free(retrynvl); 273292241e0bSTom Erickson 273392241e0bSTom Erickson return (rv); 2734fa9e4066Sahrens } 2735fa9e4066Sahrens 2736ea2f5b9eSMatthew Ahrens /* 2737ea2f5b9eSMatthew Ahrens * Check that all the properties are valid user properties. 2738ea2f5b9eSMatthew Ahrens */ 2739ea2f5b9eSMatthew Ahrens static int 27404445fffbSMatthew Ahrens zfs_check_userprops(const char *fsname, nvlist_t *nvl) 2741ea2f5b9eSMatthew Ahrens { 274292241e0bSTom Erickson nvpair_t *pair = NULL; 2743ea2f5b9eSMatthew Ahrens int error = 0; 2744ea2f5b9eSMatthew Ahrens 274592241e0bSTom Erickson while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) { 274692241e0bSTom Erickson const char *propname = nvpair_name(pair); 2747ea2f5b9eSMatthew Ahrens 2748ea2f5b9eSMatthew Ahrens if (!zfs_prop_user(propname) || 274992241e0bSTom Erickson nvpair_type(pair) != DATA_TYPE_STRING) 2750be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 2751ea2f5b9eSMatthew Ahrens 2752ea2f5b9eSMatthew Ahrens if (error = zfs_secpolicy_write_perms(fsname, 2753ea2f5b9eSMatthew Ahrens ZFS_DELEG_PERM_USERPROP, CRED())) 2754ea2f5b9eSMatthew Ahrens return (error); 2755ea2f5b9eSMatthew Ahrens 2756ea2f5b9eSMatthew Ahrens if (strlen(propname) >= ZAP_MAXNAMELEN) 2757be6fd75aSMatthew Ahrens return (SET_ERROR(ENAMETOOLONG)); 2758ea2f5b9eSMatthew Ahrens 275978f17100SMatthew Ahrens if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN) 2760ea2f5b9eSMatthew Ahrens return (E2BIG); 2761ea2f5b9eSMatthew Ahrens } 2762ea2f5b9eSMatthew Ahrens return (0); 2763ea2f5b9eSMatthew Ahrens } 2764ea2f5b9eSMatthew Ahrens 276592241e0bSTom Erickson static void 276692241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops) 276792241e0bSTom Erickson { 276892241e0bSTom Erickson nvpair_t *pair; 276992241e0bSTom Erickson 277092241e0bSTom Erickson VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 277192241e0bSTom Erickson 277292241e0bSTom Erickson pair = NULL; 277392241e0bSTom Erickson while ((pair = nvlist_next_nvpair(props, pair)) != NULL) { 277492241e0bSTom Erickson if (nvlist_exists(skipped, nvpair_name(pair))) 277592241e0bSTom Erickson continue; 277692241e0bSTom Erickson 277792241e0bSTom Erickson VERIFY(nvlist_add_nvpair(*newprops, pair) == 0); 277892241e0bSTom Erickson } 277992241e0bSTom Erickson } 278092241e0bSTom Erickson 278192241e0bSTom Erickson static int 27823b2aab18SMatthew Ahrens clear_received_props(const char *dsname, nvlist_t *props, 278392241e0bSTom Erickson nvlist_t *skipped) 278492241e0bSTom Erickson { 278592241e0bSTom Erickson int err = 0; 278692241e0bSTom Erickson nvlist_t *cleared_props = NULL; 278792241e0bSTom Erickson props_skip(props, skipped, &cleared_props); 278892241e0bSTom Erickson if (!nvlist_empty(cleared_props)) { 278992241e0bSTom Erickson /* 279092241e0bSTom Erickson * Acts on local properties until the dataset has received 279192241e0bSTom Erickson * properties at least once on or after SPA_VERSION_RECVD_PROPS. 279292241e0bSTom Erickson */ 279392241e0bSTom Erickson zprop_source_t flags = (ZPROP_SRC_NONE | 27943b2aab18SMatthew Ahrens (dsl_prop_get_hasrecvd(dsname) ? ZPROP_SRC_RECEIVED : 0)); 27953b2aab18SMatthew Ahrens err = zfs_set_prop_nvlist(dsname, flags, cleared_props, NULL); 279692241e0bSTom Erickson } 279792241e0bSTom Erickson nvlist_free(cleared_props); 279892241e0bSTom Erickson return (err); 279992241e0bSTom Erickson } 280092241e0bSTom Erickson 28013cb34c60Sahrens /* 28023cb34c60Sahrens * inputs: 28033cb34c60Sahrens * zc_name name of filesystem 28045c0b6a79SRich Morris * zc_value name of property to set 28053cb34c60Sahrens * zc_nvlist_src{_size} nvlist of properties to apply 280692241e0bSTom Erickson * zc_cookie received properties flag 28073cb34c60Sahrens * 280892241e0bSTom Erickson * outputs: 280992241e0bSTom Erickson * zc_nvlist_dst{_size} error for each unapplied received property 28103cb34c60Sahrens */ 2811fa9e4066Sahrens static int 2812e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc) 2813fa9e4066Sahrens { 2814e9dbad6fSeschrock nvlist_t *nvl; 281592241e0bSTom Erickson boolean_t received = zc->zc_cookie; 281692241e0bSTom Erickson zprop_source_t source = (received ? ZPROP_SRC_RECEIVED : 281792241e0bSTom Erickson ZPROP_SRC_LOCAL); 28184445fffbSMatthew Ahrens nvlist_t *errors; 2819e9dbad6fSeschrock int error; 2820e9dbad6fSeschrock 2821990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 2822478ed9adSEric Taylor zc->zc_iflags, &nvl)) != 0) 2823e9dbad6fSeschrock return (error); 2824e9dbad6fSeschrock 282592241e0bSTom Erickson if (received) { 2826bb0ade09Sahrens nvlist_t *origprops; 2827bb0ade09Sahrens 28283b2aab18SMatthew Ahrens if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) { 28293b2aab18SMatthew Ahrens (void) clear_received_props(zc->zc_name, 28303b2aab18SMatthew Ahrens origprops, nvl); 28313b2aab18SMatthew Ahrens nvlist_free(origprops); 2832bb0ade09Sahrens } 28333b2aab18SMatthew Ahrens 28343b2aab18SMatthew Ahrens error = dsl_prop_set_hasrecvd(zc->zc_name); 2835bb0ade09Sahrens } 2836bb0ade09Sahrens 28374445fffbSMatthew Ahrens errors = fnvlist_alloc(); 28383b2aab18SMatthew Ahrens if (error == 0) 28393b2aab18SMatthew Ahrens error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors); 284092241e0bSTom Erickson 2841dd328bf6SToomas Soome if (zc->zc_nvlist_dst != 0 && errors != NULL) { 284292241e0bSTom Erickson (void) put_nvlist(zc, errors); 284392241e0bSTom Erickson } 2844ecd6cf80Smarks 284592241e0bSTom Erickson nvlist_free(errors); 2846e9dbad6fSeschrock nvlist_free(nvl); 2847e9dbad6fSeschrock return (error); 2848fa9e4066Sahrens } 2849fa9e4066Sahrens 28503cb34c60Sahrens /* 28513cb34c60Sahrens * inputs: 28523cb34c60Sahrens * zc_name name of filesystem 28533cb34c60Sahrens * zc_value name of property to inherit 285492241e0bSTom Erickson * zc_cookie revert to received value if TRUE 28553cb34c60Sahrens * 28563cb34c60Sahrens * outputs: none 28573cb34c60Sahrens */ 2858e45ce728Sahrens static int 2859e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc) 2860e45ce728Sahrens { 286192241e0bSTom Erickson const char *propname = zc->zc_value; 286292241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 286392241e0bSTom Erickson boolean_t received = zc->zc_cookie; 286492241e0bSTom Erickson zprop_source_t source = (received 286592241e0bSTom Erickson ? ZPROP_SRC_NONE /* revert to received value, if any */ 286692241e0bSTom Erickson : ZPROP_SRC_INHERITED); /* explicitly inherit */ 286792241e0bSTom Erickson 286892241e0bSTom Erickson if (received) { 286992241e0bSTom Erickson nvlist_t *dummy; 287092241e0bSTom Erickson nvpair_t *pair; 287192241e0bSTom Erickson zprop_type_t type; 287292241e0bSTom Erickson int err; 287392241e0bSTom Erickson 287492241e0bSTom Erickson /* 287592241e0bSTom Erickson * zfs_prop_set_special() expects properties in the form of an 287692241e0bSTom Erickson * nvpair with type info. 287792241e0bSTom Erickson */ 287892241e0bSTom Erickson if (prop == ZPROP_INVAL) { 287992241e0bSTom Erickson if (!zfs_prop_user(propname)) 2880be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 288192241e0bSTom Erickson 288292241e0bSTom Erickson type = PROP_TYPE_STRING; 2883a79992aaSTom Erickson } else if (prop == ZFS_PROP_VOLSIZE || 2884a79992aaSTom Erickson prop == ZFS_PROP_VERSION) { 2885be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 288692241e0bSTom Erickson } else { 288792241e0bSTom Erickson type = zfs_prop_get_type(prop); 288892241e0bSTom Erickson } 288992241e0bSTom Erickson 289092241e0bSTom Erickson VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0); 289192241e0bSTom Erickson 289292241e0bSTom Erickson switch (type) { 289392241e0bSTom Erickson case PROP_TYPE_STRING: 289492241e0bSTom Erickson VERIFY(0 == nvlist_add_string(dummy, propname, "")); 289592241e0bSTom Erickson break; 289692241e0bSTom Erickson case PROP_TYPE_NUMBER: 289792241e0bSTom Erickson case PROP_TYPE_INDEX: 289892241e0bSTom Erickson VERIFY(0 == nvlist_add_uint64(dummy, propname, 0)); 289992241e0bSTom Erickson break; 290092241e0bSTom Erickson default: 290192241e0bSTom Erickson nvlist_free(dummy); 2902be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 290392241e0bSTom Erickson } 290492241e0bSTom Erickson 290592241e0bSTom Erickson pair = nvlist_next_nvpair(dummy, NULL); 290692241e0bSTom Erickson err = zfs_prop_set_special(zc->zc_name, source, pair); 290792241e0bSTom Erickson nvlist_free(dummy); 290892241e0bSTom Erickson if (err != -1) 290992241e0bSTom Erickson return (err); /* special property already handled */ 291092241e0bSTom Erickson } else { 291192241e0bSTom Erickson /* 291292241e0bSTom Erickson * Only check this in the non-received case. We want to allow 291392241e0bSTom Erickson * 'inherit -S' to revert non-inheritable properties like quota 291492241e0bSTom Erickson * and reservation to the received or default values even though 291592241e0bSTom Erickson * they are not considered inheritable. 291692241e0bSTom Erickson */ 291792241e0bSTom Erickson if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) 2918be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 291992241e0bSTom Erickson } 292092241e0bSTom Erickson 29214445fffbSMatthew Ahrens /* property name has been validated by zfs_secpolicy_inherit_prop() */ 29223b2aab18SMatthew Ahrens return (dsl_prop_inherit(zc->zc_name, zc->zc_value, source)); 2923e45ce728Sahrens } 2924e45ce728Sahrens 2925b1b8ab34Slling static int 292611a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc) 2927b1b8ab34Slling { 2928990b4856Slling nvlist_t *props; 2929b1b8ab34Slling spa_t *spa; 2930990b4856Slling int error; 293192241e0bSTom Erickson nvpair_t *pair; 2932b1b8ab34Slling 293392241e0bSTom Erickson if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 293492241e0bSTom Erickson zc->zc_iflags, &props)) 2935b1b8ab34Slling return (error); 2936b1b8ab34Slling 2937379c004dSEric Schrock /* 2938379c004dSEric Schrock * If the only property is the configfile, then just do a spa_lookup() 2939379c004dSEric Schrock * to handle the faulted case. 2940379c004dSEric Schrock */ 294192241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 294292241e0bSTom Erickson if (pair != NULL && strcmp(nvpair_name(pair), 2943379c004dSEric Schrock zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 && 294492241e0bSTom Erickson nvlist_next_nvpair(props, pair) == NULL) { 2945379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2946379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) { 2947379c004dSEric Schrock spa_configfile_set(spa, props, B_FALSE); 29485cabbc6bSPrashanth Sreenivasa spa_write_cachefile(spa, B_FALSE, B_TRUE); 2949379c004dSEric Schrock } 2950379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2951b693757aSEric Schrock if (spa != NULL) { 2952b693757aSEric Schrock nvlist_free(props); 2953379c004dSEric Schrock return (0); 2954b693757aSEric Schrock } 2955379c004dSEric Schrock } 2956379c004dSEric Schrock 2957b1b8ab34Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2958990b4856Slling nvlist_free(props); 2959b1b8ab34Slling return (error); 2960b1b8ab34Slling } 2961b1b8ab34Slling 2962990b4856Slling error = spa_prop_set(spa, props); 2963b1b8ab34Slling 2964990b4856Slling nvlist_free(props); 2965b1b8ab34Slling spa_close(spa, FTAG); 2966b1b8ab34Slling 2967b1b8ab34Slling return (error); 2968b1b8ab34Slling } 2969b1b8ab34Slling 2970b1b8ab34Slling static int 297111a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc) 2972b1b8ab34Slling { 2973b1b8ab34Slling spa_t *spa; 2974b1b8ab34Slling int error; 2975b1b8ab34Slling nvlist_t *nvp = NULL; 2976b1b8ab34Slling 2977379c004dSEric Schrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { 2978379c004dSEric Schrock /* 2979379c004dSEric Schrock * If the pool is faulted, there may be properties we can still 2980379c004dSEric Schrock * get (such as altroot and cachefile), so attempt to get them 2981379c004dSEric Schrock * anyway. 2982379c004dSEric Schrock */ 2983379c004dSEric Schrock mutex_enter(&spa_namespace_lock); 2984379c004dSEric Schrock if ((spa = spa_lookup(zc->zc_name)) != NULL) 2985379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2986379c004dSEric Schrock mutex_exit(&spa_namespace_lock); 2987379c004dSEric Schrock } else { 2988379c004dSEric Schrock error = spa_prop_get(spa, &nvp); 2989379c004dSEric Schrock spa_close(spa, FTAG); 2990379c004dSEric Schrock } 2991b1b8ab34Slling 2992dd328bf6SToomas Soome if (error == 0 && zc->zc_nvlist_dst != 0) 2993b1b8ab34Slling error = put_nvlist(zc, nvp); 2994b1b8ab34Slling else 2995be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 2996b1b8ab34Slling 2997379c004dSEric Schrock nvlist_free(nvp); 2998b1b8ab34Slling return (error); 2999b1b8ab34Slling } 3000b1b8ab34Slling 30013cb34c60Sahrens /* 30023cb34c60Sahrens * inputs: 30033cb34c60Sahrens * zc_name name of filesystem 30043cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 30053cb34c60Sahrens * zc_perm_action allow/unallow flag 30063cb34c60Sahrens * 30073cb34c60Sahrens * outputs: none 30083cb34c60Sahrens */ 3009ecd6cf80Smarks static int 3010ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc) 3011ecd6cf80Smarks { 3012ecd6cf80Smarks int error; 3013ecd6cf80Smarks nvlist_t *fsaclnv = NULL; 3014ecd6cf80Smarks 3015990b4856Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 3016478ed9adSEric Taylor zc->zc_iflags, &fsaclnv)) != 0) 3017ecd6cf80Smarks return (error); 3018ecd6cf80Smarks 3019ecd6cf80Smarks /* 3020ecd6cf80Smarks * Verify nvlist is constructed correctly 3021ecd6cf80Smarks */ 3022ecd6cf80Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) { 3023ecd6cf80Smarks nvlist_free(fsaclnv); 3024be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3025ecd6cf80Smarks } 3026ecd6cf80Smarks 3027ecd6cf80Smarks /* 3028ecd6cf80Smarks * If we don't have PRIV_SYS_MOUNT, then validate 3029ecd6cf80Smarks * that user is allowed to hand out each permission in 3030ecd6cf80Smarks * the nvlist(s) 3031ecd6cf80Smarks */ 3032ecd6cf80Smarks 303391ebeef5Sahrens error = secpolicy_zfs(CRED()); 30343b2aab18SMatthew Ahrens if (error != 0) { 303591ebeef5Sahrens if (zc->zc_perm_action == B_FALSE) { 303691ebeef5Sahrens error = dsl_deleg_can_allow(zc->zc_name, 303791ebeef5Sahrens fsaclnv, CRED()); 303891ebeef5Sahrens } else { 303991ebeef5Sahrens error = dsl_deleg_can_unallow(zc->zc_name, 304091ebeef5Sahrens fsaclnv, CRED()); 304191ebeef5Sahrens } 3042ecd6cf80Smarks } 3043ecd6cf80Smarks 3044ecd6cf80Smarks if (error == 0) 3045ecd6cf80Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action); 3046ecd6cf80Smarks 3047ecd6cf80Smarks nvlist_free(fsaclnv); 3048ecd6cf80Smarks return (error); 3049ecd6cf80Smarks } 3050ecd6cf80Smarks 30513cb34c60Sahrens /* 30523cb34c60Sahrens * inputs: 30533cb34c60Sahrens * zc_name name of filesystem 30543cb34c60Sahrens * 30553cb34c60Sahrens * outputs: 30563cb34c60Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions 30573cb34c60Sahrens */ 3058ecd6cf80Smarks static int 3059ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc) 3060ecd6cf80Smarks { 3061ecd6cf80Smarks nvlist_t *nvp; 3062ecd6cf80Smarks int error; 3063ecd6cf80Smarks 3064ecd6cf80Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) { 3065ecd6cf80Smarks error = put_nvlist(zc, nvp); 3066ecd6cf80Smarks nvlist_free(nvp); 3067ecd6cf80Smarks } 3068ecd6cf80Smarks 3069ecd6cf80Smarks return (error); 3070ecd6cf80Smarks } 3071ecd6cf80Smarks 3072ecd6cf80Smarks /* ARGSUSED */ 3073fa9e4066Sahrens static void 3074ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) 3075fa9e4066Sahrens { 3076da6c28aaSamw zfs_creat_t *zct = arg; 3077da6c28aaSamw 3078de8267e0Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx); 3079da6c28aaSamw } 3080da6c28aaSamw 3081de8267e0Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1) 3082da6c28aaSamw 3083da6c28aaSamw /* 3084de8267e0Stimh * inputs: 30850a48a24eStimh * os parent objset pointer (NULL if root fs) 3086f7170741SWill Andrews * fuids_ok fuids allowed in this version of the spa? 3087f7170741SWill Andrews * sa_ok SAs allowed in this version of the spa? 3088f7170741SWill Andrews * createprops list of properties requested by creator 3089de8267e0Stimh * 3090de8267e0Stimh * outputs: 3091de8267e0Stimh * zplprops values for the zplprops we attach to the master node object 30920a48a24eStimh * is_ci true if requested file system will be purely case-insensitive 3093da6c28aaSamw * 3094de8267e0Stimh * Determine the settings for utf8only, normalization and 3095de8267e0Stimh * casesensitivity. Specific values may have been requested by the 3096de8267e0Stimh * creator and/or we can inherit values from the parent dataset. If 3097de8267e0Stimh * the file system is of too early a vintage, a creator can not 3098de8267e0Stimh * request settings for these properties, even if the requested 3099de8267e0Stimh * setting is the default value. We don't actually want to create dsl 3100de8267e0Stimh * properties for these, so remove them from the source nvlist after 3101de8267e0Stimh * processing. 3102da6c28aaSamw */ 3103da6c28aaSamw static int 310414843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver, 31050a586ceaSMark Shellenbaum boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops, 31060a586ceaSMark Shellenbaum nvlist_t *zplprops, boolean_t *is_ci) 3107da6c28aaSamw { 3108de8267e0Stimh uint64_t sense = ZFS_PROP_UNDEFINED; 3109de8267e0Stimh uint64_t norm = ZFS_PROP_UNDEFINED; 3110de8267e0Stimh uint64_t u8 = ZFS_PROP_UNDEFINED; 3111da6c28aaSamw 3112de8267e0Stimh ASSERT(zplprops != NULL); 3113da6c28aaSamw 3114b127fe3cSAndriy Gapon if (os != NULL && os->os_phys->os_type != DMU_OST_ZFS) 3115b127fe3cSAndriy Gapon return (SET_ERROR(EINVAL)); 3116b127fe3cSAndriy Gapon 3117de8267e0Stimh /* 3118de8267e0Stimh * Pull out creator prop choices, if any. 3119de8267e0Stimh */ 3120de8267e0Stimh if (createprops) { 31210a48a24eStimh (void) nvlist_lookup_uint64(createprops, 31220a48a24eStimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver); 3123de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3124de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm); 3125de8267e0Stimh (void) nvlist_remove_all(createprops, 3126de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE)); 3127de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3128de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8); 3129de8267e0Stimh (void) nvlist_remove_all(createprops, 3130de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); 3131de8267e0Stimh (void) nvlist_lookup_uint64(createprops, 3132de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense); 3133de8267e0Stimh (void) nvlist_remove_all(createprops, 3134de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE)); 3135de8267e0Stimh } 3136da6c28aaSamw 3137c2a93d44Stimh /* 31380a48a24eStimh * If the zpl version requested is whacky or the file system 31390a48a24eStimh * or pool is version is too "young" to support normalization 31400a48a24eStimh * and the creator tried to set a value for one of the props, 31410a48a24eStimh * error out. 3142c2a93d44Stimh */ 31430a48a24eStimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) || 31440a48a24eStimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) || 31450a586ceaSMark Shellenbaum (zplver >= ZPL_VERSION_SA && !sa_ok) || 31460a48a24eStimh (zplver < ZPL_VERSION_NORMALIZATION && 3147de8267e0Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED || 31480a48a24eStimh sense != ZFS_PROP_UNDEFINED))) 3149be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 3150c2a93d44Stimh 3151de8267e0Stimh /* 3152de8267e0Stimh * Put the version in the zplprops 3153de8267e0Stimh */ 3154de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3155de8267e0Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0); 3156da6c28aaSamw 3157de8267e0Stimh if (norm == ZFS_PROP_UNDEFINED) 3158de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0); 3159de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3160de8267e0Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0); 3161da6c28aaSamw 3162c2a93d44Stimh /* 3163de8267e0Stimh * If we're normalizing, names must always be valid UTF-8 strings. 3164c2a93d44Stimh */ 3165de8267e0Stimh if (norm) 3166de8267e0Stimh u8 = 1; 3167de8267e0Stimh if (u8 == ZFS_PROP_UNDEFINED) 3168de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0); 3169de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3170de8267e0Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0); 3171de8267e0Stimh 3172de8267e0Stimh if (sense == ZFS_PROP_UNDEFINED) 3173de8267e0Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0); 3174de8267e0Stimh VERIFY(nvlist_add_uint64(zplprops, 3175de8267e0Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0); 3176c2a93d44Stimh 3177ab04eb8eStimh if (is_ci) 3178ab04eb8eStimh *is_ci = (sense == ZFS_CASE_INSENSITIVE); 3179ab04eb8eStimh 3180da6c28aaSamw return (0); 3181fa9e4066Sahrens } 3182fa9e4066Sahrens 31830a48a24eStimh static int 31840a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops, 31850a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 31860a48a24eStimh { 31870a586ceaSMark Shellenbaum boolean_t fuids_ok, sa_ok; 31880a48a24eStimh uint64_t zplver = ZPL_VERSION; 31890a48a24eStimh objset_t *os = NULL; 31909adfa60dSMatthew Ahrens char parentname[ZFS_MAX_DATASET_NAME_LEN]; 31910a48a24eStimh char *cp; 31920a586ceaSMark Shellenbaum spa_t *spa; 31930a586ceaSMark Shellenbaum uint64_t spa_vers; 31940a48a24eStimh int error; 31950a48a24eStimh 31960a48a24eStimh (void) strlcpy(parentname, dataset, sizeof (parentname)); 31970a48a24eStimh cp = strrchr(parentname, '/'); 31980a48a24eStimh ASSERT(cp != NULL); 31990a48a24eStimh cp[0] = '\0'; 32000a48a24eStimh 32010a586ceaSMark Shellenbaum if ((error = spa_open(dataset, &spa, FTAG)) != 0) 32020a586ceaSMark Shellenbaum return (error); 32030a586ceaSMark Shellenbaum 32040a586ceaSMark Shellenbaum spa_vers = spa_version(spa); 32050a586ceaSMark Shellenbaum spa_close(spa, FTAG); 32060a586ceaSMark Shellenbaum 32070a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 32080a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 32090a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 32100a48a24eStimh 32110a48a24eStimh /* 32120a48a24eStimh * Open parent object set so we can inherit zplprop values. 32130a48a24eStimh */ 3214503ad85cSMatthew Ahrens if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0) 32150a48a24eStimh return (error); 32160a48a24eStimh 32170a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops, 32180a48a24eStimh zplprops, is_ci); 3219503ad85cSMatthew Ahrens dmu_objset_rele(os, FTAG); 32200a48a24eStimh return (error); 32210a48a24eStimh } 32220a48a24eStimh 32230a48a24eStimh static int 32240a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, 32250a48a24eStimh nvlist_t *zplprops, boolean_t *is_ci) 32260a48a24eStimh { 32270a586ceaSMark Shellenbaum boolean_t fuids_ok; 32280a586ceaSMark Shellenbaum boolean_t sa_ok; 32290a48a24eStimh uint64_t zplver = ZPL_VERSION; 32300a48a24eStimh int error; 32310a48a24eStimh 32320a586ceaSMark Shellenbaum zplver = zfs_zpl_version_map(spa_vers); 32330a586ceaSMark Shellenbaum fuids_ok = (zplver >= ZPL_VERSION_FUID); 32340a586ceaSMark Shellenbaum sa_ok = (zplver >= ZPL_VERSION_SA); 32350a48a24eStimh 32360a586ceaSMark Shellenbaum error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok, 32370a586ceaSMark Shellenbaum createprops, zplprops, is_ci); 32380a48a24eStimh return (error); 32390a48a24eStimh } 32400a48a24eStimh 32413cb34c60Sahrens /* 32424445fffbSMatthew Ahrens * innvl: { 32434445fffbSMatthew Ahrens * "type" -> dmu_objset_type_t (int32) 32444445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 3245eb633035STom Caputi * (optional) "hidden_args" -> { "wkeydata" -> value } 3246eb633035STom Caputi * raw uint8_t array of encryption wrapping key data (32 bytes) 32474445fffbSMatthew Ahrens * } 32483cb34c60Sahrens * 32494445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 32503cb34c60Sahrens */ 3251fa9e4066Sahrens static int 32524445fffbSMatthew Ahrens zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3253fa9e4066Sahrens { 3254fa9e4066Sahrens int error = 0; 32554445fffbSMatthew Ahrens zfs_creat_t zct = { 0 }; 3256ecd6cf80Smarks nvlist_t *nvprops = NULL; 3257eb633035STom Caputi nvlist_t *hidden_args = NULL; 3258ecd6cf80Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); 32594445fffbSMatthew Ahrens int32_t type32; 32604445fffbSMatthew Ahrens dmu_objset_type_t type; 32614445fffbSMatthew Ahrens boolean_t is_insensitive = B_FALSE; 3262eb633035STom Caputi dsl_crypto_params_t *dcp = NULL; 3263fa9e4066Sahrens 32644445fffbSMatthew Ahrens if (nvlist_lookup_int32(innvl, "type", &type32) != 0) 3265be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32664445fffbSMatthew Ahrens type = type32; 32674445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3268eb633035STom Caputi (void) nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); 3269fa9e4066Sahrens 32704445fffbSMatthew Ahrens switch (type) { 3271fa9e4066Sahrens case DMU_OST_ZFS: 3272fa9e4066Sahrens cbfunc = zfs_create_cb; 3273fa9e4066Sahrens break; 3274fa9e4066Sahrens 3275fa9e4066Sahrens case DMU_OST_ZVOL: 3276fa9e4066Sahrens cbfunc = zvol_create_cb; 3277fa9e4066Sahrens break; 3278fa9e4066Sahrens 3279fa9e4066Sahrens default: 32801d452cf5Sahrens cbfunc = NULL; 3281e7cbe64fSgw break; 3282fa9e4066Sahrens } 32834445fffbSMatthew Ahrens if (strchr(fsname, '@') || 32844445fffbSMatthew Ahrens strchr(fsname, '%')) 3285be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3286fa9e4066Sahrens 3287da6c28aaSamw zct.zct_props = nvprops; 3288da6c28aaSamw 32894445fffbSMatthew Ahrens if (cbfunc == NULL) 3290be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32914445fffbSMatthew Ahrens 32924445fffbSMatthew Ahrens if (type == DMU_OST_ZVOL) { 32934445fffbSMatthew Ahrens uint64_t volsize, volblocksize; 32944445fffbSMatthew Ahrens 32954445fffbSMatthew Ahrens if (nvprops == NULL) 3296be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 32974445fffbSMatthew Ahrens if (nvlist_lookup_uint64(nvprops, 32984445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) != 0) 3299be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3300fa9e4066Sahrens 33014445fffbSMatthew Ahrens if ((error = nvlist_lookup_uint64(nvprops, 33024445fffbSMatthew Ahrens zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 33034445fffbSMatthew Ahrens &volblocksize)) != 0 && error != ENOENT) 3304be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 33054445fffbSMatthew Ahrens 33064445fffbSMatthew Ahrens if (error != 0) 33074445fffbSMatthew Ahrens volblocksize = zfs_prop_default_numeric( 33084445fffbSMatthew Ahrens ZFS_PROP_VOLBLOCKSIZE); 33094445fffbSMatthew Ahrens 33104445fffbSMatthew Ahrens if ((error = zvol_check_volblocksize( 33114445fffbSMatthew Ahrens volblocksize)) != 0 || 33124445fffbSMatthew Ahrens (error = zvol_check_volsize(volsize, 33134445fffbSMatthew Ahrens volblocksize)) != 0) 3314fa9e4066Sahrens return (error); 33154445fffbSMatthew Ahrens } else if (type == DMU_OST_ZFS) { 33164445fffbSMatthew Ahrens int error; 3317ab04eb8eStimh 33184445fffbSMatthew Ahrens /* 33194445fffbSMatthew Ahrens * We have to have normalization and 33204445fffbSMatthew Ahrens * case-folding flags correct when we do the 33214445fffbSMatthew Ahrens * file system creation, so go figure them out 33224445fffbSMatthew Ahrens * now. 33234445fffbSMatthew Ahrens */ 33244445fffbSMatthew Ahrens VERIFY(nvlist_alloc(&zct.zct_zplprops, 33254445fffbSMatthew Ahrens NV_UNIQUE_NAME, KM_SLEEP) == 0); 33264445fffbSMatthew Ahrens error = zfs_fill_zplprops(fsname, nvprops, 33274445fffbSMatthew Ahrens zct.zct_zplprops, &is_insensitive); 33284445fffbSMatthew Ahrens if (error != 0) { 33294445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 3330da6c28aaSamw return (error); 3331da6c28aaSamw } 33324445fffbSMatthew Ahrens } 3333ab04eb8eStimh 3334eb633035STom Caputi error = dsl_crypto_params_create_nvlist(DCP_CMD_NONE, nvprops, 3335eb633035STom Caputi hidden_args, &dcp); 3336eb633035STom Caputi if (error != 0) { 3337eb633035STom Caputi nvlist_free(zct.zct_zplprops); 3338eb633035STom Caputi return (error); 3339eb633035STom Caputi } 3340eb633035STom Caputi 33414445fffbSMatthew Ahrens error = dmu_objset_create(fsname, type, 3342eb633035STom Caputi is_insensitive ? DS_FLAG_CI_DATASET : 0, dcp, cbfunc, &zct); 3343eb633035STom Caputi 33444445fffbSMatthew Ahrens nvlist_free(zct.zct_zplprops); 3345eb633035STom Caputi dsl_crypto_params_free(dcp, !!error); 33465c5460e9Seschrock 33474445fffbSMatthew Ahrens /* 33484445fffbSMatthew Ahrens * It would be nice to do this atomically. 33494445fffbSMatthew Ahrens */ 33504445fffbSMatthew Ahrens if (error == 0) { 33514445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 33524445fffbSMatthew Ahrens nvprops, outnvl); 33534445fffbSMatthew Ahrens if (error != 0) 33543b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 33554445fffbSMatthew Ahrens } 33564445fffbSMatthew Ahrens return (error); 33574445fffbSMatthew Ahrens } 3358e9dbad6fSeschrock 33594445fffbSMatthew Ahrens /* 33604445fffbSMatthew Ahrens * innvl: { 33614445fffbSMatthew Ahrens * "origin" -> name of origin snapshot 33624445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value } 3363eb633035STom Caputi * (optional) "hidden_args" -> { "wkeydata" -> value } 3364eb633035STom Caputi * raw uint8_t array of encryption wrapping key data (32 bytes) 33654445fffbSMatthew Ahrens * } 33664445fffbSMatthew Ahrens * 33674445fffbSMatthew Ahrens * outnvl: propname -> error code (int32) 33684445fffbSMatthew Ahrens */ 33694445fffbSMatthew Ahrens static int 33704445fffbSMatthew Ahrens zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 33714445fffbSMatthew Ahrens { 33724445fffbSMatthew Ahrens int error = 0; 33734445fffbSMatthew Ahrens nvlist_t *nvprops = NULL; 33744445fffbSMatthew Ahrens char *origin_name; 3375e9dbad6fSeschrock 33764445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) 3377be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 33784445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); 3379e9dbad6fSeschrock 33804445fffbSMatthew Ahrens if (strchr(fsname, '@') || 33814445fffbSMatthew Ahrens strchr(fsname, '%')) 3382be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3383e9dbad6fSeschrock 33844445fffbSMatthew Ahrens if (dataset_namecheck(origin_name, NULL, NULL) != 0) 3385be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3386eb633035STom Caputi 33873b2aab18SMatthew Ahrens error = dmu_objset_clone(fsname, origin_name); 3388e9dbad6fSeschrock 3389e9dbad6fSeschrock /* 3390e9dbad6fSeschrock * It would be nice to do this atomically. 3391e9dbad6fSeschrock */ 3392e9dbad6fSeschrock if (error == 0) { 33934445fffbSMatthew Ahrens error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, 33944445fffbSMatthew Ahrens nvprops, outnvl); 339592241e0bSTom Erickson if (error != 0) 33963b2aab18SMatthew Ahrens (void) dsl_destroy_head(fsname); 3397e9dbad6fSeschrock } 3398fa9e4066Sahrens return (error); 3399fa9e4066Sahrens } 3400fa9e4066Sahrens 34015cabbc6bSPrashanth Sreenivasa /* ARGSUSED */ 34025cabbc6bSPrashanth Sreenivasa static int 34035cabbc6bSPrashanth Sreenivasa zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 34045cabbc6bSPrashanth Sreenivasa { 34055cabbc6bSPrashanth Sreenivasa if (strchr(fsname, '@') || 34065cabbc6bSPrashanth Sreenivasa strchr(fsname, '%')) 34075cabbc6bSPrashanth Sreenivasa return (SET_ERROR(EINVAL)); 34085cabbc6bSPrashanth Sreenivasa 34095cabbc6bSPrashanth Sreenivasa return (dmu_objset_remap_indirects(fsname)); 34105cabbc6bSPrashanth Sreenivasa } 34115cabbc6bSPrashanth Sreenivasa 34123cb34c60Sahrens /* 34134445fffbSMatthew Ahrens * innvl: { 34144445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 34154445fffbSMatthew Ahrens * (optional) "props" -> { prop -> value (string) } 34164445fffbSMatthew Ahrens * } 34174445fffbSMatthew Ahrens * 34184445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 34193cb34c60Sahrens */ 3420fa9e4066Sahrens static int 34214445fffbSMatthew Ahrens zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3422fa9e4066Sahrens { 34234445fffbSMatthew Ahrens nvlist_t *snaps; 34244445fffbSMatthew Ahrens nvlist_t *props = NULL; 34254445fffbSMatthew Ahrens int error, poollen; 34264445fffbSMatthew Ahrens nvpair_t *pair; 3427bb0ade09Sahrens 34284445fffbSMatthew Ahrens (void) nvlist_lookup_nvlist(innvl, "props", &props); 34294445fffbSMatthew Ahrens if ((error = zfs_check_userprops(poolname, props)) != 0) 34304445fffbSMatthew Ahrens return (error); 34314445fffbSMatthew Ahrens 34324445fffbSMatthew Ahrens if (!nvlist_empty(props) && 34334445fffbSMatthew Ahrens zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) 3434be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 34354445fffbSMatthew Ahrens 34364445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3437be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 34384445fffbSMatthew Ahrens poollen = strlen(poolname); 34394445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 34404445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 34414445fffbSMatthew Ahrens const char *name = nvpair_name(pair); 34424445fffbSMatthew Ahrens const char *cp = strchr(name, '@'); 3443bb0ade09Sahrens 34444445fffbSMatthew Ahrens /* 34454445fffbSMatthew Ahrens * The snap name must contain an @, and the part after it must 34464445fffbSMatthew Ahrens * contain only valid characters. 34474445fffbSMatthew Ahrens */ 344878f17100SMatthew Ahrens if (cp == NULL || 344978f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 3450be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3451bb0ade09Sahrens 34524445fffbSMatthew Ahrens /* 34534445fffbSMatthew Ahrens * The snap must be in the specified pool. 34544445fffbSMatthew Ahrens */ 34554445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 34564445fffbSMatthew Ahrens (name[poollen] != '/' && name[poollen] != '@')) 3457be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 34584445fffbSMatthew Ahrens 34594445fffbSMatthew Ahrens /* This must be the only snap of this fs. */ 34604445fffbSMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(snaps, pair); 34614445fffbSMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(snaps, pair2)) { 34624445fffbSMatthew Ahrens if (strncmp(name, nvpair_name(pair2), cp - name + 1) 34634445fffbSMatthew Ahrens == 0) { 3464be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 34654445fffbSMatthew Ahrens } 34664445fffbSMatthew Ahrens } 34674445fffbSMatthew Ahrens } 3468bb0ade09Sahrens 34693b2aab18SMatthew Ahrens error = dsl_dataset_snapshot(snaps, props, outnvl); 34704445fffbSMatthew Ahrens return (error); 34714445fffbSMatthew Ahrens } 34724445fffbSMatthew Ahrens 34734445fffbSMatthew Ahrens /* 34744445fffbSMatthew Ahrens * innvl: "message" -> string 34754445fffbSMatthew Ahrens */ 34764445fffbSMatthew Ahrens /* ARGSUSED */ 34774445fffbSMatthew Ahrens static int 34784445fffbSMatthew Ahrens zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 34794445fffbSMatthew Ahrens { 34804445fffbSMatthew Ahrens char *message; 34814445fffbSMatthew Ahrens spa_t *spa; 34824445fffbSMatthew Ahrens int error; 34834445fffbSMatthew Ahrens char *poolname; 34844445fffbSMatthew Ahrens 34854445fffbSMatthew Ahrens /* 34864445fffbSMatthew Ahrens * The poolname in the ioctl is not set, we get it from the TSD, 34874445fffbSMatthew Ahrens * which was set at the end of the last successful ioctl that allows 34884445fffbSMatthew Ahrens * logging. The secpolicy func already checked that it is set. 34894445fffbSMatthew Ahrens * Only one log ioctl is allowed after each successful ioctl, so 34904445fffbSMatthew Ahrens * we clear the TSD here. 34914445fffbSMatthew Ahrens */ 34924445fffbSMatthew Ahrens poolname = tsd_get(zfs_allow_log_key); 34934445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, NULL); 34944445fffbSMatthew Ahrens error = spa_open(poolname, &spa, FTAG); 34954445fffbSMatthew Ahrens strfree(poolname); 34964445fffbSMatthew Ahrens if (error != 0) 34974445fffbSMatthew Ahrens return (error); 34984445fffbSMatthew Ahrens 34994445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "message", &message) != 0) { 35004445fffbSMatthew Ahrens spa_close(spa, FTAG); 3501be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3502bb0ade09Sahrens } 3503ea2f5b9eSMatthew Ahrens 35044445fffbSMatthew Ahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { 35054445fffbSMatthew Ahrens spa_close(spa, FTAG); 3506be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 35074445fffbSMatthew Ahrens } 3508ea2f5b9eSMatthew Ahrens 35094445fffbSMatthew Ahrens error = spa_history_log(spa, message); 35104445fffbSMatthew Ahrens spa_close(spa, FTAG); 3511bb0ade09Sahrens return (error); 35121d452cf5Sahrens } 3513fa9e4066Sahrens 35143b2aab18SMatthew Ahrens /* 35153b2aab18SMatthew Ahrens * The dp_config_rwlock must not be held when calling this, because the 35163b2aab18SMatthew Ahrens * unmount may need to write out data. 35173b2aab18SMatthew Ahrens * 35183b2aab18SMatthew Ahrens * This function is best-effort. Callers must deal gracefully if it 35193b2aab18SMatthew Ahrens * remains mounted (or is remounted after this call). 3520fc7a6e3fSWill Andrews * 3521fc7a6e3fSWill Andrews * Returns 0 if the argument is not a snapshot, or it is not currently a 3522fc7a6e3fSWill Andrews * filesystem, or we were able to unmount it. Returns error code otherwise. 35233b2aab18SMatthew Ahrens */ 3524ed992b0aSSerapheim Dimitropoulos void 35253b2aab18SMatthew Ahrens zfs_unmount_snap(const char *snapname) 35261d452cf5Sahrens { 3527ed992b0aSSerapheim Dimitropoulos vfs_t *vfsp = NULL; 3528ed992b0aSSerapheim Dimitropoulos zfsvfs_t *zfsvfs = NULL; 35291d452cf5Sahrens 35303b2aab18SMatthew Ahrens if (strchr(snapname, '@') == NULL) 3531ed992b0aSSerapheim Dimitropoulos return; 35321d452cf5Sahrens 3533ed992b0aSSerapheim Dimitropoulos int err = getzfsvfs(snapname, &zfsvfs); 3534ed992b0aSSerapheim Dimitropoulos if (err != 0) { 3535ed992b0aSSerapheim Dimitropoulos ASSERT3P(zfsvfs, ==, NULL); 3536ed992b0aSSerapheim Dimitropoulos return; 3537ed992b0aSSerapheim Dimitropoulos } 3538ed992b0aSSerapheim Dimitropoulos vfsp = zfsvfs->z_vfs; 35393b2aab18SMatthew Ahrens 35403b2aab18SMatthew Ahrens ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os))); 35411d452cf5Sahrens 3542fc7a6e3fSWill Andrews err = vn_vfswlock(vfsp->vfs_vnodecovered); 35434445fffbSMatthew Ahrens VFS_RELE(vfsp); 3544fc7a6e3fSWill Andrews if (err != 0) 3545ed992b0aSSerapheim Dimitropoulos return; 35464445fffbSMatthew Ahrens 35474445fffbSMatthew Ahrens /* 35484445fffbSMatthew Ahrens * Always force the unmount for snapshots. 35494445fffbSMatthew Ahrens */ 35503b2aab18SMatthew Ahrens (void) dounmount(vfsp, MS_FORCE, kcred); 35513b2aab18SMatthew Ahrens } 35523b2aab18SMatthew Ahrens 35533b2aab18SMatthew Ahrens /* ARGSUSED */ 35543b2aab18SMatthew Ahrens static int 35553b2aab18SMatthew Ahrens zfs_unmount_snap_cb(const char *snapname, void *arg) 35563b2aab18SMatthew Ahrens { 3557ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(snapname); 3558ed992b0aSSerapheim Dimitropoulos return (0); 35593b2aab18SMatthew Ahrens } 35603b2aab18SMatthew Ahrens 35613b2aab18SMatthew Ahrens /* 35623b2aab18SMatthew Ahrens * When a clone is destroyed, its origin may also need to be destroyed, 35633b2aab18SMatthew Ahrens * in which case it must be unmounted. This routine will do that unmount 35643b2aab18SMatthew Ahrens * if necessary. 35653b2aab18SMatthew Ahrens */ 35663b2aab18SMatthew Ahrens void 35673b2aab18SMatthew Ahrens zfs_destroy_unmount_origin(const char *fsname) 35683b2aab18SMatthew Ahrens { 35693b2aab18SMatthew Ahrens int error; 35703b2aab18SMatthew Ahrens objset_t *os; 35713b2aab18SMatthew Ahrens dsl_dataset_t *ds; 35723b2aab18SMatthew Ahrens 35733b2aab18SMatthew Ahrens error = dmu_objset_hold(fsname, FTAG, &os); 35743b2aab18SMatthew Ahrens if (error != 0) 35753b2aab18SMatthew Ahrens return; 35763b2aab18SMatthew Ahrens ds = dmu_objset_ds(os); 35773b2aab18SMatthew Ahrens if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev)) { 35789adfa60dSMatthew Ahrens char originname[ZFS_MAX_DATASET_NAME_LEN]; 35793b2aab18SMatthew Ahrens dsl_dataset_name(ds->ds_prev, originname); 35803b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 3581ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(originname); 35823b2aab18SMatthew Ahrens } else { 35833b2aab18SMatthew Ahrens dmu_objset_rele(os, FTAG); 35843b2aab18SMatthew Ahrens } 35851d452cf5Sahrens } 35861d452cf5Sahrens 35873cb34c60Sahrens /* 35884445fffbSMatthew Ahrens * innvl: { 35894445fffbSMatthew Ahrens * "snaps" -> { snapshot1, snapshot2 } 35904445fffbSMatthew Ahrens * (optional boolean) "defer" 35914445fffbSMatthew Ahrens * } 35924445fffbSMatthew Ahrens * 35934445fffbSMatthew Ahrens * outnvl: snapshot -> error code (int32) 35943cb34c60Sahrens * 35953cb34c60Sahrens */ 359678f17100SMatthew Ahrens /* ARGSUSED */ 35971d452cf5Sahrens static int 35984445fffbSMatthew Ahrens zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 35991d452cf5Sahrens { 36004445fffbSMatthew Ahrens nvlist_t *snaps; 360119b94df9SMatthew Ahrens nvpair_t *pair; 36024445fffbSMatthew Ahrens boolean_t defer; 36031d452cf5Sahrens 36044445fffbSMatthew Ahrens if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) 3605be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 36064445fffbSMatthew Ahrens defer = nvlist_exists(innvl, "defer"); 360719b94df9SMatthew Ahrens 36084445fffbSMatthew Ahrens for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; 36094445fffbSMatthew Ahrens pair = nvlist_next_nvpair(snaps, pair)) { 3610ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(nvpair_name(pair)); 361178f17100SMatthew Ahrens } 361278f17100SMatthew Ahrens 361378f17100SMatthew Ahrens return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); 361478f17100SMatthew Ahrens } 361578f17100SMatthew Ahrens 361678f17100SMatthew Ahrens /* 361778f17100SMatthew Ahrens * Create bookmarks. Bookmark names are of the form <fs>#<bmark>. 361878f17100SMatthew Ahrens * All bookmarks must be in the same pool. 361978f17100SMatthew Ahrens * 362078f17100SMatthew Ahrens * innvl: { 362178f17100SMatthew Ahrens * bookmark1 -> snapshot1, bookmark2 -> snapshot2 362278f17100SMatthew Ahrens * } 362378f17100SMatthew Ahrens * 362478f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 362578f17100SMatthew Ahrens * 362678f17100SMatthew Ahrens */ 362778f17100SMatthew Ahrens /* ARGSUSED */ 362878f17100SMatthew Ahrens static int 362978f17100SMatthew Ahrens zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 363078f17100SMatthew Ahrens { 363178f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 363278f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 363378f17100SMatthew Ahrens char *snap_name; 363478f17100SMatthew Ahrens 363578f17100SMatthew Ahrens /* 363678f17100SMatthew Ahrens * Verify the snapshot argument. 363778f17100SMatthew Ahrens */ 363878f17100SMatthew Ahrens if (nvpair_value_string(pair, &snap_name) != 0) 363978f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 364078f17100SMatthew Ahrens 364178f17100SMatthew Ahrens 364278f17100SMatthew Ahrens /* Verify that the keys (bookmarks) are unique */ 364378f17100SMatthew Ahrens for (nvpair_t *pair2 = nvlist_next_nvpair(innvl, pair); 364478f17100SMatthew Ahrens pair2 != NULL; pair2 = nvlist_next_nvpair(innvl, pair2)) { 364578f17100SMatthew Ahrens if (strcmp(nvpair_name(pair), nvpair_name(pair2)) == 0) 364678f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 364778f17100SMatthew Ahrens } 364878f17100SMatthew Ahrens } 364978f17100SMatthew Ahrens 365078f17100SMatthew Ahrens return (dsl_bookmark_create(innvl, outnvl)); 365178f17100SMatthew Ahrens } 365278f17100SMatthew Ahrens 365378f17100SMatthew Ahrens /* 365478f17100SMatthew Ahrens * innvl: { 365578f17100SMatthew Ahrens * property 1, property 2, ... 365678f17100SMatthew Ahrens * } 365778f17100SMatthew Ahrens * 365878f17100SMatthew Ahrens * outnvl: { 365978f17100SMatthew Ahrens * bookmark name 1 -> { property 1, property 2, ... }, 366078f17100SMatthew Ahrens * bookmark name 2 -> { property 1, property 2, ... } 366178f17100SMatthew Ahrens * } 366278f17100SMatthew Ahrens * 366378f17100SMatthew Ahrens */ 366478f17100SMatthew Ahrens static int 366578f17100SMatthew Ahrens zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 366678f17100SMatthew Ahrens { 366778f17100SMatthew Ahrens return (dsl_get_bookmarks(fsname, innvl, outnvl)); 366878f17100SMatthew Ahrens } 366978f17100SMatthew Ahrens 367078f17100SMatthew Ahrens /* 367178f17100SMatthew Ahrens * innvl: { 367278f17100SMatthew Ahrens * bookmark name 1, bookmark name 2 367378f17100SMatthew Ahrens * } 367478f17100SMatthew Ahrens * 367578f17100SMatthew Ahrens * outnvl: bookmark -> error code (int32) 367678f17100SMatthew Ahrens * 367778f17100SMatthew Ahrens */ 367878f17100SMatthew Ahrens static int 367978f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, 368078f17100SMatthew Ahrens nvlist_t *outnvl) 368178f17100SMatthew Ahrens { 368278f17100SMatthew Ahrens int error, poollen; 368378f17100SMatthew Ahrens 368478f17100SMatthew Ahrens poollen = strlen(poolname); 368578f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); 368678f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { 368719b94df9SMatthew Ahrens const char *name = nvpair_name(pair); 368878f17100SMatthew Ahrens const char *cp = strchr(name, '#'); 36894445fffbSMatthew Ahrens 369019b94df9SMatthew Ahrens /* 369178f17100SMatthew Ahrens * The bookmark name must contain an #, and the part after it 369278f17100SMatthew Ahrens * must contain only valid characters. 369378f17100SMatthew Ahrens */ 369478f17100SMatthew Ahrens if (cp == NULL || 369578f17100SMatthew Ahrens zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 369678f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 369778f17100SMatthew Ahrens 369878f17100SMatthew Ahrens /* 369978f17100SMatthew Ahrens * The bookmark must be in the specified pool. 370019b94df9SMatthew Ahrens */ 37014445fffbSMatthew Ahrens if (strncmp(name, poolname, poollen) != 0 || 370278f17100SMatthew Ahrens (name[poollen] != '/' && name[poollen] != '#')) 3703be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 370419b94df9SMatthew Ahrens } 370519b94df9SMatthew Ahrens 370678f17100SMatthew Ahrens error = dsl_bookmark_destroy(innvl, outnvl); 370778f17100SMatthew Ahrens return (error); 37081d452cf5Sahrens } 37091d452cf5Sahrens 3710dfc11533SChris Williamson static int 3711dfc11533SChris Williamson zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, 3712dfc11533SChris Williamson nvlist_t *outnvl) 3713dfc11533SChris Williamson { 3714dfc11533SChris Williamson char *program; 3715dfc11533SChris Williamson uint64_t instrlimit, memlimit; 3716a3b28680SSerapheim Dimitropoulos boolean_t sync_flag; 3717dfc11533SChris Williamson nvpair_t *nvarg = NULL; 3718dfc11533SChris Williamson 3719dfc11533SChris Williamson if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) { 3720dfc11533SChris Williamson return (EINVAL); 3721dfc11533SChris Williamson } 3722a3b28680SSerapheim Dimitropoulos if (0 != nvlist_lookup_boolean_value(innvl, ZCP_ARG_SYNC, &sync_flag)) { 3723a3b28680SSerapheim Dimitropoulos sync_flag = B_TRUE; 3724a3b28680SSerapheim Dimitropoulos } 3725dfc11533SChris Williamson if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) { 3726dfc11533SChris Williamson instrlimit = ZCP_DEFAULT_INSTRLIMIT; 3727dfc11533SChris Williamson } 3728dfc11533SChris Williamson if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { 3729dfc11533SChris Williamson memlimit = ZCP_DEFAULT_MEMLIMIT; 3730dfc11533SChris Williamson } 3731dfc11533SChris Williamson if (0 != nvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST, &nvarg)) { 3732dfc11533SChris Williamson return (EINVAL); 3733dfc11533SChris Williamson } 3734dfc11533SChris Williamson 3735dfc11533SChris Williamson if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) 3736dfc11533SChris Williamson return (EINVAL); 37372840dce1SChris Williamson if (memlimit == 0 || memlimit > zfs_lua_max_memlimit) 3738dfc11533SChris Williamson return (EINVAL); 3739dfc11533SChris Williamson 3740a3b28680SSerapheim Dimitropoulos return (zcp_eval(poolname, program, sync_flag, instrlimit, memlimit, 3741dfc11533SChris Williamson nvarg, outnvl)); 3742dfc11533SChris Williamson } 3743dfc11533SChris Williamson 374486714001SSerapheim Dimitropoulos /* 374586714001SSerapheim Dimitropoulos * innvl: unused 374686714001SSerapheim Dimitropoulos * outnvl: empty 374786714001SSerapheim Dimitropoulos */ 374886714001SSerapheim Dimitropoulos /* ARGSUSED */ 374986714001SSerapheim Dimitropoulos static int 375086714001SSerapheim Dimitropoulos zfs_ioc_pool_checkpoint(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 375186714001SSerapheim Dimitropoulos { 375286714001SSerapheim Dimitropoulos return (spa_checkpoint(poolname)); 375386714001SSerapheim Dimitropoulos } 375486714001SSerapheim Dimitropoulos 375586714001SSerapheim Dimitropoulos /* 375686714001SSerapheim Dimitropoulos * innvl: unused 375786714001SSerapheim Dimitropoulos * outnvl: empty 375886714001SSerapheim Dimitropoulos */ 375986714001SSerapheim Dimitropoulos /* ARGSUSED */ 376086714001SSerapheim Dimitropoulos static int 376186714001SSerapheim Dimitropoulos zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, 376286714001SSerapheim Dimitropoulos nvlist_t *outnvl) 376386714001SSerapheim Dimitropoulos { 376486714001SSerapheim Dimitropoulos return (spa_checkpoint_discard(poolname)); 376586714001SSerapheim Dimitropoulos } 376686714001SSerapheim Dimitropoulos 37673cb34c60Sahrens /* 37683cb34c60Sahrens * inputs: 37693cb34c60Sahrens * zc_name name of dataset to destroy 3770842727c2SChris Kirby * zc_defer_destroy mark for deferred destroy 37713cb34c60Sahrens * 37723cb34c60Sahrens * outputs: none 37733cb34c60Sahrens */ 37741d452cf5Sahrens static int 37751d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc) 37761d452cf5Sahrens { 3777049ba636SAndriy Gapon objset_t *os; 3778049ba636SAndriy Gapon dmu_objset_type_t ost; 3779681d9761SEric Taylor int err; 3780fc7a6e3fSWill Andrews 3781049ba636SAndriy Gapon err = dmu_objset_hold(zc->zc_name, FTAG, &os); 3782049ba636SAndriy Gapon if (err != 0) 3783049ba636SAndriy Gapon return (err); 3784049ba636SAndriy Gapon ost = dmu_objset_type(os); 3785049ba636SAndriy Gapon dmu_objset_rele(os, FTAG); 3786049ba636SAndriy Gapon 3787049ba636SAndriy Gapon if (ost == DMU_OST_ZFS) 3788ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(zc->zc_name); 3789fa9e4066Sahrens 3790*6ccda740Sloli if (strchr(zc->zc_name, '@')) { 37913b2aab18SMatthew Ahrens err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); 3792*6ccda740Sloli } else { 37933b2aab18SMatthew Ahrens err = dsl_destroy_head(zc->zc_name); 3794*6ccda740Sloli if (err == EEXIST) { 3795*6ccda740Sloli /* 3796*6ccda740Sloli * It is possible that the given DS may have 3797*6ccda740Sloli * hidden child (%recv) datasets - "leftovers" 3798*6ccda740Sloli * resulting from the previously interrupted 3799*6ccda740Sloli * 'zfs receive'. 3800*6ccda740Sloli * 3801*6ccda740Sloli * 6 extra bytes for /%recv 3802*6ccda740Sloli */ 3803*6ccda740Sloli char namebuf[ZFS_MAX_DATASET_NAME_LEN + 6]; 3804*6ccda740Sloli 3805*6ccda740Sloli if (snprintf(namebuf, sizeof (namebuf), "%s/%s", 3806*6ccda740Sloli zc->zc_name, recv_clone_name) >= 3807*6ccda740Sloli sizeof (namebuf)) 3808*6ccda740Sloli return (SET_ERROR(EINVAL)); 3809*6ccda740Sloli 3810*6ccda740Sloli /* 3811*6ccda740Sloli * Try to remove the hidden child (%recv) and after 3812*6ccda740Sloli * that try to remove the target dataset. 3813*6ccda740Sloli * If the hidden child (%recv) does not exist 3814*6ccda740Sloli * the original error (EEXIST) will be returned 3815*6ccda740Sloli */ 3816*6ccda740Sloli err = dsl_destroy_head(namebuf); 3817*6ccda740Sloli if (err == 0) 3818*6ccda740Sloli err = dsl_destroy_head(zc->zc_name); 3819*6ccda740Sloli else if (err == ENOENT) 3820*6ccda740Sloli err = SET_ERROR(EEXIST); 3821*6ccda740Sloli } 3822*6ccda740Sloli } 3823049ba636SAndriy Gapon if (ost == DMU_OST_ZVOL && err == 0) 38245c987a37SChris Kirby (void) zvol_remove_minor(zc->zc_name); 3825681d9761SEric Taylor return (err); 3826fa9e4066Sahrens } 3827fa9e4066Sahrens 3828094e47e9SGeorge Wilson /* 3829094e47e9SGeorge Wilson * innvl: { 3830094e47e9SGeorge Wilson * vdevs: { 3831094e47e9SGeorge Wilson * guid 1, guid 2, ... 3832094e47e9SGeorge Wilson * }, 3833094e47e9SGeorge Wilson * func: POOL_INITIALIZE_{CANCEL|DO|SUSPEND} 3834094e47e9SGeorge Wilson * } 3835094e47e9SGeorge Wilson * 3836094e47e9SGeorge Wilson * outnvl: { 3837094e47e9SGeorge Wilson * [func: EINVAL (if provided command type didn't make sense)], 3838094e47e9SGeorge Wilson * [vdevs: { 3839094e47e9SGeorge Wilson * guid1: errno, (see function body for possible errnos) 3840094e47e9SGeorge Wilson * ... 3841094e47e9SGeorge Wilson * }] 3842094e47e9SGeorge Wilson * } 3843094e47e9SGeorge Wilson * 3844094e47e9SGeorge Wilson */ 3845094e47e9SGeorge Wilson static int 3846094e47e9SGeorge Wilson zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 3847094e47e9SGeorge Wilson { 3848094e47e9SGeorge Wilson spa_t *spa; 3849094e47e9SGeorge Wilson int error; 3850094e47e9SGeorge Wilson 3851094e47e9SGeorge Wilson error = spa_open(poolname, &spa, FTAG); 3852094e47e9SGeorge Wilson if (error != 0) 3853094e47e9SGeorge Wilson return (error); 3854094e47e9SGeorge Wilson 3855094e47e9SGeorge Wilson uint64_t cmd_type; 3856094e47e9SGeorge Wilson if (nvlist_lookup_uint64(innvl, ZPOOL_INITIALIZE_COMMAND, 3857094e47e9SGeorge Wilson &cmd_type) != 0) { 3858094e47e9SGeorge Wilson spa_close(spa, FTAG); 3859094e47e9SGeorge Wilson return (SET_ERROR(EINVAL)); 3860094e47e9SGeorge Wilson } 3861094e47e9SGeorge Wilson if (!(cmd_type == POOL_INITIALIZE_CANCEL || 3862094e47e9SGeorge Wilson cmd_type == POOL_INITIALIZE_DO || 3863094e47e9SGeorge Wilson cmd_type == POOL_INITIALIZE_SUSPEND)) { 3864094e47e9SGeorge Wilson spa_close(spa, FTAG); 3865094e47e9SGeorge Wilson return (SET_ERROR(EINVAL)); 3866094e47e9SGeorge Wilson } 3867094e47e9SGeorge Wilson 3868094e47e9SGeorge Wilson nvlist_t *vdev_guids; 3869094e47e9SGeorge Wilson if (nvlist_lookup_nvlist(innvl, ZPOOL_INITIALIZE_VDEVS, 3870094e47e9SGeorge Wilson &vdev_guids) != 0) { 3871094e47e9SGeorge Wilson spa_close(spa, FTAG); 3872094e47e9SGeorge Wilson return (SET_ERROR(EINVAL)); 3873094e47e9SGeorge Wilson } 3874094e47e9SGeorge Wilson 3875094e47e9SGeorge Wilson nvlist_t *vdev_errlist = fnvlist_alloc(); 3876094e47e9SGeorge Wilson int total_errors = 0; 3877094e47e9SGeorge Wilson 3878094e47e9SGeorge Wilson for (nvpair_t *pair = nvlist_next_nvpair(vdev_guids, NULL); 3879094e47e9SGeorge Wilson pair != NULL; pair = nvlist_next_nvpair(vdev_guids, pair)) { 3880094e47e9SGeorge Wilson uint64_t vdev_guid = fnvpair_value_uint64(pair); 3881094e47e9SGeorge Wilson 3882094e47e9SGeorge Wilson error = spa_vdev_initialize(spa, vdev_guid, cmd_type); 3883094e47e9SGeorge Wilson if (error != 0) { 3884094e47e9SGeorge Wilson char guid_as_str[MAXNAMELEN]; 3885094e47e9SGeorge Wilson 3886094e47e9SGeorge Wilson (void) snprintf(guid_as_str, sizeof (guid_as_str), 3887094e47e9SGeorge Wilson "%llu", (unsigned long long)vdev_guid); 3888094e47e9SGeorge Wilson fnvlist_add_int64(vdev_errlist, guid_as_str, error); 3889094e47e9SGeorge Wilson total_errors++; 3890094e47e9SGeorge Wilson } 3891094e47e9SGeorge Wilson } 3892094e47e9SGeorge Wilson if (fnvlist_size(vdev_errlist) > 0) { 3893094e47e9SGeorge Wilson fnvlist_add_nvlist(outnvl, ZPOOL_INITIALIZE_VDEVS, 3894094e47e9SGeorge Wilson vdev_errlist); 3895094e47e9SGeorge Wilson } 3896094e47e9SGeorge Wilson fnvlist_free(vdev_errlist); 3897094e47e9SGeorge Wilson 3898094e47e9SGeorge Wilson spa_close(spa, FTAG); 3899094e47e9SGeorge Wilson return (total_errors > 0 ? EINVAL : 0); 3900094e47e9SGeorge Wilson } 3901094e47e9SGeorge Wilson 39023cb34c60Sahrens /* 3903a7027df1SMatthew Ahrens * fsname is name of dataset to rollback (to most recent snapshot) 39043cb34c60Sahrens * 390577b17137SAndriy Gapon * innvl may contain name of expected target snapshot 3906a7027df1SMatthew Ahrens * 3907a7027df1SMatthew Ahrens * outnvl: "target" -> name of most recent snapshot 3908a7027df1SMatthew Ahrens * } 39093cb34c60Sahrens */ 3910a7027df1SMatthew Ahrens /* ARGSUSED */ 3911fa9e4066Sahrens static int 391277b17137SAndriy Gapon zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 3913fa9e4066Sahrens { 3914ae46e4c7SMatthew Ahrens zfsvfs_t *zfsvfs; 391577b17137SAndriy Gapon char *target = NULL; 39163b2aab18SMatthew Ahrens int error; 3917ae46e4c7SMatthew Ahrens 391877b17137SAndriy Gapon (void) nvlist_lookup_string(innvl, "target", &target); 391977b17137SAndriy Gapon if (target != NULL) { 392095643f75SAndriy Gapon const char *cp = strchr(target, '@'); 392177b17137SAndriy Gapon 392295643f75SAndriy Gapon /* 392395643f75SAndriy Gapon * The snap name must contain an @, and the part after it must 392495643f75SAndriy Gapon * contain only valid characters. 392595643f75SAndriy Gapon */ 392695643f75SAndriy Gapon if (cp == NULL || 392795643f75SAndriy Gapon zfs_component_namecheck(cp + 1, NULL, NULL) != 0) 392877b17137SAndriy Gapon return (SET_ERROR(EINVAL)); 392977b17137SAndriy Gapon } 393077b17137SAndriy Gapon 3931a7027df1SMatthew Ahrens if (getzfsvfs(fsname, &zfsvfs) == 0) { 3932690041b9SAndriy Gapon dsl_dataset_t *ds; 3933690041b9SAndriy Gapon 3934690041b9SAndriy Gapon ds = dmu_objset_ds(zfsvfs->z_os); 3935503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 393647f263f4Sek if (error == 0) { 393747f263f4Sek int resume_err; 39384ccbb6e7Sahrens 393977b17137SAndriy Gapon error = dsl_dataset_rollback(fsname, target, zfsvfs, 394077b17137SAndriy Gapon outnvl); 3941690041b9SAndriy Gapon resume_err = zfs_resume_fs(zfsvfs, ds); 394247f263f4Sek error = error ? error : resume_err; 394347f263f4Sek } 39444ccbb6e7Sahrens VFS_RELE(zfsvfs->z_vfs); 39454ccbb6e7Sahrens } else { 394677b17137SAndriy Gapon error = dsl_dataset_rollback(fsname, target, NULL, outnvl); 39474ccbb6e7Sahrens } 39483b2aab18SMatthew Ahrens return (error); 39493b2aab18SMatthew Ahrens } 39504ccbb6e7Sahrens 39513b2aab18SMatthew Ahrens static int 39523b2aab18SMatthew Ahrens recursive_unmount(const char *fsname, void *arg) 39533b2aab18SMatthew Ahrens { 39543b2aab18SMatthew Ahrens const char *snapname = arg; 39559adfa60dSMatthew Ahrens char fullname[ZFS_MAX_DATASET_NAME_LEN]; 3956ae46e4c7SMatthew Ahrens 39573b2aab18SMatthew Ahrens (void) snprintf(fullname, sizeof (fullname), "%s@%s", fsname, snapname); 3958ed992b0aSSerapheim Dimitropoulos zfs_unmount_snap(fullname); 3959ed992b0aSSerapheim Dimitropoulos 3960ed992b0aSSerapheim Dimitropoulos return (0); 3961fa9e4066Sahrens } 3962fa9e4066Sahrens 39633cb34c60Sahrens /* 39643cb34c60Sahrens * inputs: 39653cb34c60Sahrens * zc_name old name of dataset 39663cb34c60Sahrens * zc_value new name of dataset 39673cb34c60Sahrens * zc_cookie recursive flag (only valid for snapshots) 39683cb34c60Sahrens * 39693cb34c60Sahrens * outputs: none 39703cb34c60Sahrens */ 3971fa9e4066Sahrens static int 3972fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc) 3973fa9e4066Sahrens { 3974049ba636SAndriy Gapon objset_t *os; 3975049ba636SAndriy Gapon dmu_objset_type_t ost; 39767f1f55eaSvb boolean_t recursive = zc->zc_cookie & 1; 39773b2aab18SMatthew Ahrens char *at; 3978049ba636SAndriy Gapon int err; 3979cdf5b4caSmmusante 3980add927f8Sloli /* "zfs rename" from and to ...%recv datasets should both fail */ 3981add927f8Sloli zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 3982e9dbad6fSeschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; 3983add927f8Sloli if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 || 3984add927f8Sloli dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 3985add927f8Sloli strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%')) 3986be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 3987fa9e4066Sahrens 3988049ba636SAndriy Gapon err = dmu_objset_hold(zc->zc_name, FTAG, &os); 3989049ba636SAndriy Gapon if (err != 0) 3990049ba636SAndriy Gapon return (err); 3991049ba636SAndriy Gapon ost = dmu_objset_type(os); 3992049ba636SAndriy Gapon dmu_objset_rele(os, FTAG); 3993049ba636SAndriy Gapon 39943b2aab18SMatthew Ahrens at = strchr(zc->zc_name, '@'); 39953b2aab18SMatthew Ahrens if (at != NULL) { 39963b2aab18SMatthew Ahrens /* snaps must be in same fs */ 3997a0c1127bSSteven Hartland int error; 3998a0c1127bSSteven Hartland 39993b2aab18SMatthew Ahrens if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1)) 4000be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 40013b2aab18SMatthew Ahrens *at = '\0'; 4002049ba636SAndriy Gapon if (ost == DMU_OST_ZFS) { 4003a0c1127bSSteven Hartland error = dmu_objset_find(zc->zc_name, 40043b2aab18SMatthew Ahrens recursive_unmount, at + 1, 40053b2aab18SMatthew Ahrens recursive ? DS_FIND_CHILDREN : 0); 4006a0c1127bSSteven Hartland if (error != 0) { 4007a0c1127bSSteven Hartland *at = '@'; 40083b2aab18SMatthew Ahrens return (error); 4009a0c1127bSSteven Hartland } 40103b2aab18SMatthew Ahrens } 4011a0c1127bSSteven Hartland error = dsl_dataset_rename_snapshot(zc->zc_name, 4012a0c1127bSSteven Hartland at + 1, strchr(zc->zc_value, '@') + 1, recursive); 4013a0c1127bSSteven Hartland *at = '@'; 4014a0c1127bSSteven Hartland 4015a0c1127bSSteven Hartland return (error); 40163b2aab18SMatthew Ahrens } else { 4017049ba636SAndriy Gapon if (ost == DMU_OST_ZVOL) 40183b2aab18SMatthew Ahrens (void) zvol_remove_minor(zc->zc_name); 40193b2aab18SMatthew Ahrens return (dsl_dir_rename(zc->zc_name, zc->zc_value)); 4020fa9e4066Sahrens } 4021fa9e4066Sahrens } 4022fa9e4066Sahrens 402392241e0bSTom Erickson static int 402492241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) 402592241e0bSTom Erickson { 402692241e0bSTom Erickson const char *propname = nvpair_name(pair); 402792241e0bSTom Erickson boolean_t issnap = (strchr(dsname, '@') != NULL); 402892241e0bSTom Erickson zfs_prop_t prop = zfs_name_to_prop(propname); 402992241e0bSTom Erickson uint64_t intval; 403092241e0bSTom Erickson int err; 403192241e0bSTom Erickson 403292241e0bSTom Erickson if (prop == ZPROP_INVAL) { 403392241e0bSTom Erickson if (zfs_prop_user(propname)) { 403492241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, 403592241e0bSTom Erickson ZFS_DELEG_PERM_USERPROP, cr)) 403692241e0bSTom Erickson return (err); 403792241e0bSTom Erickson return (0); 403892241e0bSTom Erickson } 403992241e0bSTom Erickson 404092241e0bSTom Erickson if (!issnap && zfs_prop_userquota(propname)) { 404192241e0bSTom Erickson const char *perm = NULL; 404292241e0bSTom Erickson const char *uq_prefix = 404392241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA]; 404492241e0bSTom Erickson const char *gq_prefix = 404592241e0bSTom Erickson zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA]; 404692241e0bSTom Erickson 404792241e0bSTom Erickson if (strncmp(propname, uq_prefix, 404892241e0bSTom Erickson strlen(uq_prefix)) == 0) { 404992241e0bSTom Erickson perm = ZFS_DELEG_PERM_USERQUOTA; 405092241e0bSTom Erickson } else if (strncmp(propname, gq_prefix, 405192241e0bSTom Erickson strlen(gq_prefix)) == 0) { 405292241e0bSTom Erickson perm = ZFS_DELEG_PERM_GROUPQUOTA; 405392241e0bSTom Erickson } else { 405492241e0bSTom Erickson /* USERUSED and GROUPUSED are read-only */ 4055be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 405692241e0bSTom Erickson } 405792241e0bSTom Erickson 405892241e0bSTom Erickson if (err = zfs_secpolicy_write_perms(dsname, perm, cr)) 405992241e0bSTom Erickson return (err); 406092241e0bSTom Erickson return (0); 406192241e0bSTom Erickson } 406292241e0bSTom Erickson 4063be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 406492241e0bSTom Erickson } 406592241e0bSTom Erickson 406692241e0bSTom Erickson if (issnap) 4067be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 406892241e0bSTom Erickson 406992241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_NVLIST) { 407092241e0bSTom Erickson /* 407192241e0bSTom Erickson * dsl_prop_get_all_impl() returns properties in this 407292241e0bSTom Erickson * format. 407392241e0bSTom Erickson */ 407492241e0bSTom Erickson nvlist_t *attrs; 407592241e0bSTom Erickson VERIFY(nvpair_value_nvlist(pair, &attrs) == 0); 407692241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 407792241e0bSTom Erickson &pair) == 0); 407892241e0bSTom Erickson } 407992241e0bSTom Erickson 408092241e0bSTom Erickson /* 408192241e0bSTom Erickson * Check that this value is valid for this pool version 408292241e0bSTom Erickson */ 408392241e0bSTom Erickson switch (prop) { 408492241e0bSTom Erickson case ZFS_PROP_COMPRESSION: 408592241e0bSTom Erickson /* 408692241e0bSTom Erickson * If the user specified gzip compression, make sure 408792241e0bSTom Erickson * the SPA supports it. We ignore any errors here since 408892241e0bSTom Erickson * we'll catch them later. 408992241e0bSTom Erickson */ 4090b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0) { 409192241e0bSTom Erickson if (intval >= ZIO_COMPRESS_GZIP_1 && 409292241e0bSTom Erickson intval <= ZIO_COMPRESS_GZIP_9 && 409392241e0bSTom Erickson zfs_earlier_version(dsname, 409492241e0bSTom Erickson SPA_VERSION_GZIP_COMPRESSION)) { 4095be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 409692241e0bSTom Erickson } 409792241e0bSTom Erickson 409892241e0bSTom Erickson if (intval == ZIO_COMPRESS_ZLE && 409992241e0bSTom Erickson zfs_earlier_version(dsname, 410092241e0bSTom Erickson SPA_VERSION_ZLE_COMPRESSION)) 4101be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 410292241e0bSTom Erickson 4103a6f561b4SSašo Kiselkov if (intval == ZIO_COMPRESS_LZ4) { 4104a6f561b4SSašo Kiselkov spa_t *spa; 4105a6f561b4SSašo Kiselkov 4106a6f561b4SSašo Kiselkov if ((err = spa_open(dsname, &spa, FTAG)) != 0) 4107a6f561b4SSašo Kiselkov return (err); 4108a6f561b4SSašo Kiselkov 41092acef22dSMatthew Ahrens if (!spa_feature_is_enabled(spa, 41102acef22dSMatthew Ahrens SPA_FEATURE_LZ4_COMPRESS)) { 4111a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 4112be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 4113a6f561b4SSašo Kiselkov } 4114a6f561b4SSašo Kiselkov spa_close(spa, FTAG); 4115a6f561b4SSašo Kiselkov } 4116a6f561b4SSašo Kiselkov 411792241e0bSTom Erickson /* 411892241e0bSTom Erickson * If this is a bootable dataset then 411992241e0bSTom Erickson * verify that the compression algorithm 412092241e0bSTom Erickson * is supported for booting. We must return 412192241e0bSTom Erickson * something other than ENOTSUP since it 412292241e0bSTom Erickson * implies a downrev pool version. 412392241e0bSTom Erickson */ 412492241e0bSTom Erickson if (zfs_is_bootfs(dsname) && 412592241e0bSTom Erickson !BOOTFS_COMPRESS_VALID(intval)) { 4126be6fd75aSMatthew Ahrens return (SET_ERROR(ERANGE)); 412792241e0bSTom Erickson } 412892241e0bSTom Erickson } 412992241e0bSTom Erickson break; 413092241e0bSTom Erickson 413192241e0bSTom Erickson case ZFS_PROP_COPIES: 413292241e0bSTom Erickson if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS)) 4133be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 413492241e0bSTom Erickson break; 413592241e0bSTom Erickson 4136b5152584SMatthew Ahrens case ZFS_PROP_RECORDSIZE: 4137b5152584SMatthew Ahrens /* Record sizes above 128k need the feature to be enabled */ 4138b5152584SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) == 0 && 4139b5152584SMatthew Ahrens intval > SPA_OLD_MAXBLOCKSIZE) { 4140b5152584SMatthew Ahrens spa_t *spa; 4141b5152584SMatthew Ahrens 4142b5152584SMatthew Ahrens /* 4143b5152584SMatthew Ahrens * We don't allow setting the property above 1MB, 4144b5152584SMatthew Ahrens * unless the tunable has been changed. 4145b5152584SMatthew Ahrens */ 4146b5152584SMatthew Ahrens if (intval > zfs_max_recordsize || 4147b5152584SMatthew Ahrens intval > SPA_MAXBLOCKSIZE) 41486de9bb56SMatthew Ahrens return (SET_ERROR(ERANGE)); 4149b5152584SMatthew Ahrens 4150b5152584SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 4151b5152584SMatthew Ahrens return (err); 4152b5152584SMatthew Ahrens 4153b5152584SMatthew Ahrens if (!spa_feature_is_enabled(spa, 4154b5152584SMatthew Ahrens SPA_FEATURE_LARGE_BLOCKS)) { 4155b5152584SMatthew Ahrens spa_close(spa, FTAG); 4156b5152584SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 4157b5152584SMatthew Ahrens } 4158b5152584SMatthew Ahrens spa_close(spa, FTAG); 4159b5152584SMatthew Ahrens } 4160b5152584SMatthew Ahrens break; 4161b5152584SMatthew Ahrens 416254811da5SToomas Soome case ZFS_PROP_DNODESIZE: 416354811da5SToomas Soome /* Dnode sizes above 512 need the feature to be enabled */ 416454811da5SToomas Soome if (nvpair_value_uint64(pair, &intval) == 0 && 416554811da5SToomas Soome intval != ZFS_DNSIZE_LEGACY) { 416654811da5SToomas Soome spa_t *spa; 416754811da5SToomas Soome 416854811da5SToomas Soome if ((err = spa_open(dsname, &spa, FTAG)) != 0) 416954811da5SToomas Soome return (err); 417054811da5SToomas Soome 417154811da5SToomas Soome if (!spa_feature_is_enabled(spa, 417254811da5SToomas Soome SPA_FEATURE_LARGE_DNODE)) { 417354811da5SToomas Soome spa_close(spa, FTAG); 417454811da5SToomas Soome return (SET_ERROR(ENOTSUP)); 417554811da5SToomas Soome } 417654811da5SToomas Soome spa_close(spa, FTAG); 417754811da5SToomas Soome } 417854811da5SToomas Soome break; 417954811da5SToomas Soome 4180663207adSDon Brady case ZFS_PROP_SPECIAL_SMALL_BLOCKS: 4181663207adSDon Brady /* 4182663207adSDon Brady * This property could require the allocation classes 4183663207adSDon Brady * feature to be active for setting, however we allow 4184663207adSDon Brady * it so that tests of settable properties succeed. 4185663207adSDon Brady * The CLI will issue a warning in this case. 4186663207adSDon Brady */ 4187663207adSDon Brady break; 4188663207adSDon Brady 418992241e0bSTom Erickson case ZFS_PROP_SHARESMB: 419092241e0bSTom Erickson if (zpl_earlier_version(dsname, ZPL_VERSION_FUID)) 4191be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 419292241e0bSTom Erickson break; 419392241e0bSTom Erickson 419492241e0bSTom Erickson case ZFS_PROP_ACLINHERIT: 419592241e0bSTom Erickson if (nvpair_type(pair) == DATA_TYPE_UINT64 && 419692241e0bSTom Erickson nvpair_value_uint64(pair, &intval) == 0) { 419792241e0bSTom Erickson if (intval == ZFS_ACL_PASSTHROUGH_X && 419892241e0bSTom Erickson zfs_earlier_version(dsname, 419992241e0bSTom Erickson SPA_VERSION_PASSTHROUGH_X)) 4200be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 420192241e0bSTom Erickson } 420292241e0bSTom Erickson break; 420345818ee1SMatthew Ahrens 420445818ee1SMatthew Ahrens case ZFS_PROP_CHECKSUM: 420545818ee1SMatthew Ahrens case ZFS_PROP_DEDUP: 420645818ee1SMatthew Ahrens { 420745818ee1SMatthew Ahrens spa_feature_t feature; 420845818ee1SMatthew Ahrens spa_t *spa; 420945818ee1SMatthew Ahrens 421045818ee1SMatthew Ahrens /* dedup feature version checks */ 421145818ee1SMatthew Ahrens if (prop == ZFS_PROP_DEDUP && 421245818ee1SMatthew Ahrens zfs_earlier_version(dsname, SPA_VERSION_DEDUP)) 421345818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 421445818ee1SMatthew Ahrens 421545818ee1SMatthew Ahrens if (nvpair_value_uint64(pair, &intval) != 0) 421645818ee1SMatthew Ahrens return (SET_ERROR(EINVAL)); 421745818ee1SMatthew Ahrens 421845818ee1SMatthew Ahrens /* check prop value is enabled in features */ 4219971640e6Silovezfs feature = zio_checksum_to_feature(intval & ZIO_CHECKSUM_MASK); 422045818ee1SMatthew Ahrens if (feature == SPA_FEATURE_NONE) 422145818ee1SMatthew Ahrens break; 422245818ee1SMatthew Ahrens 422345818ee1SMatthew Ahrens if ((err = spa_open(dsname, &spa, FTAG)) != 0) 422445818ee1SMatthew Ahrens return (err); 42250dd498c0SToomas Soome 422645818ee1SMatthew Ahrens if (!spa_feature_is_enabled(spa, feature)) { 422745818ee1SMatthew Ahrens spa_close(spa, FTAG); 422845818ee1SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 422945818ee1SMatthew Ahrens } 423045818ee1SMatthew Ahrens spa_close(spa, FTAG); 423145818ee1SMatthew Ahrens break; 423245818ee1SMatthew Ahrens } 423392241e0bSTom Erickson } 423492241e0bSTom Erickson 423592241e0bSTom Erickson return (zfs_secpolicy_setprop(dsname, prop, pair, CRED())); 423692241e0bSTom Erickson } 423792241e0bSTom Erickson 4238a6f561b4SSašo Kiselkov /* 4239a6f561b4SSašo Kiselkov * Checks for a race condition to make sure we don't increment a feature flag 4240a6f561b4SSašo Kiselkov * multiple times. 4241a6f561b4SSašo Kiselkov */ 4242a6f561b4SSašo Kiselkov static int 42433b2aab18SMatthew Ahrens zfs_prop_activate_feature_check(void *arg, dmu_tx_t *tx) 4244a6f561b4SSašo Kiselkov { 42453b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 42462acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 4247a6f561b4SSašo Kiselkov 42482acef22dSMatthew Ahrens if (!spa_feature_is_active(spa, *featurep)) 4249a6f561b4SSašo Kiselkov return (0); 4250a6f561b4SSašo Kiselkov else 4251be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 4252a6f561b4SSašo Kiselkov } 4253a6f561b4SSašo Kiselkov 4254a6f561b4SSašo Kiselkov /* 4255a6f561b4SSašo Kiselkov * The callback invoked on feature activation in the sync task caused by 4256a6f561b4SSašo Kiselkov * zfs_prop_activate_feature. 4257a6f561b4SSašo Kiselkov */ 4258a6f561b4SSašo Kiselkov static void 42593b2aab18SMatthew Ahrens zfs_prop_activate_feature_sync(void *arg, dmu_tx_t *tx) 4260a6f561b4SSašo Kiselkov { 42613b2aab18SMatthew Ahrens spa_t *spa = dmu_tx_pool(tx)->dp_spa; 42622acef22dSMatthew Ahrens spa_feature_t *featurep = arg; 4263a6f561b4SSašo Kiselkov 42642acef22dSMatthew Ahrens spa_feature_incr(spa, *featurep, tx); 4265a6f561b4SSašo Kiselkov } 4266a6f561b4SSašo Kiselkov 42673b2aab18SMatthew Ahrens /* 42683b2aab18SMatthew Ahrens * Activates a feature on a pool in response to a property setting. This 42693b2aab18SMatthew Ahrens * creates a new sync task which modifies the pool to reflect the feature 42703b2aab18SMatthew Ahrens * as being active. 42713b2aab18SMatthew Ahrens */ 42723b2aab18SMatthew Ahrens static int 42732acef22dSMatthew Ahrens zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature) 42743b2aab18SMatthew Ahrens { 42753b2aab18SMatthew Ahrens int err; 42763b2aab18SMatthew Ahrens 42773b2aab18SMatthew Ahrens /* EBUSY here indicates that the feature is already active */ 42783b2aab18SMatthew Ahrens err = dsl_sync_task(spa_name(spa), 42793b2aab18SMatthew Ahrens zfs_prop_activate_feature_check, zfs_prop_activate_feature_sync, 42807d46dc6cSMatthew Ahrens &feature, 2, ZFS_SPACE_CHECK_RESERVED); 42813b2aab18SMatthew Ahrens 42823b2aab18SMatthew Ahrens if (err != 0 && err != EBUSY) 42833b2aab18SMatthew Ahrens return (err); 42843b2aab18SMatthew Ahrens else 42853b2aab18SMatthew Ahrens return (0); 42863b2aab18SMatthew Ahrens } 42873b2aab18SMatthew Ahrens 428892241e0bSTom Erickson /* 428992241e0bSTom Erickson * Removes properties from the given props list that fail permission checks 429092241e0bSTom Erickson * needed to clear them and to restore them in case of a receive error. For each 429192241e0bSTom Erickson * property, make sure we have both set and inherit permissions. 429292241e0bSTom Erickson * 429392241e0bSTom Erickson * Returns the first error encountered if any permission checks fail. If the 429492241e0bSTom Erickson * caller provides a non-NULL errlist, it also gives the complete list of names 429592241e0bSTom Erickson * of all the properties that failed a permission check along with the 429692241e0bSTom Erickson * corresponding error numbers. The caller is responsible for freeing the 429792241e0bSTom Erickson * returned errlist. 429892241e0bSTom Erickson * 429992241e0bSTom Erickson * If every property checks out successfully, zero is returned and the list 430092241e0bSTom Erickson * pointed at by errlist is NULL. 430192241e0bSTom Erickson */ 430292241e0bSTom Erickson static int 430392241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist) 4304745cd3c5Smaybee { 4305745cd3c5Smaybee zfs_cmd_t *zc; 430692241e0bSTom Erickson nvpair_t *pair, *next_pair; 430792241e0bSTom Erickson nvlist_t *errors; 430892241e0bSTom Erickson int err, rv = 0; 4309745cd3c5Smaybee 4310745cd3c5Smaybee if (props == NULL) 431192241e0bSTom Erickson return (0); 431292241e0bSTom Erickson 431392241e0bSTom Erickson VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0); 431492241e0bSTom Erickson 4315745cd3c5Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP); 4316745cd3c5Smaybee (void) strcpy(zc->zc_name, dataset); 431792241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 431892241e0bSTom Erickson while (pair != NULL) { 431992241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 432092241e0bSTom Erickson 432192241e0bSTom Erickson (void) strcpy(zc->zc_value, nvpair_name(pair)); 432292241e0bSTom Erickson if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 || 43234445fffbSMatthew Ahrens (err = zfs_secpolicy_inherit_prop(zc, NULL, CRED())) != 0) { 432492241e0bSTom Erickson VERIFY(nvlist_remove_nvpair(props, pair) == 0); 432592241e0bSTom Erickson VERIFY(nvlist_add_int32(errors, 432692241e0bSTom Erickson zc->zc_value, err) == 0); 432792241e0bSTom Erickson } 432892241e0bSTom Erickson pair = next_pair; 4329745cd3c5Smaybee } 4330745cd3c5Smaybee kmem_free(zc, sizeof (zfs_cmd_t)); 433192241e0bSTom Erickson 433292241e0bSTom Erickson if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) { 433392241e0bSTom Erickson nvlist_free(errors); 433492241e0bSTom Erickson errors = NULL; 433592241e0bSTom Erickson } else { 433692241e0bSTom Erickson VERIFY(nvpair_value_int32(pair, &rv) == 0); 433792241e0bSTom Erickson } 433892241e0bSTom Erickson 433992241e0bSTom Erickson if (errlist == NULL) 434092241e0bSTom Erickson nvlist_free(errors); 434192241e0bSTom Erickson else 434292241e0bSTom Erickson *errlist = errors; 434392241e0bSTom Erickson 434492241e0bSTom Erickson return (rv); 434592241e0bSTom Erickson } 434692241e0bSTom Erickson 434792241e0bSTom Erickson static boolean_t 434892241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2) 434992241e0bSTom Erickson { 435092241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_NVLIST) { 435192241e0bSTom Erickson /* dsl_prop_get_all_impl() format */ 435292241e0bSTom Erickson nvlist_t *attrs; 435392241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p1, &attrs) == 0); 435492241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 435592241e0bSTom Erickson &p1) == 0); 435692241e0bSTom Erickson } 435792241e0bSTom Erickson 435892241e0bSTom Erickson if (nvpair_type(p2) == DATA_TYPE_NVLIST) { 435992241e0bSTom Erickson nvlist_t *attrs; 436092241e0bSTom Erickson VERIFY(nvpair_value_nvlist(p2, &attrs) == 0); 436192241e0bSTom Erickson VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE, 436292241e0bSTom Erickson &p2) == 0); 436392241e0bSTom Erickson } 436492241e0bSTom Erickson 436592241e0bSTom Erickson if (nvpair_type(p1) != nvpair_type(p2)) 436692241e0bSTom Erickson return (B_FALSE); 436792241e0bSTom Erickson 436892241e0bSTom Erickson if (nvpair_type(p1) == DATA_TYPE_STRING) { 436992241e0bSTom Erickson char *valstr1, *valstr2; 437092241e0bSTom Erickson 437192241e0bSTom Erickson VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0); 437292241e0bSTom Erickson VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0); 437392241e0bSTom Erickson return (strcmp(valstr1, valstr2) == 0); 437492241e0bSTom Erickson } else { 437592241e0bSTom Erickson uint64_t intval1, intval2; 437692241e0bSTom Erickson 437792241e0bSTom Erickson VERIFY(nvpair_value_uint64(p1, &intval1) == 0); 437892241e0bSTom Erickson VERIFY(nvpair_value_uint64(p2, &intval2) == 0); 437992241e0bSTom Erickson return (intval1 == intval2); 438092241e0bSTom Erickson } 4381745cd3c5Smaybee } 4382745cd3c5Smaybee 438392241e0bSTom Erickson /* 438492241e0bSTom Erickson * Remove properties from props if they are not going to change (as determined 438592241e0bSTom Erickson * by comparison with origprops). Remove them from origprops as well, since we 438692241e0bSTom Erickson * do not need to clear or restore properties that won't change. 438792241e0bSTom Erickson */ 438892241e0bSTom Erickson static void 438992241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops) 439092241e0bSTom Erickson { 439192241e0bSTom Erickson nvpair_t *pair, *next_pair; 439292241e0bSTom Erickson 439392241e0bSTom Erickson if (origprops == NULL) 439492241e0bSTom Erickson return; /* all props need to be received */ 439592241e0bSTom Erickson 439692241e0bSTom Erickson pair = nvlist_next_nvpair(props, NULL); 439792241e0bSTom Erickson while (pair != NULL) { 439892241e0bSTom Erickson const char *propname = nvpair_name(pair); 439992241e0bSTom Erickson nvpair_t *match; 440092241e0bSTom Erickson 440192241e0bSTom Erickson next_pair = nvlist_next_nvpair(props, pair); 440292241e0bSTom Erickson 440392241e0bSTom Erickson if ((nvlist_lookup_nvpair(origprops, propname, 440492241e0bSTom Erickson &match) != 0) || !propval_equals(pair, match)) 440592241e0bSTom Erickson goto next; /* need to set received value */ 440692241e0bSTom Erickson 440792241e0bSTom Erickson /* don't clear the existing received value */ 440892241e0bSTom Erickson (void) nvlist_remove_nvpair(origprops, match); 440992241e0bSTom Erickson /* don't bother receiving the property */ 441092241e0bSTom Erickson (void) nvlist_remove_nvpair(props, pair); 441192241e0bSTom Erickson next: 441292241e0bSTom Erickson pair = next_pair; 441392241e0bSTom Erickson } 441492241e0bSTom Erickson } 441592241e0bSTom Erickson 44165878fad7SDan McDonald /* 44175878fad7SDan McDonald * Extract properties that cannot be set PRIOR to the receipt of a dataset. 44185878fad7SDan McDonald * For example, refquota cannot be set until after the receipt of a dataset, 44195878fad7SDan McDonald * because in replication streams, an older/earlier snapshot may exceed the 44205878fad7SDan McDonald * refquota. We want to receive the older/earlier snapshot, but setting 44215878fad7SDan McDonald * refquota pre-receipt will set the dsl's ACTUAL quota, which will prevent 44225878fad7SDan McDonald * the older/earlier snapshot from being received (with EDQUOT). 44235878fad7SDan McDonald * 44245878fad7SDan McDonald * The ZFS test "zfs_receive_011_pos" demonstrates such a scenario. 44255878fad7SDan McDonald * 44265878fad7SDan McDonald * libzfs will need to be judicious handling errors encountered by props 44275878fad7SDan McDonald * extracted by this function. 44285878fad7SDan McDonald */ 44295878fad7SDan McDonald static nvlist_t * 44305878fad7SDan McDonald extract_delay_props(nvlist_t *props) 44315878fad7SDan McDonald { 44325878fad7SDan McDonald nvlist_t *delayprops; 44335878fad7SDan McDonald nvpair_t *nvp, *tmp; 4434eb633035STom Caputi static const zfs_prop_t delayable[] = { 4435eb633035STom Caputi ZFS_PROP_REFQUOTA, 4436eb633035STom Caputi ZFS_PROP_KEYLOCATION, 4437eb633035STom Caputi 0 4438eb633035STom Caputi }; 44395878fad7SDan McDonald int i; 44405878fad7SDan McDonald 44415878fad7SDan McDonald VERIFY(nvlist_alloc(&delayprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); 44425878fad7SDan McDonald 44435878fad7SDan McDonald for (nvp = nvlist_next_nvpair(props, NULL); nvp != NULL; 44445878fad7SDan McDonald nvp = nvlist_next_nvpair(props, nvp)) { 44455878fad7SDan McDonald /* 44465878fad7SDan McDonald * strcmp() is safe because zfs_prop_to_name() always returns 44475878fad7SDan McDonald * a bounded string. 44485878fad7SDan McDonald */ 44495878fad7SDan McDonald for (i = 0; delayable[i] != 0; i++) { 44505878fad7SDan McDonald if (strcmp(zfs_prop_to_name(delayable[i]), 44515878fad7SDan McDonald nvpair_name(nvp)) == 0) { 44525878fad7SDan McDonald break; 44535878fad7SDan McDonald } 44545878fad7SDan McDonald } 44555878fad7SDan McDonald if (delayable[i] != 0) { 44565878fad7SDan McDonald tmp = nvlist_prev_nvpair(props, nvp); 44575878fad7SDan McDonald VERIFY(nvlist_add_nvpair(delayprops, nvp) == 0); 44585878fad7SDan McDonald VERIFY(nvlist_remove_nvpair(props, nvp) == 0); 44595878fad7SDan McDonald nvp = tmp; 44605878fad7SDan McDonald } 44615878fad7SDan McDonald } 44625878fad7SDan McDonald 44635878fad7SDan McDonald if (nvlist_empty(delayprops)) { 44645878fad7SDan McDonald nvlist_free(delayprops); 44655878fad7SDan McDonald delayprops = NULL; 44665878fad7SDan McDonald } 44675878fad7SDan McDonald return (delayprops); 44685878fad7SDan McDonald } 44695878fad7SDan McDonald 447092241e0bSTom Erickson #ifdef DEBUG 447192241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err; 447292241e0bSTom Erickson #endif 447392241e0bSTom Erickson 44743cb34c60Sahrens /* 4475*6ccda740Sloli * nvlist 'errors' is always allocated. It will contain descriptions of 4476*6ccda740Sloli * encountered errors, if any. It's the callers responsibility to free. 44773cb34c60Sahrens */ 4478fa9e4066Sahrens static int 4479*6ccda740Sloli zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops, 4480*6ccda740Sloli nvlist_t *localprops, nvlist_t *hidden_args, boolean_t force, 4481*6ccda740Sloli boolean_t resumable, int input_fd, dmu_replay_record_t *begin_record, 4482*6ccda740Sloli int cleanup_fd, uint64_t *read_bytes, uint64_t *errflags, 4483*6ccda740Sloli uint64_t *action_handle, nvlist_t **errors) 4484fa9e4066Sahrens { 44853cb34c60Sahrens dmu_recv_cookie_t drc; 448692241e0bSTom Erickson int error = 0; 448792241e0bSTom Erickson int props_error = 0; 44883cb34c60Sahrens offset_t off; 4489*6ccda740Sloli nvlist_t *local_delayprops = NULL; 4490*6ccda740Sloli nvlist_t *recv_delayprops = NULL; 449192241e0bSTom Erickson nvlist_t *origprops = NULL; /* existing properties */ 4492*6ccda740Sloli nvlist_t *origrecvd = NULL; /* existing received properties */ 449392241e0bSTom Erickson boolean_t first_recvd_props = B_FALSE; 4494*6ccda740Sloli file_t *input_fp; 4495fa9e4066Sahrens 4496*6ccda740Sloli *read_bytes = 0; 4497*6ccda740Sloli *errflags = 0; 4498*6ccda740Sloli *errors = fnvlist_alloc(); 44993cb34c60Sahrens 4500*6ccda740Sloli input_fp = getf(input_fd); 4501*6ccda740Sloli if (input_fp == NULL) 4502be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 4503f18faf3fSek 4504*6ccda740Sloli error = dmu_recv_begin(tofs, tosnap, begin_record, force, 4505*6ccda740Sloli resumable, localprops, hidden_args, origin, &drc); 45063b2aab18SMatthew Ahrens if (error != 0) 45073b2aab18SMatthew Ahrens goto out; 45083b2aab18SMatthew Ahrens 45093b2aab18SMatthew Ahrens /* 45103b2aab18SMatthew Ahrens * Set properties before we receive the stream so that they are applied 45113b2aab18SMatthew Ahrens * to the new data. Note that we must call dmu_recv_stream() if 45123b2aab18SMatthew Ahrens * dmu_recv_begin() succeeds. 45133b2aab18SMatthew Ahrens */ 4514*6ccda740Sloli if (recvprops != NULL && !drc.drc_newfs) { 45153b2aab18SMatthew Ahrens if (spa_version(dsl_dataset_get_spa(drc.drc_ds)) >= 45163b2aab18SMatthew Ahrens SPA_VERSION_RECVD_PROPS && 45173b2aab18SMatthew Ahrens !dsl_prop_get_hasrecvd(tofs)) 451892241e0bSTom Erickson first_recvd_props = B_TRUE; 451992241e0bSTom Erickson 4520745cd3c5Smaybee /* 452192241e0bSTom Erickson * If new received properties are supplied, they are to 4522*6ccda740Sloli * completely replace the existing received properties, 4523*6ccda740Sloli * so stash away the existing ones. 4524745cd3c5Smaybee */ 4525*6ccda740Sloli if (dsl_prop_get_received(tofs, &origrecvd) == 0) { 452692241e0bSTom Erickson nvlist_t *errlist = NULL; 452792241e0bSTom Erickson /* 452892241e0bSTom Erickson * Don't bother writing a property if its value won't 452992241e0bSTom Erickson * change (and avoid the unnecessary security checks). 453092241e0bSTom Erickson * 453192241e0bSTom Erickson * The first receive after SPA_VERSION_RECVD_PROPS is a 453292241e0bSTom Erickson * special case where we blow away all local properties 453392241e0bSTom Erickson * regardless. 453492241e0bSTom Erickson */ 453592241e0bSTom Erickson if (!first_recvd_props) 4536*6ccda740Sloli props_reduce(recvprops, origrecvd); 4537*6ccda740Sloli if (zfs_check_clearable(tofs, origrecvd, &errlist) != 0) 4538*6ccda740Sloli (void) nvlist_merge(*errors, errlist, 0); 453992241e0bSTom Erickson nvlist_free(errlist); 45403cb34c60Sahrens 4541*6ccda740Sloli if (clear_received_props(tofs, origrecvd, 4542*6ccda740Sloli first_recvd_props ? NULL : recvprops) != 0) 4543*6ccda740Sloli *errflags |= ZPROP_ERR_NOCLEAR; 4544*6ccda740Sloli } else { 4545*6ccda740Sloli *errflags |= ZPROP_ERR_NOCLEAR; 4546*6ccda740Sloli } 4547*6ccda740Sloli } 4548*6ccda740Sloli 4549*6ccda740Sloli /* 4550*6ccda740Sloli * Stash away existing properties so we can restore them on error unless 4551*6ccda740Sloli * we're doing the first receive after SPA_VERSION_RECVD_PROPS, in which 4552*6ccda740Sloli * case "origrecvd" will take care of that. 4553*6ccda740Sloli */ 4554*6ccda740Sloli if (localprops != NULL && !drc.drc_newfs && !first_recvd_props) { 4555*6ccda740Sloli objset_t *os; 4556*6ccda740Sloli if (dmu_objset_hold(tofs, FTAG, &os) == 0) { 4557*6ccda740Sloli if (dsl_prop_get_all(os, &origprops) != 0) { 4558*6ccda740Sloli *errflags |= ZPROP_ERR_NOCLEAR; 4559*6ccda740Sloli } 4560*6ccda740Sloli dmu_objset_rele(os, FTAG); 45613b2aab18SMatthew Ahrens } else { 4562*6ccda740Sloli *errflags |= ZPROP_ERR_NOCLEAR; 456392241e0bSTom Erickson } 45643b2aab18SMatthew Ahrens } 456592241e0bSTom Erickson 4566*6ccda740Sloli if (recvprops != NULL) { 45673b2aab18SMatthew Ahrens props_error = dsl_prop_set_hasrecvd(tofs); 45683b2aab18SMatthew Ahrens 45693b2aab18SMatthew Ahrens if (props_error == 0) { 4570*6ccda740Sloli recv_delayprops = extract_delay_props(recvprops); 45713b2aab18SMatthew Ahrens (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 4572*6ccda740Sloli recvprops, *errors); 45733b2aab18SMatthew Ahrens } 457492241e0bSTom Erickson } 457592241e0bSTom Erickson 4576*6ccda740Sloli if (localprops != NULL) { 4577*6ccda740Sloli nvlist_t *oprops = fnvlist_alloc(); 4578*6ccda740Sloli nvlist_t *xprops = fnvlist_alloc(); 4579*6ccda740Sloli nvpair_t *nvp = NULL; 4580*6ccda740Sloli 4581*6ccda740Sloli while ((nvp = nvlist_next_nvpair(localprops, nvp)) != NULL) { 4582*6ccda740Sloli if (nvpair_type(nvp) == DATA_TYPE_BOOLEAN) { 4583*6ccda740Sloli /* -x property */ 4584*6ccda740Sloli const char *name = nvpair_name(nvp); 4585*6ccda740Sloli zfs_prop_t prop = zfs_name_to_prop(name); 4586*6ccda740Sloli if (prop != ZPROP_INVAL) { 4587*6ccda740Sloli if (!zfs_prop_inheritable(prop)) 4588*6ccda740Sloli continue; 4589*6ccda740Sloli } else if (!zfs_prop_user(name)) 4590*6ccda740Sloli continue; 4591*6ccda740Sloli fnvlist_add_boolean(xprops, name); 4592*6ccda740Sloli } else { 4593*6ccda740Sloli /* -o property=value */ 4594*6ccda740Sloli fnvlist_add_nvpair(oprops, nvp); 4595*6ccda740Sloli } 4596*6ccda740Sloli } 4597*6ccda740Sloli 4598*6ccda740Sloli local_delayprops = extract_delay_props(oprops); 4599*6ccda740Sloli (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL, 4600*6ccda740Sloli oprops, *errors); 4601*6ccda740Sloli (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_INHERITED, 4602*6ccda740Sloli xprops, *errors); 4603*6ccda740Sloli 4604*6ccda740Sloli nvlist_free(oprops); 4605*6ccda740Sloli nvlist_free(xprops); 4606*6ccda740Sloli } 4607*6ccda740Sloli 4608*6ccda740Sloli off = input_fp->f_offset; 4609*6ccda740Sloli error = dmu_recv_stream(&drc, input_fp->f_vnode, &off, cleanup_fd, 4610*6ccda740Sloli action_handle); 4611a2eea2e1Sahrens 4612f4b94bdeSMatthew Ahrens if (error == 0) { 4613f4b94bdeSMatthew Ahrens zfsvfs_t *zfsvfs = NULL; 4614745cd3c5Smaybee 4615f4b94bdeSMatthew Ahrens if (getzfsvfs(tofs, &zfsvfs) == 0) { 4616f4b94bdeSMatthew Ahrens /* online recv */ 4617690041b9SAndriy Gapon dsl_dataset_t *ds; 4618f4b94bdeSMatthew Ahrens int end_err; 4619745cd3c5Smaybee 4620690041b9SAndriy Gapon ds = dmu_objset_ds(zfsvfs->z_os); 4621503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 4622f4b94bdeSMatthew Ahrens /* 4623f4b94bdeSMatthew Ahrens * If the suspend fails, then the recv_end will 4624f4b94bdeSMatthew Ahrens * likely also fail, and clean up after itself. 4625f4b94bdeSMatthew Ahrens */ 462691948b51SKeith M Wesolowski end_err = dmu_recv_end(&drc, zfsvfs); 46275c703fceSGeorge Wilson if (error == 0) 4628690041b9SAndriy Gapon error = zfs_resume_fs(zfsvfs, ds); 4629f4b94bdeSMatthew Ahrens error = error ? error : end_err; 4630f4b94bdeSMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 4631745cd3c5Smaybee } else { 463291948b51SKeith M Wesolowski error = dmu_recv_end(&drc, NULL); 46333cb34c60Sahrens } 46345878fad7SDan McDonald 46355878fad7SDan McDonald /* Set delayed properties now, after we're done receiving. */ 4636*6ccda740Sloli if (recv_delayprops != NULL && error == 0) { 46375878fad7SDan McDonald (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED, 4638*6ccda740Sloli recv_delayprops, *errors); 4639*6ccda740Sloli } 4640*6ccda740Sloli if (local_delayprops != NULL && error == 0) { 4641*6ccda740Sloli (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL, 4642*6ccda740Sloli local_delayprops, *errors); 46435878fad7SDan McDonald } 46445878fad7SDan McDonald } 46455878fad7SDan McDonald 46465878fad7SDan McDonald /* 4647*6ccda740Sloli * Merge delayed props back in with initial props, in case 4648*6ccda740Sloli * we're DEBUG and zfs_ioc_recv_inject_err is set (which means 4649*6ccda740Sloli * we have to make sure clear_received_props() includes 4650*6ccda740Sloli * the delayed properties). 4651*6ccda740Sloli * 4652*6ccda740Sloli * Since zfs_ioc_recv_inject_err is only in DEBUG kernels, 4653*6ccda740Sloli * using ASSERT() will be just like a VERIFY. 46545878fad7SDan McDonald */ 4655*6ccda740Sloli if (recv_delayprops != NULL) { 4656*6ccda740Sloli ASSERT(nvlist_merge(recvprops, recv_delayprops, 0) == 0); 4657*6ccda740Sloli nvlist_free(recv_delayprops); 4658*6ccda740Sloli } 4659*6ccda740Sloli if (local_delayprops != NULL) { 4660*6ccda740Sloli ASSERT(nvlist_merge(localprops, local_delayprops, 0) == 0); 4661*6ccda740Sloli nvlist_free(local_delayprops); 466247f263f4Sek } 46633cb34c60Sahrens 4664*6ccda740Sloli *read_bytes = off - input_fp->f_offset; 4665*6ccda740Sloli if (VOP_SEEK(input_fp->f_vnode, input_fp->f_offset, &off, NULL) == 0) 4666*6ccda740Sloli input_fp->f_offset = off; 4667a2eea2e1Sahrens 466892241e0bSTom Erickson #ifdef DEBUG 466992241e0bSTom Erickson if (zfs_ioc_recv_inject_err) { 467092241e0bSTom Erickson zfs_ioc_recv_inject_err = B_FALSE; 467192241e0bSTom Erickson error = 1; 467292241e0bSTom Erickson } 467392241e0bSTom Erickson #endif 4674*6ccda740Sloli 4675745cd3c5Smaybee /* 4676745cd3c5Smaybee * On error, restore the original props. 4677745cd3c5Smaybee */ 4678*6ccda740Sloli if (error != 0 && recvprops != NULL && !drc.drc_newfs) { 4679*6ccda740Sloli if (clear_received_props(tofs, recvprops, NULL) != 0) { 46803b2aab18SMatthew Ahrens /* 46813b2aab18SMatthew Ahrens * We failed to clear the received properties. 46823b2aab18SMatthew Ahrens * Since we may have left a $recvd value on the 46833b2aab18SMatthew Ahrens * system, we can't clear the $hasrecvd flag. 46843b2aab18SMatthew Ahrens */ 4685*6ccda740Sloli *errflags |= ZPROP_ERR_NORESTORE; 46863b2aab18SMatthew Ahrens } else if (first_recvd_props) { 46873b2aab18SMatthew Ahrens dsl_prop_unset_hasrecvd(tofs); 468892241e0bSTom Erickson } 468992241e0bSTom Erickson 4690*6ccda740Sloli if (origrecvd == NULL && !drc.drc_newfs) { 469192241e0bSTom Erickson /* We failed to stash the original properties. */ 4692*6ccda740Sloli *errflags |= ZPROP_ERR_NORESTORE; 469392241e0bSTom Erickson } 469492241e0bSTom Erickson 469592241e0bSTom Erickson /* 469692241e0bSTom Erickson * dsl_props_set() will not convert RECEIVED to LOCAL on or 469792241e0bSTom Erickson * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL 4698*6ccda740Sloli * explicitly if we're restoring local properties cleared in the 469992241e0bSTom Erickson * first new-style receive. 470092241e0bSTom Erickson */ 4701*6ccda740Sloli if (origrecvd != NULL && 470292241e0bSTom Erickson zfs_set_prop_nvlist(tofs, (first_recvd_props ? 470392241e0bSTom Erickson ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED), 4704*6ccda740Sloli origrecvd, NULL) != 0) { 470592241e0bSTom Erickson /* 470692241e0bSTom Erickson * We stashed the original properties but failed to 470792241e0bSTom Erickson * restore them. 470892241e0bSTom Erickson */ 4709*6ccda740Sloli *errflags |= ZPROP_ERR_NORESTORE; 471092241e0bSTom Erickson } 4711745cd3c5Smaybee } 4712*6ccda740Sloli if (error != 0 && localprops != NULL && !drc.drc_newfs && 4713*6ccda740Sloli !first_recvd_props) { 4714*6ccda740Sloli nvlist_t *setprops; 4715*6ccda740Sloli nvlist_t *inheritprops; 4716*6ccda740Sloli nvpair_t *nvp; 4717*6ccda740Sloli 4718*6ccda740Sloli if (origprops == NULL) { 4719*6ccda740Sloli /* We failed to stash the original properties. */ 4720*6ccda740Sloli *errflags |= ZPROP_ERR_NORESTORE; 4721*6ccda740Sloli goto out; 4722*6ccda740Sloli } 4723*6ccda740Sloli 4724*6ccda740Sloli /* Restore original props */ 4725*6ccda740Sloli setprops = fnvlist_alloc(); 4726*6ccda740Sloli inheritprops = fnvlist_alloc(); 4727*6ccda740Sloli nvp = NULL; 4728*6ccda740Sloli while ((nvp = nvlist_next_nvpair(localprops, nvp)) != NULL) { 4729*6ccda740Sloli const char *name = nvpair_name(nvp); 4730*6ccda740Sloli const char *source; 4731*6ccda740Sloli nvlist_t *attrs; 4732*6ccda740Sloli 4733*6ccda740Sloli if (!nvlist_exists(origprops, name)) { 4734*6ccda740Sloli /* 4735*6ccda740Sloli * Property was not present or was explicitly 4736*6ccda740Sloli * inherited before the receive, restore this. 4737*6ccda740Sloli */ 4738*6ccda740Sloli fnvlist_add_boolean(inheritprops, name); 4739*6ccda740Sloli continue; 4740*6ccda740Sloli } 4741*6ccda740Sloli attrs = fnvlist_lookup_nvlist(origprops, name); 4742*6ccda740Sloli source = fnvlist_lookup_string(attrs, ZPROP_SOURCE); 4743*6ccda740Sloli 4744*6ccda740Sloli /* Skip received properties */ 4745*6ccda740Sloli if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) 4746*6ccda740Sloli continue; 4747*6ccda740Sloli 4748*6ccda740Sloli if (strcmp(source, tofs) == 0) { 4749*6ccda740Sloli /* Property was locally set */ 4750*6ccda740Sloli fnvlist_add_nvlist(setprops, name, attrs); 4751*6ccda740Sloli } else { 4752*6ccda740Sloli /* Property was implicitly inherited */ 4753*6ccda740Sloli fnvlist_add_boolean(inheritprops, name); 4754*6ccda740Sloli } 4755*6ccda740Sloli } 4756*6ccda740Sloli 4757*6ccda740Sloli if (zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL, setprops, 4758*6ccda740Sloli NULL) != 0) 4759*6ccda740Sloli *errflags |= ZPROP_ERR_NORESTORE; 4760*6ccda740Sloli if (zfs_set_prop_nvlist(tofs, ZPROP_SRC_INHERITED, inheritprops, 4761*6ccda740Sloli NULL) != 0) 4762*6ccda740Sloli *errflags |= ZPROP_ERR_NORESTORE; 4763*6ccda740Sloli 4764*6ccda740Sloli nvlist_free(setprops); 4765*6ccda740Sloli nvlist_free(inheritprops); 4766*6ccda740Sloli } 4767745cd3c5Smaybee out: 4768*6ccda740Sloli releasef(input_fd); 4769*6ccda740Sloli nvlist_free(origrecvd); 4770745cd3c5Smaybee nvlist_free(origprops); 477192241e0bSTom Erickson 477292241e0bSTom Erickson if (error == 0) 477392241e0bSTom Erickson error = props_error; 477492241e0bSTom Erickson 4775fa9e4066Sahrens return (error); 4776fa9e4066Sahrens } 4777fa9e4066Sahrens 4778*6ccda740Sloli /* 4779*6ccda740Sloli * inputs: 4780*6ccda740Sloli * zc_name name of containing filesystem 4781*6ccda740Sloli * zc_nvlist_src{_size} nvlist of received properties to apply 4782*6ccda740Sloli * zc_nvlist_conf{_size} nvlist of local properties to apply 4783*6ccda740Sloli * zc_history_offset{_len} nvlist of hidden args { "wkeydata" -> value } 4784*6ccda740Sloli * zc_value name of snapshot to create 4785*6ccda740Sloli * zc_string name of clone origin (if DRR_FLAG_CLONE) 4786*6ccda740Sloli * zc_cookie file descriptor to recv from 4787*6ccda740Sloli * zc_begin_record the BEGIN record of the stream (not byteswapped) 4788*6ccda740Sloli * zc_guid force flag 4789*6ccda740Sloli * zc_cleanup_fd cleanup-on-exit file descriptor 4790*6ccda740Sloli * zc_action_handle handle for this guid/ds mapping (or zero on first call) 4791*6ccda740Sloli * zc_resumable if data is incomplete assume sender will resume 4792*6ccda740Sloli * 4793*6ccda740Sloli * outputs: 4794*6ccda740Sloli * zc_cookie number of bytes read 4795*6ccda740Sloli * zc_nvlist_dst{_size} error for each unapplied received property 4796*6ccda740Sloli * zc_obj zprop_errflags_t 4797*6ccda740Sloli * zc_action_handle handle for this guid/ds mapping 4798*6ccda740Sloli */ 4799*6ccda740Sloli static int 4800*6ccda740Sloli zfs_ioc_recv(zfs_cmd_t *zc) 4801*6ccda740Sloli { 4802*6ccda740Sloli dmu_replay_record_t begin_record; 4803*6ccda740Sloli nvlist_t *errors = NULL; 4804*6ccda740Sloli nvlist_t *recvdprops = NULL; 4805*6ccda740Sloli nvlist_t *localprops = NULL; 4806*6ccda740Sloli nvlist_t *hidden_args = NULL; 4807*6ccda740Sloli char *origin = NULL; 4808*6ccda740Sloli char *tosnap; 4809*6ccda740Sloli char tofs[ZFS_MAX_DATASET_NAME_LEN]; 4810*6ccda740Sloli int error = 0; 4811*6ccda740Sloli 4812*6ccda740Sloli if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || 4813*6ccda740Sloli strchr(zc->zc_value, '@') == NULL || 4814*6ccda740Sloli strchr(zc->zc_value, '%')) 4815*6ccda740Sloli return (SET_ERROR(EINVAL)); 4816*6ccda740Sloli 4817*6ccda740Sloli (void) strlcpy(tofs, zc->zc_value, sizeof (tofs)); 4818*6ccda740Sloli tosnap = strchr(tofs, '@'); 4819*6ccda740Sloli *tosnap++ = '\0'; 4820*6ccda740Sloli 4821*6ccda740Sloli if (zc->zc_nvlist_src != 0 && 4822*6ccda740Sloli (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 4823*6ccda740Sloli zc->zc_iflags, &recvdprops)) != 0) 4824*6ccda740Sloli return (error); 4825*6ccda740Sloli 4826*6ccda740Sloli if (zc->zc_nvlist_conf != 0 && 4827*6ccda740Sloli (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, 4828*6ccda740Sloli zc->zc_iflags, &localprops)) != 0) 4829*6ccda740Sloli return (error); 4830*6ccda740Sloli 4831*6ccda740Sloli if (zc->zc_history_offset != 0 && 4832*6ccda740Sloli (error = get_nvlist(zc->zc_history_offset, zc->zc_history_len, 4833*6ccda740Sloli zc->zc_iflags, &hidden_args)) != 0) 4834*6ccda740Sloli return (error); 4835*6ccda740Sloli 4836*6ccda740Sloli if (zc->zc_string[0]) 4837*6ccda740Sloli origin = zc->zc_string; 4838*6ccda740Sloli 4839*6ccda740Sloli begin_record.drr_type = DRR_BEGIN; 4840*6ccda740Sloli begin_record.drr_payloadlen = zc->zc_begin_record.drr_payloadlen; 4841*6ccda740Sloli begin_record.drr_u.drr_begin = zc->zc_begin_record.drr_u.drr_begin; 4842*6ccda740Sloli 4843*6ccda740Sloli error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvdprops, localprops, 4844*6ccda740Sloli hidden_args, zc->zc_guid, zc->zc_resumable, zc->zc_cookie, 4845*6ccda740Sloli &begin_record, zc->zc_cleanup_fd, &zc->zc_cookie, &zc->zc_obj, 4846*6ccda740Sloli &zc->zc_action_handle, &errors); 4847*6ccda740Sloli nvlist_free(recvdprops); 4848*6ccda740Sloli nvlist_free(localprops); 4849*6ccda740Sloli 4850*6ccda740Sloli /* 4851*6ccda740Sloli * Now that all props, initial and delayed, are set, report the prop 4852*6ccda740Sloli * errors to the caller. 4853*6ccda740Sloli */ 4854*6ccda740Sloli if (zc->zc_nvlist_dst_size != 0 && errors != NULL && 4855*6ccda740Sloli (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 || 4856*6ccda740Sloli put_nvlist(zc, errors) != 0)) { 4857*6ccda740Sloli /* 4858*6ccda740Sloli * Caller made zc->zc_nvlist_dst less than the minimum expected 4859*6ccda740Sloli * size or supplied an invalid address. 4860*6ccda740Sloli */ 4861*6ccda740Sloli error = SET_ERROR(EINVAL); 4862*6ccda740Sloli } 4863*6ccda740Sloli 4864*6ccda740Sloli nvlist_free(errors); 4865*6ccda740Sloli 4866*6ccda740Sloli return (error); 4867*6ccda740Sloli } 4868*6ccda740Sloli 48693cb34c60Sahrens /* 48703cb34c60Sahrens * inputs: 48713cb34c60Sahrens * zc_name name of snapshot to send 48723cb34c60Sahrens * zc_cookie file descriptor to send stream to 4873a7f53a56SChris Kirby * zc_obj fromorigin flag (mutually exclusive with zc_fromobj) 4874a7f53a56SChris Kirby * zc_sendobj objsetid of snapshot to send 4875a7f53a56SChris Kirby * zc_fromobj objsetid of incremental fromsnap (may be zero) 487619b94df9SMatthew Ahrens * zc_guid if set, estimate size of stream only. zc_cookie is ignored. 487719b94df9SMatthew Ahrens * output size in zc_objset_type. 4878b5152584SMatthew Ahrens * zc_flags lzc_send_flags 48793cb34c60Sahrens * 488078f17100SMatthew Ahrens * outputs: 488178f17100SMatthew Ahrens * zc_objset_type estimated size, if zc_guid is set 48823cb34c60Sahrens */ 4883fa9e4066Sahrens static int 48843cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc) 4885fa9e4066Sahrens { 4886fa9e4066Sahrens int error; 48873cb34c60Sahrens offset_t off; 488819b94df9SMatthew Ahrens boolean_t estimate = (zc->zc_guid != 0); 48895d7b4d43SMatthew Ahrens boolean_t embedok = (zc->zc_flags & 0x1); 4890b5152584SMatthew Ahrens boolean_t large_block_ok = (zc->zc_flags & 0x2); 48915602294fSDan Kimmel boolean_t compressok = (zc->zc_flags & 0x4); 4892eb633035STom Caputi boolean_t rawok = (zc->zc_flags & 0x8); 4893fa9e4066Sahrens 48943b2aab18SMatthew Ahrens if (zc->zc_obj != 0) { 48953b2aab18SMatthew Ahrens dsl_pool_t *dp; 48963b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 4897a7f53a56SChris Kirby 48983b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 48993b2aab18SMatthew Ahrens if (error != 0) 4900a7f53a56SChris Kirby return (error); 49013b2aab18SMatthew Ahrens 49023b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); 49033b2aab18SMatthew Ahrens if (error != 0) { 49043b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 4905fa9e4066Sahrens return (error); 4906fa9e4066Sahrens } 49073b2aab18SMatthew Ahrens 49083b2aab18SMatthew Ahrens if (dsl_dir_is_clone(tosnap->ds_dir)) 4909c1379625SJustin T. Gibbs zc->zc_fromobj = 4910c1379625SJustin T. Gibbs dsl_dir_phys(tosnap->ds_dir)->dd_origin_obj; 49113b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 49123b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 49134445fffbSMatthew Ahrens } 49144445fffbSMatthew Ahrens 49153b2aab18SMatthew Ahrens if (estimate) { 49163b2aab18SMatthew Ahrens dsl_pool_t *dp; 49173b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 49183b2aab18SMatthew Ahrens dsl_dataset_t *fromsnap = NULL; 49194445fffbSMatthew Ahrens 49203b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 49213b2aab18SMatthew Ahrens if (error != 0) 49223b2aab18SMatthew Ahrens return (error); 49233b2aab18SMatthew Ahrens 4924eb633035STom Caputi error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, 4925eb633035STom Caputi FTAG, &tosnap); 49263b2aab18SMatthew Ahrens if (error != 0) { 49273b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 49283b2aab18SMatthew Ahrens return (error); 49294445fffbSMatthew Ahrens } 49304445fffbSMatthew Ahrens 49313b2aab18SMatthew Ahrens if (zc->zc_fromobj != 0) { 49323b2aab18SMatthew Ahrens error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, 49333b2aab18SMatthew Ahrens FTAG, &fromsnap); 49343b2aab18SMatthew Ahrens if (error != 0) { 49353b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 49363b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 49374445fffbSMatthew Ahrens return (error); 49384445fffbSMatthew Ahrens } 49394445fffbSMatthew Ahrens } 4940fa9e4066Sahrens 4941eb633035STom Caputi error = dmu_send_estimate(tosnap, fromsnap, compressok || rawok, 494219b94df9SMatthew Ahrens &zc->zc_objset_type); 49433b2aab18SMatthew Ahrens 49443b2aab18SMatthew Ahrens if (fromsnap != NULL) 49453b2aab18SMatthew Ahrens dsl_dataset_rele(fromsnap, FTAG); 49463b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 49473b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 494819b94df9SMatthew Ahrens } else { 494919b94df9SMatthew Ahrens file_t *fp = getf(zc->zc_cookie); 49503b2aab18SMatthew Ahrens if (fp == NULL) 4951be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 4952fa9e4066Sahrens 495319b94df9SMatthew Ahrens off = fp->f_offset; 49543b2aab18SMatthew Ahrens error = dmu_send_obj(zc->zc_name, zc->zc_sendobj, 4955eb633035STom Caputi zc->zc_fromobj, embedok, large_block_ok, compressok, rawok, 4956b5152584SMatthew Ahrens zc->zc_cookie, fp->f_vnode, &off); 4957fa9e4066Sahrens 495819b94df9SMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 495919b94df9SMatthew Ahrens fp->f_offset = off; 496019b94df9SMatthew Ahrens releasef(zc->zc_cookie); 496119b94df9SMatthew Ahrens } 4962fa9e4066Sahrens return (error); 4963fa9e4066Sahrens } 4964fa9e4066Sahrens 49654e3c9f44SBill Pijewski /* 49664e3c9f44SBill Pijewski * inputs: 49674e3c9f44SBill Pijewski * zc_name name of snapshot on which to report progress 49684e3c9f44SBill Pijewski * zc_cookie file descriptor of send stream 49694e3c9f44SBill Pijewski * 49704e3c9f44SBill Pijewski * outputs: 49714e3c9f44SBill Pijewski * zc_cookie number of bytes written in send stream thus far 49724e3c9f44SBill Pijewski */ 49734e3c9f44SBill Pijewski static int 49744e3c9f44SBill Pijewski zfs_ioc_send_progress(zfs_cmd_t *zc) 49754e3c9f44SBill Pijewski { 49763b2aab18SMatthew Ahrens dsl_pool_t *dp; 49774e3c9f44SBill Pijewski dsl_dataset_t *ds; 49784e3c9f44SBill Pijewski dmu_sendarg_t *dsp = NULL; 49794e3c9f44SBill Pijewski int error; 49804e3c9f44SBill Pijewski 49813b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 49823b2aab18SMatthew Ahrens if (error != 0) 49833b2aab18SMatthew Ahrens return (error); 49843b2aab18SMatthew Ahrens 49853b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); 49863b2aab18SMatthew Ahrens if (error != 0) { 49873b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 49884e3c9f44SBill Pijewski return (error); 49893b2aab18SMatthew Ahrens } 49904e3c9f44SBill Pijewski 49914e3c9f44SBill Pijewski mutex_enter(&ds->ds_sendstream_lock); 49924e3c9f44SBill Pijewski 49934e3c9f44SBill Pijewski /* 49944e3c9f44SBill Pijewski * Iterate over all the send streams currently active on this dataset. 49954e3c9f44SBill Pijewski * If there's one which matches the specified file descriptor _and_ the 49964e3c9f44SBill Pijewski * stream was started by the current process, return the progress of 49974e3c9f44SBill Pijewski * that stream. 49984e3c9f44SBill Pijewski */ 49994e3c9f44SBill Pijewski for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL; 50004e3c9f44SBill Pijewski dsp = list_next(&ds->ds_sendstreams, dsp)) { 50014e3c9f44SBill Pijewski if (dsp->dsa_outfd == zc->zc_cookie && 50024e3c9f44SBill Pijewski dsp->dsa_proc == curproc) 50034e3c9f44SBill Pijewski break; 50044e3c9f44SBill Pijewski } 50054e3c9f44SBill Pijewski 50064e3c9f44SBill Pijewski if (dsp != NULL) 50074e3c9f44SBill Pijewski zc->zc_cookie = *(dsp->dsa_off); 50084e3c9f44SBill Pijewski else 5009be6fd75aSMatthew Ahrens error = SET_ERROR(ENOENT); 50104e3c9f44SBill Pijewski 50114e3c9f44SBill Pijewski mutex_exit(&ds->ds_sendstream_lock); 50124e3c9f44SBill Pijewski dsl_dataset_rele(ds, FTAG); 50133b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 50144e3c9f44SBill Pijewski return (error); 50154e3c9f44SBill Pijewski } 50164e3c9f44SBill Pijewski 5017ea8dc4b6Seschrock static int 5018ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc) 5019ea8dc4b6Seschrock { 5020ea8dc4b6Seschrock int id, error; 5021ea8dc4b6Seschrock 5022ea8dc4b6Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id, 5023ea8dc4b6Seschrock &zc->zc_inject_record); 5024ea8dc4b6Seschrock 5025ea8dc4b6Seschrock if (error == 0) 5026ea8dc4b6Seschrock zc->zc_guid = (uint64_t)id; 5027ea8dc4b6Seschrock 5028ea8dc4b6Seschrock return (error); 5029ea8dc4b6Seschrock } 5030ea8dc4b6Seschrock 5031ea8dc4b6Seschrock static int 5032ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc) 5033ea8dc4b6Seschrock { 5034ea8dc4b6Seschrock return (zio_clear_fault((int)zc->zc_guid)); 5035ea8dc4b6Seschrock } 5036ea8dc4b6Seschrock 5037ea8dc4b6Seschrock static int 5038ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc) 5039ea8dc4b6Seschrock { 5040ea8dc4b6Seschrock int id = (int)zc->zc_guid; 5041ea8dc4b6Seschrock int error; 5042ea8dc4b6Seschrock 5043ea8dc4b6Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name), 5044ea8dc4b6Seschrock &zc->zc_inject_record); 5045ea8dc4b6Seschrock 5046ea8dc4b6Seschrock zc->zc_guid = id; 5047ea8dc4b6Seschrock 5048ea8dc4b6Seschrock return (error); 5049ea8dc4b6Seschrock } 5050ea8dc4b6Seschrock 5051ea8dc4b6Seschrock static int 5052ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc) 5053ea8dc4b6Seschrock { 5054ea8dc4b6Seschrock spa_t *spa; 5055ea8dc4b6Seschrock int error; 5056e9dbad6fSeschrock size_t count = (size_t)zc->zc_nvlist_dst_size; 5057ea8dc4b6Seschrock 5058ea8dc4b6Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) 5059ea8dc4b6Seschrock return (error); 5060ea8dc4b6Seschrock 5061e9dbad6fSeschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst, 5062ea8dc4b6Seschrock &count); 5063ea8dc4b6Seschrock if (error == 0) 5064e9dbad6fSeschrock zc->zc_nvlist_dst_size = count; 5065ea8dc4b6Seschrock else 5066e9dbad6fSeschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa); 5067ea8dc4b6Seschrock 5068ea8dc4b6Seschrock spa_close(spa, FTAG); 5069ea8dc4b6Seschrock 5070ea8dc4b6Seschrock return (error); 5071ea8dc4b6Seschrock } 5072ea8dc4b6Seschrock 5073ea8dc4b6Seschrock static int 5074ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc) 5075ea8dc4b6Seschrock { 5076ea8dc4b6Seschrock spa_t *spa; 5077ea8dc4b6Seschrock vdev_t *vd; 5078bb8b5132Sek int error; 5079ea8dc4b6Seschrock 5080b87f3af3Sperrin /* 5081b87f3af3Sperrin * On zpool clear we also fix up missing slogs 5082b87f3af3Sperrin */ 5083b87f3af3Sperrin mutex_enter(&spa_namespace_lock); 5084b87f3af3Sperrin spa = spa_lookup(zc->zc_name); 5085b87f3af3Sperrin if (spa == NULL) { 5086b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 5087be6fd75aSMatthew Ahrens return (SET_ERROR(EIO)); 5088b87f3af3Sperrin } 5089b24ab676SJeff Bonwick if (spa_get_log_state(spa) == SPA_LOG_MISSING) { 5090b87f3af3Sperrin /* we need to let spa_open/spa_load clear the chains */ 5091b24ab676SJeff Bonwick spa_set_log_state(spa, SPA_LOG_CLEAR); 5092b87f3af3Sperrin } 5093468c413aSTim Haley spa->spa_last_open_failed = 0; 5094b87f3af3Sperrin mutex_exit(&spa_namespace_lock); 5095b87f3af3Sperrin 5096c8ee1847SVictor Latushkin if (zc->zc_cookie & ZPOOL_NO_REWIND) { 5097468c413aSTim Haley error = spa_open(zc->zc_name, &spa, FTAG); 5098468c413aSTim Haley } else { 5099468c413aSTim Haley nvlist_t *policy; 5100468c413aSTim Haley nvlist_t *config = NULL; 5101468c413aSTim Haley 5102dd328bf6SToomas Soome if (zc->zc_nvlist_src == 0) 5103be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5104468c413aSTim Haley 5105468c413aSTim Haley if ((error = get_nvlist(zc->zc_nvlist_src, 5106468c413aSTim Haley zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) { 5107468c413aSTim Haley error = spa_open_rewind(zc->zc_name, &spa, FTAG, 5108468c413aSTim Haley policy, &config); 5109468c413aSTim Haley if (config != NULL) { 51104b964adaSGeorge Wilson int err; 51114b964adaSGeorge Wilson 51124b964adaSGeorge Wilson if ((err = put_nvlist(zc, config)) != 0) 51134b964adaSGeorge Wilson error = err; 5114468c413aSTim Haley nvlist_free(config); 5115468c413aSTim Haley } 5116468c413aSTim Haley nvlist_free(policy); 5117468c413aSTim Haley } 5118468c413aSTim Haley } 5119468c413aSTim Haley 51203b2aab18SMatthew Ahrens if (error != 0) 5121ea8dc4b6Seschrock return (error); 5122ea8dc4b6Seschrock 5123e0f1c0afSOlaf Faaland /* 5124e0f1c0afSOlaf Faaland * If multihost is enabled, resuming I/O is unsafe as another 5125e0f1c0afSOlaf Faaland * host may have imported the pool. 5126e0f1c0afSOlaf Faaland */ 5127e0f1c0afSOlaf Faaland if (spa_multihost(spa) && spa_suspended(spa)) 5128e0f1c0afSOlaf Faaland return (SET_ERROR(EINVAL)); 5129e0f1c0afSOlaf Faaland 51308f18d1faSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 5131ea8dc4b6Seschrock 5132e9dbad6fSeschrock if (zc->zc_guid == 0) { 5133ea8dc4b6Seschrock vd = NULL; 5134c5904d13Seschrock } else { 5135c5904d13Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); 5136fa94a07fSbrendan if (vd == NULL) { 5137e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, ENODEV); 5138fa94a07fSbrendan spa_close(spa, FTAG); 5139be6fd75aSMatthew Ahrens return (SET_ERROR(ENODEV)); 5140fa94a07fSbrendan } 5141ea8dc4b6Seschrock } 5142ea8dc4b6Seschrock 5143e14bb325SJeff Bonwick vdev_clear(spa, vd); 5144e14bb325SJeff Bonwick 5145e14bb325SJeff Bonwick (void) spa_vdev_state_exit(spa, NULL, 0); 5146ea8dc4b6Seschrock 5147e14bb325SJeff Bonwick /* 5148e14bb325SJeff Bonwick * Resume any suspended I/Os. 5149e14bb325SJeff Bonwick */ 515054d692b7SGeorge Wilson if (zio_resume(spa) != 0) 5151be6fd75aSMatthew Ahrens error = SET_ERROR(EIO); 5152ea8dc4b6Seschrock 5153ea8dc4b6Seschrock spa_close(spa, FTAG); 5154ea8dc4b6Seschrock 515554d692b7SGeorge Wilson return (error); 5156ea8dc4b6Seschrock } 5157ea8dc4b6Seschrock 51584263d13fSGeorge Wilson static int 51594263d13fSGeorge Wilson zfs_ioc_pool_reopen(zfs_cmd_t *zc) 51604263d13fSGeorge Wilson { 51614263d13fSGeorge Wilson spa_t *spa; 51624263d13fSGeorge Wilson int error; 51634263d13fSGeorge Wilson 51644263d13fSGeorge Wilson error = spa_open(zc->zc_name, &spa, FTAG); 51653b2aab18SMatthew Ahrens if (error != 0) 51664263d13fSGeorge Wilson return (error); 51674263d13fSGeorge Wilson 51684263d13fSGeorge Wilson spa_vdev_state_enter(spa, SCL_NONE); 5169d6afdce2SGeorge Wilson 5170d6afdce2SGeorge Wilson /* 5171d6afdce2SGeorge Wilson * If a resilver is already in progress then set the 5172d6afdce2SGeorge Wilson * spa_scrub_reopen flag to B_TRUE so that we don't restart 5173d6afdce2SGeorge Wilson * the scan as a side effect of the reopen. Otherwise, let 5174d6afdce2SGeorge Wilson * vdev_open() decided if a resilver is required. 5175d6afdce2SGeorge Wilson */ 5176d6afdce2SGeorge Wilson spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); 51774263d13fSGeorge Wilson vdev_reopen(spa->spa_root_vdev); 5178d6afdce2SGeorge Wilson spa->spa_scrub_reopen = B_FALSE; 5179d6afdce2SGeorge Wilson 51804263d13fSGeorge Wilson (void) spa_vdev_state_exit(spa, NULL, 0); 51814263d13fSGeorge Wilson spa_close(spa, FTAG); 51824263d13fSGeorge Wilson return (0); 51834263d13fSGeorge Wilson } 51843cb34c60Sahrens /* 51853cb34c60Sahrens * inputs: 51863cb34c60Sahrens * zc_name name of filesystem 51873cb34c60Sahrens * 5188681d9761SEric Taylor * outputs: 5189681d9761SEric Taylor * zc_string name of conflicting snapshot, if there is one 51903cb34c60Sahrens */ 519199653d4eSeschrock static int 519299653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc) 519399653d4eSeschrock { 5194a4b8c9aaSAndrew Stormont dsl_pool_t *dp; 5195a4b8c9aaSAndrew Stormont dsl_dataset_t *ds, *ods; 5196a4b8c9aaSAndrew Stormont char origin[ZFS_MAX_DATASET_NAME_LEN]; 51970b69c2f0Sahrens char *cp; 5198a4b8c9aaSAndrew Stormont int error; 5199a4b8c9aaSAndrew Stormont 5200add927f8Sloli zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 5201add927f8Sloli if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 || 5202add927f8Sloli strchr(zc->zc_name, '%')) 5203add927f8Sloli return (SET_ERROR(EINVAL)); 5204add927f8Sloli 5205a4b8c9aaSAndrew Stormont error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 5206a4b8c9aaSAndrew Stormont if (error != 0) 5207a4b8c9aaSAndrew Stormont return (error); 5208a4b8c9aaSAndrew Stormont 5209a4b8c9aaSAndrew Stormont error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds); 5210a4b8c9aaSAndrew Stormont if (error != 0) { 5211a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 5212a4b8c9aaSAndrew Stormont return (error); 5213a4b8c9aaSAndrew Stormont } 5214a4b8c9aaSAndrew Stormont 5215a4b8c9aaSAndrew Stormont if (!dsl_dir_is_clone(ds->ds_dir)) { 5216a4b8c9aaSAndrew Stormont dsl_dataset_rele(ds, FTAG); 5217a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 5218a4b8c9aaSAndrew Stormont return (SET_ERROR(EINVAL)); 5219a4b8c9aaSAndrew Stormont } 5220a4b8c9aaSAndrew Stormont 5221a4b8c9aaSAndrew Stormont error = dsl_dataset_hold_obj(dp, 5222a4b8c9aaSAndrew Stormont dsl_dir_phys(ds->ds_dir)->dd_origin_obj, FTAG, &ods); 5223a4b8c9aaSAndrew Stormont if (error != 0) { 5224a4b8c9aaSAndrew Stormont dsl_dataset_rele(ds, FTAG); 5225a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 5226a4b8c9aaSAndrew Stormont return (error); 5227a4b8c9aaSAndrew Stormont } 5228a4b8c9aaSAndrew Stormont 5229a4b8c9aaSAndrew Stormont dsl_dataset_name(ods, origin); 5230a4b8c9aaSAndrew Stormont dsl_dataset_rele(ods, FTAG); 5231a4b8c9aaSAndrew Stormont dsl_dataset_rele(ds, FTAG); 5232a4b8c9aaSAndrew Stormont dsl_pool_rele(dp, FTAG); 52330b69c2f0Sahrens 52340b69c2f0Sahrens /* 52350b69c2f0Sahrens * We don't need to unmount *all* the origin fs's snapshots, but 52360b69c2f0Sahrens * it's easier. 52370b69c2f0Sahrens */ 5238a4b8c9aaSAndrew Stormont cp = strchr(origin, '@'); 52390b69c2f0Sahrens if (cp) 52400b69c2f0Sahrens *cp = '\0'; 5241a4b8c9aaSAndrew Stormont (void) dmu_objset_find(origin, 52423b2aab18SMatthew Ahrens zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS); 5243681d9761SEric Taylor return (dsl_dataset_promote(zc->zc_name, zc->zc_string)); 524499653d4eSeschrock } 524599653d4eSeschrock 524614843421SMatthew Ahrens /* 524714843421SMatthew Ahrens * Retrieve a single {user|group}{used|quota}@... property. 524814843421SMatthew Ahrens * 524914843421SMatthew Ahrens * inputs: 525014843421SMatthew Ahrens * zc_name name of filesystem 525114843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 525214843421SMatthew Ahrens * zc_value domain name (eg. "S-1-234-567-89") 525314843421SMatthew Ahrens * zc_guid RID/UID/GID 525414843421SMatthew Ahrens * 525514843421SMatthew Ahrens * outputs: 525614843421SMatthew Ahrens * zc_cookie property value 525714843421SMatthew Ahrens */ 525814843421SMatthew Ahrens static int 525914843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc) 526014843421SMatthew Ahrens { 526114843421SMatthew Ahrens zfsvfs_t *zfsvfs; 526214843421SMatthew Ahrens int error; 526314843421SMatthew Ahrens 526414843421SMatthew Ahrens if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS) 5265be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 526614843421SMatthew Ahrens 52671412a1a2SMark Shellenbaum error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 52683b2aab18SMatthew Ahrens if (error != 0) 526914843421SMatthew Ahrens return (error); 527014843421SMatthew Ahrens 527114843421SMatthew Ahrens error = zfs_userspace_one(zfsvfs, 527214843421SMatthew Ahrens zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie); 527314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 527414843421SMatthew Ahrens 527514843421SMatthew Ahrens return (error); 527614843421SMatthew Ahrens } 527714843421SMatthew Ahrens 527814843421SMatthew Ahrens /* 527914843421SMatthew Ahrens * inputs: 528014843421SMatthew Ahrens * zc_name name of filesystem 528114843421SMatthew Ahrens * zc_cookie zap cursor 528214843421SMatthew Ahrens * zc_objset_type zfs_userquota_prop_t 528314843421SMatthew Ahrens * zc_nvlist_dst[_size] buffer to fill (not really an nvlist) 528414843421SMatthew Ahrens * 528514843421SMatthew Ahrens * outputs: 528614843421SMatthew Ahrens * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) 528714843421SMatthew Ahrens * zc_cookie zap cursor 528814843421SMatthew Ahrens */ 528914843421SMatthew Ahrens static int 529014843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc) 529114843421SMatthew Ahrens { 529214843421SMatthew Ahrens zfsvfs_t *zfsvfs; 5293eeb85002STim Haley int bufsize = zc->zc_nvlist_dst_size; 529414843421SMatthew Ahrens 5295eeb85002STim Haley if (bufsize <= 0) 5296be6fd75aSMatthew Ahrens return (SET_ERROR(ENOMEM)); 5297eeb85002STim Haley 52981412a1a2SMark Shellenbaum int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); 52993b2aab18SMatthew Ahrens if (error != 0) 530014843421SMatthew Ahrens return (error); 530114843421SMatthew Ahrens 530214843421SMatthew Ahrens void *buf = kmem_alloc(bufsize, KM_SLEEP); 530314843421SMatthew Ahrens 530414843421SMatthew Ahrens error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, 530514843421SMatthew Ahrens buf, &zc->zc_nvlist_dst_size); 530614843421SMatthew Ahrens 530714843421SMatthew Ahrens if (error == 0) { 530814843421SMatthew Ahrens error = xcopyout(buf, 530914843421SMatthew Ahrens (void *)(uintptr_t)zc->zc_nvlist_dst, 531014843421SMatthew Ahrens zc->zc_nvlist_dst_size); 531114843421SMatthew Ahrens } 531214843421SMatthew Ahrens kmem_free(buf, bufsize); 531314843421SMatthew Ahrens zfsvfs_rele(zfsvfs, FTAG); 531414843421SMatthew Ahrens 531514843421SMatthew Ahrens return (error); 531614843421SMatthew Ahrens } 531714843421SMatthew Ahrens 531814843421SMatthew Ahrens /* 531914843421SMatthew Ahrens * inputs: 532014843421SMatthew Ahrens * zc_name name of filesystem 532114843421SMatthew Ahrens * 532214843421SMatthew Ahrens * outputs: 532314843421SMatthew Ahrens * none 532414843421SMatthew Ahrens */ 532514843421SMatthew Ahrens static int 532614843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) 532714843421SMatthew Ahrens { 532814843421SMatthew Ahrens objset_t *os; 53291195e687SMark J Musante int error = 0; 533014843421SMatthew Ahrens zfsvfs_t *zfsvfs; 533114843421SMatthew Ahrens 533214843421SMatthew Ahrens if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) { 5333503ad85cSMatthew Ahrens if (!dmu_objset_userused_enabled(zfsvfs->z_os)) { 533414843421SMatthew Ahrens /* 533514843421SMatthew Ahrens * If userused is not enabled, it may be because the 533614843421SMatthew Ahrens * objset needs to be closed & reopened (to grow the 533714843421SMatthew Ahrens * objset_phys_t). Suspend/resume the fs will do that. 533814843421SMatthew Ahrens */ 53395f5913bbSAndriy Gapon dsl_dataset_t *ds, *newds; 5340690041b9SAndriy Gapon 5341690041b9SAndriy Gapon ds = dmu_objset_ds(zfsvfs->z_os); 5342503ad85cSMatthew Ahrens error = zfs_suspend_fs(zfsvfs); 534391948b51SKeith M Wesolowski if (error == 0) { 53445f5913bbSAndriy Gapon dmu_objset_refresh_ownership(ds, &newds, 5345eb633035STom Caputi B_TRUE, zfsvfs); 53465f5913bbSAndriy Gapon error = zfs_resume_fs(zfsvfs, newds); 534791948b51SKeith M Wesolowski } 534814843421SMatthew Ahrens } 534914843421SMatthew Ahrens if (error == 0) 535014843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(zfsvfs->z_os); 535114843421SMatthew Ahrens VFS_RELE(zfsvfs->z_vfs); 535214843421SMatthew Ahrens } else { 5353503ad85cSMatthew Ahrens /* XXX kind of reading contents without owning */ 5354eb633035STom Caputi error = dmu_objset_hold_flags(zc->zc_name, B_TRUE, FTAG, &os); 53553b2aab18SMatthew Ahrens if (error != 0) 535614843421SMatthew Ahrens return (error); 535714843421SMatthew Ahrens 535814843421SMatthew Ahrens error = dmu_objset_userspace_upgrade(os); 5359eb633035STom Caputi dmu_objset_rele_flags(os, B_TRUE, FTAG); 536014843421SMatthew Ahrens } 536114843421SMatthew Ahrens 536214843421SMatthew Ahrens return (error); 536314843421SMatthew Ahrens } 536414843421SMatthew Ahrens 5365ecd6cf80Smarks /* 5366ecd6cf80Smarks * We don't want to have a hard dependency 5367ecd6cf80Smarks * against some special symbols in sharefs 5368da6c28aaSamw * nfs, and smbsrv. Determine them if needed when 5369ecd6cf80Smarks * the first file system is shared. 5370da6c28aaSamw * Neither sharefs, nfs or smbsrv are unloadable modules. 5371ecd6cf80Smarks */ 5372da6c28aaSamw int (*znfsexport_fs)(void *arg); 5373ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); 5374da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share); 5375da6c28aaSamw 5376da6c28aaSamw int zfs_nfsshare_inited; 5377da6c28aaSamw int zfs_smbshare_inited; 5378ecd6cf80Smarks 5379ecd6cf80Smarks ddi_modhandle_t nfs_mod; 5380ecd6cf80Smarks ddi_modhandle_t sharefs_mod; 5381da6c28aaSamw ddi_modhandle_t smbsrv_mod; 5382ecd6cf80Smarks kmutex_t zfs_share_lock; 5383ecd6cf80Smarks 5384da6c28aaSamw static int 5385da6c28aaSamw zfs_init_sharefs() 5386da6c28aaSamw { 5387da6c28aaSamw int error; 5388da6c28aaSamw 5389da6c28aaSamw ASSERT(MUTEX_HELD(&zfs_share_lock)); 5390da6c28aaSamw /* Both NFS and SMB shares also require sharetab support. */ 5391da6c28aaSamw if (sharefs_mod == NULL && ((sharefs_mod = 5392da6c28aaSamw ddi_modopen("fs/sharefs", 5393da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 5394be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5395da6c28aaSamw } 5396da6c28aaSamw if (zshare_fs == NULL && ((zshare_fs = 5397da6c28aaSamw (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) 5398da6c28aaSamw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { 5399be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5400da6c28aaSamw } 5401da6c28aaSamw return (0); 5402da6c28aaSamw } 5403da6c28aaSamw 5404ecd6cf80Smarks static int 5405ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc) 5406ecd6cf80Smarks { 5407ecd6cf80Smarks int error; 5408ecd6cf80Smarks int opcode; 5409ecd6cf80Smarks 5410da6c28aaSamw switch (zc->zc_share.z_sharetype) { 5411da6c28aaSamw case ZFS_SHARE_NFS: 5412da6c28aaSamw case ZFS_UNSHARE_NFS: 5413da6c28aaSamw if (zfs_nfsshare_inited == 0) { 5414da6c28aaSamw mutex_enter(&zfs_share_lock); 5415da6c28aaSamw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", 5416da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 5417da6c28aaSamw mutex_exit(&zfs_share_lock); 5418be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5419da6c28aaSamw } 5420da6c28aaSamw if (znfsexport_fs == NULL && 5421da6c28aaSamw ((znfsexport_fs = (int (*)(void *)) 5422da6c28aaSamw ddi_modsym(nfs_mod, 5423da6c28aaSamw "nfs_export", &error)) == NULL)) { 5424da6c28aaSamw mutex_exit(&zfs_share_lock); 5425be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5426da6c28aaSamw } 5427da6c28aaSamw error = zfs_init_sharefs(); 54283b2aab18SMatthew Ahrens if (error != 0) { 5429da6c28aaSamw mutex_exit(&zfs_share_lock); 5430be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5431da6c28aaSamw } 5432da6c28aaSamw zfs_nfsshare_inited = 1; 5433ecd6cf80Smarks mutex_exit(&zfs_share_lock); 5434ecd6cf80Smarks } 5435da6c28aaSamw break; 5436da6c28aaSamw case ZFS_SHARE_SMB: 5437da6c28aaSamw case ZFS_UNSHARE_SMB: 5438da6c28aaSamw if (zfs_smbshare_inited == 0) { 5439da6c28aaSamw mutex_enter(&zfs_share_lock); 5440da6c28aaSamw if (smbsrv_mod == NULL && ((smbsrv_mod = 5441da6c28aaSamw ddi_modopen("drv/smbsrv", 5442da6c28aaSamw KRTLD_MODE_FIRST, &error)) == NULL)) { 5443da6c28aaSamw mutex_exit(&zfs_share_lock); 5444be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5445da6c28aaSamw } 5446da6c28aaSamw if (zsmbexport_fs == NULL && ((zsmbexport_fs = 5447da6c28aaSamw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, 5448faa1795aSjb "smb_server_share", &error)) == NULL)) { 5449da6c28aaSamw mutex_exit(&zfs_share_lock); 5450be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5451da6c28aaSamw } 5452da6c28aaSamw error = zfs_init_sharefs(); 54533b2aab18SMatthew Ahrens if (error != 0) { 5454da6c28aaSamw mutex_exit(&zfs_share_lock); 5455be6fd75aSMatthew Ahrens return (SET_ERROR(ENOSYS)); 5456da6c28aaSamw } 5457da6c28aaSamw zfs_smbshare_inited = 1; 5458ecd6cf80Smarks mutex_exit(&zfs_share_lock); 5459ecd6cf80Smarks } 5460da6c28aaSamw break; 5461da6c28aaSamw default: 5462be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5463da6c28aaSamw } 5464ecd6cf80Smarks 5465da6c28aaSamw switch (zc->zc_share.z_sharetype) { 5466da6c28aaSamw case ZFS_SHARE_NFS: 5467da6c28aaSamw case ZFS_UNSHARE_NFS: 5468da6c28aaSamw if (error = 5469da6c28aaSamw znfsexport_fs((void *) 5470da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata)) 5471da6c28aaSamw return (error); 5472da6c28aaSamw break; 5473da6c28aaSamw case ZFS_SHARE_SMB: 5474da6c28aaSamw case ZFS_UNSHARE_SMB: 5475da6c28aaSamw if (error = zsmbexport_fs((void *) 5476da6c28aaSamw (uintptr_t)zc->zc_share.z_exportdata, 5477da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? 5478743a77edSAlan Wright B_TRUE: B_FALSE)) { 5479da6c28aaSamw return (error); 5480ecd6cf80Smarks } 5481da6c28aaSamw break; 5482ecd6cf80Smarks } 5483ecd6cf80Smarks 5484da6c28aaSamw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || 5485da6c28aaSamw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? 5486ecd6cf80Smarks SHAREFS_ADD : SHAREFS_REMOVE; 5487ecd6cf80Smarks 5488da6c28aaSamw /* 5489da6c28aaSamw * Add or remove share from sharetab 5490da6c28aaSamw */ 5491ecd6cf80Smarks error = zshare_fs(opcode, 5492ecd6cf80Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata, 5493ecd6cf80Smarks zc->zc_share.z_sharemax); 5494ecd6cf80Smarks 5495ecd6cf80Smarks return (error); 5496ecd6cf80Smarks 5497ecd6cf80Smarks } 5498ecd6cf80Smarks 5499743a77edSAlan Wright ace_t full_access[] = { 5500743a77edSAlan Wright {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0} 5501743a77edSAlan Wright }; 5502743a77edSAlan Wright 550399d5e173STim Haley /* 550499d5e173STim Haley * inputs: 550599d5e173STim Haley * zc_name name of containing filesystem 550699d5e173STim Haley * zc_obj object # beyond which we want next in-use object # 550799d5e173STim Haley * 550899d5e173STim Haley * outputs: 550999d5e173STim Haley * zc_obj next in-use object # 551099d5e173STim Haley */ 551199d5e173STim Haley static int 551299d5e173STim Haley zfs_ioc_next_obj(zfs_cmd_t *zc) 551399d5e173STim Haley { 551499d5e173STim Haley objset_t *os = NULL; 551599d5e173STim Haley int error; 551699d5e173STim Haley 5517eb633035STom Caputi error = dmu_objset_hold_flags(zc->zc_name, B_TRUE, FTAG, &os); 55183b2aab18SMatthew Ahrens if (error != 0) 551999d5e173STim Haley return (error); 552099d5e173STim Haley 552199d5e173STim Haley error = dmu_object_next(os, &zc->zc_obj, B_FALSE, 5522c1379625SJustin T. Gibbs dsl_dataset_phys(os->os_dsl_dataset)->ds_prev_snap_txg); 552399d5e173STim Haley 552499d5e173STim Haley dmu_objset_rele(os, FTAG); 552599d5e173STim Haley return (error); 552699d5e173STim Haley } 552799d5e173STim Haley 552899d5e173STim Haley /* 552999d5e173STim Haley * inputs: 553099d5e173STim Haley * zc_name name of filesystem 553199d5e173STim Haley * zc_value prefix name for snapshot 553299d5e173STim Haley * zc_cleanup_fd cleanup-on-exit file descriptor for calling process 553399d5e173STim Haley * 553499d5e173STim Haley * outputs: 55354445fffbSMatthew Ahrens * zc_value short name of new snapshot 553699d5e173STim Haley */ 553799d5e173STim Haley static int 553899d5e173STim Haley zfs_ioc_tmp_snapshot(zfs_cmd_t *zc) 553999d5e173STim Haley { 554099d5e173STim Haley char *snap_name; 55413b2aab18SMatthew Ahrens char *hold_name; 554299d5e173STim Haley int error; 55433b2aab18SMatthew Ahrens minor_t minor; 554499d5e173STim Haley 55453b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor); 55463b2aab18SMatthew Ahrens if (error != 0) 554799d5e173STim Haley return (error); 554899d5e173STim Haley 55493b2aab18SMatthew Ahrens snap_name = kmem_asprintf("%s-%016llx", zc->zc_value, 55503b2aab18SMatthew Ahrens (u_longlong_t)ddi_get_lbolt64()); 55513b2aab18SMatthew Ahrens hold_name = kmem_asprintf("%%%s", zc->zc_value); 55523b2aab18SMatthew Ahrens 55533b2aab18SMatthew Ahrens error = dsl_dataset_snapshot_tmp(zc->zc_name, snap_name, minor, 55543b2aab18SMatthew Ahrens hold_name); 55553b2aab18SMatthew Ahrens if (error == 0) 55563b2aab18SMatthew Ahrens (void) strcpy(zc->zc_value, snap_name); 555799d5e173STim Haley strfree(snap_name); 55583b2aab18SMatthew Ahrens strfree(hold_name); 55593b2aab18SMatthew Ahrens zfs_onexit_fd_rele(zc->zc_cleanup_fd); 55603b2aab18SMatthew Ahrens return (error); 556199d5e173STim Haley } 556299d5e173STim Haley 556399d5e173STim Haley /* 556499d5e173STim Haley * inputs: 556599d5e173STim Haley * zc_name name of "to" snapshot 556699d5e173STim Haley * zc_value name of "from" snapshot 556799d5e173STim Haley * zc_cookie file descriptor to write diff data on 556899d5e173STim Haley * 556999d5e173STim Haley * outputs: 557099d5e173STim Haley * dmu_diff_record_t's to the file descriptor 557199d5e173STim Haley */ 557299d5e173STim Haley static int 557399d5e173STim Haley zfs_ioc_diff(zfs_cmd_t *zc) 557499d5e173STim Haley { 557599d5e173STim Haley file_t *fp; 557699d5e173STim Haley offset_t off; 557799d5e173STim Haley int error; 557899d5e173STim Haley 557999d5e173STim Haley fp = getf(zc->zc_cookie); 55803b2aab18SMatthew Ahrens if (fp == NULL) 5581be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 558299d5e173STim Haley 558399d5e173STim Haley off = fp->f_offset; 558499d5e173STim Haley 55853b2aab18SMatthew Ahrens error = dmu_diff(zc->zc_name, zc->zc_value, fp->f_vnode, &off); 558699d5e173STim Haley 558799d5e173STim Haley if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 558899d5e173STim Haley fp->f_offset = off; 558999d5e173STim Haley releasef(zc->zc_cookie); 559099d5e173STim Haley 559199d5e173STim Haley return (error); 559299d5e173STim Haley } 559399d5e173STim Haley 5594743a77edSAlan Wright /* 5595743a77edSAlan Wright * Remove all ACL files in shares dir 5596743a77edSAlan Wright */ 5597743a77edSAlan Wright static int 5598743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp) 5599743a77edSAlan Wright { 5600743a77edSAlan Wright zap_cursor_t zc; 5601743a77edSAlan Wright zap_attribute_t zap; 5602743a77edSAlan Wright zfsvfs_t *zfsvfs = dzp->z_zfsvfs; 5603743a77edSAlan Wright int error; 5604743a77edSAlan Wright 5605743a77edSAlan Wright for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); 5606743a77edSAlan Wright (error = zap_cursor_retrieve(&zc, &zap)) == 0; 5607743a77edSAlan Wright zap_cursor_advance(&zc)) { 5608743a77edSAlan Wright if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred, 5609743a77edSAlan Wright NULL, 0)) != 0) 5610743a77edSAlan Wright break; 5611743a77edSAlan Wright } 5612743a77edSAlan Wright zap_cursor_fini(&zc); 5613743a77edSAlan Wright return (error); 5614743a77edSAlan Wright } 5615743a77edSAlan Wright 5616743a77edSAlan Wright static int 5617743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc) 5618743a77edSAlan Wright { 5619743a77edSAlan Wright vnode_t *vp; 5620743a77edSAlan Wright znode_t *dzp; 5621743a77edSAlan Wright vnode_t *resourcevp = NULL; 5622743a77edSAlan Wright znode_t *sharedir; 5623743a77edSAlan Wright zfsvfs_t *zfsvfs; 5624743a77edSAlan Wright nvlist_t *nvlist; 5625743a77edSAlan Wright char *src, *target; 5626743a77edSAlan Wright vattr_t vattr; 5627743a77edSAlan Wright vsecattr_t vsec; 5628743a77edSAlan Wright int error = 0; 5629743a77edSAlan Wright 5630743a77edSAlan Wright if ((error = lookupname(zc->zc_value, UIO_SYSSPACE, 5631743a77edSAlan Wright NO_FOLLOW, NULL, &vp)) != 0) 5632743a77edSAlan Wright return (error); 5633743a77edSAlan Wright 5634743a77edSAlan Wright /* Now make sure mntpnt and dataset are ZFS */ 5635743a77edSAlan Wright 5636743a77edSAlan Wright if (vp->v_vfsp->vfs_fstype != zfsfstype || 5637743a77edSAlan Wright (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource), 5638743a77edSAlan Wright zc->zc_name) != 0)) { 5639743a77edSAlan Wright VN_RELE(vp); 5640be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5641743a77edSAlan Wright } 5642743a77edSAlan Wright 5643743a77edSAlan Wright dzp = VTOZ(vp); 5644743a77edSAlan Wright zfsvfs = dzp->z_zfsvfs; 5645743a77edSAlan Wright ZFS_ENTER(zfsvfs); 5646743a77edSAlan Wright 56479e1320c0SMark Shellenbaum /* 56489e1320c0SMark Shellenbaum * Create share dir if its missing. 56499e1320c0SMark Shellenbaum */ 56509e1320c0SMark Shellenbaum mutex_enter(&zfsvfs->z_lock); 56519e1320c0SMark Shellenbaum if (zfsvfs->z_shares_dir == 0) { 56529e1320c0SMark Shellenbaum dmu_tx_t *tx; 56539e1320c0SMark Shellenbaum 56549e1320c0SMark Shellenbaum tx = dmu_tx_create(zfsvfs->z_os); 56559e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE, 56569e1320c0SMark Shellenbaum ZFS_SHARES_DIR); 56579e1320c0SMark Shellenbaum dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); 56589e1320c0SMark Shellenbaum error = dmu_tx_assign(tx, TXG_WAIT); 56593b2aab18SMatthew Ahrens if (error != 0) { 56609e1320c0SMark Shellenbaum dmu_tx_abort(tx); 56619e1320c0SMark Shellenbaum } else { 56629e1320c0SMark Shellenbaum error = zfs_create_share_dir(zfsvfs, tx); 56639e1320c0SMark Shellenbaum dmu_tx_commit(tx); 56649e1320c0SMark Shellenbaum } 56653b2aab18SMatthew Ahrens if (error != 0) { 56669e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 56679e1320c0SMark Shellenbaum VN_RELE(vp); 56689e1320c0SMark Shellenbaum ZFS_EXIT(zfsvfs); 56699e1320c0SMark Shellenbaum return (error); 56709e1320c0SMark Shellenbaum } 56719e1320c0SMark Shellenbaum } 56729e1320c0SMark Shellenbaum mutex_exit(&zfsvfs->z_lock); 56739e1320c0SMark Shellenbaum 56749e1320c0SMark Shellenbaum ASSERT(zfsvfs->z_shares_dir); 5675743a77edSAlan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) { 56769e1320c0SMark Shellenbaum VN_RELE(vp); 5677743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5678743a77edSAlan Wright return (error); 5679743a77edSAlan Wright } 5680743a77edSAlan Wright 5681743a77edSAlan Wright switch (zc->zc_cookie) { 5682743a77edSAlan Wright case ZFS_SMB_ACL_ADD: 5683743a77edSAlan Wright vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE; 5684743a77edSAlan Wright vattr.va_type = VREG; 5685743a77edSAlan Wright vattr.va_mode = S_IFREG|0777; 5686743a77edSAlan Wright vattr.va_uid = 0; 5687743a77edSAlan Wright vattr.va_gid = 0; 5688743a77edSAlan Wright 5689743a77edSAlan Wright vsec.vsa_mask = VSA_ACE; 5690743a77edSAlan Wright vsec.vsa_aclentp = &full_access; 5691743a77edSAlan Wright vsec.vsa_aclentsz = sizeof (full_access); 5692743a77edSAlan Wright vsec.vsa_aclcnt = 1; 5693743a77edSAlan Wright 5694743a77edSAlan Wright error = VOP_CREATE(ZTOV(sharedir), zc->zc_string, 5695743a77edSAlan Wright &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec); 5696743a77edSAlan Wright if (resourcevp) 5697743a77edSAlan Wright VN_RELE(resourcevp); 5698743a77edSAlan Wright break; 5699743a77edSAlan Wright 5700743a77edSAlan Wright case ZFS_SMB_ACL_REMOVE: 5701743a77edSAlan Wright error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred, 5702743a77edSAlan Wright NULL, 0); 5703743a77edSAlan Wright break; 5704743a77edSAlan Wright 5705743a77edSAlan Wright case ZFS_SMB_ACL_RENAME: 5706743a77edSAlan Wright if ((error = get_nvlist(zc->zc_nvlist_src, 5707478ed9adSEric Taylor zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) { 5708743a77edSAlan Wright VN_RELE(vp); 57098f5190a5SDan McDonald VN_RELE(ZTOV(sharedir)); 5710743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5711743a77edSAlan Wright return (error); 5712743a77edSAlan Wright } 5713743a77edSAlan Wright if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) || 5714743a77edSAlan Wright nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET, 5715743a77edSAlan Wright &target)) { 5716743a77edSAlan Wright VN_RELE(vp); 571789459e17SMark Shellenbaum VN_RELE(ZTOV(sharedir)); 5718743a77edSAlan Wright ZFS_EXIT(zfsvfs); 57191195e687SMark J Musante nvlist_free(nvlist); 5720743a77edSAlan Wright return (error); 5721743a77edSAlan Wright } 5722743a77edSAlan Wright error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target, 5723743a77edSAlan Wright kcred, NULL, 0); 5724743a77edSAlan Wright nvlist_free(nvlist); 5725743a77edSAlan Wright break; 5726743a77edSAlan Wright 5727743a77edSAlan Wright case ZFS_SMB_ACL_PURGE: 5728743a77edSAlan Wright error = zfs_smb_acl_purge(sharedir); 5729743a77edSAlan Wright break; 5730743a77edSAlan Wright 5731743a77edSAlan Wright default: 5732be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 5733743a77edSAlan Wright break; 5734743a77edSAlan Wright } 5735743a77edSAlan Wright 5736743a77edSAlan Wright VN_RELE(vp); 5737743a77edSAlan Wright VN_RELE(ZTOV(sharedir)); 5738743a77edSAlan Wright 5739743a77edSAlan Wright ZFS_EXIT(zfsvfs); 5740743a77edSAlan Wright 5741743a77edSAlan Wright return (error); 5742743a77edSAlan Wright } 5743743a77edSAlan Wright 5744842727c2SChris Kirby /* 57453b2aab18SMatthew Ahrens * innvl: { 57463b2aab18SMatthew Ahrens * "holds" -> { snapname -> holdname (string), ... } 57473b2aab18SMatthew Ahrens * (optional) "cleanup_fd" -> fd (int32) 57483b2aab18SMatthew Ahrens * } 5749842727c2SChris Kirby * 57503b2aab18SMatthew Ahrens * outnvl: { 57513b2aab18SMatthew Ahrens * snapname -> error value (int32) 57523b2aab18SMatthew Ahrens * ... 57533b2aab18SMatthew Ahrens * } 5754842727c2SChris Kirby */ 57553b2aab18SMatthew Ahrens /* ARGSUSED */ 5756842727c2SChris Kirby static int 57573b2aab18SMatthew Ahrens zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) 5758842727c2SChris Kirby { 5759752fd8daSJosef 'Jeff' Sipek nvpair_t *pair; 57603b2aab18SMatthew Ahrens nvlist_t *holds; 57613b2aab18SMatthew Ahrens int cleanup_fd = -1; 5762a7f53a56SChris Kirby int error; 5763a7f53a56SChris Kirby minor_t minor = 0; 5764842727c2SChris Kirby 57653b2aab18SMatthew Ahrens error = nvlist_lookup_nvlist(args, "holds", &holds); 57663b2aab18SMatthew Ahrens if (error != 0) 5767be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 5768a7f53a56SChris Kirby 5769752fd8daSJosef 'Jeff' Sipek /* make sure the user didn't pass us any invalid (empty) tags */ 5770752fd8daSJosef 'Jeff' Sipek for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; 5771752fd8daSJosef 'Jeff' Sipek pair = nvlist_next_nvpair(holds, pair)) { 5772752fd8daSJosef 'Jeff' Sipek char *htag; 5773752fd8daSJosef 'Jeff' Sipek 5774752fd8daSJosef 'Jeff' Sipek error = nvpair_value_string(pair, &htag); 5775752fd8daSJosef 'Jeff' Sipek if (error != 0) 5776752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(error)); 5777752fd8daSJosef 'Jeff' Sipek 5778752fd8daSJosef 'Jeff' Sipek if (strlen(htag) == 0) 5779752fd8daSJosef 'Jeff' Sipek return (SET_ERROR(EINVAL)); 5780752fd8daSJosef 'Jeff' Sipek } 5781752fd8daSJosef 'Jeff' Sipek 57823b2aab18SMatthew Ahrens if (nvlist_lookup_int32(args, "cleanup_fd", &cleanup_fd) == 0) { 57833b2aab18SMatthew Ahrens error = zfs_onexit_fd_hold(cleanup_fd, &minor); 57843b2aab18SMatthew Ahrens if (error != 0) 5785a7f53a56SChris Kirby return (error); 5786a7f53a56SChris Kirby } 5787a7f53a56SChris Kirby 57883b2aab18SMatthew Ahrens error = dsl_dataset_user_hold(holds, minor, errlist); 57893b2aab18SMatthew Ahrens if (minor != 0) 57903b2aab18SMatthew Ahrens zfs_onexit_fd_rele(cleanup_fd); 5791a7f53a56SChris Kirby return (error); 5792842727c2SChris Kirby } 5793842727c2SChris Kirby 5794842727c2SChris Kirby /* 57953b2aab18SMatthew Ahrens * innvl is not used. 5796842727c2SChris Kirby * 57973b2aab18SMatthew Ahrens * outnvl: { 57983b2aab18SMatthew Ahrens * holdname -> time added (uint64 seconds since epoch) 57993b2aab18SMatthew Ahrens * ... 58003b2aab18SMatthew Ahrens * } 5801842727c2SChris Kirby */ 58023b2aab18SMatthew Ahrens /* ARGSUSED */ 5803842727c2SChris Kirby static int 58043b2aab18SMatthew Ahrens zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) 5805842727c2SChris Kirby { 58069c2acf00SAlek Pinchuk ASSERT3P(args, ==, NULL); 58073b2aab18SMatthew Ahrens return (dsl_dataset_get_holds(snapname, outnvl)); 5808842727c2SChris Kirby } 5809842727c2SChris Kirby 5810842727c2SChris Kirby /* 58113b2aab18SMatthew Ahrens * innvl: { 58123b2aab18SMatthew Ahrens * snapname -> { holdname, ... } 58133b2aab18SMatthew Ahrens * ... 58143b2aab18SMatthew Ahrens * } 5815842727c2SChris Kirby * 58163b2aab18SMatthew Ahrens * outnvl: { 58173b2aab18SMatthew Ahrens * snapname -> error value (int32) 58183b2aab18SMatthew Ahrens * ... 58193b2aab18SMatthew Ahrens * } 5820842727c2SChris Kirby */ 58213b2aab18SMatthew Ahrens /* ARGSUSED */ 5822842727c2SChris Kirby static int 58233b2aab18SMatthew Ahrens zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) 5824842727c2SChris Kirby { 58253b2aab18SMatthew Ahrens return (dsl_dataset_user_release(holds, errlist)); 5826842727c2SChris Kirby } 5827842727c2SChris Kirby 582819b94df9SMatthew Ahrens /* 582919b94df9SMatthew Ahrens * inputs: 583019b94df9SMatthew Ahrens * zc_name name of new filesystem or snapshot 583119b94df9SMatthew Ahrens * zc_value full name of old snapshot 583219b94df9SMatthew Ahrens * 583319b94df9SMatthew Ahrens * outputs: 583419b94df9SMatthew Ahrens * zc_cookie space in bytes 583519b94df9SMatthew Ahrens * zc_objset_type compressed space in bytes 583619b94df9SMatthew Ahrens * zc_perm_action uncompressed space in bytes 583719b94df9SMatthew Ahrens */ 583819b94df9SMatthew Ahrens static int 583919b94df9SMatthew Ahrens zfs_ioc_space_written(zfs_cmd_t *zc) 584019b94df9SMatthew Ahrens { 584119b94df9SMatthew Ahrens int error; 58423b2aab18SMatthew Ahrens dsl_pool_t *dp; 584319b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 584419b94df9SMatthew Ahrens 58453b2aab18SMatthew Ahrens error = dsl_pool_hold(zc->zc_name, FTAG, &dp); 584619b94df9SMatthew Ahrens if (error != 0) 584719b94df9SMatthew Ahrens return (error); 58483b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &new); 58493b2aab18SMatthew Ahrens if (error != 0) { 58503b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 58513b2aab18SMatthew Ahrens return (error); 58523b2aab18SMatthew Ahrens } 58533b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, zc->zc_value, FTAG, &old); 585419b94df9SMatthew Ahrens if (error != 0) { 585519b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 58563b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 585719b94df9SMatthew Ahrens return (error); 585819b94df9SMatthew Ahrens } 585919b94df9SMatthew Ahrens 586019b94df9SMatthew Ahrens error = dsl_dataset_space_written(old, new, &zc->zc_cookie, 586119b94df9SMatthew Ahrens &zc->zc_objset_type, &zc->zc_perm_action); 586219b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 586319b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 58643b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 586519b94df9SMatthew Ahrens return (error); 586619b94df9SMatthew Ahrens } 58673b2aab18SMatthew Ahrens 586819b94df9SMatthew Ahrens /* 58694445fffbSMatthew Ahrens * innvl: { 58704445fffbSMatthew Ahrens * "firstsnap" -> snapshot name 58714445fffbSMatthew Ahrens * } 587219b94df9SMatthew Ahrens * 58734445fffbSMatthew Ahrens * outnvl: { 58744445fffbSMatthew Ahrens * "used" -> space in bytes 58754445fffbSMatthew Ahrens * "compressed" -> compressed space in bytes 58764445fffbSMatthew Ahrens * "uncompressed" -> uncompressed space in bytes 58774445fffbSMatthew Ahrens * } 587819b94df9SMatthew Ahrens */ 587919b94df9SMatthew Ahrens static int 58804445fffbSMatthew Ahrens zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) 588119b94df9SMatthew Ahrens { 588219b94df9SMatthew Ahrens int error; 58833b2aab18SMatthew Ahrens dsl_pool_t *dp; 588419b94df9SMatthew Ahrens dsl_dataset_t *new, *old; 58854445fffbSMatthew Ahrens char *firstsnap; 58864445fffbSMatthew Ahrens uint64_t used, comp, uncomp; 588719b94df9SMatthew Ahrens 58884445fffbSMatthew Ahrens if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) 5889be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 58904445fffbSMatthew Ahrens 58913b2aab18SMatthew Ahrens error = dsl_pool_hold(lastsnap, FTAG, &dp); 589219b94df9SMatthew Ahrens if (error != 0) 589319b94df9SMatthew Ahrens return (error); 58943b2aab18SMatthew Ahrens 58953b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, lastsnap, FTAG, &new); 589624218bebSAndriy Gapon if (error == 0 && !new->ds_is_snapshot) { 589724218bebSAndriy Gapon dsl_dataset_rele(new, FTAG); 589824218bebSAndriy Gapon error = SET_ERROR(EINVAL); 589924218bebSAndriy Gapon } 59003b2aab18SMatthew Ahrens if (error != 0) { 59013b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 59023b2aab18SMatthew Ahrens return (error); 59033b2aab18SMatthew Ahrens } 59043b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, firstsnap, FTAG, &old); 590524218bebSAndriy Gapon if (error == 0 && !old->ds_is_snapshot) { 590624218bebSAndriy Gapon dsl_dataset_rele(old, FTAG); 590724218bebSAndriy Gapon error = SET_ERROR(EINVAL); 590824218bebSAndriy Gapon } 590919b94df9SMatthew Ahrens if (error != 0) { 591019b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 59113b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 591219b94df9SMatthew Ahrens return (error); 591319b94df9SMatthew Ahrens } 591419b94df9SMatthew Ahrens 59154445fffbSMatthew Ahrens error = dsl_dataset_space_wouldfree(old, new, &used, &comp, &uncomp); 591619b94df9SMatthew Ahrens dsl_dataset_rele(old, FTAG); 591719b94df9SMatthew Ahrens dsl_dataset_rele(new, FTAG); 59183b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 59194445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "used", used); 59204445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "compressed", comp); 59214445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "uncompressed", uncomp); 592219b94df9SMatthew Ahrens return (error); 592319b94df9SMatthew Ahrens } 592419b94df9SMatthew Ahrens 5925ecd6cf80Smarks /* 59264445fffbSMatthew Ahrens * innvl: { 59274445fffbSMatthew Ahrens * "fd" -> file descriptor to write stream to (int32) 59284445fffbSMatthew Ahrens * (optional) "fromsnap" -> full snap name to send an incremental from 5929b5152584SMatthew Ahrens * (optional) "largeblockok" -> (value ignored) 5930b5152584SMatthew Ahrens * indicates that blocks > 128KB are permitted 59315d7b4d43SMatthew Ahrens * (optional) "embedok" -> (value ignored) 59325d7b4d43SMatthew Ahrens * presence indicates DRR_WRITE_EMBEDDED records are permitted 59335602294fSDan Kimmel * (optional) "compressok" -> (value ignored) 59345602294fSDan Kimmel * presence indicates compressed DRR_WRITE records are permitted 5935eb633035STom Caputi * (optional) "rawok" -> (value ignored) 5936eb633035STom Caputi * presence indicates raw encrypted records should be used. 59379c3fd121SMatthew Ahrens * (optional) "resume_object" and "resume_offset" -> (uint64) 59389c3fd121SMatthew Ahrens * if present, resume send stream from specified object and offset. 59394445fffbSMatthew Ahrens * } 59404445fffbSMatthew Ahrens * 59414445fffbSMatthew Ahrens * outnvl is unused 5942ecd6cf80Smarks */ 59434445fffbSMatthew Ahrens /* ARGSUSED */ 59444445fffbSMatthew Ahrens static int 59454445fffbSMatthew Ahrens zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 59464445fffbSMatthew Ahrens { 59474445fffbSMatthew Ahrens int error; 59484445fffbSMatthew Ahrens offset_t off; 59493b2aab18SMatthew Ahrens char *fromname = NULL; 59504445fffbSMatthew Ahrens int fd; 5951b5152584SMatthew Ahrens boolean_t largeblockok; 59525d7b4d43SMatthew Ahrens boolean_t embedok; 59535602294fSDan Kimmel boolean_t compressok; 5954eb633035STom Caputi boolean_t rawok; 59559c3fd121SMatthew Ahrens uint64_t resumeobj = 0; 59569c3fd121SMatthew Ahrens uint64_t resumeoff = 0; 59574445fffbSMatthew Ahrens 59584445fffbSMatthew Ahrens error = nvlist_lookup_int32(innvl, "fd", &fd); 59594445fffbSMatthew Ahrens if (error != 0) 5960be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 59614445fffbSMatthew Ahrens 59623b2aab18SMatthew Ahrens (void) nvlist_lookup_string(innvl, "fromsnap", &fromname); 59634445fffbSMatthew Ahrens 5964b5152584SMatthew Ahrens largeblockok = nvlist_exists(innvl, "largeblockok"); 59655d7b4d43SMatthew Ahrens embedok = nvlist_exists(innvl, "embedok"); 59665602294fSDan Kimmel compressok = nvlist_exists(innvl, "compressok"); 5967eb633035STom Caputi rawok = nvlist_exists(innvl, "rawok"); 59685d7b4d43SMatthew Ahrens 59699c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); 59709c3fd121SMatthew Ahrens (void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff); 59719c3fd121SMatthew Ahrens 59724445fffbSMatthew Ahrens file_t *fp = getf(fd); 59733b2aab18SMatthew Ahrens if (fp == NULL) 5974be6fd75aSMatthew Ahrens return (SET_ERROR(EBADF)); 59754445fffbSMatthew Ahrens 59764445fffbSMatthew Ahrens off = fp->f_offset; 59775602294fSDan Kimmel error = dmu_send(snapname, fromname, embedok, largeblockok, compressok, 5978eb633035STom Caputi rawok, fd, resumeobj, resumeoff, fp->f_vnode, &off); 59794445fffbSMatthew Ahrens 59804445fffbSMatthew Ahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0) 59814445fffbSMatthew Ahrens fp->f_offset = off; 59824445fffbSMatthew Ahrens releasef(fd); 59834445fffbSMatthew Ahrens return (error); 59844445fffbSMatthew Ahrens } 59854445fffbSMatthew Ahrens 59864445fffbSMatthew Ahrens /* 59874445fffbSMatthew Ahrens * Determine approximately how large a zfs send stream will be -- the number 59884445fffbSMatthew Ahrens * of bytes that will be written to the fd supplied to zfs_ioc_send_new(). 59894445fffbSMatthew Ahrens * 59904445fffbSMatthew Ahrens * innvl: { 5991643da460SMax Grossman * (optional) "from" -> full snap or bookmark name to send an incremental 5992643da460SMax Grossman * from 59935602294fSDan Kimmel * (optional) "largeblockok" -> (value ignored) 59945602294fSDan Kimmel * indicates that blocks > 128KB are permitted 59955602294fSDan Kimmel * (optional) "embedok" -> (value ignored) 59965602294fSDan Kimmel * presence indicates DRR_WRITE_EMBEDDED records are permitted 59975602294fSDan Kimmel * (optional) "compressok" -> (value ignored) 59985602294fSDan Kimmel * presence indicates compressed DRR_WRITE records are permitted 59994445fffbSMatthew Ahrens * } 60004445fffbSMatthew Ahrens * 60014445fffbSMatthew Ahrens * outnvl: { 60024445fffbSMatthew Ahrens * "space" -> bytes of space (uint64) 60034445fffbSMatthew Ahrens * } 60044445fffbSMatthew Ahrens */ 60054445fffbSMatthew Ahrens static int 60064445fffbSMatthew Ahrens zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 60074445fffbSMatthew Ahrens { 60083b2aab18SMatthew Ahrens dsl_pool_t *dp; 60093b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 60104445fffbSMatthew Ahrens int error; 60114445fffbSMatthew Ahrens char *fromname; 60125602294fSDan Kimmel boolean_t compressok; 6013eb633035STom Caputi boolean_t rawok; 60144445fffbSMatthew Ahrens uint64_t space; 60154445fffbSMatthew Ahrens 60163b2aab18SMatthew Ahrens error = dsl_pool_hold(snapname, FTAG, &dp); 60173b2aab18SMatthew Ahrens if (error != 0) 60183b2aab18SMatthew Ahrens return (error); 60193b2aab18SMatthew Ahrens 60203b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, snapname, FTAG, &tosnap); 60213b2aab18SMatthew Ahrens if (error != 0) { 60223b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 60234445fffbSMatthew Ahrens return (error); 60243b2aab18SMatthew Ahrens } 60254445fffbSMatthew Ahrens 60265602294fSDan Kimmel compressok = nvlist_exists(innvl, "compressok"); 6027eb633035STom Caputi rawok = nvlist_exists(innvl, "rawok"); 60285602294fSDan Kimmel 6029643da460SMax Grossman error = nvlist_lookup_string(innvl, "from", &fromname); 60304445fffbSMatthew Ahrens if (error == 0) { 6031643da460SMax Grossman if (strchr(fromname, '@') != NULL) { 6032643da460SMax Grossman /* 6033643da460SMax Grossman * If from is a snapshot, hold it and use the more 6034643da460SMax Grossman * efficient dmu_send_estimate to estimate send space 6035643da460SMax Grossman * size using deadlists. 6036643da460SMax Grossman */ 6037643da460SMax Grossman dsl_dataset_t *fromsnap; 6038643da460SMax Grossman error = dsl_dataset_hold(dp, fromname, FTAG, &fromsnap); 6039643da460SMax Grossman if (error != 0) 6040643da460SMax Grossman goto out; 6041eb633035STom Caputi error = dmu_send_estimate(tosnap, fromsnap, 6042eb633035STom Caputi compressok || rawok, &space); 6043643da460SMax Grossman dsl_dataset_rele(fromsnap, FTAG); 6044643da460SMax Grossman } else if (strchr(fromname, '#') != NULL) { 6045643da460SMax Grossman /* 6046643da460SMax Grossman * If from is a bookmark, fetch the creation TXG of the 6047643da460SMax Grossman * snapshot it was created from and use that to find 6048643da460SMax Grossman * blocks that were born after it. 6049643da460SMax Grossman */ 6050643da460SMax Grossman zfs_bookmark_phys_t frombm; 6051643da460SMax Grossman 6052643da460SMax Grossman error = dsl_bookmark_lookup(dp, fromname, tosnap, 6053643da460SMax Grossman &frombm); 6054643da460SMax Grossman if (error != 0) 6055643da460SMax Grossman goto out; 6056643da460SMax Grossman error = dmu_send_estimate_from_txg(tosnap, 6057eb633035STom Caputi frombm.zbm_creation_txg, compressok || rawok, 6058eb633035STom Caputi &space); 6059643da460SMax Grossman } else { 6060643da460SMax Grossman /* 6061643da460SMax Grossman * from is not properly formatted as a snapshot or 6062643da460SMax Grossman * bookmark 6063643da460SMax Grossman */ 6064643da460SMax Grossman error = SET_ERROR(EINVAL); 6065643da460SMax Grossman goto out; 60664445fffbSMatthew Ahrens } 6067643da460SMax Grossman } else { 6068b852c2f5SToomas Soome /* 6069b852c2f5SToomas Soome * If estimating the size of a full send, use dmu_send_estimate. 6070b852c2f5SToomas Soome */ 6071eb633035STom Caputi error = dmu_send_estimate(tosnap, NULL, compressok || rawok, 6072eb633035STom Caputi &space); 60734445fffbSMatthew Ahrens } 60744445fffbSMatthew Ahrens 60754445fffbSMatthew Ahrens fnvlist_add_uint64(outnvl, "space", space); 60764445fffbSMatthew Ahrens 6077643da460SMax Grossman out: 60783b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 60793b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 60804445fffbSMatthew Ahrens return (error); 60814445fffbSMatthew Ahrens } 60824445fffbSMatthew Ahrens 60839c2acf00SAlek Pinchuk /* 60849c2acf00SAlek Pinchuk * Sync the currently open TXG to disk for the specified pool. 60859c2acf00SAlek Pinchuk * This is somewhat similar to 'zfs_sync()'. 60869c2acf00SAlek Pinchuk * For cases that do not result in error this ioctl will wait for 60879c2acf00SAlek Pinchuk * the currently open TXG to commit before returning back to the caller. 60889c2acf00SAlek Pinchuk * 60899c2acf00SAlek Pinchuk * innvl: { 60909c2acf00SAlek Pinchuk * "force" -> when true, force uberblock update even if there is no dirty data. 60919c2acf00SAlek Pinchuk * In addition this will cause the vdev configuration to be written 60929c2acf00SAlek Pinchuk * out including updating the zpool cache file. (boolean_t) 60939c2acf00SAlek Pinchuk * } 60949c2acf00SAlek Pinchuk * 60959c2acf00SAlek Pinchuk * onvl is unused 60969c2acf00SAlek Pinchuk */ 60979c2acf00SAlek Pinchuk /* ARGSUSED */ 60989c2acf00SAlek Pinchuk static int 60999c2acf00SAlek Pinchuk zfs_ioc_pool_sync(const char *pool, nvlist_t *innvl, nvlist_t *onvl) 61009c2acf00SAlek Pinchuk { 61019c2acf00SAlek Pinchuk int err; 61029c2acf00SAlek Pinchuk boolean_t force; 61039c2acf00SAlek Pinchuk spa_t *spa; 61049c2acf00SAlek Pinchuk 61059c2acf00SAlek Pinchuk if ((err = spa_open(pool, &spa, FTAG)) != 0) 61069c2acf00SAlek Pinchuk return (err); 61079c2acf00SAlek Pinchuk 61089c2acf00SAlek Pinchuk force = fnvlist_lookup_boolean_value(innvl, "force"); 61099c2acf00SAlek Pinchuk if (force) { 61109c2acf00SAlek Pinchuk spa_config_enter(spa, SCL_CONFIG, FTAG, RW_WRITER); 61119c2acf00SAlek Pinchuk vdev_config_dirty(spa->spa_root_vdev); 61129c2acf00SAlek Pinchuk spa_config_exit(spa, SCL_CONFIG, FTAG); 61139c2acf00SAlek Pinchuk } 61149c2acf00SAlek Pinchuk txg_wait_synced(spa_get_dsl(spa), 0); 61159c2acf00SAlek Pinchuk 61169c2acf00SAlek Pinchuk spa_close(spa, FTAG); 61179c2acf00SAlek Pinchuk 61189c2acf00SAlek Pinchuk return (err); 61199c2acf00SAlek Pinchuk } 61209c2acf00SAlek Pinchuk 6121eb633035STom Caputi /* 6122eb633035STom Caputi * Load a user's wrapping key into the kernel. 6123eb633035STom Caputi * innvl: { 6124eb633035STom Caputi * "hidden_args" -> { "wkeydata" -> value } 6125eb633035STom Caputi * raw uint8_t array of encryption wrapping key data (32 bytes) 6126eb633035STom Caputi * (optional) "noop" -> (value ignored) 6127eb633035STom Caputi * presence indicated key should only be verified, not loaded 6128eb633035STom Caputi * } 6129eb633035STom Caputi */ 6130eb633035STom Caputi /* ARGSUSED */ 6131eb633035STom Caputi static int 6132eb633035STom Caputi zfs_ioc_load_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) 6133eb633035STom Caputi { 6134eb633035STom Caputi int ret = 0; 6135eb633035STom Caputi dsl_crypto_params_t *dcp = NULL; 6136eb633035STom Caputi nvlist_t *hidden_args; 6137eb633035STom Caputi boolean_t noop = nvlist_exists(innvl, "noop"); 6138eb633035STom Caputi 6139eb633035STom Caputi if (strchr(dsname, '@') != NULL || strchr(dsname, '%') != NULL) { 6140eb633035STom Caputi ret = SET_ERROR(EINVAL); 6141eb633035STom Caputi goto error; 6142eb633035STom Caputi } 6143eb633035STom Caputi 6144eb633035STom Caputi ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); 6145eb633035STom Caputi if (ret != 0) { 6146eb633035STom Caputi ret = SET_ERROR(EINVAL); 6147eb633035STom Caputi goto error; 6148eb633035STom Caputi } 6149eb633035STom Caputi 6150eb633035STom Caputi ret = dsl_crypto_params_create_nvlist(DCP_CMD_NONE, NULL, 6151eb633035STom Caputi hidden_args, &dcp); 6152eb633035STom Caputi if (ret != 0) 6153eb633035STom Caputi goto error; 6154eb633035STom Caputi 6155eb633035STom Caputi ret = spa_keystore_load_wkey(dsname, dcp, noop); 6156eb633035STom Caputi if (ret != 0) 6157eb633035STom Caputi goto error; 6158eb633035STom Caputi 6159eb633035STom Caputi dsl_crypto_params_free(dcp, noop); 6160eb633035STom Caputi 6161eb633035STom Caputi return (0); 6162eb633035STom Caputi 6163eb633035STom Caputi error: 6164eb633035STom Caputi dsl_crypto_params_free(dcp, B_TRUE); 6165eb633035STom Caputi return (ret); 6166eb633035STom Caputi } 6167eb633035STom Caputi 6168eb633035STom Caputi /* 6169eb633035STom Caputi * Unload a user's wrapping key from the kernel. 6170eb633035STom Caputi * Both innvl and outnvl are unused. 6171eb633035STom Caputi */ 6172eb633035STom Caputi /* ARGSUSED */ 6173eb633035STom Caputi static int 6174eb633035STom Caputi zfs_ioc_unload_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) 6175eb633035STom Caputi { 6176eb633035STom Caputi int ret = 0; 6177eb633035STom Caputi 6178eb633035STom Caputi if (strchr(dsname, '@') != NULL || strchr(dsname, '%') != NULL) { 6179eb633035STom Caputi ret = (SET_ERROR(EINVAL)); 6180eb633035STom Caputi goto out; 6181eb633035STom Caputi } 6182eb633035STom Caputi 6183eb633035STom Caputi ret = spa_keystore_unload_wkey(dsname); 6184eb633035STom Caputi if (ret != 0) 6185eb633035STom Caputi goto out; 6186eb633035STom Caputi 6187eb633035STom Caputi out: 6188eb633035STom Caputi return (ret); 6189eb633035STom Caputi } 6190eb633035STom Caputi 6191eb633035STom Caputi /* 6192eb633035STom Caputi * Changes a user's wrapping key used to decrypt a dataset. The keyformat, 6193eb633035STom Caputi * keylocation, pbkdf2salt, and pbkdf2iters properties can also be specified 6194eb633035STom Caputi * here to change how the key is derived in userspace. 6195eb633035STom Caputi * 6196eb633035STom Caputi * innvl: { 6197eb633035STom Caputi * "hidden_args" (optional) -> { "wkeydata" -> value } 6198eb633035STom Caputi * raw uint8_t array of new encryption wrapping key data (32 bytes) 6199eb633035STom Caputi * "props" (optional) -> { prop -> value } 6200eb633035STom Caputi * } 6201eb633035STom Caputi * 6202eb633035STom Caputi * outnvl is unused 6203eb633035STom Caputi */ 6204eb633035STom Caputi /* ARGSUSED */ 6205eb633035STom Caputi static int 6206eb633035STom Caputi zfs_ioc_change_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) 6207eb633035STom Caputi { 6208eb633035STom Caputi int ret; 6209eb633035STom Caputi uint64_t cmd = DCP_CMD_NONE; 6210eb633035STom Caputi dsl_crypto_params_t *dcp = NULL; 6211eb633035STom Caputi nvlist_t *args = NULL, *hidden_args = NULL; 6212eb633035STom Caputi 6213eb633035STom Caputi if (strchr(dsname, '@') != NULL || strchr(dsname, '%') != NULL) { 6214eb633035STom Caputi ret = (SET_ERROR(EINVAL)); 6215eb633035STom Caputi goto error; 6216eb633035STom Caputi } 6217eb633035STom Caputi 6218eb633035STom Caputi (void) nvlist_lookup_uint64(innvl, "crypt_cmd", &cmd); 6219eb633035STom Caputi (void) nvlist_lookup_nvlist(innvl, "props", &args); 6220eb633035STom Caputi (void) nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); 6221eb633035STom Caputi 6222eb633035STom Caputi ret = dsl_crypto_params_create_nvlist(cmd, args, hidden_args, &dcp); 6223eb633035STom Caputi if (ret != 0) 6224eb633035STom Caputi goto error; 6225eb633035STom Caputi 6226eb633035STom Caputi ret = spa_keystore_change_key(dsname, dcp); 6227eb633035STom Caputi if (ret != 0) 6228eb633035STom Caputi goto error; 6229eb633035STom Caputi 6230eb633035STom Caputi dsl_crypto_params_free(dcp, B_FALSE); 6231eb633035STom Caputi 6232eb633035STom Caputi return (0); 6233eb633035STom Caputi 6234eb633035STom Caputi error: 6235eb633035STom Caputi dsl_crypto_params_free(dcp, B_TRUE); 6236eb633035STom Caputi return (ret); 6237eb633035STom Caputi } 6238eb633035STom Caputi 62394445fffbSMatthew Ahrens static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; 62404445fffbSMatthew Ahrens 62414445fffbSMatthew Ahrens static void 62424445fffbSMatthew Ahrens zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 62434445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 62444445fffbSMatthew Ahrens boolean_t log_history, zfs_ioc_poolcheck_t pool_check) 62454445fffbSMatthew Ahrens { 62464445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 62474445fffbSMatthew Ahrens 62484445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 62494445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 62504445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 62514445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 62524445fffbSMatthew Ahrens 62534445fffbSMatthew Ahrens vec->zvec_legacy_func = func; 62544445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 62554445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 62564445fffbSMatthew Ahrens vec->zvec_allow_log = log_history; 62574445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 62584445fffbSMatthew Ahrens } 62594445fffbSMatthew Ahrens 62604445fffbSMatthew Ahrens /* 62614445fffbSMatthew Ahrens * See the block comment at the beginning of this file for details on 62624445fffbSMatthew Ahrens * each argument to this function. 62634445fffbSMatthew Ahrens */ 62644445fffbSMatthew Ahrens static void 62654445fffbSMatthew Ahrens zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, 62664445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, 62674445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, 62684445fffbSMatthew Ahrens boolean_t allow_log) 62694445fffbSMatthew Ahrens { 62704445fffbSMatthew Ahrens zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; 62714445fffbSMatthew Ahrens 62724445fffbSMatthew Ahrens ASSERT3U(ioc, >=, ZFS_IOC_FIRST); 62734445fffbSMatthew Ahrens ASSERT3U(ioc, <, ZFS_IOC_LAST); 62744445fffbSMatthew Ahrens ASSERT3P(vec->zvec_legacy_func, ==, NULL); 62754445fffbSMatthew Ahrens ASSERT3P(vec->zvec_func, ==, NULL); 62764445fffbSMatthew Ahrens 62774445fffbSMatthew Ahrens /* if we are logging, the name must be valid */ 62784445fffbSMatthew Ahrens ASSERT(!allow_log || namecheck != NO_NAME); 62794445fffbSMatthew Ahrens 62804445fffbSMatthew Ahrens vec->zvec_name = name; 62814445fffbSMatthew Ahrens vec->zvec_func = func; 62824445fffbSMatthew Ahrens vec->zvec_secpolicy = secpolicy; 62834445fffbSMatthew Ahrens vec->zvec_namecheck = namecheck; 62844445fffbSMatthew Ahrens vec->zvec_pool_check = pool_check; 62854445fffbSMatthew Ahrens vec->zvec_smush_outnvlist = smush_outnvlist; 62864445fffbSMatthew Ahrens vec->zvec_allow_log = allow_log; 62874445fffbSMatthew Ahrens } 62884445fffbSMatthew Ahrens 62894445fffbSMatthew Ahrens static void 62904445fffbSMatthew Ahrens zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 62914445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, boolean_t log_history, 62924445fffbSMatthew Ahrens zfs_ioc_poolcheck_t pool_check) 62934445fffbSMatthew Ahrens { 62944445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 62954445fffbSMatthew Ahrens POOL_NAME, log_history, pool_check); 62964445fffbSMatthew Ahrens } 62974445fffbSMatthew Ahrens 62984445fffbSMatthew Ahrens static void 62994445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 63004445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check) 63014445fffbSMatthew Ahrens { 63024445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 63034445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, pool_check); 63044445fffbSMatthew Ahrens } 63054445fffbSMatthew Ahrens 63064445fffbSMatthew Ahrens static void 63074445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 63084445fffbSMatthew Ahrens { 63094445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config, 63104445fffbSMatthew Ahrens POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 63114445fffbSMatthew Ahrens } 63124445fffbSMatthew Ahrens 63134445fffbSMatthew Ahrens static void 63144445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 63154445fffbSMatthew Ahrens zfs_secpolicy_func_t *secpolicy) 63164445fffbSMatthew Ahrens { 63174445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 63184445fffbSMatthew Ahrens NO_NAME, B_FALSE, POOL_CHECK_NONE); 63194445fffbSMatthew Ahrens } 63204445fffbSMatthew Ahrens 63214445fffbSMatthew Ahrens static void 63224445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc, 63234445fffbSMatthew Ahrens zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) 63244445fffbSMatthew Ahrens { 63254445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 63264445fffbSMatthew Ahrens DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED); 63274445fffbSMatthew Ahrens } 63284445fffbSMatthew Ahrens 63294445fffbSMatthew Ahrens static void 63304445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) 63314445fffbSMatthew Ahrens { 63324445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ioc, func, 63334445fffbSMatthew Ahrens zfs_secpolicy_read); 63344445fffbSMatthew Ahrens } 63354445fffbSMatthew Ahrens 63364445fffbSMatthew Ahrens static void 63374445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, 63389a686fbcSPaul Dagnelie zfs_secpolicy_func_t *secpolicy) 63394445fffbSMatthew Ahrens { 63404445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ioc, func, secpolicy, 63414445fffbSMatthew Ahrens DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 63424445fffbSMatthew Ahrens } 63434445fffbSMatthew Ahrens 63444445fffbSMatthew Ahrens static void 63454445fffbSMatthew Ahrens zfs_ioctl_init(void) 63464445fffbSMatthew Ahrens { 63474445fffbSMatthew Ahrens zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, 63484445fffbSMatthew Ahrens zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, 63494445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 63504445fffbSMatthew Ahrens 63514445fffbSMatthew Ahrens zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, 63524445fffbSMatthew Ahrens zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, 63534445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); 63544445fffbSMatthew Ahrens 63554445fffbSMatthew Ahrens zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, 63564445fffbSMatthew Ahrens zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, 63574445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 63584445fffbSMatthew Ahrens 63594445fffbSMatthew Ahrens zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, 63604445fffbSMatthew Ahrens zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, 63614445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 63624445fffbSMatthew Ahrens 63634445fffbSMatthew Ahrens zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, 63644445fffbSMatthew Ahrens zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, 63654445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 63664445fffbSMatthew Ahrens 63674445fffbSMatthew Ahrens zfs_ioctl_register("create", ZFS_IOC_CREATE, 63684445fffbSMatthew Ahrens zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, 63694445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 63704445fffbSMatthew Ahrens 63714445fffbSMatthew Ahrens zfs_ioctl_register("clone", ZFS_IOC_CLONE, 63724445fffbSMatthew Ahrens zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, 63734445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 63744445fffbSMatthew Ahrens 63755cabbc6bSPrashanth Sreenivasa zfs_ioctl_register("remap", ZFS_IOC_REMAP, 63765cabbc6bSPrashanth Sreenivasa zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME, 63775cabbc6bSPrashanth Sreenivasa POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); 63785cabbc6bSPrashanth Sreenivasa 63794445fffbSMatthew Ahrens zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, 63804445fffbSMatthew Ahrens zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, 63814445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 63824445fffbSMatthew Ahrens 63833b2aab18SMatthew Ahrens zfs_ioctl_register("hold", ZFS_IOC_HOLD, 63843b2aab18SMatthew Ahrens zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, 63853b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 63863b2aab18SMatthew Ahrens zfs_ioctl_register("release", ZFS_IOC_RELEASE, 63873b2aab18SMatthew Ahrens zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, 63883b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 63893b2aab18SMatthew Ahrens 63903b2aab18SMatthew Ahrens zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, 63913b2aab18SMatthew Ahrens zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, 63923b2aab18SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 63933b2aab18SMatthew Ahrens 6394a7027df1SMatthew Ahrens zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, 6395a7027df1SMatthew Ahrens zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, 6396a7027df1SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); 6397a7027df1SMatthew Ahrens 639878f17100SMatthew Ahrens zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, 639978f17100SMatthew Ahrens zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, 640078f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 640178f17100SMatthew Ahrens 640278f17100SMatthew Ahrens zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, 640378f17100SMatthew Ahrens zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, 640478f17100SMatthew Ahrens POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); 640578f17100SMatthew Ahrens 640678f17100SMatthew Ahrens zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, 640778f17100SMatthew Ahrens zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, 640878f17100SMatthew Ahrens POOL_NAME, 640978f17100SMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 641078f17100SMatthew Ahrens 6411dfc11533SChris Williamson zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, 6412dfc11533SChris Williamson zfs_ioc_channel_program, zfs_secpolicy_config, 6413dfc11533SChris Williamson POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, 6414dfc11533SChris Williamson B_TRUE); 6415dfc11533SChris Williamson 641686714001SSerapheim Dimitropoulos zfs_ioctl_register("zpool_checkpoint", ZFS_IOC_POOL_CHECKPOINT, 641786714001SSerapheim Dimitropoulos zfs_ioc_pool_checkpoint, zfs_secpolicy_config, POOL_NAME, 641886714001SSerapheim Dimitropoulos POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 641986714001SSerapheim Dimitropoulos 642086714001SSerapheim Dimitropoulos zfs_ioctl_register("zpool_discard_checkpoint", 642186714001SSerapheim Dimitropoulos ZFS_IOC_POOL_DISCARD_CHECKPOINT, zfs_ioc_pool_discard_checkpoint, 642286714001SSerapheim Dimitropoulos zfs_secpolicy_config, POOL_NAME, 642386714001SSerapheim Dimitropoulos POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 642486714001SSerapheim Dimitropoulos 6425094e47e9SGeorge Wilson zfs_ioctl_register("initialize", ZFS_IOC_POOL_INITIALIZE, 6426094e47e9SGeorge Wilson zfs_ioc_pool_initialize, zfs_secpolicy_config, POOL_NAME, 6427094e47e9SGeorge Wilson POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); 6428094e47e9SGeorge Wilson 64299c2acf00SAlek Pinchuk zfs_ioctl_register("sync", ZFS_IOC_POOL_SYNC, 64309c2acf00SAlek Pinchuk zfs_ioc_pool_sync, zfs_secpolicy_none, POOL_NAME, 64319c2acf00SAlek Pinchuk POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); 64329c2acf00SAlek Pinchuk 6433eb633035STom Caputi zfs_ioctl_register("load-key", ZFS_IOC_LOAD_KEY, 6434eb633035STom Caputi zfs_ioc_load_key, zfs_secpolicy_load_key, 6435eb633035STom Caputi DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE); 6436eb633035STom Caputi zfs_ioctl_register("unload-key", ZFS_IOC_UNLOAD_KEY, 6437eb633035STom Caputi zfs_ioc_unload_key, zfs_secpolicy_load_key, 6438eb633035STom Caputi DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE); 6439eb633035STom Caputi zfs_ioctl_register("change-key", ZFS_IOC_CHANGE_KEY, 6440eb633035STom Caputi zfs_ioc_change_key, zfs_secpolicy_change_key, 6441eb633035STom Caputi DATASET_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, 6442eb633035STom Caputi B_TRUE, B_TRUE); 6443eb633035STom Caputi 64444445fffbSMatthew Ahrens /* IOCTLS that use the legacy function signature */ 64454445fffbSMatthew Ahrens 64464445fffbSMatthew Ahrens zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, 64474445fffbSMatthew Ahrens zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); 64484445fffbSMatthew Ahrens 64494445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, 64504445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 64514445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN, 64524445fffbSMatthew Ahrens zfs_ioc_pool_scan); 64534445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE, 64544445fffbSMatthew Ahrens zfs_ioc_pool_upgrade); 64554445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD, 64564445fffbSMatthew Ahrens zfs_ioc_vdev_add); 64574445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE, 64584445fffbSMatthew Ahrens zfs_ioc_vdev_remove); 64594445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SET_STATE, 64604445fffbSMatthew Ahrens zfs_ioc_vdev_set_state); 64614445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH, 64624445fffbSMatthew Ahrens zfs_ioc_vdev_attach); 64634445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH, 64644445fffbSMatthew Ahrens zfs_ioc_vdev_detach); 64654445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH, 64664445fffbSMatthew Ahrens zfs_ioc_vdev_setpath); 64674445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU, 64684445fffbSMatthew Ahrens zfs_ioc_vdev_setfru); 64694445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS, 64704445fffbSMatthew Ahrens zfs_ioc_pool_set_props); 64714445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SPLIT, 64724445fffbSMatthew Ahrens zfs_ioc_vdev_split); 64734445fffbSMatthew Ahrens zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_REGUID, 64744445fffbSMatthew Ahrens zfs_ioc_pool_reguid); 64754445fffbSMatthew Ahrens 64764445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_CONFIGS, 64774445fffbSMatthew Ahrens zfs_ioc_pool_configs, zfs_secpolicy_none); 64784445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_TRYIMPORT, 64794445fffbSMatthew Ahrens zfs_ioc_pool_tryimport, zfs_secpolicy_config); 64804445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_FAULT, 64814445fffbSMatthew Ahrens zfs_ioc_inject_fault, zfs_secpolicy_inject); 64824445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_CLEAR_FAULT, 64834445fffbSMatthew Ahrens zfs_ioc_clear_fault, zfs_secpolicy_inject); 64844445fffbSMatthew Ahrens zfs_ioctl_register_pool_meta(ZFS_IOC_INJECT_LIST_NEXT, 64854445fffbSMatthew Ahrens zfs_ioc_inject_list_next, zfs_secpolicy_inject); 64864445fffbSMatthew Ahrens 64874445fffbSMatthew Ahrens /* 64884445fffbSMatthew Ahrens * pool destroy, and export don't log the history as part of 64894445fffbSMatthew Ahrens * zfsdev_ioctl, but rather zfs_ioc_pool_export 64904445fffbSMatthew Ahrens * does the logging of those commands. 64914445fffbSMatthew Ahrens */ 64924445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_DESTROY, zfs_ioc_pool_destroy, 64934445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 64944445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_EXPORT, zfs_ioc_pool_export, 64954445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); 64964445fffbSMatthew Ahrens 64974445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_STATS, zfs_ioc_pool_stats, 64984445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 64994445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_PROPS, zfs_ioc_pool_get_props, 65004445fffbSMatthew Ahrens zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); 65014445fffbSMatthew Ahrens 65024445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_ERROR_LOG, zfs_ioc_error_log, 65034445fffbSMatthew Ahrens zfs_secpolicy_inject, B_FALSE, POOL_CHECK_SUSPENDED); 65044445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_DSOBJ_TO_DSNAME, 65054445fffbSMatthew Ahrens zfs_ioc_dsobj_to_dsname, 65064445fffbSMatthew Ahrens zfs_secpolicy_diff, B_FALSE, POOL_CHECK_SUSPENDED); 65074445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_HISTORY, 65084445fffbSMatthew Ahrens zfs_ioc_pool_get_history, 65094445fffbSMatthew Ahrens zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); 65104445fffbSMatthew Ahrens 65114445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, 65124445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); 65134445fffbSMatthew Ahrens 65144445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, 6515f4c1745bSloli zfs_secpolicy_config, B_TRUE, POOL_CHECK_READONLY); 65164445fffbSMatthew Ahrens zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, 65174445fffbSMatthew Ahrens zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); 65184445fffbSMatthew Ahrens 65194445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, 65204445fffbSMatthew Ahrens zfs_ioc_space_written); 65214445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, 65224445fffbSMatthew Ahrens zfs_ioc_objset_recvd_props); 65234445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, 65244445fffbSMatthew Ahrens zfs_ioc_next_obj); 65254445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, 65264445fffbSMatthew Ahrens zfs_ioc_get_fsacl); 65274445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_STATS, 65284445fffbSMatthew Ahrens zfs_ioc_objset_stats); 65294445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_ZPLPROPS, 65304445fffbSMatthew Ahrens zfs_ioc_objset_zplprops); 65314445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_DATASET_LIST_NEXT, 65324445fffbSMatthew Ahrens zfs_ioc_dataset_list_next); 65334445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SNAPSHOT_LIST_NEXT, 65344445fffbSMatthew Ahrens zfs_ioc_snapshot_list_next); 65354445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read(ZFS_IOC_SEND_PROGRESS, 65364445fffbSMatthew Ahrens zfs_ioc_send_progress); 65374445fffbSMatthew Ahrens 65384445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_DIFF, 65394445fffbSMatthew Ahrens zfs_ioc_diff, zfs_secpolicy_diff); 65404445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_STATS, 65414445fffbSMatthew Ahrens zfs_ioc_obj_to_stats, zfs_secpolicy_diff); 65424445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_PATH, 65434445fffbSMatthew Ahrens zfs_ioc_obj_to_path, zfs_secpolicy_diff); 65444445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_ONE, 65454445fffbSMatthew Ahrens zfs_ioc_userspace_one, zfs_secpolicy_userspace_one); 65464445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_USERSPACE_MANY, 65474445fffbSMatthew Ahrens zfs_ioc_userspace_many, zfs_secpolicy_userspace_many); 65484445fffbSMatthew Ahrens zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND, 65494445fffbSMatthew Ahrens zfs_ioc_send, zfs_secpolicy_send); 65504445fffbSMatthew Ahrens 65514445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, 65524445fffbSMatthew Ahrens zfs_secpolicy_none); 65534445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, 65544445fffbSMatthew Ahrens zfs_secpolicy_destroy); 65554445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename, 65564445fffbSMatthew Ahrens zfs_secpolicy_rename); 65574445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv, 65584445fffbSMatthew Ahrens zfs_secpolicy_recv); 65594445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote, 65604445fffbSMatthew Ahrens zfs_secpolicy_promote); 65614445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP, 65624445fffbSMatthew Ahrens zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop); 65634445fffbSMatthew Ahrens zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl, 65644445fffbSMatthew Ahrens zfs_secpolicy_set_fsacl); 65654445fffbSMatthew Ahrens 65664445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share, 65674445fffbSMatthew Ahrens zfs_secpolicy_share, POOL_CHECK_NONE); 65684445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl, 65694445fffbSMatthew Ahrens zfs_secpolicy_smb_acl, POOL_CHECK_NONE); 65704445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERSPACE_UPGRADE, 65714445fffbSMatthew Ahrens zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, 65724445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 65734445fffbSMatthew Ahrens zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT, 65744445fffbSMatthew Ahrens zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, 65754445fffbSMatthew Ahrens POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); 65764445fffbSMatthew Ahrens } 6577fa9e4066Sahrens 657854d692b7SGeorge Wilson int 6579f9af39baSGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type, 6580f9af39baSGeorge Wilson zfs_ioc_poolcheck_t check) 658154d692b7SGeorge Wilson { 658254d692b7SGeorge Wilson spa_t *spa; 658354d692b7SGeorge Wilson int error; 658454d692b7SGeorge Wilson 658554d692b7SGeorge Wilson ASSERT(type == POOL_NAME || type == DATASET_NAME); 658654d692b7SGeorge Wilson 6587f9af39baSGeorge Wilson if (check & POOL_CHECK_NONE) 6588f9af39baSGeorge Wilson return (0); 6589f9af39baSGeorge Wilson 659014843421SMatthew Ahrens error = spa_open(name, &spa, FTAG); 659154d692b7SGeorge Wilson if (error == 0) { 6592f9af39baSGeorge Wilson if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa)) 6593be6fd75aSMatthew Ahrens error = SET_ERROR(EAGAIN); 6594f9af39baSGeorge Wilson else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa)) 6595be6fd75aSMatthew Ahrens error = SET_ERROR(EROFS); 659654d692b7SGeorge Wilson spa_close(spa, FTAG); 659754d692b7SGeorge Wilson } 659854d692b7SGeorge Wilson return (error); 659954d692b7SGeorge Wilson } 660054d692b7SGeorge Wilson 6601c99e4bdcSChris Kirby /* 6602c99e4bdcSChris Kirby * Find a free minor number. 6603c99e4bdcSChris Kirby */ 6604c99e4bdcSChris Kirby minor_t 6605c99e4bdcSChris Kirby zfsdev_minor_alloc(void) 6606c99e4bdcSChris Kirby { 6607c99e4bdcSChris Kirby static minor_t last_minor; 6608c99e4bdcSChris Kirby minor_t m; 6609c99e4bdcSChris Kirby 6610c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 6611c99e4bdcSChris Kirby 6612c99e4bdcSChris Kirby for (m = last_minor + 1; m != last_minor; m++) { 6613c99e4bdcSChris Kirby if (m > ZFSDEV_MAX_MINOR) 6614c99e4bdcSChris Kirby m = 1; 6615c99e4bdcSChris Kirby if (ddi_get_soft_state(zfsdev_state, m) == NULL) { 6616c99e4bdcSChris Kirby last_minor = m; 6617c99e4bdcSChris Kirby return (m); 6618c99e4bdcSChris Kirby } 6619c99e4bdcSChris Kirby } 6620c99e4bdcSChris Kirby 6621c99e4bdcSChris Kirby return (0); 6622c99e4bdcSChris Kirby } 6623c99e4bdcSChris Kirby 6624c99e4bdcSChris Kirby static int 6625c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp) 6626c99e4bdcSChris Kirby { 6627c99e4bdcSChris Kirby minor_t minor; 6628c99e4bdcSChris Kirby zfs_soft_state_t *zs; 6629c99e4bdcSChris Kirby 6630c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 6631c99e4bdcSChris Kirby ASSERT(getminor(*devp) == 0); 6632c99e4bdcSChris Kirby 6633c99e4bdcSChris Kirby minor = zfsdev_minor_alloc(); 6634c99e4bdcSChris Kirby if (minor == 0) 6635be6fd75aSMatthew Ahrens return (SET_ERROR(ENXIO)); 6636c99e4bdcSChris Kirby 6637c99e4bdcSChris Kirby if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS) 6638be6fd75aSMatthew Ahrens return (SET_ERROR(EAGAIN)); 6639c99e4bdcSChris Kirby 6640c99e4bdcSChris Kirby *devp = makedevice(getemajor(*devp), minor); 6641c99e4bdcSChris Kirby 6642c99e4bdcSChris Kirby zs = ddi_get_soft_state(zfsdev_state, minor); 6643c99e4bdcSChris Kirby zs->zss_type = ZSST_CTLDEV; 6644c99e4bdcSChris Kirby zfs_onexit_init((zfs_onexit_t **)&zs->zss_data); 6645c99e4bdcSChris Kirby 6646c99e4bdcSChris Kirby return (0); 6647c99e4bdcSChris Kirby } 6648c99e4bdcSChris Kirby 6649c99e4bdcSChris Kirby static void 6650c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor) 6651c99e4bdcSChris Kirby { 6652c99e4bdcSChris Kirby ASSERT(MUTEX_HELD(&zfsdev_state_lock)); 6653c99e4bdcSChris Kirby 6654c99e4bdcSChris Kirby zfs_onexit_destroy(zo); 6655c99e4bdcSChris Kirby ddi_soft_state_free(zfsdev_state, minor); 6656c99e4bdcSChris Kirby } 6657c99e4bdcSChris Kirby 6658c99e4bdcSChris Kirby void * 6659c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which) 6660c99e4bdcSChris Kirby { 6661c99e4bdcSChris Kirby zfs_soft_state_t *zp; 6662c99e4bdcSChris Kirby 6663c99e4bdcSChris Kirby zp = ddi_get_soft_state(zfsdev_state, minor); 6664c99e4bdcSChris Kirby if (zp == NULL || zp->zss_type != which) 6665c99e4bdcSChris Kirby return (NULL); 6666c99e4bdcSChris Kirby 6667c99e4bdcSChris Kirby return (zp->zss_data); 6668c99e4bdcSChris Kirby } 6669c99e4bdcSChris Kirby 6670c99e4bdcSChris Kirby static int 6671c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr) 6672c99e4bdcSChris Kirby { 6673c99e4bdcSChris Kirby int error = 0; 6674c99e4bdcSChris Kirby 6675c99e4bdcSChris Kirby if (getminor(*devp) != 0) 6676c99e4bdcSChris Kirby return (zvol_open(devp, flag, otyp, cr)); 6677c99e4bdcSChris Kirby 6678c99e4bdcSChris Kirby /* This is the control device. Allocate a new minor if requested. */ 6679c99e4bdcSChris Kirby if (flag & FEXCL) { 6680c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 6681c99e4bdcSChris Kirby error = zfs_ctldev_init(devp); 6682c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 6683c99e4bdcSChris Kirby } 6684c99e4bdcSChris Kirby 6685c99e4bdcSChris Kirby return (error); 6686c99e4bdcSChris Kirby } 6687c99e4bdcSChris Kirby 6688c99e4bdcSChris Kirby static int 6689c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr) 6690c99e4bdcSChris Kirby { 6691c99e4bdcSChris Kirby zfs_onexit_t *zo; 6692c99e4bdcSChris Kirby minor_t minor = getminor(dev); 6693c99e4bdcSChris Kirby 6694c99e4bdcSChris Kirby if (minor == 0) 6695c99e4bdcSChris Kirby return (0); 6696c99e4bdcSChris Kirby 6697c99e4bdcSChris Kirby mutex_enter(&zfsdev_state_lock); 6698c99e4bdcSChris Kirby zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV); 6699c99e4bdcSChris Kirby if (zo == NULL) { 6700c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 6701c99e4bdcSChris Kirby return (zvol_close(dev, flag, otyp, cr)); 6702c99e4bdcSChris Kirby } 6703c99e4bdcSChris Kirby zfs_ctldev_destroy(zo, minor); 6704c99e4bdcSChris Kirby mutex_exit(&zfsdev_state_lock); 6705c99e4bdcSChris Kirby 6706c99e4bdcSChris Kirby return (0); 6707c99e4bdcSChris Kirby } 6708c99e4bdcSChris Kirby 6709fa9e4066Sahrens static int 6710fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 6711fa9e4066Sahrens { 6712fa9e4066Sahrens zfs_cmd_t *zc; 67134445fffbSMatthew Ahrens uint_t vecnum; 67144445fffbSMatthew Ahrens int error, rc, len; 6715c99e4bdcSChris Kirby minor_t minor = getminor(dev); 67164445fffbSMatthew Ahrens const zfs_ioc_vec_t *vec; 67174445fffbSMatthew Ahrens char *saved_poolname = NULL; 67184445fffbSMatthew Ahrens nvlist_t *innvl = NULL; 6719fa9e4066Sahrens 6720c99e4bdcSChris Kirby if (minor != 0 && 6721c99e4bdcSChris Kirby zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL) 6722fa9e4066Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp)); 6723fa9e4066Sahrens 67244445fffbSMatthew Ahrens vecnum = cmd - ZFS_IOC_FIRST; 672591ebeef5Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); 6726fa9e4066Sahrens 67274445fffbSMatthew Ahrens if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) 6728be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 67294445fffbSMatthew Ahrens vec = &zfs_ioc_vec[vecnum]; 6730fa9e4066Sahrens 6731fa9e4066Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP); 6732fa9e4066Sahrens 6733478ed9adSEric Taylor error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); 67344445fffbSMatthew Ahrens if (error != 0) { 6735be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 67364445fffbSMatthew Ahrens goto out; 67374445fffbSMatthew Ahrens } 6738fa9e4066Sahrens 67394445fffbSMatthew Ahrens zc->zc_iflags = flag & FKIOCTL; 67404445fffbSMatthew Ahrens if (zc->zc_nvlist_src_size != 0) { 67414445fffbSMatthew Ahrens error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size, 67424445fffbSMatthew Ahrens zc->zc_iflags, &innvl); 67434445fffbSMatthew Ahrens if (error != 0) 67444445fffbSMatthew Ahrens goto out; 67454445fffbSMatthew Ahrens } 6746fa9e4066Sahrens 6747fa9e4066Sahrens /* 6748fa9e4066Sahrens * Ensure that all pool/dataset names are valid before we pass down to 6749fa9e4066Sahrens * the lower layers. 6750fa9e4066Sahrens */ 67514445fffbSMatthew Ahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; 67524445fffbSMatthew Ahrens switch (vec->zvec_namecheck) { 67534445fffbSMatthew Ahrens case POOL_NAME: 67544445fffbSMatthew Ahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) 6755be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 67564445fffbSMatthew Ahrens else 6757f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 67584445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 67594445fffbSMatthew Ahrens break; 6760fa9e4066Sahrens 67614445fffbSMatthew Ahrens case DATASET_NAME: 67624445fffbSMatthew Ahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0) 6763be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL); 67644445fffbSMatthew Ahrens else 6765f9af39baSGeorge Wilson error = pool_status_check(zc->zc_name, 67664445fffbSMatthew Ahrens vec->zvec_namecheck, vec->zvec_pool_check); 67674445fffbSMatthew Ahrens break; 67685ad82045Snd 67694445fffbSMatthew Ahrens case NO_NAME: 67704445fffbSMatthew Ahrens break; 6771fa9e4066Sahrens } 6772fa9e4066Sahrens 6773fa9e4066Sahrens 677445b17475SAlex Wilson if (error == 0) 67754445fffbSMatthew Ahrens error = vec->zvec_secpolicy(zc, innvl, cr); 67764445fffbSMatthew Ahrens 67774445fffbSMatthew Ahrens if (error != 0) 67784445fffbSMatthew Ahrens goto out; 67794445fffbSMatthew Ahrens 67804445fffbSMatthew Ahrens /* legacy ioctls can modify zc_name */ 678178f17100SMatthew Ahrens len = strcspn(zc->zc_name, "/@#") + 1; 67824445fffbSMatthew Ahrens saved_poolname = kmem_alloc(len, KM_SLEEP); 67834445fffbSMatthew Ahrens (void) strlcpy(saved_poolname, zc->zc_name, len); 67844445fffbSMatthew Ahrens 67854445fffbSMatthew Ahrens if (vec->zvec_func != NULL) { 67864445fffbSMatthew Ahrens nvlist_t *outnvl; 67874445fffbSMatthew Ahrens int puterror = 0; 67884445fffbSMatthew Ahrens spa_t *spa; 67894445fffbSMatthew Ahrens nvlist_t *lognv = NULL; 67904445fffbSMatthew Ahrens 67914445fffbSMatthew Ahrens ASSERT(vec->zvec_legacy_func == NULL); 67924445fffbSMatthew Ahrens 67934445fffbSMatthew Ahrens /* 67944445fffbSMatthew Ahrens * Add the innvl to the lognv before calling the func, 67954445fffbSMatthew Ahrens * in case the func changes the innvl. 67964445fffbSMatthew Ahrens */ 67974445fffbSMatthew Ahrens if (vec->zvec_allow_log) { 67984445fffbSMatthew Ahrens lognv = fnvlist_alloc(); 67994445fffbSMatthew Ahrens fnvlist_add_string(lognv, ZPOOL_HIST_IOCTL, 68004445fffbSMatthew Ahrens vec->zvec_name); 68014445fffbSMatthew Ahrens if (!nvlist_empty(innvl)) { 68024445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_INPUT_NVL, 68034445fffbSMatthew Ahrens innvl); 68044445fffbSMatthew Ahrens } 68054445fffbSMatthew Ahrens } 68064445fffbSMatthew Ahrens 68074445fffbSMatthew Ahrens outnvl = fnvlist_alloc(); 68084445fffbSMatthew Ahrens error = vec->zvec_func(zc->zc_name, innvl, outnvl); 68094445fffbSMatthew Ahrens 6810dfc11533SChris Williamson /* 6811d0cb1fb9SDon Brady * Some commands can partially execute, modify state, and still 6812dfc11533SChris Williamson * return an error. In these cases, attempt to record what 6813dfc11533SChris Williamson * was modified. 6814dfc11533SChris Williamson */ 6815dfc11533SChris Williamson if ((error == 0 || 6816dfc11533SChris Williamson (cmd == ZFS_IOC_CHANNEL_PROGRAM && error != EINVAL)) && 6817dfc11533SChris Williamson vec->zvec_allow_log && 68184445fffbSMatthew Ahrens spa_open(zc->zc_name, &spa, FTAG) == 0) { 68194445fffbSMatthew Ahrens if (!nvlist_empty(outnvl)) { 68204445fffbSMatthew Ahrens fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, 68214445fffbSMatthew Ahrens outnvl); 68224445fffbSMatthew Ahrens } 6823dfc11533SChris Williamson if (error != 0) { 6824dfc11533SChris Williamson fnvlist_add_int64(lognv, ZPOOL_HIST_ERRNO, 6825dfc11533SChris Williamson error); 6826dfc11533SChris Williamson } 68274445fffbSMatthew Ahrens (void) spa_history_log_nvl(spa, lognv); 68284445fffbSMatthew Ahrens spa_close(spa, FTAG); 68294445fffbSMatthew Ahrens } 68304445fffbSMatthew Ahrens fnvlist_free(lognv); 68314445fffbSMatthew Ahrens 68324445fffbSMatthew Ahrens if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) { 68334445fffbSMatthew Ahrens int smusherror = 0; 68344445fffbSMatthew Ahrens if (vec->zvec_smush_outnvlist) { 68354445fffbSMatthew Ahrens smusherror = nvlist_smush(outnvl, 68364445fffbSMatthew Ahrens zc->zc_nvlist_dst_size); 68374445fffbSMatthew Ahrens } 68384445fffbSMatthew Ahrens if (smusherror == 0) 68394445fffbSMatthew Ahrens puterror = put_nvlist(zc, outnvl); 68404445fffbSMatthew Ahrens } 68414445fffbSMatthew Ahrens 68424445fffbSMatthew Ahrens if (puterror != 0) 68434445fffbSMatthew Ahrens error = puterror; 68444445fffbSMatthew Ahrens 68454445fffbSMatthew Ahrens nvlist_free(outnvl); 68464445fffbSMatthew Ahrens } else { 68474445fffbSMatthew Ahrens error = vec->zvec_legacy_func(zc); 68484445fffbSMatthew Ahrens } 68494445fffbSMatthew Ahrens 68504445fffbSMatthew Ahrens out: 68514445fffbSMatthew Ahrens nvlist_free(innvl); 6852478ed9adSEric Taylor rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag); 68534445fffbSMatthew Ahrens if (error == 0 && rc != 0) 6854be6fd75aSMatthew Ahrens error = SET_ERROR(EFAULT); 68554445fffbSMatthew Ahrens if (error == 0 && vec->zvec_allow_log) { 68564445fffbSMatthew Ahrens char *s = tsd_get(zfs_allow_log_key); 68574445fffbSMatthew Ahrens if (s != NULL) 68584445fffbSMatthew Ahrens strfree(s); 68594445fffbSMatthew Ahrens (void) tsd_set(zfs_allow_log_key, saved_poolname); 68604445fffbSMatthew Ahrens } else { 68614445fffbSMatthew Ahrens if (saved_poolname != NULL) 68624445fffbSMatthew Ahrens strfree(saved_poolname); 6863ecd6cf80Smarks } 6864fa9e4066Sahrens 6865fa9e4066Sahrens kmem_free(zc, sizeof (zfs_cmd_t)); 6866fa9e4066Sahrens return (error); 6867fa9e4066Sahrens } 6868fa9e4066Sahrens 6869fa9e4066Sahrens static int 6870fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 6871fa9e4066Sahrens { 6872fa9e4066Sahrens if (cmd != DDI_ATTACH) 6873fa9e4066Sahrens return (DDI_FAILURE); 6874fa9e4066Sahrens 6875fa9e4066Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0, 6876fa9e4066Sahrens DDI_PSEUDO, 0) == DDI_FAILURE) 6877fa9e4066Sahrens return (DDI_FAILURE); 6878fa9e4066Sahrens 6879fa9e4066Sahrens zfs_dip = dip; 6880fa9e4066Sahrens 6881fa9e4066Sahrens ddi_report_dev(dip); 6882fa9e4066Sahrens 6883fa9e4066Sahrens return (DDI_SUCCESS); 6884fa9e4066Sahrens } 6885fa9e4066Sahrens 6886fa9e4066Sahrens static int 6887fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 6888fa9e4066Sahrens { 6889fa9e4066Sahrens if (spa_busy() || zfs_busy() || zvol_busy()) 6890fa9e4066Sahrens return (DDI_FAILURE); 6891fa9e4066Sahrens 6892fa9e4066Sahrens if (cmd != DDI_DETACH) 6893fa9e4066Sahrens return (DDI_FAILURE); 6894fa9e4066Sahrens 6895fa9e4066Sahrens zfs_dip = NULL; 6896fa9e4066Sahrens 6897fa9e4066Sahrens ddi_prop_remove_all(dip); 6898fa9e4066Sahrens ddi_remove_minor_node(dip, NULL); 6899fa9e4066Sahrens 6900fa9e4066Sahrens return (DDI_SUCCESS); 6901fa9e4066Sahrens } 6902fa9e4066Sahrens 6903fa9e4066Sahrens /*ARGSUSED*/ 6904fa9e4066Sahrens static int 6905fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 6906fa9e4066Sahrens { 6907fa9e4066Sahrens switch (infocmd) { 6908fa9e4066Sahrens case DDI_INFO_DEVT2DEVINFO: 6909fa9e4066Sahrens *result = zfs_dip; 6910fa9e4066Sahrens return (DDI_SUCCESS); 6911fa9e4066Sahrens 6912fa9e4066Sahrens case DDI_INFO_DEVT2INSTANCE: 6913a0965f35Sbonwick *result = (void *)0; 6914fa9e4066Sahrens return (DDI_SUCCESS); 6915fa9e4066Sahrens } 6916fa9e4066Sahrens 6917fa9e4066Sahrens return (DDI_FAILURE); 6918fa9e4066Sahrens } 6919fa9e4066Sahrens 6920fa9e4066Sahrens /* 6921fa9e4066Sahrens * OK, so this is a little weird. 6922fa9e4066Sahrens * 6923fa9e4066Sahrens * /dev/zfs is the control node, i.e. minor 0. 6924fa9e4066Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0. 6925fa9e4066Sahrens * 6926fa9e4066Sahrens * /dev/zfs has basically nothing to do except serve up ioctls, 6927fa9e4066Sahrens * so most of the standard driver entry points are in zvol.c. 6928fa9e4066Sahrens */ 6929fa9e4066Sahrens static struct cb_ops zfs_cb_ops = { 6930c99e4bdcSChris Kirby zfsdev_open, /* open */ 6931c99e4bdcSChris Kirby zfsdev_close, /* close */ 6932fa9e4066Sahrens zvol_strategy, /* strategy */ 6933fa9e4066Sahrens nodev, /* print */ 6934e7cbe64fSgw zvol_dump, /* dump */ 6935fa9e4066Sahrens zvol_read, /* read */ 6936fa9e4066Sahrens zvol_write, /* write */ 6937fa9e4066Sahrens zfsdev_ioctl, /* ioctl */ 6938fa9e4066Sahrens nodev, /* devmap */ 6939fa9e4066Sahrens nodev, /* mmap */ 6940fa9e4066Sahrens nodev, /* segmap */ 6941fa9e4066Sahrens nochpoll, /* poll */ 6942fa9e4066Sahrens ddi_prop_op, /* prop_op */ 6943fa9e4066Sahrens NULL, /* streamtab */ 6944fa9e4066Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 6945fa9e4066Sahrens CB_REV, /* version */ 6946feb08c6bSbillm nodev, /* async read */ 6947feb08c6bSbillm nodev, /* async write */ 6948fa9e4066Sahrens }; 6949fa9e4066Sahrens 6950fa9e4066Sahrens static struct dev_ops zfs_dev_ops = { 6951fa9e4066Sahrens DEVO_REV, /* version */ 6952fa9e4066Sahrens 0, /* refcnt */ 6953fa9e4066Sahrens zfs_info, /* info */ 6954fa9e4066Sahrens nulldev, /* identify */ 6955fa9e4066Sahrens nulldev, /* probe */ 6956fa9e4066Sahrens zfs_attach, /* attach */ 6957fa9e4066Sahrens zfs_detach, /* detach */ 6958fa9e4066Sahrens nodev, /* reset */ 6959fa9e4066Sahrens &zfs_cb_ops, /* driver operations */ 696019397407SSherry Moore NULL, /* no bus operations */ 696119397407SSherry Moore NULL, /* power */ 696219397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 6963fa9e4066Sahrens }; 6964fa9e4066Sahrens 6965fa9e4066Sahrens static struct modldrv zfs_modldrv = { 696619397407SSherry Moore &mod_driverops, 696719397407SSherry Moore "ZFS storage pool", 696819397407SSherry Moore &zfs_dev_ops 6969fa9e4066Sahrens }; 6970fa9e4066Sahrens 6971fa9e4066Sahrens static struct modlinkage modlinkage = { 6972fa9e4066Sahrens MODREV_1, 6973fa9e4066Sahrens (void *)&zfs_modlfs, 6974fa9e4066Sahrens (void *)&zfs_modldrv, 6975fa9e4066Sahrens NULL 6976fa9e4066Sahrens }; 6977fa9e4066Sahrens 69784445fffbSMatthew Ahrens static void 69794445fffbSMatthew Ahrens zfs_allow_log_destroy(void *arg) 69804445fffbSMatthew Ahrens { 69814445fffbSMatthew Ahrens char *poolname = arg; 69824445fffbSMatthew Ahrens strfree(poolname); 69834445fffbSMatthew Ahrens } 6984ec533521Sfr 6985fa9e4066Sahrens int 6986fa9e4066Sahrens _init(void) 6987fa9e4066Sahrens { 6988fa9e4066Sahrens int error; 6989fa9e4066Sahrens 6990a0965f35Sbonwick spa_init(FREAD | FWRITE); 6991a0965f35Sbonwick zfs_init(); 6992a0965f35Sbonwick zvol_init(); 69934445fffbSMatthew Ahrens zfs_ioctl_init(); 6994a0965f35Sbonwick 6995a0965f35Sbonwick if ((error = mod_install(&modlinkage)) != 0) { 6996a0965f35Sbonwick zvol_fini(); 6997a0965f35Sbonwick zfs_fini(); 6998a0965f35Sbonwick spa_fini(); 6999fa9e4066Sahrens return (error); 7000a0965f35Sbonwick } 7001fa9e4066Sahrens 7002ec533521Sfr tsd_create(&zfs_fsyncer_key, NULL); 70034445fffbSMatthew Ahrens tsd_create(&rrw_tsd_key, rrw_tsd_destroy); 70044445fffbSMatthew Ahrens tsd_create(&zfs_allow_log_key, zfs_allow_log_destroy); 7005ec533521Sfr 7006fa9e4066Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li); 7007fa9e4066Sahrens ASSERT(error == 0); 7008ecd6cf80Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL); 7009fa9e4066Sahrens 7010fa9e4066Sahrens return (0); 7011fa9e4066Sahrens } 7012fa9e4066Sahrens 7013fa9e4066Sahrens int 7014fa9e4066Sahrens _fini(void) 7015fa9e4066Sahrens { 7016fa9e4066Sahrens int error; 7017fa9e4066Sahrens 7018ea8dc4b6Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled) 7019be6fd75aSMatthew Ahrens return (SET_ERROR(EBUSY)); 7020fa9e4066Sahrens 7021fa9e4066Sahrens if ((error = mod_remove(&modlinkage)) != 0) 7022fa9e4066Sahrens return (error); 7023fa9e4066Sahrens 7024fa9e4066Sahrens zvol_fini(); 7025fa9e4066Sahrens zfs_fini(); 7026fa9e4066Sahrens spa_fini(); 7027da6c28aaSamw if (zfs_nfsshare_inited) 7028ecd6cf80Smarks (void) ddi_modclose(nfs_mod); 7029da6c28aaSamw if (zfs_smbshare_inited) 7030da6c28aaSamw (void) ddi_modclose(smbsrv_mod); 7031da6c28aaSamw if (zfs_nfsshare_inited || zfs_smbshare_inited) 7032ecd6cf80Smarks (void) ddi_modclose(sharefs_mod); 7033fa9e4066Sahrens 7034ec533521Sfr tsd_destroy(&zfs_fsyncer_key); 7035fa9e4066Sahrens ldi_ident_release(zfs_li); 7036fa9e4066Sahrens zfs_li = NULL; 7037ecd6cf80Smarks mutex_destroy(&zfs_share_lock); 7038fa9e4066Sahrens 7039fa9e4066Sahrens return (error); 7040fa9e4066Sahrens } 7041fa9e4066Sahrens 7042fa9e4066Sahrens int 7043fa9e4066Sahrens _info(struct modinfo *modinfop) 7044fa9e4066Sahrens { 7045fa9e4066Sahrens return (mod_info(&modlinkage, modinfop)); 7046fa9e4066Sahrens } 7047