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 
50 extern	char	*getfullblkname();
51 
52 extern dm_desc_type_t drive_assoc_types[];
53 extern dm_desc_type_t bus_assoc_types[];
54 extern dm_desc_type_t controller_assoc_types[];
55 extern dm_desc_type_t media_assoc_types[];
56 extern dm_desc_type_t slice_assoc_types[];
57 extern dm_desc_type_t partition_assoc_types[];
58 extern dm_desc_type_t path_assoc_types[];
59 extern dm_desc_type_t alias_assoc_types[];
60 
61 
62 static dm_descriptor_t *ptr_array_to_desc_array(descriptor_t **ptrs, int *errp);
63 static descriptor_t **desc_array_to_ptr_array(dm_descriptor_t *da, int *errp);
64 static int build_usage_string(char *dname, char *by, char *data, char **use,
65 	int *found, int *errp);
66 
67 void
dm_free_descriptor(dm_descriptor_t desc)68 dm_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 
82 void
dm_free_descriptors(dm_descriptor_t * desc_list)83 dm_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*/
103 void
dm_free_name(char * name)104 dm_free_name(char *name)
105 {
106 	free(name);
107 }
108 
109 dm_descriptor_t *
dm_get_associated_descriptors(dm_descriptor_t desc,dm_desc_type_t type,int * errp)110 dm_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 
169 dm_desc_type_t *
dm_get_associated_types(dm_desc_type_t type)170 dm_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 
194 nvlist_t *
dm_get_attributes(dm_descriptor_t desc,int * errp)195 dm_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 
253 dm_descriptor_t
dm_get_descriptor_by_name(dm_desc_type_t desc_type,char * name,int * errp)254 dm_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 
298 dm_descriptor_t *
dm_get_descriptors(dm_desc_type_t type,int filter[],int * errp)299 dm_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 
341 char *
dm_get_name(dm_descriptor_t desc,int * errp)342 dm_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 
406 nvlist_t *
dm_get_stats(dm_descriptor_t desc,int stat_type,int * errp)407 dm_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 
476 dm_desc_type_t
dm_get_type(dm_descriptor_t desc)477 dm_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  */
498 void
dm_get_slices(char * drive,dm_descriptor_t ** slices,int * errp)499 dm_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  */
537 void
dm_get_slice_stats(char * slice,nvlist_t ** dev_stats,int * errp)538 dm_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  */
565 int
dm_isoverlapping(char * slicename,char ** overlaps_with,int * errp)566 dm_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 
717 out:
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  */
737 int
dm_get_swapentries(swaptbl_t ** stp,int * errp)738 dm_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 */
791 void
dm_free_swapentries(swaptbl_t * stp)792 dm_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  */
803 int
dm_inuse_swap(const char * dev_name,int * errp)804 dm_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  */
845 int
dm_inuse(char * dev_name,char ** msg,dm_who_type_t who,int * errp)846 dm_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 	}
1047 out:
1048 	if (dname != NULL)
1049 		free(dname);
1050 	nvlist_free(dev_stats);
1051 
1052 	return (found);
1053 }
1054 
1055 void
dm_get_usage_string(char * what,char * how,char ** usage_string)1056 dm_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(8)."
1069 			    "\n");
1070 		} else {
1071 			*usage_string = dgettext(TEXT_DOMAIN,
1072 			    "%s is currently mounted on %s."
1073 			    " Please see umount(8).\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(8)."
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(8)."
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(8).\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(8)."
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(8).\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(8).\n");
1109 	}
1110 }
1111 void
libdiskmgt_add_str(nvlist_t * attrs,char * name,char * val,int * errp)1112 libdiskmgt_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 
1119 descriptor_t **
libdiskmgt_empty_desc_array(int * errp)1120 libdiskmgt_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 
1135 void
libdiskmgt_init_debug()1136 libdiskmgt_init_debug()
1137 {
1138 	char	*valp;
1139 
1140 	if ((valp = getenv(DM_DEBUG)) != NULL) {
1141 		dm_debug = atoi(valp);
1142 	}
1143 }
1144 
1145 int
libdiskmgt_str_eq(char * nm1,char * nm2)1146 libdiskmgt_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*/
1177 static descriptor_t **
desc_array_to_ptr_array(dm_descriptor_t * descs,int * errp)1178 desc_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*/
1208 static dm_descriptor_t *
ptr_array_to_desc_array(descriptor_t ** ptrs,int * errp)1209 ptr_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  */
1247 static int
build_usage_string(char * dname,char * by,char * data,char ** msg,int * found,int * errp)1248 build_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