xref: /onnv-gate/usr/src/cmd/lvm/metassist/layout/layout_svm_util.c (revision 62:5e51ad5d0496)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 
35 #include <meta.h>
36 #include <sdssc.h>
37 #include <mdiox.h>
38 #include <meta_repartition.h>
39 
40 #include "volume_dlist.h"
41 #include "volume_error.h"
42 #include "volume_output.h"
43 
44 #include "layout_device_util.h"
45 #include "layout_discovery.h"
46 #include "layout_dlist_util.h"
47 #include "layout_request.h"
48 #include "layout_svm_util.h"
49 
50 static int	_max_hsps = 1000;	/* # of HSPs (arbitrary limit) */
51 static int	_max_devs = 8192;	/* # of SVM volumes allowed */
52 static int	_max_devs_cfg = 128;	/* # of SVM volumes configured */
53 static int	_max_sets = 4;		/* # of SVM disk sets */
54 
55 /* volume name prefixes for generating new names */
56 static const char	*_hsp_prefix	= "hsp";
57 static const char	*_dev_prefix	= "d";
58 
59 /*
60  * dynamically allocated arrays to track used HSP (hspXXX) and volume
61  * names (dXXX) by number
62  */
63 static	boolean_t	*hsps_by_number	= NULL;
64 static	boolean_t	*devs_by_number	= NULL;
65 
66 /*
67  * This struct remembers a diskset and the names of
68  * the disks in the set
69  */
70 typedef struct {
71 	char	*name;
72 	dlist_t	*disknames;
73 	dlist_t	*hsps;
74 } diskset_t;
75 
76 /*
77  * list of diskset_t for known disksets
78  */
79 static dlist_t	*_disksets = NULL;
80 
81 static int add_diskset(
82 	char *diskset);
83 
84 static int add_diskset_diskname(
85 	char *diskset,
86 	char *diskname);
87 
88 static int add_diskset_hsp(
89 	char *diskset,
90 	char *hspname);
91 
92 static int add_diskset_hsp_spare(
93 	char *diskset,
94 	char *hspname,
95 	char *spare);
96 
97 static int is_disk_in_local_diskset(
98 	dm_descriptor_t	disk,
99 	boolean_t	*bool);
100 
101 static int is_disk_in_named_diskset(
102 	dm_descriptor_t	disk,
103 	char		*dsname,
104 	boolean_t	*bool);
105 
106 /* SVM snapshot stuff */
107 typedef enum {
108 	SVM_DISKSET = 0,
109 	SVM_MDB,
110 	SVM_STRIPE,
111 	SVM_MIRROR,
112 	SVM_RAID,
113 	SVM_TRANS,
114 	SVM_SP,
115 	SVM_HSP,
116 	SVM_HS,
117 	SVM_DRIVE
118 } svm_type_t;
119 
120 typedef struct svm_snap_entry {
121 	struct svm_snap_entry	*next;
122 	char			*diskset;
123 	svm_type_t		type;
124 	char			*name;
125 	char			*slice;
126 } svm_snap_t;
127 
128 static svm_snap_t	*svm_snapshot(int *errp);
129 static void	free_svm_snapshot(svm_snap_t *listp);
130 
131 static char	*type_name(svm_type_t type);
132 static int	add_record(
133 	svm_snap_t **listp,
134 	char	*setname,
135 	svm_type_t type,
136 	char	*mname,
137 	char	*slice_name);
138 static int	diskset_info(svm_snap_t **listp, mdsetname_t *sp);
139 static void	free_names(mdnamelist_t *nlp);
140 static int	load_svm(svm_snap_t **listp);
141 static int	new_entry(
142 	svm_snap_t **listp,
143 	char	*sname,
144 	svm_type_t type,
145 	char	*mname,
146 	mdsetname_t *sp);
147 
148 /*
149  * FUNCTION:	scan_svm_names(char *diskset)
150  *
151  * INPUT:	diskset	- a char * disk set name
152  *
153  * PURPOSE:	Take a snapshot of the current SVM config.
154  *
155  *		Scan it and remember:
156  *		1. all known disk sets
157  * 		s. the disks in the named disk set
158  *		3. the used device and HSP names in the named disk set
159  *		4. the HSPs in the disk set
160  *		5. the spares in the HSPs
161  */
162 int
163 scan_svm_names(
164 	char	*diskset)
165 {
166 	int		ndisks = 0;
167 	int		nhsps = 0;
168 	int		ndevices = 0;
169 	int		nsets = 0;
170 
171 	int		number = 0;
172 	int		error = 0;
173 	svm_snap_t	*headp = NULL;
174 	svm_snap_t	*listp = NULL;
175 	char		*tablefmt = "  %-20s %-10s %-20s %-10s\n";
176 
177 	oprintf(OUTPUT_TERSE,
178 		gettext("\nScanning system SVM configuration...\n"));
179 
180 	headp = svm_snapshot(&error);
181 	if (error != 0) {
182 	    oprintf(OUTPUT_TERSE,
183 		    gettext("failed to scan SVM devices\n"));
184 	    return (error);
185 	}
186 
187 	if (error == 0) {
188 	    if ((error = get_max_number_of_devices(&_max_devs_cfg)) == 0) {
189 		oprintf(OUTPUT_VERBOSE,
190 			gettext("  configured maximum number of "
191 				"volumes: %d\n"),
192 			_max_devs_cfg);
193 	    }
194 	}
195 
196 	if (error == 0) {
197 	    if ((error = get_max_number_of_disksets(&_max_sets)) == 0) {
198 		oprintf(OUTPUT_VERBOSE,
199 			gettext("  configured maximum number of "
200 				"disk sets: %d\n"),
201 			_max_sets);
202 	    }
203 	}
204 
205 	if (error == 0) {
206 	    /* array is realloc'ed as necessary */
207 	    if ((hsps_by_number =
208 		(boolean_t *)calloc(_max_hsps, sizeof (boolean_t))) == NULL) {
209 		oprintf(OUTPUT_TERSE,
210 			gettext("failed to allocate HSP name array\n"));
211 		error = ENOMEM;
212 	    }
213 	}
214 
215 	if (error == 0) {
216 	    /* array is realloc'ed as necessary */
217 	    if ((devs_by_number =
218 		(boolean_t *)calloc(_max_devs, sizeof (boolean_t))) == NULL) {
219 		oprintf(OUTPUT_TERSE,
220 			gettext("failed to allocate volume name array\n"));
221 		error = ENOMEM;
222 	    }
223 	}
224 
225 	if ((error == 0) && (get_max_verbosity() >= OUTPUT_DEBUG)) {
226 	    (void) oprintf(OUTPUT_DEBUG, "\n");
227 	    (void) oprintf(OUTPUT_DEBUG,
228 		    tablefmt,
229 		    gettext("disk set"),
230 		    gettext("dev type"),
231 		    gettext("name"),
232 		    gettext("slice"));
233 	    (void) oprintf(OUTPUT_DEBUG,
234 		    "  -----------------------------------"
235 		    "-----------------------------------\n");
236 	}
237 
238 	for (listp = headp; listp != NULL && error == 0; listp = listp->next) {
239 
240 	    oprintf(OUTPUT_DEBUG,
241 		    tablefmt,
242 		    listp->diskset,
243 		    type_name(listp->type),
244 		    listp->name,
245 		    listp->slice);
246 
247 	    switch (listp->type) {
248 	    case SVM_DISKSET:
249 
250 		error = add_diskset(listp->name);
251 		++nsets;
252 		break;
253 
254 	    case SVM_DRIVE:
255 
256 		error = add_diskset_diskname(listp->diskset, listp->name);
257 
258 		/* is this drive in the requested diskset? */
259 		if (string_case_compare(diskset, listp->diskset) == 0) {
260 		    ++ndisks;
261 		}
262 		break;
263 
264 	    case SVM_MIRROR:
265 	    case SVM_RAID:
266 	    case SVM_TRANS:
267 	    case SVM_SP:
268 	    case SVM_STRIPE:
269 
270 		/* is this SVM volume in the requested diskset? */
271 		if (string_case_compare(diskset, listp->diskset) == 0) {
272 
273 		    /* isolate device name from "poolname/dXXXX" */
274 		    char *cp = strrchr(listp->name, '/');
275 		    if (cp != NULL) {
276 			++cp;
277 		    } else {
278 			cp = listp->name;
279 		    }
280 
281 		    /* BEGIN CSTYLED */
282 		    /*
283 		     * names for requested devices and HSPs are remembered
284 		     * so that the default name generation scheme knows
285 		     * which names are already being used
286 		     */
287 		    /* END CSTYLED */
288 		    /* extract device number from name "dXXXX" */
289 		    if (sscanf(cp, "d%d", &number) != EOF) {
290 			oprintf(OUTPUT_DEBUG,
291 				gettext("  device: %6s   number: %3d\n"),
292 				cp, number);
293 
294 			if (number > _max_devs) {
295 			    /* hit current limit, expand it */
296 			    boolean_t *tmp =
297 				(boolean_t *)realloc((void *)_max_devs,
298 					(number * sizeof (boolean_t)));
299 
300 			    if (tmp == NULL) {
301 				error = ENOMEM;
302 			    } else {
303 				_max_devs = number;
304 				devs_by_number = tmp;
305 			    }
306 			}
307 
308 			if ((error == 0) &&
309 				(devs_by_number[number] == B_FALSE)) {
310 			    devs_by_number[number] = B_TRUE;
311 			    ++ndevices;
312 			}
313 		    }
314 		}
315 		break;
316 
317 	    case SVM_HSP:
318 
319 		/* is this HSP in the requested diskset? */
320 		if (string_case_compare(diskset, listp->diskset) == 0) {
321 
322 		    /* isolate HSP name from "poolname/hspXXX" */
323 		    char *cp = strrchr(listp->name, '/');
324 		    if (cp != NULL) {
325 			++cp;
326 		    } else {
327 			cp = listp->name;
328 		    }
329 
330 		    /* extract pool number from name "hspXXX" */
331 		    if (sscanf(cp, "hsp%03d", &number) != EOF) {
332 			oprintf(OUTPUT_DEBUG,
333 				gettext("     HSP: %6s   number: %3d\n"),
334 				cp, number);
335 
336 			if (number > _max_hsps) {
337 			    /* hit our arbitrary limit, double it */
338 			    boolean_t *tmp =
339 				(boolean_t *)realloc((void *)hsps_by_number,
340 					2 * _max_hsps * sizeof (boolean_t));
341 
342 			    if (tmp != NULL) {
343 				_max_hsps *= 2;
344 				hsps_by_number = tmp;
345 			    } else {
346 				error = ENOMEM;
347 			    }
348 			}
349 
350 			if ((error == 0) &&
351 				(hsps_by_number[number] == B_FALSE)) {
352 			    hsps_by_number[number] = B_TRUE;
353 			    error = add_diskset_hsp(diskset, cp);
354 			    ++nhsps;
355 			}
356 		    }
357 		}
358 
359 		break;
360 
361 	    case SVM_HS:
362 
363 		/* is this hot spare in the requested disk set? */
364 		if (string_case_compare(diskset, listp->diskset) == 0) {
365 
366 		    /* isolate HSP name from "poolname/hspXXXX" */
367 		    char *cp = strrchr(listp->name, '/');
368 		    if (cp != NULL) {
369 			++cp;
370 		    } else {
371 			cp = listp->name;
372 		    }
373 
374 		    error = add_diskset_hsp_spare(diskset, cp, listp->slice);
375 		}
376 		break;
377 
378 	    case SVM_MDB:
379 	    default:
380 		break;
381 	    }
382 	}
383 
384 	free_svm_snapshot(headp);
385 
386 	if (error == 0) {
387 	    /* available diskset?  subtract 1 for the local set */
388 	    if ((diskset_exists(diskset) != B_TRUE) &&
389 		(nsets >= _max_sets)) {
390 		volume_set_error(
391 			gettext("Disk set \"%s\" cannot be created, the "
392 				"maximum number of disk sets (%d) already "
393 				"exists.\n"),
394 			diskset, _max_sets);
395 		error = -1;
396 	    }
397 	}
398 
399 	if (error == 0) {
400 	    oprintf(OUTPUT_VERBOSE,
401 		    gettext("\n  Disk set \"%s\" has:\n\n"), diskset);
402 	    oprintf(OUTPUT_VERBOSE,
403 		    gettext("    %d drives\n"), ndisks);
404 	    oprintf(OUTPUT_VERBOSE,
405 		    gettext("    %d volumes\n"), ndevices);
406 	    oprintf(OUTPUT_VERBOSE,
407 		    gettext("    %d HSPs\n"), nhsps);
408 	} else {
409 	    free(hsps_by_number);
410 	    free(devs_by_number);
411 	    hsps_by_number = (boolean_t *)NULL;
412 	    devs_by_number = (boolean_t *)NULL;
413 	}
414 
415 	return (error);
416 }
417 
418 /*
419  * FUNCTION:	release_svm_names()
420  *
421  * PURPOSE:	Release snapshot of the current SVM config.
422  *
423  *		Free memory allocated by scan_svm_names()
424  */
425 void
426 release_svm_names()
427 {
428 	dlist_t *iter;
429 
430 	for (iter = _disksets; iter != NULL; iter = iter->next) {
431 	    diskset_t *diskset = (diskset_t *)iter->obj;
432 	    dlist_free_items(diskset->disknames, free);
433 	    dlist_free_items(diskset->hsps, free_devconfig);
434 	    free(diskset->name);
435 	}
436 	dlist_free_items(_disksets, free);
437 	_disksets = NULL;
438 
439 	if (hsps_by_number != NULL)
440 	    free(hsps_by_number);
441 	if (devs_by_number != NULL)
442 	    free(devs_by_number);
443 
444 	hsps_by_number = (boolean_t *)NULL;
445 	devs_by_number = (boolean_t *)NULL;
446 }
447 
448 /*
449  * FUNCTION:	diskset_exists(char *diskset)
450  *
451  * INPUT:	dsname	- a char * diskset name
452  *
453  * RETURNS:	boolean_t - B_TRUE if the named diskset exists
454  *			 B_FALSE otherwise
455  *
456  * PURPOSE:	Checks the list of known disk sets and determines
457  *		if the input name is in that list.
458  */
459 boolean_t
460 diskset_exists(
461 	char	*dsname)
462 {
463 	dlist_t *iter;
464 
465 	for (iter = _disksets; iter != NULL; iter = iter->next) {
466 	    diskset_t *diskset = (diskset_t *)iter->obj;
467 	    if (string_case_compare(dsname, diskset->name) == 0) {
468 		return (B_TRUE);
469 	    }
470 	}
471 
472 	return (B_FALSE);
473 }
474 
475 /*
476  * FUNCTION:	add_diskset(char *dsname)
477  *
478  * INPUT:	dsname	- a char * disk set name
479  *
480  * RETURNS:	int	- 0 on success
481  *			 !0 otherwise
482  *
483  * PURPOSE:	Add the named disk set to the list of known disk sets.
484  */
485 static int
486 add_diskset(
487 	char	*dsname)
488 {
489 	dlist_t	*iter;
490 	int	error = 0;
491 
492 	for (iter = _disksets; iter != NULL; iter = iter->next) {
493 	    diskset_t *diskset = (diskset_t *)iter->obj;
494 	    if (string_case_compare(diskset->name, dsname) == 0) {
495 		break;
496 	    }
497 	}
498 
499 	if (iter == NULL) {
500 
501 	    dlist_t *item = NULL;
502 	    diskset_t *diskset = (diskset_t *)calloc(1, sizeof (diskset_t));
503 
504 	    if (diskset == NULL) {
505 		error = ENOMEM;
506 	    } else {
507 		diskset->hsps = NULL;
508 		diskset->name = strdup(dsname);
509 		if (diskset->name == NULL) {
510 		    free(diskset);
511 		    error = ENOMEM;
512 		} else {
513 		    if ((item = dlist_new_item(diskset)) == NULL) {
514 			free(diskset->name);
515 			free(diskset);
516 			error = ENOMEM;
517 		    } else {
518 			_disksets = dlist_append(item, _disksets, AT_HEAD);
519 			oprintf(OUTPUT_DEBUG,
520 				gettext("  added disk set %s \n"), dsname);
521 		    }
522 		}
523 	    }
524 	}
525 
526 	return (error);
527 }
528 
529 /*
530  * FUNCTION:	add_diskset_diskname(char *diskset, char *diskname)
531  *
532  * INPUT:	dsname	- a char * disk set name
533  *		diskname - a char * disk name
534  *
535  * RETURNS:	int	- 0 on success
536  *			 !0 otherwise
537  *
538  * PURPOSE:	Add the disk name to the named disk set's list of disks.
539  *
540  *		The input diskname is fully qualified with the path
541  *		to the raw disk device (/dev/rdsk/cXtXdXsX) which is
542  *		not relevant, so it is removed.
543  */
544 static int
545 add_diskset_diskname(
546 	char	*dsname,
547 	char	*diskname)
548 {
549 	dlist_t *iter;
550 	int error = 0;
551 
552 	for (iter = _disksets; iter != NULL; iter = iter->next) {
553 
554 	    diskset_t *diskset = (diskset_t *)iter->obj;
555 	    if (string_case_compare(diskset->name, dsname) == 0) {
556 
557 		dlist_t *item = NULL;
558 		char *name = NULL;
559 		char *cp = NULL;
560 
561 		/* trim leading path */
562 		if ((cp = strrchr(diskname, '/')) != 0) {
563 		    if ((name = strdup(cp+1)) == NULL) {
564 			error = ENOMEM;
565 		    }
566 		} else if ((name = strdup(diskname)) == NULL) {
567 		    error = ENOMEM;
568 		}
569 
570 		if ((item = dlist_new_item(name)) == NULL) {
571 		    free(name);
572 		    error = ENOMEM;
573 		} else {
574 		    diskset->disknames =
575 			dlist_append(item, diskset->disknames, AT_HEAD);
576 		}
577 
578 		break;
579 	    }
580 	}
581 
582 	if ((error == 0) && (iter == NULL)) {
583 	    /* new disk set */
584 	    if ((error = add_diskset(dsname)) == 0) {
585 		return (add_diskset_diskname(dsname, diskname));
586 	    }
587 	}
588 
589 	return (error);
590 }
591 
592 /*
593  * FUNCTION:	add_diskset_hsp(char *dsname, char *hspname)
594  *
595  * INPUT:	dsname	- a char * disk set name
596  *		hspname	- a char * HSP name
597  *
598  * RETURNS:	int	- 0 on success
599  *			 !0 otherwise
600  *
601  * PURPOSE:	Model a new HSP for the named disk set.
602  *
603  *		Metassist can use existing HSPs to service new volumes.
604  *
605  *		It is necessary to have a model of what HSPs currently
606  *		exist for each disk set.
607  *
608  *		This function takes information found during discovery
609  *		and turns it into a form usable by the HSP layout code.
610  */
611 static int
612 add_diskset_hsp(
613 	char	*dsname,
614 	char	*hspname)
615 {
616 	dlist_t	*iter;
617 	int	error = 0;
618 
619 	for (iter = _disksets; iter != NULL; iter = iter->next) {
620 
621 	    diskset_t	*diskset = (diskset_t *)iter->obj;
622 
623 	    if (string_case_compare(diskset->name, dsname) == 0) {
624 
625 		dlist_t		*item = NULL;
626 		devconfig_t	*hsp = NULL;
627 
628 		if (((error = new_devconfig(&hsp, TYPE_HSP)) != 0) ||
629 		    (error = devconfig_set_name(hsp, hspname))) {
630 		    free_devconfig(hsp);
631 		} else {
632 		    if ((item = dlist_new_item(hsp)) == NULL) {
633 			free_devconfig(hsp);
634 			error = ENOMEM;
635 		    } else {
636 			diskset->hsps =
637 			    dlist_append(item, diskset->hsps, AT_TAIL);
638 
639 			oprintf(OUTPUT_DEBUG,
640 				gettext("  added %s to disk set %s\n"),
641 				hspname, dsname);
642 		    }
643 		}
644 		break;
645 	    }
646 	}
647 
648 	if ((error == 0) && (iter == NULL)) {
649 	    if ((error = add_diskset(dsname)) == 0) {
650 		return (add_diskset_hsp(dsname, hspname));
651 	    }
652 	}
653 
654 	return (error);
655 }
656 
657 /*
658  * FUNCTION:	add_diskset_hsp_spare(char *dsname, char *hspname,
659  *			char *sparename)
660  *
661  * INPUT:	dsname	- a char * diskset name
662  *		hspname	- a char * HSP name
663  *		sparename - a char * hot spare (slice) name
664  *
665  * RETURNS:	int	- 0 on success
666  *			 !0 otherwise
667  *
668  * PURPOSE:	Locate the named hot spare pool in the named disk set and
669  *		add the named spare slice to its list of spares.
670  *
671  *		Metassist can use existing HSPs to service new volumes.
672  *
673  *		It is necessary to have a model of what HSPs currently
674  *		exist for each disk set.
675  *
676  *		This function takes information found during discovery
677  *		and turns it into a form usable by the HSP layout code.
678  */
679 static int
680 add_diskset_hsp_spare(
681 	char	*dsname,
682 	char	*hspname,
683 	char	*sparename)
684 {
685 	dlist_t	*iter;
686 	int	error = 0;
687 
688 	for (iter = _disksets; iter != NULL; iter = iter->next) {
689 
690 	    diskset_t	*diskset = (diskset_t *)iter->obj;
691 
692 	    if (string_case_compare(diskset->name, dsname) == 0) {
693 
694 		dlist_t *item =
695 		    dlist_find(
696 			    diskset->hsps, hspname,
697 			    compare_string_to_devconfig_name);
698 
699 		if (item != NULL) {
700 
701 		    /* add spare to HSP */
702 		    devconfig_t	*hsp = (devconfig_t *)item->obj;
703 		    dm_descriptor_t slice = (dm_descriptor_t)0;
704 
705 		    (void) slice_get_by_name(sparename, &slice);
706 		    if (slice == (dm_descriptor_t)0) {
707 			oprintf(OUTPUT_TERSE,
708 				gettext("warning: ignoring nonexistent "
709 					"slice %s defined in %s\n"),
710 				sparename, hspname);
711 		    } else {
712 
713 			uint64_t nbytes = 0;
714 			uint32_t index = 0;
715 			devconfig_t *spare = NULL;
716 
717 			/* build a devconfig_t model of the slice */
718 			if (((error = slice_get_size(slice, &nbytes)) != 0) ||
719 			    (error = slice_get_index(slice, &index)) ||
720 			    (error = new_devconfig(&spare, TYPE_SLICE)) ||
721 			    (error = devconfig_set_name(spare, sparename)) ||
722 			    (error = devconfig_set_size(spare, nbytes)) ||
723 			    (error = devconfig_set_slice_index(spare, index))) {
724 			    free_devconfig(spare);
725 			} else {
726 
727 			    if ((item = dlist_new_item(spare)) == NULL) {
728 				error = ENOMEM;
729 				free_devconfig(spare);
730 			    } else {
731 				dlist_t	*spares;
732 				spares = devconfig_get_components(hsp);
733 				spares = dlist_append(item, spares, AT_TAIL);
734 				devconfig_set_components(hsp, spares);
735 
736 				oprintf(OUTPUT_DEBUG,
737 					gettext("  added %s to %s in "
738 						"disk set %s\n"),
739 					sparename, hspname, dsname);
740 			    }
741 			}
742 		    }
743 
744 		    break;
745 
746 		} else {
747 		    if ((error = add_diskset_hsp(dsname, hspname)) == 0) {
748 			return (add_diskset_hsp_spare(
749 					dsname, hspname, sparename));
750 		    }
751 		}
752 	    }
753 	}
754 
755 	return (error);
756 }
757 
758 /*
759  * Return a list of disks in the given diskset.
760  *
761  * @param       dsname
762  *              The name of the named disk set, or "" for the local
763  *              set.
764  *
765  * @param       disks
766  *              RETURN: pointer to the list of disks in the given disk
767  *              set
768  *
769  * @return      0 if succesful, non-zero otherwise
770  */
771 int
772 get_disks_in_diskset(
773 	char *dsname,
774 	dlist_t **disks)
775 {
776 	dlist_t *known_disks;
777 	int error = 0;
778 
779 	*disks = NULL;
780 
781 	if ((error = get_known_disks(&known_disks)) == 0) {
782 	    dlist_t *iter;
783 
784 	    /* For each known disk... */
785 	    for (iter = known_disks;
786 		iter != NULL && error == 0;
787 		iter = iter->next) {
788 		dm_descriptor_t disk = (uintptr_t)iter->obj;
789 		boolean_t in_diskset = B_FALSE;
790 
791 		/* If this disk is in the given set... */
792 		error = is_disk_in_diskset(disk, dsname, &in_diskset);
793 		if (error == 0 && in_diskset == B_TRUE) {
794 		    dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
795 		    *disks = dlist_append(item, *disks, AT_TAIL);
796 		}
797 	    }
798 	}
799 
800 	return (error);
801 }
802 
803 /*
804  * FUNCTION:	is_disk_in_diskset(dm_descriptor_t disk, char *dsname,
805  *			boolean_t *bool)
806  *
807  * INPUT:	disk	- dm_descriptor_t disk handle
808  *		dsname	- char * diskset name, or MD_LOCAL_NAME for
809  *		the local set.
810  *
811  * OUTPUT:	bool	- pointer to a boolean_t to hold the result
812  *
813  * RETURNS:	int	- 0 on success
814  *			 !0 otherwise
815  *
816  * PURPOSE:	Determine if the input disk is known to be in the
817  *		given diskset.
818  */
819 int
820 is_disk_in_diskset(
821 	dm_descriptor_t	disk,
822 	char		*dsname,
823 	boolean_t	*bool)
824 {
825 	if (string_case_compare(dsname, MD_LOCAL_NAME) == 0) {
826 	    return (is_disk_in_local_diskset(disk, bool));
827 	}
828 
829 	return (is_disk_in_named_diskset(disk, dsname, bool));
830 }
831 
832 static int
833 is_disk_in_local_diskset(
834 	dm_descriptor_t	disk,
835 	boolean_t	*bool)
836 {
837 	dlist_t *iter;
838 	dlist_t *aliases = NULL;
839 	boolean_t in_named_diskset = B_FALSE;
840 	char *name = NULL;
841 	int error = 0;
842 
843 	*bool = B_FALSE;
844 
845 	error = get_display_name(disk, &name);
846 	if (error == 0) {
847 
848 	    error = get_aliases(disk, &aliases);
849 	    if (error == 0) {
850 
851 		/* For each known disk set... */
852 		for (iter = _disksets;
853 		    iter != NULL && in_named_diskset == B_FALSE;
854 		    iter = iter->next) {
855 
856 		    diskset_t *diskset = (diskset_t *)iter->obj;
857 		    dlist_t *names = diskset->disknames;
858 
859 		    /* Check disk name */
860 		    in_named_diskset = dlist_contains(
861 			names, name, compare_device_names);
862 
863 		    /* Check disk aliases */
864 		    if (in_named_diskset == B_FALSE) {
865 			dlist_t *iter2;
866 			for (iter2 = aliases;
867 			    iter2 != NULL && in_named_diskset == B_FALSE;
868 			    iter2 = iter2->next) {
869 			    in_named_diskset = dlist_contains(names,
870 				(char *)iter2->obj, compare_device_names);
871 			}
872 		    }
873 		}
874 	    }
875 	}
876 
877 	if (error == 0) {
878 	    *bool = (in_named_diskset == B_TRUE ? B_FALSE : B_TRUE);
879 	}
880 
881 	return (error);
882 }
883 
884 static int
885 is_disk_in_named_diskset(
886 	dm_descriptor_t	disk,
887 	char		*dsname,
888 	boolean_t	*bool)
889 {
890 	dlist_t		*iter;
891 	int		error = 0;
892 	boolean_t 	in_diskset = B_FALSE;
893 
894 	*bool = B_FALSE;
895 
896 	for (iter = _disksets;
897 	    (iter != NULL) && (in_diskset == B_FALSE);
898 	    iter = iter->next) {
899 
900 	    diskset_t *diskset = (diskset_t *)iter->obj;
901 
902 	    if (string_case_compare(diskset->name, dsname) == 0) {
903 
904 		dlist_t *names = diskset->disknames;
905 		dlist_t *aliases = NULL;
906 		char	*name = NULL;
907 
908 		((error = get_display_name(disk, &name)) != 0) ||
909 		(error = get_aliases(disk, &aliases));
910 		if (error != 0) {
911 		    break;
912 		}
913 
914 		/* check disk name */
915 		in_diskset = dlist_contains(names, name, compare_device_names);
916 
917 		/* check disk aliases */
918 		if (in_diskset == B_FALSE) {
919 		    dlist_t *iter2;
920 		    for (iter2 = aliases;
921 			(iter2 != NULL) && (in_diskset == B_FALSE);
922 			iter2 = iter2->next) {
923 			in_diskset = dlist_contains(names,
924 				(char *)iter2->obj, compare_device_names);
925 		    }
926 		}
927 	    }
928 	}
929 
930 	*bool = in_diskset;
931 
932 	return (error);
933 }
934 
935 /*
936  * FUNCTION:	is_disk_in_other_diskset(dm_descriptor_t disk, char *dsname,
937  *			boolean_t *bool)
938  *
939  * INPUT:	disk	- dm_descriptor_t disk handle
940  *		dsname	- char * disk set name
941  *
942  * OUTPUT:	bool	- pointer to a boolean_t to hold the result.
943  *
944  * RETURNS:	int	- 0 on success
945  *			 !0 otherwise
946  *
947  * PURPOSE:	Determine if the named disk is known to be in a disk set
948  *		other than the named disk set.
949  */
950 int
951 is_disk_in_other_diskset(
952 	dm_descriptor_t disk,
953 	char	*dsname,
954 	boolean_t *bool)
955 {
956 	boolean_t in_other = B_FALSE;
957 	dlist_t	*iter;
958 	dlist_t *aliases = NULL;
959 	char	*name = NULL;
960 	char	*cp = NULL;
961 	int	error = 0;
962 
963 	((error = get_display_name(disk, &name)) != 0) ||
964 	(error = get_aliases(disk, &aliases));
965 	if (error != 0) {
966 	    return (error);
967 	}
968 
969 	/*
970 	 * discard the leading path, it is probably /dev/dsk
971 	 * and the disk set disk names are all /dev/rdsk/...
972 	 *
973 	 * aliases do not have leading paths
974 	 */
975 	cp = strrchr(name, '/');
976 	if (cp != NULL) {
977 	    ++cp;
978 	} else {
979 	    cp = name;
980 	}
981 	name = cp;
982 
983 	for (iter = _disksets;
984 	    (iter != NULL) && (in_other == B_FALSE);
985 	    iter = iter->next) {
986 
987 	    diskset_t	*diskset = (diskset_t *)iter->obj;
988 	    dlist_t	*names = diskset->disknames;
989 
990 	    if (string_case_compare(diskset->name, dsname) == 0) {
991 		/* skip named disk set */
992 		continue;
993 	    }
994 
995 	    /* see if disk's name is in disk set's name list */
996 	    in_other = dlist_contains(names, name, compare_device_names);
997 
998 	    /* see if any of the disk's aliases is in name list */
999 	    if (in_other == B_FALSE) {
1000 		dlist_t *iter2;
1001 		for (iter2 = aliases;
1002 		    (iter2 != NULL) && (in_other == B_FALSE);
1003 		    iter2 = iter2->next) {
1004 
1005 		    in_other = dlist_contains(names,
1006 			    (char *)iter2->obj, compare_device_names);
1007 		}
1008 	    }
1009 	}
1010 
1011 	*bool = in_other;
1012 
1013 	return (error);
1014 }
1015 
1016 /*
1017  * FUNCTION:	hsp_get_default_for_diskset(char *diskset,
1018  *			devconfig_t **hsp)
1019  *
1020  * INPUT:	diskset	- char * disk set name
1021  *
1022  * RETURNS:	devconfig_t * - pointer to the first HSP in the disk set
1023  *			NULL if none found
1024  *
1025  * PURPOSE:	Locate the first HSP in the named disk set.
1026  */
1027 int
1028 hsp_get_default_for_diskset(
1029 	char	*diskset,
1030 	devconfig_t **hsp)
1031 {
1032 	dlist_t		*iter = _disksets;
1033 
1034 	*hsp = NULL;
1035 
1036 	for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) {
1037 	    diskset_t *set = (diskset_t *)iter->obj;
1038 	    if (string_case_compare(set->name, diskset) == 0) {
1039 		dlist_t *item = set->hsps;
1040 		if (item != NULL) {
1041 		    *hsp = item->obj;
1042 		}
1043 	    }
1044 	}
1045 
1046 	return (0);
1047 }
1048 
1049 /*
1050  * FUNCTION:	get_n_metadb_replicas(int *nreplicas)
1051  *
1052  * OUTPUT:	nreplicas - pointer to int to hold the result
1053  *
1054  * RETURNS:	int	- 0 on success
1055  *			 !0 on failure
1056  *
1057  * PURPOSE:	Check the number of replicas configured for the local set.
1058  */
1059 int
1060 get_n_metadb_replicas(
1061 	int	*nreplicas)
1062 {
1063 	mdsetname_t	*sp;
1064 	md_replicalist_t *rlp = NULL;
1065 	md_error_t	mderror = mdnullerror;
1066 	int		error = 0;
1067 
1068 	*nreplicas = 0;
1069 
1070 	sp = metasetname(MD_LOCAL_NAME, &mderror);
1071 	if (!mdisok(&mderror)) {
1072 	    volume_set_error(mde_sperror(&mderror, NULL));
1073 	    mdclrerror(&mderror);
1074 	    error = -1;
1075 	} else {
1076 	    *nreplicas = metareplicalist(sp, MD_BASICNAME_OK, &rlp, &mderror);
1077 	    if (!mdisok(&mderror)) {
1078 		volume_set_error(mde_sperror(&mderror, NULL));
1079 		mdclrerror(&mderror);
1080 		error = -1;
1081 	    } else if (rlp != NULL) {
1082 		metafreereplicalist(rlp);
1083 		rlp = NULL;
1084 	    }
1085 
1086 	    if (*nreplicas < 0) {
1087 		*nreplicas = 0;
1088 	    }
1089 	}
1090 
1091 	return (error);
1092 }
1093 
1094 /*
1095  * FUNCTION:	hsp_get_by_name(char *diskset, char *name,
1096  *		devconfig_t **hsp)
1097  *
1098  * INPUT:	diskset	- char * disk set name
1099  *		name	- char * HSP name
1100  *
1101  * OUTPUT:	hsp	- a devconfig_t * - pointer to hold
1102  *			  the named HSP if none found
1103  *
1104  * PURPOSE:	Locate the named HSP in the named disk set.
1105  */
1106 int
1107 hsp_get_by_name(
1108 	char		*diskset,
1109 	char		*name,
1110 	devconfig_t	**hsp)
1111 {
1112 	dlist_t		*iter = _disksets;
1113 
1114 	*hsp = NULL;
1115 
1116 	for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) {
1117 	    diskset_t *set = (diskset_t *)iter->obj;
1118 	    if (string_case_compare(set->name, diskset) == 0) {
1119 		dlist_t *item = dlist_find(
1120 			set->hsps, name, compare_string_to_devconfig_name);
1121 		if (item != NULL) {
1122 		    *hsp = item->obj;
1123 		}
1124 	    }
1125 	}
1126 
1127 	return (0);
1128 }
1129 
1130 /*
1131  * FUNCTION:	is_volume_name_valid(char *name)
1132  *
1133  * OUTPUT:	name	- pointer to a char * volume name
1134  *
1135  * RETURNS:	boolean_t - B_TRUE if the input name is valid
1136  *			 B_FALSE otherwise
1137  *
1138  * PURPOSE:	Wrapper around libmeta volume name validation method.
1139  */
1140 boolean_t
1141 is_volume_name_valid(
1142 	char		*name)
1143 {
1144 	return (is_metaname(name));
1145 }
1146 
1147 /*
1148  * FUNCTION:	is_hsp_name_valid(char *name)
1149  *
1150  * INPUT:	name	- char * HSP name
1151  *
1152  * RETURNS:	boolean_t - B_TRUE if the input name is valid
1153  *			 B_FALSE otherwise
1154  *
1155  * PURPOSE:	Wrapper around libmeta HSP name validation method.
1156  */
1157 boolean_t
1158 is_hsp_name_valid(
1159 	char		*name)
1160 {
1161 	return (is_hspname(name));
1162 }
1163 
1164 /*
1165  * FUNCTION:	extract_index(char *name, char *prefix, char *num_fmt,
1166  *			int *index)
1167  *
1168  * INPUT:	name	- const char * volume name
1169  *		prefix	- const char * fixed part of format string
1170  *		num_fmt	- const char * format of number to extract (e.g. %d)
1171  *
1172  * OUTPUT:	index	- pointer to int to hold numeric part of name
1173  *
1174  * RETURNS:	boolean_t - B_TRUE if the input name is parsed correctly
1175  *			 B_FALSE otherwise
1176  *
1177  * PURPOSE:	Extract the numeric portion of a device name for use
1178  *		by higher-level functions.
1179  */
1180 static boolean_t
1181 extract_index(
1182 	const char	*name,
1183 	const char	*prefix,
1184 	const char	*num_fmt,
1185 	int		*index)
1186 {
1187 	char		buf[MAXNAMELEN];
1188 	const char	*cp;
1189 	const char	*fmt = buf;
1190 
1191 	if ((cp = strrchr(name, '/')) != NULL) {
1192 	    ++cp;
1193 	} else {
1194 	    cp = name;
1195 	}
1196 
1197 	(void) snprintf(buf, sizeof (buf), "%s%s", prefix, num_fmt);
1198 	if (sscanf(cp, fmt, index) == 1)
1199 		return (B_TRUE);
1200 	else
1201 		return (B_FALSE);
1202 }
1203 
1204 /*
1205  * FUNCTION:	is_volume_name_in_range(char *name)
1206  *
1207  * INPUT:	name	- char * volume name
1208  *
1209  * RETURNS:	boolean_t - B_TRUE if the input name is in the allowed
1210  *				range of names
1211  *			 B_FALSE otherwise
1212  *
1213  * PURPOSE:	Determine if the input volume name is within the allowed
1214  *		range of device names (0 <= n < max # of devices configured).
1215  */
1216 boolean_t
1217 is_volume_name_in_range(
1218 	char		*name)
1219 {
1220 	int		index = -1;
1221 
1222 	if (extract_index(name, _dev_prefix, "%d", &index)) {
1223 	    if (index >= 0 && index < _max_devs_cfg) {
1224 		return (B_TRUE);
1225 	    }
1226 	}
1227 
1228 	return (B_FALSE);
1229 }
1230 
1231 /*
1232  * FUNCTION:	reserve_volume_name(char *name)
1233  *
1234  * INPUT:	name	- a char * volume name
1235  *
1236  * RETURNS:	int	- 0 on success
1237  *			 !0 otherwise
1238  *
1239  * PURPOSE:	Mark a volume name/number as used.
1240  *
1241  *		Assumes that the input name has been validated.
1242  *
1243  *		if the name is not currently available, return -1
1244  */
1245 int
1246 reserve_volume_name(
1247 	char		*name)
1248 {
1249 	int		index = -1;
1250 
1251 	if (extract_index(name, _dev_prefix, "%d", &index)) {
1252 	    if (devs_by_number[index] != B_TRUE) {
1253 		devs_by_number[index] = B_TRUE;
1254 		return (0);
1255 	    }
1256 	}
1257 
1258 	return (-1);
1259 }
1260 
1261 /*
1262  * FUNCTION:	reserve_hsp_name(char *name)
1263  *
1264  * INPUT:	name	- a char * hsp name
1265  *
1266  * RETURNS:	int	- 0 on success
1267  *			 !0 otherwise
1268  *
1269  * PURPOSE:	Mark a HSP name/number as used.
1270  *
1271  *		Assumes that the input name has been validated.
1272  *
1273  *		if the name is not currently available, return -1
1274  */
1275 int
1276 reserve_hsp_name(
1277 	char		*name)
1278 {
1279 	int		index = -1;
1280 
1281 	if (extract_index(name, _hsp_prefix, "%03d", &index)) {
1282 	    if (hsps_by_number[index] != B_TRUE) {
1283 		hsps_by_number[index] = B_TRUE;
1284 		return (0);
1285 	    }
1286 	}
1287 
1288 	return (-1);
1289 }
1290 
1291 /*
1292  * FUNCTION:	release_volume_name(char *name)
1293  *
1294  * INPUT:	name	- a char * volume name
1295  *
1296  * PURPOSE:	release the input volume name.
1297  *
1298  *		Extract volume number from the input name
1299  *		and use it to index into the array of used
1300  *		volume numbers.  Make that volume number
1301  *		available for use again.
1302  */
1303 void
1304 release_volume_name(
1305 	char		*name)
1306 {
1307 	int		index = -1;
1308 
1309 	if (name != NULL && extract_index(name, _dev_prefix, "%d", &index)) {
1310 		oprintf(OUTPUT_DEBUG,
1311 			gettext("released volume name %s%d\n"),
1312 			_dev_prefix, index);
1313 		devs_by_number[index] = B_FALSE;
1314 	}
1315 }
1316 
1317 /*
1318  * FUNCTION:	release_hsp_name(char *name)
1319  *
1320  * INPUT:	name	- a char * HSP name
1321  *
1322  * PURPOSE:	release the input HSP name.
1323  *
1324  *		Extract volume number from the input name
1325  *		and use it to index into the array of used
1326  *		hsp numbers.  Make that hsp number available
1327  *		for use again.
1328  */
1329 void
1330 release_hsp_name(
1331 	char		*name)
1332 {
1333 	int		index = -1;
1334 
1335 	if (name != NULL && extract_index(name, _hsp_prefix, "%d", &index)) {
1336 		oprintf(OUTPUT_DEBUG,
1337 			gettext("released hsp name %s%d\n"),
1338 			_hsp_prefix, index);
1339 		hsps_by_number[index] = B_FALSE;
1340 	}
1341 }
1342 
1343 /*
1344  * FUNCTION:	get_next_volume_name(char **name)
1345  *
1346  * OUTPUT:	name	- pointer to a char * to hold volume name
1347  *
1348  * RETURNS:	int	- 0 on success
1349  *			 !0 otherwise
1350  *
1351  * PURPOSE:	generate a new volume name using the standard device
1352  *		name prefix and the lowest available device number.
1353  *
1354  *		if type == MIRROR, determine the next available mirror
1355  *		name according to the convention that a mirror name is
1356  *		a multiple of 10.
1357  *
1358  *		If such a name is unavailable, use the next available name.
1359  */
1360 int
1361 get_next_volume_name(
1362     char		**name,
1363     component_type_t	type)
1364 {
1365 	int	next = 0;
1366 
1367 	for (next = 0; next < _max_devs_cfg; ++next) {
1368 	    if ((type == TYPE_MIRROR && ((next % 10) != 0)) ||
1369 		(type != TYPE_MIRROR && ((next % 10) == 0))) {
1370 		/* use/save multiples of 10 for mirrors */
1371 		continue;
1372 	    }
1373 	    if (devs_by_number[next] != B_TRUE) {
1374 		break;
1375 	    }
1376 	}
1377 
1378 	if ((next == _max_devs_cfg) && (type == TYPE_MIRROR)) {
1379 	    /* try next sequentially available name */
1380 	    for (next = 0; next < _max_devs_cfg; ++next) {
1381 		if (devs_by_number[next] != B_TRUE) {
1382 		    break;
1383 		}
1384 	    }
1385 	}
1386 
1387 	if (next == _max_devs_cfg) {
1388 	    volume_set_error(
1389 		    gettext("ran out of logical volume names.\n"));
1390 	    return (-1);
1391 	}
1392 
1393 	*name = (char *)calloc(MAXNAMELEN, sizeof (char));
1394 	if (*name == NULL) {
1395 	    return (ENOMEM);
1396 	}
1397 
1398 	(void) snprintf(*name, MAXNAMELEN-1, "%s%d", _dev_prefix, next);
1399 
1400 	devs_by_number[next] = B_TRUE;
1401 	return (0);
1402 }
1403 
1404 /*
1405  * FUNCTION:	get_next_submirror_name(char *mname, char **subname)
1406  *
1407  * INPUT:	mname	- pointer to a char * mirror name
1408  * OUTPUT:	subname	- pointer to a char * to hold submirror name
1409  *
1410  * RETURNS:	int	- 0 on success
1411  *			 !0 otherwise
1412  *
1413  * PURPOSE:	Determine the next available submirror name according
1414  *		to the convention that each submirror name is a sequential
1415  *		increment of its mirror's name.
1416  *
1417  *		If such a name is unavailable, return the next sequentially
1418  *		available volume name.
1419  */
1420 int
1421 get_next_submirror_name(
1422 	char	*mname,
1423 	char	**subname)
1424 {
1425 	char	buf[MAXNAMELEN];
1426 	int 	error = 0;
1427 	int	next = 0;
1428 	int	i = 0;
1429 
1430 	*subname = NULL;
1431 
1432 	/* try next sequential name: mirror + 1... */
1433 	if (extract_index(mname, _dev_prefix, "%d", &next)) {
1434 	    for (i = next + 1; i < _max_devs_cfg; i++) {
1435 		if ((i % 10) == 0) {
1436 		    /* save for mirrors */
1437 		    continue;
1438 		}
1439 		if (devs_by_number[i] == B_FALSE) {
1440 		    (void) snprintf(buf, MAXNAMELEN-1, "%s%d", _dev_prefix, i);
1441 		    if ((*subname = strdup(buf)) != NULL) {
1442 			devs_by_number[i] = B_TRUE;
1443 		    } else {
1444 			error = ENOMEM;
1445 		    }
1446 		    break;
1447 		}
1448 	    }
1449 	}
1450 
1451 	if ((error == 0) && (*subname == NULL)) {
1452 	    /* name adhering to convention isn't available, */
1453 	    /* use next sequentially available name */
1454 	    error = get_next_volume_name(subname, TYPE_STRIPE);
1455 	}
1456 
1457 	return (error);
1458 }
1459 
1460 /*
1461  * FUNCTION:	get_next_hsp_name(char **name)
1462  *
1463  * OUTPUT:	name	- pointer to a char * to hold name
1464  *
1465  * RETURNS:	int	- 0 on success
1466  *			 !0 otherwise
1467  *
1468  * PURPOSE:	Helper which generates a new hotsparepool name
1469  *		using the standard name prefix and the lowest
1470  *		available hsp number.
1471  */
1472 int
1473 get_next_hsp_name(
1474     char	**name)
1475 {
1476 	int	 next = 0;
1477 
1478 	for (next = 0; next < _max_hsps; ++next) {
1479 	    if (hsps_by_number[next] != B_TRUE) {
1480 		break;
1481 	    }
1482 	}
1483 
1484 	if (next == _max_hsps) {
1485 	    volume_set_error(gettext("ran out of HSP names"));
1486 	    return (-1);
1487 	}
1488 
1489 	*name = (char *)calloc(MAXNAMELEN, sizeof (char));
1490 	if (*name == NULL) {
1491 	    oprintf(OUTPUT_TERSE,
1492 		    gettext("failed to allocate volume name string, "
1493 			    "out of memory"));
1494 	    return (ENOMEM);
1495 	}
1496 
1497 	(void) snprintf(*name, MAXNAMELEN-1, "%s%03d", _hsp_prefix, next);
1498 
1499 	hsps_by_number[next] = B_TRUE;
1500 
1501 	return (0);
1502 }
1503 
1504 static char *
1505 type_name(
1506 	svm_type_t type)
1507 {
1508 	switch (type) {
1509 	case SVM_DISKSET:	return (gettext("disk set"));
1510 	case SVM_MDB:		return (gettext("metadb"));
1511 	case SVM_STRIPE:	return (gettext("stripe"));
1512 	case SVM_MIRROR:	return (gettext("mirror"));
1513 	case SVM_RAID:		return (gettext("raid"));
1514 	case SVM_TRANS:		return (gettext("trans"));
1515 	case SVM_SP:		return (gettext("soft partition"));
1516 	case SVM_HSP:		return (gettext("hot spare pool"));
1517 	case SVM_HS:		return (gettext("hot spare"));
1518 	case SVM_DRIVE:		return (gettext("drive"));
1519 	default:		return (gettext("unknown"));
1520 	}
1521 }
1522 
1523 static svm_snap_t *
1524 svm_snapshot(int *errp)
1525 {
1526 	svm_snap_t	*svm_listp = NULL;
1527 
1528 	*errp = 0;
1529 
1530 	/* initialize the cluster library entry points */
1531 	if (sdssc_bind_library() == SDSSC_ERROR) {
1532 
1533 	    volume_set_error(gettext("sdssc_bin_library() failed\n"));
1534 	    *errp = -1;
1535 
1536 	} else {
1537 
1538 	    /* load the SVM cache */
1539 	    *errp = load_svm(&svm_listp);
1540 
1541 	    if (*errp != 0) {
1542 		free_svm_snapshot(svm_listp);
1543 		svm_listp = NULL;
1544 	    }
1545 
1546 	}
1547 
1548 	return (svm_listp);
1549 }
1550 
1551 static void
1552 free_svm_snapshot(svm_snap_t *listp) {
1553 
1554 	svm_snap_t	*nextp;
1555 
1556 	while (listp != NULL) {
1557 	    nextp = listp->next;
1558 	    free((void *)listp->diskset);
1559 	    free((void *)listp->name);
1560 	    free((void *)listp->slice);
1561 	    free((void *)listp);
1562 	    listp = nextp;
1563 	}
1564 }
1565 
1566 static int
1567 add_record(
1568 	svm_snap_t **listp,
1569 	char *setname,
1570 	svm_type_t type,
1571 	char *mname,
1572 	char *slice_name)
1573 {
1574 	svm_snap_t *sp;
1575 
1576 	sp = (svm_snap_t *)malloc(sizeof (svm_snap_t));
1577 	if (sp == NULL) {
1578 	    return (ENOMEM);
1579 	}
1580 
1581 	if ((sp->diskset = strdup(setname)) == NULL) {
1582 	    free(sp);
1583 	    return (ENOMEM);
1584 	}
1585 
1586 	if ((sp->name = strdup(mname)) == NULL) {
1587 	    free(sp->diskset);
1588 	    free(sp);
1589 	    return (ENOMEM);
1590 	}
1591 
1592 	sp->type = type;
1593 
1594 	if ((sp->slice = strdup(slice_name)) == NULL) {
1595 	    free(sp->diskset);
1596 	    free(sp->name);
1597 	    free(sp);
1598 	    return (ENOMEM);
1599 	}
1600 
1601 	sp->next = *listp;
1602 	*listp = sp;
1603 
1604 	return (0);
1605 }
1606 
1607 static int
1608 diskset_info(
1609 	svm_snap_t **listp,
1610 	mdsetname_t *sp)
1611 {
1612 	md_error_t		error = mdnullerror;
1613 	md_replicalist_t	*replica_list = NULL;
1614 	md_replicalist_t	*mdbp;
1615 	mdnamelist_t		*nlp;
1616 	mdnamelist_t		*trans_list = NULL;
1617 	mdnamelist_t		*mirror_list = NULL;
1618 	mdnamelist_t		*raid_list = NULL;
1619 	mdnamelist_t		*stripe_list = NULL;
1620 	mdnamelist_t		*sp_list = NULL;
1621 	mdhspnamelist_t		*hsp_list = NULL;
1622 
1623 	if (metareplicalist(sp, MD_BASICNAME_OK, &replica_list, &error) < 0) {
1624 	    /* there are no metadb's; that is ok, no need to check the rest */
1625 	    mdclrerror(&error);
1626 	    return (0);
1627 	}
1628 	mdclrerror(&error);
1629 
1630 	for (mdbp = replica_list; mdbp != NULL; mdbp = mdbp->rl_next) {
1631 	    char size[MAXPATHLEN];
1632 
1633 	    (void) snprintf(size, sizeof (size), "%d",
1634 		    (int)mdbp->rl_repp->r_nblk);
1635 
1636 	    if (new_entry(listp, mdbp->rl_repp->r_namep->cname, SVM_MDB, size,
1637 		sp)) {
1638 		metafreereplicalist(replica_list);
1639 		return (ENOMEM);
1640 	    }
1641 	}
1642 	metafreereplicalist(replica_list);
1643 
1644 	if (meta_get_trans_names(sp, &trans_list, 0, &error) >= 0) {
1645 	    for (nlp = trans_list; nlp != NULL; nlp = nlp->next) {
1646 		if (new_entry(listp, nlp->namep->cname, SVM_TRANS,
1647 		    nlp->namep->cname, sp)) {
1648 		    free_names(trans_list);
1649 		    return (ENOMEM);
1650 		}
1651 	    }
1652 
1653 	    free_names(trans_list);
1654 	}
1655 	mdclrerror(&error);
1656 
1657 	if (meta_get_mirror_names(sp, &mirror_list, 0, &error) >= 0) {
1658 	    for (nlp = mirror_list; nlp != NULL; nlp = nlp->next) {
1659 		if (add_record(listp, sp->setname, SVM_MIRROR,
1660 		    nlp->namep->cname, "")) {
1661 		    free_names(mirror_list);
1662 		    return (ENOMEM);
1663 		}
1664 	    }
1665 
1666 	    free_names(mirror_list);
1667 	}
1668 	mdclrerror(&error);
1669 
1670 	if (meta_get_raid_names(sp, &raid_list, 0, &error) >= 0) {
1671 	    for (nlp = raid_list; nlp != NULL; nlp = nlp->next) {
1672 		mdname_t	*mdn;
1673 		md_raid_t	*raid;
1674 
1675 		mdn = metaname(&sp, nlp->namep->cname, &error);
1676 		mdclrerror(&error);
1677 		if (mdn == NULL) {
1678 		    continue;
1679 		}
1680 
1681 		raid = meta_get_raid(sp, mdn, &error);
1682 		mdclrerror(&error);
1683 
1684 		if (raid != NULL) {
1685 		    int i;
1686 
1687 		    for (i = 0; i < raid->cols.cols_len; i++) {
1688 			if (new_entry(listp,
1689 			    raid->cols.cols_val[i].colnamep->cname, SVM_RAID,
1690 			    nlp->namep->cname, sp)) {
1691 			    free_names(raid_list);
1692 			    return (ENOMEM);
1693 			}
1694 		    }
1695 		}
1696 	    }
1697 
1698 	    free_names(raid_list);
1699 	}
1700 	mdclrerror(&error);
1701 
1702 	if (meta_get_stripe_names(sp, &stripe_list, 0, &error) >= 0) {
1703 	    for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) {
1704 		mdname_t	*mdn;
1705 		md_stripe_t	*stripe;
1706 
1707 		mdn = metaname(&sp, nlp->namep->cname, &error);
1708 		mdclrerror(&error);
1709 		if (mdn == NULL) {
1710 		    continue;
1711 		}
1712 
1713 		stripe = meta_get_stripe(sp, mdn, &error);
1714 		mdclrerror(&error);
1715 
1716 		if (stripe != NULL) {
1717 		    int i;
1718 
1719 		    for (i = 0; i < stripe->rows.rows_len; i++) {
1720 			md_row_t	*rowp;
1721 			int		j;
1722 
1723 			rowp = &stripe->rows.rows_val[i];
1724 
1725 			for (j = 0; j < rowp->comps.comps_len; j++) {
1726 			    md_comp_t	*component;
1727 
1728 			    component = &rowp->comps.comps_val[j];
1729 			    if (new_entry(listp, component->compnamep->cname,
1730 				SVM_STRIPE, nlp->namep->cname, sp)) {
1731 				free_names(stripe_list);
1732 				return (ENOMEM);
1733 			    }
1734 			}
1735 		    }
1736 		}
1737 	    }
1738 
1739 	    free_names(stripe_list);
1740 	}
1741 	mdclrerror(&error);
1742 
1743 	if (meta_get_sp_names(sp, &sp_list, 0, &error) >= 0) {
1744 	    for (nlp = sp_list; nlp != NULL; nlp = nlp->next) {
1745 		mdname_t	*mdn;
1746 		md_sp_t		*soft_part;
1747 
1748 		mdn = metaname(&sp, nlp->namep->cname, &error);
1749 		mdclrerror(&error);
1750 		if (mdn == NULL) {
1751 		    continue;
1752 		}
1753 
1754 		soft_part = meta_get_sp(sp, mdn, &error);
1755 		mdclrerror(&error);
1756 
1757 		if (soft_part != NULL) {
1758 		    if (new_entry(listp, soft_part->compnamep->cname, SVM_SP,
1759 			nlp->namep->cname, sp)) {
1760 			free_names(sp_list);
1761 			return (ENOMEM);
1762 		    }
1763 		}
1764 	    }
1765 
1766 	    free_names(sp_list);
1767 	}
1768 	mdclrerror(&error);
1769 
1770 	if (meta_get_hsp_names(sp, &hsp_list, 0, &error) >= 0) {
1771 	    mdhspnamelist_t *nlp;
1772 
1773 	    for (nlp = hsp_list; nlp != NULL; nlp = nlp->next) {
1774 		md_hsp_t	*hsp;
1775 
1776 		hsp = meta_get_hsp(sp, nlp->hspnamep, &error);
1777 		mdclrerror(&error);
1778 		if (hsp != NULL) {
1779 		    int	i;
1780 
1781 		    for (i = 0; i < hsp->hotspares.hotspares_len; i++) {
1782 			md_hs_t	*hs;
1783 
1784 			hs = &hsp->hotspares.hotspares_val[i];
1785 
1786 			if (add_record(listp, sp->setname, SVM_HS,
1787 			    nlp->hspnamep->hspname, hs->hsnamep->bname)) {
1788 			    metafreehspnamelist(hsp_list);
1789 			    return (ENOMEM);
1790 			}
1791 		    }
1792 		}
1793 
1794 		if (add_record(listp, sp->setname, SVM_HSP,
1795 		    nlp->hspnamep->hspname, "")) {
1796 		    metafreehspnamelist(hsp_list);
1797 		    return (ENOMEM);
1798 		}
1799 	    }
1800 
1801 	    metafreehspnamelist(hsp_list);
1802 	}
1803 
1804 	mdclrerror(&error);
1805 
1806 	return (0);
1807 }
1808 
1809 static void
1810 free_names(
1811 	mdnamelist_t *nlp)
1812 {
1813 	mdnamelist_t *p;
1814 
1815 	for (p = nlp; p != NULL; p = p->next) {
1816 	    meta_invalidate_name(p->namep);
1817 	}
1818 	metafreenamelist(nlp);
1819 }
1820 
1821 /*
1822  * Create a list of SVM devices
1823  */
1824 static int
1825 load_svm(
1826 	svm_snap_t **listp)
1827 {
1828 	int		max_sets;
1829 	md_error_t	error = mdnullerror;
1830 	int		i;
1831 
1832 	if ((max_sets = get_max_sets(&error)) == 0) {
1833 	    return (0);
1834 	}
1835 
1836 	if (!mdisok(&error)) {
1837 	    volume_set_error(
1838 		    gettext("failed to get maximum number of disk sets.\n"));
1839 	    mdclrerror(&error);
1840 	    return (-1);
1841 	}
1842 
1843 	/* for each possible set number, see if we really have a disk set */
1844 	for (i = 0; i < max_sets; i++) {
1845 	    mdsetname_t		*sp;
1846 
1847 	    if ((sp = metasetnosetname(i, &error)) == NULL) {
1848 		if (!mdisok(&error) && error.info.errclass == MDEC_RPC) {
1849 		    /* rpc error - no metasets */
1850 		    break;
1851 		}
1852 
1853 		mdclrerror(&error);
1854 		continue;
1855 	    }
1856 
1857 	    mdclrerror(&error);
1858 
1859 	    if (add_record(listp, sp->setname, SVM_DISKSET, sp->setname, "")) {
1860 		metaflushsetname(sp);
1861 		return (ENOMEM);
1862 	    }
1863 
1864 	    /* check for drives in disk sets */
1865 	    if (sp->setno != 0) {
1866 		md_drive_desc	*dd;
1867 
1868 		dd = metaget_drivedesc(sp, MD_BASICNAME_OK | PRINT_FAST,
1869 		    &error);
1870 		mdclrerror(&error);
1871 		for (; dd != NULL; dd = dd->dd_next) {
1872 		    if (add_record(listp, sp->setname, SVM_DRIVE,
1873 			dd->dd_dnp->rname, "")) {
1874 			metaflushsetname(sp);
1875 			return (ENOMEM);
1876 		    }
1877 		}
1878 	    }
1879 
1880 	    if (diskset_info(listp, sp)) {
1881 		metaflushsetname(sp);
1882 		return (ENOMEM);
1883 	    }
1884 
1885 	    metaflushsetname(sp);
1886 	}
1887 
1888 	mdclrerror(&error);
1889 
1890 	return (0);
1891 }
1892 
1893 /* determine if 'sp' is built on a slice */
1894 static int
1895 new_entry(
1896 	svm_snap_t **listp,
1897 	char *slice_name,
1898 	svm_type_t type,
1899 	char *mname,
1900 	mdsetname_t *sp)
1901 {
1902 	mdname_t	*mdn;
1903 	md_error_t	 error = mdnullerror;
1904 
1905 	mdn = metaname(&sp, slice_name, &error);
1906 	if (!mdisok(&error)) {
1907 	    mdn = NULL;
1908 	}
1909 	mdclrerror(&error);
1910 
1911 	if (mdn != NULL && (
1912 	    mdn->drivenamep->type == MDT_ACCES ||
1913 	    mdn->drivenamep->type == MDT_COMP ||
1914 	    mdn->drivenamep->type == MDT_FAST_COMP)) {
1915 
1916 	    return (add_record(listp, sp->setname, type, mname, mdn->bname));
1917 	} else {
1918 	    return (add_record(listp, sp->setname, type, mname, ""));
1919 	}
1920 }
1921 
1922 /*
1923  * FUNCTION:	get_default_stripe_interlace()
1924  *
1925  * RETURNS:	uint64_t - default stripe interlace value
1926  *
1927  * PURPOSE:	Helper which retrieves the default stripe interlace
1928  *		from libmeta.
1929  */
1930 uint64_t
1931 get_default_stripe_interlace()
1932 {
1933 	/* convert back to bytes */
1934 	return ((uint64_t)meta_default_stripe_interlace() * DEV_BSIZE);
1935 }
1936 
1937 /*
1938  * FUNCTION:	get_max_number_of_devices(int *max)
1939  *
1940  * OUTPUT:	max	- pointer to int to hold the configured maximum number
1941  *				of SVM devices
1942  *
1943  * RETURNS:	int	- 0 on success
1944  *			 !0 otherwise
1945  *
1946  * PURPOSE:	Helper which determines the maximum number of allowed
1947  *		SVM devices configured for the system.
1948  *
1949  *		Wrapper around libmeta function meta_get_max_nunits().
1950  */
1951 int
1952 get_max_number_of_devices(
1953 	int	*max)
1954 {
1955 	md_error_t	mderror = mdnullerror;
1956 
1957 	*max = meta_get_nunits(&mderror);
1958 	if (!mdisok(&mderror)) {
1959 	    volume_set_error(mde_sperror(&mderror, NULL));
1960 	    mdclrerror(&mderror);
1961 	    return (-1);
1962 	}
1963 
1964 	return (0);
1965 }
1966 
1967 /*
1968  * FUNCTION:	get_max_number_of_disksets(int *max)
1969  *
1970  * OUTPUT:	max	- pointer to in to hold the configured maximum number
1971  *				of disk sets
1972  *
1973  * RETURNS:	int	- 0 on success
1974  *			 !0 otherwise
1975  *
1976  * PURPOSE:	Helper which determines the maximum number of allowed
1977  *		disk sets which has been configured for the system.
1978  *
1979  *		Wrapper around libmeta function get_max_sets().
1980  */
1981 int
1982 get_max_number_of_disksets(
1983 	int	*max)
1984 {
1985 	md_error_t	mderror = mdnullerror;
1986 
1987 	*max = get_max_sets(&mderror);
1988 	if (!mdisok(&mderror)) {
1989 	    volume_set_error(mde_sperror(&mderror, NULL));
1990 	    mdclrerror(&mderror);
1991 	    return (-1);
1992 	}
1993 
1994 	return (0);
1995 }
1996 
1997 /*
1998  * FUNCTION:	is_reserved_replica_slice_index(char *diskset, char *dname,
1999  *			uint32_t index, boolean_t *bool)
2000  *
2001  * INPUT:	diskset	- char * disk set name
2002  *		dname	- char * disk name
2003  *		index	- integer index of interest
2004  *
2005  * OUTPUT:	bool	- pointer to a boolean_t to hold the result
2006  *
2007  * RETURNS:	int	-  0 - success
2008  *			  !0 - failure
2009  *
2010  * PURPOSE:	Helper which determines if the input slice index on
2011  *		the named disk in the named disk set is the replica
2012  *		slice that is reserved on disks in disk sets.
2013  *
2014  *		The named disk is assumed to be in the named disk set.
2015  *
2016  *		Determines if metassist is being run in a simulated
2017  *		hardware enironment, if not the libmeta function to
2018  *		determine the replica slice index is called.
2019  *
2020  *		If simulation is active, then a local implementation
2021  *		is used to determine the replica slice index.
2022  */
2023 int
2024 is_reserved_replica_slice_index(
2025 	char *diskset,
2026 	char *dname,
2027 	uint32_t index,
2028 	boolean_t *bool)
2029 {
2030 	int		error = 0;
2031 	boolean_t	sim = B_FALSE;
2032 	static char	*simfile = "METASSISTSIMFILE";
2033 
2034 	sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0));
2035 
2036 	if (sim != B_TRUE) {
2037 
2038 	    /* sim disabled: use meta_replicaslice() */
2039 
2040 	    md_error_t		mderror = mdnullerror;
2041 	    mdsetname_t		*sp;
2042 	    mddrivename_t	*dnp;
2043 	    uint_t		replicaslice;
2044 
2045 	    /* slice assumed to be on disk in the named disk set */
2046 	    sp = metasetname(diskset, &mderror);
2047 	    if (!mdisok(&mderror)) {
2048 		volume_set_error(mde_sperror(&mderror, NULL));
2049 		mdclrerror(&mderror);
2050 		return (-1);
2051 	    }
2052 
2053 	    dnp = metadrivename(&sp, dname, &mderror);
2054 	    if (!mdisok(&mderror)) {
2055 		volume_set_error(mde_sperror(&mderror, NULL));
2056 		mdclrerror(&mderror);
2057 		return (-1);
2058 	    }
2059 
2060 	    if (meta_replicaslice(dnp, &replicaslice, &mderror) != 0) {
2061 		volume_set_error(mde_sperror(&mderror, NULL));
2062 		mdclrerror(&mderror);
2063 		return (-1);
2064 	    }
2065 
2066 	    *bool = (replicaslice == (uint_t)index);
2067 
2068 	} else {
2069 
2070 	    dm_descriptor_t	disk;
2071 	    boolean_t		efi = B_FALSE;
2072 
2073 	    /* sim enabled: use same logic as meta_replicaslice() */
2074 	    ((error = disk_get_by_name(dname, &disk)) != 0) ||
2075 	    (error = disk_get_is_efi(disk, &efi));
2076 	    if (error == 0) {
2077 
2078 		if (efi == B_FALSE) {
2079 		    *bool = (index == MD_SLICE7);
2080 		} else {
2081 		    *bool = (index == MD_SLICE6);
2082 		}
2083 	    }
2084 	}
2085 
2086 	return (error);
2087 }
2088