Whamcloud - gitweb
LU-1930 build: Back end file system fixes.
[fs/lustre-release.git] / lustre / utils / mount_utils_zfs.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License version 2 for more details.  A copy is
14  * included in the COPYING file that accompanied this code.
15
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2011, 2012 Whamcloud, Inc.
24  * Use is subject to license terms.
25  *
26  */
27 /*
28  * Author: Brian Behlendorf <behlendorf1@llnl.gov>
29  */
30 #include "mount_utils.h"
31 #include <stdio.h>
32 #include <string.h>
33 #include <libzfs.h>
34 #include <dlfcn.h>
35
36 /* Persistent mount data is stored in these user  attributes */
37 #define LDD_VERSION_PROP                "lustre:version"
38 #define LDD_FLAGS_PROP                  "lustre:flags"
39 #define LDD_INDEX_PROP                  "lustre:index"
40 #define LDD_FSNAME_PROP                 "lustre:fsname"
41 #define LDD_SVNAME_PROP                 "lustre:svname"
42 #define LDD_UUID_PROP                   "lustre:uuid"
43 #define LDD_USERDATA_PROP               "lustre:userdata"
44 #define LDD_MOUNTOPTS_PROP              "lustre:mountopts"
45 #define LDD_MGSNODE_PROP                "lustre:mgsnode"
46 #define LDD_FAILNODE_PROP               "lustre:failnode"
47 #define LDD_FAILMODE_PROP               "lustre:failmode"
48 #define LDD_IDENTITY_UPCALL_PROP        "lustre:identity_upcall"
49
50 /* indicate if the ZFS OSD has been successfully setup */
51 static int osd_zfs_setup = 0;
52
53 static libzfs_handle_t *g_zfs;
54
55 /* dynamic linking handles for libzfs & libnvpair */
56 static void *handle_libzfs;
57 static void *handle_nvpair;
58
59 /* symbol table looked up with dlsym */
60 struct zfs_symbols {
61         libzfs_handle_t *(*libzfs_init)(void);
62         void            (*libzfs_fini)(libzfs_handle_t *);
63         int             (*libzfs_load_module)(char *);
64         zfs_handle_t*   (*zfs_open)(libzfs_handle_t *, const char *, int);
65         int             (*zfs_destroy)(zfs_handle_t *, boolean_t);
66         void            (*zfs_close)(zfs_handle_t *);
67         int     (*zfs_prop_set)(zfs_handle_t*, const char*, const char*);
68         nvlist_t*       (*zfs_get_user_props)  (zfs_handle_t *);
69         int             (*zfs_name_valid)(const char *, zfs_type_t);
70         zpool_handle_t* (*zpool_open)(libzfs_handle_t *, const char *);
71         void            (*zpool_close)(zpool_handle_t *zhp);
72         int             (*nvlist_lookup_string)(nvlist_t*, const char*, char**);
73         int     (*nvlist_lookup_nvlist)(nvlist_t *, const char *, nvlist_t **);
74 };
75
76 static struct zfs_symbols sym;
77 void zfs_fini(void);
78
79 #define DLSYM(handle, func)                                        \
80         do {                                                       \
81                 sym.func = (typeof(sym.func))dlsym(handle, #func); \
82         } while(0)
83
84 /* populate the symbol table after a successful call to dlopen() */
85 static int zfs_populate_symbols(void)
86 {
87         char *error;
88
89         dlerror(); /* Clear any existing error */
90
91         DLSYM(handle_libzfs, libzfs_init);
92 #define libzfs_init (*sym.libzfs_init)
93         DLSYM(handle_libzfs, libzfs_fini);
94 #define libzfs_fini (*sym.libzfs_fini)
95         DLSYM(handle_libzfs, libzfs_load_module);
96 #define libzfs_load_module (*sym.libzfs_load_module)
97         DLSYM(handle_libzfs, zfs_open);
98 #define zfs_open (*sym.zfs_open)
99         DLSYM(handle_libzfs, zfs_destroy);
100 #define zfs_destroy (*sym.zfs_destroy)
101         DLSYM(handle_libzfs, zfs_close);
102 #define zfs_close (*sym.zfs_close)
103         DLSYM(handle_libzfs, zfs_prop_set);
104 #define zfs_prop_set (*sym.zfs_prop_set)
105         DLSYM(handle_libzfs, zfs_get_user_props);
106 #define zfs_get_user_props (*sym.zfs_get_user_props)
107         DLSYM(handle_libzfs, zfs_name_valid);
108 #define zfs_name_valid (*sym.zfs_name_valid)
109         DLSYM(handle_libzfs, zpool_open);
110 #define zpool_open (*sym.zpool_open)
111         DLSYM(handle_libzfs, zpool_close);
112 #define zpool_close (*sym.zpool_close)
113         DLSYM(handle_nvpair, nvlist_lookup_string);
114 #define nvlist_lookup_string (*sym.nvlist_lookup_string)
115         DLSYM(handle_nvpair, nvlist_lookup_nvlist);
116 #define nvlist_lookup_nvlist (*sym.nvlist_lookup_nvlist)
117
118         error = dlerror();
119         if (error != NULL) {
120                 fatal();
121                 fprintf(stderr, "%s\n", error);
122                 return EINVAL;
123         }
124         return 0;
125 }
126
127 static int zfs_set_prop_int(zfs_handle_t *zhp, char *prop, __u32 val)
128 {
129         char str[64];
130         int ret;
131
132         (void) snprintf(str, sizeof (str), "%lu", (unsigned long)val);
133         vprint("  %s=%s\n", prop, str);
134         ret = zfs_prop_set(zhp, prop, str);
135
136         return ret;
137 }
138
139 /*
140  * Write the zfs property string, note that properties with a NULL or
141  * zero-length value will not be written and 0 returned.
142  */
143 static int zfs_set_prop_str(zfs_handle_t *zhp, char *prop, char *val)
144 {
145         int ret = 0;
146
147         if (val && strlen(val) > 0) {
148                 vprint("  %s=%s\n", prop, val);
149                 ret = zfs_prop_set(zhp, prop, val);
150         }
151
152         return ret;
153 }
154
155 static int zfs_set_prop_param(zfs_handle_t *zhp, struct lustre_disk_data *ldd,
156                               char *param, char *prop)
157 {
158         char *str;
159         int ret = 0;
160
161         if (get_param(ldd->ldd_params, param, &str) == 0) {
162                 vprint("  %s=%s\n", prop, str);
163                 ret = zfs_prop_set(zhp, prop, str);
164                 free(str);
165         }
166
167         return ret;
168 }
169
170 static int osd_check_zfs_setup(void)
171 {
172         if (osd_zfs_setup == 0) {
173                 /* setup failed */
174                 fatal();
175                 fprintf(stderr, "Failed to initialize ZFS library. Are the ZFS "
176                         "packages and modules correctly installed?\n");
177         }
178         return osd_zfs_setup == 1;
179 }
180
181 /* Write the server config as properties associated with the dataset */
182 int zfs_write_ldd(struct mkfs_opts *mop)
183 {
184         struct lustre_disk_data *ldd = &mop->mo_ldd;
185         char *ds = mop->mo_device;
186         zfs_handle_t *zhp;
187         int ret = EINVAL;
188
189         if (osd_check_zfs_setup() == 0)
190                 return EINVAL;
191
192         zhp = zfs_open(g_zfs, ds, ZFS_TYPE_FILESYSTEM);
193         if (zhp == NULL) {
194                 fprintf(stderr, "Failed to open zfs dataset %s\n", ds);
195                 goto out;
196         }
197
198         vprint("Writing %s properties\n", ds);
199
200         ret = zfs_set_prop_int(zhp, LDD_VERSION_PROP, ldd->ldd_config_ver);
201         if (ret)
202                 goto out_close;
203
204         ret = zfs_set_prop_int(zhp, LDD_FLAGS_PROP, ldd->ldd_flags);
205         if (ret)
206                 goto out_close;
207
208         ret = zfs_set_prop_int(zhp, LDD_INDEX_PROP, ldd->ldd_svindex);
209         if (ret)
210                 goto out_close;
211
212         ret = zfs_set_prop_str(zhp, LDD_FSNAME_PROP, ldd->ldd_fsname);
213         if (ret)
214                 goto out_close;
215
216         ret = zfs_set_prop_str(zhp, LDD_SVNAME_PROP, ldd->ldd_svname);
217         if (ret)
218                 goto out_close;
219
220         ret = zfs_set_prop_str(zhp, LDD_UUID_PROP, (char *)ldd->ldd_uuid);
221         if (ret)
222                 goto out_close;
223
224         ret = zfs_set_prop_str(zhp, LDD_USERDATA_PROP, ldd->ldd_userdata);
225         if (ret)
226                 goto out_close;
227
228         ret = zfs_set_prop_str(zhp, LDD_MOUNTOPTS_PROP, ldd->ldd_mount_opts);
229         if (ret)
230                 goto out_close;
231
232         ret = zfs_set_prop_param(zhp, ldd, PARAM_MGSNODE, LDD_MGSNODE_PROP);
233         if (ret)
234                 goto out_close;
235
236         ret = zfs_set_prop_param(zhp, ldd, PARAM_FAILNODE, LDD_FAILNODE_PROP);
237         if (ret)
238                 goto out_close;
239
240         ret = zfs_set_prop_param(zhp, ldd, PARAM_FAILMODE, LDD_FAILMODE_PROP);
241         if (ret)
242                 goto out_close;
243
244         ret = zfs_set_prop_param(zhp, ldd, PARAM_MDT PARAM_ID_UPCALL,
245                                  LDD_IDENTITY_UPCALL_PROP);
246         if (ret)
247                 goto out_close;
248
249 out_close:
250         zfs_close(zhp);
251 out:
252         return ret;
253 }
254
255 static int zfs_get_prop_int(zfs_handle_t *zhp, char *prop, __u32 *val)
256 {
257         nvlist_t *propval;
258         char *propstr;
259         int ret;
260
261         ret = nvlist_lookup_nvlist(zfs_get_user_props(zhp), prop, &propval);
262         if (ret)
263                 return ret;
264
265         ret = nvlist_lookup_string(propval, ZPROP_VALUE, &propstr);
266         if (ret)
267                 return ret;
268
269         errno = 0;
270         *val = strtoul(propstr, NULL, 10);
271         if (errno)
272                 return errno;
273
274         return ret;
275 }
276
277 static int zfs_get_prop_str(zfs_handle_t *zhp, char *prop, char *val)
278 {
279         nvlist_t *propval;
280         char *propstr;
281         int ret;
282
283         ret = nvlist_lookup_nvlist(zfs_get_user_props(zhp), prop, &propval);
284         if (ret)
285                 return ret;
286
287         ret = nvlist_lookup_string(propval, ZPROP_VALUE, &propstr);
288         if (ret)
289                 return ret;
290
291         (void) strcpy(val, propstr);
292
293         return ret;
294 }
295
296 static int zfs_get_prop_param(zfs_handle_t *zhp, struct lustre_disk_data *ldd,
297                 char *param, char *prop)
298 {
299         nvlist_t *propval;
300         char *propstr;
301         int ret;
302
303         ret = nvlist_lookup_nvlist(zfs_get_user_props(zhp), prop, &propval);
304         if (ret)
305                 return ret;
306
307         ret = nvlist_lookup_string(propval, ZPROP_VALUE, &propstr);
308         if (ret)
309                 return ret;
310
311         ret = add_param(ldd->ldd_params, param, propstr);
312
313         return ret;
314 }
315
316 /*
317  * Read the server config as properties associated with the dataset.
318  * Missing entries as not treated error and are simply skipped.
319  */
320 int zfs_read_ldd(char *ds,  struct lustre_disk_data *ldd)
321 {
322         zfs_handle_t *zhp;
323         int ret = EINVAL;
324
325         if (osd_check_zfs_setup() == 0)
326                 return EINVAL;
327
328         zhp = zfs_open(g_zfs, ds, ZFS_TYPE_FILESYSTEM);
329         if (zhp == NULL)
330                 goto out;
331
332         ret = zfs_get_prop_int(zhp, LDD_VERSION_PROP, &ldd->ldd_config_ver);
333         if (ret && (ret != ENOENT))
334                 goto out_close;
335
336         ret = zfs_get_prop_int(zhp, LDD_FLAGS_PROP, &ldd->ldd_flags);
337         if (ret && (ret != ENOENT))
338                 goto out_close;
339
340         ret = zfs_get_prop_int(zhp, LDD_INDEX_PROP, &ldd->ldd_svindex);
341         if (ret && (ret != ENOENT))
342                 goto out_close;
343
344         ret = zfs_get_prop_str(zhp, LDD_FSNAME_PROP, ldd->ldd_fsname);
345         if (ret && (ret != ENOENT))
346                 goto out_close;
347
348         ret = zfs_get_prop_str(zhp, LDD_SVNAME_PROP, ldd->ldd_svname);
349         if (ret && (ret != ENOENT))
350                 goto out_close;
351
352         ret = zfs_get_prop_str(zhp, LDD_UUID_PROP, (char *)ldd->ldd_uuid);
353         if (ret && (ret != ENOENT))
354                 goto out_close;
355
356         ret = zfs_get_prop_str(zhp, LDD_USERDATA_PROP, ldd->ldd_userdata);
357         if (ret && (ret != ENOENT))
358                 goto out_close;
359
360         ret = zfs_get_prop_str(zhp, LDD_MOUNTOPTS_PROP, ldd->ldd_mount_opts);
361         if (ret && (ret != ENOENT))
362                 goto out_close;
363
364         ret = zfs_get_prop_param(zhp, ldd, PARAM_MGSNODE, LDD_MGSNODE_PROP);
365         if (ret && (ret != ENOENT))
366                 goto out_close;
367
368         ret = zfs_get_prop_param(zhp, ldd, PARAM_FAILNODE, LDD_FAILNODE_PROP);
369         if (ret && (ret != ENOENT))
370                 goto out_close;
371
372         ret = zfs_get_prop_param(zhp, ldd, PARAM_FAILMODE, LDD_FAILMODE_PROP);
373         if (ret && (ret != ENOENT))
374                 goto out_close;
375
376         ret = zfs_get_prop_param(zhp, ldd, PARAM_MDT PARAM_ID_UPCALL,
377                                  LDD_IDENTITY_UPCALL_PROP);
378         if (ret && (ret != ENOENT))
379                 goto out_close;
380
381         ldd->ldd_mount_type = LDD_MT_ZFS;
382         ret = 0;
383 out_close:
384         zfs_close(zhp);
385 out:
386         return ret;
387 }
388
389 int zfs_is_lustre(char *ds, unsigned *mount_type)
390 {
391         struct lustre_disk_data tmp_ldd;
392         int ret;
393
394         if (osd_zfs_setup == 0)
395                 return 0;
396
397         ret = zfs_read_ldd(ds, &tmp_ldd);
398         if ((ret == 0) && (tmp_ldd.ldd_config_ver > 0) &&
399             (strlen(tmp_ldd.ldd_fsname) > 0) &&
400             (strlen(tmp_ldd.ldd_svname) > 0)) {
401                 *mount_type = tmp_ldd.ldd_mount_type;
402                 return 1;
403         }
404
405         return 0;
406 }
407
408 static char *zfs_mkfs_opts(struct mkfs_opts *mop, char *str, int len)
409 {
410         memset(str, 0, len);
411
412         if (strlen(mop->mo_mkfsopts) != 0)
413                 snprintf(str, len, " -o %s", mop->mo_mkfsopts);
414
415         return str;
416 }
417
418 static int zfs_create_vdev(struct mkfs_opts *mop, char *vdev)
419 {
420         int ret = 0;
421
422         /* Silently ignore reserved vdev names */
423         if ((strncmp(vdev, "disk", 4) == 0) ||
424             (strncmp(vdev, "file", 4) == 0) ||
425             (strncmp(vdev, "mirror", 6) == 0) ||
426             (strncmp(vdev, "raidz", 5) == 0) ||
427             (strncmp(vdev, "spare", 5) == 0) ||
428             (strncmp(vdev, "log", 3) == 0) ||
429             (strncmp(vdev, "cache", 5) == 0))
430                 return ret;
431
432         /*
433          * Verify a file exists at the provided absolute path.  If it doesn't
434          * and mo_device_sz is set attempt to create a file vdev to be used.
435          * Relative paths will be passed directly to 'zpool create' which
436          * will check multiple multiple locations under /dev/.
437          */
438         if (vdev[0] == '/') {
439                 ret = access(vdev, F_OK);
440                 if (ret == 0)
441                         return ret;
442
443                 ret = errno;
444                 if (ret != ENOENT) {
445                         fatal();
446                         fprintf(stderr, "Unable to access required vdev "
447                                 "for pool %s (%d)\n", vdev, ret);
448                         return ret;
449                 }
450
451                 if (mop->mo_device_sz == 0) {
452                         fatal();
453                         fprintf(stderr, "Unable to create vdev due to "
454                                 "missing --device-size=#N(KB) parameter\n");
455                         return EINVAL;
456                 }
457
458                 ret = file_create(vdev, mop->mo_device_sz);
459                 if (ret) {
460                         fatal();
461                         fprintf(stderr, "Unable to create vdev %s (%d)\n",
462                                 vdev, ret);
463                         return ret;
464                 }
465         }
466
467         return ret;
468 }
469
470 int zfs_make_lustre(struct mkfs_opts *mop)
471 {
472         zfs_handle_t *zhp;
473         zpool_handle_t *php;
474         char *pool = NULL;
475         char *mkfs_cmd = NULL;
476         char *mkfs_tmp = NULL;
477         char *ds = mop->mo_device;
478         int pool_exists = 0, ret;
479
480         if (osd_check_zfs_setup() == 0)
481                 return EINVAL;
482
483         /* no automatic index with zfs backend */
484         if (mop->mo_ldd.ldd_flags & LDD_F_NEED_INDEX) {
485                 fatal();
486                 fprintf(stderr, "The target index must be specified with "
487                                 "--index\n");
488                 return EINVAL;
489         }
490
491         pool = strdup(ds);
492         if (pool == NULL)
493                 return ENOMEM;
494
495         mkfs_cmd = malloc(PATH_MAX);
496         if (mkfs_cmd == NULL) {
497                 ret = ENOMEM;
498                 goto out;
499         }
500
501         mkfs_tmp = malloc(PATH_MAX);
502         if (mkfs_tmp == NULL) {
503                 ret = ENOMEM;
504                 goto out;
505         }
506
507         /* Due to zfs_name_valid() check the '/' must exist */
508         strchr(pool, '/')[0] = '\0';
509
510         /* If --reformat was given attempt to destroy the previous dataset */
511         if ((mop->mo_flags & MO_FORCEFORMAT) &&
512             ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_FILESYSTEM)) != NULL)) {
513
514                 ret = zfs_destroy(zhp, 0);
515                 if (ret) {
516                         zfs_close(zhp);
517                         fprintf(stderr, "Failed destroy zfs dataset %s (%d)\n",
518                                 ds, ret);
519                         goto out;
520                 }
521
522                 zfs_close(zhp);
523         }
524
525         /*
526          * Create the zpool if the vdevs have been specified and the pool
527          * does not already exists.  The pool creation itself will be done
528          * with the zpool command rather than the zpool_create() library call
529          * so the existing zpool error handling can be leveraged.
530          */
531         php = zpool_open(g_zfs, pool);
532         if (php) {
533                 pool_exists = 1;
534                 zpool_close(php);
535         }
536
537         if ((mop->mo_pool_vdevs != NULL) && (pool_exists == 0)) {
538
539                 memset(mkfs_cmd, 0, PATH_MAX);
540                 snprintf(mkfs_cmd, PATH_MAX,
541                         "zpool create -f -O canmount=off %s", pool);
542
543                 /* Append the vdev config and create file vdevs as required */
544                 while (*mop->mo_pool_vdevs != NULL) {
545                         strscat(mkfs_cmd, " ", PATH_MAX);
546                         strscat(mkfs_cmd, *mop->mo_pool_vdevs, PATH_MAX);
547
548                         ret = zfs_create_vdev(mop, *mop->mo_pool_vdevs);
549                         if (ret)
550                                 goto out;
551
552                         mop->mo_pool_vdevs++;
553                 }
554
555                 vprint("mkfs_cmd = %s\n", mkfs_cmd);
556                 ret = run_command(mkfs_cmd, PATH_MAX);
557                 if (ret) {
558                         fatal();
559                         fprintf(stderr, "Unable to create pool %s (%d)\n",
560                                 pool, ret);
561                         goto out;
562                 }
563         }
564
565         /*
566          * Create the ZFS filesystem with any required mkfs options:
567          * - canmount=off is set to prevent zfs automounting
568          * - version=4 is set because SA are not yet handled by the osd
569          */
570         memset(mkfs_cmd, 0, PATH_MAX);
571         snprintf(mkfs_cmd, PATH_MAX,
572                  "zfs create -o canmount=off -o xattr=sa%s %s",
573                  zfs_mkfs_opts(mop, mkfs_tmp, PATH_MAX),
574                  ds);
575
576         vprint("mkfs_cmd = %s\n", mkfs_cmd);
577         ret = run_command(mkfs_cmd, PATH_MAX);
578         if (ret) {
579                 fatal();
580                 fprintf(stderr, "Unable to create filesystem %s (%d)\n",
581                         ds, ret);
582                 goto out;
583         }
584
585 out:
586         if (pool != NULL)
587                 free(pool);
588
589         if (mkfs_cmd != NULL)
590                 free(mkfs_cmd);
591
592         if (mkfs_tmp != NULL)
593                 free(mkfs_tmp);
594
595         return ret;
596 }
597
598 int zfs_prepare_lustre(struct mkfs_opts *mop,
599                 char *default_mountopts, int default_len,
600                 char *always_mountopts, int always_len)
601 {
602         int ret;
603
604         if (osd_check_zfs_setup() == 0)
605                 return EINVAL;
606
607         ret = zfs_name_valid(mop->mo_device, ZFS_TYPE_FILESYSTEM);
608         if (!ret) {
609                 fatal();
610                 fprintf(stderr, "Invalid filesystem name %s\n", mop->mo_device);
611                 return EINVAL;
612         }
613
614         return 0;
615 }
616
617 int zfs_tune_lustre(char *dev, struct mount_opts *mop)
618 {
619         if (osd_check_zfs_setup() == 0)
620                 return EINVAL;
621
622         return 0;
623 }
624
625 int zfs_label_lustre(struct mount_opts *mop)
626 {
627         zfs_handle_t *zhp;
628         int ret;
629
630         if (osd_check_zfs_setup() == 0)
631                 return EINVAL;
632
633         zhp = zfs_open(g_zfs, mop->mo_source, ZFS_TYPE_FILESYSTEM);
634         if (zhp == NULL)
635                 return EINVAL;
636
637         ret = zfs_set_prop_str(zhp, LDD_SVNAME_PROP, mop->mo_ldd.ldd_svname);
638         zfs_close(zhp);
639
640         return ret;
641 }
642
643 int zfs_init(void)
644 {
645         int ret = 0;
646
647         /* If the ZFS libs are not installed, don't print an error to avoid
648          * spamming ldiskfs users. An error message will still be printed if
649          * someone tries to do some real work involving a ZFS backend */
650
651         handle_libzfs = dlopen("libzfs.so", RTLD_LAZY);
652         if (handle_libzfs == NULL)
653                 return EINVAL;
654
655         handle_nvpair = dlopen("libnvpair.so", RTLD_LAZY);
656         if (handle_nvpair == NULL) {
657                 ret = EINVAL;
658                 goto out;
659         }
660
661         ret = zfs_populate_symbols();
662         if (ret)
663                 goto out;
664
665         if (libzfs_load_module("zfs") != 0) {
666                 /* The ZFS modules are not installed */
667                 ret = EINVAL;
668                 goto out;
669         }
670
671         g_zfs = libzfs_init();
672         if (g_zfs == NULL) {
673                 fprintf(stderr, "Failed to initialize ZFS library\n");
674                 ret = EINVAL;
675         }
676 out:
677         osd_zfs_setup = 1;
678         if (ret)
679                 zfs_fini();
680         return ret;
681 }
682
683 void zfs_fini(void)
684 {
685         if (g_zfs) {
686                 libzfs_fini(g_zfs);
687                 g_zfs = NULL;
688         }
689         if (handle_nvpair) {
690                 dlclose(handle_nvpair);
691                 handle_nvpair = NULL;
692         }
693         if (handle_libzfs) {
694                 dlclose(handle_libzfs);
695                 handle_libzfs = NULL;
696         }
697
698         osd_zfs_setup = 0;
699 }