1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <fcntl.h>
27#include <libdevinfo.h>
28#include <stdio.h>
29#include <sys/sunddi.h>
30#include <sys/types.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <string.h>
34#include <libintl.h>
35#include <locale.h>
36#include <sys/debug.h>
37#include <strings.h>
38#include <sys/stat.h>
39#include <sys/swap.h>
40
41#include "libdiskmgt.h"
42#include "disks_private.h"
43#include "partition.h"
44
45#define	ANY_ZPOOL_USE(who) \
46	(((who) == DM_WHO_ZPOOL_FORCE) || \
47	((who) == DM_WHO_ZPOOL) || \
48	((who) == DM_WHO_ZPOOL_SPARE))
49
50extern	char	*getfullblkname();
51
52extern dm_desc_type_t drive_assoc_types[];
53extern dm_desc_type_t bus_assoc_types[];
54extern dm_desc_type_t controller_assoc_types[];
55extern dm_desc_type_t media_assoc_types[];
56extern dm_desc_type_t slice_assoc_types[];
57extern dm_desc_type_t partition_assoc_types[];
58extern dm_desc_type_t path_assoc_types[];
59extern dm_desc_type_t alias_assoc_types[];
60
61
62static dm_descriptor_t *ptr_array_to_desc_array(descriptor_t **ptrs, int *errp);
63static descriptor_t **desc_array_to_ptr_array(dm_descriptor_t *da, int *errp);
64static int build_usage_string(char *dname, char *by, char *data, char **use,
65	int *found, int *errp);
66
67void
68dm_free_descriptor(dm_descriptor_t desc)
69{
70	descriptor_t	*dp;
71
72	if (desc == 0) {
73		return;
74	}
75	dp = (descriptor_t *)(uintptr_t)desc;
76
77	cache_wlock();
78	cache_free_descriptor(dp);
79	cache_unlock();
80}
81
82void
83dm_free_descriptors(dm_descriptor_t *desc_list)
84{
85	descriptor_t	**dp;
86	int		error;
87
88	if (desc_list == NULL) {
89		return;
90	}
91	dp = desc_array_to_ptr_array(desc_list, &error);
92	if (error != 0) {
93		free(desc_list);
94		return;
95	}
96
97	cache_wlock();
98	cache_free_descriptors(dp);
99	cache_unlock();
100}
101
102/*ARGSUSED*/
103void
104dm_free_name(char *name)
105{
106	free(name);
107}
108
109dm_descriptor_t *
110dm_get_associated_descriptors(dm_descriptor_t desc, dm_desc_type_t type,
111    int *errp)
112{
113	descriptor_t **descs = NULL;
114	descriptor_t  *dp;
115
116
117	dp = (descriptor_t *)(uintptr_t)desc;
118
119	cache_wlock();
120
121	if (!cache_is_valid_desc(dp)) {
122		cache_unlock();
123		*errp = EBADF;
124		return (NULL);
125	}
126
127	/* verify that the descriptor is still valid */
128	if (dp->p.generic == NULL) {
129		cache_unlock();
130		*errp = ENODEV;
131		return (NULL);
132	}
133
134	switch (dp->type) {
135	case DM_DRIVE:
136		descs = drive_get_assoc_descriptors(dp, type, errp);
137		break;
138	case DM_BUS:
139		descs = bus_get_assoc_descriptors(dp, type, errp);
140		break;
141	case DM_CONTROLLER:
142		descs = controller_get_assoc_descriptors(dp, type, errp);
143		break;
144	case DM_MEDIA:
145		descs = media_get_assoc_descriptors(dp, type, errp);
146		break;
147	case DM_SLICE:
148		descs = slice_get_assoc_descriptors(dp, type, errp);
149		break;
150	case DM_PARTITION:
151		descs = partition_get_assoc_descriptors(dp, type, errp);
152		break;
153	case DM_PATH:
154		descs = path_get_assoc_descriptors(dp, type, errp);
155		break;
156	case DM_ALIAS:
157		descs = alias_get_assoc_descriptors(dp, type, errp);
158		break;
159	default:
160		*errp = EINVAL;
161		break;
162	}
163
164	cache_unlock();
165
166	return (ptr_array_to_desc_array(descs, errp));
167}
168
169dm_desc_type_t *
170dm_get_associated_types(dm_desc_type_t type)
171{
172	switch (type) {
173	case DM_DRIVE:
174		return (drive_assoc_types);
175	case DM_BUS:
176		return (bus_assoc_types);
177	case DM_CONTROLLER:
178		return (controller_assoc_types);
179	case DM_MEDIA:
180		return (media_assoc_types);
181	case DM_SLICE:
182		return (slice_assoc_types);
183	case DM_PARTITION:
184		return (partition_assoc_types);
185	case DM_PATH:
186		return (path_assoc_types);
187	case DM_ALIAS:
188		return (alias_assoc_types);
189	}
190
191	return (NULL);
192}
193
194nvlist_t *
195dm_get_attributes(dm_descriptor_t desc, int *errp)
196{
197	descriptor_t	*dp;
198	nvlist_t	*attrs = NULL;
199
200
201	dp = (descriptor_t *)(uintptr_t)desc;
202
203	cache_rlock();
204
205	if (!cache_is_valid_desc(dp)) {
206		cache_unlock();
207		*errp = EBADF;
208		return (NULL);
209	}
210
211	/* verify that the descriptor is still valid */
212	if (dp->p.generic == NULL) {
213		cache_unlock();
214		*errp = ENODEV;
215		return (NULL);
216	}
217
218	switch (dp->type) {
219	case DM_DRIVE:
220		attrs = drive_get_attributes(dp, errp);
221		break;
222	case DM_BUS:
223		attrs = bus_get_attributes(dp, errp);
224		break;
225	case DM_CONTROLLER:
226		attrs = controller_get_attributes(dp, errp);
227		break;
228	case DM_MEDIA:
229		attrs = media_get_attributes(dp, errp);
230		break;
231	case DM_SLICE:
232		attrs = slice_get_attributes(dp, errp);
233		break;
234	case DM_PARTITION:
235		attrs = partition_get_attributes(dp, errp);
236		break;
237	case DM_PATH:
238		attrs = path_get_attributes(dp, errp);
239		break;
240	case DM_ALIAS:
241		attrs = alias_get_attributes(dp, errp);
242		break;
243	default:
244		*errp = EINVAL;
245		break;
246	}
247
248	cache_unlock();
249
250	return (attrs);
251}
252
253dm_descriptor_t
254dm_get_descriptor_by_name(dm_desc_type_t desc_type, char *name, int *errp)
255{
256	dm_descriptor_t desc = 0;
257
258
259	cache_wlock();
260
261	switch (desc_type) {
262	case DM_DRIVE:
263		desc = (uintptr_t)drive_get_descriptor_by_name(name, errp);
264		break;
265	case DM_BUS:
266		desc = (uintptr_t)bus_get_descriptor_by_name(name, errp);
267		break;
268	case DM_CONTROLLER:
269		desc = (uintptr_t)controller_get_descriptor_by_name(name,
270		    errp);
271		break;
272	case DM_MEDIA:
273		desc = (uintptr_t)media_get_descriptor_by_name(name, errp);
274		break;
275	case DM_SLICE:
276		desc = (uintptr_t)slice_get_descriptor_by_name(name, errp);
277		break;
278	case DM_PARTITION:
279		desc = (uintptr_t)partition_get_descriptor_by_name(name,
280		    errp);
281		break;
282	case DM_PATH:
283		desc = (uintptr_t)path_get_descriptor_by_name(name, errp);
284		break;
285	case DM_ALIAS:
286		desc = (uintptr_t)alias_get_descriptor_by_name(name, errp);
287		break;
288	default:
289		*errp = EINVAL;
290		break;
291	}
292
293	cache_unlock();
294
295	return (desc);
296}
297
298dm_descriptor_t *
299dm_get_descriptors(dm_desc_type_t type, int filter[], int *errp)
300{
301	descriptor_t **descs = NULL;
302
303
304	cache_wlock();
305
306	switch (type) {
307	case DM_DRIVE:
308		descs = drive_get_descriptors(filter, errp);
309		break;
310	case DM_BUS:
311		descs = bus_get_descriptors(filter, errp);
312		break;
313	case DM_CONTROLLER:
314		descs = controller_get_descriptors(filter, errp);
315		break;
316	case DM_MEDIA:
317		descs = media_get_descriptors(filter, errp);
318		break;
319	case DM_SLICE:
320		descs = slice_get_descriptors(filter, errp);
321		break;
322	case DM_PARTITION:
323		descs = partition_get_descriptors(filter, errp);
324		break;
325	case DM_PATH:
326		descs = path_get_descriptors(filter, errp);
327		break;
328	case DM_ALIAS:
329		descs = alias_get_descriptors(filter, errp);
330		break;
331	default:
332		*errp = EINVAL;
333		break;
334	}
335
336	cache_unlock();
337
338	return (ptr_array_to_desc_array(descs, errp));
339}
340
341char *
342dm_get_name(dm_descriptor_t desc, int *errp)
343{
344	descriptor_t	*dp;
345	char		*nm = NULL;
346	char		*name = NULL;
347
348	dp = (descriptor_t *)(uintptr_t)desc;
349
350	cache_rlock();
351
352	if (!cache_is_valid_desc(dp)) {
353		cache_unlock();
354		*errp = EBADF;
355		return (NULL);
356	}
357
358	/* verify that the descriptor is still valid */
359	if (dp->p.generic == NULL) {
360		cache_unlock();
361		*errp = ENODEV;
362		return (NULL);
363	}
364
365	switch (dp->type) {
366	case DM_DRIVE:
367		nm = (drive_get_name(dp));
368		break;
369	case DM_BUS:
370		nm = (bus_get_name(dp));
371		break;
372	case DM_CONTROLLER:
373		nm = (controller_get_name(dp));
374		break;
375	case DM_MEDIA:
376		nm = (media_get_name(dp));
377		break;
378	case DM_SLICE:
379		nm = (slice_get_name(dp));
380		break;
381	case DM_PARTITION:
382		nm = (partition_get_name(dp));
383		break;
384	case DM_PATH:
385		nm = (path_get_name(dp));
386		break;
387	case DM_ALIAS:
388		nm = (alias_get_name(dp));
389		break;
390	}
391
392	cache_unlock();
393
394	*errp = 0;
395	if (nm != NULL) {
396		name = strdup(nm);
397		if (name == NULL) {
398			*errp = ENOMEM;
399			return (NULL);
400		}
401		return (name);
402	}
403	return (NULL);
404}
405
406nvlist_t *
407dm_get_stats(dm_descriptor_t desc, int stat_type, int *errp)
408{
409	descriptor_t  *dp;
410	nvlist_t	*stats = NULL;
411
412
413	dp = (descriptor_t *)(uintptr_t)desc;
414
415	cache_rlock();
416
417	if (!cache_is_valid_desc(dp)) {
418		cache_unlock();
419		*errp = EBADF;
420		return (NULL);
421	}
422
423	/* verify that the descriptor is still valid */
424	if (dp->p.generic == NULL) {
425		cache_unlock();
426		*errp = ENODEV;
427		return (NULL);
428	}
429
430	switch (dp->type) {
431	case DM_DRIVE:
432		stats = drive_get_stats(dp, stat_type, errp);
433		break;
434	case DM_BUS:
435		stats = bus_get_stats(dp, stat_type, errp);
436		break;
437	case DM_CONTROLLER:
438		stats = controller_get_stats(dp, stat_type, errp);
439		break;
440	case DM_MEDIA:
441		stats = media_get_stats(dp, stat_type, errp);
442		break;
443	case DM_SLICE:
444		if (stat_type == DM_SLICE_STAT_USE) {
445			/*
446			 * If NOINUSE_CHECK is set, we do not perform
447			 * the in use checking if the user has set stat_type
448			 * DM_SLICE_STAT_USE
449			 */
450			if (NOINUSE_SET) {
451				stats = NULL;
452				break;
453			}
454		}
455		stats = slice_get_stats(dp, stat_type, errp);
456		break;
457	case DM_PARTITION:
458		stats = partition_get_stats(dp, stat_type, errp);
459		break;
460	case DM_PATH:
461		stats = path_get_stats(dp, stat_type, errp);
462		break;
463	case DM_ALIAS:
464		stats = alias_get_stats(dp, stat_type, errp);
465		break;
466	default:
467		*errp = EINVAL;
468		break;
469	}
470
471	cache_unlock();
472
473	return (stats);
474}
475
476dm_desc_type_t
477dm_get_type(dm_descriptor_t desc)
478{
479	descriptor_t  *dp;
480
481	dp = (descriptor_t *)(uintptr_t)desc;
482
483	cache_rlock();
484
485	if (!cache_is_valid_desc(dp)) {
486		cache_unlock();
487		return (-1);
488	}
489
490	cache_unlock();
491
492	return (dp->type);
493}
494/*
495 * Returns, via slices paramater, a dm_descriptor_t list of
496 * slices for the named disk drive.
497 */
498void
499dm_get_slices(char *drive, dm_descriptor_t **slices, int *errp)
500{
501	dm_descriptor_t alias;
502	dm_descriptor_t	*media;
503	dm_descriptor_t *disk;
504
505	*slices = NULL;
506	*errp = 0;
507
508	if (drive == NULL) {
509		return;
510	}
511
512	alias = dm_get_descriptor_by_name(DM_ALIAS, drive, errp);
513
514	/*
515	 * Errors must be handled by the caller. The dm_descriptor_t *
516	 * values will be NULL if an error occured in these calls.
517	 */
518
519	if (alias != 0) {
520		disk = dm_get_associated_descriptors(alias, DM_DRIVE, errp);
521		dm_free_descriptor(alias);
522		if (disk != NULL) {
523			media = dm_get_associated_descriptors(*disk,
524			    DM_MEDIA, errp);
525			dm_free_descriptors(disk);
526			if (media != NULL) {
527				*slices = dm_get_associated_descriptors(*media,
528				    DM_SLICE, errp);
529				dm_free_descriptors(media);
530			}
531		}
532	}
533}
534/*
535 * Convenience function to get slice stats
536 */
537void
538dm_get_slice_stats(char *slice, nvlist_t **dev_stats, int *errp)
539{
540	dm_descriptor_t	devp;
541
542	*dev_stats = NULL;
543	*errp = 0;
544
545	if (slice == NULL) {
546		return;
547	}
548
549	/*
550	 * Errors must be handled by the caller. The dm_descriptor_t *
551	 * values will be NULL if an error occured in these calls.
552	 */
553	devp = dm_get_descriptor_by_name(DM_SLICE, slice, errp);
554	if (devp != 0) {
555		*dev_stats = dm_get_stats(devp, DM_SLICE_STAT_USE, errp);
556		dm_free_descriptor(devp);
557	}
558}
559
560/*
561 * Checks for overlapping slices.   If the given device is a slice, and it
562 * overlaps with any non-backup slice on the disk, return true with a detailed
563 * description similar to dm_inuse().
564 */
565int
566dm_isoverlapping(char *slicename, char **overlaps_with, int *errp)
567{
568	dm_descriptor_t slice = 0;
569	dm_descriptor_t *media = NULL;
570	dm_descriptor_t *slices = NULL;
571	int 		i = 0;
572	uint32_t	in_snum;
573	uint64_t 	start_block = 0;
574	uint64_t 	end_block = 0;
575	uint64_t 	media_size = 0;
576	uint64_t 	size = 0;
577	nvlist_t 	*media_attrs = NULL;
578	nvlist_t 	*slice_attrs = NULL;
579	int		ret = 0;
580
581	slice = dm_get_descriptor_by_name(DM_SLICE, slicename, errp);
582	if (slice == 0)
583		goto out;
584
585	/*
586	 * Get the list of slices be fetching the associated media, and then all
587	 * associated slices.
588	 */
589	media = dm_get_associated_descriptors(slice, DM_MEDIA, errp);
590	if (media == NULL || *media == 0 || *errp != 0)
591		goto out;
592
593	slices = dm_get_associated_descriptors(*media, DM_SLICE, errp);
594	if (slices == NULL || *slices == 0 || *errp != 0)
595		goto out;
596
597	media_attrs = dm_get_attributes(*media, errp);
598	if (media_attrs == NULL || *errp)
599		goto out;
600
601	*errp = nvlist_lookup_uint64(media_attrs, DM_NACCESSIBLE, &media_size);
602	if (*errp != 0)
603		goto out;
604
605	slice_attrs = dm_get_attributes(slice, errp);
606	if (slice_attrs == NULL || *errp != 0)
607		goto out;
608
609	*errp = nvlist_lookup_uint64(slice_attrs, DM_START, &start_block);
610	if (*errp != 0)
611		goto out;
612
613	*errp = nvlist_lookup_uint64(slice_attrs, DM_SIZE, &size);
614	if (*errp != 0)
615		goto out;
616
617	*errp = nvlist_lookup_uint32(slice_attrs, DM_INDEX, &in_snum);
618	if (*errp != 0)
619		goto out;
620
621	end_block = (start_block + size) - 1;
622
623	for (i = 0; slices[i]; i ++) {
624		uint64_t other_start;
625		uint64_t other_end;
626		uint64_t other_size;
627		uint32_t snum;
628
629		nvlist_t *other_attrs = dm_get_attributes(slices[i], errp);
630
631		if (other_attrs == NULL)
632			continue;
633
634		if (*errp != 0)
635			goto out;
636
637		*errp = nvlist_lookup_uint64(other_attrs, DM_START,
638		    &other_start);
639		if (*errp) {
640			nvlist_free(other_attrs);
641			goto out;
642		}
643
644		*errp = nvlist_lookup_uint64(other_attrs, DM_SIZE,
645		    &other_size);
646
647		if (*errp) {
648			nvlist_free(other_attrs);
649			ret = -1;
650			goto out;
651		}
652
653		other_end = (other_size + other_start) - 1;
654
655		*errp = nvlist_lookup_uint32(other_attrs, DM_INDEX,
656		    &snum);
657
658		if (*errp) {
659			nvlist_free(other_attrs);
660			ret = -1;
661			goto out;
662		}
663
664		/*
665		 * Check to see if there are > 2 overlapping regions
666		 * on this media in the same region as this slice.
667		 * This is done by assuming the following:
668		 *   	Slice 2 is the backup slice if it is the size
669		 *	of the whole disk
670		 * If slice 2 is the overlap and slice 2 is the size of
671		 * the whole disk, continue. If another slice is found
672		 * that overlaps with our slice, return it.
673		 * There is the potential that there is more than one slice
674		 * that our slice overlaps with, however, we only return
675		 * the first overlapping slice we find.
676		 *
677		 */
678		if (start_block >= other_start && start_block <= other_end) {
679			if ((snum == 2 && (other_size == media_size)) ||
680			    snum == in_snum) {
681				continue;
682			} else {
683				char *str = dm_get_name(slices[i], errp);
684				if (*errp != 0) {
685					nvlist_free(other_attrs);
686					ret = -1;
687					goto out;
688				}
689				*overlaps_with = strdup(str);
690				dm_free_name(str);
691				nvlist_free(other_attrs);
692				ret = 1;
693				goto out;
694			}
695		} else if (other_start >= start_block &&
696		    other_start <= end_block) {
697			if ((snum == 2 && (other_size == media_size)) ||
698			    snum == in_snum) {
699				continue;
700			} else {
701				char *str = dm_get_name(slices[i], errp);
702				if (*errp != 0) {
703					nvlist_free(other_attrs);
704					ret = -1;
705					goto out;
706				}
707				*overlaps_with = strdup(str);
708				dm_free_name(str);
709				nvlist_free(other_attrs);
710				ret = 1;
711				goto out;
712			}
713		}
714		nvlist_free(other_attrs);
715	}
716
717out:
718	nvlist_free(media_attrs);
719	nvlist_free(slice_attrs);
720
721	if (slices)
722		dm_free_descriptors(slices);
723	if (media)
724		dm_free_descriptors(media);
725	if (slice)
726		dm_free_descriptor(slice);
727
728	return (ret);
729}
730
731/*
732 * Get the full list of swap entries.  Returns -1 on error, or >= 0 to
733 * indicate the number of entries in the list.  Callers are responsible
734 * for calling dm_free_swapentries() to deallocate memory.  If this
735 * returns 0, the swaptbl_t still needs to be freed.
736 */
737int
738dm_get_swapentries(swaptbl_t **stp, int *errp)
739{
740	int count, i;
741	swaptbl_t *tbl;
742	char *ptr;
743
744	*stp = NULL;
745
746	/* get number of swap entries */
747	if ((count = swapctl(SC_GETNSWP, NULL)) < 0) {
748		*errp = errno;
749		return (-1);
750	}
751
752	if (count == 0) {
753		return (0);
754	}
755
756	/* allocate space */
757	tbl = calloc(1, sizeof (int) + count * sizeof (swapent_t));
758	if (tbl == NULL) {
759		*errp = ENOMEM;
760		return (-1);
761	}
762
763	ptr = calloc(1, count * MAXPATHLEN);
764	if (ptr == NULL) {
765		*errp = ENOMEM;
766		free(tbl);
767		return (-1);
768	}
769
770	/* set up pointers to the pathnames */
771	tbl->swt_n = count;
772	for (i = 0; i < count; i++) {
773		tbl->swt_ent[i].ste_path = ptr;
774		ptr += MAXPATHLEN;
775	}
776
777	/* get list of swap paths */
778	count = swapctl(SC_LIST, tbl);
779	if (count < 0) {
780		*errp = errno;
781		free(ptr);
782		free(tbl);
783		return (-1);
784	}
785
786	*stp = tbl;
787	return (count);
788}
789
790/* ARGSUSED */
791void
792dm_free_swapentries(swaptbl_t *stp)
793{
794	ASSERT(stp != NULL);
795
796	free(stp->swt_ent[0].ste_path);
797	free(stp);
798}
799
800/*
801 * Check a slice to see if it's being used by swap.
802 */
803int
804dm_inuse_swap(const char *dev_name, int *errp)
805{
806	int count;
807	int found;
808	swaptbl_t *tbl = NULL;
809
810	*errp = 0;
811
812	count = dm_get_swapentries(&tbl, errp);
813	if (count < 0 || *errp) {
814		if (tbl)
815			dm_free_swapentries(tbl);
816		return (-1);
817	}
818
819	/* if there are no swap entries, we're done */
820	if (!count) {
821		return (0);
822	}
823
824	ASSERT(tbl != NULL);
825
826	found = 0;
827	while (count--) {
828		if (strcmp(dev_name, tbl->swt_ent[count].ste_path) == 0) {
829			found = 1;
830			break;
831		}
832	}
833
834	dm_free_swapentries(tbl);
835	return (found);
836}
837
838/*
839 * Returns 'in use' details, if found, about a specific dev_name,
840 * based on the caller(who). It is important to note that it is possible
841 * for there to be more than one 'in use' statistic regarding a dev_name.
842 * The **msg parameter returns a list of 'in use' details. This message
843 * is formatted via gettext().
844 */
845int
846dm_inuse(char *dev_name, char **msg, dm_who_type_t who, int *errp)
847{
848	nvlist_t *dev_stats = NULL;
849	char *by, *data;
850	nvpair_t *nvwhat = NULL;
851	nvpair_t *nvdesc = NULL;
852	int	found = 0;
853	int	err;
854	char	*dname = NULL;
855
856	*errp = 0;
857	*msg = NULL;
858
859	/*
860	 * If the user doesn't want to do in use checking, return.
861	 */
862
863	if (NOINUSE_SET)
864		return (0);
865
866	dname = getfullblkname(dev_name);
867	/*
868	 * If we cannot find the block name, we cannot check the device
869	 * for in use statistics. So, return found, which is == 0.
870	 */
871	if (dname == NULL || *dname == '\0') {
872		return (found);
873	}
874
875	/*
876	 * Slice stats for swap devices are only returned if mounted
877	 * (e.g. /tmp).  Other devices or files being used for swap
878	 * are ignored, so we add a special check here to use swapctl(2)
879	 * to perform in-use checking.
880	 */
881	if (ANY_ZPOOL_USE(who) && (err = dm_inuse_swap(dname, errp))) {
882
883		/* on error, dm_inuse_swap sets errp */
884		if (err < 0) {
885			free(dname);
886			return (err);
887		}
888
889		/* simulate a mounted swap device */
890		(void) build_usage_string(dname, DM_USE_MOUNT, "swap", msg,
891		    &found, errp);
892
893		/* if this fails, dm_get_usage_string changed */
894		ASSERT(found == 1);
895
896		free(dname);
897		return (found);
898	}
899
900	dm_get_slice_stats(dname, &dev_stats, errp);
901	if (dev_stats == NULL) {
902		/*
903		 * If there is an error, but it isn't a no device found error
904		 * return the error as recorded. Otherwise, with a full
905		 * block name, we might not be able to get the slice
906		 * associated, and will get an ENODEV error.
907		 */
908		if (*errp == ENODEV)
909			*errp = 0;
910		free(dname);
911		return (found);
912	}
913
914	for (;;) {
915
916		nvwhat = nvlist_next_nvpair(dev_stats, nvdesc);
917		nvdesc = nvlist_next_nvpair(dev_stats, nvwhat);
918
919		/*
920		 * End of the list found.
921		 */
922		if (nvwhat == NULL || nvdesc == NULL) {
923			break;
924		}
925		/*
926		 * Otherwise, we check to see if this client(who) cares
927		 * about this in use scenario
928		 */
929
930		ASSERT(strcmp(nvpair_name(nvwhat), DM_USED_BY) == 0);
931		ASSERT(strcmp(nvpair_name(nvdesc), DM_USED_NAME) == 0);
932		/*
933		 * If we error getting the string value continue on
934		 * to the next pair(if there is one)
935		 */
936		if (nvpair_value_string(nvwhat, &by)) {
937			continue;
938		}
939		if (nvpair_value_string(nvdesc, &data)) {
940			continue;
941		}
942
943		switch (who) {
944			case DM_WHO_MKFS:
945				/*
946				 * mkfs is not in use for these cases.
947				 * All others are in use.
948				 */
949				if (strcmp(by, DM_USE_LU) == 0 ||
950				    strcmp(by, DM_USE_FS) == 0 ||
951				    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
952					break;
953				}
954				if (build_usage_string(dname,
955				    by, data, msg, &found, errp) != 0) {
956					if (*errp) {
957						goto out;
958					}
959				}
960				break;
961			case DM_WHO_SWAP:
962				/*
963				 * Not in use for this.
964				 */
965				if (strcmp(by, DM_USE_DUMP) == 0 ||
966				    strcmp(by, DM_USE_FS) == 0 ||
967				    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
968					break;
969				}
970				if (strcmp(by, DM_USE_LU) == 0 &&
971				    strcmp(data, "-") == 0) {
972					break;
973				}
974				if (strcmp(by, DM_USE_VFSTAB) == 0 &&
975				    strcmp(data, "") == 0) {
976					break;
977				}
978				if (build_usage_string(dname,
979				    by, data, msg, &found, errp) != 0) {
980					if (*errp) {
981						goto out;
982					}
983				}
984				break;
985			case DM_WHO_DUMP:
986				/*
987				 * Not in use for this.
988				 */
989				if ((strcmp(by, DM_USE_MOUNT) == 0 &&
990				    strcmp(data, "swap") == 0) ||
991				    strcmp(by, DM_USE_DUMP) == 0 ||
992				    strcmp(by, DM_USE_FS) == 0 ||
993				    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
994					break;
995				}
996				if (build_usage_string(dname,
997				    by, data, msg, &found, errp)) {
998					if (*errp) {
999						goto out;
1000					}
1001				}
1002				break;
1003
1004			case DM_WHO_FORMAT:
1005				if (strcmp(by, DM_USE_FS) == 0 ||
1006				    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0)
1007					break;
1008				if (build_usage_string(dname,
1009				    by, data, msg, &found, errp) != 0) {
1010					if (*errp) {
1011						goto out;
1012					}
1013				}
1014				break;
1015
1016			case DM_WHO_ZPOOL_FORCE:
1017				if (strcmp(by, DM_USE_FS) == 0 ||
1018				    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0)
1019					break;
1020				/* FALLTHROUGH */
1021			case DM_WHO_ZPOOL:
1022				if (build_usage_string(dname,
1023				    by, data, msg, &found, errp) != 0) {
1024					if (*errp)
1025						goto out;
1026				}
1027				break;
1028
1029			case DM_WHO_ZPOOL_SPARE:
1030				if (strcmp(by, DM_USE_SPARE_ZPOOL) != 0) {
1031					if (build_usage_string(dname, by,
1032					    data, msg, &found, errp) != 0) {
1033						if (*errp)
1034							goto out;
1035					}
1036				}
1037				break;
1038
1039			default:
1040				/*
1041				 * nothing found in use for this client
1042				 * of libdiskmgt. Default is 'not in use'.
1043				 */
1044				break;
1045		}
1046	}
1047out:
1048	if (dname != NULL)
1049		free(dname);
1050	nvlist_free(dev_stats);
1051
1052	return (found);
1053}
1054
1055void
1056dm_get_usage_string(char *what, char *how, char **usage_string)
1057{
1058
1059
1060	if (usage_string == NULL || what == NULL) {
1061		return;
1062	}
1063	*usage_string = NULL;
1064
1065	if (strcmp(what, DM_USE_MOUNT) == 0) {
1066		if (strcmp(how, "swap") == 0) {
1067			*usage_string = dgettext(TEXT_DOMAIN,
1068			    "%s is currently used by swap. Please see swap(1M)."
1069			    "\n");
1070		} else {
1071			*usage_string = dgettext(TEXT_DOMAIN,
1072			    "%s is currently mounted on %s."
1073			    " Please see umount(1M).\n");
1074		}
1075	} else if (strcmp(what, DM_USE_VFSTAB) == 0) {
1076		*usage_string = dgettext(TEXT_DOMAIN,
1077		    "%s is normally mounted on %s according to /etc/vfstab. "
1078		    "Please remove this entry to use this device.\n");
1079	} else if (strcmp(what, DM_USE_FS) == 0) {
1080		*usage_string = dgettext(TEXT_DOMAIN,
1081		    "%s contains a %s filesystem.\n");
1082	} else if (strcmp(what, DM_USE_VXVM) == 0) {
1083		*usage_string = dgettext(TEXT_DOMAIN,
1084		    "%s is part of VxVM volume %s.\n");
1085	} else if (strcmp(what, DM_USE_LU) == 0) {
1086		*usage_string = dgettext(TEXT_DOMAIN,
1087		    "%s is in use for live upgrade %s. Please see ludelete(1M)."
1088		    "\n");
1089	} else if (strcmp(what, DM_USE_DUMP) == 0) {
1090		*usage_string = dgettext(TEXT_DOMAIN,
1091		    "%s is in use by %s. Please see dumpadm(1M)."
1092		    "\n");
1093	} else if (strcmp(what, DM_USE_EXPORTED_ZPOOL) == 0) {
1094		*usage_string = dgettext(TEXT_DOMAIN,
1095		    "%s is part of exported or potentially active ZFS pool %s. "
1096		    "Please see zpool(1M).\n");
1097	} else if (strcmp(what, DM_USE_ACTIVE_ZPOOL) == 0) {
1098		*usage_string = dgettext(TEXT_DOMAIN,
1099		    "%s is part of active ZFS pool %s. Please see zpool(1M)."
1100		    "\n");
1101	} else if (strcmp(what, DM_USE_SPARE_ZPOOL) == 0) {
1102		*usage_string = dgettext(TEXT_DOMAIN,
1103		    "%s is reserved as a hot spare for ZFS pool %s.  Please "
1104		    "see zpool(1M).\n");
1105	} else if (strcmp(what, DM_USE_L2CACHE_ZPOOL) == 0) {
1106		*usage_string = dgettext(TEXT_DOMAIN,
1107		    "%s is in use as a cache device for ZFS pool %s.  "
1108		    "Please see zpool(1M).\n");
1109	}
1110}
1111void
1112libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, int *errp)
1113{
1114	if (*errp == 0) {
1115		*errp = nvlist_add_string(attrs, name, val);
1116	}
1117}
1118
1119descriptor_t **
1120libdiskmgt_empty_desc_array(int *errp)
1121{
1122	descriptor_t	**empty;
1123
1124	empty = (descriptor_t **)calloc(1, sizeof (descriptor_t *));
1125	if (empty == NULL) {
1126		*errp = ENOMEM;
1127		return (NULL);
1128	}
1129	empty[0] = NULL;
1130
1131	*errp = 0;
1132	return (empty);
1133}
1134
1135void
1136libdiskmgt_init_debug()
1137{
1138	char	*valp;
1139
1140	if ((valp = getenv(DM_DEBUG)) != NULL) {
1141		dm_debug = atoi(valp);
1142	}
1143}
1144
1145int
1146libdiskmgt_str_eq(char *nm1, char *nm2)
1147{
1148	if (nm1 == NULL) {
1149		if (dm_debug) {
1150			(void) fprintf(stderr, "WARNING: str_eq nm1 NULL\n");
1151		}
1152
1153		if (nm2 == NULL) {
1154			return (1);
1155		} else {
1156			return (0);
1157		}
1158	}
1159
1160	/* nm1 != NULL */
1161
1162	if (nm2 == NULL) {
1163		if (dm_debug) {
1164			(void) fprintf(stderr, "WARNING: str_eq nm2 NULL\n");
1165		}
1166		return (0);
1167	}
1168
1169	if (strcmp(nm1, nm2) == 0) {
1170		return (1);
1171	}
1172
1173	return (0);
1174}
1175
1176/*ARGSUSED*/
1177static descriptor_t **
1178desc_array_to_ptr_array(dm_descriptor_t *descs, int *errp)
1179{
1180#ifdef _LP64
1181	return ((descriptor_t **)descs);
1182#else
1183	/* convert the 64 bit descriptors to 32 bit ptrs */
1184	int	cnt;
1185	int	i;
1186	descriptor_t **da;
1187
1188	for (cnt = 0; descs[cnt]; cnt++)
1189		;
1190
1191	da = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
1192	if (da == NULL) {
1193		*errp = ENOMEM;
1194		return (NULL);
1195	}
1196
1197	for (i = 0; descs[i]; i++) {
1198		da[i] = (descriptor_t *)(uintptr_t)descs[i];
1199	}
1200	*errp = 0;
1201	free(descs);
1202
1203	return (da);
1204#endif
1205}
1206
1207/*ARGSUSED*/
1208static dm_descriptor_t *
1209ptr_array_to_desc_array(descriptor_t **ptrs, int *errp)
1210{
1211#ifdef _LP64
1212	return ((dm_descriptor_t *)ptrs);
1213#else
1214	/* convert the 32 bit ptrs to the 64 bit descriptors */
1215	int	cnt;
1216	int	i;
1217	dm_descriptor_t *da;
1218
1219	if (*errp != 0 || ptrs == NULL) {
1220		return (NULL);
1221	}
1222
1223	for (cnt = 0; ptrs[cnt]; cnt++)
1224		;
1225
1226	da = (dm_descriptor_t *)calloc(cnt + 1, sizeof (dm_descriptor_t));
1227	if (da == NULL) {
1228		*errp = ENOMEM;
1229		return (NULL);
1230	}
1231
1232	for (i = 0; ptrs[i]; i++) {
1233		da[i] = (uintptr_t)ptrs[i];
1234	}
1235	*errp = 0;
1236	free(ptrs);
1237
1238	return (da);
1239#endif
1240}
1241/*
1242 * Build the usage string for the in use data. Return the build string in
1243 * the msg parameter. This function takes care of reallocing all the memory
1244 * for this usage string. Usage string is returned already formatted for
1245 * localization.
1246 */
1247static int
1248build_usage_string(char *dname, char *by, char *data, char **msg,
1249    int *found, int *errp)
1250{
1251	int	len0;
1252	int	len1;
1253	char	*use;
1254	char	*p;
1255
1256	*errp = 0;
1257
1258	dm_get_usage_string(by, data, &use);
1259	if (!use) {
1260		return (-1);
1261	}
1262
1263	if (*msg)
1264		len0 = strlen(*msg);
1265	else
1266		len0 = 0;
1267	/* LINTED */
1268	len1 = snprintf(NULL, 0, use, dname, data);
1269
1270	/*
1271	 * If multiple in use details they
1272	 * are listed 1 per line for ease of
1273	 * reading. dm_find_usage_string
1274	 * formats these appropriately.
1275	 */
1276	if ((p = realloc(*msg, len0 + len1 + 1)) == NULL) {
1277		*errp = errno;
1278		free(*msg);
1279		return (-1);
1280	}
1281	*msg = p;
1282
1283	/* LINTED */
1284	(void) snprintf(*msg + len0, len1 + 1, use, dname, data);
1285	(*found)++;
1286	return (0);
1287}
1288