zfs_ioctl.c revision 168775
1168404Spjd/*
2168404Spjd * CDDL HEADER START
3168404Spjd *
4168404Spjd * The contents of this file are subject to the terms of the
5168404Spjd * Common Development and Distribution License (the "License").
6168404Spjd * You may not use this file except in compliance with the License.
7168404Spjd *
8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9168404Spjd * or http://www.opensolaris.org/os/licensing.
10168404Spjd * See the License for the specific language governing permissions
11168404Spjd * and limitations under the License.
12168404Spjd *
13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each
14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15168404Spjd * If applicable, add the following below this CDDL HEADER, with the
16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18168404Spjd *
19168404Spjd * CDDL HEADER END
20168404Spjd */
21168404Spjd/*
22168404Spjd * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23168404Spjd * Use is subject to license terms.
24168404Spjd */
25168404Spjd
26168404Spjd#pragma ident	"%Z%%M%	%I%	%E% SMI"
27168404Spjd
28168404Spjd#include <sys/param.h>
29168404Spjd#include <sys/systm.h>
30168404Spjd#include <sys/conf.h>
31168404Spjd#include <sys/kernel.h>
32168404Spjd#include <sys/lock.h>
33168404Spjd#include <sys/malloc.h>
34168404Spjd#include <sys/mutex.h>
35168404Spjd#include <sys/proc.h>
36168404Spjd#include <sys/errno.h>
37168404Spjd#include <sys/uio.h>
38168404Spjd#include <sys/file.h>
39168404Spjd#include <sys/kmem.h>
40168404Spjd#include <sys/conf.h>
41168404Spjd#include <sys/cmn_err.h>
42168404Spjd#include <sys/stat.h>
43168404Spjd#include <sys/zfs_ioctl.h>
44168404Spjd#include <sys/zap.h>
45168404Spjd#include <sys/spa.h>
46168404Spjd#include <sys/spa_impl.h>
47168404Spjd#include <sys/vdev.h>
48168404Spjd#include <sys/vdev_impl.h>
49168404Spjd#include <sys/dmu.h>
50168404Spjd#include <sys/dsl_dir.h>
51168404Spjd#include <sys/dsl_dataset.h>
52168404Spjd#include <sys/dsl_prop.h>
53168404Spjd#include <sys/nvpair.h>
54168404Spjd#include <sys/mount.h>
55168404Spjd#include <sys/taskqueue.h>
56168404Spjd#include <sys/sdt.h>
57168404Spjd#include <sys/varargs.h>
58168404Spjd#include <sys/fs/zfs.h>
59168404Spjd#include <sys/zfs_ctldir.h>
60168404Spjd#include <sys/zvol.h>
61168404Spjd
62168404Spjd#include "zfs_namecheck.h"
63168404Spjd#include "zfs_prop.h"
64168404Spjd
65168404SpjdCTASSERT(sizeof(zfs_cmd_t) <= PAGE_SIZE);
66168404Spjd
67168404Spjdstatic struct cdev *zfsdev;
68168404Spjd
69168404Spjdextern void zfs_init(void);
70168404Spjdextern void zfs_fini(void);
71168404Spjd
72168404Spjdtypedef int zfs_ioc_func_t(zfs_cmd_t *);
73168404Spjdtypedef int zfs_secpolicy_func_t(const char *, cred_t *);
74168404Spjd
75168404Spjdtypedef struct zfs_ioc_vec {
76168404Spjd	zfs_ioc_func_t		*zvec_func;
77168404Spjd	zfs_secpolicy_func_t	*zvec_secpolicy;
78168404Spjd	enum {
79168404Spjd		no_name,
80168404Spjd		pool_name,
81168404Spjd		dataset_name
82168404Spjd	}			zvec_namecheck;
83168404Spjd} zfs_ioc_vec_t;
84168404Spjd
85168404Spjd/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
86168404Spjdvoid
87168404Spjd__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
88168404Spjd{
89168404Spjd	const char *newfile;
90168404Spjd	char buf[256];
91168404Spjd	va_list adx;
92168404Spjd
93168404Spjd	/*
94168404Spjd	 * Get rid of annoying "../common/" prefix to filename.
95168404Spjd	 */
96168404Spjd	newfile = strrchr(file, '/');
97168404Spjd	if (newfile != NULL) {
98168404Spjd		newfile = newfile + 1; /* Get rid of leading / */
99168404Spjd	} else {
100168404Spjd		newfile = file;
101168404Spjd	}
102168404Spjd
103168404Spjd	va_start(adx, fmt);
104168404Spjd	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
105168404Spjd	va_end(adx);
106168404Spjd
107168404Spjd	/*
108168404Spjd	 * To get this data, use the zfs-dprintf probe as so:
109168404Spjd	 * dtrace -q -n 'zfs-dprintf \
110168404Spjd	 *	/stringof(arg0) == "dbuf.c"/ \
111168404Spjd	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
112168404Spjd	 * arg0 = file name
113168404Spjd	 * arg1 = function name
114168404Spjd	 * arg2 = line number
115168404Spjd	 * arg3 = message
116168404Spjd	 */
117168404Spjd	DTRACE_PROBE4(zfs__dprintf,
118168404Spjd	    char *, newfile, char *, func, int, line, char *, buf);
119168404Spjd}
120168404Spjd
121168404Spjd/*
122168404Spjd * Policy for top-level read operations (list pools).  Requires no privileges,
123168404Spjd * and can be used in the local zone, as there is no associated dataset.
124168404Spjd */
125168404Spjd/* ARGSUSED */
126168404Spjdstatic int
127168404Spjdzfs_secpolicy_none(const char *unused1, cred_t *cr)
128168404Spjd{
129168404Spjd	return (0);
130168404Spjd}
131168404Spjd
132168404Spjd/*
133168404Spjd * Policy for dataset read operations (list children, get statistics).  Requires
134168404Spjd * no privileges, but must be visible in the local zone.
135168404Spjd */
136168404Spjd/* ARGSUSED */
137168404Spjdstatic int
138168404Spjdzfs_secpolicy_read(const char *dataset, cred_t *cr)
139168404Spjd{
140168404Spjd	if (INGLOBALZONE(curproc) ||
141168404Spjd	    zone_dataset_visible(dataset, NULL))
142168404Spjd		return (0);
143168404Spjd
144168404Spjd	return (ENOENT);
145168404Spjd}
146168404Spjd
147168404Spjdstatic int
148168404Spjdzfs_dozonecheck(const char *dataset, cred_t *cr)
149168404Spjd{
150168404Spjd	uint64_t zoned;
151168404Spjd	int writable = 1;
152168404Spjd
153168404Spjd	/*
154168404Spjd	 * The dataset must be visible by this zone -- check this first
155168404Spjd	 * so they don't see EPERM on something they shouldn't know about.
156168404Spjd	 */
157168404Spjd	if (!INGLOBALZONE(curproc) &&
158168404Spjd	    !zone_dataset_visible(dataset, &writable))
159168404Spjd		return (ENOENT);
160168404Spjd
161168404Spjd	if (dsl_prop_get_integer(dataset, "jailed", &zoned, NULL))
162168404Spjd		return (ENOENT);
163168404Spjd
164168404Spjd	if (INGLOBALZONE(curproc)) {
165168404Spjd		/*
166168404Spjd		 * If the fs is zoned, only root can access it from the
167168404Spjd		 * global zone.
168168404Spjd		 */
169168404Spjd		if (secpolicy_zfs(cr) && zoned)
170168404Spjd			return (EPERM);
171168404Spjd	} else {
172168404Spjd		/*
173168404Spjd		 * If we are in a local zone, the 'zoned' property must be set.
174168404Spjd		 */
175168404Spjd		if (!zoned)
176168404Spjd			return (EPERM);
177168404Spjd
178168404Spjd		/* must be writable by this zone */
179168404Spjd		if (!writable)
180168404Spjd			return (EPERM);
181168404Spjd	}
182168404Spjd	return (0);
183168404Spjd}
184168404Spjd
185168404Spjd/*
186168404Spjd * Policy for dataset write operations (create children, set properties, etc).
187168404Spjd * Requires SYS_MOUNT privilege, and must be writable in the local zone.
188168404Spjd */
189168404Spjdint
190168404Spjdzfs_secpolicy_write(const char *dataset, cred_t *cr)
191168404Spjd{
192168404Spjd	int error;
193168404Spjd
194168404Spjd	if (error = zfs_dozonecheck(dataset, cr))
195168404Spjd		return (error);
196168404Spjd
197168404Spjd	return (secpolicy_zfs(cr));
198168404Spjd}
199168404Spjd
200168404Spjd/*
201168404Spjd * Policy for operations that want to write a dataset's parent:
202168404Spjd * create, destroy, snapshot, clone, restore.
203168404Spjd */
204168404Spjdstatic int
205168404Spjdzfs_secpolicy_parent(const char *dataset, cred_t *cr)
206168404Spjd{
207168404Spjd	char parentname[MAXNAMELEN];
208168404Spjd	char *cp;
209168404Spjd
210168404Spjd	/*
211168404Spjd	 * Remove the @bla or /bla from the end of the name to get the parent.
212168404Spjd	 */
213168404Spjd	(void) strncpy(parentname, dataset, sizeof (parentname));
214168404Spjd	cp = strrchr(parentname, '@');
215168404Spjd	if (cp != NULL) {
216168404Spjd		cp[0] = '\0';
217168404Spjd	} else {
218168404Spjd		cp = strrchr(parentname, '/');
219168404Spjd		if (cp == NULL)
220168404Spjd			return (ENOENT);
221168404Spjd		cp[0] = '\0';
222168404Spjd
223168404Spjd	}
224168404Spjd
225168404Spjd	return (zfs_secpolicy_write(parentname, cr));
226168404Spjd}
227168404Spjd
228168404Spjd/*
229168404Spjd * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
230168404Spjd * SYS_CONFIG privilege, which is not available in a local zone.
231168404Spjd */
232168404Spjd/* ARGSUSED */
233168404Spjdstatic int
234168404Spjdzfs_secpolicy_config(const char *unused, cred_t *cr)
235168404Spjd{
236168404Spjd	if (secpolicy_sys_config(cr, B_FALSE) != 0)
237168404Spjd		return (EPERM);
238168404Spjd
239168404Spjd	return (0);
240168404Spjd}
241168404Spjd
242168404Spjd/*
243168404Spjd * Policy for fault injection.  Requires all privileges.
244168404Spjd */
245168404Spjd/* ARGSUSED */
246168404Spjdstatic int
247168404Spjdzfs_secpolicy_inject(const char *unused, cred_t *cr)
248168404Spjd{
249168404Spjd	return (secpolicy_zinject(cr));
250168404Spjd}
251168404Spjd
252168404Spjd/*
253168404Spjd * Policy for dataset backup operations (sendbackup).
254168404Spjd * Requires SYS_MOUNT privilege, and must be writable in the local zone.
255168404Spjd */
256168404Spjdstatic int
257168404Spjdzfs_secpolicy_operator(const char *dataset, cred_t *cr)
258168404Spjd{
259168404Spjd	int writable = 1;
260168404Spjd
261168404Spjd	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(dataset, &writable))
262168404Spjd		return (ENOENT);
263168404Spjd	if (secpolicy_zfs(cr) != 0 && !groupmember(GID_OPERATOR, cr))
264168404Spjd		return (EPERM);
265168404Spjd	return (0);
266168404Spjd}
267168404Spjd
268168404Spjd/*
269168404Spjd * Returns the nvlist as specified by the user in the zfs_cmd_t.
270168404Spjd */
271168404Spjdstatic int
272168404Spjdget_nvlist(zfs_cmd_t *zc, nvlist_t **nvp)
273168404Spjd{
274168404Spjd	char *packed;
275168404Spjd	size_t size;
276168404Spjd	int error;
277168404Spjd	nvlist_t *config = NULL;
278168404Spjd
279168404Spjd	/*
280168404Spjd	 * Read in and unpack the user-supplied nvlist.
281168404Spjd	 */
282168404Spjd	if ((size = zc->zc_nvlist_src_size) == 0)
283168404Spjd		return (EINVAL);
284168404Spjd
285168404Spjd	packed = kmem_alloc(size, KM_SLEEP);
286168404Spjd
287168404Spjd	if ((error = xcopyin((void *)(uintptr_t)zc->zc_nvlist_src, packed,
288168404Spjd	    size)) != 0) {
289168404Spjd		kmem_free(packed, size);
290168404Spjd		return (error);
291168404Spjd	}
292168404Spjd
293168404Spjd	if ((error = nvlist_unpack(packed, size, &config, 0)) != 0) {
294168404Spjd		kmem_free(packed, size);
295168404Spjd		return (error);
296168404Spjd	}
297168404Spjd
298168404Spjd	kmem_free(packed, size);
299168404Spjd
300168404Spjd	*nvp = config;
301168404Spjd	return (0);
302168404Spjd}
303168404Spjd
304168404Spjdstatic int
305168404Spjdput_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
306168404Spjd{
307168404Spjd	char *packed = NULL;
308168404Spjd	size_t size;
309168404Spjd	int error;
310168404Spjd
311168404Spjd	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
312168404Spjd
313168404Spjd	if (size > zc->zc_nvlist_dst_size) {
314168404Spjd		/*
315168404Spjd		 * Solaris returns ENOMEM here, because even if an error is
316168404Spjd		 * returned from an ioctl(2), new zc_nvlist_dst_size will be
317168404Spjd		 * passed to the userland. This is not the case for FreeBSD.
318168404Spjd		 * We need to return 0, so the kernel will copy the
319168404Spjd		 * zc_nvlist_dst_size back and the userland can discover that a
320168404Spjd		 * bigger buffer is needed.
321168404Spjd		 */
322168404Spjd		error = 0;
323168404Spjd	} else {
324168404Spjd		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
325168404Spjd		    KM_SLEEP) == 0);
326168404Spjd		error = xcopyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
327168404Spjd		    size);
328168404Spjd		kmem_free(packed, size);
329168404Spjd	}
330168404Spjd
331168404Spjd	zc->zc_nvlist_dst_size = size;
332168404Spjd	return (error);
333168404Spjd}
334168404Spjd
335168404Spjdstatic int
336168404Spjdzfs_ioc_pool_create(zfs_cmd_t *zc)
337168404Spjd{
338168404Spjd	int error;
339168404Spjd	nvlist_t *config;
340168404Spjd
341168404Spjd	if ((error = get_nvlist(zc, &config)) != 0)
342168404Spjd		return (error);
343168404Spjd
344168404Spjd	error = spa_create(zc->zc_name, config, zc->zc_value[0] == '\0' ?
345168404Spjd	    NULL : zc->zc_value);
346168404Spjd
347168404Spjd	nvlist_free(config);
348168404Spjd
349168404Spjd	return (error);
350168404Spjd}
351168404Spjd
352168404Spjdstatic int
353168404Spjdzfs_ioc_pool_destroy(zfs_cmd_t *zc)
354168404Spjd{
355168404Spjd	return (spa_destroy(zc->zc_name));
356168404Spjd}
357168404Spjd
358168404Spjdstatic int
359168404Spjdzfs_ioc_pool_import(zfs_cmd_t *zc)
360168404Spjd{
361168404Spjd	int error;
362168404Spjd	nvlist_t *config;
363168404Spjd	uint64_t guid;
364168404Spjd
365168404Spjd	if ((error = get_nvlist(zc, &config)) != 0)
366168404Spjd		return (error);
367168404Spjd
368168404Spjd	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
369168404Spjd	    guid != zc->zc_guid)
370168404Spjd		error = EINVAL;
371168404Spjd	else
372168404Spjd		error = spa_import(zc->zc_name, config,
373168404Spjd		    zc->zc_value[0] == '\0' ? NULL : zc->zc_value);
374168404Spjd
375168404Spjd	nvlist_free(config);
376168404Spjd
377168404Spjd	return (error);
378168404Spjd}
379168404Spjd
380168404Spjdstatic int
381168404Spjdzfs_ioc_pool_export(zfs_cmd_t *zc)
382168404Spjd{
383168404Spjd	return (spa_export(zc->zc_name, NULL));
384168404Spjd}
385168404Spjd
386168404Spjdstatic int
387168404Spjdzfs_ioc_pool_configs(zfs_cmd_t *zc)
388168404Spjd{
389168404Spjd	nvlist_t *configs;
390168404Spjd	int error;
391168404Spjd
392168404Spjd	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
393168404Spjd		return (EEXIST);
394168404Spjd
395168404Spjd	error = put_nvlist(zc, configs);
396168404Spjd
397168404Spjd	nvlist_free(configs);
398168404Spjd
399168404Spjd	return (error);
400168404Spjd}
401168404Spjd
402168404Spjdstatic int
403168404Spjdzfs_ioc_pool_stats(zfs_cmd_t *zc)
404168404Spjd{
405168404Spjd	nvlist_t *config;
406168404Spjd	int error;
407168404Spjd	int ret = 0;
408168404Spjd
409168404Spjd	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
410168404Spjd	    sizeof (zc->zc_value));
411168404Spjd
412168404Spjd	if (config != NULL) {
413168404Spjd		ret = put_nvlist(zc, config);
414168404Spjd		nvlist_free(config);
415168404Spjd
416168404Spjd		/*
417168404Spjd		 * The config may be present even if 'error' is non-zero.
418168404Spjd		 * In this case we return success, and preserve the real errno
419168404Spjd		 * in 'zc_cookie'.
420168404Spjd		 */
421168404Spjd		zc->zc_cookie = error;
422168404Spjd	} else {
423168404Spjd		ret = error;
424168404Spjd	}
425168404Spjd
426168404Spjd	return (ret);
427168404Spjd}
428168404Spjd
429168404Spjd/*
430168404Spjd * Try to import the given pool, returning pool stats as appropriate so that
431168404Spjd * user land knows which devices are available and overall pool health.
432168404Spjd */
433168404Spjdstatic int
434168404Spjdzfs_ioc_pool_tryimport(zfs_cmd_t *zc)
435168404Spjd{
436168404Spjd	nvlist_t *tryconfig, *config;
437168404Spjd	int error;
438168404Spjd
439168404Spjd	if ((error = get_nvlist(zc, &tryconfig)) != 0)
440168404Spjd		return (error);
441168404Spjd
442168404Spjd	config = spa_tryimport(tryconfig);
443168404Spjd
444168404Spjd	nvlist_free(tryconfig);
445168404Spjd
446168404Spjd	if (config == NULL)
447168404Spjd		return (EINVAL);
448168404Spjd
449168404Spjd	error = put_nvlist(zc, config);
450168404Spjd	nvlist_free(config);
451168404Spjd
452168404Spjd	return (error);
453168404Spjd}
454168404Spjd
455168404Spjdstatic int
456168404Spjdzfs_ioc_pool_scrub(zfs_cmd_t *zc)
457168404Spjd{
458168404Spjd	spa_t *spa;
459168404Spjd	int error;
460168404Spjd
461168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
462168404Spjd		return (error);
463168404Spjd
464168404Spjd	error = spa_scrub(spa, zc->zc_cookie, B_FALSE);
465168404Spjd
466168404Spjd	spa_close(spa, FTAG);
467168404Spjd
468168404Spjd	return (error);
469168404Spjd}
470168404Spjd
471168404Spjdstatic int
472168404Spjdzfs_ioc_pool_freeze(zfs_cmd_t *zc)
473168404Spjd{
474168404Spjd	spa_t *spa;
475168404Spjd	int error;
476168404Spjd
477168404Spjd	error = spa_open(zc->zc_name, &spa, FTAG);
478168404Spjd	if (error == 0) {
479168404Spjd		spa_freeze(spa);
480168404Spjd		spa_close(spa, FTAG);
481168404Spjd	}
482168404Spjd	return (error);
483168404Spjd}
484168404Spjd
485168404Spjdstatic int
486168404Spjdzfs_ioc_pool_upgrade(zfs_cmd_t *zc)
487168404Spjd{
488168404Spjd	spa_t *spa;
489168404Spjd	int error;
490168404Spjd
491168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
492168404Spjd		return (error);
493168404Spjd
494168404Spjd	spa_upgrade(spa);
495168404Spjd
496168404Spjd	spa_close(spa, FTAG);
497168404Spjd
498168404Spjd	return (error);
499168404Spjd}
500168404Spjd
501168404Spjdstatic int
502168404Spjdzfs_ioc_pool_get_history(zfs_cmd_t *zc)
503168404Spjd{
504168404Spjd	spa_t *spa;
505168404Spjd	char *hist_buf;
506168404Spjd	uint64_t size;
507168404Spjd	int error;
508168404Spjd
509168404Spjd	if ((size = zc->zc_history_len) == 0)
510168404Spjd		return (EINVAL);
511168404Spjd
512168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
513168404Spjd		return (error);
514168404Spjd
515168404Spjd	if (spa_version(spa) < ZFS_VERSION_ZPOOL_HISTORY) {
516168404Spjd		spa_close(spa, FTAG);
517168404Spjd		return (ENOTSUP);
518168404Spjd	}
519168404Spjd
520168404Spjd	hist_buf = kmem_alloc(size, KM_SLEEP);
521168404Spjd	if ((error = spa_history_get(spa, &zc->zc_history_offset,
522168404Spjd	    &zc->zc_history_len, hist_buf)) == 0) {
523168404Spjd		error = xcopyout(hist_buf, (char *)(uintptr_t)zc->zc_history,
524168404Spjd		    zc->zc_history_len);
525168404Spjd	}
526168404Spjd
527168404Spjd	spa_close(spa, FTAG);
528168404Spjd	kmem_free(hist_buf, size);
529168404Spjd	return (error);
530168404Spjd}
531168404Spjd
532168404Spjdstatic int
533168404Spjdzfs_ioc_pool_log_history(zfs_cmd_t *zc)
534168404Spjd{
535168404Spjd	spa_t *spa;
536168404Spjd	char *history_str = NULL;
537168404Spjd	size_t size;
538168404Spjd	int error;
539168404Spjd
540168404Spjd	size = zc->zc_history_len;
541168404Spjd	if (size == 0 || size > HIS_MAX_RECORD_LEN)
542168404Spjd		return (EINVAL);
543168404Spjd
544168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
545168404Spjd		return (error);
546168404Spjd
547168404Spjd	if (spa_version(spa) < ZFS_VERSION_ZPOOL_HISTORY) {
548168404Spjd		spa_close(spa, FTAG);
549168404Spjd		return (ENOTSUP);
550168404Spjd	}
551168404Spjd
552168404Spjd	/* add one for the NULL delimiter */
553168404Spjd	size++;
554168404Spjd	history_str = kmem_alloc(size, KM_SLEEP);
555168404Spjd	if ((error = xcopyin((void *)(uintptr_t)zc->zc_history, history_str,
556168404Spjd	    size)) != 0) {
557168404Spjd		spa_close(spa, FTAG);
558168404Spjd		kmem_free(history_str, size);
559168404Spjd		return (error);
560168404Spjd	}
561168404Spjd	history_str[size - 1] = '\0';
562168404Spjd
563168404Spjd	error = spa_history_log(spa, history_str, zc->zc_history_offset);
564168404Spjd
565168404Spjd	spa_close(spa, FTAG);
566168404Spjd	kmem_free(history_str, size);
567168404Spjd
568168404Spjd	return (error);
569168404Spjd}
570168404Spjd
571168404Spjdstatic int
572168404Spjdzfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
573168404Spjd{
574168404Spjd	int error;
575168404Spjd
576168404Spjd	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
577168404Spjd		return (error);
578168404Spjd
579168404Spjd	return (0);
580168404Spjd}
581168404Spjd
582168404Spjdstatic int
583168404Spjdzfs_ioc_obj_to_path(zfs_cmd_t *zc)
584168404Spjd{
585168404Spjd	objset_t *osp;
586168404Spjd	int error;
587168404Spjd
588168404Spjd	if ((error = dmu_objset_open(zc->zc_name, DMU_OST_ZFS,
589168404Spjd	    DS_MODE_NONE | DS_MODE_READONLY, &osp)) != 0)
590168404Spjd		return (error);
591168404Spjd
592168404Spjd	error = zfs_obj_to_path(osp, zc->zc_obj, zc->zc_value,
593168404Spjd	    sizeof (zc->zc_value));
594168404Spjd	dmu_objset_close(osp);
595168404Spjd
596168404Spjd	return (error);
597168404Spjd}
598168404Spjd
599168404Spjdstatic int
600168404Spjdzfs_ioc_vdev_add(zfs_cmd_t *zc)
601168404Spjd{
602168404Spjd	spa_t *spa;
603168404Spjd	int error;
604168404Spjd	nvlist_t *config;
605168404Spjd
606168404Spjd	error = spa_open(zc->zc_name, &spa, FTAG);
607168404Spjd	if (error != 0)
608168404Spjd		return (error);
609168404Spjd
610168404Spjd	/*
611168404Spjd	 * A root pool with concatenated devices is not supported.
612168404Spjd	 * Thus, can not add a device to a root pool with one device.
613168404Spjd	 */
614168404Spjd	if (spa->spa_root_vdev->vdev_children == 1 && spa->spa_bootfs != 0) {
615168404Spjd		spa_close(spa, FTAG);
616168404Spjd		return (EDOM);
617168404Spjd	}
618168404Spjd
619168404Spjd	if ((error = get_nvlist(zc, &config)) == 0) {
620168404Spjd		error = spa_vdev_add(spa, config);
621168404Spjd		nvlist_free(config);
622168404Spjd	}
623168404Spjd
624168404Spjd	spa_close(spa, FTAG);
625168404Spjd	return (error);
626168404Spjd}
627168404Spjd
628168404Spjdstatic int
629168404Spjdzfs_ioc_vdev_remove(zfs_cmd_t *zc)
630168404Spjd{
631168404Spjd	spa_t *spa;
632168404Spjd	int error;
633168404Spjd
634168404Spjd	error = spa_open(zc->zc_name, &spa, FTAG);
635168404Spjd	if (error != 0)
636168404Spjd		return (error);
637168404Spjd	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
638168404Spjd	spa_close(spa, FTAG);
639168404Spjd	return (error);
640168404Spjd}
641168404Spjd
642168404Spjdstatic int
643168404Spjdzfs_ioc_vdev_online(zfs_cmd_t *zc)
644168404Spjd{
645168404Spjd	spa_t *spa;
646168404Spjd	int error;
647168404Spjd
648168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
649168404Spjd		return (error);
650168404Spjd	error = vdev_online(spa, zc->zc_guid);
651168404Spjd	spa_close(spa, FTAG);
652168404Spjd	return (error);
653168404Spjd}
654168404Spjd
655168404Spjdstatic int
656168404Spjdzfs_ioc_vdev_offline(zfs_cmd_t *zc)
657168404Spjd{
658168404Spjd	spa_t *spa;
659168404Spjd	int istmp = zc->zc_cookie;
660168404Spjd	int error;
661168404Spjd
662168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
663168404Spjd		return (error);
664168404Spjd	error = vdev_offline(spa, zc->zc_guid, istmp);
665168404Spjd	spa_close(spa, FTAG);
666168404Spjd	return (error);
667168404Spjd}
668168404Spjd
669168404Spjdstatic int
670168404Spjdzfs_ioc_vdev_attach(zfs_cmd_t *zc)
671168404Spjd{
672168404Spjd	spa_t *spa;
673168404Spjd	int replacing = zc->zc_cookie;
674168404Spjd	nvlist_t *config;
675168404Spjd	int error;
676168404Spjd
677168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
678168404Spjd		return (error);
679168404Spjd
680168404Spjd	if ((error = get_nvlist(zc, &config)) == 0) {
681168404Spjd		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
682168404Spjd		nvlist_free(config);
683168404Spjd	}
684168404Spjd
685168404Spjd	spa_close(spa, FTAG);
686168404Spjd	return (error);
687168404Spjd}
688168404Spjd
689168404Spjdstatic int
690168404Spjdzfs_ioc_vdev_detach(zfs_cmd_t *zc)
691168404Spjd{
692168404Spjd	spa_t *spa;
693168404Spjd	int error;
694168404Spjd
695168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
696168404Spjd		return (error);
697168404Spjd
698168404Spjd	error = spa_vdev_detach(spa, zc->zc_guid, B_FALSE);
699168404Spjd
700168404Spjd	spa_close(spa, FTAG);
701168404Spjd	return (error);
702168404Spjd}
703168404Spjd
704168404Spjdstatic int
705168404Spjdzfs_ioc_vdev_setpath(zfs_cmd_t *zc)
706168404Spjd{
707168404Spjd	spa_t *spa;
708168404Spjd	char *path = zc->zc_value;
709168404Spjd	uint64_t guid = zc->zc_guid;
710168404Spjd	int error;
711168404Spjd
712168404Spjd	error = spa_open(zc->zc_name, &spa, FTAG);
713168404Spjd	if (error != 0)
714168404Spjd		return (error);
715168404Spjd
716168404Spjd	error = spa_vdev_setpath(spa, guid, path);
717168404Spjd	spa_close(spa, FTAG);
718168404Spjd	return (error);
719168404Spjd}
720168404Spjd
721168404Spjdstatic int
722168404Spjdzfs_ioc_objset_stats(zfs_cmd_t *zc)
723168404Spjd{
724168404Spjd	objset_t *os = NULL;
725168404Spjd	int error;
726168404Spjd	nvlist_t *nv;
727168404Spjd
728168404Spjdretry:
729168404Spjd	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
730168404Spjd	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
731168404Spjd	if (error != 0) {
732168404Spjd		/*
733168404Spjd		 * This is ugly: dmu_objset_open() can return EBUSY if
734168404Spjd		 * the objset is held exclusively. Fortunately this hold is
735168404Spjd		 * only for a short while, so we retry here.
736168404Spjd		 * This avoids user code having to handle EBUSY,
737168404Spjd		 * for example for a "zfs list".
738168404Spjd		 */
739168404Spjd		if (error == EBUSY) {
740168404Spjd			delay(1);
741168404Spjd			goto retry;
742168404Spjd		}
743168404Spjd		return (error);
744168404Spjd	}
745168404Spjd
746168404Spjd	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
747168404Spjd
748168404Spjd	if (zc->zc_nvlist_dst != 0 &&
749168404Spjd	    (error = dsl_prop_get_all(os, &nv)) == 0) {
750168404Spjd		dmu_objset_stats(os, nv);
751168404Spjd		/*
752168404Spjd		 * NB: zvol_get_stats() will read the objset contents,
753168404Spjd		 * which we aren't supposed to do with a
754168404Spjd		 * DS_MODE_STANDARD open, because it could be
755168404Spjd		 * inconsistent.  So this is a bit of a workaround...
756168404Spjd		 */
757168404Spjd		if (!zc->zc_objset_stats.dds_inconsistent &&
758168404Spjd		    dmu_objset_type(os) == DMU_OST_ZVOL)
759168404Spjd			VERIFY(zvol_get_stats(os, nv) == 0);
760168404Spjd		error = put_nvlist(zc, nv);
761168404Spjd		nvlist_free(nv);
762168404Spjd	}
763168404Spjd
764168404Spjd	spa_altroot(dmu_objset_spa(os), zc->zc_value, sizeof (zc->zc_value));
765168404Spjd
766168404Spjd	dmu_objset_close(os);
767168404Spjd	if (error == ENOMEM)
768168404Spjd		error = 0;
769168404Spjd	return (error);
770168404Spjd}
771168404Spjd
772168404Spjdstatic int
773168404Spjdzfs_ioc_dataset_list_next(zfs_cmd_t *zc)
774168404Spjd{
775168404Spjd	objset_t *os;
776168404Spjd	int error;
777168404Spjd	char *p;
778168404Spjd
779168404Spjdretry:
780168404Spjd	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
781168404Spjd	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
782168404Spjd	if (error != 0) {
783168404Spjd		/*
784168404Spjd		 * This is ugly: dmu_objset_open() can return EBUSY if
785168404Spjd		 * the objset is held exclusively. Fortunately this hold is
786168404Spjd		 * only for a short while, so we retry here.
787168404Spjd		 * This avoids user code having to handle EBUSY,
788168404Spjd		 * for example for a "zfs list".
789168404Spjd		 */
790168404Spjd		if (error == EBUSY) {
791168404Spjd			delay(1);
792168404Spjd			goto retry;
793168404Spjd		}
794168404Spjd		if (error == ENOENT)
795168404Spjd			error = ESRCH;
796168404Spjd		return (error);
797168404Spjd	}
798168404Spjd
799168404Spjd	p = strrchr(zc->zc_name, '/');
800168404Spjd	if (p == NULL || p[1] != '\0')
801168404Spjd		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
802168404Spjd	p = zc->zc_name + strlen(zc->zc_name);
803168404Spjd
804168404Spjd	do {
805168404Spjd		error = dmu_dir_list_next(os,
806168404Spjd		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
807168404Spjd		    NULL, &zc->zc_cookie);
808168404Spjd		if (error == ENOENT)
809168404Spjd			error = ESRCH;
810168404Spjd	} while (error == 0 && !INGLOBALZONE(curproc) &&
811168404Spjd	    !zone_dataset_visible(zc->zc_name, NULL));
812168404Spjd
813168404Spjd	/*
814168404Spjd	 * If it's a hidden dataset (ie. with a '$' in its name), don't
815168404Spjd	 * try to get stats for it.  Userland will skip over it.
816168404Spjd	 */
817168404Spjd	if (error == 0 && strchr(zc->zc_name, '$') == NULL)
818168404Spjd		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
819168404Spjd
820168404Spjd	dmu_objset_close(os);
821168404Spjd	return (error);
822168404Spjd}
823168404Spjd
824168404Spjdstatic int
825168404Spjdzfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
826168404Spjd{
827168404Spjd	objset_t *os;
828168404Spjd	int error;
829168404Spjd
830168404Spjdretry:
831168404Spjd	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
832168404Spjd	    DS_MODE_STANDARD | DS_MODE_READONLY, &os);
833168404Spjd	if (error != 0) {
834168404Spjd		/*
835168404Spjd		 * This is ugly: dmu_objset_open() can return EBUSY if
836168404Spjd		 * the objset is held exclusively. Fortunately this hold is
837168404Spjd		 * only for a short while, so we retry here.
838168404Spjd		 * This avoids user code having to handle EBUSY,
839168404Spjd		 * for example for a "zfs list".
840168404Spjd		 */
841168404Spjd		if (error == EBUSY) {
842168404Spjd			delay(1);
843168404Spjd			goto retry;
844168404Spjd		}
845168404Spjd		if (error == ENOENT)
846168404Spjd			error = ESRCH;
847168404Spjd		return (error);
848168404Spjd	}
849168404Spjd
850168404Spjd	/*
851168404Spjd	 * A dataset name of maximum length cannot have any snapshots,
852168404Spjd	 * so exit immediately.
853168404Spjd	 */
854168404Spjd	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
855168404Spjd		dmu_objset_close(os);
856168404Spjd		return (ESRCH);
857168404Spjd	}
858168404Spjd
859168404Spjd	error = dmu_snapshot_list_next(os,
860168404Spjd	    sizeof (zc->zc_name) - strlen(zc->zc_name),
861168404Spjd	    zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie);
862168404Spjd	if (error == ENOENT)
863168404Spjd		error = ESRCH;
864168404Spjd
865168404Spjd	if (error == 0)
866168404Spjd		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
867168404Spjd
868168404Spjd	dmu_objset_close(os);
869168404Spjd	return (error);
870168404Spjd}
871168404Spjd
872168404Spjdstatic int
873168404Spjdzfs_set_prop_nvlist(const char *name, dev_t dev, cred_t *cr, nvlist_t *nvl)
874168404Spjd{
875168404Spjd	nvpair_t *elem;
876168404Spjd	int error;
877168404Spjd	const char *propname;
878168404Spjd	zfs_prop_t prop;
879168404Spjd	uint64_t intval;
880168404Spjd	char *strval;
881168404Spjd	char buf[MAXNAMELEN];
882168404Spjd	const char *p;
883168404Spjd	spa_t *spa;
884168404Spjd
885168404Spjd	elem = NULL;
886168404Spjd	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
887168404Spjd		propname = nvpair_name(elem);
888168404Spjd
889168404Spjd		if ((prop = zfs_name_to_prop(propname)) ==
890168404Spjd		    ZFS_PROP_INVAL) {
891168404Spjd			/*
892168404Spjd			 * If this is a user-defined property, it must be a
893168404Spjd			 * string, and there is no further validation to do.
894168404Spjd			 */
895168404Spjd			if (!zfs_prop_user(propname) ||
896168404Spjd			    nvpair_type(elem) != DATA_TYPE_STRING)
897168404Spjd				return (EINVAL);
898168404Spjd
899168404Spjd			VERIFY(nvpair_value_string(elem, &strval) == 0);
900168404Spjd			error = dsl_prop_set(name, propname, 1,
901168404Spjd			    strlen(strval) + 1, strval);
902168404Spjd			if (error == 0)
903168404Spjd				continue;
904168404Spjd			else
905168404Spjd				return (error);
906168404Spjd		}
907168404Spjd
908168404Spjd		/*
909168404Spjd		 * Check permissions for special properties.
910168404Spjd		 */
911168404Spjd		switch (prop) {
912168404Spjd		case ZFS_PROP_ZONED:
913168404Spjd			/*
914168404Spjd			 * Disallow setting of 'zoned' from within a local zone.
915168404Spjd			 */
916168404Spjd			if (!INGLOBALZONE(curproc))
917168404Spjd				return (EPERM);
918168404Spjd			break;
919168404Spjd
920168404Spjd		case ZFS_PROP_QUOTA:
921168404Spjd			if (error = zfs_dozonecheck(name, cr))
922168404Spjd				return (error);
923168404Spjd
924168404Spjd			if (!INGLOBALZONE(curproc)) {
925168404Spjd				uint64_t zoned;
926168404Spjd				char setpoint[MAXNAMELEN];
927168404Spjd				int dslen;
928168404Spjd				/*
929168404Spjd				 * Unprivileged users are allowed to modify the
930168404Spjd				 * quota on things *under* (ie. contained by)
931168404Spjd				 * the thing they own.
932168404Spjd				 */
933168404Spjd				if (dsl_prop_get_integer(name, "jailed", &zoned,
934168404Spjd				    setpoint))
935168404Spjd					return (EPERM);
936168404Spjd				if (!zoned) /* this shouldn't happen */
937168404Spjd					return (EPERM);
938168404Spjd				dslen = strlen(name);
939168404Spjd				if (dslen <= strlen(setpoint))
940168404Spjd					return (EPERM);
941168404Spjd			}
942168404Spjd			break;
943168404Spjd
944168404Spjd		case ZFS_PROP_COMPRESSION:
945168404Spjd			/*
946168404Spjd			 * If the user specified gzip compression, make sure
947168404Spjd			 * the SPA supports it. We ignore any errors here since
948168404Spjd			 * we'll catch them later.
949168404Spjd			 */
950168404Spjd			if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
951168404Spjd			    nvpair_value_uint64(elem, &intval) == 0 &&
952168404Spjd			    intval >= ZIO_COMPRESS_GZIP_1 &&
953168404Spjd			    intval <= ZIO_COMPRESS_GZIP_9) {
954168404Spjd				if ((p = strchr(name, '/')) == NULL) {
955168404Spjd					p = name;
956168404Spjd				} else {
957168404Spjd					bcopy(name, buf, p - name);
958168404Spjd					buf[p - name] = '\0';
959168404Spjd					p = buf;
960168404Spjd				}
961168404Spjd
962168404Spjd				if (spa_open(p, &spa, FTAG) == 0) {
963168404Spjd					if (spa_version(spa) <
964168404Spjd					    ZFS_VERSION_GZIP_COMPRESSION) {
965168404Spjd						spa_close(spa, FTAG);
966168404Spjd						return (ENOTSUP);
967168404Spjd					}
968168404Spjd
969168404Spjd					spa_close(spa, FTAG);
970168404Spjd				}
971168404Spjd			}
972168404Spjd			break;
973168404Spjd		}
974168404Spjd
975168404Spjd		switch (prop) {
976168404Spjd		case ZFS_PROP_QUOTA:
977168404Spjd			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
978168404Spjd			    (error = dsl_dir_set_quota(name,
979168404Spjd			    intval)) != 0)
980168404Spjd				return (error);
981168404Spjd			break;
982168404Spjd
983168404Spjd		case ZFS_PROP_RESERVATION:
984168404Spjd			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
985168404Spjd			    (error = dsl_dir_set_reservation(name,
986168404Spjd			    intval)) != 0)
987168404Spjd				return (error);
988168404Spjd			break;
989168404Spjd
990168404Spjd		case ZFS_PROP_VOLSIZE:
991168404Spjd			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
992168404Spjd			    (error = zvol_set_volsize(name, dev,
993168404Spjd			    intval)) != 0)
994168404Spjd				return (error);
995168404Spjd			break;
996168404Spjd
997168404Spjd		case ZFS_PROP_VOLBLOCKSIZE:
998168404Spjd			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
999168404Spjd			    (error = zvol_set_volblocksize(name,
1000168404Spjd			    intval)) != 0)
1001168404Spjd				return (error);
1002168404Spjd			break;
1003168404Spjd
1004168404Spjd		default:
1005168404Spjd			if (nvpair_type(elem) == DATA_TYPE_STRING) {
1006168404Spjd				if (zfs_prop_get_type(prop) !=
1007168404Spjd				    prop_type_string)
1008168404Spjd					return (EINVAL);
1009168404Spjd				VERIFY(nvpair_value_string(elem, &strval) == 0);
1010168404Spjd				if ((error = dsl_prop_set(name,
1011168404Spjd				    nvpair_name(elem), 1, strlen(strval) + 1,
1012168404Spjd				    strval)) != 0)
1013168404Spjd					return (error);
1014168404Spjd			} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
1015168404Spjd				const char *unused;
1016168404Spjd
1017168404Spjd				VERIFY(nvpair_value_uint64(elem, &intval) == 0);
1018168404Spjd
1019168404Spjd				switch (zfs_prop_get_type(prop)) {
1020168404Spjd				case prop_type_number:
1021168404Spjd					break;
1022168404Spjd				case prop_type_boolean:
1023168404Spjd					if (intval > 1)
1024168404Spjd						return (EINVAL);
1025168404Spjd					break;
1026168404Spjd				case prop_type_string:
1027168404Spjd					return (EINVAL);
1028168404Spjd				case prop_type_index:
1029168404Spjd					if (zfs_prop_index_to_string(prop,
1030168404Spjd					    intval, &unused) != 0)
1031168404Spjd						return (EINVAL);
1032168404Spjd					break;
1033168404Spjd				default:
1034168404Spjd					cmn_err(CE_PANIC, "unknown property "
1035168404Spjd					    "type");
1036168404Spjd					break;
1037168404Spjd				}
1038168404Spjd
1039168404Spjd				if ((error = dsl_prop_set(name, propname,
1040168404Spjd				    8, 1, &intval)) != 0)
1041168404Spjd					return (error);
1042168404Spjd			} else {
1043168404Spjd				return (EINVAL);
1044168404Spjd			}
1045168404Spjd			break;
1046168404Spjd		}
1047168404Spjd	}
1048168404Spjd
1049168404Spjd	return (0);
1050168404Spjd}
1051168404Spjd
1052168404Spjdstatic int
1053168404Spjdzfs_ioc_set_prop(zfs_cmd_t *zc)
1054168404Spjd{
1055168404Spjd	nvlist_t *nvl;
1056168404Spjd	int error;
1057168404Spjd	zfs_prop_t prop;
1058168404Spjd
1059168404Spjd	/*
1060168404Spjd	 * If zc_value is set, then this is an attempt to inherit a value.
1061168404Spjd	 * Otherwise, zc_nvlist refers to a list of properties to set.
1062168404Spjd	 */
1063168404Spjd	if (zc->zc_value[0] != '\0') {
1064168404Spjd		if (!zfs_prop_user(zc->zc_value) &&
1065168404Spjd		    ((prop = zfs_name_to_prop(zc->zc_value)) ==
1066168404Spjd		    ZFS_PROP_INVAL ||
1067168404Spjd		    !zfs_prop_inheritable(prop)))
1068168404Spjd			return (EINVAL);
1069168404Spjd
1070168404Spjd		return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL));
1071168404Spjd	}
1072168404Spjd
1073168404Spjd	if ((error = get_nvlist(zc, &nvl)) != 0)
1074168404Spjd		return (error);
1075168404Spjd
1076168404Spjd	error = zfs_set_prop_nvlist(zc->zc_name, zc->zc_dev,
1077168404Spjd	    (cred_t *)(uintptr_t)zc->zc_cred, nvl);
1078168404Spjd	nvlist_free(nvl);
1079168404Spjd	return (error);
1080168404Spjd}
1081168404Spjd
1082168404Spjdstatic int
1083168404Spjdzfs_ioc_pool_props_set(zfs_cmd_t *zc)
1084168404Spjd{
1085168404Spjd	nvlist_t *nvl;
1086168404Spjd	int error, reset_bootfs = 0;
1087168404Spjd	uint64_t objnum;
1088168404Spjd	zpool_prop_t prop;
1089168404Spjd	nvpair_t *elem;
1090168404Spjd	char *propname, *strval;
1091168404Spjd	spa_t *spa;
1092168404Spjd	vdev_t *rvdev;
1093168404Spjd	char *vdev_type;
1094168404Spjd	objset_t *os;
1095168404Spjd
1096168404Spjd	if ((error = get_nvlist(zc, &nvl)) != 0)
1097168404Spjd		return (error);
1098168404Spjd
1099168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
1100168404Spjd		nvlist_free(nvl);
1101168404Spjd		return (error);
1102168404Spjd	}
1103168404Spjd
1104168404Spjd	if (spa_version(spa) < ZFS_VERSION_BOOTFS) {
1105168404Spjd		nvlist_free(nvl);
1106168404Spjd		spa_close(spa, FTAG);
1107168404Spjd		return (ENOTSUP);
1108168404Spjd	}
1109168404Spjd
1110168404Spjd	elem = NULL;
1111168404Spjd	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
1112168404Spjd
1113168404Spjd		propname = nvpair_name(elem);
1114168404Spjd
1115168404Spjd		if ((prop = zpool_name_to_prop(propname)) ==
1116168404Spjd		    ZFS_PROP_INVAL) {
1117168404Spjd			nvlist_free(nvl);
1118168404Spjd			spa_close(spa, FTAG);
1119168404Spjd			return (EINVAL);
1120168404Spjd		}
1121168404Spjd
1122168404Spjd		switch (prop) {
1123168404Spjd		case ZFS_PROP_BOOTFS:
1124168404Spjd			/*
1125168404Spjd			 * A bootable filesystem can not be on a RAIDZ pool
1126168404Spjd			 * nor a striped pool with more than 1 device.
1127168404Spjd			 */
1128168404Spjd			rvdev = spa->spa_root_vdev;
1129168404Spjd			vdev_type =
1130168404Spjd			    rvdev->vdev_child[0]->vdev_ops->vdev_op_type;
1131168404Spjd			if (strcmp(vdev_type, VDEV_TYPE_RAIDZ) == 0 ||
1132168404Spjd			    (strcmp(vdev_type, VDEV_TYPE_MIRROR) != 0 &&
1133168404Spjd			    rvdev->vdev_children > 1)) {
1134168404Spjd				error = ENOTSUP;
1135168404Spjd				break;
1136168404Spjd			}
1137168404Spjd
1138168404Spjd			reset_bootfs = 1;
1139168404Spjd
1140168404Spjd			VERIFY(nvpair_value_string(elem, &strval) == 0);
1141168404Spjd			if (strval == NULL || strval[0] == '\0') {
1142168404Spjd				objnum =
1143168404Spjd				    zfs_prop_default_numeric(ZFS_PROP_BOOTFS);
1144168404Spjd				break;
1145168404Spjd			}
1146168404Spjd
1147168404Spjd			if (error = dmu_objset_open(strval, DMU_OST_ZFS,
1148168404Spjd			    DS_MODE_STANDARD | DS_MODE_READONLY, &os))
1149168404Spjd				break;
1150168404Spjd			objnum = dmu_objset_id(os);
1151168404Spjd			dmu_objset_close(os);
1152168404Spjd			break;
1153168404Spjd
1154168404Spjd		default:
1155168404Spjd			error = EINVAL;
1156168404Spjd		}
1157168404Spjd
1158168404Spjd		if (error)
1159168404Spjd			break;
1160168404Spjd	}
1161168404Spjd	if (error == 0) {
1162168404Spjd		if (reset_bootfs) {
1163168404Spjd			VERIFY(nvlist_remove(nvl,
1164168404Spjd			    zpool_prop_to_name(ZFS_PROP_BOOTFS),
1165168404Spjd			    DATA_TYPE_STRING) == 0);
1166168404Spjd			VERIFY(nvlist_add_uint64(nvl,
1167168404Spjd			    zpool_prop_to_name(ZFS_PROP_BOOTFS), objnum) == 0);
1168168404Spjd		}
1169168404Spjd		error = spa_set_props(spa, nvl);
1170168404Spjd	}
1171168404Spjd
1172168404Spjd	nvlist_free(nvl);
1173168404Spjd	spa_close(spa, FTAG);
1174168404Spjd
1175168404Spjd	return (error);
1176168404Spjd}
1177168404Spjd
1178168404Spjdstatic int
1179168404Spjdzfs_ioc_pool_props_get(zfs_cmd_t *zc)
1180168404Spjd{
1181168404Spjd	spa_t *spa;
1182168404Spjd	int error;
1183168404Spjd	nvlist_t *nvp = NULL;
1184168404Spjd
1185168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1186168404Spjd		return (error);
1187168404Spjd
1188168404Spjd	error = spa_get_props(spa, &nvp);
1189168404Spjd
1190168404Spjd	if (error == 0 && zc->zc_nvlist_dst != 0)
1191168404Spjd		error = put_nvlist(zc, nvp);
1192168404Spjd	else
1193168404Spjd		error = EFAULT;
1194168404Spjd
1195168404Spjd	spa_close(spa, FTAG);
1196168404Spjd
1197168404Spjd	if (nvp)
1198168404Spjd		nvlist_free(nvp);
1199168404Spjd	return (error);
1200168404Spjd}
1201168404Spjd
1202168404Spjdstatic int
1203168404Spjdzfs_ioc_create_minor(zfs_cmd_t *zc)
1204168404Spjd{
1205168404Spjd	return (zvol_create_minor(zc->zc_name, zc->zc_dev));
1206168404Spjd}
1207168404Spjd
1208168404Spjdstatic int
1209168404Spjdzfs_ioc_remove_minor(zfs_cmd_t *zc)
1210168404Spjd{
1211168404Spjd	return (zvol_remove_minor(zc->zc_name));
1212168404Spjd}
1213168404Spjd
1214168404Spjd/*
1215168404Spjd * Search the vfs list for a specified resource.  Returns a pointer to it
1216168404Spjd * or NULL if no suitable entry is found. The caller of this routine
1217168404Spjd * is responsible for releasing the returned vfs pointer.
1218168404Spjd */
1219168404Spjdstatic vfs_t *
1220168404Spjdzfs_get_vfs(const char *resource)
1221168404Spjd{
1222168404Spjd	vfs_t *vfsp;
1223168404Spjd
1224168404Spjd	mtx_lock(&mountlist_mtx);
1225168404Spjd	TAILQ_FOREACH(vfsp, &mountlist, mnt_list) {
1226168404Spjd		if (strcmp(vfsp->mnt_stat.f_mntfromname, resource) == 0) {
1227168404Spjd			VFS_HOLD(vfsp);
1228168404Spjd			break;
1229168404Spjd		}
1230168404Spjd	}
1231168404Spjd	mtx_unlock(&mountlist_mtx);
1232168404Spjd	return (vfsp);
1233168404Spjd}
1234168404Spjd
1235168404Spjdstatic void
1236168404Spjdzfs_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
1237168404Spjd{
1238168404Spjd	zfs_create_data_t *zc = arg;
1239168404Spjd
1240168404Spjd	zfs_create_fs(os, (cred_t *)(uintptr_t)zc->zc_cred, tx);
1241168404Spjd}
1242168404Spjd
1243168404Spjdstatic int
1244168404Spjdzfs_ioc_create(zfs_cmd_t *zc)
1245168404Spjd{
1246168404Spjd	objset_t *clone;
1247168404Spjd	int error = 0;
1248168404Spjd	zfs_create_data_t cbdata = { 0 };
1249168404Spjd	void (*cbfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
1250168404Spjd	dmu_objset_type_t type = zc->zc_objset_type;
1251168404Spjd
1252168404Spjd	switch (type) {
1253168404Spjd
1254168404Spjd	case DMU_OST_ZFS:
1255168404Spjd		cbfunc = zfs_create_cb;
1256168404Spjd		break;
1257168404Spjd
1258168404Spjd	case DMU_OST_ZVOL:
1259168404Spjd		cbfunc = zvol_create_cb;
1260168404Spjd		break;
1261168404Spjd
1262168404Spjd	default:
1263168404Spjd		cbfunc = NULL;
1264168404Spjd	}
1265168404Spjd	if (strchr(zc->zc_name, '@'))
1266168404Spjd		return (EINVAL);
1267168404Spjd
1268168404Spjd	if (zc->zc_nvlist_src != 0 &&
1269168404Spjd	    (error = get_nvlist(zc, &cbdata.zc_props)) != 0)
1270168404Spjd		return (error);
1271168404Spjd
1272168404Spjd	cbdata.zc_cred = (cred_t *)(uintptr_t)zc->zc_cred;
1273168404Spjd	cbdata.zc_dev = (dev_t)zc->zc_dev;
1274168404Spjd
1275168404Spjd	if (zc->zc_value[0] != '\0') {
1276168404Spjd		/*
1277168404Spjd		 * We're creating a clone of an existing snapshot.
1278168404Spjd		 */
1279168404Spjd		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
1280168404Spjd		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
1281168404Spjd			nvlist_free(cbdata.zc_props);
1282168404Spjd			return (EINVAL);
1283168404Spjd		}
1284168404Spjd
1285168404Spjd		error = dmu_objset_open(zc->zc_value, type,
1286168404Spjd		    DS_MODE_STANDARD | DS_MODE_READONLY, &clone);
1287168404Spjd		if (error) {
1288168404Spjd			nvlist_free(cbdata.zc_props);
1289168404Spjd			return (error);
1290168404Spjd		}
1291168404Spjd		error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL);
1292168404Spjd		dmu_objset_close(clone);
1293168404Spjd	} else {
1294168404Spjd		if (cbfunc == NULL) {
1295168404Spjd			nvlist_free(cbdata.zc_props);
1296168404Spjd			return (EINVAL);
1297168404Spjd		}
1298168404Spjd
1299168404Spjd		if (type == DMU_OST_ZVOL) {
1300168404Spjd			uint64_t volsize, volblocksize;
1301168404Spjd
1302168404Spjd			if (cbdata.zc_props == NULL ||
1303168404Spjd			    nvlist_lookup_uint64(cbdata.zc_props,
1304168404Spjd			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1305168404Spjd			    &volsize) != 0) {
1306168404Spjd				nvlist_free(cbdata.zc_props);
1307168404Spjd				return (EINVAL);
1308168404Spjd			}
1309168404Spjd
1310168404Spjd			if ((error = nvlist_lookup_uint64(cbdata.zc_props,
1311168404Spjd			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
1312168404Spjd			    &volblocksize)) != 0 && error != ENOENT) {
1313168404Spjd				nvlist_free(cbdata.zc_props);
1314168404Spjd				return (EINVAL);
1315168404Spjd			}
1316168404Spjd
1317168404Spjd			if (error != 0)
1318168404Spjd				volblocksize = zfs_prop_default_numeric(
1319168404Spjd				    ZFS_PROP_VOLBLOCKSIZE);
1320168404Spjd
1321168404Spjd			if ((error = zvol_check_volblocksize(
1322168404Spjd			    volblocksize)) != 0 ||
1323168404Spjd			    (error = zvol_check_volsize(volsize,
1324168404Spjd			    volblocksize)) != 0) {
1325168404Spjd				nvlist_free(cbdata.zc_props);
1326168404Spjd				return (error);
1327168404Spjd			}
1328168404Spjd		}
1329168404Spjd
1330168404Spjd		error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc,
1331168404Spjd		    &cbdata);
1332168404Spjd	}
1333168404Spjd
1334168404Spjd	/*
1335168404Spjd	 * It would be nice to do this atomically.
1336168404Spjd	 */
1337168404Spjd	if (error == 0) {
1338168404Spjd		if ((error = zfs_set_prop_nvlist(zc->zc_name,
1339168404Spjd		    zc->zc_dev, (cred_t *)(uintptr_t)zc->zc_cred,
1340168404Spjd		    cbdata.zc_props)) != 0)
1341168404Spjd			(void) dmu_objset_destroy(zc->zc_name);
1342168404Spjd	}
1343168404Spjd
1344168404Spjd	nvlist_free(cbdata.zc_props);
1345168404Spjd	return (error);
1346168404Spjd}
1347168404Spjd
1348168404Spjdstatic int
1349168404Spjdzfs_ioc_snapshot(zfs_cmd_t *zc)
1350168404Spjd{
1351168404Spjd	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
1352168404Spjd		return (EINVAL);
1353168404Spjd	return (dmu_objset_snapshot(zc->zc_name,
1354168404Spjd	    zc->zc_value, zc->zc_cookie));
1355168404Spjd}
1356168404Spjd
1357168676Spjdint
1358168404Spjdzfs_unmount_snap(char *name, void *arg)
1359168404Spjd{
1360168404Spjd	char *snapname = arg;
1361168404Spjd	char *cp;
1362168404Spjd	vfs_t *vfsp = NULL;
1363168404Spjd
1364168404Spjd	/*
1365168404Spjd	 * Snapshots (which are under .zfs control) must be unmounted
1366168404Spjd	 * before they can be destroyed.
1367168404Spjd	 */
1368168404Spjd
1369168404Spjd	if (snapname) {
1370168404Spjd		(void) strcat(name, "@");
1371168404Spjd		(void) strcat(name, snapname);
1372168404Spjd		vfsp = zfs_get_vfs(name);
1373168404Spjd		cp = strchr(name, '@');
1374168404Spjd		*cp = '\0';
1375168404Spjd	} else if (strchr(name, '@')) {
1376168404Spjd		vfsp = zfs_get_vfs(name);
1377168404Spjd	}
1378168404Spjd
1379168404Spjd	if (vfsp) {
1380168404Spjd		/*
1381168404Spjd		 * Always force the unmount for snapshots.
1382168404Spjd		 */
1383168404Spjd		int flag = MS_FORCE;
1384168404Spjd		int err;
1385168404Spjd
1386168404Spjd		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
1387168404Spjd			VFS_RELE(vfsp);
1388168404Spjd			return (err);
1389168404Spjd		}
1390168404Spjd		VFS_RELE(vfsp);
1391168404Spjd		mtx_lock(&Giant);	/* dounmount() */
1392168404Spjd		dounmount(vfsp, flag, curthread);
1393168404Spjd		mtx_unlock(&Giant);	/* dounmount() */
1394168404Spjd	}
1395168404Spjd	return (0);
1396168404Spjd}
1397168404Spjd
1398168404Spjdstatic int
1399168404Spjdzfs_ioc_destroy_snaps(zfs_cmd_t *zc)
1400168404Spjd{
1401168404Spjd	int err;
1402168404Spjd
1403168404Spjd	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
1404168404Spjd		return (EINVAL);
1405168404Spjd	err = dmu_objset_find(zc->zc_name,
1406168404Spjd	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
1407168404Spjd	if (err)
1408168404Spjd		return (err);
1409168404Spjd	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value));
1410168404Spjd}
1411168404Spjd
1412168404Spjdstatic int
1413168404Spjdzfs_ioc_destroy(zfs_cmd_t *zc)
1414168404Spjd{
1415168404Spjd	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
1416168404Spjd		int err = zfs_unmount_snap(zc->zc_name, NULL);
1417168404Spjd		if (err)
1418168404Spjd			return (err);
1419168404Spjd	}
1420168404Spjd
1421168404Spjd	return (dmu_objset_destroy(zc->zc_name));
1422168404Spjd}
1423168404Spjd
1424168404Spjdstatic int
1425168404Spjdzfs_ioc_rollback(zfs_cmd_t *zc)
1426168404Spjd{
1427168404Spjd	return (dmu_objset_rollback(zc->zc_name));
1428168404Spjd}
1429168404Spjd
1430168404Spjdstatic int
1431168404Spjdzfs_ioc_rename(zfs_cmd_t *zc)
1432168404Spjd{
1433168676Spjd	int recursive = zc->zc_cookie & 1;
1434168676Spjd
1435168404Spjd	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
1436168404Spjd	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0)
1437168404Spjd		return (EINVAL);
1438168404Spjd
1439168676Spjd	/*
1440168676Spjd	 * Unmount snapshot unless we're doing a recursive rename,
1441168676Spjd	 * in which case the dataset code figures out which snapshots
1442168676Spjd	 * to unmount.
1443168676Spjd	 */
1444168676Spjd	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
1445168404Spjd	    zc->zc_objset_type == DMU_OST_ZFS) {
1446168404Spjd		int err = zfs_unmount_snap(zc->zc_name, NULL);
1447168404Spjd		if (err)
1448168404Spjd			return (err);
1449168404Spjd	}
1450168404Spjd
1451168676Spjd	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
1452168404Spjd}
1453168404Spjd
1454168404Spjdstatic int
1455168404Spjdzfs_ioc_recvbackup(zfs_cmd_t *zc)
1456168404Spjd{
1457168404Spjd	kthread_t *td = curthread;
1458168404Spjd	struct file *fp;
1459168404Spjd	int error;
1460168404Spjd	offset_t new_off;
1461168404Spjd
1462168404Spjd	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
1463168404Spjd	    strchr(zc->zc_value, '@') == NULL)
1464168404Spjd		return (EINVAL);
1465168404Spjd
1466168404Spjd	error = fget_read(td, zc->zc_cookie, &fp);
1467168404Spjd	if (error)
1468168404Spjd		return (error);
1469168404Spjd
1470168404Spjd	error = dmu_recvbackup(zc->zc_value, &zc->zc_begin_record,
1471168404Spjd	    &zc->zc_cookie, (boolean_t)zc->zc_guid, fp,
1472168404Spjd	    fp->f_offset);
1473168404Spjd
1474168404Spjd	new_off = fp->f_offset + zc->zc_cookie;
1475168404Spjd	fp->f_offset = new_off;
1476168404Spjd
1477168404Spjd	fdrop(fp, td);
1478168404Spjd	return (error);
1479168404Spjd}
1480168404Spjd
1481168404Spjdstatic int
1482168404Spjdzfs_ioc_sendbackup(zfs_cmd_t *zc)
1483168404Spjd{
1484168404Spjd	kthread_t *td = curthread;
1485168404Spjd	struct file *fp;
1486168404Spjd	objset_t *fromsnap = NULL;
1487168404Spjd	objset_t *tosnap;
1488168404Spjd	int error, fd;
1489168404Spjd
1490168404Spjd	error = dmu_objset_open(zc->zc_name, DMU_OST_ANY,
1491168404Spjd	    DS_MODE_STANDARD | DS_MODE_READONLY, &tosnap);
1492168404Spjd	if (error)
1493168404Spjd		return (error);
1494168404Spjd
1495168404Spjd	if (zc->zc_value[0] != '\0') {
1496168404Spjd		char buf[MAXPATHLEN];
1497168404Spjd		char *cp;
1498168404Spjd
1499168404Spjd		(void) strncpy(buf, zc->zc_name, sizeof (buf));
1500168404Spjd		cp = strchr(buf, '@');
1501168404Spjd		if (cp)
1502168404Spjd			*(cp+1) = 0;
1503168404Spjd		(void) strlcat(buf, zc->zc_value, sizeof (buf));
1504168404Spjd		error = dmu_objset_open(buf, DMU_OST_ANY,
1505168404Spjd		    DS_MODE_STANDARD | DS_MODE_READONLY, &fromsnap);
1506168404Spjd		if (error) {
1507168404Spjd			dmu_objset_close(tosnap);
1508168404Spjd			return (error);
1509168404Spjd		}
1510168404Spjd	}
1511168404Spjd
1512168404Spjd	fd = zc->zc_cookie;
1513168404Spjd	error = fget_write(td, fd, &fp);
1514168404Spjd	if (error) {
1515168404Spjd		dmu_objset_close(tosnap);
1516168404Spjd		if (fromsnap)
1517168404Spjd			dmu_objset_close(fromsnap);
1518168404Spjd		return (error);
1519168404Spjd	}
1520168404Spjd
1521168404Spjd	error = dmu_sendbackup(tosnap, fromsnap, fp);
1522168404Spjd
1523168404Spjd	fdrop(fp, td);
1524168404Spjd	if (fromsnap)
1525168404Spjd		dmu_objset_close(fromsnap);
1526168404Spjd	dmu_objset_close(tosnap);
1527168404Spjd	return (error);
1528168404Spjd}
1529168404Spjd
1530168404Spjdstatic int
1531168404Spjdzfs_ioc_inject_fault(zfs_cmd_t *zc)
1532168404Spjd{
1533168404Spjd	int id, error;
1534168404Spjd
1535168404Spjd	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
1536168404Spjd	    &zc->zc_inject_record);
1537168404Spjd
1538168404Spjd	if (error == 0)
1539168404Spjd		zc->zc_guid = (uint64_t)id;
1540168404Spjd
1541168404Spjd	return (error);
1542168404Spjd}
1543168404Spjd
1544168404Spjdstatic int
1545168404Spjdzfs_ioc_clear_fault(zfs_cmd_t *zc)
1546168404Spjd{
1547168404Spjd	return (zio_clear_fault((int)zc->zc_guid));
1548168404Spjd}
1549168404Spjd
1550168404Spjdstatic int
1551168404Spjdzfs_ioc_inject_list_next(zfs_cmd_t *zc)
1552168404Spjd{
1553168404Spjd	int id = (int)zc->zc_guid;
1554168404Spjd	int error;
1555168404Spjd
1556168404Spjd	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
1557168404Spjd	    &zc->zc_inject_record);
1558168404Spjd
1559168404Spjd	zc->zc_guid = id;
1560168404Spjd
1561168404Spjd	return (error);
1562168404Spjd}
1563168404Spjd
1564168404Spjdstatic int
1565168404Spjdzfs_ioc_error_log(zfs_cmd_t *zc)
1566168404Spjd{
1567168404Spjd	spa_t *spa;
1568168404Spjd	int error;
1569168404Spjd	size_t count = (size_t)zc->zc_nvlist_dst_size;
1570168404Spjd
1571168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1572168404Spjd		return (error);
1573168404Spjd
1574168404Spjd	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
1575168404Spjd	    &count);
1576168404Spjd	if (error == 0)
1577168404Spjd		zc->zc_nvlist_dst_size = count;
1578168404Spjd	else
1579168404Spjd		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
1580168404Spjd
1581168404Spjd	spa_close(spa, FTAG);
1582168404Spjd
1583168404Spjd	return (error);
1584168404Spjd}
1585168404Spjd
1586168404Spjdstatic int
1587168404Spjdzfs_ioc_clear(zfs_cmd_t *zc)
1588168404Spjd{
1589168404Spjd	spa_t *spa;
1590168404Spjd	vdev_t *vd;
1591168404Spjd	int error;
1592168404Spjd
1593168404Spjd	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1594168404Spjd		return (error);
1595168404Spjd
1596168404Spjd	spa_config_enter(spa, RW_WRITER, FTAG);
1597168404Spjd
1598168404Spjd	if (zc->zc_guid == 0) {
1599168404Spjd		vd = NULL;
1600168404Spjd	} else if ((vd = spa_lookup_by_guid(spa, zc->zc_guid)) == NULL) {
1601168404Spjd		spa_config_exit(spa, FTAG);
1602168404Spjd		spa_close(spa, FTAG);
1603168404Spjd		return (ENODEV);
1604168404Spjd	}
1605168404Spjd
1606168404Spjd	vdev_clear(spa, vd);
1607168404Spjd
1608168404Spjd	spa_config_exit(spa, FTAG);
1609168404Spjd
1610168404Spjd	spa_close(spa, FTAG);
1611168404Spjd
1612168404Spjd	return (0);
1613168404Spjd}
1614168404Spjd
1615168404Spjdstatic int
1616168404Spjdzfs_ioc_promote(zfs_cmd_t *zc)
1617168404Spjd{
1618168404Spjd	char *cp;
1619168404Spjd
1620168404Spjd	/*
1621168404Spjd	 * We don't need to unmount *all* the origin fs's snapshots, but
1622168404Spjd	 * it's easier.
1623168404Spjd	 */
1624168404Spjd	cp = strchr(zc->zc_value, '@');
1625168404Spjd	if (cp)
1626168404Spjd		*cp = '\0';
1627168404Spjd	(void) dmu_objset_find(zc->zc_value,
1628168404Spjd	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
1629168404Spjd	return (dsl_dataset_promote(zc->zc_name));
1630168404Spjd}
1631168404Spjd
1632168404Spjdstatic int
1633168404Spjdzfs_ioc_jail(zfs_cmd_t *zc)
1634168404Spjd{
1635168404Spjd
1636168404Spjd	return (zone_dataset_attach((cred_t *)(uintptr_t)zc->zc_cred,
1637168404Spjd	    zc->zc_name, (int)zc->zc_jailid));
1638168404Spjd}
1639168404Spjd
1640168404Spjdstatic int
1641168404Spjdzfs_ioc_unjail(zfs_cmd_t *zc)
1642168404Spjd{
1643168404Spjd
1644168404Spjd	return (zone_dataset_detach((cred_t *)(uintptr_t)zc->zc_cred,
1645168404Spjd	    zc->zc_name, (int)zc->zc_jailid));
1646168404Spjd}
1647168404Spjd
1648168404Spjdstatic zfs_ioc_vec_t zfs_ioc_vec[] = {
1649168404Spjd	{ zfs_ioc_pool_create,		zfs_secpolicy_config,	pool_name },
1650168404Spjd	{ zfs_ioc_pool_destroy,		zfs_secpolicy_config,	pool_name },
1651168404Spjd	{ zfs_ioc_pool_import,		zfs_secpolicy_config,	pool_name },
1652168404Spjd	{ zfs_ioc_pool_export,		zfs_secpolicy_config,	pool_name },
1653168404Spjd	{ zfs_ioc_pool_configs,		zfs_secpolicy_none,	no_name },
1654168404Spjd	{ zfs_ioc_pool_stats,		zfs_secpolicy_read,	pool_name },
1655168404Spjd	{ zfs_ioc_pool_tryimport,	zfs_secpolicy_config,	no_name },
1656168404Spjd	{ zfs_ioc_pool_scrub,		zfs_secpolicy_config,	pool_name },
1657168404Spjd	{ zfs_ioc_pool_freeze,		zfs_secpolicy_config,	no_name },
1658168404Spjd	{ zfs_ioc_pool_upgrade,		zfs_secpolicy_config,	pool_name },
1659168404Spjd	{ zfs_ioc_pool_get_history,	zfs_secpolicy_config,	pool_name },
1660168404Spjd	{ zfs_ioc_pool_log_history,	zfs_secpolicy_config,	pool_name },
1661168404Spjd	{ zfs_ioc_vdev_add,		zfs_secpolicy_config,	pool_name },
1662168404Spjd	{ zfs_ioc_vdev_remove,		zfs_secpolicy_config,	pool_name },
1663168404Spjd	{ zfs_ioc_vdev_online,		zfs_secpolicy_config,	pool_name },
1664168404Spjd	{ zfs_ioc_vdev_offline,		zfs_secpolicy_config,	pool_name },
1665168404Spjd	{ zfs_ioc_vdev_attach,		zfs_secpolicy_config,	pool_name },
1666168404Spjd	{ zfs_ioc_vdev_detach,		zfs_secpolicy_config,	pool_name },
1667168404Spjd	{ zfs_ioc_vdev_setpath,		zfs_secpolicy_config,	pool_name },
1668168404Spjd	{ zfs_ioc_objset_stats,		zfs_secpolicy_read,	dataset_name },
1669168404Spjd	{ zfs_ioc_dataset_list_next,	zfs_secpolicy_read,	dataset_name },
1670168404Spjd	{ zfs_ioc_snapshot_list_next,	zfs_secpolicy_read,	dataset_name },
1671168404Spjd	{ zfs_ioc_set_prop,		zfs_secpolicy_write,	dataset_name },
1672168404Spjd	{ zfs_ioc_create_minor,		zfs_secpolicy_config,	dataset_name },
1673168404Spjd	{ zfs_ioc_remove_minor,		zfs_secpolicy_config,	dataset_name },
1674168404Spjd	{ zfs_ioc_create,		zfs_secpolicy_parent,	dataset_name },
1675168404Spjd	{ zfs_ioc_destroy,		zfs_secpolicy_parent,	dataset_name },
1676168404Spjd	{ zfs_ioc_rollback,		zfs_secpolicy_write,	dataset_name },
1677168404Spjd	{ zfs_ioc_rename,		zfs_secpolicy_write,	dataset_name },
1678168404Spjd	{ zfs_ioc_recvbackup,		zfs_secpolicy_write,	dataset_name },
1679168404Spjd	{ zfs_ioc_sendbackup,		zfs_secpolicy_operator,	dataset_name },
1680168404Spjd	{ zfs_ioc_inject_fault,		zfs_secpolicy_inject,	no_name },
1681168404Spjd	{ zfs_ioc_clear_fault,		zfs_secpolicy_inject,	no_name },
1682168404Spjd	{ zfs_ioc_inject_list_next,	zfs_secpolicy_inject,	no_name },
1683168404Spjd	{ zfs_ioc_error_log,		zfs_secpolicy_inject,	pool_name },
1684168404Spjd	{ zfs_ioc_clear,		zfs_secpolicy_config,	pool_name },
1685168404Spjd	{ zfs_ioc_promote,		zfs_secpolicy_write,	dataset_name },
1686168404Spjd	{ zfs_ioc_destroy_snaps,	zfs_secpolicy_write,	dataset_name },
1687168404Spjd	{ zfs_ioc_snapshot,		zfs_secpolicy_operator,	dataset_name },
1688168404Spjd	{ zfs_ioc_dsobj_to_dsname,	zfs_secpolicy_config,	pool_name },
1689168404Spjd	{ zfs_ioc_obj_to_path,		zfs_secpolicy_config,	no_name },
1690168404Spjd	{ zfs_ioc_pool_props_set,	zfs_secpolicy_config,	pool_name },
1691168404Spjd	{ zfs_ioc_pool_props_get,	zfs_secpolicy_read,	pool_name },
1692168404Spjd	{ zfs_ioc_jail,			zfs_secpolicy_config,	dataset_name },
1693168404Spjd	{ zfs_ioc_unjail,		zfs_secpolicy_config,	dataset_name }
1694168404Spjd};
1695168404Spjd
1696168404Spjdstatic int
1697168404Spjdzfsdev_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
1698168404Spjd    struct thread *td)
1699168404Spjd{
1700168404Spjd	zfs_cmd_t *zc = (void *)addr;
1701168404Spjd	uint_t vec;
1702168404Spjd	int error;
1703168404Spjd
1704168404Spjd	vec = ZFS_IOC(cmd);
1705168404Spjd
1706168404Spjd	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
1707168404Spjd		return (EINVAL);
1708168404Spjd
1709168404Spjd	zc->zc_cred = (uintptr_t)td->td_ucred;
1710168404Spjd	zc->zc_dev = (uintptr_t)dev;
1711168404Spjd	error = zfs_ioc_vec[vec].zvec_secpolicy(zc->zc_name, td->td_ucred);
1712168404Spjd
1713168404Spjd	/*
1714168404Spjd	 * Ensure that all pool/dataset names are valid before we pass down to
1715168404Spjd	 * the lower layers.
1716168404Spjd	 */
1717168404Spjd	if (error == 0) {
1718168404Spjd		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
1719168404Spjd		switch (zfs_ioc_vec[vec].zvec_namecheck) {
1720168404Spjd		case pool_name:
1721168404Spjd			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
1722168404Spjd				error = EINVAL;
1723168404Spjd			break;
1724168404Spjd
1725168404Spjd		case dataset_name:
1726168404Spjd			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
1727168404Spjd				error = EINVAL;
1728168404Spjd			break;
1729168404Spjd
1730168404Spjd		case no_name:
1731168404Spjd			break;
1732168404Spjd		}
1733168404Spjd	}
1734168404Spjd
1735168404Spjd	if (error == 0)
1736168404Spjd		error = zfs_ioc_vec[vec].zvec_func(zc);
1737168404Spjd
1738168404Spjd	return (error);
1739168404Spjd}
1740168404Spjd
1741168404Spjd/*
1742168404Spjd * OK, so this is a little weird.
1743168404Spjd *
1744168404Spjd * /dev/zfs is the control node, i.e. minor 0.
1745168404Spjd * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
1746168404Spjd *
1747168404Spjd * /dev/zfs has basically nothing to do except serve up ioctls,
1748168404Spjd * so most of the standard driver entry points are in zvol.c.
1749168404Spjd */
1750168404Spjdstatic struct cdevsw zfs_cdevsw = {
1751168404Spjd	.d_version =	D_VERSION,
1752168404Spjd	.d_ioctl =	zfsdev_ioctl,
1753168404Spjd	.d_name =	ZFS_DEV_NAME
1754168404Spjd};
1755168404Spjd
1756168404Spjdstatic void
1757168404Spjdzfsdev_init(void)
1758168404Spjd{
1759168404Spjd	zfsdev = make_dev(&zfs_cdevsw, 0x0, UID_ROOT, GID_OPERATOR, 0660,
1760168404Spjd	    ZFS_DEV_NAME);
1761168404Spjd}
1762168404Spjd
1763168404Spjdstatic void
1764168404Spjdzfsdev_fini(void)
1765168404Spjd{
1766168404Spjd	if (zfsdev != NULL)
1767168404Spjd		destroy_dev(zfsdev);
1768168404Spjd}
1769168404Spjd
1770168404Spjdstatic struct task zfs_start_task;
1771168404Spjd
1772168404Spjdstatic void
1773168404Spjdzfs_start(void *context __unused, int pending __unused)
1774168404Spjd{
1775168404Spjd
1776168404Spjd	zfsdev_init();
1777168404Spjd	spa_init(FREAD | FWRITE);
1778168404Spjd	zfs_init();
1779168404Spjd	zvol_init();
1780168404Spjd	printf("ZFS storage pool version " ZFS_VERSION_STRING "\n");
1781168404Spjd}
1782168404Spjd
1783168404Spjdstatic int
1784168404Spjdzfs_modevent(module_t mod, int type, void *unused __unused)
1785168404Spjd{
1786168404Spjd	int error;
1787168404Spjd
1788168404Spjd	error = EOPNOTSUPP;
1789168404Spjd	switch (type) {
1790168404Spjd	case MOD_LOAD:
1791168404Spjd		printf("WARNING: ZFS is considered to be an experimental "
1792168404Spjd		    "feature in FreeBSD.\n");
1793168404Spjd		TASK_INIT(&zfs_start_task, 0, zfs_start, NULL);
1794168404Spjd		taskqueue_enqueue(taskqueue_thread, &zfs_start_task);
1795168404Spjd		error = 0;
1796168404Spjd		break;
1797168404Spjd	case MOD_UNLOAD:
1798168775Spjd		if (spa_busy() || zfs_busy() || zvol_busy() ||
1799168404Spjd		    zio_injection_enabled) {
1800168404Spjd			error = EBUSY;
1801168404Spjd			break;
1802168404Spjd		}
1803168404Spjd		zvol_fini();
1804168404Spjd		zfs_fini();
1805168404Spjd		spa_fini();
1806168404Spjd		zfsdev_fini();
1807168404Spjd		error = 0;
1808168404Spjd		break;
1809168404Spjd	}
1810168404Spjd	return (error);
1811168404Spjd}
1812168404Spjd
1813168404Spjdstatic moduledata_t zfs_mod = {
1814168404Spjd	"zfsctrl",
1815168404Spjd	zfs_modevent,
1816168404Spjd	0
1817168404Spjd};
1818168404SpjdDECLARE_MODULE(zfsctrl, zfs_mod, SI_SUB_MOUNT_ROOT, SI_ORDER_ANY);
1819