1789Sahrens /*
2789Sahrens * CDDL HEADER START
3789Sahrens *
4789Sahrens * The contents of this file are subject to the terms of the
51485Slling * Common Development and Distribution License (the "License").
61485Slling * You may not use this file except in compliance with the License.
7789Sahrens *
8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens * or http://www.opensolaris.org/os/licensing.
10789Sahrens * See the License for the specific language governing permissions
11789Sahrens * and limitations under the License.
12789Sahrens *
13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens *
19789Sahrens * CDDL HEADER END
20789Sahrens */
21789Sahrens /*
2212296SLin.Ling@Sun.COM * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens */
24789Sahrens
25789Sahrens #include <sys/types.h>
26789Sahrens #include <sys/param.h>
27789Sahrens #include <sys/errno.h>
28789Sahrens #include <sys/uio.h>
29789Sahrens #include <sys/buf.h>
30789Sahrens #include <sys/modctl.h>
31789Sahrens #include <sys/open.h>
32789Sahrens #include <sys/file.h>
33789Sahrens #include <sys/kmem.h>
34789Sahrens #include <sys/conf.h>
35789Sahrens #include <sys/cmn_err.h>
36789Sahrens #include <sys/stat.h>
37789Sahrens #include <sys/zfs_ioctl.h>
3810972SRic.Aleshire@Sun.COM #include <sys/zfs_vfsops.h>
395331Samw #include <sys/zfs_znode.h>
40789Sahrens #include <sys/zap.h>
41789Sahrens #include <sys/spa.h>
423912Slling #include <sys/spa_impl.h>
43789Sahrens #include <sys/vdev.h>
4410972SRic.Aleshire@Sun.COM #include <sys/priv_impl.h>
45789Sahrens #include <sys/dmu.h>
46789Sahrens #include <sys/dsl_dir.h>
47789Sahrens #include <sys/dsl_dataset.h>
48789Sahrens #include <sys/dsl_prop.h>
494543Smarks #include <sys/dsl_deleg.h>
504543Smarks #include <sys/dmu_objset.h>
51789Sahrens #include <sys/ddi.h>
52789Sahrens #include <sys/sunddi.h>
53789Sahrens #include <sys/sunldi.h>
54789Sahrens #include <sys/policy.h>
55789Sahrens #include <sys/zone.h>
56789Sahrens #include <sys/nvpair.h>
57789Sahrens #include <sys/pathname.h>
58789Sahrens #include <sys/mount.h>
59789Sahrens #include <sys/sdt.h>
60789Sahrens #include <sys/fs/zfs.h>
61789Sahrens #include <sys/zfs_ctldir.h>
625331Samw #include <sys/zfs_dir.h>
6312527SChris.Kirby@oracle.com #include <sys/zfs_onexit.h>
642885Sahrens #include <sys/zvol.h>
6512296SLin.Ling@Sun.COM #include <sys/dsl_scan.h>
664543Smarks #include <sharefs/share.h>
675326Sek110237 #include <sys/dmu_objset.h>
68789Sahrens
69789Sahrens #include "zfs_namecheck.h"
702676Seschrock #include "zfs_prop.h"
714543Smarks #include "zfs_deleg.h"
7211935SMark.Shellenbaum@Sun.COM #include "zfs_comutil.h"
73789Sahrens
74789Sahrens extern struct modlfs zfs_modlfs;
75789Sahrens
76789Sahrens extern void zfs_init(void);
77789Sahrens extern void zfs_fini(void);
78789Sahrens
79789Sahrens ldi_ident_t zfs_li = NULL;
80789Sahrens dev_info_t *zfs_dip;
81789Sahrens
82789Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *);
834543Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
84789Sahrens
859234SGeorge.Wilson@Sun.COM typedef enum {
869234SGeorge.Wilson@Sun.COM NO_NAME,
879234SGeorge.Wilson@Sun.COM POOL_NAME,
889234SGeorge.Wilson@Sun.COM DATASET_NAME
899234SGeorge.Wilson@Sun.COM } zfs_ioc_namecheck_t;
909234SGeorge.Wilson@Sun.COM
91*13049SGeorge.Wilson@Sun.COM typedef enum {
92*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE = 1 << 0,
93*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED = 1 << 1,
94*13049SGeorge.Wilson@Sun.COM POOL_CHECK_READONLY = 1 << 2
95*13049SGeorge.Wilson@Sun.COM } zfs_ioc_poolcheck_t;
96*13049SGeorge.Wilson@Sun.COM
97789Sahrens typedef struct zfs_ioc_vec {
98789Sahrens zfs_ioc_func_t *zvec_func;
99789Sahrens zfs_secpolicy_func_t *zvec_secpolicy;
1009234SGeorge.Wilson@Sun.COM zfs_ioc_namecheck_t zvec_namecheck;
1014543Smarks boolean_t zvec_his_log;
102*13049SGeorge.Wilson@Sun.COM zfs_ioc_poolcheck_t zvec_pool_check;
103789Sahrens } zfs_ioc_vec_t;
104789Sahrens
1059396SMatthew.Ahrens@Sun.COM /* This array is indexed by zfs_userquota_prop_t */
1069396SMatthew.Ahrens@Sun.COM static const char *userquota_perms[] = {
1079396SMatthew.Ahrens@Sun.COM ZFS_DELEG_PERM_USERUSED,
1089396SMatthew.Ahrens@Sun.COM ZFS_DELEG_PERM_USERQUOTA,
1099396SMatthew.Ahrens@Sun.COM ZFS_DELEG_PERM_GROUPUSED,
1109396SMatthew.Ahrens@Sun.COM ZFS_DELEG_PERM_GROUPQUOTA,
1119396SMatthew.Ahrens@Sun.COM };
1129396SMatthew.Ahrens@Sun.COM
1139396SMatthew.Ahrens@Sun.COM static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
11411022STom.Erickson@Sun.COM static int zfs_check_settable(const char *name, nvpair_t *property,
11511022STom.Erickson@Sun.COM cred_t *cr);
11611022STom.Erickson@Sun.COM static int zfs_check_clearable(char *dataset, nvlist_t *props,
11711022STom.Erickson@Sun.COM nvlist_t **errors);
1187184Stimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1197184Stimh boolean_t *);
12011022STom.Erickson@Sun.COM int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **);
1217184Stimh
122789Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
123789Sahrens void
__dprintf(const char * file,const char * func,int line,const char * fmt,...)124789Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
125789Sahrens {
126789Sahrens const char *newfile;
12712296SLin.Ling@Sun.COM char buf[512];
128789Sahrens va_list adx;
129789Sahrens
130789Sahrens /*
131789Sahrens * Get rid of annoying "../common/" prefix to filename.
132789Sahrens */
133789Sahrens newfile = strrchr(file, '/');
134789Sahrens if (newfile != NULL) {
135789Sahrens newfile = newfile + 1; /* Get rid of leading / */
136789Sahrens } else {
137789Sahrens newfile = file;
138789Sahrens }
139789Sahrens
140789Sahrens va_start(adx, fmt);
141789Sahrens (void) vsnprintf(buf, sizeof (buf), fmt, adx);
142789Sahrens va_end(adx);
143789Sahrens
144789Sahrens /*
145789Sahrens * To get this data, use the zfs-dprintf probe as so:
146789Sahrens * dtrace -q -n 'zfs-dprintf \
147789Sahrens * /stringof(arg0) == "dbuf.c"/ \
148789Sahrens * {printf("%s: %s", stringof(arg1), stringof(arg3))}'
149789Sahrens * arg0 = file name
150789Sahrens * arg1 = function name
151789Sahrens * arg2 = line number
152789Sahrens * arg3 = message
153789Sahrens */
154789Sahrens DTRACE_PROBE4(zfs__dprintf,
155789Sahrens char *, newfile, char *, func, int, line, char *, buf);
156789Sahrens }
157789Sahrens
1584543Smarks static void
history_str_free(char * buf)1594715Sek110237 history_str_free(char *buf)
1604715Sek110237 {
1614715Sek110237 kmem_free(buf, HIS_MAX_RECORD_LEN);
1624715Sek110237 }
1634715Sek110237
1644715Sek110237 static char *
history_str_get(zfs_cmd_t * zc)1654715Sek110237 history_str_get(zfs_cmd_t *zc)
1664715Sek110237 {
1674715Sek110237 char *buf;
1684715Sek110237
1694715Sek110237 if (zc->zc_history == NULL)
1704715Sek110237 return (NULL);
1714715Sek110237
1724715Sek110237 buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
1734715Sek110237 if (copyinstr((void *)(uintptr_t)zc->zc_history,
1744715Sek110237 buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
1754715Sek110237 history_str_free(buf);
1764715Sek110237 return (NULL);
1774715Sek110237 }
1784715Sek110237
1794715Sek110237 buf[HIS_MAX_RECORD_LEN -1] = '\0';
1804715Sek110237
1814715Sek110237 return (buf);
1824715Sek110237 }
1834715Sek110237
1845375Stimh /*
1857042Sgw25295 * Check to see if the named dataset is currently defined as bootable
1867042Sgw25295 */
1877042Sgw25295 static boolean_t
zfs_is_bootfs(const char * name)1887042Sgw25295 zfs_is_bootfs(const char *name)
1897042Sgw25295 {
19010298SMatthew.Ahrens@Sun.COM objset_t *os;
19110298SMatthew.Ahrens@Sun.COM
19210298SMatthew.Ahrens@Sun.COM if (dmu_objset_hold(name, FTAG, &os) == 0) {
19310298SMatthew.Ahrens@Sun.COM boolean_t ret;
19410922SJeff.Bonwick@Sun.COM ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
19510298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
19610298SMatthew.Ahrens@Sun.COM return (ret);
1977042Sgw25295 }
19810298SMatthew.Ahrens@Sun.COM return (B_FALSE);
1997042Sgw25295 }
2007042Sgw25295
2017042Sgw25295 /*
2027184Stimh * zfs_earlier_version
2035375Stimh *
2045375Stimh * Return non-zero if the spa version is less than requested version.
2055375Stimh */
2065331Samw static int
zfs_earlier_version(const char * name,int version)2077184Stimh zfs_earlier_version(const char *name, int version)
2085331Samw {
2095331Samw spa_t *spa;
2105331Samw
2115331Samw if (spa_open(name, &spa, FTAG) == 0) {
2125331Samw if (spa_version(spa) < version) {
2135331Samw spa_close(spa, FTAG);
2145331Samw return (1);
2155331Samw }
2165331Samw spa_close(spa, FTAG);
2175331Samw }
2185331Samw return (0);
2195331Samw }
2205331Samw
2215977Smarks /*
2226689Smaybee * zpl_earlier_version
2235977Smarks *
2246689Smaybee * Return TRUE if the ZPL version is less than requested version.
2255977Smarks */
2266689Smaybee static boolean_t
zpl_earlier_version(const char * name,int version)2276689Smaybee zpl_earlier_version(const char *name, int version)
2285977Smarks {
2295977Smarks objset_t *os;
2306689Smaybee boolean_t rc = B_TRUE;
2315977Smarks
23210298SMatthew.Ahrens@Sun.COM if (dmu_objset_hold(name, FTAG, &os) == 0) {
2336689Smaybee uint64_t zplversion;
2346689Smaybee
23510298SMatthew.Ahrens@Sun.COM if (dmu_objset_type(os) != DMU_OST_ZFS) {
23610298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
23710298SMatthew.Ahrens@Sun.COM return (B_TRUE);
23810298SMatthew.Ahrens@Sun.COM }
23910298SMatthew.Ahrens@Sun.COM /* XXX reading from non-owned objset */
2406689Smaybee if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
2416689Smaybee rc = zplversion < version;
24210298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
2435977Smarks }
2445977Smarks return (rc);
2455977Smarks }
2465977Smarks
2474715Sek110237 static void
zfs_log_history(zfs_cmd_t * zc)2484543Smarks zfs_log_history(zfs_cmd_t *zc)
2494543Smarks {
2504543Smarks spa_t *spa;
2514603Sahrens char *buf;
2524543Smarks
2534715Sek110237 if ((buf = history_str_get(zc)) == NULL)
2544577Sahrens return;
2554577Sahrens
2564715Sek110237 if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
2574715Sek110237 if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
2584715Sek110237 (void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
2594715Sek110237 spa_close(spa, FTAG);
2604543Smarks }
2614715Sek110237 history_str_free(buf);
2624543Smarks }
2634543Smarks
264789Sahrens /*
265789Sahrens * Policy for top-level read operations (list pools). Requires no privileges,
266789Sahrens * and can be used in the local zone, as there is no associated dataset.
267789Sahrens */
268789Sahrens /* ARGSUSED */
269789Sahrens static int
zfs_secpolicy_none(zfs_cmd_t * zc,cred_t * cr)2704543Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
271789Sahrens {
272789Sahrens return (0);
273789Sahrens }
274789Sahrens
275789Sahrens /*
276789Sahrens * Policy for dataset read operations (list children, get statistics). Requires
277789Sahrens * no privileges, but must be visible in the local zone.
278789Sahrens */
279789Sahrens /* ARGSUSED */
280789Sahrens static int
zfs_secpolicy_read(zfs_cmd_t * zc,cred_t * cr)2814543Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
282789Sahrens {
283789Sahrens if (INGLOBALZONE(curproc) ||
2844543Smarks zone_dataset_visible(zc->zc_name, NULL))
285789Sahrens return (0);
286789Sahrens
287789Sahrens return (ENOENT);
288789Sahrens }
289789Sahrens
290789Sahrens static int
zfs_dozonecheck_impl(const char * dataset,uint64_t zoned,cred_t * cr)29112786SChris.Kirby@oracle.com zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr)
292789Sahrens {
293789Sahrens int writable = 1;
294789Sahrens
295789Sahrens /*
296789Sahrens * The dataset must be visible by this zone -- check this first
297789Sahrens * so they don't see EPERM on something they shouldn't know about.
298789Sahrens */
299789Sahrens if (!INGLOBALZONE(curproc) &&
300789Sahrens !zone_dataset_visible(dataset, &writable))
301789Sahrens return (ENOENT);
302789Sahrens
303789Sahrens if (INGLOBALZONE(curproc)) {
304789Sahrens /*
305789Sahrens * If the fs is zoned, only root can access it from the
306789Sahrens * global zone.
307789Sahrens */
308789Sahrens if (secpolicy_zfs(cr) && zoned)
309789Sahrens return (EPERM);
310789Sahrens } else {
311789Sahrens /*
312789Sahrens * If we are in a local zone, the 'zoned' property must be set.
313789Sahrens */
314789Sahrens if (!zoned)
315789Sahrens return (EPERM);
316789Sahrens
317789Sahrens /* must be writable by this zone */
318789Sahrens if (!writable)
319789Sahrens return (EPERM);
320789Sahrens }
321789Sahrens return (0);
322789Sahrens }
323789Sahrens
32412786SChris.Kirby@oracle.com static int
zfs_dozonecheck(const char * dataset,cred_t * cr)32512786SChris.Kirby@oracle.com zfs_dozonecheck(const char *dataset, cred_t *cr)
32612786SChris.Kirby@oracle.com {
32712786SChris.Kirby@oracle.com uint64_t zoned;
32812786SChris.Kirby@oracle.com
32912786SChris.Kirby@oracle.com if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
33012786SChris.Kirby@oracle.com return (ENOENT);
33112786SChris.Kirby@oracle.com
33212786SChris.Kirby@oracle.com return (zfs_dozonecheck_impl(dataset, zoned, cr));
33312786SChris.Kirby@oracle.com }
33412786SChris.Kirby@oracle.com
33512786SChris.Kirby@oracle.com static int
zfs_dozonecheck_ds(const char * dataset,dsl_dataset_t * ds,cred_t * cr)33612786SChris.Kirby@oracle.com zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr)
33712786SChris.Kirby@oracle.com {
33812786SChris.Kirby@oracle.com uint64_t zoned;
33912786SChris.Kirby@oracle.com
34012786SChris.Kirby@oracle.com rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
34112786SChris.Kirby@oracle.com if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL)) {
34212786SChris.Kirby@oracle.com rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
34312786SChris.Kirby@oracle.com return (ENOENT);
34412786SChris.Kirby@oracle.com }
34512786SChris.Kirby@oracle.com rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
34612786SChris.Kirby@oracle.com
34712786SChris.Kirby@oracle.com return (zfs_dozonecheck_impl(dataset, zoned, cr));
34812786SChris.Kirby@oracle.com }
34912786SChris.Kirby@oracle.com
350789Sahrens int
zfs_secpolicy_write_perms(const char * name,const char * perm,cred_t * cr)3514543Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
352789Sahrens {
353789Sahrens int error;
354789Sahrens
3554543Smarks error = zfs_dozonecheck(name, cr);
3564543Smarks if (error == 0) {
3574543Smarks error = secpolicy_zfs(cr);
3584670Sahrens if (error)
3594543Smarks error = dsl_deleg_access(name, perm, cr);
3604543Smarks }
3614543Smarks return (error);
3624543Smarks }
3634543Smarks
36412786SChris.Kirby@oracle.com int
zfs_secpolicy_write_perms_ds(const char * name,dsl_dataset_t * ds,const char * perm,cred_t * cr)36512786SChris.Kirby@oracle.com zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds,
36612786SChris.Kirby@oracle.com const char *perm, cred_t *cr)
36712786SChris.Kirby@oracle.com {
36812786SChris.Kirby@oracle.com int error;
36912786SChris.Kirby@oracle.com
37012786SChris.Kirby@oracle.com error = zfs_dozonecheck_ds(name, ds, cr);
37112786SChris.Kirby@oracle.com if (error == 0) {
37212786SChris.Kirby@oracle.com error = secpolicy_zfs(cr);
37312786SChris.Kirby@oracle.com if (error)
37412786SChris.Kirby@oracle.com error = dsl_deleg_access_impl(ds, perm, cr);
37512786SChris.Kirby@oracle.com }
37612786SChris.Kirby@oracle.com return (error);
37712786SChris.Kirby@oracle.com }
37812786SChris.Kirby@oracle.com
37910972SRic.Aleshire@Sun.COM /*
38010972SRic.Aleshire@Sun.COM * Policy for setting the security label property.
38110972SRic.Aleshire@Sun.COM *
38210972SRic.Aleshire@Sun.COM * Returns 0 for success, non-zero for access and other errors.
38310972SRic.Aleshire@Sun.COM */
38410972SRic.Aleshire@Sun.COM static int
zfs_set_slabel_policy(const char * name,char * strval,cred_t * cr)38511022STom.Erickson@Sun.COM zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
38610972SRic.Aleshire@Sun.COM {
38710972SRic.Aleshire@Sun.COM char ds_hexsl[MAXNAMELEN];
38810972SRic.Aleshire@Sun.COM bslabel_t ds_sl, new_sl;
38910972SRic.Aleshire@Sun.COM boolean_t new_default = FALSE;
39010972SRic.Aleshire@Sun.COM uint64_t zoned;
39110972SRic.Aleshire@Sun.COM int needed_priv = -1;
39210972SRic.Aleshire@Sun.COM int error;
39310972SRic.Aleshire@Sun.COM
39410972SRic.Aleshire@Sun.COM /* First get the existing dataset label. */
39510972SRic.Aleshire@Sun.COM error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
39610972SRic.Aleshire@Sun.COM 1, sizeof (ds_hexsl), &ds_hexsl, NULL);
39710972SRic.Aleshire@Sun.COM if (error)
39810972SRic.Aleshire@Sun.COM return (EPERM);
39910972SRic.Aleshire@Sun.COM
40010972SRic.Aleshire@Sun.COM if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
40110972SRic.Aleshire@Sun.COM new_default = TRUE;
40210972SRic.Aleshire@Sun.COM
40310972SRic.Aleshire@Sun.COM /* The label must be translatable */
40410972SRic.Aleshire@Sun.COM if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
40510972SRic.Aleshire@Sun.COM return (EINVAL);
40610972SRic.Aleshire@Sun.COM
40710972SRic.Aleshire@Sun.COM /*
40810972SRic.Aleshire@Sun.COM * In a non-global zone, disallow attempts to set a label that
40910972SRic.Aleshire@Sun.COM * doesn't match that of the zone; otherwise no other checks
41010972SRic.Aleshire@Sun.COM * are needed.
41110972SRic.Aleshire@Sun.COM */
41210972SRic.Aleshire@Sun.COM if (!INGLOBALZONE(curproc)) {
41310972SRic.Aleshire@Sun.COM if (new_default || !blequal(&new_sl, CR_SL(CRED())))
41410972SRic.Aleshire@Sun.COM return (EPERM);
41510972SRic.Aleshire@Sun.COM return (0);
41610972SRic.Aleshire@Sun.COM }
41710972SRic.Aleshire@Sun.COM
41810972SRic.Aleshire@Sun.COM /*
41910972SRic.Aleshire@Sun.COM * For global-zone datasets (i.e., those whose zoned property is
42010972SRic.Aleshire@Sun.COM * "off", verify that the specified new label is valid for the
42110972SRic.Aleshire@Sun.COM * global zone.
42210972SRic.Aleshire@Sun.COM */
42310972SRic.Aleshire@Sun.COM if (dsl_prop_get_integer(name,
42410972SRic.Aleshire@Sun.COM zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
42510972SRic.Aleshire@Sun.COM return (EPERM);
42610972SRic.Aleshire@Sun.COM if (!zoned) {
42710972SRic.Aleshire@Sun.COM if (zfs_check_global_label(name, strval) != 0)
42810972SRic.Aleshire@Sun.COM return (EPERM);
42910972SRic.Aleshire@Sun.COM }
43010972SRic.Aleshire@Sun.COM
43110972SRic.Aleshire@Sun.COM /*
43210972SRic.Aleshire@Sun.COM * If the existing dataset label is nondefault, check if the
43310972SRic.Aleshire@Sun.COM * dataset is mounted (label cannot be changed while mounted).
43410972SRic.Aleshire@Sun.COM * Get the zfsvfs; if there isn't one, then the dataset isn't
43510972SRic.Aleshire@Sun.COM * mounted (or isn't a dataset, doesn't exist, ...).
43610972SRic.Aleshire@Sun.COM */
43710972SRic.Aleshire@Sun.COM if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
43811022STom.Erickson@Sun.COM objset_t *os;
43911022STom.Erickson@Sun.COM static char *setsl_tag = "setsl_tag";
44011022STom.Erickson@Sun.COM
44110972SRic.Aleshire@Sun.COM /*
44210972SRic.Aleshire@Sun.COM * Try to own the dataset; abort if there is any error,
44310972SRic.Aleshire@Sun.COM * (e.g., already mounted, in use, or other error).
44410972SRic.Aleshire@Sun.COM */
44510972SRic.Aleshire@Sun.COM error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
44611022STom.Erickson@Sun.COM setsl_tag, &os);
44710972SRic.Aleshire@Sun.COM if (error)
44810972SRic.Aleshire@Sun.COM return (EPERM);
44910972SRic.Aleshire@Sun.COM
45011022STom.Erickson@Sun.COM dmu_objset_disown(os, setsl_tag);
45111022STom.Erickson@Sun.COM
45210972SRic.Aleshire@Sun.COM if (new_default) {
45310972SRic.Aleshire@Sun.COM needed_priv = PRIV_FILE_DOWNGRADE_SL;
45410972SRic.Aleshire@Sun.COM goto out_check;
45510972SRic.Aleshire@Sun.COM }
45610972SRic.Aleshire@Sun.COM
45710972SRic.Aleshire@Sun.COM if (hexstr_to_label(strval, &new_sl) != 0)
45810972SRic.Aleshire@Sun.COM return (EPERM);
45910972SRic.Aleshire@Sun.COM
46010972SRic.Aleshire@Sun.COM if (blstrictdom(&ds_sl, &new_sl))
46110972SRic.Aleshire@Sun.COM needed_priv = PRIV_FILE_DOWNGRADE_SL;
46210972SRic.Aleshire@Sun.COM else if (blstrictdom(&new_sl, &ds_sl))
46310972SRic.Aleshire@Sun.COM needed_priv = PRIV_FILE_UPGRADE_SL;
46410972SRic.Aleshire@Sun.COM } else {
46510972SRic.Aleshire@Sun.COM /* dataset currently has a default label */
46610972SRic.Aleshire@Sun.COM if (!new_default)
46710972SRic.Aleshire@Sun.COM needed_priv = PRIV_FILE_UPGRADE_SL;
46810972SRic.Aleshire@Sun.COM }
46910972SRic.Aleshire@Sun.COM
47010972SRic.Aleshire@Sun.COM out_check:
47110972SRic.Aleshire@Sun.COM if (needed_priv != -1)
47210972SRic.Aleshire@Sun.COM return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
47310972SRic.Aleshire@Sun.COM return (0);
47410972SRic.Aleshire@Sun.COM }
47510972SRic.Aleshire@Sun.COM
4764543Smarks static int
zfs_secpolicy_setprop(const char * dsname,zfs_prop_t prop,nvpair_t * propval,cred_t * cr)47711022STom.Erickson@Sun.COM zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
47811022STom.Erickson@Sun.COM cred_t *cr)
4794543Smarks {
48011022STom.Erickson@Sun.COM char *strval;
48111022STom.Erickson@Sun.COM
4824543Smarks /*
4834543Smarks * Check permissions for special properties.
4844543Smarks */
4854543Smarks switch (prop) {
4864543Smarks case ZFS_PROP_ZONED:
4874543Smarks /*
4884543Smarks * Disallow setting of 'zoned' from within a local zone.
4894543Smarks */
4904543Smarks if (!INGLOBALZONE(curproc))
4914543Smarks return (EPERM);
4924543Smarks break;
493789Sahrens
4944543Smarks case ZFS_PROP_QUOTA:
4954543Smarks if (!INGLOBALZONE(curproc)) {
4964543Smarks uint64_t zoned;
4974543Smarks char setpoint[MAXNAMELEN];
4984543Smarks /*
4994543Smarks * Unprivileged users are allowed to modify the
5004543Smarks * quota on things *under* (ie. contained by)
5014543Smarks * the thing they own.
5024543Smarks */
50311022STom.Erickson@Sun.COM if (dsl_prop_get_integer(dsname, "zoned", &zoned,
5044543Smarks setpoint))
5054543Smarks return (EPERM);
50611022STom.Erickson@Sun.COM if (!zoned || strlen(dsname) <= strlen(setpoint))
5074543Smarks return (EPERM);
5084543Smarks }
5094670Sahrens break;
51010972SRic.Aleshire@Sun.COM
51110972SRic.Aleshire@Sun.COM case ZFS_PROP_MLSLABEL:
51210972SRic.Aleshire@Sun.COM if (!is_system_labeled())
51310972SRic.Aleshire@Sun.COM return (EPERM);
51411022STom.Erickson@Sun.COM
51511022STom.Erickson@Sun.COM if (nvpair_value_string(propval, &strval) == 0) {
51611022STom.Erickson@Sun.COM int err;
51711022STom.Erickson@Sun.COM
51811022STom.Erickson@Sun.COM err = zfs_set_slabel_policy(dsname, strval, CRED());
51911022STom.Erickson@Sun.COM if (err != 0)
52011022STom.Erickson@Sun.COM return (err);
52111022STom.Erickson@Sun.COM }
52210972SRic.Aleshire@Sun.COM break;
5234543Smarks }
5244543Smarks
52511022STom.Erickson@Sun.COM return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
526789Sahrens }
527789Sahrens
5284543Smarks int
zfs_secpolicy_fsacl(zfs_cmd_t * zc,cred_t * cr)5294543Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
5304543Smarks {
5314543Smarks int error;
5324543Smarks
5334543Smarks error = zfs_dozonecheck(zc->zc_name, cr);
5344543Smarks if (error)
5354543Smarks return (error);
5364543Smarks
5374543Smarks /*
5384543Smarks * permission to set permissions will be evaluated later in
5394543Smarks * dsl_deleg_can_allow()
5404543Smarks */
5414543Smarks return (0);
5424543Smarks }
5434543Smarks
5444543Smarks int
zfs_secpolicy_rollback(zfs_cmd_t * zc,cred_t * cr)5454543Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
5464543Smarks {
54710588SEric.Taylor@Sun.COM return (zfs_secpolicy_write_perms(zc->zc_name,
54810588SEric.Taylor@Sun.COM ZFS_DELEG_PERM_ROLLBACK, cr));
5494543Smarks }
5504543Smarks
5514543Smarks int
zfs_secpolicy_send(zfs_cmd_t * zc,cred_t * cr)5524543Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
5534543Smarks {
55412786SChris.Kirby@oracle.com spa_t *spa;
55512786SChris.Kirby@oracle.com dsl_pool_t *dp;
55612786SChris.Kirby@oracle.com dsl_dataset_t *ds;
55712786SChris.Kirby@oracle.com char *cp;
55812786SChris.Kirby@oracle.com int error;
55912786SChris.Kirby@oracle.com
56012786SChris.Kirby@oracle.com /*
56112786SChris.Kirby@oracle.com * Generate the current snapshot name from the given objsetid, then
56212786SChris.Kirby@oracle.com * use that name for the secpolicy/zone checks.
56312786SChris.Kirby@oracle.com */
56412786SChris.Kirby@oracle.com cp = strchr(zc->zc_name, '@');
56512786SChris.Kirby@oracle.com if (cp == NULL)
56612786SChris.Kirby@oracle.com return (EINVAL);
56712786SChris.Kirby@oracle.com error = spa_open(zc->zc_name, &spa, FTAG);
56812786SChris.Kirby@oracle.com if (error)
56912786SChris.Kirby@oracle.com return (error);
57012786SChris.Kirby@oracle.com
57112786SChris.Kirby@oracle.com dp = spa_get_dsl(spa);
57212786SChris.Kirby@oracle.com rw_enter(&dp->dp_config_rwlock, RW_READER);
57312786SChris.Kirby@oracle.com error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
57412786SChris.Kirby@oracle.com rw_exit(&dp->dp_config_rwlock);
57512786SChris.Kirby@oracle.com spa_close(spa, FTAG);
57612786SChris.Kirby@oracle.com if (error)
57712786SChris.Kirby@oracle.com return (error);
57812786SChris.Kirby@oracle.com
57912786SChris.Kirby@oracle.com dsl_dataset_name(ds, zc->zc_name);
58012786SChris.Kirby@oracle.com
58112786SChris.Kirby@oracle.com error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds,
58212786SChris.Kirby@oracle.com ZFS_DELEG_PERM_SEND, cr);
58312786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
58412786SChris.Kirby@oracle.com
58512786SChris.Kirby@oracle.com return (error);
5864543Smarks }
5874543Smarks
5888845Samw@Sun.COM static int
zfs_secpolicy_deleg_share(zfs_cmd_t * zc,cred_t * cr)5898845Samw@Sun.COM zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
5908845Samw@Sun.COM {
5918845Samw@Sun.COM vnode_t *vp;
5928845Samw@Sun.COM int error;
5938845Samw@Sun.COM
5948845Samw@Sun.COM if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
5958845Samw@Sun.COM NO_FOLLOW, NULL, &vp)) != 0)
5968845Samw@Sun.COM return (error);
5978845Samw@Sun.COM
5988845Samw@Sun.COM /* Now make sure mntpnt and dataset are ZFS */
5998845Samw@Sun.COM
6008845Samw@Sun.COM if (vp->v_vfsp->vfs_fstype != zfsfstype ||
6018845Samw@Sun.COM (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
6028845Samw@Sun.COM zc->zc_name) != 0)) {
6038845Samw@Sun.COM VN_RELE(vp);
6048845Samw@Sun.COM return (EPERM);
6058845Samw@Sun.COM }
6068845Samw@Sun.COM
6078845Samw@Sun.COM VN_RELE(vp);
6088845Samw@Sun.COM return (dsl_deleg_access(zc->zc_name,
6098845Samw@Sun.COM ZFS_DELEG_PERM_SHARE, cr));
6108845Samw@Sun.COM }
6118845Samw@Sun.COM
6124543Smarks int
zfs_secpolicy_share(zfs_cmd_t * zc,cred_t * cr)6134543Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
6144543Smarks {
6154543Smarks if (!INGLOBALZONE(curproc))
6164543Smarks return (EPERM);
6174543Smarks
6185367Sahrens if (secpolicy_nfs(cr) == 0) {
6194543Smarks return (0);
6204543Smarks } else {
6218845Samw@Sun.COM return (zfs_secpolicy_deleg_share(zc, cr));
6228845Samw@Sun.COM }
6238845Samw@Sun.COM }
6248845Samw@Sun.COM
6258845Samw@Sun.COM int
zfs_secpolicy_smb_acl(zfs_cmd_t * zc,cred_t * cr)6268845Samw@Sun.COM zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
6278845Samw@Sun.COM {
6288845Samw@Sun.COM if (!INGLOBALZONE(curproc))
6298845Samw@Sun.COM return (EPERM);
6308845Samw@Sun.COM
6318845Samw@Sun.COM if (secpolicy_smb(cr) == 0) {
6328845Samw@Sun.COM return (0);
6338845Samw@Sun.COM } else {
6348845Samw@Sun.COM return (zfs_secpolicy_deleg_share(zc, cr));
6354543Smarks }
6364543Smarks }
6374543Smarks
638789Sahrens static int
zfs_get_parent(const char * datasetname,char * parent,int parentsize)6394543Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
640789Sahrens {
641789Sahrens char *cp;
642789Sahrens
643789Sahrens /*
644789Sahrens * Remove the @bla or /bla from the end of the name to get the parent.
645789Sahrens */
6464543Smarks (void) strncpy(parent, datasetname, parentsize);
6474543Smarks cp = strrchr(parent, '@');
648789Sahrens if (cp != NULL) {
649789Sahrens cp[0] = '\0';
650789Sahrens } else {
6514543Smarks cp = strrchr(parent, '/');
652789Sahrens if (cp == NULL)
653789Sahrens return (ENOENT);
654789Sahrens cp[0] = '\0';
655789Sahrens }
656789Sahrens
6574543Smarks return (0);
6584543Smarks }
6594543Smarks
6604543Smarks int
zfs_secpolicy_destroy_perms(const char * name,cred_t * cr)6614543Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
6624543Smarks {
6634543Smarks int error;
6644543Smarks
6654543Smarks if ((error = zfs_secpolicy_write_perms(name,
6664543Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6674543Smarks return (error);
6684543Smarks
6694543Smarks return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
6704543Smarks }
6714543Smarks
6724543Smarks static int
zfs_secpolicy_destroy(zfs_cmd_t * zc,cred_t * cr)6734543Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
6744543Smarks {
6754543Smarks return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
6764543Smarks }
6774543Smarks
6784543Smarks /*
67911314Swilliam.gorrell@sun.com * Destroying snapshots with delegated permissions requires
68011314Swilliam.gorrell@sun.com * descendent mount and destroy permissions.
68111314Swilliam.gorrell@sun.com * Reassemble the full filesystem@snap name so dsl_deleg_access()
68211314Swilliam.gorrell@sun.com * can do the correct permission check.
68311314Swilliam.gorrell@sun.com *
68411314Swilliam.gorrell@sun.com * Since this routine is used when doing a recursive destroy of snapshots
68511314Swilliam.gorrell@sun.com * and destroying snapshots requires descendent permissions, a successfull
68611314Swilliam.gorrell@sun.com * check of the top level snapshot applies to snapshots of all descendent
68711314Swilliam.gorrell@sun.com * datasets as well.
68811314Swilliam.gorrell@sun.com */
68911314Swilliam.gorrell@sun.com static int
zfs_secpolicy_destroy_snaps(zfs_cmd_t * zc,cred_t * cr)69011314Swilliam.gorrell@sun.com zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr)
69111314Swilliam.gorrell@sun.com {
69211314Swilliam.gorrell@sun.com int error;
69311314Swilliam.gorrell@sun.com char *dsname;
69411314Swilliam.gorrell@sun.com
69511314Swilliam.gorrell@sun.com dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
69611314Swilliam.gorrell@sun.com
69711314Swilliam.gorrell@sun.com error = zfs_secpolicy_destroy_perms(dsname, cr);
69811314Swilliam.gorrell@sun.com
69911314Swilliam.gorrell@sun.com strfree(dsname);
70011314Swilliam.gorrell@sun.com return (error);
70111314Swilliam.gorrell@sun.com }
70211314Swilliam.gorrell@sun.com
7034543Smarks int
zfs_secpolicy_rename_perms(const char * from,const char * to,cred_t * cr)7044543Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
7054543Smarks {
70611022STom.Erickson@Sun.COM char parentname[MAXNAMELEN];
7074543Smarks int error;
7084543Smarks
7094543Smarks if ((error = zfs_secpolicy_write_perms(from,
7104543Smarks ZFS_DELEG_PERM_RENAME, cr)) != 0)
7114543Smarks return (error);
7124543Smarks
7134543Smarks if ((error = zfs_secpolicy_write_perms(from,
7144543Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7154543Smarks return (error);
7164543Smarks
7174543Smarks if ((error = zfs_get_parent(to, parentname,
7184543Smarks sizeof (parentname))) != 0)
7194543Smarks return (error);
7204543Smarks
7214543Smarks if ((error = zfs_secpolicy_write_perms(parentname,
7224543Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0)
7234543Smarks return (error);
7244543Smarks
7254543Smarks if ((error = zfs_secpolicy_write_perms(parentname,
7264543Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7274543Smarks return (error);
7284543Smarks
7294543Smarks return (error);
7304543Smarks }
7314543Smarks
7324543Smarks static int
zfs_secpolicy_rename(zfs_cmd_t * zc,cred_t * cr)7334543Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
7344543Smarks {
7354543Smarks return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
7364543Smarks }
7374543Smarks
7384543Smarks static int
zfs_secpolicy_promote(zfs_cmd_t * zc,cred_t * cr)7394543Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
7404543Smarks {
74111022STom.Erickson@Sun.COM char parentname[MAXNAMELEN];
7424543Smarks objset_t *clone;
7434543Smarks int error;
7444543Smarks
7454543Smarks error = zfs_secpolicy_write_perms(zc->zc_name,
7464543Smarks ZFS_DELEG_PERM_PROMOTE, cr);
7474543Smarks if (error)
7484543Smarks return (error);
7494543Smarks
75010298SMatthew.Ahrens@Sun.COM error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
7514543Smarks
7524543Smarks if (error == 0) {
7534543Smarks dsl_dataset_t *pclone = NULL;
7544543Smarks dsl_dir_t *dd;
75510298SMatthew.Ahrens@Sun.COM dd = clone->os_dsl_dataset->ds_dir;
7564543Smarks
7574543Smarks rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
7586689Smaybee error = dsl_dataset_hold_obj(dd->dd_pool,
7596689Smaybee dd->dd_phys->dd_origin_obj, FTAG, &pclone);
7604543Smarks rw_exit(&dd->dd_pool->dp_config_rwlock);
7614543Smarks if (error) {
76210298SMatthew.Ahrens@Sun.COM dmu_objset_rele(clone, FTAG);
7634543Smarks return (error);
7644543Smarks }
7654543Smarks
7664543Smarks error = zfs_secpolicy_write_perms(zc->zc_name,
7674543Smarks ZFS_DELEG_PERM_MOUNT, cr);
7684543Smarks
7694543Smarks dsl_dataset_name(pclone, parentname);
77010298SMatthew.Ahrens@Sun.COM dmu_objset_rele(clone, FTAG);
7716689Smaybee dsl_dataset_rele(pclone, FTAG);
7724543Smarks if (error == 0)
7734543Smarks error = zfs_secpolicy_write_perms(parentname,
7744543Smarks ZFS_DELEG_PERM_PROMOTE, cr);
7754543Smarks }
7764543Smarks return (error);
7774543Smarks }
7784543Smarks
7794543Smarks static int
zfs_secpolicy_receive(zfs_cmd_t * zc,cred_t * cr)7804543Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
7814543Smarks {
7824543Smarks int error;
7834543Smarks
7844543Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name,
7854543Smarks ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
7864543Smarks return (error);
7874543Smarks
7884543Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_name,
7894543Smarks ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7904543Smarks return (error);
7914543Smarks
7924543Smarks return (zfs_secpolicy_write_perms(zc->zc_name,
7934543Smarks ZFS_DELEG_PERM_CREATE, cr));
7944543Smarks }
7954543Smarks
7964543Smarks int
zfs_secpolicy_snapshot_perms(const char * name,cred_t * cr)7974543Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
7984543Smarks {
79910588SEric.Taylor@Sun.COM return (zfs_secpolicy_write_perms(name,
80010588SEric.Taylor@Sun.COM ZFS_DELEG_PERM_SNAPSHOT, cr));
8014543Smarks }
8024543Smarks
8034543Smarks static int
zfs_secpolicy_snapshot(zfs_cmd_t * zc,cred_t * cr)8044543Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
8054543Smarks {
8064543Smarks
8074543Smarks return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
8084543Smarks }
8094543Smarks
8104543Smarks static int
zfs_secpolicy_create(zfs_cmd_t * zc,cred_t * cr)8114543Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
8124543Smarks {
81311022STom.Erickson@Sun.COM char parentname[MAXNAMELEN];
81411022STom.Erickson@Sun.COM int error;
8154543Smarks
8164543Smarks if ((error = zfs_get_parent(zc->zc_name, parentname,
8174543Smarks sizeof (parentname))) != 0)
8184543Smarks return (error);
8194543Smarks
8204543Smarks if (zc->zc_value[0] != '\0') {
8214543Smarks if ((error = zfs_secpolicy_write_perms(zc->zc_value,
8224543Smarks ZFS_DELEG_PERM_CLONE, cr)) != 0)
8234543Smarks return (error);
8244543Smarks }
8254543Smarks
8264543Smarks if ((error = zfs_secpolicy_write_perms(parentname,
8274543Smarks ZFS_DELEG_PERM_CREATE, cr)) != 0)
8284543Smarks return (error);
8294543Smarks
8304543Smarks error = zfs_secpolicy_write_perms(parentname,
8314543Smarks ZFS_DELEG_PERM_MOUNT, cr);
8324543Smarks
8334543Smarks return (error);
8344543Smarks }
8354543Smarks
8364543Smarks static int
zfs_secpolicy_umount(zfs_cmd_t * zc,cred_t * cr)8374543Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
8384543Smarks {
8394543Smarks int error;
8404543Smarks
8414543Smarks error = secpolicy_fs_unmount(cr, NULL);
8424543Smarks if (error) {
8434543Smarks error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
8444543Smarks }
8454543Smarks return (error);
846789Sahrens }
847789Sahrens
848789Sahrens /*
849789Sahrens * Policy for pool operations - create/destroy pools, add vdevs, etc. Requires
850789Sahrens * SYS_CONFIG privilege, which is not available in a local zone.
851789Sahrens */
852789Sahrens /* ARGSUSED */
853789Sahrens static int
zfs_secpolicy_config(zfs_cmd_t * zc,cred_t * cr)8544543Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
855789Sahrens {
856789Sahrens if (secpolicy_sys_config(cr, B_FALSE) != 0)
857789Sahrens return (EPERM);
858789Sahrens
859789Sahrens return (0);
860789Sahrens }
861789Sahrens
862789Sahrens /*
86313043STim.Haley@Sun.COM * Policy for object to name lookups.
86413043STim.Haley@Sun.COM */
86513043STim.Haley@Sun.COM /* ARGSUSED */
86613043STim.Haley@Sun.COM static int
zfs_secpolicy_diff(zfs_cmd_t * zc,cred_t * cr)86713043STim.Haley@Sun.COM zfs_secpolicy_diff(zfs_cmd_t *zc, cred_t *cr)
86813043STim.Haley@Sun.COM {
86913043STim.Haley@Sun.COM int error;
87013043STim.Haley@Sun.COM
87113043STim.Haley@Sun.COM if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0)
87213043STim.Haley@Sun.COM return (0);
87313043STim.Haley@Sun.COM
87413043STim.Haley@Sun.COM error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr);
87513043STim.Haley@Sun.COM return (error);
87613043STim.Haley@Sun.COM }
87713043STim.Haley@Sun.COM
87813043STim.Haley@Sun.COM /*
8791544Seschrock * Policy for fault injection. Requires all privileges.
8801544Seschrock */
8811544Seschrock /* ARGSUSED */
8821544Seschrock static int
zfs_secpolicy_inject(zfs_cmd_t * zc,cred_t * cr)8834543Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
8841544Seschrock {
8851544Seschrock return (secpolicy_zinject(cr));
8861544Seschrock }
8871544Seschrock
8884849Sahrens static int
zfs_secpolicy_inherit(zfs_cmd_t * zc,cred_t * cr)8894849Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
8904849Sahrens {
8914849Sahrens zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
8924849Sahrens
8935094Slling if (prop == ZPROP_INVAL) {
8944849Sahrens if (!zfs_prop_user(zc->zc_value))
8954849Sahrens return (EINVAL);
8964849Sahrens return (zfs_secpolicy_write_perms(zc->zc_name,
8974849Sahrens ZFS_DELEG_PERM_USERPROP, cr));
8984849Sahrens } else {
89911022STom.Erickson@Sun.COM return (zfs_secpolicy_setprop(zc->zc_name, prop,
90011022STom.Erickson@Sun.COM NULL, cr));
9014849Sahrens }
9024849Sahrens }
9034849Sahrens
9049396SMatthew.Ahrens@Sun.COM static int
zfs_secpolicy_userspace_one(zfs_cmd_t * zc,cred_t * cr)9059396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
9069396SMatthew.Ahrens@Sun.COM {
9079396SMatthew.Ahrens@Sun.COM int err = zfs_secpolicy_read(zc, cr);
9089396SMatthew.Ahrens@Sun.COM if (err)
9099396SMatthew.Ahrens@Sun.COM return (err);
9109396SMatthew.Ahrens@Sun.COM
9119396SMatthew.Ahrens@Sun.COM if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
9129396SMatthew.Ahrens@Sun.COM return (EINVAL);
9139396SMatthew.Ahrens@Sun.COM
9149396SMatthew.Ahrens@Sun.COM if (zc->zc_value[0] == 0) {
9159396SMatthew.Ahrens@Sun.COM /*
9169396SMatthew.Ahrens@Sun.COM * They are asking about a posix uid/gid. If it's
9179396SMatthew.Ahrens@Sun.COM * themself, allow it.
9189396SMatthew.Ahrens@Sun.COM */
9199396SMatthew.Ahrens@Sun.COM if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
9209396SMatthew.Ahrens@Sun.COM zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
9219396SMatthew.Ahrens@Sun.COM if (zc->zc_guid == crgetuid(cr))
9229396SMatthew.Ahrens@Sun.COM return (0);
9239396SMatthew.Ahrens@Sun.COM } else {
9249396SMatthew.Ahrens@Sun.COM if (groupmember(zc->zc_guid, cr))
9259396SMatthew.Ahrens@Sun.COM return (0);
9269396SMatthew.Ahrens@Sun.COM }
9279396SMatthew.Ahrens@Sun.COM }
9289396SMatthew.Ahrens@Sun.COM
9299396SMatthew.Ahrens@Sun.COM return (zfs_secpolicy_write_perms(zc->zc_name,
9309396SMatthew.Ahrens@Sun.COM userquota_perms[zc->zc_objset_type], cr));
9319396SMatthew.Ahrens@Sun.COM }
9329396SMatthew.Ahrens@Sun.COM
9339396SMatthew.Ahrens@Sun.COM static int
zfs_secpolicy_userspace_many(zfs_cmd_t * zc,cred_t * cr)9349396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
9359396SMatthew.Ahrens@Sun.COM {
9369396SMatthew.Ahrens@Sun.COM int err = zfs_secpolicy_read(zc, cr);
9379396SMatthew.Ahrens@Sun.COM if (err)
9389396SMatthew.Ahrens@Sun.COM return (err);
9399396SMatthew.Ahrens@Sun.COM
9409396SMatthew.Ahrens@Sun.COM if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
9419396SMatthew.Ahrens@Sun.COM return (EINVAL);
9429396SMatthew.Ahrens@Sun.COM
9439396SMatthew.Ahrens@Sun.COM return (zfs_secpolicy_write_perms(zc->zc_name,
9449396SMatthew.Ahrens@Sun.COM userquota_perms[zc->zc_objset_type], cr));
9459396SMatthew.Ahrens@Sun.COM }
9469396SMatthew.Ahrens@Sun.COM
9479396SMatthew.Ahrens@Sun.COM static int
zfs_secpolicy_userspace_upgrade(zfs_cmd_t * zc,cred_t * cr)9489396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
9499396SMatthew.Ahrens@Sun.COM {
95011022STom.Erickson@Sun.COM return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
95111022STom.Erickson@Sun.COM NULL, cr));
9529396SMatthew.Ahrens@Sun.COM }
9539396SMatthew.Ahrens@Sun.COM
95410242Schris.kirby@sun.com static int
zfs_secpolicy_hold(zfs_cmd_t * zc,cred_t * cr)95510242Schris.kirby@sun.com zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
95610242Schris.kirby@sun.com {
95710242Schris.kirby@sun.com return (zfs_secpolicy_write_perms(zc->zc_name,
95810242Schris.kirby@sun.com ZFS_DELEG_PERM_HOLD, cr));
95910242Schris.kirby@sun.com }
96010242Schris.kirby@sun.com
96110242Schris.kirby@sun.com static int
zfs_secpolicy_release(zfs_cmd_t * zc,cred_t * cr)96210242Schris.kirby@sun.com zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
96310242Schris.kirby@sun.com {
96410242Schris.kirby@sun.com return (zfs_secpolicy_write_perms(zc->zc_name,
96510242Schris.kirby@sun.com ZFS_DELEG_PERM_RELEASE, cr));
96610242Schris.kirby@sun.com }
96710242Schris.kirby@sun.com
9681544Seschrock /*
96913043STim.Haley@Sun.COM * Policy for allowing temporary snapshots to be taken or released
97013043STim.Haley@Sun.COM */
97113043STim.Haley@Sun.COM static int
zfs_secpolicy_tmp_snapshot(zfs_cmd_t * zc,cred_t * cr)97213043STim.Haley@Sun.COM zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, cred_t *cr)
97313043STim.Haley@Sun.COM {
97413043STim.Haley@Sun.COM /*
97513043STim.Haley@Sun.COM * A temporary snapshot is the same as a snapshot,
97613043STim.Haley@Sun.COM * hold, destroy and release all rolled into one.
97713043STim.Haley@Sun.COM * Delegated diff alone is sufficient that we allow this.
97813043STim.Haley@Sun.COM */
97913043STim.Haley@Sun.COM int error;
98013043STim.Haley@Sun.COM
98113043STim.Haley@Sun.COM if ((error = zfs_secpolicy_write_perms(zc->zc_name,
98213043STim.Haley@Sun.COM ZFS_DELEG_PERM_DIFF, cr)) == 0)
98313043STim.Haley@Sun.COM return (0);
98413043STim.Haley@Sun.COM
98513043STim.Haley@Sun.COM error = zfs_secpolicy_snapshot(zc, cr);
98613043STim.Haley@Sun.COM if (!error)
98713043STim.Haley@Sun.COM error = zfs_secpolicy_hold(zc, cr);
98813043STim.Haley@Sun.COM if (!error)
98913043STim.Haley@Sun.COM error = zfs_secpolicy_release(zc, cr);
99013043STim.Haley@Sun.COM if (!error)
99113043STim.Haley@Sun.COM error = zfs_secpolicy_destroy(zc, cr);
99213043STim.Haley@Sun.COM return (error);
99313043STim.Haley@Sun.COM }
99413043STim.Haley@Sun.COM
99513043STim.Haley@Sun.COM /*
996789Sahrens * Returns the nvlist as specified by the user in the zfs_cmd_t.
997789Sahrens */
998789Sahrens static int
get_nvlist(uint64_t nvl,uint64_t size,int iflag,nvlist_t ** nvp)9999643SEric.Taylor@Sun.COM get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
1000789Sahrens {
1001789Sahrens char *packed;
1002789Sahrens int error;
10035094Slling nvlist_t *list = NULL;
1004789Sahrens
1005789Sahrens /*
10062676Seschrock * Read in and unpack the user-supplied nvlist.
1007789Sahrens */
10085094Slling if (size == 0)
1009789Sahrens return (EINVAL);
1010789Sahrens
1011789Sahrens packed = kmem_alloc(size, KM_SLEEP);
1012789Sahrens
10139643SEric.Taylor@Sun.COM if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
10149643SEric.Taylor@Sun.COM iflag)) != 0) {
1015789Sahrens kmem_free(packed, size);
1016789Sahrens return (error);
1017789Sahrens }
1018789Sahrens
10195094Slling if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
1020789Sahrens kmem_free(packed, size);
1021789Sahrens return (error);
1022789Sahrens }
1023789Sahrens
1024789Sahrens kmem_free(packed, size);
1025789Sahrens
10265094Slling *nvp = list;
1027789Sahrens return (0);
1028789Sahrens }
1029789Sahrens
1030789Sahrens static int
fit_error_list(zfs_cmd_t * zc,nvlist_t ** errors)103111022STom.Erickson@Sun.COM fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
103211022STom.Erickson@Sun.COM {
103311022STom.Erickson@Sun.COM size_t size;
103411022STom.Erickson@Sun.COM
103511022STom.Erickson@Sun.COM VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
103611022STom.Erickson@Sun.COM
103711022STom.Erickson@Sun.COM if (size > zc->zc_nvlist_dst_size) {
103811022STom.Erickson@Sun.COM nvpair_t *more_errors;
103911022STom.Erickson@Sun.COM int n = 0;
104011022STom.Erickson@Sun.COM
104111022STom.Erickson@Sun.COM if (zc->zc_nvlist_dst_size < 1024)
104211022STom.Erickson@Sun.COM return (ENOMEM);
104311022STom.Erickson@Sun.COM
104411022STom.Erickson@Sun.COM VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
104511022STom.Erickson@Sun.COM more_errors = nvlist_prev_nvpair(*errors, NULL);
104611022STom.Erickson@Sun.COM
104711022STom.Erickson@Sun.COM do {
104811022STom.Erickson@Sun.COM nvpair_t *pair = nvlist_prev_nvpair(*errors,
104911022STom.Erickson@Sun.COM more_errors);
105011022STom.Erickson@Sun.COM VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
105111022STom.Erickson@Sun.COM n++;
105211022STom.Erickson@Sun.COM VERIFY(nvlist_size(*errors, &size,
105311022STom.Erickson@Sun.COM NV_ENCODE_NATIVE) == 0);
105411022STom.Erickson@Sun.COM } while (size > zc->zc_nvlist_dst_size);
105511022STom.Erickson@Sun.COM
105611022STom.Erickson@Sun.COM VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
105711022STom.Erickson@Sun.COM VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
105811022STom.Erickson@Sun.COM ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
105911022STom.Erickson@Sun.COM ASSERT(size <= zc->zc_nvlist_dst_size);
106011022STom.Erickson@Sun.COM }
106111022STom.Erickson@Sun.COM
106211022STom.Erickson@Sun.COM return (0);
106311022STom.Erickson@Sun.COM }
106411022STom.Erickson@Sun.COM
106511022STom.Erickson@Sun.COM static int
put_nvlist(zfs_cmd_t * zc,nvlist_t * nvl)10662676Seschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
10672676Seschrock {
10682676Seschrock char *packed = NULL;
106911807SSam.Falkner@Sun.COM int error = 0;
10702676Seschrock size_t size;
10712676Seschrock
10722676Seschrock VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
10732676Seschrock
10742676Seschrock if (size > zc->zc_nvlist_dst_size) {
10752676Seschrock error = ENOMEM;
10762676Seschrock } else {
10774611Smarks packed = kmem_alloc(size, KM_SLEEP);
10782676Seschrock VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
10792676Seschrock KM_SLEEP) == 0);
108011807SSam.Falkner@Sun.COM if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
108111807SSam.Falkner@Sun.COM size, zc->zc_iflags) != 0)
108211807SSam.Falkner@Sun.COM error = EFAULT;
10832676Seschrock kmem_free(packed, size);
10842676Seschrock }
10852676Seschrock
10862676Seschrock zc->zc_nvlist_dst_size = size;
10872676Seschrock return (error);
10882676Seschrock }
10892676Seschrock
10902676Seschrock static int
getzfsvfs(const char * dsname,zfsvfs_t ** zfvp)109111185SSean.McEnroe@Sun.COM getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
10929396SMatthew.Ahrens@Sun.COM {
10939396SMatthew.Ahrens@Sun.COM objset_t *os;
10949396SMatthew.Ahrens@Sun.COM int error;
10959396SMatthew.Ahrens@Sun.COM
109610298SMatthew.Ahrens@Sun.COM error = dmu_objset_hold(dsname, FTAG, &os);
10979396SMatthew.Ahrens@Sun.COM if (error)
10989396SMatthew.Ahrens@Sun.COM return (error);
109910298SMatthew.Ahrens@Sun.COM if (dmu_objset_type(os) != DMU_OST_ZFS) {
110010298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
110110298SMatthew.Ahrens@Sun.COM return (EINVAL);
110210298SMatthew.Ahrens@Sun.COM }
110310298SMatthew.Ahrens@Sun.COM
110410298SMatthew.Ahrens@Sun.COM mutex_enter(&os->os_user_ptr_lock);
110511185SSean.McEnroe@Sun.COM *zfvp = dmu_objset_get_user(os);
110611185SSean.McEnroe@Sun.COM if (*zfvp) {
110711185SSean.McEnroe@Sun.COM VFS_HOLD((*zfvp)->z_vfs);
11089396SMatthew.Ahrens@Sun.COM } else {
11099396SMatthew.Ahrens@Sun.COM error = ESRCH;
11109396SMatthew.Ahrens@Sun.COM }
111110298SMatthew.Ahrens@Sun.COM mutex_exit(&os->os_user_ptr_lock);
111210298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
11139396SMatthew.Ahrens@Sun.COM return (error);
11149396SMatthew.Ahrens@Sun.COM }
11159396SMatthew.Ahrens@Sun.COM
11169396SMatthew.Ahrens@Sun.COM /*
11179396SMatthew.Ahrens@Sun.COM * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
11189396SMatthew.Ahrens@Sun.COM * case its z_vfs will be NULL, and it will be opened as the owner.
11199396SMatthew.Ahrens@Sun.COM */
11209396SMatthew.Ahrens@Sun.COM static int
zfsvfs_hold(const char * name,void * tag,zfsvfs_t ** zfvp,boolean_t writer)112112620SMark.Shellenbaum@Oracle.COM zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer)
11229396SMatthew.Ahrens@Sun.COM {
11239396SMatthew.Ahrens@Sun.COM int error = 0;
11249396SMatthew.Ahrens@Sun.COM
112511185SSean.McEnroe@Sun.COM if (getzfsvfs(name, zfvp) != 0)
112611185SSean.McEnroe@Sun.COM error = zfsvfs_create(name, zfvp);
11279396SMatthew.Ahrens@Sun.COM if (error == 0) {
112812620SMark.Shellenbaum@Oracle.COM rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER :
112912620SMark.Shellenbaum@Oracle.COM RW_READER, tag);
113011185SSean.McEnroe@Sun.COM if ((*zfvp)->z_unmounted) {
11319396SMatthew.Ahrens@Sun.COM /*
11329396SMatthew.Ahrens@Sun.COM * XXX we could probably try again, since the unmounting
11339396SMatthew.Ahrens@Sun.COM * thread should be just about to disassociate the
11349396SMatthew.Ahrens@Sun.COM * objset from the zfsvfs.
11359396SMatthew.Ahrens@Sun.COM */
113611185SSean.McEnroe@Sun.COM rrw_exit(&(*zfvp)->z_teardown_lock, tag);
11379396SMatthew.Ahrens@Sun.COM return (EBUSY);
11389396SMatthew.Ahrens@Sun.COM }
11399396SMatthew.Ahrens@Sun.COM }
11409396SMatthew.Ahrens@Sun.COM return (error);
11419396SMatthew.Ahrens@Sun.COM }
11429396SMatthew.Ahrens@Sun.COM
11439396SMatthew.Ahrens@Sun.COM static void
zfsvfs_rele(zfsvfs_t * zfsvfs,void * tag)11449396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
11459396SMatthew.Ahrens@Sun.COM {
11469396SMatthew.Ahrens@Sun.COM rrw_exit(&zfsvfs->z_teardown_lock, tag);
11479396SMatthew.Ahrens@Sun.COM
11489396SMatthew.Ahrens@Sun.COM if (zfsvfs->z_vfs) {
11499396SMatthew.Ahrens@Sun.COM VFS_RELE(zfsvfs->z_vfs);
11509396SMatthew.Ahrens@Sun.COM } else {
115110298SMatthew.Ahrens@Sun.COM dmu_objset_disown(zfsvfs->z_os, zfsvfs);
11529396SMatthew.Ahrens@Sun.COM zfsvfs_free(zfsvfs);
11539396SMatthew.Ahrens@Sun.COM }
11549396SMatthew.Ahrens@Sun.COM }
11559396SMatthew.Ahrens@Sun.COM
11569396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_pool_create(zfs_cmd_t * zc)1157789Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1158789Sahrens {
1159789Sahrens int error;
11605094Slling nvlist_t *config, *props = NULL;
11617184Stimh nvlist_t *rootprops = NULL;
11627184Stimh nvlist_t *zplprops = NULL;
11634715Sek110237 char *buf;
1164789Sahrens
11655094Slling if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
11669643SEric.Taylor@Sun.COM zc->zc_iflags, &config))
11674988Sek110237 return (error);
11684715Sek110237
11695094Slling if (zc->zc_nvlist_src_size != 0 && (error =
11709643SEric.Taylor@Sun.COM get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
11719643SEric.Taylor@Sun.COM zc->zc_iflags, &props))) {
11725094Slling nvlist_free(config);
11735094Slling return (error);
11745094Slling }
11755094Slling
11767184Stimh if (props) {
11777184Stimh nvlist_t *nvl = NULL;
11787184Stimh uint64_t version = SPA_VERSION;
11797184Stimh
11807184Stimh (void) nvlist_lookup_uint64(props,
11817184Stimh zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
11827184Stimh if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
11837184Stimh error = EINVAL;
11847184Stimh goto pool_props_bad;
11857184Stimh }
11867184Stimh (void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
11877184Stimh if (nvl) {
11887184Stimh error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
11897184Stimh if (error != 0) {
11907184Stimh nvlist_free(config);
11917184Stimh nvlist_free(props);
11927184Stimh return (error);
11937184Stimh }
11947184Stimh (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
11957184Stimh }
11967184Stimh VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11977184Stimh error = zfs_fill_zplprops_root(version, rootprops,
11987184Stimh zplprops, NULL);
11997184Stimh if (error)
12007184Stimh goto pool_props_bad;
12017184Stimh }
12027184Stimh
12034988Sek110237 buf = history_str_get(zc);
1204789Sahrens
12057184Stimh error = spa_create(zc->zc_name, config, props, buf, zplprops);
12067184Stimh
12077184Stimh /*
12087184Stimh * Set the remaining root properties
12097184Stimh */
121011022STom.Erickson@Sun.COM if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
121111022STom.Erickson@Sun.COM ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
12127184Stimh (void) spa_destroy(zc->zc_name);
1213789Sahrens
12144988Sek110237 if (buf != NULL)
12154988Sek110237 history_str_free(buf);
12165094Slling
12177184Stimh pool_props_bad:
12187184Stimh nvlist_free(rootprops);
12197184Stimh nvlist_free(zplprops);
1220789Sahrens nvlist_free(config);
12217184Stimh nvlist_free(props);
12225094Slling
1223789Sahrens return (error);
1224789Sahrens }
1225789Sahrens
1226789Sahrens static int
zfs_ioc_pool_destroy(zfs_cmd_t * zc)1227789Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1228789Sahrens {
12294543Smarks int error;
12304543Smarks zfs_log_history(zc);
12314543Smarks error = spa_destroy(zc->zc_name);
123210588SEric.Taylor@Sun.COM if (error == 0)
123310588SEric.Taylor@Sun.COM zvol_remove_minors(zc->zc_name);
12344543Smarks return (error);
1235789Sahrens }
1236789Sahrens
1237789Sahrens static int
zfs_ioc_pool_import(zfs_cmd_t * zc)1238789Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1239789Sahrens {
12405094Slling nvlist_t *config, *props = NULL;
1241789Sahrens uint64_t guid;
124210921STim.Haley@Sun.COM int error;
1243789Sahrens
12445094Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
12459643SEric.Taylor@Sun.COM zc->zc_iflags, &config)) != 0)
1246789Sahrens return (error);
1247789Sahrens
12485094Slling if (zc->zc_nvlist_src_size != 0 && (error =
12499643SEric.Taylor@Sun.COM get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
12509643SEric.Taylor@Sun.COM zc->zc_iflags, &props))) {
12515094Slling nvlist_free(config);
12525094Slling return (error);
12535094Slling }
12545094Slling
1255789Sahrens if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
12561544Seschrock guid != zc->zc_guid)
1257789Sahrens error = EINVAL;
1258789Sahrens else
125912949SGeorge.Wilson@Sun.COM error = spa_import(zc->zc_name, config, props, zc->zc_cookie);
126012949SGeorge.Wilson@Sun.COM
126112949SGeorge.Wilson@Sun.COM if (zc->zc_nvlist_dst != 0) {
126212949SGeorge.Wilson@Sun.COM int err;
126312949SGeorge.Wilson@Sun.COM
126412949SGeorge.Wilson@Sun.COM if ((err = put_nvlist(zc, config)) != 0)
126512949SGeorge.Wilson@Sun.COM error = err;
126612949SGeorge.Wilson@Sun.COM }
126710921STim.Haley@Sun.COM
1268789Sahrens nvlist_free(config);
1269789Sahrens
12705094Slling if (props)
12715094Slling nvlist_free(props);
12725094Slling
1273789Sahrens return (error);
1274789Sahrens }
1275789Sahrens
1276789Sahrens static int
zfs_ioc_pool_export(zfs_cmd_t * zc)1277789Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1278789Sahrens {
12794543Smarks int error;
12807214Slling boolean_t force = (boolean_t)zc->zc_cookie;
12818211SGeorge.Wilson@Sun.COM boolean_t hardforce = (boolean_t)zc->zc_guid;
12827214Slling
12834543Smarks zfs_log_history(zc);
12848211SGeorge.Wilson@Sun.COM error = spa_export(zc->zc_name, NULL, force, hardforce);
128510588SEric.Taylor@Sun.COM if (error == 0)
128610588SEric.Taylor@Sun.COM zvol_remove_minors(zc->zc_name);
12874543Smarks return (error);
1288789Sahrens }
1289789Sahrens
1290789Sahrens static int
zfs_ioc_pool_configs(zfs_cmd_t * zc)1291789Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1292789Sahrens {
1293789Sahrens nvlist_t *configs;
1294789Sahrens int error;
1295789Sahrens
1296789Sahrens if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1297789Sahrens return (EEXIST);
1298789Sahrens
12992676Seschrock error = put_nvlist(zc, configs);
1300789Sahrens
1301789Sahrens nvlist_free(configs);
1302789Sahrens
1303789Sahrens return (error);
1304789Sahrens }
1305789Sahrens
1306789Sahrens static int
zfs_ioc_pool_stats(zfs_cmd_t * zc)1307789Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1308789Sahrens {
1309789Sahrens nvlist_t *config;
1310789Sahrens int error;
13111544Seschrock int ret = 0;
1312789Sahrens
13132676Seschrock error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
13142676Seschrock sizeof (zc->zc_value));
1315789Sahrens
1316789Sahrens if (config != NULL) {
13172676Seschrock ret = put_nvlist(zc, config);
1318789Sahrens nvlist_free(config);
13191544Seschrock
13201544Seschrock /*
13211544Seschrock * The config may be present even if 'error' is non-zero.
13221544Seschrock * In this case we return success, and preserve the real errno
13231544Seschrock * in 'zc_cookie'.
13241544Seschrock */
13251544Seschrock zc->zc_cookie = error;
1326789Sahrens } else {
13271544Seschrock ret = error;
1328789Sahrens }
1329789Sahrens
13301544Seschrock return (ret);
1331789Sahrens }
1332789Sahrens
1333789Sahrens /*
1334789Sahrens * Try to import the given pool, returning pool stats as appropriate so that
1335789Sahrens * user land knows which devices are available and overall pool health.
1336789Sahrens */
1337789Sahrens static int
zfs_ioc_pool_tryimport(zfs_cmd_t * zc)1338789Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1339789Sahrens {
1340789Sahrens nvlist_t *tryconfig, *config;
1341789Sahrens int error;
1342789Sahrens
13435094Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
13449643SEric.Taylor@Sun.COM zc->zc_iflags, &tryconfig)) != 0)
1345789Sahrens return (error);
1346789Sahrens
1347789Sahrens config = spa_tryimport(tryconfig);
1348789Sahrens
1349789Sahrens nvlist_free(tryconfig);
1350789Sahrens
1351789Sahrens if (config == NULL)
1352789Sahrens return (EINVAL);
1353789Sahrens
13542676Seschrock error = put_nvlist(zc, config);
1355789Sahrens nvlist_free(config);
1356789Sahrens
1357789Sahrens return (error);
1358789Sahrens }
1359789Sahrens
136012296SLin.Ling@Sun.COM /*
136112296SLin.Ling@Sun.COM * inputs:
136212296SLin.Ling@Sun.COM * zc_name name of the pool
136312296SLin.Ling@Sun.COM * zc_cookie scan func (pool_scan_func_t)
136412296SLin.Ling@Sun.COM */
1365789Sahrens static int
zfs_ioc_pool_scan(zfs_cmd_t * zc)136612296SLin.Ling@Sun.COM zfs_ioc_pool_scan(zfs_cmd_t *zc)
1367789Sahrens {
1368789Sahrens spa_t *spa;
1369789Sahrens int error;
1370789Sahrens
13712926Sek110237 if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
13722926Sek110237 return (error);
13732926Sek110237
137412296SLin.Ling@Sun.COM if (zc->zc_cookie == POOL_SCAN_NONE)
137512296SLin.Ling@Sun.COM error = spa_scan_stop(spa);
137612296SLin.Ling@Sun.COM else
137712296SLin.Ling@Sun.COM error = spa_scan(spa, zc->zc_cookie);
13782926Sek110237
13792926Sek110237 spa_close(spa, FTAG);
13802926Sek110237
1381789Sahrens return (error);
1382789Sahrens }
1383789Sahrens
1384789Sahrens static int
zfs_ioc_pool_freeze(zfs_cmd_t * zc)1385789Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1386789Sahrens {
1387789Sahrens spa_t *spa;
1388789Sahrens int error;
1389789Sahrens
1390789Sahrens error = spa_open(zc->zc_name, &spa, FTAG);
1391789Sahrens if (error == 0) {
1392789Sahrens spa_freeze(spa);
1393789Sahrens spa_close(spa, FTAG);
1394789Sahrens }
1395789Sahrens return (error);
1396789Sahrens }
1397789Sahrens
1398789Sahrens static int
zfs_ioc_pool_upgrade(zfs_cmd_t * zc)13991760Seschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
14001760Seschrock {
14011760Seschrock spa_t *spa;
14021760Seschrock int error;
14031760Seschrock
14042926Sek110237 if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
14052926Sek110237 return (error);
14062926Sek110237
14075118Slling if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
14085118Slling spa_close(spa, FTAG);
14095118Slling return (EINVAL);
14105118Slling }
14115118Slling
14125094Slling spa_upgrade(spa, zc->zc_cookie);
14132926Sek110237 spa_close(spa, FTAG);
14142926Sek110237
14152926Sek110237 return (error);
14162926Sek110237 }
14172926Sek110237
14182926Sek110237 static int
zfs_ioc_pool_get_history(zfs_cmd_t * zc)14192926Sek110237 zfs_ioc_pool_get_history(zfs_cmd_t *zc)
14202926Sek110237 {
14212926Sek110237 spa_t *spa;
14222926Sek110237 char *hist_buf;
14232926Sek110237 uint64_t size;
14242926Sek110237 int error;
14252926Sek110237
14262926Sek110237 if ((size = zc->zc_history_len) == 0)
14272926Sek110237 return (EINVAL);
14282926Sek110237
14292926Sek110237 if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
14302926Sek110237 return (error);
14312926Sek110237
14324577Sahrens if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
14333863Sek110237 spa_close(spa, FTAG);
14343863Sek110237 return (ENOTSUP);
14353863Sek110237 }
14363863Sek110237
14372926Sek110237 hist_buf = kmem_alloc(size, KM_SLEEP);
14382926Sek110237 if ((error = spa_history_get(spa, &zc->zc_history_offset,
14392926Sek110237 &zc->zc_history_len, hist_buf)) == 0) {
14409643SEric.Taylor@Sun.COM error = ddi_copyout(hist_buf,
14419643SEric.Taylor@Sun.COM (void *)(uintptr_t)zc->zc_history,
14429643SEric.Taylor@Sun.COM zc->zc_history_len, zc->zc_iflags);
14432926Sek110237 }
14442926Sek110237
14452926Sek110237 spa_close(spa, FTAG);
14462926Sek110237 kmem_free(hist_buf, size);
14472926Sek110237 return (error);
14482926Sek110237 }
14492926Sek110237
14502926Sek110237 static int
zfs_ioc_dsobj_to_dsname(zfs_cmd_t * zc)14513444Sek110237 zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
14523444Sek110237 {
14533444Sek110237 int error;
14543444Sek110237
14553912Slling if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
14563444Sek110237 return (error);
14573444Sek110237
14583444Sek110237 return (0);
14593444Sek110237 }
14603444Sek110237
146110298SMatthew.Ahrens@Sun.COM /*
146210298SMatthew.Ahrens@Sun.COM * inputs:
146310298SMatthew.Ahrens@Sun.COM * zc_name name of filesystem
146410298SMatthew.Ahrens@Sun.COM * zc_obj object to find
146510298SMatthew.Ahrens@Sun.COM *
146610298SMatthew.Ahrens@Sun.COM * outputs:
146710298SMatthew.Ahrens@Sun.COM * zc_value name of object
146810298SMatthew.Ahrens@Sun.COM */
14693444Sek110237 static int
zfs_ioc_obj_to_path(zfs_cmd_t * zc)14703444Sek110237 zfs_ioc_obj_to_path(zfs_cmd_t *zc)
14713444Sek110237 {
147210298SMatthew.Ahrens@Sun.COM objset_t *os;
14733444Sek110237 int error;
14743444Sek110237
147510298SMatthew.Ahrens@Sun.COM /* XXX reading from objset not owned */
147610298SMatthew.Ahrens@Sun.COM if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
14773444Sek110237 return (error);
147810298SMatthew.Ahrens@Sun.COM if (dmu_objset_type(os) != DMU_OST_ZFS) {
147910298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
148010298SMatthew.Ahrens@Sun.COM return (EINVAL);
148110298SMatthew.Ahrens@Sun.COM }
148210298SMatthew.Ahrens@Sun.COM error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
14833444Sek110237 sizeof (zc->zc_value));
148410298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
14853444Sek110237
14863444Sek110237 return (error);
14873444Sek110237 }
14883444Sek110237
148913043STim.Haley@Sun.COM /*
149013043STim.Haley@Sun.COM * inputs:
149113043STim.Haley@Sun.COM * zc_name name of filesystem
149213043STim.Haley@Sun.COM * zc_obj object to find
149313043STim.Haley@Sun.COM *
149413043STim.Haley@Sun.COM * outputs:
149513043STim.Haley@Sun.COM * zc_stat stats on object
149613043STim.Haley@Sun.COM * zc_value path to object
149713043STim.Haley@Sun.COM */
149813043STim.Haley@Sun.COM static int
zfs_ioc_obj_to_stats(zfs_cmd_t * zc)149913043STim.Haley@Sun.COM zfs_ioc_obj_to_stats(zfs_cmd_t *zc)
150013043STim.Haley@Sun.COM {
150113043STim.Haley@Sun.COM objset_t *os;
150213043STim.Haley@Sun.COM int error;
150313043STim.Haley@Sun.COM
150413043STim.Haley@Sun.COM /* XXX reading from objset not owned */
150513043STim.Haley@Sun.COM if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
150613043STim.Haley@Sun.COM return (error);
150713043STim.Haley@Sun.COM if (dmu_objset_type(os) != DMU_OST_ZFS) {
150813043STim.Haley@Sun.COM dmu_objset_rele(os, FTAG);
150913043STim.Haley@Sun.COM return (EINVAL);
151013043STim.Haley@Sun.COM }
151113043STim.Haley@Sun.COM error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value,
151213043STim.Haley@Sun.COM sizeof (zc->zc_value));
151313043STim.Haley@Sun.COM dmu_objset_rele(os, FTAG);
151413043STim.Haley@Sun.COM
151513043STim.Haley@Sun.COM return (error);
151613043STim.Haley@Sun.COM }
151713043STim.Haley@Sun.COM
15183444Sek110237 static int
zfs_ioc_vdev_add(zfs_cmd_t * zc)1519789Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1520789Sahrens {
1521789Sahrens spa_t *spa;
1522789Sahrens int error;
15236423Sgw25295 nvlist_t *config, **l2cache, **spares;
15246423Sgw25295 uint_t nl2cache = 0, nspares = 0;
1525789Sahrens
1526789Sahrens error = spa_open(zc->zc_name, &spa, FTAG);
1527789Sahrens if (error != 0)
1528789Sahrens return (error);
1529789Sahrens
15305450Sbrendan error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15319643SEric.Taylor@Sun.COM zc->zc_iflags, &config);
15325450Sbrendan (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
15335450Sbrendan &l2cache, &nl2cache);
15345450Sbrendan
15356423Sgw25295 (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
15366423Sgw25295 &spares, &nspares);
15376423Sgw25295
15383912Slling /*
15393912Slling * A root pool with concatenated devices is not supported.
15406423Sgw25295 * Thus, can not add a device to a root pool.
15416423Sgw25295 *
15426423Sgw25295 * Intent log device can not be added to a rootpool because
15436423Sgw25295 * during mountroot, zil is replayed, a seperated log device
15446423Sgw25295 * can not be accessed during the mountroot time.
15456423Sgw25295 *
15466423Sgw25295 * l2cache and spare devices are ok to be added to a rootpool.
15473912Slling */
154810922SJeff.Bonwick@Sun.COM if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
154911422SMark.Musante@Sun.COM nvlist_free(config);
15503912Slling spa_close(spa, FTAG);
15513912Slling return (EDOM);
15523912Slling }
15533912Slling
15545450Sbrendan if (error == 0) {
1555789Sahrens error = spa_vdev_add(spa, config);
1556789Sahrens nvlist_free(config);
1557789Sahrens }
1558789Sahrens spa_close(spa, FTAG);
1559789Sahrens return (error);
1560789Sahrens }
1561789Sahrens
156212296SLin.Ling@Sun.COM /*
156312296SLin.Ling@Sun.COM * inputs:
156412296SLin.Ling@Sun.COM * zc_name name of the pool
156512296SLin.Ling@Sun.COM * zc_nvlist_conf nvlist of devices to remove
156612296SLin.Ling@Sun.COM * zc_cookie to stop the remove?
156712296SLin.Ling@Sun.COM */
1568789Sahrens static int
zfs_ioc_vdev_remove(zfs_cmd_t * zc)1569789Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1570789Sahrens {
15712082Seschrock spa_t *spa;
15722082Seschrock int error;
15732082Seschrock
15742082Seschrock error = spa_open(zc->zc_name, &spa, FTAG);
15752082Seschrock if (error != 0)
15762082Seschrock return (error);
15772082Seschrock error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
15782082Seschrock spa_close(spa, FTAG);
15792082Seschrock return (error);
1580789Sahrens }
1581789Sahrens
1582789Sahrens static int
zfs_ioc_vdev_set_state(zfs_cmd_t * zc)15834451Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1584789Sahrens {
1585789Sahrens spa_t *spa;
1586789Sahrens int error;
15874451Seschrock vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1588789Sahrens
15892926Sek110237 if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1590789Sahrens return (error);
15914451Seschrock switch (zc->zc_cookie) {
15924451Seschrock case VDEV_STATE_ONLINE:
15934451Seschrock error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
15944451Seschrock break;
15954451Seschrock
15964451Seschrock case VDEV_STATE_OFFLINE:
15974451Seschrock error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
15984451Seschrock break;
1599789Sahrens
16004451Seschrock case VDEV_STATE_FAULTED:
160110817SEric.Schrock@Sun.COM if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
160210817SEric.Schrock@Sun.COM zc->zc_obj != VDEV_AUX_EXTERNAL)
160310817SEric.Schrock@Sun.COM zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
160410817SEric.Schrock@Sun.COM
160510817SEric.Schrock@Sun.COM error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
16064451Seschrock break;
1607789Sahrens
16084451Seschrock case VDEV_STATE_DEGRADED:
160910817SEric.Schrock@Sun.COM if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
161010817SEric.Schrock@Sun.COM zc->zc_obj != VDEV_AUX_EXTERNAL)
161110817SEric.Schrock@Sun.COM zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
161210817SEric.Schrock@Sun.COM
161310817SEric.Schrock@Sun.COM error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
16144451Seschrock break;
16154451Seschrock
16164451Seschrock default:
16174451Seschrock error = EINVAL;
16184451Seschrock }
16194451Seschrock zc->zc_cookie = newstate;
1620789Sahrens spa_close(spa, FTAG);
1621789Sahrens return (error);
1622789Sahrens }
1623789Sahrens
1624789Sahrens static int
zfs_ioc_vdev_attach(zfs_cmd_t * zc)1625789Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1626789Sahrens {
1627789Sahrens spa_t *spa;
1628789Sahrens int replacing = zc->zc_cookie;
1629789Sahrens nvlist_t *config;
1630789Sahrens int error;
1631789Sahrens
16322926Sek110237 if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1633789Sahrens return (error);
1634789Sahrens
16355094Slling if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
16369643SEric.Taylor@Sun.COM zc->zc_iflags, &config)) == 0) {
16371544Seschrock error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1638789Sahrens nvlist_free(config);
1639789Sahrens }
1640789Sahrens
1641789Sahrens spa_close(spa, FTAG);
1642789Sahrens return (error);
1643789Sahrens }
1644789Sahrens
1645789Sahrens static int
zfs_ioc_vdev_detach(zfs_cmd_t * zc)1646789Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1647789Sahrens {
1648789Sahrens spa_t *spa;
1649789Sahrens int error;
1650789Sahrens
16512926Sek110237 if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1652789Sahrens return (error);
1653789Sahrens
16548241SJeff.Bonwick@Sun.COM error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1655789Sahrens
1656789Sahrens spa_close(spa, FTAG);
1657789Sahrens return (error);
1658789Sahrens }
1659789Sahrens
1660789Sahrens static int
zfs_ioc_vdev_split(zfs_cmd_t * zc)166111422SMark.Musante@Sun.COM zfs_ioc_vdev_split(zfs_cmd_t *zc)
166211422SMark.Musante@Sun.COM {
166311422SMark.Musante@Sun.COM spa_t *spa;
166411422SMark.Musante@Sun.COM nvlist_t *config, *props = NULL;
166511422SMark.Musante@Sun.COM int error;
166611422SMark.Musante@Sun.COM boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
166711422SMark.Musante@Sun.COM
166811422SMark.Musante@Sun.COM if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
166911422SMark.Musante@Sun.COM return (error);
167011422SMark.Musante@Sun.COM
167111422SMark.Musante@Sun.COM if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
167211422SMark.Musante@Sun.COM zc->zc_iflags, &config)) {
167311422SMark.Musante@Sun.COM spa_close(spa, FTAG);
167411422SMark.Musante@Sun.COM return (error);
167511422SMark.Musante@Sun.COM }
167611422SMark.Musante@Sun.COM
167711422SMark.Musante@Sun.COM if (zc->zc_nvlist_src_size != 0 && (error =
167811422SMark.Musante@Sun.COM get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
167911422SMark.Musante@Sun.COM zc->zc_iflags, &props))) {
168011422SMark.Musante@Sun.COM spa_close(spa, FTAG);
168111422SMark.Musante@Sun.COM nvlist_free(config);
168211422SMark.Musante@Sun.COM return (error);
168311422SMark.Musante@Sun.COM }
168411422SMark.Musante@Sun.COM
168511422SMark.Musante@Sun.COM error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
168611422SMark.Musante@Sun.COM
168711422SMark.Musante@Sun.COM spa_close(spa, FTAG);
168811422SMark.Musante@Sun.COM
168911422SMark.Musante@Sun.COM nvlist_free(config);
169011422SMark.Musante@Sun.COM nvlist_free(props);
169111422SMark.Musante@Sun.COM
169211422SMark.Musante@Sun.COM return (error);
169311422SMark.Musante@Sun.COM }
169411422SMark.Musante@Sun.COM
169511422SMark.Musante@Sun.COM static int
zfs_ioc_vdev_setpath(zfs_cmd_t * zc)16961354Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
16971354Seschrock {
16981354Seschrock spa_t *spa;
16992676Seschrock char *path = zc->zc_value;
17001544Seschrock uint64_t guid = zc->zc_guid;
17011354Seschrock int error;
17021354Seschrock
17031354Seschrock error = spa_open(zc->zc_name, &spa, FTAG);
17041354Seschrock if (error != 0)
17051354Seschrock return (error);
17061354Seschrock
17071354Seschrock error = spa_vdev_setpath(spa, guid, path);
17081354Seschrock spa_close(spa, FTAG);
17091354Seschrock return (error);
17101354Seschrock }
17111354Seschrock
17129425SEric.Schrock@Sun.COM static int
zfs_ioc_vdev_setfru(zfs_cmd_t * zc)17139425SEric.Schrock@Sun.COM zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
17149425SEric.Schrock@Sun.COM {
17159425SEric.Schrock@Sun.COM spa_t *spa;
17169425SEric.Schrock@Sun.COM char *fru = zc->zc_value;
17179425SEric.Schrock@Sun.COM uint64_t guid = zc->zc_guid;
17189425SEric.Schrock@Sun.COM int error;
17199425SEric.Schrock@Sun.COM
17209425SEric.Schrock@Sun.COM error = spa_open(zc->zc_name, &spa, FTAG);
17219425SEric.Schrock@Sun.COM if (error != 0)
17229425SEric.Schrock@Sun.COM return (error);
17239425SEric.Schrock@Sun.COM
17249425SEric.Schrock@Sun.COM error = spa_vdev_setfru(spa, guid, fru);
17259425SEric.Schrock@Sun.COM spa_close(spa, FTAG);
17269425SEric.Schrock@Sun.COM return (error);
17279425SEric.Schrock@Sun.COM }
17289425SEric.Schrock@Sun.COM
17291354Seschrock static int
zfs_ioc_objset_stats_impl(zfs_cmd_t * zc,objset_t * os)173012786SChris.Kirby@oracle.com zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
1731789Sahrens {
173212786SChris.Kirby@oracle.com int error = 0;
17331356Seschrock nvlist_t *nv;
1734789Sahrens
17352885Sahrens dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1736789Sahrens
17372856Snd150628 if (zc->zc_nvlist_dst != 0 &&
173811022STom.Erickson@Sun.COM (error = dsl_prop_get_all(os, &nv)) == 0) {
17392885Sahrens dmu_objset_stats(os, nv);
17403087Sahrens /*
17415147Srm160521 * NB: zvol_get_stats() will read the objset contents,
17423087Sahrens * which we aren't supposed to do with a
17436689Smaybee * DS_MODE_USER hold, because it could be
17443087Sahrens * inconsistent. So this is a bit of a workaround...
174510298SMatthew.Ahrens@Sun.COM * XXX reading with out owning
17463087Sahrens */
17474577Sahrens if (!zc->zc_objset_stats.dds_inconsistent) {
17484577Sahrens if (dmu_objset_type(os) == DMU_OST_ZVOL)
17494577Sahrens VERIFY(zvol_get_stats(os, nv) == 0);
17504577Sahrens }
17512676Seschrock error = put_nvlist(zc, nv);
17521356Seschrock nvlist_free(nv);
17531356Seschrock }
1754789Sahrens
175512786SChris.Kirby@oracle.com return (error);
175612786SChris.Kirby@oracle.com }
175712786SChris.Kirby@oracle.com
175812786SChris.Kirby@oracle.com /*
175912786SChris.Kirby@oracle.com * inputs:
176012786SChris.Kirby@oracle.com * zc_name name of filesystem
176112786SChris.Kirby@oracle.com * zc_nvlist_dst_size size of buffer for property nvlist
176212786SChris.Kirby@oracle.com *
176312786SChris.Kirby@oracle.com * outputs:
176412786SChris.Kirby@oracle.com * zc_objset_stats stats
176512786SChris.Kirby@oracle.com * zc_nvlist_dst property nvlist
176612786SChris.Kirby@oracle.com * zc_nvlist_dst_size size of property nvlist
176712786SChris.Kirby@oracle.com */
176812786SChris.Kirby@oracle.com static int
zfs_ioc_objset_stats(zfs_cmd_t * zc)176912786SChris.Kirby@oracle.com zfs_ioc_objset_stats(zfs_cmd_t *zc)
177012786SChris.Kirby@oracle.com {
177112786SChris.Kirby@oracle.com objset_t *os = NULL;
177212786SChris.Kirby@oracle.com int error;
177312786SChris.Kirby@oracle.com
177412786SChris.Kirby@oracle.com if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
177512786SChris.Kirby@oracle.com return (error);
177612786SChris.Kirby@oracle.com
177712786SChris.Kirby@oracle.com error = zfs_ioc_objset_stats_impl(zc, os);
177812786SChris.Kirby@oracle.com
177910298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
178012786SChris.Kirby@oracle.com
1781789Sahrens return (error);
1782789Sahrens }
1783789Sahrens
178411022STom.Erickson@Sun.COM /*
178511022STom.Erickson@Sun.COM * inputs:
178611022STom.Erickson@Sun.COM * zc_name name of filesystem
178711022STom.Erickson@Sun.COM * zc_nvlist_dst_size size of buffer for property nvlist
178811022STom.Erickson@Sun.COM *
178911022STom.Erickson@Sun.COM * outputs:
179011022STom.Erickson@Sun.COM * zc_nvlist_dst received property nvlist
179111022STom.Erickson@Sun.COM * zc_nvlist_dst_size size of received property nvlist
179211022STom.Erickson@Sun.COM *
179311022STom.Erickson@Sun.COM * Gets received properties (distinct from local properties on or after
179411022STom.Erickson@Sun.COM * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
179511022STom.Erickson@Sun.COM * local property values.
179611022STom.Erickson@Sun.COM */
179711022STom.Erickson@Sun.COM static int
zfs_ioc_objset_recvd_props(zfs_cmd_t * zc)179811022STom.Erickson@Sun.COM zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
179911022STom.Erickson@Sun.COM {
180011022STom.Erickson@Sun.COM objset_t *os = NULL;
180111022STom.Erickson@Sun.COM int error;
180211022STom.Erickson@Sun.COM nvlist_t *nv;
180311022STom.Erickson@Sun.COM
180411022STom.Erickson@Sun.COM if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
180511022STom.Erickson@Sun.COM return (error);
180611022STom.Erickson@Sun.COM
180711022STom.Erickson@Sun.COM /*
180811022STom.Erickson@Sun.COM * Without this check, we would return local property values if the
180911022STom.Erickson@Sun.COM * caller has not already received properties on or after
181011022STom.Erickson@Sun.COM * SPA_VERSION_RECVD_PROPS.
181111022STom.Erickson@Sun.COM */
181211022STom.Erickson@Sun.COM if (!dsl_prop_get_hasrecvd(os)) {
181311022STom.Erickson@Sun.COM dmu_objset_rele(os, FTAG);
181411022STom.Erickson@Sun.COM return (ENOTSUP);
181511022STom.Erickson@Sun.COM }
181611022STom.Erickson@Sun.COM
181711022STom.Erickson@Sun.COM if (zc->zc_nvlist_dst != 0 &&
181811022STom.Erickson@Sun.COM (error = dsl_prop_get_received(os, &nv)) == 0) {
181911022STom.Erickson@Sun.COM error = put_nvlist(zc, nv);
182011022STom.Erickson@Sun.COM nvlist_free(nv);
182111022STom.Erickson@Sun.COM }
182211022STom.Erickson@Sun.COM
182311022STom.Erickson@Sun.COM dmu_objset_rele(os, FTAG);
182411022STom.Erickson@Sun.COM return (error);
182511022STom.Erickson@Sun.COM }
182611022STom.Erickson@Sun.COM
18275498Stimh static int
nvl_add_zplprop(objset_t * os,nvlist_t * props,zfs_prop_t prop)18285498Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
18295498Stimh {
18305498Stimh uint64_t value;
18315498Stimh int error;
18325498Stimh
18335498Stimh /*
18345498Stimh * zfs_get_zplprop() will either find a value or give us
18355498Stimh * the default value (if there is one).
18365498Stimh */
18375498Stimh if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
18385498Stimh return (error);
18395498Stimh VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
18405498Stimh return (0);
18415498Stimh }
18425498Stimh
18435498Stimh /*
18445498Stimh * inputs:
18455498Stimh * zc_name name of filesystem
18465498Stimh * zc_nvlist_dst_size size of buffer for zpl property nvlist
18475498Stimh *
18485498Stimh * outputs:
18495498Stimh * zc_nvlist_dst zpl property nvlist
18505498Stimh * zc_nvlist_dst_size size of zpl property nvlist
18515498Stimh */
18525498Stimh static int
zfs_ioc_objset_zplprops(zfs_cmd_t * zc)18535498Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
18545498Stimh {
18555498Stimh objset_t *os;
18565498Stimh int err;
18575498Stimh
185810298SMatthew.Ahrens@Sun.COM /* XXX reading without owning */
185910298SMatthew.Ahrens@Sun.COM if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
18605498Stimh return (err);
18615498Stimh
18625498Stimh dmu_objset_fast_stat(os, &zc->zc_objset_stats);
18635498Stimh
18645498Stimh /*
18655498Stimh * NB: nvl_add_zplprop() will read the objset contents,
18666689Smaybee * which we aren't supposed to do with a DS_MODE_USER
18676689Smaybee * hold, because it could be inconsistent.
18685498Stimh */
18695498Stimh if (zc->zc_nvlist_dst != NULL &&
18705498Stimh !zc->zc_objset_stats.dds_inconsistent &&
18715498Stimh dmu_objset_type(os) == DMU_OST_ZFS) {
18725498Stimh nvlist_t *nv;
18735498Stimh
18745498Stimh VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
18755498Stimh if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
18765498Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
18775498Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
18785498Stimh (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
18795498Stimh err = put_nvlist(zc, nv);
18805498Stimh nvlist_free(nv);
18815498Stimh } else {
18825498Stimh err = ENOENT;
18835498Stimh }
188410298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
18855498Stimh return (err);
18865498Stimh }
18875498Stimh
18889396SMatthew.Ahrens@Sun.COM static boolean_t
dataset_name_hidden(const char * name)18899396SMatthew.Ahrens@Sun.COM dataset_name_hidden(const char *name)
18909396SMatthew.Ahrens@Sun.COM {
18919396SMatthew.Ahrens@Sun.COM /*
18929396SMatthew.Ahrens@Sun.COM * Skip over datasets that are not visible in this zone,
18939396SMatthew.Ahrens@Sun.COM * internal datasets (which have a $ in their name), and
18949396SMatthew.Ahrens@Sun.COM * temporary datasets (which have a % in their name).
18959396SMatthew.Ahrens@Sun.COM */
18969396SMatthew.Ahrens@Sun.COM if (strchr(name, '$') != NULL)
18979396SMatthew.Ahrens@Sun.COM return (B_TRUE);
18989396SMatthew.Ahrens@Sun.COM if (strchr(name, '%') != NULL)
18999396SMatthew.Ahrens@Sun.COM return (B_TRUE);
19009396SMatthew.Ahrens@Sun.COM if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
19019396SMatthew.Ahrens@Sun.COM return (B_TRUE);
19029396SMatthew.Ahrens@Sun.COM return (B_FALSE);
19039396SMatthew.Ahrens@Sun.COM }
19049396SMatthew.Ahrens@Sun.COM
19055367Sahrens /*
19065367Sahrens * inputs:
19075367Sahrens * zc_name name of filesystem
19085367Sahrens * zc_cookie zap cursor
19095367Sahrens * zc_nvlist_dst_size size of buffer for property nvlist
19105367Sahrens *
19115367Sahrens * outputs:
19125367Sahrens * zc_name name of next filesystem
19139396SMatthew.Ahrens@Sun.COM * zc_cookie zap cursor
19145367Sahrens * zc_objset_stats stats
19155367Sahrens * zc_nvlist_dst property nvlist
19165367Sahrens * zc_nvlist_dst_size size of property nvlist
19175367Sahrens */
1918789Sahrens static int
zfs_ioc_dataset_list_next(zfs_cmd_t * zc)1919789Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1920789Sahrens {
1921885Sahrens objset_t *os;
1922789Sahrens int error;
1923789Sahrens char *p;
192411546SChris.Kirby@sun.com size_t orig_len = strlen(zc->zc_name);
192511546SChris.Kirby@sun.com
192611546SChris.Kirby@sun.com top:
192710298SMatthew.Ahrens@Sun.COM if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
1928885Sahrens if (error == ENOENT)
1929885Sahrens error = ESRCH;
1930885Sahrens return (error);
1931789Sahrens }
1932789Sahrens
1933789Sahrens p = strrchr(zc->zc_name, '/');
1934789Sahrens if (p == NULL || p[1] != '\0')
1935789Sahrens (void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1936789Sahrens p = zc->zc_name + strlen(zc->zc_name);
1937789Sahrens
19388697SRichard.Morris@Sun.COM /*
19398697SRichard.Morris@Sun.COM * Pre-fetch the datasets. dmu_objset_prefetch() always returns 0
19408697SRichard.Morris@Sun.COM * but is not declared void because its called by dmu_objset_find().
19418697SRichard.Morris@Sun.COM */
19428415SRichard.Morris@Sun.COM if (zc->zc_cookie == 0) {
19438415SRichard.Morris@Sun.COM uint64_t cookie = 0;
19448415SRichard.Morris@Sun.COM int len = sizeof (zc->zc_name) - (p - zc->zc_name);
19458415SRichard.Morris@Sun.COM
19468415SRichard.Morris@Sun.COM while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
19478697SRichard.Morris@Sun.COM (void) dmu_objset_prefetch(p, NULL);
19488415SRichard.Morris@Sun.COM }
19498415SRichard.Morris@Sun.COM
1950789Sahrens do {
1951885Sahrens error = dmu_dir_list_next(os,
1952885Sahrens sizeof (zc->zc_name) - (p - zc->zc_name), p,
1953885Sahrens NULL, &zc->zc_cookie);
1954789Sahrens if (error == ENOENT)
1955789Sahrens error = ESRCH;
195610588SEric.Taylor@Sun.COM } while (error == 0 && dataset_name_hidden(zc->zc_name) &&
195710588SEric.Taylor@Sun.COM !(zc->zc_iflags & FKIOCTL));
195810298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
1959789Sahrens
196010588SEric.Taylor@Sun.COM /*
196110588SEric.Taylor@Sun.COM * If it's an internal dataset (ie. with a '$' in its name),
196210588SEric.Taylor@Sun.COM * don't try to get stats for it, otherwise we'll return ENOENT.
196310588SEric.Taylor@Sun.COM */
196411546SChris.Kirby@sun.com if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
1965885Sahrens error = zfs_ioc_objset_stats(zc); /* fill in the stats */
196611546SChris.Kirby@sun.com if (error == ENOENT) {
196711546SChris.Kirby@sun.com /* We lost a race with destroy, get the next one. */
196811546SChris.Kirby@sun.com zc->zc_name[orig_len] = '\0';
196911546SChris.Kirby@sun.com goto top;
197011546SChris.Kirby@sun.com }
197111546SChris.Kirby@sun.com }
1972789Sahrens return (error);
1973789Sahrens }
1974789Sahrens
19755367Sahrens /*
19765367Sahrens * inputs:
19775367Sahrens * zc_name name of filesystem
19785367Sahrens * zc_cookie zap cursor
19795367Sahrens * zc_nvlist_dst_size size of buffer for property nvlist
19805367Sahrens *
19815367Sahrens * outputs:
19825367Sahrens * zc_name name of next snapshot
19835367Sahrens * zc_objset_stats stats
19845367Sahrens * zc_nvlist_dst property nvlist
19855367Sahrens * zc_nvlist_dst_size size of property nvlist
19865367Sahrens */
1987789Sahrens static int
zfs_ioc_snapshot_list_next(zfs_cmd_t * zc)1988789Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1989789Sahrens {
1990885Sahrens objset_t *os;
1991789Sahrens int error;
1992789Sahrens
199311546SChris.Kirby@sun.com top:
199410474SRichard.Morris@Sun.COM if (zc->zc_cookie == 0)
199510474SRichard.Morris@Sun.COM (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
199610474SRichard.Morris@Sun.COM NULL, DS_FIND_SNAPSHOTS);
199710474SRichard.Morris@Sun.COM
199810298SMatthew.Ahrens@Sun.COM error = dmu_objset_hold(zc->zc_name, FTAG, &os);
19996689Smaybee if (error)
20006689Smaybee return (error == ENOENT ? ESRCH : error);
2001789Sahrens
20021003Slling /*
20031003Slling * A dataset name of maximum length cannot have any snapshots,
20041003Slling * so exit immediately.
20051003Slling */
20061003Slling if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
200710298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
20081003Slling return (ESRCH);
2009789Sahrens }
2010789Sahrens
2011885Sahrens error = dmu_snapshot_list_next(os,
2012885Sahrens sizeof (zc->zc_name) - strlen(zc->zc_name),
201312786SChris.Kirby@oracle.com zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
201412786SChris.Kirby@oracle.com NULL);
201512786SChris.Kirby@oracle.com
201611546SChris.Kirby@sun.com if (error == 0) {
201712786SChris.Kirby@oracle.com dsl_dataset_t *ds;
201812786SChris.Kirby@oracle.com dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
201912786SChris.Kirby@oracle.com
202012786SChris.Kirby@oracle.com /*
202112786SChris.Kirby@oracle.com * Since we probably don't have a hold on this snapshot,
202212786SChris.Kirby@oracle.com * it's possible that the objsetid could have been destroyed
202312786SChris.Kirby@oracle.com * and reused for a new objset. It's OK if this happens during
202412786SChris.Kirby@oracle.com * a zfs send operation, since the new createtxg will be
202512786SChris.Kirby@oracle.com * beyond the range we're interested in.
202612786SChris.Kirby@oracle.com */
202712786SChris.Kirby@oracle.com rw_enter(&dp->dp_config_rwlock, RW_READER);
202812786SChris.Kirby@oracle.com error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds);
202912786SChris.Kirby@oracle.com rw_exit(&dp->dp_config_rwlock);
203012786SChris.Kirby@oracle.com if (error) {
203112786SChris.Kirby@oracle.com if (error == ENOENT) {
203212786SChris.Kirby@oracle.com /* Racing with destroy, get the next one. */
203312786SChris.Kirby@oracle.com *strchr(zc->zc_name, '@') = '\0';
203412786SChris.Kirby@oracle.com dmu_objset_rele(os, FTAG);
203512786SChris.Kirby@oracle.com goto top;
203612786SChris.Kirby@oracle.com }
203712786SChris.Kirby@oracle.com } else {
203812786SChris.Kirby@oracle.com objset_t *ossnap;
203912786SChris.Kirby@oracle.com
204012786SChris.Kirby@oracle.com error = dmu_objset_from_ds(ds, &ossnap);
204112786SChris.Kirby@oracle.com if (error == 0)
204212786SChris.Kirby@oracle.com error = zfs_ioc_objset_stats_impl(zc, ossnap);
204312786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
204411546SChris.Kirby@sun.com }
204511546SChris.Kirby@sun.com } else if (error == ENOENT) {
20466689Smaybee error = ESRCH;
204711546SChris.Kirby@sun.com }
2048789Sahrens
204912786SChris.Kirby@oracle.com dmu_objset_rele(os, FTAG);
20505367Sahrens /* if we failed, undo the @ that we tacked on to zc_name */
20516689Smaybee if (error)
20525367Sahrens *strchr(zc->zc_name, '@') = '\0';
2053789Sahrens return (error);
2054789Sahrens }
2055789Sahrens
205611022STom.Erickson@Sun.COM static int
zfs_prop_set_userquota(const char * dsname,nvpair_t * pair)205711022STom.Erickson@Sun.COM zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
205811022STom.Erickson@Sun.COM {
205911022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
206011022STom.Erickson@Sun.COM uint64_t *valary;
206111022STom.Erickson@Sun.COM unsigned int vallen;
206211022STom.Erickson@Sun.COM const char *domain;
206311933STim.Haley@Sun.COM char *dash;
206411022STom.Erickson@Sun.COM zfs_userquota_prop_t type;
206511022STom.Erickson@Sun.COM uint64_t rid;
206611022STom.Erickson@Sun.COM uint64_t quota;
206711022STom.Erickson@Sun.COM zfsvfs_t *zfsvfs;
206811022STom.Erickson@Sun.COM int err;
206911022STom.Erickson@Sun.COM
207011022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
207111022STom.Erickson@Sun.COM nvlist_t *attrs;
207211022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
207311933STim.Haley@Sun.COM if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
207411933STim.Haley@Sun.COM &pair) != 0)
207511933STim.Haley@Sun.COM return (EINVAL);
207611022STom.Erickson@Sun.COM }
207711022STom.Erickson@Sun.COM
207811933STim.Haley@Sun.COM /*
207911933STim.Haley@Sun.COM * A correctly constructed propname is encoded as
208011933STim.Haley@Sun.COM * userquota@<rid>-<domain>.
208111933STim.Haley@Sun.COM */
208211933STim.Haley@Sun.COM if ((dash = strchr(propname, '-')) == NULL ||
208311933STim.Haley@Sun.COM nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
208411933STim.Haley@Sun.COM vallen != 3)
208511933STim.Haley@Sun.COM return (EINVAL);
208611933STim.Haley@Sun.COM
208711933STim.Haley@Sun.COM domain = dash + 1;
208811022STom.Erickson@Sun.COM type = valary[0];
208911022STom.Erickson@Sun.COM rid = valary[1];
209011022STom.Erickson@Sun.COM quota = valary[2];
209111022STom.Erickson@Sun.COM
209212620SMark.Shellenbaum@Oracle.COM err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE);
209311022STom.Erickson@Sun.COM if (err == 0) {
209411022STom.Erickson@Sun.COM err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
209511022STom.Erickson@Sun.COM zfsvfs_rele(zfsvfs, FTAG);
209611022STom.Erickson@Sun.COM }
209711022STom.Erickson@Sun.COM
209811022STom.Erickson@Sun.COM return (err);
209911022STom.Erickson@Sun.COM }
210011022STom.Erickson@Sun.COM
210111022STom.Erickson@Sun.COM /*
210211022STom.Erickson@Sun.COM * If the named property is one that has a special function to set its value,
210311022STom.Erickson@Sun.COM * return 0 on success and a positive error code on failure; otherwise if it is
210411022STom.Erickson@Sun.COM * not one of the special properties handled by this function, return -1.
210511022STom.Erickson@Sun.COM *
210611933STim.Haley@Sun.COM * XXX: It would be better for callers of the property interface if we handled
210711022STom.Erickson@Sun.COM * these special cases in dsl_prop.c (in the dsl layer).
210811022STom.Erickson@Sun.COM */
210911022STom.Erickson@Sun.COM static int
zfs_prop_set_special(const char * dsname,zprop_source_t source,nvpair_t * pair)211011022STom.Erickson@Sun.COM zfs_prop_set_special(const char *dsname, zprop_source_t source,
211111022STom.Erickson@Sun.COM nvpair_t *pair)
2112789Sahrens {
211311022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
211411022STom.Erickson@Sun.COM zfs_prop_t prop = zfs_name_to_prop(propname);
211511022STom.Erickson@Sun.COM uint64_t intval;
211611022STom.Erickson@Sun.COM int err;
211711022STom.Erickson@Sun.COM
211811022STom.Erickson@Sun.COM if (prop == ZPROP_INVAL) {
211911022STom.Erickson@Sun.COM if (zfs_prop_userquota(propname))
212011022STom.Erickson@Sun.COM return (zfs_prop_set_userquota(dsname, pair));
212111022STom.Erickson@Sun.COM return (-1);
212211022STom.Erickson@Sun.COM }
212311022STom.Erickson@Sun.COM
212411022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
212511022STom.Erickson@Sun.COM nvlist_t *attrs;
212611022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
212711022STom.Erickson@Sun.COM VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
212811022STom.Erickson@Sun.COM &pair) == 0);
212911022STom.Erickson@Sun.COM }
213011022STom.Erickson@Sun.COM
213111022STom.Erickson@Sun.COM if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
213211022STom.Erickson@Sun.COM return (-1);
213311022STom.Erickson@Sun.COM
213411022STom.Erickson@Sun.COM VERIFY(0 == nvpair_value_uint64(pair, &intval));
213511022STom.Erickson@Sun.COM
213611022STom.Erickson@Sun.COM switch (prop) {
213711022STom.Erickson@Sun.COM case ZFS_PROP_QUOTA:
213811022STom.Erickson@Sun.COM err = dsl_dir_set_quota(dsname, source, intval);
213911022STom.Erickson@Sun.COM break;
214011022STom.Erickson@Sun.COM case ZFS_PROP_REFQUOTA:
214111022STom.Erickson@Sun.COM err = dsl_dataset_set_quota(dsname, source, intval);
214211022STom.Erickson@Sun.COM break;
214311022STom.Erickson@Sun.COM case ZFS_PROP_RESERVATION:
214411022STom.Erickson@Sun.COM err = dsl_dir_set_reservation(dsname, source, intval);
214511022STom.Erickson@Sun.COM break;
214611022STom.Erickson@Sun.COM case ZFS_PROP_REFRESERVATION:
214711022STom.Erickson@Sun.COM err = dsl_dataset_set_reservation(dsname, source, intval);
214811022STom.Erickson@Sun.COM break;
214911022STom.Erickson@Sun.COM case ZFS_PROP_VOLSIZE:
215011022STom.Erickson@Sun.COM err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
215111022STom.Erickson@Sun.COM intval);
215211022STom.Erickson@Sun.COM break;
215311022STom.Erickson@Sun.COM case ZFS_PROP_VERSION:
215411022STom.Erickson@Sun.COM {
215511022STom.Erickson@Sun.COM zfsvfs_t *zfsvfs;
215611022STom.Erickson@Sun.COM
215712620SMark.Shellenbaum@Oracle.COM if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0)
215811022STom.Erickson@Sun.COM break;
215911022STom.Erickson@Sun.COM
216011022STom.Erickson@Sun.COM err = zfs_set_version(zfsvfs, intval);
216111022STom.Erickson@Sun.COM zfsvfs_rele(zfsvfs, FTAG);
216211022STom.Erickson@Sun.COM
216311022STom.Erickson@Sun.COM if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
216411147SGeorge.Wilson@Sun.COM zfs_cmd_t *zc;
216511147SGeorge.Wilson@Sun.COM
216611147SGeorge.Wilson@Sun.COM zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
216711147SGeorge.Wilson@Sun.COM (void) strcpy(zc->zc_name, dsname);
216811147SGeorge.Wilson@Sun.COM (void) zfs_ioc_userspace_upgrade(zc);
216911147SGeorge.Wilson@Sun.COM kmem_free(zc, sizeof (zfs_cmd_t));
217011022STom.Erickson@Sun.COM }
217111022STom.Erickson@Sun.COM break;
217211022STom.Erickson@Sun.COM }
217311022STom.Erickson@Sun.COM
217411022STom.Erickson@Sun.COM default:
217511022STom.Erickson@Sun.COM err = -1;
217611022STom.Erickson@Sun.COM }
217711022STom.Erickson@Sun.COM
217811022STom.Erickson@Sun.COM return (err);
217911022STom.Erickson@Sun.COM }
218011022STom.Erickson@Sun.COM
218111022STom.Erickson@Sun.COM /*
218211022STom.Erickson@Sun.COM * This function is best effort. If it fails to set any of the given properties,
218311022STom.Erickson@Sun.COM * it continues to set as many as it can and returns the first error
218411022STom.Erickson@Sun.COM * encountered. If the caller provides a non-NULL errlist, it also gives the
218511022STom.Erickson@Sun.COM * complete list of names of all the properties it failed to set along with the
218611022STom.Erickson@Sun.COM * corresponding error numbers. The caller is responsible for freeing the
218711022STom.Erickson@Sun.COM * returned errlist.
218811022STom.Erickson@Sun.COM *
218911022STom.Erickson@Sun.COM * If every property is set successfully, zero is returned and the list pointed
219011022STom.Erickson@Sun.COM * at by errlist is NULL.
219111022STom.Erickson@Sun.COM */
219211022STom.Erickson@Sun.COM int
zfs_set_prop_nvlist(const char * dsname,zprop_source_t source,nvlist_t * nvl,nvlist_t ** errlist)219311022STom.Erickson@Sun.COM zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
219411022STom.Erickson@Sun.COM nvlist_t **errlist)
219511022STom.Erickson@Sun.COM {
219611022STom.Erickson@Sun.COM nvpair_t *pair;
219711022STom.Erickson@Sun.COM nvpair_t *propval;
219811045STom.Erickson@Sun.COM int rv = 0;
21992676Seschrock uint64_t intval;
22002676Seschrock char *strval;
22018697SRichard.Morris@Sun.COM nvlist_t *genericnvl;
220211022STom.Erickson@Sun.COM nvlist_t *errors;
220311022STom.Erickson@Sun.COM nvlist_t *retrynvl;
22044543Smarks
22058697SRichard.Morris@Sun.COM VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220611022STom.Erickson@Sun.COM VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220711022STom.Erickson@Sun.COM VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220811022STom.Erickson@Sun.COM
220911022STom.Erickson@Sun.COM retry:
221011022STom.Erickson@Sun.COM pair = NULL;
221111022STom.Erickson@Sun.COM while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
221211022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
22134670Sahrens zfs_prop_t prop = zfs_name_to_prop(propname);
221411181STom.Erickson@Sun.COM int err = 0;
22154543Smarks
221611022STom.Erickson@Sun.COM /* decode the property value */
221711022STom.Erickson@Sun.COM propval = pair;
221811022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
221911022STom.Erickson@Sun.COM nvlist_t *attrs;
222011022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
222111933STim.Haley@Sun.COM if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
222211933STim.Haley@Sun.COM &propval) != 0)
222311933STim.Haley@Sun.COM err = EINVAL;
22244543Smarks }
22252676Seschrock
222611022STom.Erickson@Sun.COM /* Validate value type */
222711933STim.Haley@Sun.COM if (err == 0 && prop == ZPROP_INVAL) {
222811022STom.Erickson@Sun.COM if (zfs_prop_user(propname)) {
222911022STom.Erickson@Sun.COM if (nvpair_type(propval) != DATA_TYPE_STRING)
223011022STom.Erickson@Sun.COM err = EINVAL;
223111022STom.Erickson@Sun.COM } else if (zfs_prop_userquota(propname)) {
223211022STom.Erickson@Sun.COM if (nvpair_type(propval) !=
223311022STom.Erickson@Sun.COM DATA_TYPE_UINT64_ARRAY)
223411022STom.Erickson@Sun.COM err = EINVAL;
22359396SMatthew.Ahrens@Sun.COM }
223611933STim.Haley@Sun.COM } else if (err == 0) {
223711022STom.Erickson@Sun.COM if (nvpair_type(propval) == DATA_TYPE_STRING) {
223811022STom.Erickson@Sun.COM if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
223911022STom.Erickson@Sun.COM err = EINVAL;
224011022STom.Erickson@Sun.COM } else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
22412885Sahrens const char *unused;
22422885Sahrens
224311022STom.Erickson@Sun.COM VERIFY(nvpair_value_uint64(propval,
224411022STom.Erickson@Sun.COM &intval) == 0);
22452676Seschrock
22462676Seschrock switch (zfs_prop_get_type(prop)) {
22474787Sahrens case PROP_TYPE_NUMBER:
22482676Seschrock break;
22494787Sahrens case PROP_TYPE_STRING:
225011022STom.Erickson@Sun.COM err = EINVAL;
225111022STom.Erickson@Sun.COM break;
22524787Sahrens case PROP_TYPE_INDEX:
22532717Seschrock if (zfs_prop_index_to_string(prop,
225411022STom.Erickson@Sun.COM intval, &unused) != 0)
225511022STom.Erickson@Sun.COM err = EINVAL;
22562676Seschrock break;
22572676Seschrock default:
22584577Sahrens cmn_err(CE_PANIC,
22594577Sahrens "unknown property type");
22602676Seschrock }
22612676Seschrock } else {
226211022STom.Erickson@Sun.COM err = EINVAL;
226311022STom.Erickson@Sun.COM }
226411022STom.Erickson@Sun.COM }
226511022STom.Erickson@Sun.COM
226611022STom.Erickson@Sun.COM /* Validate permissions */
226711022STom.Erickson@Sun.COM if (err == 0)
226811022STom.Erickson@Sun.COM err = zfs_check_settable(dsname, pair, CRED());
226911022STom.Erickson@Sun.COM
227011022STom.Erickson@Sun.COM if (err == 0) {
227111022STom.Erickson@Sun.COM err = zfs_prop_set_special(dsname, source, pair);
227211022STom.Erickson@Sun.COM if (err == -1) {
227311022STom.Erickson@Sun.COM /*
227411022STom.Erickson@Sun.COM * For better performance we build up a list of
227511022STom.Erickson@Sun.COM * properties to set in a single transaction.
227611022STom.Erickson@Sun.COM */
227711022STom.Erickson@Sun.COM err = nvlist_add_nvpair(genericnvl, pair);
227811022STom.Erickson@Sun.COM } else if (err != 0 && nvl != retrynvl) {
227911022STom.Erickson@Sun.COM /*
228011022STom.Erickson@Sun.COM * This may be a spurious error caused by
228111022STom.Erickson@Sun.COM * receiving quota and reservation out of order.
228211022STom.Erickson@Sun.COM * Try again in a second pass.
228311022STom.Erickson@Sun.COM */
228411022STom.Erickson@Sun.COM err = nvlist_add_nvpair(retrynvl, pair);
22852676Seschrock }
228611022STom.Erickson@Sun.COM }
228711022STom.Erickson@Sun.COM
228811022STom.Erickson@Sun.COM if (err != 0)
228911022STom.Erickson@Sun.COM VERIFY(nvlist_add_int32(errors, propname, err) == 0);
229011022STom.Erickson@Sun.COM }
229111022STom.Erickson@Sun.COM
229211022STom.Erickson@Sun.COM if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
229311022STom.Erickson@Sun.COM nvl = retrynvl;
229411022STom.Erickson@Sun.COM goto retry;
229511022STom.Erickson@Sun.COM }
229611022STom.Erickson@Sun.COM
229711022STom.Erickson@Sun.COM if (!nvlist_empty(genericnvl) &&
229811022STom.Erickson@Sun.COM dsl_props_set(dsname, source, genericnvl) != 0) {
229911022STom.Erickson@Sun.COM /*
230011022STom.Erickson@Sun.COM * If this fails, we still want to set as many properties as we
230111022STom.Erickson@Sun.COM * can, so try setting them individually.
230211022STom.Erickson@Sun.COM */
230311022STom.Erickson@Sun.COM pair = NULL;
230411022STom.Erickson@Sun.COM while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
230511022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
230611181STom.Erickson@Sun.COM int err = 0;
230711022STom.Erickson@Sun.COM
230811022STom.Erickson@Sun.COM propval = pair;
230911022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
231011022STom.Erickson@Sun.COM nvlist_t *attrs;
231111022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
231211022STom.Erickson@Sun.COM VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
231311022STom.Erickson@Sun.COM &propval) == 0);
231411022STom.Erickson@Sun.COM }
231511022STom.Erickson@Sun.COM
231611022STom.Erickson@Sun.COM if (nvpair_type(propval) == DATA_TYPE_STRING) {
231711022STom.Erickson@Sun.COM VERIFY(nvpair_value_string(propval,
231811022STom.Erickson@Sun.COM &strval) == 0);
231911022STom.Erickson@Sun.COM err = dsl_prop_set(dsname, propname, source, 1,
232011022STom.Erickson@Sun.COM strlen(strval) + 1, strval);
232111022STom.Erickson@Sun.COM } else {
232211022STom.Erickson@Sun.COM VERIFY(nvpair_value_uint64(propval,
232311022STom.Erickson@Sun.COM &intval) == 0);
232411022STom.Erickson@Sun.COM err = dsl_prop_set(dsname, propname, source, 8,
232511022STom.Erickson@Sun.COM 1, &intval);
232611022STom.Erickson@Sun.COM }
232711022STom.Erickson@Sun.COM
232811022STom.Erickson@Sun.COM if (err != 0) {
232911022STom.Erickson@Sun.COM VERIFY(nvlist_add_int32(errors, propname,
233011022STom.Erickson@Sun.COM err) == 0);
233111022STom.Erickson@Sun.COM }
23322676Seschrock }
23332676Seschrock }
233411022STom.Erickson@Sun.COM nvlist_free(genericnvl);
233511022STom.Erickson@Sun.COM nvlist_free(retrynvl);
233611022STom.Erickson@Sun.COM
233711022STom.Erickson@Sun.COM if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
233811022STom.Erickson@Sun.COM nvlist_free(errors);
233911022STom.Erickson@Sun.COM errors = NULL;
234011022STom.Erickson@Sun.COM } else {
234111022STom.Erickson@Sun.COM VERIFY(nvpair_value_int32(pair, &rv) == 0);
23428697SRichard.Morris@Sun.COM }
234311022STom.Erickson@Sun.COM
234411022STom.Erickson@Sun.COM if (errlist == NULL)
234511022STom.Erickson@Sun.COM nvlist_free(errors);
234611022STom.Erickson@Sun.COM else
234711022STom.Erickson@Sun.COM *errlist = errors;
234811022STom.Erickson@Sun.COM
234911022STom.Erickson@Sun.COM return (rv);
2350789Sahrens }
2351789Sahrens
23525367Sahrens /*
23539355SMatthew.Ahrens@Sun.COM * Check that all the properties are valid user properties.
23549355SMatthew.Ahrens@Sun.COM */
23559355SMatthew.Ahrens@Sun.COM static int
zfs_check_userprops(char * fsname,nvlist_t * nvl)23569355SMatthew.Ahrens@Sun.COM zfs_check_userprops(char *fsname, nvlist_t *nvl)
23579355SMatthew.Ahrens@Sun.COM {
235811022STom.Erickson@Sun.COM nvpair_t *pair = NULL;
23599355SMatthew.Ahrens@Sun.COM int error = 0;
23609355SMatthew.Ahrens@Sun.COM
236111022STom.Erickson@Sun.COM while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
236211022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
23639355SMatthew.Ahrens@Sun.COM char *valstr;
23649355SMatthew.Ahrens@Sun.COM
23659355SMatthew.Ahrens@Sun.COM if (!zfs_prop_user(propname) ||
236611022STom.Erickson@Sun.COM nvpair_type(pair) != DATA_TYPE_STRING)
23679355SMatthew.Ahrens@Sun.COM return (EINVAL);
23689355SMatthew.Ahrens@Sun.COM
23699355SMatthew.Ahrens@Sun.COM if (error = zfs_secpolicy_write_perms(fsname,
23709355SMatthew.Ahrens@Sun.COM ZFS_DELEG_PERM_USERPROP, CRED()))
23719355SMatthew.Ahrens@Sun.COM return (error);
23729355SMatthew.Ahrens@Sun.COM
23739355SMatthew.Ahrens@Sun.COM if (strlen(propname) >= ZAP_MAXNAMELEN)
23749355SMatthew.Ahrens@Sun.COM return (ENAMETOOLONG);
23759355SMatthew.Ahrens@Sun.COM
237611022STom.Erickson@Sun.COM VERIFY(nvpair_value_string(pair, &valstr) == 0);
23779355SMatthew.Ahrens@Sun.COM if (strlen(valstr) >= ZAP_MAXVALUELEN)
23789355SMatthew.Ahrens@Sun.COM return (E2BIG);
23799355SMatthew.Ahrens@Sun.COM }
23809355SMatthew.Ahrens@Sun.COM return (0);
23819355SMatthew.Ahrens@Sun.COM }
23829355SMatthew.Ahrens@Sun.COM
238311022STom.Erickson@Sun.COM static void
props_skip(nvlist_t * props,nvlist_t * skipped,nvlist_t ** newprops)238411022STom.Erickson@Sun.COM props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
238511022STom.Erickson@Sun.COM {
238611022STom.Erickson@Sun.COM nvpair_t *pair;
238711022STom.Erickson@Sun.COM
238811022STom.Erickson@Sun.COM VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
238911022STom.Erickson@Sun.COM
239011022STom.Erickson@Sun.COM pair = NULL;
239111022STom.Erickson@Sun.COM while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
239211022STom.Erickson@Sun.COM if (nvlist_exists(skipped, nvpair_name(pair)))
239311022STom.Erickson@Sun.COM continue;
239411022STom.Erickson@Sun.COM
239511022STom.Erickson@Sun.COM VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
239611022STom.Erickson@Sun.COM }
239711022STom.Erickson@Sun.COM }
239811022STom.Erickson@Sun.COM
239911022STom.Erickson@Sun.COM static int
clear_received_props(objset_t * os,const char * fs,nvlist_t * props,nvlist_t * skipped)240011022STom.Erickson@Sun.COM clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
240111022STom.Erickson@Sun.COM nvlist_t *skipped)
240211022STom.Erickson@Sun.COM {
240311022STom.Erickson@Sun.COM int err = 0;
240411022STom.Erickson@Sun.COM nvlist_t *cleared_props = NULL;
240511022STom.Erickson@Sun.COM props_skip(props, skipped, &cleared_props);
240611022STom.Erickson@Sun.COM if (!nvlist_empty(cleared_props)) {
240711022STom.Erickson@Sun.COM /*
240811022STom.Erickson@Sun.COM * Acts on local properties until the dataset has received
240911022STom.Erickson@Sun.COM * properties at least once on or after SPA_VERSION_RECVD_PROPS.
241011022STom.Erickson@Sun.COM */
241111022STom.Erickson@Sun.COM zprop_source_t flags = (ZPROP_SRC_NONE |
241211022STom.Erickson@Sun.COM (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
241311022STom.Erickson@Sun.COM err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
241411022STom.Erickson@Sun.COM }
241511022STom.Erickson@Sun.COM nvlist_free(cleared_props);
241611022STom.Erickson@Sun.COM return (err);
241711022STom.Erickson@Sun.COM }
241811022STom.Erickson@Sun.COM
24199355SMatthew.Ahrens@Sun.COM /*
24205367Sahrens * inputs:
24215367Sahrens * zc_name name of filesystem
24228697SRichard.Morris@Sun.COM * zc_value name of property to set
24235367Sahrens * zc_nvlist_src{_size} nvlist of properties to apply
242411022STom.Erickson@Sun.COM * zc_cookie received properties flag
24255367Sahrens *
242611022STom.Erickson@Sun.COM * outputs:
242711022STom.Erickson@Sun.COM * zc_nvlist_dst{_size} error for each unapplied received property
24285367Sahrens */
2429789Sahrens static int
zfs_ioc_set_prop(zfs_cmd_t * zc)24302676Seschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2431789Sahrens {
24322676Seschrock nvlist_t *nvl;
243311022STom.Erickson@Sun.COM boolean_t received = zc->zc_cookie;
243411022STom.Erickson@Sun.COM zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
243511022STom.Erickson@Sun.COM ZPROP_SRC_LOCAL);
243611022STom.Erickson@Sun.COM nvlist_t *errors = NULL;
24372676Seschrock int error;
2438789Sahrens
24395094Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
24409643SEric.Taylor@Sun.COM zc->zc_iflags, &nvl)) != 0)
24412676Seschrock return (error);
24422676Seschrock
244311022STom.Erickson@Sun.COM if (received) {
24447265Sahrens nvlist_t *origprops;
24457265Sahrens objset_t *os;
24467265Sahrens
244710298SMatthew.Ahrens@Sun.COM if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
244811022STom.Erickson@Sun.COM if (dsl_prop_get_received(os, &origprops) == 0) {
244911022STom.Erickson@Sun.COM (void) clear_received_props(os,
245011022STom.Erickson@Sun.COM zc->zc_name, origprops, nvl);
24517265Sahrens nvlist_free(origprops);
24527265Sahrens }
245311022STom.Erickson@Sun.COM
245411022STom.Erickson@Sun.COM dsl_prop_set_hasrecvd(os);
245510298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
24567265Sahrens }
24577265Sahrens }
24587265Sahrens
245911022STom.Erickson@Sun.COM error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
246011022STom.Erickson@Sun.COM
246111022STom.Erickson@Sun.COM if (zc->zc_nvlist_dst != NULL && errors != NULL) {
246211022STom.Erickson@Sun.COM (void) put_nvlist(zc, errors);
246311022STom.Erickson@Sun.COM }
246411022STom.Erickson@Sun.COM
246511022STom.Erickson@Sun.COM nvlist_free(errors);
24662676Seschrock nvlist_free(nvl);
24672676Seschrock return (error);
2468789Sahrens }
2469789Sahrens
24705367Sahrens /*
24715367Sahrens * inputs:
24725367Sahrens * zc_name name of filesystem
24735367Sahrens * zc_value name of property to inherit
247411022STom.Erickson@Sun.COM * zc_cookie revert to received value if TRUE
24755367Sahrens *
24765367Sahrens * outputs: none
24775367Sahrens */
2478789Sahrens static int
zfs_ioc_inherit_prop(zfs_cmd_t * zc)24794849Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
24804849Sahrens {
248111022STom.Erickson@Sun.COM const char *propname = zc->zc_value;
248211022STom.Erickson@Sun.COM zfs_prop_t prop = zfs_name_to_prop(propname);
248311022STom.Erickson@Sun.COM boolean_t received = zc->zc_cookie;
248411022STom.Erickson@Sun.COM zprop_source_t source = (received
248511022STom.Erickson@Sun.COM ? ZPROP_SRC_NONE /* revert to received value, if any */
248611022STom.Erickson@Sun.COM : ZPROP_SRC_INHERITED); /* explicitly inherit */
248711022STom.Erickson@Sun.COM
248811022STom.Erickson@Sun.COM if (received) {
248911022STom.Erickson@Sun.COM nvlist_t *dummy;
249011022STom.Erickson@Sun.COM nvpair_t *pair;
249111022STom.Erickson@Sun.COM zprop_type_t type;
249211022STom.Erickson@Sun.COM int err;
249311022STom.Erickson@Sun.COM
249411022STom.Erickson@Sun.COM /*
249511022STom.Erickson@Sun.COM * zfs_prop_set_special() expects properties in the form of an
249611022STom.Erickson@Sun.COM * nvpair with type info.
249711022STom.Erickson@Sun.COM */
249811022STom.Erickson@Sun.COM if (prop == ZPROP_INVAL) {
249911022STom.Erickson@Sun.COM if (!zfs_prop_user(propname))
250011022STom.Erickson@Sun.COM return (EINVAL);
250111022STom.Erickson@Sun.COM
250211022STom.Erickson@Sun.COM type = PROP_TYPE_STRING;
250311515STom.Erickson@Sun.COM } else if (prop == ZFS_PROP_VOLSIZE ||
250411515STom.Erickson@Sun.COM prop == ZFS_PROP_VERSION) {
250511515STom.Erickson@Sun.COM return (EINVAL);
250611022STom.Erickson@Sun.COM } else {
250711022STom.Erickson@Sun.COM type = zfs_prop_get_type(prop);
250811022STom.Erickson@Sun.COM }
250911022STom.Erickson@Sun.COM
251011022STom.Erickson@Sun.COM VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
251111022STom.Erickson@Sun.COM
251211022STom.Erickson@Sun.COM switch (type) {
251311022STom.Erickson@Sun.COM case PROP_TYPE_STRING:
251411022STom.Erickson@Sun.COM VERIFY(0 == nvlist_add_string(dummy, propname, ""));
251511022STom.Erickson@Sun.COM break;
251611022STom.Erickson@Sun.COM case PROP_TYPE_NUMBER:
251711022STom.Erickson@Sun.COM case PROP_TYPE_INDEX:
251811022STom.Erickson@Sun.COM VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
251911022STom.Erickson@Sun.COM break;
252011022STom.Erickson@Sun.COM default:
252111022STom.Erickson@Sun.COM nvlist_free(dummy);
252211022STom.Erickson@Sun.COM return (EINVAL);
252311022STom.Erickson@Sun.COM }
252411022STom.Erickson@Sun.COM
252511022STom.Erickson@Sun.COM pair = nvlist_next_nvpair(dummy, NULL);
252611022STom.Erickson@Sun.COM err = zfs_prop_set_special(zc->zc_name, source, pair);
252711022STom.Erickson@Sun.COM nvlist_free(dummy);
252811022STom.Erickson@Sun.COM if (err != -1)
252911022STom.Erickson@Sun.COM return (err); /* special property already handled */
253011022STom.Erickson@Sun.COM } else {
253111022STom.Erickson@Sun.COM /*
253211022STom.Erickson@Sun.COM * Only check this in the non-received case. We want to allow
253311022STom.Erickson@Sun.COM * 'inherit -S' to revert non-inheritable properties like quota
253411022STom.Erickson@Sun.COM * and reservation to the received or default values even though
253511022STom.Erickson@Sun.COM * they are not considered inheritable.
253611022STom.Erickson@Sun.COM */
253711022STom.Erickson@Sun.COM if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
253811022STom.Erickson@Sun.COM return (EINVAL);
253911022STom.Erickson@Sun.COM }
254011022STom.Erickson@Sun.COM
25414849Sahrens /* the property name has been validated by zfs_secpolicy_inherit() */
254211022STom.Erickson@Sun.COM return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
25434849Sahrens }
25444849Sahrens
25454849Sahrens static int
zfs_ioc_pool_set_props(zfs_cmd_t * zc)25464098Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
25473912Slling {
25485094Slling nvlist_t *props;
25493912Slling spa_t *spa;
25505094Slling int error;
255111022STom.Erickson@Sun.COM nvpair_t *pair;
255211022STom.Erickson@Sun.COM
255311022STom.Erickson@Sun.COM if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
255411022STom.Erickson@Sun.COM zc->zc_iflags, &props))
25553912Slling return (error);
25563912Slling
25578525SEric.Schrock@Sun.COM /*
25588525SEric.Schrock@Sun.COM * If the only property is the configfile, then just do a spa_lookup()
25598525SEric.Schrock@Sun.COM * to handle the faulted case.
25608525SEric.Schrock@Sun.COM */
256111022STom.Erickson@Sun.COM pair = nvlist_next_nvpair(props, NULL);
256211022STom.Erickson@Sun.COM if (pair != NULL && strcmp(nvpair_name(pair),
25638525SEric.Schrock@Sun.COM zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
256411022STom.Erickson@Sun.COM nvlist_next_nvpair(props, pair) == NULL) {
25658525SEric.Schrock@Sun.COM mutex_enter(&spa_namespace_lock);
25668525SEric.Schrock@Sun.COM if ((spa = spa_lookup(zc->zc_name)) != NULL) {
25678525SEric.Schrock@Sun.COM spa_configfile_set(spa, props, B_FALSE);
25688525SEric.Schrock@Sun.COM spa_config_sync(spa, B_FALSE, B_TRUE);
25698525SEric.Schrock@Sun.COM }
25708525SEric.Schrock@Sun.COM mutex_exit(&spa_namespace_lock);
257110672SEric.Schrock@Sun.COM if (spa != NULL) {
257210672SEric.Schrock@Sun.COM nvlist_free(props);
25738525SEric.Schrock@Sun.COM return (0);
257410672SEric.Schrock@Sun.COM }
25758525SEric.Schrock@Sun.COM }
25768525SEric.Schrock@Sun.COM
25773912Slling if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
25785094Slling nvlist_free(props);
25793912Slling return (error);
25803912Slling }
25813912Slling
25825094Slling error = spa_prop_set(spa, props);
25833912Slling
25845094Slling nvlist_free(props);
25853912Slling spa_close(spa, FTAG);
25863912Slling
25873912Slling return (error);
25883912Slling }
25893912Slling
25903912Slling static int
zfs_ioc_pool_get_props(zfs_cmd_t * zc)25914098Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
25923912Slling {
25933912Slling spa_t *spa;
25943912Slling int error;
25953912Slling nvlist_t *nvp = NULL;
25963912Slling
25978525SEric.Schrock@Sun.COM if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
25988525SEric.Schrock@Sun.COM /*
25998525SEric.Schrock@Sun.COM * If the pool is faulted, there may be properties we can still
26008525SEric.Schrock@Sun.COM * get (such as altroot and cachefile), so attempt to get them
26018525SEric.Schrock@Sun.COM * anyway.
26028525SEric.Schrock@Sun.COM */
26038525SEric.Schrock@Sun.COM mutex_enter(&spa_namespace_lock);
26048525SEric.Schrock@Sun.COM if ((spa = spa_lookup(zc->zc_name)) != NULL)
26058525SEric.Schrock@Sun.COM error = spa_prop_get(spa, &nvp);
26068525SEric.Schrock@Sun.COM mutex_exit(&spa_namespace_lock);
26078525SEric.Schrock@Sun.COM } else {
26088525SEric.Schrock@Sun.COM error = spa_prop_get(spa, &nvp);
26098525SEric.Schrock@Sun.COM spa_close(spa, FTAG);
26108525SEric.Schrock@Sun.COM }
26113912Slling
26123912Slling if (error == 0 && zc->zc_nvlist_dst != NULL)
26133912Slling error = put_nvlist(zc, nvp);
26143912Slling else
26153912Slling error = EFAULT;
26163912Slling
26178525SEric.Schrock@Sun.COM nvlist_free(nvp);
26183912Slling return (error);
26193912Slling }
26203912Slling
26215367Sahrens /*
26225367Sahrens * inputs:
26235367Sahrens * zc_name name of filesystem
26245367Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions
26255367Sahrens * zc_perm_action allow/unallow flag
26265367Sahrens *
26275367Sahrens * outputs: none
26285367Sahrens */
26294543Smarks static int
zfs_ioc_set_fsacl(zfs_cmd_t * zc)26304543Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
26314543Smarks {
26324543Smarks int error;
26334543Smarks nvlist_t *fsaclnv = NULL;
26344543Smarks
26355094Slling if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
26369643SEric.Taylor@Sun.COM zc->zc_iflags, &fsaclnv)) != 0)
26374543Smarks return (error);
26384543Smarks
26394543Smarks /*
26404543Smarks * Verify nvlist is constructed correctly
26414543Smarks */
26424543Smarks if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
26434543Smarks nvlist_free(fsaclnv);
26444543Smarks return (EINVAL);
26454543Smarks }
26464543Smarks
26474543Smarks /*
26484543Smarks * If we don't have PRIV_SYS_MOUNT, then validate
26494543Smarks * that user is allowed to hand out each permission in
26504543Smarks * the nvlist(s)
26514543Smarks */
26524543Smarks
26534787Sahrens error = secpolicy_zfs(CRED());
26544543Smarks if (error) {
26554787Sahrens if (zc->zc_perm_action == B_FALSE) {
26564787Sahrens error = dsl_deleg_can_allow(zc->zc_name,
26574787Sahrens fsaclnv, CRED());
26584787Sahrens } else {
26594787Sahrens error = dsl_deleg_can_unallow(zc->zc_name,
26604787Sahrens fsaclnv, CRED());
26614787Sahrens }
26624543Smarks }
26634543Smarks
26644543Smarks if (error == 0)
26654543Smarks error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
26664543Smarks
26674543Smarks nvlist_free(fsaclnv);
26684543Smarks return (error);
26694543Smarks }
26704543Smarks
26715367Sahrens /*
26725367Sahrens * inputs:
26735367Sahrens * zc_name name of filesystem
26745367Sahrens *
26755367Sahrens * outputs:
26765367Sahrens * zc_nvlist_src{_size} nvlist of delegated permissions
26775367Sahrens */
26784543Smarks static int
zfs_ioc_get_fsacl(zfs_cmd_t * zc)26794543Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
26804543Smarks {
26814543Smarks nvlist_t *nvp;
26824543Smarks int error;
26834543Smarks
26844543Smarks if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
26854543Smarks error = put_nvlist(zc, nvp);
26864543Smarks nvlist_free(nvp);
26874543Smarks }
26884543Smarks
26894543Smarks return (error);
26904543Smarks }
26914543Smarks
26925367Sahrens /*
2693789Sahrens * Search the vfs list for a specified resource. Returns a pointer to it
2694789Sahrens * or NULL if no suitable entry is found. The caller of this routine
2695789Sahrens * is responsible for releasing the returned vfs pointer.
2696789Sahrens */
2697789Sahrens static vfs_t *
zfs_get_vfs(const char * resource)2698789Sahrens zfs_get_vfs(const char *resource)
2699789Sahrens {
2700789Sahrens struct vfs *vfsp;
2701789Sahrens struct vfs *vfs_found = NULL;
2702789Sahrens
2703789Sahrens vfs_list_read_lock();
2704789Sahrens vfsp = rootvfs;
2705789Sahrens do {
2706789Sahrens if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2707789Sahrens VFS_HOLD(vfsp);
2708789Sahrens vfs_found = vfsp;
2709789Sahrens break;
2710789Sahrens }
2711789Sahrens vfsp = vfsp->vfs_next;
2712789Sahrens } while (vfsp != rootvfs);
2713789Sahrens vfs_list_unlock();
2714789Sahrens return (vfs_found);
2715789Sahrens }
2716789Sahrens
27174543Smarks /* ARGSUSED */
2718789Sahrens static void
zfs_create_cb(objset_t * os,void * arg,cred_t * cr,dmu_tx_t * tx)27194543Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2720789Sahrens {
27215331Samw zfs_creat_t *zct = arg;
27225498Stimh
27235498Stimh zfs_create_fs(os, cr, zct->zct_zplprops, tx);
27245331Samw }
27255331Samw
27265498Stimh #define ZFS_PROP_UNDEFINED ((uint64_t)-1)
27275498Stimh
27285331Samw /*
27295498Stimh * inputs:
27307184Stimh * createprops list of properties requested by creator
27317184Stimh * default_zplver zpl version to use if unspecified in createprops
27327184Stimh * fuids_ok fuids allowed in this version of the spa?
27337184Stimh * os parent objset pointer (NULL if root fs)
27345331Samw *
27355498Stimh * outputs:
27365498Stimh * zplprops values for the zplprops we attach to the master node object
27377184Stimh * is_ci true if requested file system will be purely case-insensitive
27385331Samw *
27395498Stimh * Determine the settings for utf8only, normalization and
27405498Stimh * casesensitivity. Specific values may have been requested by the
27415498Stimh * creator and/or we can inherit values from the parent dataset. If
27425498Stimh * the file system is of too early a vintage, a creator can not
27435498Stimh * request settings for these properties, even if the requested
27445498Stimh * setting is the default value. We don't actually want to create dsl
27455498Stimh * properties for these, so remove them from the source nvlist after
27465498Stimh * processing.
27475331Samw */
27485331Samw static int
zfs_fill_zplprops_impl(objset_t * os,uint64_t zplver,boolean_t fuids_ok,boolean_t sa_ok,nvlist_t * createprops,nvlist_t * zplprops,boolean_t * is_ci)27499396SMatthew.Ahrens@Sun.COM zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
275011935SMark.Shellenbaum@Sun.COM boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
275111935SMark.Shellenbaum@Sun.COM nvlist_t *zplprops, boolean_t *is_ci)
27525331Samw {
27535498Stimh uint64_t sense = ZFS_PROP_UNDEFINED;
27545498Stimh uint64_t norm = ZFS_PROP_UNDEFINED;
27555498Stimh uint64_t u8 = ZFS_PROP_UNDEFINED;
27565498Stimh
27575498Stimh ASSERT(zplprops != NULL);
27585498Stimh
27595375Stimh /*
27605498Stimh * Pull out creator prop choices, if any.
27615375Stimh */
27625498Stimh if (createprops) {
27635498Stimh (void) nvlist_lookup_uint64(createprops,
27647184Stimh zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
27657184Stimh (void) nvlist_lookup_uint64(createprops,
27665498Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
27675498Stimh (void) nvlist_remove_all(createprops,
27685498Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE));
27695498Stimh (void) nvlist_lookup_uint64(createprops,
27705498Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
27715498Stimh (void) nvlist_remove_all(createprops,
27725498Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
27735498Stimh (void) nvlist_lookup_uint64(createprops,
27745498Stimh zfs_prop_to_name(ZFS_PROP_CASE), &sense);
27755498Stimh (void) nvlist_remove_all(createprops,
27765498Stimh zfs_prop_to_name(ZFS_PROP_CASE));
27775331Samw }
27785331Samw
27795375Stimh /*
27807184Stimh * If the zpl version requested is whacky or the file system
27817184Stimh * or pool is version is too "young" to support normalization
27827184Stimh * and the creator tried to set a value for one of the props,
27837184Stimh * error out.
27845498Stimh */
27857184Stimh if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
27867184Stimh (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
278711935SMark.Shellenbaum@Sun.COM (zplver >= ZPL_VERSION_SA && !sa_ok) ||
27887184Stimh (zplver < ZPL_VERSION_NORMALIZATION &&
27895498Stimh (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
27907184Stimh sense != ZFS_PROP_UNDEFINED)))
27915498Stimh return (ENOTSUP);
27925498Stimh
27935498Stimh /*
27945498Stimh * Put the version in the zplprops
27955498Stimh */
27965498Stimh VERIFY(nvlist_add_uint64(zplprops,
27975498Stimh zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
27985498Stimh
27995498Stimh if (norm == ZFS_PROP_UNDEFINED)
28005498Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
28015498Stimh VERIFY(nvlist_add_uint64(zplprops,
28025498Stimh zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
28035498Stimh
28045498Stimh /*
28055498Stimh * If we're normalizing, names must always be valid UTF-8 strings.
28065498Stimh */
28075498Stimh if (norm)
28085498Stimh u8 = 1;
28095498Stimh if (u8 == ZFS_PROP_UNDEFINED)
28105498Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
28115498Stimh VERIFY(nvlist_add_uint64(zplprops,
28125498Stimh zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
28135498Stimh
28145498Stimh if (sense == ZFS_PROP_UNDEFINED)
28155498Stimh VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
28165498Stimh VERIFY(nvlist_add_uint64(zplprops,
28175498Stimh zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
28185498Stimh
28196492Stimh if (is_ci)
28206492Stimh *is_ci = (sense == ZFS_CASE_INSENSITIVE);
28216492Stimh
28227184Stimh return (0);
28237184Stimh }
28247184Stimh
28257184Stimh static int
zfs_fill_zplprops(const char * dataset,nvlist_t * createprops,nvlist_t * zplprops,boolean_t * is_ci)28267184Stimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
28277184Stimh nvlist_t *zplprops, boolean_t *is_ci)
28287184Stimh {
282911935SMark.Shellenbaum@Sun.COM boolean_t fuids_ok, sa_ok;
28307184Stimh uint64_t zplver = ZPL_VERSION;
28317184Stimh objset_t *os = NULL;
28327184Stimh char parentname[MAXNAMELEN];
28337184Stimh char *cp;
283411935SMark.Shellenbaum@Sun.COM spa_t *spa;
283511935SMark.Shellenbaum@Sun.COM uint64_t spa_vers;
28367184Stimh int error;
28377184Stimh
28387184Stimh (void) strlcpy(parentname, dataset, sizeof (parentname));
28397184Stimh cp = strrchr(parentname, '/');
28407184Stimh ASSERT(cp != NULL);
28417184Stimh cp[0] = '\0';
28427184Stimh
284311935SMark.Shellenbaum@Sun.COM if ((error = spa_open(dataset, &spa, FTAG)) != 0)
284411935SMark.Shellenbaum@Sun.COM return (error);
284511935SMark.Shellenbaum@Sun.COM
284611935SMark.Shellenbaum@Sun.COM spa_vers = spa_version(spa);
284711935SMark.Shellenbaum@Sun.COM spa_close(spa, FTAG);
284811935SMark.Shellenbaum@Sun.COM
284911935SMark.Shellenbaum@Sun.COM zplver = zfs_zpl_version_map(spa_vers);
285011935SMark.Shellenbaum@Sun.COM fuids_ok = (zplver >= ZPL_VERSION_FUID);
285111935SMark.Shellenbaum@Sun.COM sa_ok = (zplver >= ZPL_VERSION_SA);
28527184Stimh
28537184Stimh /*
28547184Stimh * Open parent object set so we can inherit zplprop values.
28557184Stimh */
285610298SMatthew.Ahrens@Sun.COM if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
28577184Stimh return (error);
28587184Stimh
285911935SMark.Shellenbaum@Sun.COM error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
28607184Stimh zplprops, is_ci);
286110298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
28627184Stimh return (error);
28637184Stimh }
28647184Stimh
28657184Stimh static int
zfs_fill_zplprops_root(uint64_t spa_vers,nvlist_t * createprops,nvlist_t * zplprops,boolean_t * is_ci)28667184Stimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
28677184Stimh nvlist_t *zplprops, boolean_t *is_ci)
28687184Stimh {
286911935SMark.Shellenbaum@Sun.COM boolean_t fuids_ok;
287011935SMark.Shellenbaum@Sun.COM boolean_t sa_ok;
28717184Stimh uint64_t zplver = ZPL_VERSION;
28727184Stimh int error;
28737184Stimh
287411935SMark.Shellenbaum@Sun.COM zplver = zfs_zpl_version_map(spa_vers);
287511935SMark.Shellenbaum@Sun.COM fuids_ok = (zplver >= ZPL_VERSION_FUID);
287611935SMark.Shellenbaum@Sun.COM sa_ok = (zplver >= ZPL_VERSION_SA);
287711935SMark.Shellenbaum@Sun.COM
287811935SMark.Shellenbaum@Sun.COM error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
287911935SMark.Shellenbaum@Sun.COM createprops, zplprops, is_ci);
28807184Stimh return (error);
2881789Sahrens }
2882789Sahrens
28835367Sahrens /*
28845367Sahrens * inputs:
28855367Sahrens * zc_objset_type type of objset to create (fs vs zvol)
28865367Sahrens * zc_name name of new objset
28875367Sahrens * zc_value name of snapshot to clone from (may be empty)
28885367Sahrens * zc_nvlist_src{_size} nvlist of properties to apply
28895367Sahrens *
28905498Stimh * outputs: none
28915367Sahrens */
2892789Sahrens static int
zfs_ioc_create(zfs_cmd_t * zc)2893789Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2894789Sahrens {
2895789Sahrens objset_t *clone;
2896789Sahrens int error = 0;
28975331Samw zfs_creat_t zct;
28984543Smarks nvlist_t *nvprops = NULL;
28994543Smarks void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2900789Sahrens dmu_objset_type_t type = zc->zc_objset_type;
2901789Sahrens
2902789Sahrens switch (type) {
2903789Sahrens
2904789Sahrens case DMU_OST_ZFS:
2905789Sahrens cbfunc = zfs_create_cb;
2906789Sahrens break;
2907789Sahrens
2908789Sahrens case DMU_OST_ZVOL:
2909789Sahrens cbfunc = zvol_create_cb;
2910789Sahrens break;
2911789Sahrens
2912789Sahrens default:
29132199Sahrens cbfunc = NULL;
29146423Sgw25295 break;
29152199Sahrens }
29165326Sek110237 if (strchr(zc->zc_name, '@') ||
29175326Sek110237 strchr(zc->zc_name, '%'))
2918789Sahrens return (EINVAL);
2919789Sahrens
29202676Seschrock if (zc->zc_nvlist_src != NULL &&
29215094Slling (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
29229643SEric.Taylor@Sun.COM zc->zc_iflags, &nvprops)) != 0)
29232676Seschrock return (error);
29242676Seschrock
29255498Stimh zct.zct_zplprops = NULL;
29265331Samw zct.zct_props = nvprops;
29275331Samw
29282676Seschrock if (zc->zc_value[0] != '\0') {
2929789Sahrens /*
2930789Sahrens * We're creating a clone of an existing snapshot.
2931789Sahrens */
29322676Seschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
29332676Seschrock if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
29344543Smarks nvlist_free(nvprops);
2935789Sahrens return (EINVAL);
29362676Seschrock }
2937789Sahrens
293810298SMatthew.Ahrens@Sun.COM error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
29392676Seschrock if (error) {
29404543Smarks nvlist_free(nvprops);
2941789Sahrens return (error);
29422676Seschrock }
29436492Stimh
294410272SMatthew.Ahrens@Sun.COM error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
294510298SMatthew.Ahrens@Sun.COM dmu_objset_rele(clone, FTAG);
29465331Samw if (error) {
29475331Samw nvlist_free(nvprops);
29485331Samw return (error);
29495331Samw }
2950789Sahrens } else {
29516492Stimh boolean_t is_insensitive = B_FALSE;
29526492Stimh
29532676Seschrock if (cbfunc == NULL) {
29544543Smarks nvlist_free(nvprops);
29552199Sahrens return (EINVAL);
29562676Seschrock }
29572676Seschrock
2958789Sahrens if (type == DMU_OST_ZVOL) {
29592676Seschrock uint64_t volsize, volblocksize;
29602676Seschrock
29614543Smarks if (nvprops == NULL ||
29624543Smarks nvlist_lookup_uint64(nvprops,
29632676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE),
29642676Seschrock &volsize) != 0) {
29654543Smarks nvlist_free(nvprops);
29662676Seschrock return (EINVAL);
29672676Seschrock }
29682676Seschrock
29694543Smarks if ((error = nvlist_lookup_uint64(nvprops,
29702676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
29712676Seschrock &volblocksize)) != 0 && error != ENOENT) {
29724543Smarks nvlist_free(nvprops);
29732676Seschrock return (EINVAL);
29742676Seschrock }
29751133Seschrock
29762676Seschrock if (error != 0)
29772676Seschrock volblocksize = zfs_prop_default_numeric(
29782676Seschrock ZFS_PROP_VOLBLOCKSIZE);
29792676Seschrock
29802676Seschrock if ((error = zvol_check_volblocksize(
29812676Seschrock volblocksize)) != 0 ||
29822676Seschrock (error = zvol_check_volsize(volsize,
29832676Seschrock volblocksize)) != 0) {
29844543Smarks nvlist_free(nvprops);
2985789Sahrens return (error);
29862676Seschrock }
29874577Sahrens } else if (type == DMU_OST_ZFS) {
29885331Samw int error;
29895331Samw
29905498Stimh /*
29915331Samw * We have to have normalization and
29925331Samw * case-folding flags correct when we do the
29935331Samw * file system creation, so go figure them out
29945498Stimh * now.
29955331Samw */
29965498Stimh VERIFY(nvlist_alloc(&zct.zct_zplprops,
29975498Stimh NV_UNIQUE_NAME, KM_SLEEP) == 0);
29985498Stimh error = zfs_fill_zplprops(zc->zc_name, nvprops,
29997184Stimh zct.zct_zplprops, &is_insensitive);
30005331Samw if (error != 0) {
30015331Samw nvlist_free(nvprops);
30025498Stimh nvlist_free(zct.zct_zplprops);
30035331Samw return (error);
30044577Sahrens }
30052676Seschrock }
300610272SMatthew.Ahrens@Sun.COM error = dmu_objset_create(zc->zc_name, type,
30076492Stimh is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
30085498Stimh nvlist_free(zct.zct_zplprops);
3009789Sahrens }
30102676Seschrock
30112676Seschrock /*
30122676Seschrock * It would be nice to do this atomically.
30132676Seschrock */
30142676Seschrock if (error == 0) {
301511022STom.Erickson@Sun.COM error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
301611022STom.Erickson@Sun.COM nvprops, NULL);
301711022STom.Erickson@Sun.COM if (error != 0)
301810242Schris.kirby@sun.com (void) dmu_objset_destroy(zc->zc_name, B_FALSE);
30192676Seschrock }
30204543Smarks nvlist_free(nvprops);
3021789Sahrens return (error);
3022789Sahrens }
3023789Sahrens
30245367Sahrens /*
30255367Sahrens * inputs:
30265367Sahrens * zc_name name of filesystem
30275367Sahrens * zc_value short name of snapshot
30285367Sahrens * zc_cookie recursive flag
30299396SMatthew.Ahrens@Sun.COM * zc_nvlist_src[_size] property list
30305367Sahrens *
303110588SEric.Taylor@Sun.COM * outputs:
303210588SEric.Taylor@Sun.COM * zc_value short snapname (i.e. part after the '@')
30335367Sahrens */
3034789Sahrens static int
zfs_ioc_snapshot(zfs_cmd_t * zc)30352199Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
30362199Sahrens {
30377265Sahrens nvlist_t *nvprops = NULL;
30387265Sahrens int error;
30397265Sahrens boolean_t recursive = zc->zc_cookie;
30407265Sahrens
30412676Seschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
30422199Sahrens return (EINVAL);
30437265Sahrens
30447265Sahrens if (zc->zc_nvlist_src != NULL &&
30457265Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
30469643SEric.Taylor@Sun.COM zc->zc_iflags, &nvprops)) != 0)
30477265Sahrens return (error);
30487265Sahrens
30499355SMatthew.Ahrens@Sun.COM error = zfs_check_userprops(zc->zc_name, nvprops);
30509355SMatthew.Ahrens@Sun.COM if (error)
30519355SMatthew.Ahrens@Sun.COM goto out;
30529355SMatthew.Ahrens@Sun.COM
305311022STom.Erickson@Sun.COM if (!nvlist_empty(nvprops) &&
30549355SMatthew.Ahrens@Sun.COM zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
30559355SMatthew.Ahrens@Sun.COM error = ENOTSUP;
30569355SMatthew.Ahrens@Sun.COM goto out;
30577265Sahrens }
30589355SMatthew.Ahrens@Sun.COM
305913043STim.Haley@Sun.COM error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, NULL,
306013043STim.Haley@Sun.COM nvprops, recursive, B_FALSE, -1);
30619355SMatthew.Ahrens@Sun.COM
30629355SMatthew.Ahrens@Sun.COM out:
30637265Sahrens nvlist_free(nvprops);
30647265Sahrens return (error);
30652199Sahrens }
30662199Sahrens
30674007Smmusante int
zfs_unmount_snap(const char * name,void * arg)306811209SMatthew.Ahrens@Sun.COM zfs_unmount_snap(const char *name, void *arg)
3069789Sahrens {
30702417Sahrens vfs_t *vfsp = NULL;
30712199Sahrens
30726689Smaybee if (arg) {
30736689Smaybee char *snapname = arg;
307411209SMatthew.Ahrens@Sun.COM char *fullname = kmem_asprintf("%s@%s", name, snapname);
307511209SMatthew.Ahrens@Sun.COM vfsp = zfs_get_vfs(fullname);
307611209SMatthew.Ahrens@Sun.COM strfree(fullname);
30772417Sahrens } else if (strchr(name, '@')) {
30782199Sahrens vfsp = zfs_get_vfs(name);
30792199Sahrens }
30802199Sahrens
30812199Sahrens if (vfsp) {
30822199Sahrens /*
30832199Sahrens * Always force the unmount for snapshots.
30842199Sahrens */
30852199Sahrens int flag = MS_FORCE;
3086789Sahrens int err;
3087789Sahrens
30882199Sahrens if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
30892199Sahrens VFS_RELE(vfsp);
30902199Sahrens return (err);
30912199Sahrens }
30922199Sahrens VFS_RELE(vfsp);
30932199Sahrens if ((err = dounmount(vfsp, flag, kcred)) != 0)
30942199Sahrens return (err);
30952199Sahrens }
30962199Sahrens return (0);
30972199Sahrens }
30982199Sahrens
30995367Sahrens /*
31005367Sahrens * inputs:
310110242Schris.kirby@sun.com * zc_name name of filesystem
310210242Schris.kirby@sun.com * zc_value short name of snapshot
310310242Schris.kirby@sun.com * zc_defer_destroy mark for deferred destroy
31045367Sahrens *
31055367Sahrens * outputs: none
31065367Sahrens */
31072199Sahrens static int
zfs_ioc_destroy_snaps(zfs_cmd_t * zc)31082199Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
31092199Sahrens {
31102199Sahrens int err;
3111789Sahrens
31122676Seschrock if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
31132199Sahrens return (EINVAL);
31142199Sahrens err = dmu_objset_find(zc->zc_name,
31152676Seschrock zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
31162199Sahrens if (err)
31172199Sahrens return (err);
311810242Schris.kirby@sun.com return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
311910242Schris.kirby@sun.com zc->zc_defer_destroy));
31202199Sahrens }
31212199Sahrens
31225367Sahrens /*
31235367Sahrens * inputs:
31245367Sahrens * zc_name name of dataset to destroy
31255367Sahrens * zc_objset_type type of objset
312610242Schris.kirby@sun.com * zc_defer_destroy mark for deferred destroy
31275367Sahrens *
31285367Sahrens * outputs: none
31295367Sahrens */
31302199Sahrens static int
zfs_ioc_destroy(zfs_cmd_t * zc)31312199Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
31322199Sahrens {
313310588SEric.Taylor@Sun.COM int err;
31342199Sahrens if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
313510588SEric.Taylor@Sun.COM err = zfs_unmount_snap(zc->zc_name, NULL);
31362199Sahrens if (err)
31372199Sahrens return (err);
3138789Sahrens }
3139789Sahrens
314010588SEric.Taylor@Sun.COM err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
314110588SEric.Taylor@Sun.COM if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
314210693Schris.kirby@sun.com (void) zvol_remove_minor(zc->zc_name);
314310588SEric.Taylor@Sun.COM return (err);
3144789Sahrens }
3145789Sahrens
31465367Sahrens /*
31475367Sahrens * inputs:
31485446Sahrens * zc_name name of dataset to rollback (to most recent snapshot)
31495367Sahrens *
31505367Sahrens * outputs: none
31515367Sahrens */
3152789Sahrens static int
zfs_ioc_rollback(zfs_cmd_t * zc)3153789Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
3154789Sahrens {
315510272SMatthew.Ahrens@Sun.COM dsl_dataset_t *ds, *clone;
31565446Sahrens int error;
315710272SMatthew.Ahrens@Sun.COM zfsvfs_t *zfsvfs;
315810272SMatthew.Ahrens@Sun.COM char *clone_name;
315910272SMatthew.Ahrens@Sun.COM
316010272SMatthew.Ahrens@Sun.COM error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
316110272SMatthew.Ahrens@Sun.COM if (error)
316210272SMatthew.Ahrens@Sun.COM return (error);
316310272SMatthew.Ahrens@Sun.COM
316410272SMatthew.Ahrens@Sun.COM /* must not be a snapshot */
316510272SMatthew.Ahrens@Sun.COM if (dsl_dataset_is_snapshot(ds)) {
316610272SMatthew.Ahrens@Sun.COM dsl_dataset_rele(ds, FTAG);
316710272SMatthew.Ahrens@Sun.COM return (EINVAL);
316810272SMatthew.Ahrens@Sun.COM }
316910272SMatthew.Ahrens@Sun.COM
317010272SMatthew.Ahrens@Sun.COM /* must have a most recent snapshot */
317110272SMatthew.Ahrens@Sun.COM if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
317210272SMatthew.Ahrens@Sun.COM dsl_dataset_rele(ds, FTAG);
317310272SMatthew.Ahrens@Sun.COM return (EINVAL);
317410272SMatthew.Ahrens@Sun.COM }
31755446Sahrens
31765446Sahrens /*
317710272SMatthew.Ahrens@Sun.COM * Create clone of most recent snapshot.
31785446Sahrens */
317910272SMatthew.Ahrens@Sun.COM clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
318010272SMatthew.Ahrens@Sun.COM error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
31815446Sahrens if (error)
318210272SMatthew.Ahrens@Sun.COM goto out;
318310272SMatthew.Ahrens@Sun.COM
318410298SMatthew.Ahrens@Sun.COM error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
318510272SMatthew.Ahrens@Sun.COM if (error)
318610272SMatthew.Ahrens@Sun.COM goto out;
318710272SMatthew.Ahrens@Sun.COM
318810272SMatthew.Ahrens@Sun.COM /*
318910272SMatthew.Ahrens@Sun.COM * Do clone swap.
319010272SMatthew.Ahrens@Sun.COM */
31919396SMatthew.Ahrens@Sun.COM if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
319210298SMatthew.Ahrens@Sun.COM error = zfs_suspend_fs(zfsvfs);
31936083Sek110237 if (error == 0) {
31946083Sek110237 int resume_err;
31956083Sek110237
319610272SMatthew.Ahrens@Sun.COM if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
319710272SMatthew.Ahrens@Sun.COM error = dsl_dataset_clone_swap(clone, ds,
319810272SMatthew.Ahrens@Sun.COM B_TRUE);
319910272SMatthew.Ahrens@Sun.COM dsl_dataset_disown(ds, FTAG);
320010272SMatthew.Ahrens@Sun.COM ds = NULL;
320110272SMatthew.Ahrens@Sun.COM } else {
320210272SMatthew.Ahrens@Sun.COM error = EBUSY;
320310272SMatthew.Ahrens@Sun.COM }
320410298SMatthew.Ahrens@Sun.COM resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
32056083Sek110237 error = error ? error : resume_err;
32066083Sek110237 }
32075446Sahrens VFS_RELE(zfsvfs->z_vfs);
32085446Sahrens } else {
320910272SMatthew.Ahrens@Sun.COM if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
321010272SMatthew.Ahrens@Sun.COM error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
321110272SMatthew.Ahrens@Sun.COM dsl_dataset_disown(ds, FTAG);
321210272SMatthew.Ahrens@Sun.COM ds = NULL;
321310272SMatthew.Ahrens@Sun.COM } else {
321410272SMatthew.Ahrens@Sun.COM error = EBUSY;
321510272SMatthew.Ahrens@Sun.COM }
32165446Sahrens }
321710272SMatthew.Ahrens@Sun.COM
321810272SMatthew.Ahrens@Sun.COM /*
321910272SMatthew.Ahrens@Sun.COM * Destroy clone (which also closes it).
322010272SMatthew.Ahrens@Sun.COM */
322110272SMatthew.Ahrens@Sun.COM (void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
322210272SMatthew.Ahrens@Sun.COM
322310272SMatthew.Ahrens@Sun.COM out:
322410272SMatthew.Ahrens@Sun.COM strfree(clone_name);
322510272SMatthew.Ahrens@Sun.COM if (ds)
322610272SMatthew.Ahrens@Sun.COM dsl_dataset_rele(ds, FTAG);
32275446Sahrens return (error);
3228789Sahrens }
3229789Sahrens
32305367Sahrens /*
32315367Sahrens * inputs:
32325367Sahrens * zc_name old name of dataset
32335367Sahrens * zc_value new name of dataset
32345367Sahrens * zc_cookie recursive flag (only valid for snapshots)
32355367Sahrens *
32365367Sahrens * outputs: none
32375367Sahrens */
3238789Sahrens static int
zfs_ioc_rename(zfs_cmd_t * zc)3239789Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3240789Sahrens {
32414490Svb160487 boolean_t recursive = zc->zc_cookie & 1;
32424007Smmusante
32432676Seschrock zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
32445326Sek110237 if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
32455326Sek110237 strchr(zc->zc_value, '%'))
3246789Sahrens return (EINVAL);
3247789Sahrens
32484007Smmusante /*
32494007Smmusante * Unmount snapshot unless we're doing a recursive rename,
32504007Smmusante * in which case the dataset code figures out which snapshots
32514007Smmusante * to unmount.
32524007Smmusante */
32534007Smmusante if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3254789Sahrens zc->zc_objset_type == DMU_OST_ZFS) {
32552199Sahrens int err = zfs_unmount_snap(zc->zc_name, NULL);
32562199Sahrens if (err)
32572199Sahrens return (err);
3258789Sahrens }
325910588SEric.Taylor@Sun.COM if (zc->zc_objset_type == DMU_OST_ZVOL)
326010588SEric.Taylor@Sun.COM (void) zvol_remove_minor(zc->zc_name);
32614007Smmusante return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3262789Sahrens }
3263789Sahrens
326411022STom.Erickson@Sun.COM static int
zfs_check_settable(const char * dsname,nvpair_t * pair,cred_t * cr)326511022STom.Erickson@Sun.COM zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
326611022STom.Erickson@Sun.COM {
326711022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
326811022STom.Erickson@Sun.COM boolean_t issnap = (strchr(dsname, '@') != NULL);
326911022STom.Erickson@Sun.COM zfs_prop_t prop = zfs_name_to_prop(propname);
327011022STom.Erickson@Sun.COM uint64_t intval;
327111022STom.Erickson@Sun.COM int err;
327211022STom.Erickson@Sun.COM
327311022STom.Erickson@Sun.COM if (prop == ZPROP_INVAL) {
327411022STom.Erickson@Sun.COM if (zfs_prop_user(propname)) {
327511022STom.Erickson@Sun.COM if (err = zfs_secpolicy_write_perms(dsname,
327611022STom.Erickson@Sun.COM ZFS_DELEG_PERM_USERPROP, cr))
327711022STom.Erickson@Sun.COM return (err);
327811022STom.Erickson@Sun.COM return (0);
327911022STom.Erickson@Sun.COM }
328011022STom.Erickson@Sun.COM
328111022STom.Erickson@Sun.COM if (!issnap && zfs_prop_userquota(propname)) {
328211022STom.Erickson@Sun.COM const char *perm = NULL;
328311022STom.Erickson@Sun.COM const char *uq_prefix =
328411022STom.Erickson@Sun.COM zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
328511022STom.Erickson@Sun.COM const char *gq_prefix =
328611022STom.Erickson@Sun.COM zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
328711022STom.Erickson@Sun.COM
328811022STom.Erickson@Sun.COM if (strncmp(propname, uq_prefix,
328911022STom.Erickson@Sun.COM strlen(uq_prefix)) == 0) {
329011022STom.Erickson@Sun.COM perm = ZFS_DELEG_PERM_USERQUOTA;
329111022STom.Erickson@Sun.COM } else if (strncmp(propname, gq_prefix,
329211022STom.Erickson@Sun.COM strlen(gq_prefix)) == 0) {
329311022STom.Erickson@Sun.COM perm = ZFS_DELEG_PERM_GROUPQUOTA;
329411022STom.Erickson@Sun.COM } else {
329511022STom.Erickson@Sun.COM /* USERUSED and GROUPUSED are read-only */
329611022STom.Erickson@Sun.COM return (EINVAL);
329711022STom.Erickson@Sun.COM }
329811022STom.Erickson@Sun.COM
329911022STom.Erickson@Sun.COM if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
330011022STom.Erickson@Sun.COM return (err);
330111022STom.Erickson@Sun.COM return (0);
330211022STom.Erickson@Sun.COM }
330311022STom.Erickson@Sun.COM
330411022STom.Erickson@Sun.COM return (EINVAL);
330511022STom.Erickson@Sun.COM }
330611022STom.Erickson@Sun.COM
330711022STom.Erickson@Sun.COM if (issnap)
330811022STom.Erickson@Sun.COM return (EINVAL);
330911022STom.Erickson@Sun.COM
331011022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
331111022STom.Erickson@Sun.COM /*
331211022STom.Erickson@Sun.COM * dsl_prop_get_all_impl() returns properties in this
331311022STom.Erickson@Sun.COM * format.
331411022STom.Erickson@Sun.COM */
331511022STom.Erickson@Sun.COM nvlist_t *attrs;
331611022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
331711022STom.Erickson@Sun.COM VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
331811022STom.Erickson@Sun.COM &pair) == 0);
331911022STom.Erickson@Sun.COM }
332011022STom.Erickson@Sun.COM
332111022STom.Erickson@Sun.COM /*
332211022STom.Erickson@Sun.COM * Check that this value is valid for this pool version
332311022STom.Erickson@Sun.COM */
332411022STom.Erickson@Sun.COM switch (prop) {
332511022STom.Erickson@Sun.COM case ZFS_PROP_COMPRESSION:
332611022STom.Erickson@Sun.COM /*
332711022STom.Erickson@Sun.COM * If the user specified gzip compression, make sure
332811022STom.Erickson@Sun.COM * the SPA supports it. We ignore any errors here since
332911022STom.Erickson@Sun.COM * we'll catch them later.
333011022STom.Erickson@Sun.COM */
333111022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
333211022STom.Erickson@Sun.COM nvpair_value_uint64(pair, &intval) == 0) {
333311022STom.Erickson@Sun.COM if (intval >= ZIO_COMPRESS_GZIP_1 &&
333411022STom.Erickson@Sun.COM intval <= ZIO_COMPRESS_GZIP_9 &&
333511022STom.Erickson@Sun.COM zfs_earlier_version(dsname,
333611022STom.Erickson@Sun.COM SPA_VERSION_GZIP_COMPRESSION)) {
333711022STom.Erickson@Sun.COM return (ENOTSUP);
333811022STom.Erickson@Sun.COM }
333911022STom.Erickson@Sun.COM
334011022STom.Erickson@Sun.COM if (intval == ZIO_COMPRESS_ZLE &&
334111022STom.Erickson@Sun.COM zfs_earlier_version(dsname,
334211022STom.Erickson@Sun.COM SPA_VERSION_ZLE_COMPRESSION))
334311022STom.Erickson@Sun.COM return (ENOTSUP);
334411022STom.Erickson@Sun.COM
334511022STom.Erickson@Sun.COM /*
334611022STom.Erickson@Sun.COM * If this is a bootable dataset then
334711022STom.Erickson@Sun.COM * verify that the compression algorithm
334811022STom.Erickson@Sun.COM * is supported for booting. We must return
334911022STom.Erickson@Sun.COM * something other than ENOTSUP since it
335011022STom.Erickson@Sun.COM * implies a downrev pool version.
335111022STom.Erickson@Sun.COM */
335211022STom.Erickson@Sun.COM if (zfs_is_bootfs(dsname) &&
335311022STom.Erickson@Sun.COM !BOOTFS_COMPRESS_VALID(intval)) {
335411022STom.Erickson@Sun.COM return (ERANGE);
335511022STom.Erickson@Sun.COM }
335611022STom.Erickson@Sun.COM }
335711022STom.Erickson@Sun.COM break;
335811022STom.Erickson@Sun.COM
335911022STom.Erickson@Sun.COM case ZFS_PROP_COPIES:
336011022STom.Erickson@Sun.COM if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
336111022STom.Erickson@Sun.COM return (ENOTSUP);
336211022STom.Erickson@Sun.COM break;
336311022STom.Erickson@Sun.COM
336411022STom.Erickson@Sun.COM case ZFS_PROP_DEDUP:
336511022STom.Erickson@Sun.COM if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
336611022STom.Erickson@Sun.COM return (ENOTSUP);
336711022STom.Erickson@Sun.COM break;
336811022STom.Erickson@Sun.COM
336911022STom.Erickson@Sun.COM case ZFS_PROP_SHARESMB:
337011022STom.Erickson@Sun.COM if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
337111022STom.Erickson@Sun.COM return (ENOTSUP);
337211022STom.Erickson@Sun.COM break;
337311022STom.Erickson@Sun.COM
337411022STom.Erickson@Sun.COM case ZFS_PROP_ACLINHERIT:
337511022STom.Erickson@Sun.COM if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
337611022STom.Erickson@Sun.COM nvpair_value_uint64(pair, &intval) == 0) {
337711022STom.Erickson@Sun.COM if (intval == ZFS_ACL_PASSTHROUGH_X &&
337811022STom.Erickson@Sun.COM zfs_earlier_version(dsname,
337911022STom.Erickson@Sun.COM SPA_VERSION_PASSTHROUGH_X))
338011022STom.Erickson@Sun.COM return (ENOTSUP);
338111022STom.Erickson@Sun.COM }
338211022STom.Erickson@Sun.COM break;
338311022STom.Erickson@Sun.COM }
338411022STom.Erickson@Sun.COM
338511022STom.Erickson@Sun.COM return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
338611022STom.Erickson@Sun.COM }
338711022STom.Erickson@Sun.COM
338811022STom.Erickson@Sun.COM /*
338911022STom.Erickson@Sun.COM * Removes properties from the given props list that fail permission checks
339011022STom.Erickson@Sun.COM * needed to clear them and to restore them in case of a receive error. For each
339111022STom.Erickson@Sun.COM * property, make sure we have both set and inherit permissions.
339211022STom.Erickson@Sun.COM *
339311022STom.Erickson@Sun.COM * Returns the first error encountered if any permission checks fail. If the
339411022STom.Erickson@Sun.COM * caller provides a non-NULL errlist, it also gives the complete list of names
339511022STom.Erickson@Sun.COM * of all the properties that failed a permission check along with the
339611022STom.Erickson@Sun.COM * corresponding error numbers. The caller is responsible for freeing the
339711022STom.Erickson@Sun.COM * returned errlist.
339811022STom.Erickson@Sun.COM *
339911022STom.Erickson@Sun.COM * If every property checks out successfully, zero is returned and the list
340011022STom.Erickson@Sun.COM * pointed at by errlist is NULL.
340111022STom.Erickson@Sun.COM */
340211022STom.Erickson@Sun.COM static int
zfs_check_clearable(char * dataset,nvlist_t * props,nvlist_t ** errlist)340311022STom.Erickson@Sun.COM zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
34046689Smaybee {
34056689Smaybee zfs_cmd_t *zc;
340611022STom.Erickson@Sun.COM nvpair_t *pair, *next_pair;
340711022STom.Erickson@Sun.COM nvlist_t *errors;
340811022STom.Erickson@Sun.COM int err, rv = 0;
34096689Smaybee
34106689Smaybee if (props == NULL)
341111022STom.Erickson@Sun.COM return (0);
341211022STom.Erickson@Sun.COM
341311022STom.Erickson@Sun.COM VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
341411022STom.Erickson@Sun.COM
34156689Smaybee zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
34166689Smaybee (void) strcpy(zc->zc_name, dataset);
341711022STom.Erickson@Sun.COM pair = nvlist_next_nvpair(props, NULL);
341811022STom.Erickson@Sun.COM while (pair != NULL) {
341911022STom.Erickson@Sun.COM next_pair = nvlist_next_nvpair(props, pair);
342011022STom.Erickson@Sun.COM
342111022STom.Erickson@Sun.COM (void) strcpy(zc->zc_value, nvpair_name(pair));
342211022STom.Erickson@Sun.COM if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
342311022STom.Erickson@Sun.COM (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
342411022STom.Erickson@Sun.COM VERIFY(nvlist_remove_nvpair(props, pair) == 0);
342511022STom.Erickson@Sun.COM VERIFY(nvlist_add_int32(errors,
342611022STom.Erickson@Sun.COM zc->zc_value, err) == 0);
342711022STom.Erickson@Sun.COM }
342811022STom.Erickson@Sun.COM pair = next_pair;
34296689Smaybee }
34306689Smaybee kmem_free(zc, sizeof (zfs_cmd_t));
343111022STom.Erickson@Sun.COM
343211022STom.Erickson@Sun.COM if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
343311022STom.Erickson@Sun.COM nvlist_free(errors);
343411022STom.Erickson@Sun.COM errors = NULL;
343511022STom.Erickson@Sun.COM } else {
343611022STom.Erickson@Sun.COM VERIFY(nvpair_value_int32(pair, &rv) == 0);
343711022STom.Erickson@Sun.COM }
343811022STom.Erickson@Sun.COM
343911022STom.Erickson@Sun.COM if (errlist == NULL)
344011022STom.Erickson@Sun.COM nvlist_free(errors);
344111022STom.Erickson@Sun.COM else
344211022STom.Erickson@Sun.COM *errlist = errors;
344311022STom.Erickson@Sun.COM
344411022STom.Erickson@Sun.COM return (rv);
34456689Smaybee }
34466689Smaybee
344711022STom.Erickson@Sun.COM static boolean_t
propval_equals(nvpair_t * p1,nvpair_t * p2)344811022STom.Erickson@Sun.COM propval_equals(nvpair_t *p1, nvpair_t *p2)
344911022STom.Erickson@Sun.COM {
345011022STom.Erickson@Sun.COM if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
345111022STom.Erickson@Sun.COM /* dsl_prop_get_all_impl() format */
345211022STom.Erickson@Sun.COM nvlist_t *attrs;
345311022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
345411022STom.Erickson@Sun.COM VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
345511022STom.Erickson@Sun.COM &p1) == 0);
345611022STom.Erickson@Sun.COM }
345711022STom.Erickson@Sun.COM
345811022STom.Erickson@Sun.COM if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
345911022STom.Erickson@Sun.COM nvlist_t *attrs;
346011022STom.Erickson@Sun.COM VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
346111022STom.Erickson@Sun.COM VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
346211022STom.Erickson@Sun.COM &p2) == 0);
346311022STom.Erickson@Sun.COM }
346411022STom.Erickson@Sun.COM
346511022STom.Erickson@Sun.COM if (nvpair_type(p1) != nvpair_type(p2))
346611022STom.Erickson@Sun.COM return (B_FALSE);
346711022STom.Erickson@Sun.COM
346811022STom.Erickson@Sun.COM if (nvpair_type(p1) == DATA_TYPE_STRING) {
346911022STom.Erickson@Sun.COM char *valstr1, *valstr2;
347011022STom.Erickson@Sun.COM
347111022STom.Erickson@Sun.COM VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
347211022STom.Erickson@Sun.COM VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
347311022STom.Erickson@Sun.COM return (strcmp(valstr1, valstr2) == 0);
347411022STom.Erickson@Sun.COM } else {
347511022STom.Erickson@Sun.COM uint64_t intval1, intval2;
347611022STom.Erickson@Sun.COM
347711022STom.Erickson@Sun.COM VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
347811022STom.Erickson@Sun.COM VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
347911022STom.Erickson@Sun.COM return (intval1 == intval2);
348011022STom.Erickson@Sun.COM }
348111022STom.Erickson@Sun.COM }
348211022STom.Erickson@Sun.COM
348311022STom.Erickson@Sun.COM /*
348411022STom.Erickson@Sun.COM * Remove properties from props if they are not going to change (as determined
348511022STom.Erickson@Sun.COM * by comparison with origprops). Remove them from origprops as well, since we
348611022STom.Erickson@Sun.COM * do not need to clear or restore properties that won't change.
348711022STom.Erickson@Sun.COM */
348811022STom.Erickson@Sun.COM static void
props_reduce(nvlist_t * props,nvlist_t * origprops)348911022STom.Erickson@Sun.COM props_reduce(nvlist_t *props, nvlist_t *origprops)
349011022STom.Erickson@Sun.COM {
349111022STom.Erickson@Sun.COM nvpair_t *pair, *next_pair;
349211022STom.Erickson@Sun.COM
349311022STom.Erickson@Sun.COM if (origprops == NULL)
349411022STom.Erickson@Sun.COM return; /* all props need to be received */
349511022STom.Erickson@Sun.COM
349611022STom.Erickson@Sun.COM pair = nvlist_next_nvpair(props, NULL);
349711022STom.Erickson@Sun.COM while (pair != NULL) {
349811022STom.Erickson@Sun.COM const char *propname = nvpair_name(pair);
349911022STom.Erickson@Sun.COM nvpair_t *match;
350011022STom.Erickson@Sun.COM
350111022STom.Erickson@Sun.COM next_pair = nvlist_next_nvpair(props, pair);
350211022STom.Erickson@Sun.COM
350311022STom.Erickson@Sun.COM if ((nvlist_lookup_nvpair(origprops, propname,
350411022STom.Erickson@Sun.COM &match) != 0) || !propval_equals(pair, match))
350511022STom.Erickson@Sun.COM goto next; /* need to set received value */
350611022STom.Erickson@Sun.COM
350711022STom.Erickson@Sun.COM /* don't clear the existing received value */
350811022STom.Erickson@Sun.COM (void) nvlist_remove_nvpair(origprops, match);
350911022STom.Erickson@Sun.COM /* don't bother receiving the property */
351011022STom.Erickson@Sun.COM (void) nvlist_remove_nvpair(props, pair);
351111022STom.Erickson@Sun.COM next:
351211022STom.Erickson@Sun.COM pair = next_pair;
351311022STom.Erickson@Sun.COM }
351411022STom.Erickson@Sun.COM }
351511022STom.Erickson@Sun.COM
351611022STom.Erickson@Sun.COM #ifdef DEBUG
351711022STom.Erickson@Sun.COM static boolean_t zfs_ioc_recv_inject_err;
351811022STom.Erickson@Sun.COM #endif
351911022STom.Erickson@Sun.COM
35205367Sahrens /*
35215367Sahrens * inputs:
35225367Sahrens * zc_name name of containing filesystem
35235367Sahrens * zc_nvlist_src{_size} nvlist of properties to apply
35245367Sahrens * zc_value name of snapshot to create
35255367Sahrens * zc_string name of clone origin (if DRR_FLAG_CLONE)
35265367Sahrens * zc_cookie file descriptor to recv from
35275367Sahrens * zc_begin_record the BEGIN record of the stream (not byteswapped)
35285367Sahrens * zc_guid force flag
352912527SChris.Kirby@oracle.com * zc_cleanup_fd cleanup-on-exit file descriptor
353012527SChris.Kirby@oracle.com * zc_action_handle handle for this guid/ds mapping (or zero on first call)
35315367Sahrens *
35325367Sahrens * outputs:
35335367Sahrens * zc_cookie number of bytes read
353411022STom.Erickson@Sun.COM * zc_nvlist_dst{_size} error for each unapplied received property
353511022STom.Erickson@Sun.COM * zc_obj zprop_errflags_t
353612527SChris.Kirby@oracle.com * zc_action_handle handle for this guid/ds mapping
35375367Sahrens */
3538789Sahrens static int
zfs_ioc_recv(zfs_cmd_t * zc)35395367Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3540789Sahrens {
3541789Sahrens file_t *fp;
35425326Sek110237 objset_t *os;
35435367Sahrens dmu_recv_cookie_t drc;
35445326Sek110237 boolean_t force = (boolean_t)zc->zc_guid;
354511022STom.Erickson@Sun.COM int fd;
354611022STom.Erickson@Sun.COM int error = 0;
354711022STom.Erickson@Sun.COM int props_error = 0;
354811022STom.Erickson@Sun.COM nvlist_t *errors;
35495367Sahrens offset_t off;
355011022STom.Erickson@Sun.COM nvlist_t *props = NULL; /* sent properties */
355111022STom.Erickson@Sun.COM nvlist_t *origprops = NULL; /* existing properties */
35525367Sahrens objset_t *origin = NULL;
35535367Sahrens char *tosnap;
35545367Sahrens char tofs[ZFS_MAXNAMELEN];
355511022STom.Erickson@Sun.COM boolean_t first_recvd_props = B_FALSE;
3556789Sahrens
35573265Sahrens if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
35585326Sek110237 strchr(zc->zc_value, '@') == NULL ||
35595326Sek110237 strchr(zc->zc_value, '%'))
35603265Sahrens return (EINVAL);
35613265Sahrens
35625367Sahrens (void) strcpy(tofs, zc->zc_value);
35635367Sahrens tosnap = strchr(tofs, '@');
356411022STom.Erickson@Sun.COM *tosnap++ = '\0';
35655367Sahrens
35665367Sahrens if (zc->zc_nvlist_src != NULL &&
35675367Sahrens (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
35689643SEric.Taylor@Sun.COM zc->zc_iflags, &props)) != 0)
35695367Sahrens return (error);
35705367Sahrens
3571789Sahrens fd = zc->zc_cookie;
3572789Sahrens fp = getf(fd);
35735367Sahrens if (fp == NULL) {
35745367Sahrens nvlist_free(props);
3575789Sahrens return (EBADF);
35765367Sahrens }
35775326Sek110237
357811022STom.Erickson@Sun.COM VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
357911022STom.Erickson@Sun.COM
358010298SMatthew.Ahrens@Sun.COM if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
358111022STom.Erickson@Sun.COM if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
358211022STom.Erickson@Sun.COM !dsl_prop_get_hasrecvd(os)) {
358311022STom.Erickson@Sun.COM first_recvd_props = B_TRUE;
358411022STom.Erickson@Sun.COM }
358511022STom.Erickson@Sun.COM
35866689Smaybee /*
358711022STom.Erickson@Sun.COM * If new received properties are supplied, they are to
358811022STom.Erickson@Sun.COM * completely replace the existing received properties, so stash
358911022STom.Erickson@Sun.COM * away the existing ones.
35906689Smaybee */
359111022STom.Erickson@Sun.COM if (dsl_prop_get_received(os, &origprops) == 0) {
359211022STom.Erickson@Sun.COM nvlist_t *errlist = NULL;
359311022STom.Erickson@Sun.COM /*
359411022STom.Erickson@Sun.COM * Don't bother writing a property if its value won't
359511022STom.Erickson@Sun.COM * change (and avoid the unnecessary security checks).
359611022STom.Erickson@Sun.COM *
359711022STom.Erickson@Sun.COM * The first receive after SPA_VERSION_RECVD_PROPS is a
359811022STom.Erickson@Sun.COM * special case where we blow away all local properties
359911022STom.Erickson@Sun.COM * regardless.
360011022STom.Erickson@Sun.COM */
360111022STom.Erickson@Sun.COM if (!first_recvd_props)
360211022STom.Erickson@Sun.COM props_reduce(props, origprops);
360311022STom.Erickson@Sun.COM if (zfs_check_clearable(tofs, origprops,
360411022STom.Erickson@Sun.COM &errlist) != 0)
360511022STom.Erickson@Sun.COM (void) nvlist_merge(errors, errlist, 0);
360611022STom.Erickson@Sun.COM nvlist_free(errlist);
360711022STom.Erickson@Sun.COM }
360810298SMatthew.Ahrens@Sun.COM
360910298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
36105326Sek110237 }
36115326Sek110237
36125367Sahrens if (zc->zc_string[0]) {
361310298SMatthew.Ahrens@Sun.COM error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
36146689Smaybee if (error)
36156689Smaybee goto out;
36165367Sahrens }
36175367Sahrens
361811007SLori.Alt@Sun.COM error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
361911007SLori.Alt@Sun.COM &zc->zc_begin_record, force, origin, &drc);
36205367Sahrens if (origin)
362110298SMatthew.Ahrens@Sun.COM dmu_objset_rele(origin, FTAG);
36226689Smaybee if (error)
36236689Smaybee goto out;
36245326Sek110237
36255326Sek110237 /*
362611022STom.Erickson@Sun.COM * Set properties before we receive the stream so that they are applied
362711022STom.Erickson@Sun.COM * to the new data. Note that we must call dmu_recv_stream() if
362811022STom.Erickson@Sun.COM * dmu_recv_begin() succeeds.
36295326Sek110237 */
36305367Sahrens if (props) {
363111022STom.Erickson@Sun.COM nvlist_t *errlist;
363211022STom.Erickson@Sun.COM
363311022STom.Erickson@Sun.COM if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
363411022STom.Erickson@Sun.COM if (drc.drc_newfs) {
363511022STom.Erickson@Sun.COM if (spa_version(os->os_spa) >=
363611022STom.Erickson@Sun.COM SPA_VERSION_RECVD_PROPS)
363711022STom.Erickson@Sun.COM first_recvd_props = B_TRUE;
363811022STom.Erickson@Sun.COM } else if (origprops != NULL) {
363911022STom.Erickson@Sun.COM if (clear_received_props(os, tofs, origprops,
364011022STom.Erickson@Sun.COM first_recvd_props ? NULL : props) != 0)
364111022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NOCLEAR;
364211022STom.Erickson@Sun.COM } else {
364311022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NOCLEAR;
364411022STom.Erickson@Sun.COM }
364511022STom.Erickson@Sun.COM dsl_prop_set_hasrecvd(os);
364611022STom.Erickson@Sun.COM } else if (!drc.drc_newfs) {
364711022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NOCLEAR;
364811022STom.Erickson@Sun.COM }
364911022STom.Erickson@Sun.COM
365011022STom.Erickson@Sun.COM (void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
365111022STom.Erickson@Sun.COM props, &errlist);
365211022STom.Erickson@Sun.COM (void) nvlist_merge(errors, errlist, 0);
365311022STom.Erickson@Sun.COM nvlist_free(errlist);
365411022STom.Erickson@Sun.COM }
365511022STom.Erickson@Sun.COM
365611022STom.Erickson@Sun.COM if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
36576689Smaybee /*
365811022STom.Erickson@Sun.COM * Caller made zc->zc_nvlist_dst less than the minimum expected
365911022STom.Erickson@Sun.COM * size or supplied an invalid address.
36606689Smaybee */
366111022STom.Erickson@Sun.COM props_error = EINVAL;
36625367Sahrens }
36635367Sahrens
36645367Sahrens off = fp->f_offset;
366512527SChris.Kirby@oracle.com error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
366612527SChris.Kirby@oracle.com &zc->zc_action_handle);
36675367Sahrens
366810204SMatthew.Ahrens@Sun.COM if (error == 0) {
366910204SMatthew.Ahrens@Sun.COM zfsvfs_t *zfsvfs = NULL;
367010204SMatthew.Ahrens@Sun.COM
367110204SMatthew.Ahrens@Sun.COM if (getzfsvfs(tofs, &zfsvfs) == 0) {
367210204SMatthew.Ahrens@Sun.COM /* online recv */
367310204SMatthew.Ahrens@Sun.COM int end_err;
367410298SMatthew.Ahrens@Sun.COM
367510298SMatthew.Ahrens@Sun.COM error = zfs_suspend_fs(zfsvfs);
367610204SMatthew.Ahrens@Sun.COM /*
367710204SMatthew.Ahrens@Sun.COM * If the suspend fails, then the recv_end will
367810204SMatthew.Ahrens@Sun.COM * likely also fail, and clean up after itself.
367910204SMatthew.Ahrens@Sun.COM */
368010204SMatthew.Ahrens@Sun.COM end_err = dmu_recv_end(&drc);
368111812SGeorge.Wilson@Sun.COM if (error == 0)
368211812SGeorge.Wilson@Sun.COM error = zfs_resume_fs(zfsvfs, tofs);
368310204SMatthew.Ahrens@Sun.COM error = error ? error : end_err;
368410204SMatthew.Ahrens@Sun.COM VFS_RELE(zfsvfs->z_vfs);
368510204SMatthew.Ahrens@Sun.COM } else {
36866689Smaybee error = dmu_recv_end(&drc);
36875367Sahrens }
36886083Sek110237 }
36895367Sahrens
36905367Sahrens zc->zc_cookie = off - fp->f_offset;
36915367Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36925367Sahrens fp->f_offset = off;
36932885Sahrens
369411022STom.Erickson@Sun.COM #ifdef DEBUG
369511022STom.Erickson@Sun.COM if (zfs_ioc_recv_inject_err) {
369611022STom.Erickson@Sun.COM zfs_ioc_recv_inject_err = B_FALSE;
369711022STom.Erickson@Sun.COM error = 1;
369811022STom.Erickson@Sun.COM }
369911022STom.Erickson@Sun.COM #endif
37006689Smaybee /*
37016689Smaybee * On error, restore the original props.
37026689Smaybee */
37036689Smaybee if (error && props) {
370411022STom.Erickson@Sun.COM if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
370511022STom.Erickson@Sun.COM if (clear_received_props(os, tofs, props, NULL) != 0) {
370611022STom.Erickson@Sun.COM /*
370711022STom.Erickson@Sun.COM * We failed to clear the received properties.
370811022STom.Erickson@Sun.COM * Since we may have left a $recvd value on the
370911022STom.Erickson@Sun.COM * system, we can't clear the $hasrecvd flag.
371011022STom.Erickson@Sun.COM */
371111022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NORESTORE;
371211022STom.Erickson@Sun.COM } else if (first_recvd_props) {
371311022STom.Erickson@Sun.COM dsl_prop_unset_hasrecvd(os);
371411022STom.Erickson@Sun.COM }
371511022STom.Erickson@Sun.COM dmu_objset_rele(os, FTAG);
371611022STom.Erickson@Sun.COM } else if (!drc.drc_newfs) {
371711022STom.Erickson@Sun.COM /* We failed to clear the received properties. */
371811022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NORESTORE;
371911022STom.Erickson@Sun.COM }
372011022STom.Erickson@Sun.COM
372111022STom.Erickson@Sun.COM if (origprops == NULL && !drc.drc_newfs) {
372211022STom.Erickson@Sun.COM /* We failed to stash the original properties. */
372311022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NORESTORE;
372411022STom.Erickson@Sun.COM }
372511022STom.Erickson@Sun.COM
372611022STom.Erickson@Sun.COM /*
372711022STom.Erickson@Sun.COM * dsl_props_set() will not convert RECEIVED to LOCAL on or
372811022STom.Erickson@Sun.COM * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
372911022STom.Erickson@Sun.COM * explictly if we're restoring local properties cleared in the
373011022STom.Erickson@Sun.COM * first new-style receive.
373111022STom.Erickson@Sun.COM */
373211022STom.Erickson@Sun.COM if (origprops != NULL &&
373311022STom.Erickson@Sun.COM zfs_set_prop_nvlist(tofs, (first_recvd_props ?
373411022STom.Erickson@Sun.COM ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
373511022STom.Erickson@Sun.COM origprops, NULL) != 0) {
373611022STom.Erickson@Sun.COM /*
373711022STom.Erickson@Sun.COM * We stashed the original properties but failed to
373811022STom.Erickson@Sun.COM * restore them.
373911022STom.Erickson@Sun.COM */
374011022STom.Erickson@Sun.COM zc->zc_obj |= ZPROP_ERR_NORESTORE;
374111022STom.Erickson@Sun.COM }
37426689Smaybee }
37436689Smaybee out:
37446689Smaybee nvlist_free(props);
37456689Smaybee nvlist_free(origprops);
374611022STom.Erickson@Sun.COM nvlist_free(errors);
3747789Sahrens releasef(fd);
374811022STom.Erickson@Sun.COM
374911022STom.Erickson@Sun.COM if (error == 0)
375011022STom.Erickson@Sun.COM error = props_error;
375111022STom.Erickson@Sun.COM
3752789Sahrens return (error);
3753789Sahrens }
3754789Sahrens
37555367Sahrens /*
37565367Sahrens * inputs:
37575367Sahrens * zc_name name of snapshot to send
37585367Sahrens * zc_cookie file descriptor to send stream to
375912786SChris.Kirby@oracle.com * zc_obj fromorigin flag (mutually exclusive with zc_fromobj)
376012786SChris.Kirby@oracle.com * zc_sendobj objsetid of snapshot to send
376112786SChris.Kirby@oracle.com * zc_fromobj objsetid of incremental fromsnap (may be zero)
37625367Sahrens *
37635367Sahrens * outputs: none
37645367Sahrens */
3765789Sahrens static int
zfs_ioc_send(zfs_cmd_t * zc)37665367Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3767789Sahrens {
3768789Sahrens objset_t *fromsnap = NULL;
3769789Sahrens objset_t *tosnap;
3770789Sahrens file_t *fp;
3771789Sahrens int error;
37725367Sahrens offset_t off;
377312786SChris.Kirby@oracle.com dsl_dataset_t *ds;
377412786SChris.Kirby@oracle.com dsl_dataset_t *dsfrom = NULL;
377512786SChris.Kirby@oracle.com spa_t *spa;
377612786SChris.Kirby@oracle.com dsl_pool_t *dp;
377712786SChris.Kirby@oracle.com
377812786SChris.Kirby@oracle.com error = spa_open(zc->zc_name, &spa, FTAG);
3779789Sahrens if (error)
3780789Sahrens return (error);
3781789Sahrens
378212786SChris.Kirby@oracle.com dp = spa_get_dsl(spa);
378312786SChris.Kirby@oracle.com rw_enter(&dp->dp_config_rwlock, RW_READER);
378412786SChris.Kirby@oracle.com error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
378512786SChris.Kirby@oracle.com rw_exit(&dp->dp_config_rwlock);
378612786SChris.Kirby@oracle.com if (error) {
378712786SChris.Kirby@oracle.com spa_close(spa, FTAG);
378812786SChris.Kirby@oracle.com return (error);
378912786SChris.Kirby@oracle.com }
379012786SChris.Kirby@oracle.com
379112786SChris.Kirby@oracle.com error = dmu_objset_from_ds(ds, &tosnap);
379212786SChris.Kirby@oracle.com if (error) {
379312786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
379412786SChris.Kirby@oracle.com spa_close(spa, FTAG);
379512786SChris.Kirby@oracle.com return (error);
379612786SChris.Kirby@oracle.com }
379712786SChris.Kirby@oracle.com
379812786SChris.Kirby@oracle.com if (zc->zc_fromobj != 0) {
379912786SChris.Kirby@oracle.com rw_enter(&dp->dp_config_rwlock, RW_READER);
380012786SChris.Kirby@oracle.com error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &dsfrom);
380112786SChris.Kirby@oracle.com rw_exit(&dp->dp_config_rwlock);
380212786SChris.Kirby@oracle.com spa_close(spa, FTAG);
3803789Sahrens if (error) {
380412786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
3805789Sahrens return (error);
3806789Sahrens }
380712786SChris.Kirby@oracle.com error = dmu_objset_from_ds(dsfrom, &fromsnap);
380812786SChris.Kirby@oracle.com if (error) {
380912786SChris.Kirby@oracle.com dsl_dataset_rele(dsfrom, FTAG);
381012786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
381112786SChris.Kirby@oracle.com return (error);
381212786SChris.Kirby@oracle.com }
381312786SChris.Kirby@oracle.com } else {
381412786SChris.Kirby@oracle.com spa_close(spa, FTAG);
3815789Sahrens }
3816789Sahrens
3817789Sahrens fp = getf(zc->zc_cookie);
3818789Sahrens if (fp == NULL) {
381912786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
382012786SChris.Kirby@oracle.com if (dsfrom)
382112786SChris.Kirby@oracle.com dsl_dataset_rele(dsfrom, FTAG);
3822789Sahrens return (EBADF);
3823789Sahrens }
3824789Sahrens
38255367Sahrens off = fp->f_offset;
38265367Sahrens error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
38275367Sahrens
38285367Sahrens if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
38295367Sahrens fp->f_offset = off;
3830789Sahrens releasef(zc->zc_cookie);
383112786SChris.Kirby@oracle.com if (dsfrom)
383212786SChris.Kirby@oracle.com dsl_dataset_rele(dsfrom, FTAG);
383312786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
3834789Sahrens return (error);
3835789Sahrens }
3836789Sahrens
38371544Seschrock static int
zfs_ioc_inject_fault(zfs_cmd_t * zc)38381544Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
38391544Seschrock {
38401544Seschrock int id, error;
38411544Seschrock
38421544Seschrock error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
38431544Seschrock &zc->zc_inject_record);
38441544Seschrock
38451544Seschrock if (error == 0)
38461544Seschrock zc->zc_guid = (uint64_t)id;
38471544Seschrock
38481544Seschrock return (error);
38491544Seschrock }
38501544Seschrock
38511544Seschrock static int
zfs_ioc_clear_fault(zfs_cmd_t * zc)38521544Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
38531544Seschrock {
38541544Seschrock return (zio_clear_fault((int)zc->zc_guid));
38551544Seschrock }
38561544Seschrock
38571544Seschrock static int
zfs_ioc_inject_list_next(zfs_cmd_t * zc)38581544Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
38591544Seschrock {
38601544Seschrock int id = (int)zc->zc_guid;
38611544Seschrock int error;
38621544Seschrock
38631544Seschrock error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
38641544Seschrock &zc->zc_inject_record);
38651544Seschrock
38661544Seschrock zc->zc_guid = id;
38671544Seschrock
38681544Seschrock return (error);
38691544Seschrock }
38701544Seschrock
38711544Seschrock static int
zfs_ioc_error_log(zfs_cmd_t * zc)38721544Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
38731544Seschrock {
38741544Seschrock spa_t *spa;
38751544Seschrock int error;
38762676Seschrock size_t count = (size_t)zc->zc_nvlist_dst_size;
38771544Seschrock
38781544Seschrock if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
38791544Seschrock return (error);
38801544Seschrock
38812676Seschrock error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
38821544Seschrock &count);
38831544Seschrock if (error == 0)
38842676Seschrock zc->zc_nvlist_dst_size = count;
38851544Seschrock else
38862676Seschrock zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
38871544Seschrock
38881544Seschrock spa_close(spa, FTAG);
38891544Seschrock
38901544Seschrock return (error);
38911544Seschrock }
38921544Seschrock
38931544Seschrock static int
zfs_ioc_clear(zfs_cmd_t * zc)38941544Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
38951544Seschrock {
38961544Seschrock spa_t *spa;
38971544Seschrock vdev_t *vd;
38981544Seschrock int error;
38991544Seschrock
39007294Sperrin /*
39017294Sperrin * On zpool clear we also fix up missing slogs
39027294Sperrin */
39037294Sperrin mutex_enter(&spa_namespace_lock);
39047294Sperrin spa = spa_lookup(zc->zc_name);
39057294Sperrin if (spa == NULL) {
39067294Sperrin mutex_exit(&spa_namespace_lock);
39077294Sperrin return (EIO);
39087294Sperrin }
390910922SJeff.Bonwick@Sun.COM if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
39107294Sperrin /* we need to let spa_open/spa_load clear the chains */
391110922SJeff.Bonwick@Sun.COM spa_set_log_state(spa, SPA_LOG_CLEAR);
39127294Sperrin }
391310921STim.Haley@Sun.COM spa->spa_last_open_failed = 0;
39147294Sperrin mutex_exit(&spa_namespace_lock);
39157294Sperrin
391611727SVictor.Latushkin@Sun.COM if (zc->zc_cookie & ZPOOL_NO_REWIND) {
391710921STim.Haley@Sun.COM error = spa_open(zc->zc_name, &spa, FTAG);
391810921STim.Haley@Sun.COM } else {
391910921STim.Haley@Sun.COM nvlist_t *policy;
392010921STim.Haley@Sun.COM nvlist_t *config = NULL;
392110921STim.Haley@Sun.COM
392210921STim.Haley@Sun.COM if (zc->zc_nvlist_src == NULL)
392310921STim.Haley@Sun.COM return (EINVAL);
392410921STim.Haley@Sun.COM
392510921STim.Haley@Sun.COM if ((error = get_nvlist(zc->zc_nvlist_src,
392610921STim.Haley@Sun.COM zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
392710921STim.Haley@Sun.COM error = spa_open_rewind(zc->zc_name, &spa, FTAG,
392810921STim.Haley@Sun.COM policy, &config);
392910921STim.Haley@Sun.COM if (config != NULL) {
393012949SGeorge.Wilson@Sun.COM int err;
393112949SGeorge.Wilson@Sun.COM
393212949SGeorge.Wilson@Sun.COM if ((err = put_nvlist(zc, config)) != 0)
393312949SGeorge.Wilson@Sun.COM error = err;
393410921STim.Haley@Sun.COM nvlist_free(config);
393510921STim.Haley@Sun.COM }
393610921STim.Haley@Sun.COM nvlist_free(policy);
393710921STim.Haley@Sun.COM }
393810921STim.Haley@Sun.COM }
393910921STim.Haley@Sun.COM
394010921STim.Haley@Sun.COM if (error)
39411544Seschrock return (error);
39421544Seschrock
394310685SGeorge.Wilson@Sun.COM spa_vdev_state_enter(spa, SCL_NONE);
39441544Seschrock
39452676Seschrock if (zc->zc_guid == 0) {
39461544Seschrock vd = NULL;
39476643Seschrock } else {
39486643Seschrock vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
39495450Sbrendan if (vd == NULL) {
39507754SJeff.Bonwick@Sun.COM (void) spa_vdev_state_exit(spa, NULL, ENODEV);
39515450Sbrendan spa_close(spa, FTAG);
39525450Sbrendan return (ENODEV);
39535450Sbrendan }
39541544Seschrock }
39551544Seschrock
39567754SJeff.Bonwick@Sun.COM vdev_clear(spa, vd);
39577754SJeff.Bonwick@Sun.COM
39587754SJeff.Bonwick@Sun.COM (void) spa_vdev_state_exit(spa, NULL, 0);
39597754SJeff.Bonwick@Sun.COM
39607754SJeff.Bonwick@Sun.COM /*
39617754SJeff.Bonwick@Sun.COM * Resume any suspended I/Os.
39627754SJeff.Bonwick@Sun.COM */
39639234SGeorge.Wilson@Sun.COM if (zio_resume(spa) != 0)
39649234SGeorge.Wilson@Sun.COM error = EIO;
39651544Seschrock
39661544Seschrock spa_close(spa, FTAG);
39671544Seschrock
39689234SGeorge.Wilson@Sun.COM return (error);
39691544Seschrock }
39701544Seschrock
39715367Sahrens /*
39725367Sahrens * inputs:
39735367Sahrens * zc_name name of filesystem
39745367Sahrens * zc_value name of origin snapshot
39755367Sahrens *
397610588SEric.Taylor@Sun.COM * outputs:
397710588SEric.Taylor@Sun.COM * zc_string name of conflicting snapshot, if there is one
39785367Sahrens */
39791544Seschrock static int
zfs_ioc_promote(zfs_cmd_t * zc)39802082Seschrock zfs_ioc_promote(zfs_cmd_t *zc)
39812082Seschrock {
39822417Sahrens char *cp;
39832417Sahrens
39842417Sahrens /*
39852417Sahrens * We don't need to unmount *all* the origin fs's snapshots, but
39862417Sahrens * it's easier.
39872417Sahrens */
39882676Seschrock cp = strchr(zc->zc_value, '@');
39892417Sahrens if (cp)
39902417Sahrens *cp = '\0';
39912676Seschrock (void) dmu_objset_find(zc->zc_value,
39922417Sahrens zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
399310588SEric.Taylor@Sun.COM return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
39942082Seschrock }
39952082Seschrock
39964543Smarks /*
39979396SMatthew.Ahrens@Sun.COM * Retrieve a single {user|group}{used|quota}@... property.
39989396SMatthew.Ahrens@Sun.COM *
39999396SMatthew.Ahrens@Sun.COM * inputs:
40009396SMatthew.Ahrens@Sun.COM * zc_name name of filesystem
40019396SMatthew.Ahrens@Sun.COM * zc_objset_type zfs_userquota_prop_t
40029396SMatthew.Ahrens@Sun.COM * zc_value domain name (eg. "S-1-234-567-89")
40039396SMatthew.Ahrens@Sun.COM * zc_guid RID/UID/GID
40049396SMatthew.Ahrens@Sun.COM *
40059396SMatthew.Ahrens@Sun.COM * outputs:
40069396SMatthew.Ahrens@Sun.COM * zc_cookie property value
40079396SMatthew.Ahrens@Sun.COM */
40089396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_userspace_one(zfs_cmd_t * zc)40099396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_one(zfs_cmd_t *zc)
40109396SMatthew.Ahrens@Sun.COM {
40119396SMatthew.Ahrens@Sun.COM zfsvfs_t *zfsvfs;
40129396SMatthew.Ahrens@Sun.COM int error;
40139396SMatthew.Ahrens@Sun.COM
40149396SMatthew.Ahrens@Sun.COM if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
40159396SMatthew.Ahrens@Sun.COM return (EINVAL);
40169396SMatthew.Ahrens@Sun.COM
401712620SMark.Shellenbaum@Oracle.COM error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
40189396SMatthew.Ahrens@Sun.COM if (error)
40199396SMatthew.Ahrens@Sun.COM return (error);
40209396SMatthew.Ahrens@Sun.COM
40219396SMatthew.Ahrens@Sun.COM error = zfs_userspace_one(zfsvfs,
40229396SMatthew.Ahrens@Sun.COM zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
40239396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs, FTAG);
40249396SMatthew.Ahrens@Sun.COM
40259396SMatthew.Ahrens@Sun.COM return (error);
40269396SMatthew.Ahrens@Sun.COM }
40279396SMatthew.Ahrens@Sun.COM
40289396SMatthew.Ahrens@Sun.COM /*
40299396SMatthew.Ahrens@Sun.COM * inputs:
40309396SMatthew.Ahrens@Sun.COM * zc_name name of filesystem
40319396SMatthew.Ahrens@Sun.COM * zc_cookie zap cursor
40329396SMatthew.Ahrens@Sun.COM * zc_objset_type zfs_userquota_prop_t
40339396SMatthew.Ahrens@Sun.COM * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
40349396SMatthew.Ahrens@Sun.COM *
40359396SMatthew.Ahrens@Sun.COM * outputs:
40369396SMatthew.Ahrens@Sun.COM * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t)
40379396SMatthew.Ahrens@Sun.COM * zc_cookie zap cursor
40389396SMatthew.Ahrens@Sun.COM */
40399396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_userspace_many(zfs_cmd_t * zc)40409396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_many(zfs_cmd_t *zc)
40419396SMatthew.Ahrens@Sun.COM {
40429396SMatthew.Ahrens@Sun.COM zfsvfs_t *zfsvfs;
404311933STim.Haley@Sun.COM int bufsize = zc->zc_nvlist_dst_size;
404411933STim.Haley@Sun.COM
404511933STim.Haley@Sun.COM if (bufsize <= 0)
404611933STim.Haley@Sun.COM return (ENOMEM);
404711933STim.Haley@Sun.COM
404812620SMark.Shellenbaum@Oracle.COM int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
40499396SMatthew.Ahrens@Sun.COM if (error)
40509396SMatthew.Ahrens@Sun.COM return (error);
40519396SMatthew.Ahrens@Sun.COM
40529396SMatthew.Ahrens@Sun.COM void *buf = kmem_alloc(bufsize, KM_SLEEP);
40539396SMatthew.Ahrens@Sun.COM
40549396SMatthew.Ahrens@Sun.COM error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
40559396SMatthew.Ahrens@Sun.COM buf, &zc->zc_nvlist_dst_size);
40569396SMatthew.Ahrens@Sun.COM
40579396SMatthew.Ahrens@Sun.COM if (error == 0) {
40589396SMatthew.Ahrens@Sun.COM error = xcopyout(buf,
40599396SMatthew.Ahrens@Sun.COM (void *)(uintptr_t)zc->zc_nvlist_dst,
40609396SMatthew.Ahrens@Sun.COM zc->zc_nvlist_dst_size);
40619396SMatthew.Ahrens@Sun.COM }
40629396SMatthew.Ahrens@Sun.COM kmem_free(buf, bufsize);
40639396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs, FTAG);
40649396SMatthew.Ahrens@Sun.COM
40659396SMatthew.Ahrens@Sun.COM return (error);
40669396SMatthew.Ahrens@Sun.COM }
40679396SMatthew.Ahrens@Sun.COM
40689396SMatthew.Ahrens@Sun.COM /*
40699396SMatthew.Ahrens@Sun.COM * inputs:
40709396SMatthew.Ahrens@Sun.COM * zc_name name of filesystem
40719396SMatthew.Ahrens@Sun.COM *
40729396SMatthew.Ahrens@Sun.COM * outputs:
40739396SMatthew.Ahrens@Sun.COM * none
40749396SMatthew.Ahrens@Sun.COM */
40759396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_userspace_upgrade(zfs_cmd_t * zc)40769396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
40779396SMatthew.Ahrens@Sun.COM {
40789396SMatthew.Ahrens@Sun.COM objset_t *os;
407911422SMark.Musante@Sun.COM int error = 0;
40809396SMatthew.Ahrens@Sun.COM zfsvfs_t *zfsvfs;
40819396SMatthew.Ahrens@Sun.COM
40829396SMatthew.Ahrens@Sun.COM if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
408310298SMatthew.Ahrens@Sun.COM if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
40849396SMatthew.Ahrens@Sun.COM /*
40859396SMatthew.Ahrens@Sun.COM * If userused is not enabled, it may be because the
40869396SMatthew.Ahrens@Sun.COM * objset needs to be closed & reopened (to grow the
40879396SMatthew.Ahrens@Sun.COM * objset_phys_t). Suspend/resume the fs will do that.
40889396SMatthew.Ahrens@Sun.COM */
408910298SMatthew.Ahrens@Sun.COM error = zfs_suspend_fs(zfsvfs);
409010298SMatthew.Ahrens@Sun.COM if (error == 0)
409110298SMatthew.Ahrens@Sun.COM error = zfs_resume_fs(zfsvfs, zc->zc_name);
40929396SMatthew.Ahrens@Sun.COM }
40939396SMatthew.Ahrens@Sun.COM if (error == 0)
40949396SMatthew.Ahrens@Sun.COM error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
40959396SMatthew.Ahrens@Sun.COM VFS_RELE(zfsvfs->z_vfs);
40969396SMatthew.Ahrens@Sun.COM } else {
409710298SMatthew.Ahrens@Sun.COM /* XXX kind of reading contents without owning */
409810298SMatthew.Ahrens@Sun.COM error = dmu_objset_hold(zc->zc_name, FTAG, &os);
40999396SMatthew.Ahrens@Sun.COM if (error)
41009396SMatthew.Ahrens@Sun.COM return (error);
41019396SMatthew.Ahrens@Sun.COM
41029396SMatthew.Ahrens@Sun.COM error = dmu_objset_userspace_upgrade(os);
410310298SMatthew.Ahrens@Sun.COM dmu_objset_rele(os, FTAG);
41049396SMatthew.Ahrens@Sun.COM }
41059396SMatthew.Ahrens@Sun.COM
41069396SMatthew.Ahrens@Sun.COM return (error);
41079396SMatthew.Ahrens@Sun.COM }
41089396SMatthew.Ahrens@Sun.COM
41099396SMatthew.Ahrens@Sun.COM /*
41104543Smarks * We don't want to have a hard dependency
41114543Smarks * against some special symbols in sharefs
41125331Samw * nfs, and smbsrv. Determine them if needed when
41134543Smarks * the first file system is shared.
41145331Samw * Neither sharefs, nfs or smbsrv are unloadable modules.
41154543Smarks */
41165331Samw int (*znfsexport_fs)(void *arg);
41174543Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
41185331Samw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
41195331Samw
41205331Samw int zfs_nfsshare_inited;
41215331Samw int zfs_smbshare_inited;
41225331Samw
41234543Smarks ddi_modhandle_t nfs_mod;
41244543Smarks ddi_modhandle_t sharefs_mod;
41255331Samw ddi_modhandle_t smbsrv_mod;
41264543Smarks kmutex_t zfs_share_lock;
41274543Smarks
41284543Smarks static int
zfs_init_sharefs()41295331Samw zfs_init_sharefs()
41305331Samw {
41315331Samw int error;
41325331Samw
41335331Samw ASSERT(MUTEX_HELD(&zfs_share_lock));
41345331Samw /* Both NFS and SMB shares also require sharetab support. */
41355331Samw if (sharefs_mod == NULL && ((sharefs_mod =
41365331Samw ddi_modopen("fs/sharefs",
41375331Samw KRTLD_MODE_FIRST, &error)) == NULL)) {
41385331Samw return (ENOSYS);
41395331Samw }
41405331Samw if (zshare_fs == NULL && ((zshare_fs =
41415331Samw (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
41425331Samw ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
41435331Samw return (ENOSYS);
41445331Samw }
41455331Samw return (0);
41465331Samw }
41475331Samw
41485331Samw static int
zfs_ioc_share(zfs_cmd_t * zc)41494543Smarks zfs_ioc_share(zfs_cmd_t *zc)
41504543Smarks {
41514543Smarks int error;
41524543Smarks int opcode;
41534543Smarks
41545331Samw switch (zc->zc_share.z_sharetype) {
41555331Samw case ZFS_SHARE_NFS:
41565331Samw case ZFS_UNSHARE_NFS:
41575331Samw if (zfs_nfsshare_inited == 0) {
41585331Samw mutex_enter(&zfs_share_lock);
41595331Samw if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
41605331Samw KRTLD_MODE_FIRST, &error)) == NULL)) {
41615331Samw mutex_exit(&zfs_share_lock);
41625331Samw return (ENOSYS);
41635331Samw }
41645331Samw if (znfsexport_fs == NULL &&
41655331Samw ((znfsexport_fs = (int (*)(void *))
41665331Samw ddi_modsym(nfs_mod,
41675331Samw "nfs_export", &error)) == NULL)) {
41685331Samw mutex_exit(&zfs_share_lock);
41695331Samw return (ENOSYS);
41705331Samw }
41715331Samw error = zfs_init_sharefs();
41725331Samw if (error) {
41735331Samw mutex_exit(&zfs_share_lock);
41745331Samw return (ENOSYS);
41755331Samw }
41765331Samw zfs_nfsshare_inited = 1;
41774543Smarks mutex_exit(&zfs_share_lock);
41784543Smarks }
41795331Samw break;
41805331Samw case ZFS_SHARE_SMB:
41815331Samw case ZFS_UNSHARE_SMB:
41825331Samw if (zfs_smbshare_inited == 0) {
41835331Samw mutex_enter(&zfs_share_lock);
41845331Samw if (smbsrv_mod == NULL && ((smbsrv_mod =
41855331Samw ddi_modopen("drv/smbsrv",
41865331Samw KRTLD_MODE_FIRST, &error)) == NULL)) {
41875331Samw mutex_exit(&zfs_share_lock);
41885331Samw return (ENOSYS);
41895331Samw }
41905331Samw if (zsmbexport_fs == NULL && ((zsmbexport_fs =
41915331Samw (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
41926139Sjb150015 "smb_server_share", &error)) == NULL)) {
41935331Samw mutex_exit(&zfs_share_lock);
41945331Samw return (ENOSYS);
41955331Samw }
41965331Samw error = zfs_init_sharefs();
41975331Samw if (error) {
41985331Samw mutex_exit(&zfs_share_lock);
41995331Samw return (ENOSYS);
42005331Samw }
42015331Samw zfs_smbshare_inited = 1;
42024543Smarks mutex_exit(&zfs_share_lock);
42034543Smarks }
42045331Samw break;
42055331Samw default:
42065331Samw return (EINVAL);
42074543Smarks }
42084543Smarks
42095331Samw switch (zc->zc_share.z_sharetype) {
42105331Samw case ZFS_SHARE_NFS:
42115331Samw case ZFS_UNSHARE_NFS:
42125331Samw if (error =
42135331Samw znfsexport_fs((void *)
42145331Samw (uintptr_t)zc->zc_share.z_exportdata))
42155331Samw return (error);
42165331Samw break;
42175331Samw case ZFS_SHARE_SMB:
42185331Samw case ZFS_UNSHARE_SMB:
42195331Samw if (error = zsmbexport_fs((void *)
42205331Samw (uintptr_t)zc->zc_share.z_exportdata,
42215331Samw zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
42228845Samw@Sun.COM B_TRUE: B_FALSE)) {
42235331Samw return (error);
42245331Samw }
42255331Samw break;
42265331Samw }
42275331Samw
42285331Samw opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
42295331Samw zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
42304543Smarks SHAREFS_ADD : SHAREFS_REMOVE;
42314543Smarks
42325331Samw /*
42335331Samw * Add or remove share from sharetab
42345331Samw */
42354543Smarks error = zshare_fs(opcode,
42364543Smarks (void *)(uintptr_t)zc->zc_share.z_sharedata,
42374543Smarks zc->zc_share.z_sharemax);
42384543Smarks
42394543Smarks return (error);
42404543Smarks
42414543Smarks }
42424543Smarks
42438845Samw@Sun.COM ace_t full_access[] = {
42448845Samw@Sun.COM {(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
42458845Samw@Sun.COM };
42468845Samw@Sun.COM
42478845Samw@Sun.COM /*
424813043STim.Haley@Sun.COM * inputs:
424913043STim.Haley@Sun.COM * zc_name name of containing filesystem
425013043STim.Haley@Sun.COM * zc_obj object # beyond which we want next in-use object #
425113043STim.Haley@Sun.COM *
425213043STim.Haley@Sun.COM * outputs:
425313043STim.Haley@Sun.COM * zc_obj next in-use object #
425413043STim.Haley@Sun.COM */
425513043STim.Haley@Sun.COM static int
zfs_ioc_next_obj(zfs_cmd_t * zc)425613043STim.Haley@Sun.COM zfs_ioc_next_obj(zfs_cmd_t *zc)
425713043STim.Haley@Sun.COM {
425813043STim.Haley@Sun.COM objset_t *os = NULL;
425913043STim.Haley@Sun.COM int error;
426013043STim.Haley@Sun.COM
426113043STim.Haley@Sun.COM error = dmu_objset_hold(zc->zc_name, FTAG, &os);
426213043STim.Haley@Sun.COM if (error)
426313043STim.Haley@Sun.COM return (error);
426413043STim.Haley@Sun.COM
426513043STim.Haley@Sun.COM error = dmu_object_next(os, &zc->zc_obj, B_FALSE,
426613043STim.Haley@Sun.COM os->os_dsl_dataset->ds_phys->ds_prev_snap_txg);
426713043STim.Haley@Sun.COM
426813043STim.Haley@Sun.COM dmu_objset_rele(os, FTAG);
426913043STim.Haley@Sun.COM return (error);
427013043STim.Haley@Sun.COM }
427113043STim.Haley@Sun.COM
427213043STim.Haley@Sun.COM /*
427313043STim.Haley@Sun.COM * inputs:
427413043STim.Haley@Sun.COM * zc_name name of filesystem
427513043STim.Haley@Sun.COM * zc_value prefix name for snapshot
427613043STim.Haley@Sun.COM * zc_cleanup_fd cleanup-on-exit file descriptor for calling process
427713043STim.Haley@Sun.COM *
427813043STim.Haley@Sun.COM * outputs:
427913043STim.Haley@Sun.COM */
428013043STim.Haley@Sun.COM static int
zfs_ioc_tmp_snapshot(zfs_cmd_t * zc)428113043STim.Haley@Sun.COM zfs_ioc_tmp_snapshot(zfs_cmd_t *zc)
428213043STim.Haley@Sun.COM {
428313043STim.Haley@Sun.COM char *snap_name;
428413043STim.Haley@Sun.COM int error;
428513043STim.Haley@Sun.COM
428613043STim.Haley@Sun.COM snap_name = kmem_asprintf("%s-%016llx", zc->zc_value,
428713043STim.Haley@Sun.COM (u_longlong_t)ddi_get_lbolt64());
428813043STim.Haley@Sun.COM
428913043STim.Haley@Sun.COM if (strlen(snap_name) >= MAXNAMELEN) {
429013043STim.Haley@Sun.COM strfree(snap_name);
429113043STim.Haley@Sun.COM return (E2BIG);
429213043STim.Haley@Sun.COM }
429313043STim.Haley@Sun.COM
429413043STim.Haley@Sun.COM error = dmu_objset_snapshot(zc->zc_name, snap_name, snap_name,
429513043STim.Haley@Sun.COM NULL, B_FALSE, B_TRUE, zc->zc_cleanup_fd);
429613043STim.Haley@Sun.COM if (error != 0) {
429713043STim.Haley@Sun.COM strfree(snap_name);
429813043STim.Haley@Sun.COM return (error);
429913043STim.Haley@Sun.COM }
430013043STim.Haley@Sun.COM
430113043STim.Haley@Sun.COM (void) strcpy(zc->zc_value, snap_name);
430213043STim.Haley@Sun.COM strfree(snap_name);
430313043STim.Haley@Sun.COM return (0);
430413043STim.Haley@Sun.COM }
430513043STim.Haley@Sun.COM
430613043STim.Haley@Sun.COM /*
430713043STim.Haley@Sun.COM * inputs:
430813043STim.Haley@Sun.COM * zc_name name of "to" snapshot
430913043STim.Haley@Sun.COM * zc_value name of "from" snapshot
431013043STim.Haley@Sun.COM * zc_cookie file descriptor to write diff data on
431113043STim.Haley@Sun.COM *
431213043STim.Haley@Sun.COM * outputs:
431313043STim.Haley@Sun.COM * dmu_diff_record_t's to the file descriptor
431413043STim.Haley@Sun.COM */
431513043STim.Haley@Sun.COM static int
zfs_ioc_diff(zfs_cmd_t * zc)431613043STim.Haley@Sun.COM zfs_ioc_diff(zfs_cmd_t *zc)
431713043STim.Haley@Sun.COM {
431813043STim.Haley@Sun.COM objset_t *fromsnap;
431913043STim.Haley@Sun.COM objset_t *tosnap;
432013043STim.Haley@Sun.COM file_t *fp;
432113043STim.Haley@Sun.COM offset_t off;
432213043STim.Haley@Sun.COM int error;
432313043STim.Haley@Sun.COM
432413043STim.Haley@Sun.COM error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
432513043STim.Haley@Sun.COM if (error)
432613043STim.Haley@Sun.COM return (error);
432713043STim.Haley@Sun.COM
432813043STim.Haley@Sun.COM error = dmu_objset_hold(zc->zc_value, FTAG, &fromsnap);
432913043STim.Haley@Sun.COM if (error) {
433013043STim.Haley@Sun.COM dmu_objset_rele(tosnap, FTAG);
433113043STim.Haley@Sun.COM return (error);
433213043STim.Haley@Sun.COM }
433313043STim.Haley@Sun.COM
433413043STim.Haley@Sun.COM fp = getf(zc->zc_cookie);
433513043STim.Haley@Sun.COM if (fp == NULL) {
433613043STim.Haley@Sun.COM dmu_objset_rele(fromsnap, FTAG);
433713043STim.Haley@Sun.COM dmu_objset_rele(tosnap, FTAG);
433813043STim.Haley@Sun.COM return (EBADF);
433913043STim.Haley@Sun.COM }
434013043STim.Haley@Sun.COM
434113043STim.Haley@Sun.COM off = fp->f_offset;
434213043STim.Haley@Sun.COM
434313043STim.Haley@Sun.COM error = dmu_diff(tosnap, fromsnap, fp->f_vnode, &off);
434413043STim.Haley@Sun.COM
434513043STim.Haley@Sun.COM if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
434613043STim.Haley@Sun.COM fp->f_offset = off;
434713043STim.Haley@Sun.COM releasef(zc->zc_cookie);
434813043STim.Haley@Sun.COM
434913043STim.Haley@Sun.COM dmu_objset_rele(fromsnap, FTAG);
435013043STim.Haley@Sun.COM dmu_objset_rele(tosnap, FTAG);
435113043STim.Haley@Sun.COM return (error);
435213043STim.Haley@Sun.COM }
435313043STim.Haley@Sun.COM
435413043STim.Haley@Sun.COM /*
43558845Samw@Sun.COM * Remove all ACL files in shares dir
43568845Samw@Sun.COM */
43578845Samw@Sun.COM static int
zfs_smb_acl_purge(znode_t * dzp)43588845Samw@Sun.COM zfs_smb_acl_purge(znode_t *dzp)
43598845Samw@Sun.COM {
43608845Samw@Sun.COM zap_cursor_t zc;
43618845Samw@Sun.COM zap_attribute_t zap;
43628845Samw@Sun.COM zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
43638845Samw@Sun.COM int error;
43648845Samw@Sun.COM
43658845Samw@Sun.COM for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
43668845Samw@Sun.COM (error = zap_cursor_retrieve(&zc, &zap)) == 0;
43678845Samw@Sun.COM zap_cursor_advance(&zc)) {
43688845Samw@Sun.COM if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
43698845Samw@Sun.COM NULL, 0)) != 0)
43708845Samw@Sun.COM break;
43718845Samw@Sun.COM }
43728845Samw@Sun.COM zap_cursor_fini(&zc);
43738845Samw@Sun.COM return (error);
43748845Samw@Sun.COM }
43758845Samw@Sun.COM
43768845Samw@Sun.COM static int
zfs_ioc_smb_acl(zfs_cmd_t * zc)43778845Samw@Sun.COM zfs_ioc_smb_acl(zfs_cmd_t *zc)
43788845Samw@Sun.COM {
43798845Samw@Sun.COM vnode_t *vp;
43808845Samw@Sun.COM znode_t *dzp;
43818845Samw@Sun.COM vnode_t *resourcevp = NULL;
43828845Samw@Sun.COM znode_t *sharedir;
43838845Samw@Sun.COM zfsvfs_t *zfsvfs;
43848845Samw@Sun.COM nvlist_t *nvlist;
43858845Samw@Sun.COM char *src, *target;
43868845Samw@Sun.COM vattr_t vattr;
43878845Samw@Sun.COM vsecattr_t vsec;
43888845Samw@Sun.COM int error = 0;
43898845Samw@Sun.COM
43908845Samw@Sun.COM if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
43918845Samw@Sun.COM NO_FOLLOW, NULL, &vp)) != 0)
43928845Samw@Sun.COM return (error);
43938845Samw@Sun.COM
43948845Samw@Sun.COM /* Now make sure mntpnt and dataset are ZFS */
43958845Samw@Sun.COM
43968845Samw@Sun.COM if (vp->v_vfsp->vfs_fstype != zfsfstype ||
43978845Samw@Sun.COM (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
43988845Samw@Sun.COM zc->zc_name) != 0)) {
43998845Samw@Sun.COM VN_RELE(vp);
44008845Samw@Sun.COM return (EINVAL);
44018845Samw@Sun.COM }
44028845Samw@Sun.COM
44038845Samw@Sun.COM dzp = VTOZ(vp);
44048845Samw@Sun.COM zfsvfs = dzp->z_zfsvfs;
44058845Samw@Sun.COM ZFS_ENTER(zfsvfs);
44068845Samw@Sun.COM
44079030SMark.Shellenbaum@Sun.COM /*
44089030SMark.Shellenbaum@Sun.COM * Create share dir if its missing.
44099030SMark.Shellenbaum@Sun.COM */
44109030SMark.Shellenbaum@Sun.COM mutex_enter(&zfsvfs->z_lock);
44119030SMark.Shellenbaum@Sun.COM if (zfsvfs->z_shares_dir == 0) {
44129030SMark.Shellenbaum@Sun.COM dmu_tx_t *tx;
44139030SMark.Shellenbaum@Sun.COM
44149030SMark.Shellenbaum@Sun.COM tx = dmu_tx_create(zfsvfs->z_os);
44159030SMark.Shellenbaum@Sun.COM dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
44169030SMark.Shellenbaum@Sun.COM ZFS_SHARES_DIR);
44179030SMark.Shellenbaum@Sun.COM dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
44189030SMark.Shellenbaum@Sun.COM error = dmu_tx_assign(tx, TXG_WAIT);
44199030SMark.Shellenbaum@Sun.COM if (error) {
44209030SMark.Shellenbaum@Sun.COM dmu_tx_abort(tx);
44219030SMark.Shellenbaum@Sun.COM } else {
44229030SMark.Shellenbaum@Sun.COM error = zfs_create_share_dir(zfsvfs, tx);
44239030SMark.Shellenbaum@Sun.COM dmu_tx_commit(tx);
44249030SMark.Shellenbaum@Sun.COM }
44259030SMark.Shellenbaum@Sun.COM if (error) {
44269030SMark.Shellenbaum@Sun.COM mutex_exit(&zfsvfs->z_lock);
44279030SMark.Shellenbaum@Sun.COM VN_RELE(vp);
44289030SMark.Shellenbaum@Sun.COM ZFS_EXIT(zfsvfs);
44299030SMark.Shellenbaum@Sun.COM return (error);
44309030SMark.Shellenbaum@Sun.COM }
44319030SMark.Shellenbaum@Sun.COM }
44329030SMark.Shellenbaum@Sun.COM mutex_exit(&zfsvfs->z_lock);
44339030SMark.Shellenbaum@Sun.COM
44349030SMark.Shellenbaum@Sun.COM ASSERT(zfsvfs->z_shares_dir);
44358845Samw@Sun.COM if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
44369030SMark.Shellenbaum@Sun.COM VN_RELE(vp);
44378845Samw@Sun.COM ZFS_EXIT(zfsvfs);
44388845Samw@Sun.COM return (error);
44398845Samw@Sun.COM }
44408845Samw@Sun.COM
44418845Samw@Sun.COM switch (zc->zc_cookie) {
44428845Samw@Sun.COM case ZFS_SMB_ACL_ADD:
44438845Samw@Sun.COM vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
44448845Samw@Sun.COM vattr.va_type = VREG;
44458845Samw@Sun.COM vattr.va_mode = S_IFREG|0777;
44468845Samw@Sun.COM vattr.va_uid = 0;
44478845Samw@Sun.COM vattr.va_gid = 0;
44488845Samw@Sun.COM
44498845Samw@Sun.COM vsec.vsa_mask = VSA_ACE;
44508845Samw@Sun.COM vsec.vsa_aclentp = &full_access;
44518845Samw@Sun.COM vsec.vsa_aclentsz = sizeof (full_access);
44528845Samw@Sun.COM vsec.vsa_aclcnt = 1;
44538845Samw@Sun.COM
44548845Samw@Sun.COM error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
44558845Samw@Sun.COM &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
44568845Samw@Sun.COM if (resourcevp)
44578845Samw@Sun.COM VN_RELE(resourcevp);
44588845Samw@Sun.COM break;
44598845Samw@Sun.COM
44608845Samw@Sun.COM case ZFS_SMB_ACL_REMOVE:
44618845Samw@Sun.COM error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
44628845Samw@Sun.COM NULL, 0);
44638845Samw@Sun.COM break;
44648845Samw@Sun.COM
44658845Samw@Sun.COM case ZFS_SMB_ACL_RENAME:
44668845Samw@Sun.COM if ((error = get_nvlist(zc->zc_nvlist_src,
44679643SEric.Taylor@Sun.COM zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
44688845Samw@Sun.COM VN_RELE(vp);
44698845Samw@Sun.COM ZFS_EXIT(zfsvfs);
44708845Samw@Sun.COM return (error);
44718845Samw@Sun.COM }
44728845Samw@Sun.COM if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
44738845Samw@Sun.COM nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
44748845Samw@Sun.COM &target)) {
44758845Samw@Sun.COM VN_RELE(vp);
44769179SMark.Shellenbaum@Sun.COM VN_RELE(ZTOV(sharedir));
44778845Samw@Sun.COM ZFS_EXIT(zfsvfs);
447811422SMark.Musante@Sun.COM nvlist_free(nvlist);
44798845Samw@Sun.COM return (error);
44808845Samw@Sun.COM }
44818845Samw@Sun.COM error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
44828845Samw@Sun.COM kcred, NULL, 0);
44838845Samw@Sun.COM nvlist_free(nvlist);
44848845Samw@Sun.COM break;
44858845Samw@Sun.COM
44868845Samw@Sun.COM case ZFS_SMB_ACL_PURGE:
44878845Samw@Sun.COM error = zfs_smb_acl_purge(sharedir);
44888845Samw@Sun.COM break;
44898845Samw@Sun.COM
44908845Samw@Sun.COM default:
44918845Samw@Sun.COM error = EINVAL;
44928845Samw@Sun.COM break;
44938845Samw@Sun.COM }
44948845Samw@Sun.COM
44958845Samw@Sun.COM VN_RELE(vp);
44968845Samw@Sun.COM VN_RELE(ZTOV(sharedir));
44978845Samw@Sun.COM
44988845Samw@Sun.COM ZFS_EXIT(zfsvfs);
44998845Samw@Sun.COM
45008845Samw@Sun.COM return (error);
45018845Samw@Sun.COM }
45028845Samw@Sun.COM
45034543Smarks /*
450410242Schris.kirby@sun.com * inputs:
450512527SChris.Kirby@oracle.com * zc_name name of filesystem
450612527SChris.Kirby@oracle.com * zc_value short name of snap
450712527SChris.Kirby@oracle.com * zc_string user-supplied tag for this hold
450812527SChris.Kirby@oracle.com * zc_cookie recursive flag
450912527SChris.Kirby@oracle.com * zc_temphold set if hold is temporary
451012527SChris.Kirby@oracle.com * zc_cleanup_fd cleanup-on-exit file descriptor for calling process
451112786SChris.Kirby@oracle.com * zc_sendobj if non-zero, the objid for zc_name@zc_value
451212786SChris.Kirby@oracle.com * zc_createtxg if zc_sendobj is non-zero, snap must have zc_createtxg
451310242Schris.kirby@sun.com *
451410242Schris.kirby@sun.com * outputs: none
451510242Schris.kirby@sun.com */
451610242Schris.kirby@sun.com static int
zfs_ioc_hold(zfs_cmd_t * zc)451710242Schris.kirby@sun.com zfs_ioc_hold(zfs_cmd_t *zc)
451810242Schris.kirby@sun.com {
451910242Schris.kirby@sun.com boolean_t recursive = zc->zc_cookie;
452012786SChris.Kirby@oracle.com spa_t *spa;
452112786SChris.Kirby@oracle.com dsl_pool_t *dp;
452212786SChris.Kirby@oracle.com dsl_dataset_t *ds;
452312786SChris.Kirby@oracle.com int error;
452412786SChris.Kirby@oracle.com minor_t minor = 0;
452510242Schris.kirby@sun.com
452610242Schris.kirby@sun.com if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
452710242Schris.kirby@sun.com return (EINVAL);
452810242Schris.kirby@sun.com
452912786SChris.Kirby@oracle.com if (zc->zc_sendobj == 0) {
453012786SChris.Kirby@oracle.com return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
453112786SChris.Kirby@oracle.com zc->zc_string, recursive, zc->zc_temphold,
453212786SChris.Kirby@oracle.com zc->zc_cleanup_fd));
453312786SChris.Kirby@oracle.com }
453412786SChris.Kirby@oracle.com
453512786SChris.Kirby@oracle.com if (recursive)
453612786SChris.Kirby@oracle.com return (EINVAL);
453712786SChris.Kirby@oracle.com
453812786SChris.Kirby@oracle.com error = spa_open(zc->zc_name, &spa, FTAG);
453912786SChris.Kirby@oracle.com if (error)
454012786SChris.Kirby@oracle.com return (error);
454112786SChris.Kirby@oracle.com
454212786SChris.Kirby@oracle.com dp = spa_get_dsl(spa);
454312786SChris.Kirby@oracle.com rw_enter(&dp->dp_config_rwlock, RW_READER);
454412786SChris.Kirby@oracle.com error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
454512786SChris.Kirby@oracle.com rw_exit(&dp->dp_config_rwlock);
454612786SChris.Kirby@oracle.com spa_close(spa, FTAG);
454712786SChris.Kirby@oracle.com if (error)
454812786SChris.Kirby@oracle.com return (error);
454912786SChris.Kirby@oracle.com
455012786SChris.Kirby@oracle.com /*
455112786SChris.Kirby@oracle.com * Until we have a hold on this snapshot, it's possible that
455212786SChris.Kirby@oracle.com * zc_sendobj could've been destroyed and reused as part
455312786SChris.Kirby@oracle.com * of a later txg. Make sure we're looking at the right object.
455412786SChris.Kirby@oracle.com */
455512786SChris.Kirby@oracle.com if (zc->zc_createtxg != ds->ds_phys->ds_creation_txg) {
455612786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
455712786SChris.Kirby@oracle.com return (ENOENT);
455812786SChris.Kirby@oracle.com }
455912786SChris.Kirby@oracle.com
456012786SChris.Kirby@oracle.com if (zc->zc_cleanup_fd != -1 && zc->zc_temphold) {
456112786SChris.Kirby@oracle.com error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor);
456212786SChris.Kirby@oracle.com if (error) {
456312786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
456412786SChris.Kirby@oracle.com return (error);
456512786SChris.Kirby@oracle.com }
456612786SChris.Kirby@oracle.com }
456712786SChris.Kirby@oracle.com
456812786SChris.Kirby@oracle.com error = dsl_dataset_user_hold_for_send(ds, zc->zc_string,
456912786SChris.Kirby@oracle.com zc->zc_temphold);
457012786SChris.Kirby@oracle.com if (minor != 0) {
457112786SChris.Kirby@oracle.com if (error == 0) {
457212786SChris.Kirby@oracle.com dsl_register_onexit_hold_cleanup(ds, zc->zc_string,
457312786SChris.Kirby@oracle.com minor);
457412786SChris.Kirby@oracle.com }
457512786SChris.Kirby@oracle.com zfs_onexit_fd_rele(zc->zc_cleanup_fd);
457612786SChris.Kirby@oracle.com }
457712786SChris.Kirby@oracle.com dsl_dataset_rele(ds, FTAG);
457812786SChris.Kirby@oracle.com
457912786SChris.Kirby@oracle.com return (error);
458010242Schris.kirby@sun.com }
458110242Schris.kirby@sun.com
458210242Schris.kirby@sun.com /*
458310242Schris.kirby@sun.com * inputs:
458412527SChris.Kirby@oracle.com * zc_name name of dataset from which we're releasing a user hold
458510242Schris.kirby@sun.com * zc_value short name of snap
458612527SChris.Kirby@oracle.com * zc_string user-supplied tag for this hold
458710242Schris.kirby@sun.com * zc_cookie recursive flag
458810242Schris.kirby@sun.com *
458912527SChris.Kirby@oracle.com * outputs: none
459010242Schris.kirby@sun.com */
459110242Schris.kirby@sun.com static int
zfs_ioc_release(zfs_cmd_t * zc)459210242Schris.kirby@sun.com zfs_ioc_release(zfs_cmd_t *zc)
459310242Schris.kirby@sun.com {
459410242Schris.kirby@sun.com boolean_t recursive = zc->zc_cookie;
459510242Schris.kirby@sun.com
459610242Schris.kirby@sun.com if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
459710242Schris.kirby@sun.com return (EINVAL);
459810242Schris.kirby@sun.com
459910242Schris.kirby@sun.com return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
460010242Schris.kirby@sun.com zc->zc_string, recursive));
460110242Schris.kirby@sun.com }
460210242Schris.kirby@sun.com
460310242Schris.kirby@sun.com /*
460410242Schris.kirby@sun.com * inputs:
460510242Schris.kirby@sun.com * zc_name name of filesystem
460610242Schris.kirby@sun.com *
460710242Schris.kirby@sun.com * outputs:
460810242Schris.kirby@sun.com * zc_nvlist_src{_size} nvlist of snapshot holds
460910242Schris.kirby@sun.com */
461010242Schris.kirby@sun.com static int
zfs_ioc_get_holds(zfs_cmd_t * zc)461110242Schris.kirby@sun.com zfs_ioc_get_holds(zfs_cmd_t *zc)
461210242Schris.kirby@sun.com {
461310242Schris.kirby@sun.com nvlist_t *nvp;
461410242Schris.kirby@sun.com int error;
461510242Schris.kirby@sun.com
461610242Schris.kirby@sun.com if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
461710242Schris.kirby@sun.com error = put_nvlist(zc, nvp);
461810242Schris.kirby@sun.com nvlist_free(nvp);
461910242Schris.kirby@sun.com }
462010242Schris.kirby@sun.com
462110242Schris.kirby@sun.com return (error);
462210242Schris.kirby@sun.com }
462310242Schris.kirby@sun.com
462410242Schris.kirby@sun.com /*
46254988Sek110237 * pool create, destroy, and export don't log the history as part of
46264988Sek110237 * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
46274988Sek110237 * do the logging of those commands.
46284543Smarks */
4629789Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
46309234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4631*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46329234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_destroy, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4633*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46349234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4635*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46369234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4637*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46389234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_configs, zfs_secpolicy_none, NO_NAME, B_FALSE,
4639*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46409234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
4641*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46429234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
4643*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
464412296SLin.Ling@Sun.COM { zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4645*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46469234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
4647*13049SGeorge.Wilson@Sun.COM POOL_CHECK_READONLY },
46489234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_upgrade, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4649*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46509234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4651*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46529234SGeorge.Wilson@Sun.COM { zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4653*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46549234SGeorge.Wilson@Sun.COM { zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4655*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46569234SGeorge.Wilson@Sun.COM { zfs_ioc_vdev_set_state, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4657*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46589234SGeorge.Wilson@Sun.COM { zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4659*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46609234SGeorge.Wilson@Sun.COM { zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4661*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46629234SGeorge.Wilson@Sun.COM { zfs_ioc_vdev_setpath, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4663*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46649425SEric.Schrock@Sun.COM { zfs_ioc_vdev_setfru, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4665*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46669234SGeorge.Wilson@Sun.COM { zfs_ioc_objset_stats, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4667*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED },
46689234SGeorge.Wilson@Sun.COM { zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4669*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46709234SGeorge.Wilson@Sun.COM { zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4671*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED },
46729234SGeorge.Wilson@Sun.COM { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4673*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED },
4674*13049SGeorge.Wilson@Sun.COM { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE,
4675*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4676*13049SGeorge.Wilson@Sun.COM { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE,
4677*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46789234SGeorge.Wilson@Sun.COM { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
4679*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46809234SGeorge.Wilson@Sun.COM { zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
4681*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4682*13049SGeorge.Wilson@Sun.COM { zfs_ioc_rename, zfs_secpolicy_rename, DATASET_NAME, B_TRUE,
4683*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4684*13049SGeorge.Wilson@Sun.COM { zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE,
4685*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4686*13049SGeorge.Wilson@Sun.COM { zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE,
4687*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46889234SGeorge.Wilson@Sun.COM { zfs_ioc_inject_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
4689*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46909234SGeorge.Wilson@Sun.COM { zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
4691*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46929234SGeorge.Wilson@Sun.COM { zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
4693*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46949234SGeorge.Wilson@Sun.COM { zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
4695*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
4696*13049SGeorge.Wilson@Sun.COM { zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4697*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
46989234SGeorge.Wilson@Sun.COM { zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
4699*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
470011314Swilliam.gorrell@sun.com { zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
4701*13049SGeorge.Wilson@Sun.COM B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47029234SGeorge.Wilson@Sun.COM { zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
4703*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
470413043STim.Haley@Sun.COM { zfs_ioc_dsobj_to_dsname, zfs_secpolicy_diff, POOL_NAME, B_FALSE,
4705*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
470613043STim.Haley@Sun.COM { zfs_ioc_obj_to_path, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
4707*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED },
47089234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_set_props, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4709*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47109234SGeorge.Wilson@Sun.COM { zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
4711*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
47129234SGeorge.Wilson@Sun.COM { zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
4713*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47149234SGeorge.Wilson@Sun.COM { zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4715*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
4716*13049SGeorge.Wilson@Sun.COM { zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE,
4717*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
47189234SGeorge.Wilson@Sun.COM { zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
4719*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47209234SGeorge.Wilson@Sun.COM { zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
4721*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
4722*13049SGeorge.Wilson@Sun.COM { zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, DATASET_NAME,
4723*13049SGeorge.Wilson@Sun.COM B_FALSE, POOL_CHECK_NONE },
4724*13049SGeorge.Wilson@Sun.COM { zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, DATASET_NAME,
4725*13049SGeorge.Wilson@Sun.COM B_FALSE, POOL_CHECK_NONE },
47269396SMatthew.Ahrens@Sun.COM { zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
4727*13049SGeorge.Wilson@Sun.COM DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4728*13049SGeorge.Wilson@Sun.COM { zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE,
4729*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
473010242Schris.kirby@sun.com { zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
4731*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
473210242Schris.kirby@sun.com { zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4733*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED },
473411022STom.Erickson@Sun.COM { zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4735*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
473611422SMark.Musante@Sun.COM { zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4737*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
473813043STim.Haley@Sun.COM { zfs_ioc_next_obj, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4739*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
4740*13049SGeorge.Wilson@Sun.COM { zfs_ioc_diff, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
4741*13049SGeorge.Wilson@Sun.COM POOL_CHECK_NONE },
474213043STim.Haley@Sun.COM { zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, DATASET_NAME,
4743*13049SGeorge.Wilson@Sun.COM B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
474413043STim.Haley@Sun.COM { zfs_ioc_obj_to_stats, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
4745*13049SGeorge.Wilson@Sun.COM POOL_CHECK_SUSPENDED }
4746789Sahrens };
4747789Sahrens
47489234SGeorge.Wilson@Sun.COM int
pool_status_check(const char * name,zfs_ioc_namecheck_t type,zfs_ioc_poolcheck_t check)4749*13049SGeorge.Wilson@Sun.COM pool_status_check(const char *name, zfs_ioc_namecheck_t type,
4750*13049SGeorge.Wilson@Sun.COM zfs_ioc_poolcheck_t check)
47519234SGeorge.Wilson@Sun.COM {
47529234SGeorge.Wilson@Sun.COM spa_t *spa;
47539234SGeorge.Wilson@Sun.COM int error;
47549234SGeorge.Wilson@Sun.COM
47559234SGeorge.Wilson@Sun.COM ASSERT(type == POOL_NAME || type == DATASET_NAME);
47569234SGeorge.Wilson@Sun.COM
4757*13049SGeorge.Wilson@Sun.COM if (check & POOL_CHECK_NONE)
4758*13049SGeorge.Wilson@Sun.COM return (0);
4759*13049SGeorge.Wilson@Sun.COM
47609396SMatthew.Ahrens@Sun.COM error = spa_open(name, &spa, FTAG);
47619234SGeorge.Wilson@Sun.COM if (error == 0) {
4762*13049SGeorge.Wilson@Sun.COM if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa))
47639234SGeorge.Wilson@Sun.COM error = EAGAIN;
4764*13049SGeorge.Wilson@Sun.COM else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa))
4765*13049SGeorge.Wilson@Sun.COM error = EROFS;
47669234SGeorge.Wilson@Sun.COM spa_close(spa, FTAG);
47679234SGeorge.Wilson@Sun.COM }
47689234SGeorge.Wilson@Sun.COM return (error);
47699234SGeorge.Wilson@Sun.COM }
47709234SGeorge.Wilson@Sun.COM
477112527SChris.Kirby@oracle.com /*
477212527SChris.Kirby@oracle.com * Find a free minor number.
477312527SChris.Kirby@oracle.com */
477412527SChris.Kirby@oracle.com minor_t
zfsdev_minor_alloc(void)477512527SChris.Kirby@oracle.com zfsdev_minor_alloc(void)
477612527SChris.Kirby@oracle.com {
477712527SChris.Kirby@oracle.com static minor_t last_minor;
477812527SChris.Kirby@oracle.com minor_t m;
477912527SChris.Kirby@oracle.com
478012527SChris.Kirby@oracle.com ASSERT(MUTEX_HELD(&zfsdev_state_lock));
478112527SChris.Kirby@oracle.com
478212527SChris.Kirby@oracle.com for (m = last_minor + 1; m != last_minor; m++) {
478312527SChris.Kirby@oracle.com if (m > ZFSDEV_MAX_MINOR)
478412527SChris.Kirby@oracle.com m = 1;
478512527SChris.Kirby@oracle.com if (ddi_get_soft_state(zfsdev_state, m) == NULL) {
478612527SChris.Kirby@oracle.com last_minor = m;
478712527SChris.Kirby@oracle.com return (m);
478812527SChris.Kirby@oracle.com }
478912527SChris.Kirby@oracle.com }
479012527SChris.Kirby@oracle.com
479112527SChris.Kirby@oracle.com return (0);
479212527SChris.Kirby@oracle.com }
479312527SChris.Kirby@oracle.com
479412527SChris.Kirby@oracle.com static int
zfs_ctldev_init(dev_t * devp)479512527SChris.Kirby@oracle.com zfs_ctldev_init(dev_t *devp)
479612527SChris.Kirby@oracle.com {
479712527SChris.Kirby@oracle.com minor_t minor;
479812527SChris.Kirby@oracle.com zfs_soft_state_t *zs;
479912527SChris.Kirby@oracle.com
480012527SChris.Kirby@oracle.com ASSERT(MUTEX_HELD(&zfsdev_state_lock));
480112527SChris.Kirby@oracle.com ASSERT(getminor(*devp) == 0);
480212527SChris.Kirby@oracle.com
480312527SChris.Kirby@oracle.com minor = zfsdev_minor_alloc();
480412527SChris.Kirby@oracle.com if (minor == 0)
480512527SChris.Kirby@oracle.com return (ENXIO);
480612527SChris.Kirby@oracle.com
480712527SChris.Kirby@oracle.com if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS)
480812527SChris.Kirby@oracle.com return (EAGAIN);
480912527SChris.Kirby@oracle.com
481012527SChris.Kirby@oracle.com *devp = makedevice(getemajor(*devp), minor);
481112527SChris.Kirby@oracle.com
481212527SChris.Kirby@oracle.com zs = ddi_get_soft_state(zfsdev_state, minor);
481312527SChris.Kirby@oracle.com zs->zss_type = ZSST_CTLDEV;
481412527SChris.Kirby@oracle.com zfs_onexit_init((zfs_onexit_t **)&zs->zss_data);
481512527SChris.Kirby@oracle.com
481612527SChris.Kirby@oracle.com return (0);
481712527SChris.Kirby@oracle.com }
481812527SChris.Kirby@oracle.com
481912527SChris.Kirby@oracle.com static void
zfs_ctldev_destroy(zfs_onexit_t * zo,minor_t minor)482012527SChris.Kirby@oracle.com zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor)
482112527SChris.Kirby@oracle.com {
482212527SChris.Kirby@oracle.com ASSERT(MUTEX_HELD(&zfsdev_state_lock));
482312527SChris.Kirby@oracle.com
482412527SChris.Kirby@oracle.com zfs_onexit_destroy(zo);
482512527SChris.Kirby@oracle.com ddi_soft_state_free(zfsdev_state, minor);
482612527SChris.Kirby@oracle.com }
482712527SChris.Kirby@oracle.com
482812527SChris.Kirby@oracle.com void *
zfsdev_get_soft_state(minor_t minor,enum zfs_soft_state_type which)482912527SChris.Kirby@oracle.com zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which)
483012527SChris.Kirby@oracle.com {
483112527SChris.Kirby@oracle.com zfs_soft_state_t *zp;
483212527SChris.Kirby@oracle.com
483312527SChris.Kirby@oracle.com zp = ddi_get_soft_state(zfsdev_state, minor);
483412527SChris.Kirby@oracle.com if (zp == NULL || zp->zss_type != which)
483512527SChris.Kirby@oracle.com return (NULL);
483612527SChris.Kirby@oracle.com
483712527SChris.Kirby@oracle.com return (zp->zss_data);
483812527SChris.Kirby@oracle.com }
483912527SChris.Kirby@oracle.com
484012527SChris.Kirby@oracle.com static int
zfsdev_open(dev_t * devp,int flag,int otyp,cred_t * cr)484112527SChris.Kirby@oracle.com zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr)
484212527SChris.Kirby@oracle.com {
484312527SChris.Kirby@oracle.com int error = 0;
484412527SChris.Kirby@oracle.com
484512527SChris.Kirby@oracle.com if (getminor(*devp) != 0)
484612527SChris.Kirby@oracle.com return (zvol_open(devp, flag, otyp, cr));
484712527SChris.Kirby@oracle.com
484812527SChris.Kirby@oracle.com /* This is the control device. Allocate a new minor if requested. */
484912527SChris.Kirby@oracle.com if (flag & FEXCL) {
485012527SChris.Kirby@oracle.com mutex_enter(&zfsdev_state_lock);
485112527SChris.Kirby@oracle.com error = zfs_ctldev_init(devp);
485212527SChris.Kirby@oracle.com mutex_exit(&zfsdev_state_lock);
485312527SChris.Kirby@oracle.com }
485412527SChris.Kirby@oracle.com
485512527SChris.Kirby@oracle.com return (error);
485612527SChris.Kirby@oracle.com }
485712527SChris.Kirby@oracle.com
485812527SChris.Kirby@oracle.com static int
zfsdev_close(dev_t dev,int flag,int otyp,cred_t * cr)485912527SChris.Kirby@oracle.com zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr)
486012527SChris.Kirby@oracle.com {
486112527SChris.Kirby@oracle.com zfs_onexit_t *zo;
486212527SChris.Kirby@oracle.com minor_t minor = getminor(dev);
486312527SChris.Kirby@oracle.com
486412527SChris.Kirby@oracle.com if (minor == 0)
486512527SChris.Kirby@oracle.com return (0);
486612527SChris.Kirby@oracle.com
486712527SChris.Kirby@oracle.com mutex_enter(&zfsdev_state_lock);
486812527SChris.Kirby@oracle.com zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
486912527SChris.Kirby@oracle.com if (zo == NULL) {
487012527SChris.Kirby@oracle.com mutex_exit(&zfsdev_state_lock);
487112527SChris.Kirby@oracle.com return (zvol_close(dev, flag, otyp, cr));
487212527SChris.Kirby@oracle.com }
487312527SChris.Kirby@oracle.com zfs_ctldev_destroy(zo, minor);
487412527SChris.Kirby@oracle.com mutex_exit(&zfsdev_state_lock);
487512527SChris.Kirby@oracle.com
487612527SChris.Kirby@oracle.com return (0);
487712527SChris.Kirby@oracle.com }
487812527SChris.Kirby@oracle.com
4879789Sahrens static int
zfsdev_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cr,int * rvalp)4880789Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4881789Sahrens {
4882789Sahrens zfs_cmd_t *zc;
4883789Sahrens uint_t vec;
48842199Sahrens int error, rc;
488512527SChris.Kirby@oracle.com minor_t minor = getminor(dev);
488612527SChris.Kirby@oracle.com
488712527SChris.Kirby@oracle.com if (minor != 0 &&
488812527SChris.Kirby@oracle.com zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL)
4889789Sahrens return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4890789Sahrens
4891789Sahrens vec = cmd - ZFS_IOC;
48924787Sahrens ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4893789Sahrens
4894789Sahrens if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4895789Sahrens return (EINVAL);
4896789Sahrens
4897789Sahrens zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4898789Sahrens
48999643SEric.Taylor@Sun.COM error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
490011807SSam.Falkner@Sun.COM if (error != 0)
490111807SSam.Falkner@Sun.COM error = EFAULT;
4902789Sahrens
490310588SEric.Taylor@Sun.COM if ((error == 0) && !(flag & FKIOCTL))
49044543Smarks error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4905789Sahrens
4906789Sahrens /*
4907789Sahrens * Ensure that all pool/dataset names are valid before we pass down to
4908789Sahrens * the lower layers.
4909789Sahrens */
4910789Sahrens if (error == 0) {
4911789Sahrens zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
49129643SEric.Taylor@Sun.COM zc->zc_iflags = flag & FKIOCTL;
4913789Sahrens switch (zfs_ioc_vec[vec].zvec_namecheck) {
49144577Sahrens case POOL_NAME:
4915789Sahrens if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4916789Sahrens error = EINVAL;
4917*13049SGeorge.Wilson@Sun.COM error = pool_status_check(zc->zc_name,
4918*13049SGeorge.Wilson@Sun.COM zfs_ioc_vec[vec].zvec_namecheck,
4919*13049SGeorge.Wilson@Sun.COM zfs_ioc_vec[vec].zvec_pool_check);
4920789Sahrens break;
4921789Sahrens
49224577Sahrens case DATASET_NAME:
4923789Sahrens if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4924789Sahrens error = EINVAL;
4925*13049SGeorge.Wilson@Sun.COM error = pool_status_check(zc->zc_name,
4926*13049SGeorge.Wilson@Sun.COM zfs_ioc_vec[vec].zvec_namecheck,
4927*13049SGeorge.Wilson@Sun.COM zfs_ioc_vec[vec].zvec_pool_check);
4928789Sahrens break;
49292856Snd150628
49304577Sahrens case NO_NAME:
49312856Snd150628 break;
4932789Sahrens }
4933789Sahrens }
4934789Sahrens
4935789Sahrens if (error == 0)
4936789Sahrens error = zfs_ioc_vec[vec].zvec_func(zc);
4937789Sahrens
49389643SEric.Taylor@Sun.COM rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
49394543Smarks if (error == 0) {
494011807SSam.Falkner@Sun.COM if (rc != 0)
494111807SSam.Falkner@Sun.COM error = EFAULT;
49429396SMatthew.Ahrens@Sun.COM if (zfs_ioc_vec[vec].zvec_his_log)
49434543Smarks zfs_log_history(zc);
49444543Smarks }
4945789Sahrens
4946789Sahrens kmem_free(zc, sizeof (zfs_cmd_t));
4947789Sahrens return (error);
4948789Sahrens }
4949789Sahrens
4950789Sahrens static int
zfs_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4951789Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4952789Sahrens {
4953789Sahrens if (cmd != DDI_ATTACH)
4954789Sahrens return (DDI_FAILURE);
4955789Sahrens
4956789Sahrens if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4957789Sahrens DDI_PSEUDO, 0) == DDI_FAILURE)
4958789Sahrens return (DDI_FAILURE);
4959789Sahrens
4960789Sahrens zfs_dip = dip;
4961789Sahrens
4962789Sahrens ddi_report_dev(dip);
4963789Sahrens
4964789Sahrens return (DDI_SUCCESS);
4965789Sahrens }
4966789Sahrens
4967789Sahrens static int
zfs_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4968789Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4969789Sahrens {
4970789Sahrens if (spa_busy() || zfs_busy() || zvol_busy())
4971789Sahrens return (DDI_FAILURE);
4972789Sahrens
4973789Sahrens if (cmd != DDI_DETACH)
4974789Sahrens return (DDI_FAILURE);
4975789Sahrens
4976789Sahrens zfs_dip = NULL;
4977789Sahrens
4978789Sahrens ddi_prop_remove_all(dip);
4979789Sahrens ddi_remove_minor_node(dip, NULL);
4980789Sahrens
4981789Sahrens return (DDI_SUCCESS);
4982789Sahrens }
4983789Sahrens
4984789Sahrens /*ARGSUSED*/
4985789Sahrens static int
zfs_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)4986789Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4987789Sahrens {
4988789Sahrens switch (infocmd) {
4989789Sahrens case DDI_INFO_DEVT2DEVINFO:
4990789Sahrens *result = zfs_dip;
4991789Sahrens return (DDI_SUCCESS);
4992789Sahrens
4993789Sahrens case DDI_INFO_DEVT2INSTANCE:
4994849Sbonwick *result = (void *)0;
4995789Sahrens return (DDI_SUCCESS);
4996789Sahrens }
4997789Sahrens
4998789Sahrens return (DDI_FAILURE);
4999789Sahrens }
5000789Sahrens
5001789Sahrens /*
5002789Sahrens * OK, so this is a little weird.
5003789Sahrens *
5004789Sahrens * /dev/zfs is the control node, i.e. minor 0.
5005789Sahrens * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
5006789Sahrens *
5007789Sahrens * /dev/zfs has basically nothing to do except serve up ioctls,
5008789Sahrens * so most of the standard driver entry points are in zvol.c.
5009789Sahrens */
5010789Sahrens static struct cb_ops zfs_cb_ops = {
501112527SChris.Kirby@oracle.com zfsdev_open, /* open */
501212527SChris.Kirby@oracle.com zfsdev_close, /* close */
5013789Sahrens zvol_strategy, /* strategy */
5014789Sahrens nodev, /* print */
50156423Sgw25295 zvol_dump, /* dump */
5016789Sahrens zvol_read, /* read */
5017789Sahrens zvol_write, /* write */
5018789Sahrens zfsdev_ioctl, /* ioctl */
5019789Sahrens nodev, /* devmap */
5020789Sahrens nodev, /* mmap */
5021789Sahrens nodev, /* segmap */
5022789Sahrens nochpoll, /* poll */
5023789Sahrens ddi_prop_op, /* prop_op */
5024789Sahrens NULL, /* streamtab */
5025789Sahrens D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */
5026789Sahrens CB_REV, /* version */
50273638Sbillm nodev, /* async read */
50283638Sbillm nodev, /* async write */
5029789Sahrens };
5030789Sahrens
5031789Sahrens static struct dev_ops zfs_dev_ops = {
5032789Sahrens DEVO_REV, /* version */
5033789Sahrens 0, /* refcnt */
5034789Sahrens zfs_info, /* info */
5035789Sahrens nulldev, /* identify */
5036789Sahrens nulldev, /* probe */
5037789Sahrens zfs_attach, /* attach */
5038789Sahrens zfs_detach, /* detach */
5039789Sahrens nodev, /* reset */
5040789Sahrens &zfs_cb_ops, /* driver operations */
50417656SSherry.Moore@Sun.COM NULL, /* no bus operations */
50427656SSherry.Moore@Sun.COM NULL, /* power */
50437656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
5044789Sahrens };
5045789Sahrens
5046789Sahrens static struct modldrv zfs_modldrv = {
50477656SSherry.Moore@Sun.COM &mod_driverops,
50487656SSherry.Moore@Sun.COM "ZFS storage pool",
50497656SSherry.Moore@Sun.COM &zfs_dev_ops
5050789Sahrens };
5051789Sahrens
5052789Sahrens static struct modlinkage modlinkage = {
5053789Sahrens MODREV_1,
5054789Sahrens (void *)&zfs_modlfs,
5055789Sahrens (void *)&zfs_modldrv,
5056789Sahrens NULL
5057789Sahrens };
5058789Sahrens
50594720Sfr157268
50604720Sfr157268 uint_t zfs_fsyncer_key;
50615326Sek110237 extern uint_t rrw_tsd_key;
50624720Sfr157268
5063789Sahrens int
_init(void)5064789Sahrens _init(void)
5065789Sahrens {
5066789Sahrens int error;
5067789Sahrens
5068849Sbonwick spa_init(FREAD | FWRITE);
5069849Sbonwick zfs_init();
5070849Sbonwick zvol_init();
5071849Sbonwick
5072849Sbonwick if ((error = mod_install(&modlinkage)) != 0) {
5073849Sbonwick zvol_fini();
5074849Sbonwick zfs_fini();
5075849Sbonwick spa_fini();
5076789Sahrens return (error);
5077849Sbonwick }
5078789Sahrens
50794720Sfr157268 tsd_create(&zfs_fsyncer_key, NULL);
50805326Sek110237 tsd_create(&rrw_tsd_key, NULL);
50814720Sfr157268
5082789Sahrens error = ldi_ident_from_mod(&modlinkage, &zfs_li);
5083789Sahrens ASSERT(error == 0);
50844543Smarks mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
5085789Sahrens
5086789Sahrens return (0);
5087789Sahrens }
5088789Sahrens
5089789Sahrens int
_fini(void)5090789Sahrens _fini(void)
5091789Sahrens {
5092789Sahrens int error;
5093789Sahrens
50941544Seschrock if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
5095789Sahrens return (EBUSY);
5096789Sahrens
5097789Sahrens if ((error = mod_remove(&modlinkage)) != 0)
5098789Sahrens return (error);
5099789Sahrens
5100789Sahrens zvol_fini();
5101789Sahrens zfs_fini();
5102789Sahrens spa_fini();
51035331Samw if (zfs_nfsshare_inited)
51044543Smarks (void) ddi_modclose(nfs_mod);
51055331Samw if (zfs_smbshare_inited)
51065331Samw (void) ddi_modclose(smbsrv_mod);
51075331Samw if (zfs_nfsshare_inited || zfs_smbshare_inited)
51084543Smarks (void) ddi_modclose(sharefs_mod);
5109789Sahrens
51104720Sfr157268 tsd_destroy(&zfs_fsyncer_key);
5111789Sahrens ldi_ident_release(zfs_li);
5112789Sahrens zfs_li = NULL;
51134543Smarks mutex_destroy(&zfs_share_lock);
5114789Sahrens
5115789Sahrens return (error);
5116789Sahrens }
5117789Sahrens
5118789Sahrens int
_info(struct modinfo * modinfop)5119789Sahrens _info(struct modinfo *modinfop)
5120789Sahrens {
5121789Sahrens return (mod_info(&modlinkage, modinfop));
5122789Sahrens }
5123