xref: /illumos-gate/usr/src/cmd/zinject/zinject.c (revision ea8dc4b6)
1*ea8dc4b6Seschrock /*
2*ea8dc4b6Seschrock  * CDDL HEADER START
3*ea8dc4b6Seschrock  *
4*ea8dc4b6Seschrock  * The contents of this file are subject to the terms of the
5*ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6*ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7*ea8dc4b6Seschrock  *
8*ea8dc4b6Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*ea8dc4b6Seschrock  * or http://www.opensolaris.org/os/licensing.
10*ea8dc4b6Seschrock  * See the License for the specific language governing permissions
11*ea8dc4b6Seschrock  * and limitations under the License.
12*ea8dc4b6Seschrock  *
13*ea8dc4b6Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14*ea8dc4b6Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*ea8dc4b6Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16*ea8dc4b6Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17*ea8dc4b6Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18*ea8dc4b6Seschrock  *
19*ea8dc4b6Seschrock  * CDDL HEADER END
20*ea8dc4b6Seschrock  */
21*ea8dc4b6Seschrock /*
22*ea8dc4b6Seschrock  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*ea8dc4b6Seschrock  * Use is subject to license terms.
24*ea8dc4b6Seschrock  */
25*ea8dc4b6Seschrock 
26*ea8dc4b6Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*ea8dc4b6Seschrock 
28*ea8dc4b6Seschrock /*
29*ea8dc4b6Seschrock  * ZFS Fault Injector
30*ea8dc4b6Seschrock  *
31*ea8dc4b6Seschrock  * This userland component takes a set of options and uses libzpool to translate
32*ea8dc4b6Seschrock  * from a user-visible object type and name to an internal representation.
33*ea8dc4b6Seschrock  * There are two basic types of faults: device faults and data faults.
34*ea8dc4b6Seschrock  *
35*ea8dc4b6Seschrock  *
36*ea8dc4b6Seschrock  * DEVICE FAULTS
37*ea8dc4b6Seschrock  *
38*ea8dc4b6Seschrock  * Errors can be injected into a particular vdev using the '-d' option.  This
39*ea8dc4b6Seschrock  * option takes a path or vdev GUID to uniquely identify the device within a
40*ea8dc4b6Seschrock  * pool.  There are two types of errors that can be injected, EIO and ENXIO,
41*ea8dc4b6Seschrock  * that can be controlled through the '-t' option.  The default is ENXIO.  For
42*ea8dc4b6Seschrock  * EIO failures, any attempt to read data from the device will return EIO, but
43*ea8dc4b6Seschrock  * subsequent attempt to reopen the device will succeed.  For ENXIO failures,
44*ea8dc4b6Seschrock  * any attempt to read from the device will return EIO, but any attempt to
45*ea8dc4b6Seschrock  * reopen the device will also return ENXIO.
46*ea8dc4b6Seschrock  *
47*ea8dc4b6Seschrock  * This form of the command looks like:
48*ea8dc4b6Seschrock  *
49*ea8dc4b6Seschrock  * 	zinject -d device [-t type] pool
50*ea8dc4b6Seschrock  *
51*ea8dc4b6Seschrock  *
52*ea8dc4b6Seschrock  * DATA FAULTS
53*ea8dc4b6Seschrock  *
54*ea8dc4b6Seschrock  * We begin with a tuple of the form:
55*ea8dc4b6Seschrock  *
56*ea8dc4b6Seschrock  * 	<type,level,range,object>
57*ea8dc4b6Seschrock  *
58*ea8dc4b6Seschrock  * 	type	A string describing the type of data to target.  Each type
59*ea8dc4b6Seschrock  * 		implicitly describes how to interpret 'object'. Currently,
60*ea8dc4b6Seschrock  * 		the following values are supported:
61*ea8dc4b6Seschrock  *
62*ea8dc4b6Seschrock  * 		data		User data for a file
63*ea8dc4b6Seschrock  * 		dnode		Dnode for a file or directory
64*ea8dc4b6Seschrock  *
65*ea8dc4b6Seschrock  *		The following MOS objects are special.  Instead of injecting
66*ea8dc4b6Seschrock  *		errors on a particular object or blkid, we inject errors across
67*ea8dc4b6Seschrock  *		all objects of the given type.
68*ea8dc4b6Seschrock  *
69*ea8dc4b6Seschrock  * 		mos		Any data in the MOS
70*ea8dc4b6Seschrock  * 		mosdir		object directory
71*ea8dc4b6Seschrock  * 		config		pool configuration
72*ea8dc4b6Seschrock  * 		bplist		blkptr list
73*ea8dc4b6Seschrock  * 		spacemap	spacemap
74*ea8dc4b6Seschrock  * 		metaslab	metaslab
75*ea8dc4b6Seschrock  * 		errlog		persistent error log
76*ea8dc4b6Seschrock  *
77*ea8dc4b6Seschrock  * 	level	Object level.  Defaults to '0', not applicable to all types.  If
78*ea8dc4b6Seschrock  * 		a range is given, this corresponds to the indirect block
79*ea8dc4b6Seschrock  * 		corresponding to the specific range.
80*ea8dc4b6Seschrock  *
81*ea8dc4b6Seschrock  *	range	A numerical range [start,end) within the object.  Defaults to
82*ea8dc4b6Seschrock  *		the full size of the file.
83*ea8dc4b6Seschrock  *
84*ea8dc4b6Seschrock  * 	object	A string describing the logical location of the object.  For
85*ea8dc4b6Seschrock  * 		files and directories (currently the only supported types),
86*ea8dc4b6Seschrock  * 		this is the path of the object on disk.
87*ea8dc4b6Seschrock  *
88*ea8dc4b6Seschrock  * This is translated, via libzpool, into the following internal representation:
89*ea8dc4b6Seschrock  *
90*ea8dc4b6Seschrock  * 	<type,objset,object,level,range>
91*ea8dc4b6Seschrock  *
92*ea8dc4b6Seschrock  * These types should be self-explanatory.  This tuple is then passed to the
93*ea8dc4b6Seschrock  * kernel via a special ioctl() to initiate fault injection for the given
94*ea8dc4b6Seschrock  * object.  Note that 'type' is not strictly necessary for fault injection, but
95*ea8dc4b6Seschrock  * is used when translating existing faults into a human-readable string.
96*ea8dc4b6Seschrock  *
97*ea8dc4b6Seschrock  *
98*ea8dc4b6Seschrock  * The command itself takes one of the forms:
99*ea8dc4b6Seschrock  *
100*ea8dc4b6Seschrock  * 	zinject
101*ea8dc4b6Seschrock  * 	zinject <-a | -u pool>
102*ea8dc4b6Seschrock  * 	zinject -c <id|all>
103*ea8dc4b6Seschrock  * 	zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
104*ea8dc4b6Seschrock  *	    [-r range] <object>
105*ea8dc4b6Seschrock  * 	zinject [-f freq] [-a] [-m] [-u] -b objset:object:level:start:end pool
106*ea8dc4b6Seschrock  *
107*ea8dc4b6Seschrock  * With no arguments, the command prints all currently registered injection
108*ea8dc4b6Seschrock  * handlers, with their numeric identifiers.
109*ea8dc4b6Seschrock  *
110*ea8dc4b6Seschrock  * The '-c' option will clear the given handler, or all handlers if 'all' is
111*ea8dc4b6Seschrock  * specified.
112*ea8dc4b6Seschrock  *
113*ea8dc4b6Seschrock  * The '-e' option takes a string describing the errno to simulate.  This must
114*ea8dc4b6Seschrock  * be either 'io' or 'checksum'.  In most cases this will result in the same
115*ea8dc4b6Seschrock  * behavior, but RAID-Z will produce a different set of ereports for this
116*ea8dc4b6Seschrock  * situation.
117*ea8dc4b6Seschrock  *
118*ea8dc4b6Seschrock  * The '-a', '-u', and '-m' flags toggle internal flush behavior.  If '-a' is
119*ea8dc4b6Seschrock  * specified, then the ARC cache is flushed appropriately.  If '-u' is
120*ea8dc4b6Seschrock  * specified, then the underlying SPA is unloaded.  Either of these flags can be
121*ea8dc4b6Seschrock  * specified independently of any other handlers.  The '-m' flag automatically
122*ea8dc4b6Seschrock  * does an unmount and remount of the underlying dataset to aid in flushing the
123*ea8dc4b6Seschrock  * cache.
124*ea8dc4b6Seschrock  *
125*ea8dc4b6Seschrock  * The '-f' flag controls the frequency of errors injected, expressed as a
126*ea8dc4b6Seschrock  * integer percentage between 1 and 100.  The default is 100.
127*ea8dc4b6Seschrock  *
128*ea8dc4b6Seschrock  * The this form is responsible for actually injecting the handler into the
129*ea8dc4b6Seschrock  * framework.  It takes the arguments described above, translates them to the
130*ea8dc4b6Seschrock  * internal tuple using libzpool, and then issues an ioctl() to register the
131*ea8dc4b6Seschrock  * handler.
132*ea8dc4b6Seschrock  *
133*ea8dc4b6Seschrock  * The final form can target a specific bookmark, regardless of whether a
134*ea8dc4b6Seschrock  * human-readable interface has been designed.  It allows developers to specify
135*ea8dc4b6Seschrock  * a particular block by number.
136*ea8dc4b6Seschrock  */
137*ea8dc4b6Seschrock 
138*ea8dc4b6Seschrock #include <errno.h>
139*ea8dc4b6Seschrock #include <fcntl.h>
140*ea8dc4b6Seschrock #include <stdio.h>
141*ea8dc4b6Seschrock #include <stdlib.h>
142*ea8dc4b6Seschrock #include <strings.h>
143*ea8dc4b6Seschrock #include <unistd.h>
144*ea8dc4b6Seschrock 
145*ea8dc4b6Seschrock #include <sys/fs/zfs.h>
146*ea8dc4b6Seschrock #include <sys/mount.h>
147*ea8dc4b6Seschrock 
148*ea8dc4b6Seschrock #include <libzfs.h>
149*ea8dc4b6Seschrock 
150*ea8dc4b6Seschrock #undef verify	/* both libzfs.h and zfs_context.h want to define this */
151*ea8dc4b6Seschrock 
152*ea8dc4b6Seschrock #include "zinject.h"
153*ea8dc4b6Seschrock 
154*ea8dc4b6Seschrock int zfs_fd;
155*ea8dc4b6Seschrock 
156*ea8dc4b6Seschrock #define	ECKSUM	EBADE
157*ea8dc4b6Seschrock 
158*ea8dc4b6Seschrock static const char *errtable[TYPE_INVAL] = {
159*ea8dc4b6Seschrock 	"data",
160*ea8dc4b6Seschrock 	"dnode",
161*ea8dc4b6Seschrock 	"mos",
162*ea8dc4b6Seschrock 	"mosdir",
163*ea8dc4b6Seschrock 	"metaslab",
164*ea8dc4b6Seschrock 	"config",
165*ea8dc4b6Seschrock 	"bplist",
166*ea8dc4b6Seschrock 	"spacemap",
167*ea8dc4b6Seschrock 	"errlog"
168*ea8dc4b6Seschrock };
169*ea8dc4b6Seschrock 
170*ea8dc4b6Seschrock static err_type_t
171*ea8dc4b6Seschrock name_to_type(const char *arg)
172*ea8dc4b6Seschrock {
173*ea8dc4b6Seschrock 	int i;
174*ea8dc4b6Seschrock 	for (i = 0; i < TYPE_INVAL; i++)
175*ea8dc4b6Seschrock 		if (strcmp(errtable[i], arg) == 0)
176*ea8dc4b6Seschrock 			return (i);
177*ea8dc4b6Seschrock 
178*ea8dc4b6Seschrock 	return (TYPE_INVAL);
179*ea8dc4b6Seschrock }
180*ea8dc4b6Seschrock 
181*ea8dc4b6Seschrock static const char *
182*ea8dc4b6Seschrock type_to_name(uint64_t type)
183*ea8dc4b6Seschrock {
184*ea8dc4b6Seschrock 	switch (type) {
185*ea8dc4b6Seschrock 	case DMU_OT_OBJECT_DIRECTORY:
186*ea8dc4b6Seschrock 		return ("mosdir");
187*ea8dc4b6Seschrock 	case DMU_OT_OBJECT_ARRAY:
188*ea8dc4b6Seschrock 		return ("metaslab");
189*ea8dc4b6Seschrock 	case DMU_OT_PACKED_NVLIST:
190*ea8dc4b6Seschrock 		return ("config");
191*ea8dc4b6Seschrock 	case DMU_OT_BPLIST:
192*ea8dc4b6Seschrock 		return ("bplist");
193*ea8dc4b6Seschrock 	case DMU_OT_SPACE_MAP:
194*ea8dc4b6Seschrock 		return ("spacemap");
195*ea8dc4b6Seschrock 	case DMU_OT_ERROR_LOG:
196*ea8dc4b6Seschrock 		return ("errlog");
197*ea8dc4b6Seschrock 	default:
198*ea8dc4b6Seschrock 		return ("-");
199*ea8dc4b6Seschrock 	}
200*ea8dc4b6Seschrock }
201*ea8dc4b6Seschrock 
202*ea8dc4b6Seschrock 
203*ea8dc4b6Seschrock /*
204*ea8dc4b6Seschrock  * Print usage message.
205*ea8dc4b6Seschrock  */
206*ea8dc4b6Seschrock void
207*ea8dc4b6Seschrock usage(void)
208*ea8dc4b6Seschrock {
209*ea8dc4b6Seschrock 	(void) printf(
210*ea8dc4b6Seschrock 	    "usage:\n"
211*ea8dc4b6Seschrock 	    "\n"
212*ea8dc4b6Seschrock 	    "\tzinject\n"
213*ea8dc4b6Seschrock 	    "\n"
214*ea8dc4b6Seschrock 	    "\t\tList all active injection records.\n"
215*ea8dc4b6Seschrock 	    "\n"
216*ea8dc4b6Seschrock 	    "\tzinject -c <id|all>\n"
217*ea8dc4b6Seschrock 	    "\n"
218*ea8dc4b6Seschrock 	    "\t\tClear the particular record (if given a numeric ID), or\n"
219*ea8dc4b6Seschrock 	    "\t\tall records if 'all' is specificed.\n"
220*ea8dc4b6Seschrock 	    "\n"
221*ea8dc4b6Seschrock 	    "\tzinject -d device [-e errno] pool\n"
222*ea8dc4b6Seschrock 	    "\t\tInject a fault into a particular device.  'errno' can either\n"
223*ea8dc4b6Seschrock 	    "\t\tbe 'nxio' (the default) or 'io'.\n"
224*ea8dc4b6Seschrock 	    "\n"
225*ea8dc4b6Seschrock 	    "\tzinject -b objset:object:level:blkid pool\n"
226*ea8dc4b6Seschrock 	    "\n"
227*ea8dc4b6Seschrock 	    "\t\tInject an error into pool 'pool' with the numeric bookmark\n"
228*ea8dc4b6Seschrock 	    "\t\tspecified by the remaining tuple.  Each number is in\n"
229*ea8dc4b6Seschrock 	    "\t\thexidecimal, and only one block can be specified.\n"
230*ea8dc4b6Seschrock 	    "\n"
231*ea8dc4b6Seschrock 	    "\tzinject [-q] <-t type> [-e errno] [-l level] [-r range]\n"
232*ea8dc4b6Seschrock 	    "\t    [-a] [-m] [-u] [-f freq] <object>\n"
233*ea8dc4b6Seschrock 	    "\n"
234*ea8dc4b6Seschrock 	    "\t\tInject an error into the object specified by the '-t' option\n"
235*ea8dc4b6Seschrock 	    "\t\tand the object descriptor.  The 'object' parameter is\n"
236*ea8dc4b6Seschrock 	    "\t\tinterperted depending on the '-t' option.\n"
237*ea8dc4b6Seschrock 	    "\n"
238*ea8dc4b6Seschrock 	    "\t\t-q\tQuiet mode.  Only print out the handler number added.\n"
239*ea8dc4b6Seschrock 	    "\t\t-e\tInject a specific error.  Must be either 'io' or\n"
240*ea8dc4b6Seschrock 	    "\t\t\t'checksum'.  Default is 'io'.\n"
241*ea8dc4b6Seschrock 	    "\t\t-l\tInject error at a particular block level. Default is "
242*ea8dc4b6Seschrock 	    "0.\n"
243*ea8dc4b6Seschrock 	    "\t\t-m\tAutomatically remount underlying filesystem.\n"
244*ea8dc4b6Seschrock 	    "\t\t-r\tInject error over a particular logical range of an\n"
245*ea8dc4b6Seschrock 	    "\t\t\tobject.  Will be translated to the appropriate blkid\n"
246*ea8dc4b6Seschrock 	    "\t\t\trange according to the object's properties.\n"
247*ea8dc4b6Seschrock 	    "\t\t-a\tFlush the ARC cache.  Can be specified without any\n"
248*ea8dc4b6Seschrock 	    "\t\t\tassociated object.\n"
249*ea8dc4b6Seschrock 	    "\t\t-u\tUnload the associated pool.  Can be specified with only\n"
250*ea8dc4b6Seschrock 	    "\t\t\ta pool object.\n"
251*ea8dc4b6Seschrock 	    "\t\t-f\tOnly inject errors a fraction of the time.  Expressed as\n"
252*ea8dc4b6Seschrock 	    "\t\t\ta percentage between 1 and 100.\n"
253*ea8dc4b6Seschrock 	    "\n"
254*ea8dc4b6Seschrock 	    "\t-t data\t\tInject an error into the plain file contents of a\n"
255*ea8dc4b6Seschrock 	    "\t\t\tfile.  The object must be specified as a complete path\n"
256*ea8dc4b6Seschrock 	    "\t\t\tto a file on a ZFS filesystem.\n"
257*ea8dc4b6Seschrock 	    "\n"
258*ea8dc4b6Seschrock 	    "\t-t dnode\tInject an error into the metadnode in the block\n"
259*ea8dc4b6Seschrock 	    "\t\t\tcorresponding to the dnode for a file or directory.  The\n"
260*ea8dc4b6Seschrock 	    "\t\t\t'-r' option is incompatible with this mode.  The object\n"
261*ea8dc4b6Seschrock 	    "\t\t\tis specified as a complete path to a file or directory\n"
262*ea8dc4b6Seschrock 	    "\t\t\ton a ZFS filesystem.\n"
263*ea8dc4b6Seschrock 	    "\n"
264*ea8dc4b6Seschrock 	    "\t-t <mos>\tInject errors into the MOS for objects of the given\n"
265*ea8dc4b6Seschrock 	    "\t\t\ttype.  Valid types are: mos, mosdir, config, bplist,\n"
266*ea8dc4b6Seschrock 	    "\t\t\tspacemap, metaslab, errlog\n");
267*ea8dc4b6Seschrock }
268*ea8dc4b6Seschrock 
269*ea8dc4b6Seschrock static int
270*ea8dc4b6Seschrock iter_handlers(int (*func)(int, const char *, zinject_record_t *, void *),
271*ea8dc4b6Seschrock     void *data)
272*ea8dc4b6Seschrock {
273*ea8dc4b6Seschrock 	zfs_cmd_t zc;
274*ea8dc4b6Seschrock 	int ret;
275*ea8dc4b6Seschrock 
276*ea8dc4b6Seschrock 	zc.zc_guid = 0;
277*ea8dc4b6Seschrock 
278*ea8dc4b6Seschrock 	while (ioctl(zfs_fd, ZFS_IOC_INJECT_LIST_NEXT, &zc) == 0)
279*ea8dc4b6Seschrock 		if ((ret = func((int)zc.zc_guid, zc.zc_name,
280*ea8dc4b6Seschrock 		    &zc.zc_inject_record, data)) != 0)
281*ea8dc4b6Seschrock 			return (ret);
282*ea8dc4b6Seschrock 
283*ea8dc4b6Seschrock 	return (0);
284*ea8dc4b6Seschrock }
285*ea8dc4b6Seschrock 
286*ea8dc4b6Seschrock static int
287*ea8dc4b6Seschrock print_data_handler(int id, const char *pool, zinject_record_t *record,
288*ea8dc4b6Seschrock     void *data)
289*ea8dc4b6Seschrock {
290*ea8dc4b6Seschrock 	int *count = data;
291*ea8dc4b6Seschrock 
292*ea8dc4b6Seschrock 	if (record->zi_guid != 0)
293*ea8dc4b6Seschrock 		return (0);
294*ea8dc4b6Seschrock 
295*ea8dc4b6Seschrock 	if (*count == 0) {
296*ea8dc4b6Seschrock 		(void) printf("%3s  %-15s  %-6s  %-6s  %-8s  %3s  %-15s\n",
297*ea8dc4b6Seschrock 		    "ID", "POOL", "OBJSET", "OBJECT", "TYPE", "LVL",  "RANGE");
298*ea8dc4b6Seschrock 		(void) printf("---  ---------------  ------  "
299*ea8dc4b6Seschrock 		    "------  --------  ---  ---------------\n");
300*ea8dc4b6Seschrock 	}
301*ea8dc4b6Seschrock 
302*ea8dc4b6Seschrock 	*count += 1;
303*ea8dc4b6Seschrock 
304*ea8dc4b6Seschrock 	(void) printf("%3d  %-15s  %-6llu  %-6llu  %-8s  %3d  ", id, pool,
305*ea8dc4b6Seschrock 	    (u_longlong_t)record->zi_objset, (u_longlong_t)record->zi_object,
306*ea8dc4b6Seschrock 	    type_to_name(record->zi_type), record->zi_level);
307*ea8dc4b6Seschrock 
308*ea8dc4b6Seschrock 	if (record->zi_start == 0 &&
309*ea8dc4b6Seschrock 	    record->zi_end == -1ULL)
310*ea8dc4b6Seschrock 		(void) printf("all\n");
311*ea8dc4b6Seschrock 	else
312*ea8dc4b6Seschrock 		(void) printf("[%llu, %llu]\n", (u_longlong_t)record->zi_start,
313*ea8dc4b6Seschrock 		    (u_longlong_t)record->zi_end);
314*ea8dc4b6Seschrock 
315*ea8dc4b6Seschrock 	return (0);
316*ea8dc4b6Seschrock }
317*ea8dc4b6Seschrock 
318*ea8dc4b6Seschrock static int
319*ea8dc4b6Seschrock print_device_handler(int id, const char *pool, zinject_record_t *record,
320*ea8dc4b6Seschrock     void *data)
321*ea8dc4b6Seschrock {
322*ea8dc4b6Seschrock 	int *count = data;
323*ea8dc4b6Seschrock 
324*ea8dc4b6Seschrock 	if (record->zi_guid == 0)
325*ea8dc4b6Seschrock 		return (0);
326*ea8dc4b6Seschrock 
327*ea8dc4b6Seschrock 	if (*count == 0) {
328*ea8dc4b6Seschrock 		(void) printf("%3s  %-15s  %s\n", "ID", "POOL", "GUID");
329*ea8dc4b6Seschrock 		(void) printf("---  ---------------  ----------------\n");
330*ea8dc4b6Seschrock 	}
331*ea8dc4b6Seschrock 
332*ea8dc4b6Seschrock 	*count += 1;
333*ea8dc4b6Seschrock 
334*ea8dc4b6Seschrock 	(void) printf("%3d  %-15s  %llx\n", id, pool,
335*ea8dc4b6Seschrock 	    (u_longlong_t)record->zi_guid);
336*ea8dc4b6Seschrock 
337*ea8dc4b6Seschrock 	return (0);
338*ea8dc4b6Seschrock }
339*ea8dc4b6Seschrock 
340*ea8dc4b6Seschrock /*
341*ea8dc4b6Seschrock  * Print all registered error handlers.  Returns the number of handlers
342*ea8dc4b6Seschrock  * registered.
343*ea8dc4b6Seschrock  */
344*ea8dc4b6Seschrock static int
345*ea8dc4b6Seschrock print_all_handlers(void)
346*ea8dc4b6Seschrock {
347*ea8dc4b6Seschrock 	int count = 0;
348*ea8dc4b6Seschrock 
349*ea8dc4b6Seschrock 	(void) iter_handlers(print_device_handler, &count);
350*ea8dc4b6Seschrock 	(void) printf("\n");
351*ea8dc4b6Seschrock 	count = 0;
352*ea8dc4b6Seschrock 	(void) iter_handlers(print_data_handler, &count);
353*ea8dc4b6Seschrock 
354*ea8dc4b6Seschrock 	return (count);
355*ea8dc4b6Seschrock }
356*ea8dc4b6Seschrock 
357*ea8dc4b6Seschrock /* ARGSUSED */
358*ea8dc4b6Seschrock static int
359*ea8dc4b6Seschrock cancel_one_handler(int id, const char *pool, zinject_record_t *record,
360*ea8dc4b6Seschrock     void *data)
361*ea8dc4b6Seschrock {
362*ea8dc4b6Seschrock 	zfs_cmd_t zc;
363*ea8dc4b6Seschrock 
364*ea8dc4b6Seschrock 	zc.zc_guid = (uint64_t)id;
365*ea8dc4b6Seschrock 
366*ea8dc4b6Seschrock 	if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
367*ea8dc4b6Seschrock 		(void) fprintf(stderr, "failed to remove handler %d: %s\n",
368*ea8dc4b6Seschrock 		    id, strerror(errno));
369*ea8dc4b6Seschrock 		return (1);
370*ea8dc4b6Seschrock 	}
371*ea8dc4b6Seschrock 
372*ea8dc4b6Seschrock 	return (0);
373*ea8dc4b6Seschrock }
374*ea8dc4b6Seschrock 
375*ea8dc4b6Seschrock /*
376*ea8dc4b6Seschrock  * Remove all fault injection handlers.
377*ea8dc4b6Seschrock  */
378*ea8dc4b6Seschrock static int
379*ea8dc4b6Seschrock cancel_all_handlers(void)
380*ea8dc4b6Seschrock {
381*ea8dc4b6Seschrock 	int ret = iter_handlers(cancel_one_handler, NULL);
382*ea8dc4b6Seschrock 
383*ea8dc4b6Seschrock 	(void) printf("removed all registered handlers\n");
384*ea8dc4b6Seschrock 
385*ea8dc4b6Seschrock 	return (ret);
386*ea8dc4b6Seschrock }
387*ea8dc4b6Seschrock 
388*ea8dc4b6Seschrock /*
389*ea8dc4b6Seschrock  * Remove a specific fault injection handler.
390*ea8dc4b6Seschrock  */
391*ea8dc4b6Seschrock static int
392*ea8dc4b6Seschrock cancel_handler(int id)
393*ea8dc4b6Seschrock {
394*ea8dc4b6Seschrock 	zfs_cmd_t zc;
395*ea8dc4b6Seschrock 
396*ea8dc4b6Seschrock 	zc.zc_guid = (uint64_t)id;
397*ea8dc4b6Seschrock 
398*ea8dc4b6Seschrock 	if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
399*ea8dc4b6Seschrock 		(void) fprintf(stderr, "failed to remove handler %d: %s\n",
400*ea8dc4b6Seschrock 		    id, strerror(errno));
401*ea8dc4b6Seschrock 		return (1);
402*ea8dc4b6Seschrock 	}
403*ea8dc4b6Seschrock 
404*ea8dc4b6Seschrock 	(void) printf("removed handler %d\n", id);
405*ea8dc4b6Seschrock 
406*ea8dc4b6Seschrock 	return (0);
407*ea8dc4b6Seschrock }
408*ea8dc4b6Seschrock 
409*ea8dc4b6Seschrock /*
410*ea8dc4b6Seschrock  * Register a new fault injection handler.
411*ea8dc4b6Seschrock  */
412*ea8dc4b6Seschrock static int
413*ea8dc4b6Seschrock register_handler(const char *pool, int flags, zinject_record_t *record,
414*ea8dc4b6Seschrock     int quiet)
415*ea8dc4b6Seschrock {
416*ea8dc4b6Seschrock 	zfs_cmd_t zc;
417*ea8dc4b6Seschrock 
418*ea8dc4b6Seschrock 	(void) strcpy(zc.zc_name, pool);
419*ea8dc4b6Seschrock 	zc.zc_inject_record = *record;
420*ea8dc4b6Seschrock 	zc.zc_guid = flags;
421*ea8dc4b6Seschrock 
422*ea8dc4b6Seschrock 	if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
423*ea8dc4b6Seschrock 		(void) fprintf(stderr, "failed to add handler: %s\n",
424*ea8dc4b6Seschrock 		    strerror(errno));
425*ea8dc4b6Seschrock 		return (1);
426*ea8dc4b6Seschrock 	}
427*ea8dc4b6Seschrock 
428*ea8dc4b6Seschrock 	if (flags & ZINJECT_NULL)
429*ea8dc4b6Seschrock 		return (0);
430*ea8dc4b6Seschrock 
431*ea8dc4b6Seschrock 	if (quiet) {
432*ea8dc4b6Seschrock 		(void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
433*ea8dc4b6Seschrock 	} else {
434*ea8dc4b6Seschrock 		(void) printf("Added handler %llu with the following "
435*ea8dc4b6Seschrock 		    "properties:\n", (u_longlong_t)zc.zc_guid);
436*ea8dc4b6Seschrock 		(void) printf("  pool: %s\n", pool);
437*ea8dc4b6Seschrock 		if (record->zi_guid) {
438*ea8dc4b6Seschrock 			(void) printf("  vdev: %llx\n",
439*ea8dc4b6Seschrock 			    (u_longlong_t)record->zi_guid);
440*ea8dc4b6Seschrock 		} else {
441*ea8dc4b6Seschrock 			(void) printf("objset: %llu\n",
442*ea8dc4b6Seschrock 			    (u_longlong_t)record->zi_objset);
443*ea8dc4b6Seschrock 			(void) printf("object: %llu\n",
444*ea8dc4b6Seschrock 			    (u_longlong_t)record->zi_object);
445*ea8dc4b6Seschrock 			(void) printf("  type: %llu\n",
446*ea8dc4b6Seschrock 			    (u_longlong_t)record->zi_type);
447*ea8dc4b6Seschrock 			(void) printf(" level: %d\n", record->zi_level);
448*ea8dc4b6Seschrock 			if (record->zi_start == 0 &&
449*ea8dc4b6Seschrock 			    record->zi_end == -1ULL)
450*ea8dc4b6Seschrock 				(void) printf(" range: all\n");
451*ea8dc4b6Seschrock 			else
452*ea8dc4b6Seschrock 				(void) printf(" range: [%llu, %llu)\n",
453*ea8dc4b6Seschrock 				    (u_longlong_t)record->zi_start,
454*ea8dc4b6Seschrock 				    (u_longlong_t)record->zi_end);
455*ea8dc4b6Seschrock 		}
456*ea8dc4b6Seschrock 	}
457*ea8dc4b6Seschrock 
458*ea8dc4b6Seschrock 	return (0);
459*ea8dc4b6Seschrock }
460*ea8dc4b6Seschrock 
461*ea8dc4b6Seschrock int
462*ea8dc4b6Seschrock main(int argc, char **argv)
463*ea8dc4b6Seschrock {
464*ea8dc4b6Seschrock 	int c;
465*ea8dc4b6Seschrock 	char *range = NULL;
466*ea8dc4b6Seschrock 	char *cancel = NULL;
467*ea8dc4b6Seschrock 	char *end;
468*ea8dc4b6Seschrock 	char *raw = NULL;
469*ea8dc4b6Seschrock 	char *device = NULL;
470*ea8dc4b6Seschrock 	int level = 0;
471*ea8dc4b6Seschrock 	int quiet = 0;
472*ea8dc4b6Seschrock 	int error = 0;
473*ea8dc4b6Seschrock 	int domount = 0;
474*ea8dc4b6Seschrock 	err_type_t type = TYPE_INVAL;
475*ea8dc4b6Seschrock 	zinject_record_t record = { 0 };
476*ea8dc4b6Seschrock 	char pool[MAXNAMELEN];
477*ea8dc4b6Seschrock 	char dataset[MAXNAMELEN];
478*ea8dc4b6Seschrock 	zfs_handle_t *zhp;
479*ea8dc4b6Seschrock 	int ret;
480*ea8dc4b6Seschrock 	int flags = 0;
481*ea8dc4b6Seschrock 
482*ea8dc4b6Seschrock 	if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
483*ea8dc4b6Seschrock 		(void) fprintf(stderr, "failed to open ZFS device\n");
484*ea8dc4b6Seschrock 		return (1);
485*ea8dc4b6Seschrock 	}
486*ea8dc4b6Seschrock 
487*ea8dc4b6Seschrock 	if (argc == 1) {
488*ea8dc4b6Seschrock 		/*
489*ea8dc4b6Seschrock 		 * No arguments.  Print the available handlers.  If there are no
490*ea8dc4b6Seschrock 		 * available handlers, direct the user to '-h' for help
491*ea8dc4b6Seschrock 		 * information.
492*ea8dc4b6Seschrock 		 */
493*ea8dc4b6Seschrock 		if (print_all_handlers() == 0) {
494*ea8dc4b6Seschrock 			(void) printf("No handlers registered.\n");
495*ea8dc4b6Seschrock 			(void) printf("Run 'zinject -h' for usage "
496*ea8dc4b6Seschrock 			    "information.\n");
497*ea8dc4b6Seschrock 		}
498*ea8dc4b6Seschrock 
499*ea8dc4b6Seschrock 		return (0);
500*ea8dc4b6Seschrock 	}
501*ea8dc4b6Seschrock 
502*ea8dc4b6Seschrock 	while ((c = getopt(argc, argv, ":ab:d:f:qhc:t:l:mr:e:u")) != -1) {
503*ea8dc4b6Seschrock 		switch (c) {
504*ea8dc4b6Seschrock 		case 'a':
505*ea8dc4b6Seschrock 			flags |= ZINJECT_FLUSH_ARC;
506*ea8dc4b6Seschrock 			break;
507*ea8dc4b6Seschrock 		case 'b':
508*ea8dc4b6Seschrock 			raw = optarg;
509*ea8dc4b6Seschrock 			break;
510*ea8dc4b6Seschrock 		case 'c':
511*ea8dc4b6Seschrock 			cancel = optarg;
512*ea8dc4b6Seschrock 			break;
513*ea8dc4b6Seschrock 		case 'd':
514*ea8dc4b6Seschrock 			device = optarg;
515*ea8dc4b6Seschrock 			break;
516*ea8dc4b6Seschrock 		case 'e':
517*ea8dc4b6Seschrock 			if (strcasecmp(optarg, "io") == 0) {
518*ea8dc4b6Seschrock 				error = EIO;
519*ea8dc4b6Seschrock 			} else if (strcasecmp(optarg, "checksum") == 0) {
520*ea8dc4b6Seschrock 				error = ECKSUM;
521*ea8dc4b6Seschrock 			} else if (strcasecmp(optarg, "nxio") == 0) {
522*ea8dc4b6Seschrock 				error = ENXIO;
523*ea8dc4b6Seschrock 			} else {
524*ea8dc4b6Seschrock 				(void) fprintf(stderr, "invalid error type "
525*ea8dc4b6Seschrock 				    "'%s': must be 'io', 'checksum' or "
526*ea8dc4b6Seschrock 				    "'nxio'\n", optarg);
527*ea8dc4b6Seschrock 				usage();
528*ea8dc4b6Seschrock 				return (1);
529*ea8dc4b6Seschrock 			}
530*ea8dc4b6Seschrock 			break;
531*ea8dc4b6Seschrock 		case 'f':
532*ea8dc4b6Seschrock 			record.zi_freq = atoi(optarg);
533*ea8dc4b6Seschrock 			if (record.zi_freq < 1 || record.zi_freq > 100) {
534*ea8dc4b6Seschrock 				(void) fprintf(stderr, "frequency range must "
535*ea8dc4b6Seschrock 				    "be in the range (0, 100]\n");
536*ea8dc4b6Seschrock 				return (1);
537*ea8dc4b6Seschrock 			}
538*ea8dc4b6Seschrock 			break;
539*ea8dc4b6Seschrock 		case 'h':
540*ea8dc4b6Seschrock 			usage();
541*ea8dc4b6Seschrock 			return (0);
542*ea8dc4b6Seschrock 		case 'l':
543*ea8dc4b6Seschrock 			level = (int)strtol(optarg, &end, 10);
544*ea8dc4b6Seschrock 			if (*end != '\0') {
545*ea8dc4b6Seschrock 				(void) fprintf(stderr, "invalid level '%s': "
546*ea8dc4b6Seschrock 				    "must be an integer\n", optarg);
547*ea8dc4b6Seschrock 				usage();
548*ea8dc4b6Seschrock 				return (1);
549*ea8dc4b6Seschrock 			}
550*ea8dc4b6Seschrock 			break;
551*ea8dc4b6Seschrock 		case 'm':
552*ea8dc4b6Seschrock 			domount = 1;
553*ea8dc4b6Seschrock 			break;
554*ea8dc4b6Seschrock 		case 'q':
555*ea8dc4b6Seschrock 			quiet = 1;
556*ea8dc4b6Seschrock 			break;
557*ea8dc4b6Seschrock 		case 'r':
558*ea8dc4b6Seschrock 			range = optarg;
559*ea8dc4b6Seschrock 			break;
560*ea8dc4b6Seschrock 		case 't':
561*ea8dc4b6Seschrock 			if ((type = name_to_type(optarg)) == TYPE_INVAL) {
562*ea8dc4b6Seschrock 				(void) fprintf(stderr, "invalid type '%s'\n",
563*ea8dc4b6Seschrock 				    optarg);
564*ea8dc4b6Seschrock 				usage();
565*ea8dc4b6Seschrock 				return (1);
566*ea8dc4b6Seschrock 			}
567*ea8dc4b6Seschrock 			break;
568*ea8dc4b6Seschrock 		case 'u':
569*ea8dc4b6Seschrock 			flags |= ZINJECT_UNLOAD_SPA;
570*ea8dc4b6Seschrock 			break;
571*ea8dc4b6Seschrock 		case ':':
572*ea8dc4b6Seschrock 			(void) fprintf(stderr, "option -%c requires an "
573*ea8dc4b6Seschrock 			    "operand\n", optopt);
574*ea8dc4b6Seschrock 			usage();
575*ea8dc4b6Seschrock 			return (1);
576*ea8dc4b6Seschrock 		case '?':
577*ea8dc4b6Seschrock 			(void) fprintf(stderr, "invalid option '%c'\n",
578*ea8dc4b6Seschrock 			    optopt);
579*ea8dc4b6Seschrock 			usage();
580*ea8dc4b6Seschrock 			return (2);
581*ea8dc4b6Seschrock 		}
582*ea8dc4b6Seschrock 	}
583*ea8dc4b6Seschrock 
584*ea8dc4b6Seschrock 	argc -= optind;
585*ea8dc4b6Seschrock 	argv += optind;
586*ea8dc4b6Seschrock 
587*ea8dc4b6Seschrock 	if (cancel != NULL) {
588*ea8dc4b6Seschrock 		/*
589*ea8dc4b6Seschrock 		 * '-c' is invalid with any other options.
590*ea8dc4b6Seschrock 		 */
591*ea8dc4b6Seschrock 		if (raw != NULL || range != NULL || type != TYPE_INVAL ||
592*ea8dc4b6Seschrock 		    level != 0) {
593*ea8dc4b6Seschrock 			(void) fprintf(stderr, "cancel (-c) incompatible with "
594*ea8dc4b6Seschrock 			    "any other options\n");
595*ea8dc4b6Seschrock 			usage();
596*ea8dc4b6Seschrock 			return (2);
597*ea8dc4b6Seschrock 		}
598*ea8dc4b6Seschrock 		if (argc != 0) {
599*ea8dc4b6Seschrock 			(void) fprintf(stderr, "extraneous argument to '-c'\n");
600*ea8dc4b6Seschrock 			usage();
601*ea8dc4b6Seschrock 			return (2);
602*ea8dc4b6Seschrock 		}
603*ea8dc4b6Seschrock 
604*ea8dc4b6Seschrock 		if (strcmp(cancel, "all") == 0) {
605*ea8dc4b6Seschrock 			return (cancel_all_handlers());
606*ea8dc4b6Seschrock 		} else {
607*ea8dc4b6Seschrock 			int id = (int)strtol(cancel, &end, 10);
608*ea8dc4b6Seschrock 			if (*end != '\0') {
609*ea8dc4b6Seschrock 				(void) fprintf(stderr, "invalid handle id '%s':"
610*ea8dc4b6Seschrock 				    " must be an integer or 'all'\n", cancel);
611*ea8dc4b6Seschrock 				usage();
612*ea8dc4b6Seschrock 				return (1);
613*ea8dc4b6Seschrock 			}
614*ea8dc4b6Seschrock 			return (cancel_handler(id));
615*ea8dc4b6Seschrock 		}
616*ea8dc4b6Seschrock 	}
617*ea8dc4b6Seschrock 
618*ea8dc4b6Seschrock 	if (device != NULL) {
619*ea8dc4b6Seschrock 		/*
620*ea8dc4b6Seschrock 		 * Device (-d) injection uses a completely different mechanism
621*ea8dc4b6Seschrock 		 * for doing injection, so handle it separately here.
622*ea8dc4b6Seschrock 		 */
623*ea8dc4b6Seschrock 		if (raw != NULL || range != NULL || type != TYPE_INVAL ||
624*ea8dc4b6Seschrock 		    level != 0) {
625*ea8dc4b6Seschrock 			(void) fprintf(stderr, "device (-d) incompatible with "
626*ea8dc4b6Seschrock 			    "data error injection\n");
627*ea8dc4b6Seschrock 			usage();
628*ea8dc4b6Seschrock 			return (2);
629*ea8dc4b6Seschrock 		}
630*ea8dc4b6Seschrock 
631*ea8dc4b6Seschrock 		if (argc != 1) {
632*ea8dc4b6Seschrock 			(void) fprintf(stderr, "device (-d) injection requires "
633*ea8dc4b6Seschrock 			    "a single pool name\n");
634*ea8dc4b6Seschrock 			usage();
635*ea8dc4b6Seschrock 			return (2);
636*ea8dc4b6Seschrock 		}
637*ea8dc4b6Seschrock 
638*ea8dc4b6Seschrock 		(void) strcpy(pool, argv[0]);
639*ea8dc4b6Seschrock 		dataset[0] = '\0';
640*ea8dc4b6Seschrock 
641*ea8dc4b6Seschrock 		if (error == ECKSUM) {
642*ea8dc4b6Seschrock 			(void) fprintf(stderr, "device error type must be "
643*ea8dc4b6Seschrock 			    "'io' or 'nxio'\n");
644*ea8dc4b6Seschrock 			return (1);
645*ea8dc4b6Seschrock 		}
646*ea8dc4b6Seschrock 
647*ea8dc4b6Seschrock 		if (translate_device(pool, device, &record) != 0)
648*ea8dc4b6Seschrock 			return (1);
649*ea8dc4b6Seschrock 		if (!error)
650*ea8dc4b6Seschrock 			error = ENXIO;
651*ea8dc4b6Seschrock 	} else if (raw != NULL) {
652*ea8dc4b6Seschrock 		if (range != NULL || type != TYPE_INVAL || level != 0) {
653*ea8dc4b6Seschrock 			(void) fprintf(stderr, "raw (-b) format with "
654*ea8dc4b6Seschrock 			    "any other options\n");
655*ea8dc4b6Seschrock 			usage();
656*ea8dc4b6Seschrock 			return (2);
657*ea8dc4b6Seschrock 		}
658*ea8dc4b6Seschrock 
659*ea8dc4b6Seschrock 		if (argc != 1) {
660*ea8dc4b6Seschrock 			(void) fprintf(stderr, "raw (-b) format expects a "
661*ea8dc4b6Seschrock 			    "single pool name\n");
662*ea8dc4b6Seschrock 			usage();
663*ea8dc4b6Seschrock 			return (2);
664*ea8dc4b6Seschrock 		}
665*ea8dc4b6Seschrock 
666*ea8dc4b6Seschrock 		(void) strcpy(pool, argv[0]);
667*ea8dc4b6Seschrock 		dataset[0] = '\0';
668*ea8dc4b6Seschrock 
669*ea8dc4b6Seschrock 		if (error == ENXIO) {
670*ea8dc4b6Seschrock 			(void) fprintf(stderr, "data error type must be "
671*ea8dc4b6Seschrock 			    "'checksum' or 'io'\n");
672*ea8dc4b6Seschrock 			return (1);
673*ea8dc4b6Seschrock 		}
674*ea8dc4b6Seschrock 
675*ea8dc4b6Seschrock 		if (translate_raw(raw, &record) != 0)
676*ea8dc4b6Seschrock 			return (1);
677*ea8dc4b6Seschrock 		if (!error)
678*ea8dc4b6Seschrock 			error = EIO;
679*ea8dc4b6Seschrock 	} else if (type == TYPE_INVAL) {
680*ea8dc4b6Seschrock 		if (flags == 0) {
681*ea8dc4b6Seschrock 			(void) fprintf(stderr, "at least one of '-b', '-d', "
682*ea8dc4b6Seschrock 			    "'-t', '-a', or '-u' must be specified\n");
683*ea8dc4b6Seschrock 			usage();
684*ea8dc4b6Seschrock 			return (2);
685*ea8dc4b6Seschrock 		}
686*ea8dc4b6Seschrock 
687*ea8dc4b6Seschrock 		if (argc == 1 && (flags & ZINJECT_UNLOAD_SPA)) {
688*ea8dc4b6Seschrock 			(void) strcpy(pool, argv[0]);
689*ea8dc4b6Seschrock 			dataset[0] = '\0';
690*ea8dc4b6Seschrock 		} else if (argc != 0) {
691*ea8dc4b6Seschrock 			(void) fprintf(stderr, "extraneous argument for "
692*ea8dc4b6Seschrock 			    "'-f'\n");
693*ea8dc4b6Seschrock 			usage();
694*ea8dc4b6Seschrock 			return (2);
695*ea8dc4b6Seschrock 		}
696*ea8dc4b6Seschrock 
697*ea8dc4b6Seschrock 		flags |= ZINJECT_NULL;
698*ea8dc4b6Seschrock 	} else {
699*ea8dc4b6Seschrock 		if (argc != 1) {
700*ea8dc4b6Seschrock 			(void) fprintf(stderr, "missing object\n");
701*ea8dc4b6Seschrock 			usage();
702*ea8dc4b6Seschrock 			return (2);
703*ea8dc4b6Seschrock 		}
704*ea8dc4b6Seschrock 
705*ea8dc4b6Seschrock 		if (error == ENXIO) {
706*ea8dc4b6Seschrock 			(void) fprintf(stderr, "data error type must be "
707*ea8dc4b6Seschrock 			    "'checksum' or 'io'\n");
708*ea8dc4b6Seschrock 			return (1);
709*ea8dc4b6Seschrock 		}
710*ea8dc4b6Seschrock 
711*ea8dc4b6Seschrock 		if (translate_record(type, argv[0], range, level, &record, pool,
712*ea8dc4b6Seschrock 		    dataset) != 0)
713*ea8dc4b6Seschrock 			return (1);
714*ea8dc4b6Seschrock 		if (!error)
715*ea8dc4b6Seschrock 			error = EIO;
716*ea8dc4b6Seschrock 	}
717*ea8dc4b6Seschrock 
718*ea8dc4b6Seschrock 	/*
719*ea8dc4b6Seschrock 	 * If this is pool-wide metadata, unmount everything.  The ioctl() will
720*ea8dc4b6Seschrock 	 * unload the pool, so that we trigger spa-wide reopen of metadata next
721*ea8dc4b6Seschrock 	 * time we access the pool.
722*ea8dc4b6Seschrock 	 */
723*ea8dc4b6Seschrock 	if (dataset[0] != '\0' && domount) {
724*ea8dc4b6Seschrock 		if ((zhp = zfs_open(dataset, ZFS_TYPE_ANY)) == NULL)
725*ea8dc4b6Seschrock 			return (1);
726*ea8dc4b6Seschrock 
727*ea8dc4b6Seschrock 		if (zfs_unmount(zhp, NULL, 0) != 0)
728*ea8dc4b6Seschrock 			return (1);
729*ea8dc4b6Seschrock 	}
730*ea8dc4b6Seschrock 
731*ea8dc4b6Seschrock 	record.zi_error = error;
732*ea8dc4b6Seschrock 
733*ea8dc4b6Seschrock 	ret = register_handler(pool, flags, &record, quiet);
734*ea8dc4b6Seschrock 
735*ea8dc4b6Seschrock 	if (dataset[0] != '\0' && domount)
736*ea8dc4b6Seschrock 		ret = (zfs_mount(zhp, NULL, 0) != 0);
737*ea8dc4b6Seschrock 
738*ea8dc4b6Seschrock 	return (ret);
739*ea8dc4b6Seschrock }
740