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