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