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 == NULL) {
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 = NULL;
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 != NULL) {
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 != NULL) {
555 *dev_stats = dm_get_stats(devp, DM_SLICE_STAT_USE,
556 errp);
557 dm_free_descriptor(devp);
558 }
559 }
560
561 /*
562 * Checks for overlapping slices. If the given device is a slice, and it
563 * overlaps with any non-backup slice on the disk, return true with a detailed
564 * description similar to dm_inuse().
565 */
566 int
dm_isoverlapping(char * slicename,char ** overlaps_with,int * errp)567 dm_isoverlapping(char *slicename, char **overlaps_with, int *errp)
568 {
569 dm_descriptor_t slice = NULL;
570 dm_descriptor_t *media = NULL;
571 dm_descriptor_t *slices = NULL;
572 int i = 0;
573 uint32_t in_snum;
574 uint64_t start_block = 0;
575 uint64_t end_block = 0;
576 uint64_t media_size = 0;
577 uint64_t size = 0;
578 nvlist_t *media_attrs = NULL;
579 nvlist_t *slice_attrs = NULL;
580 int ret = 0;
581
582 slice = dm_get_descriptor_by_name(DM_SLICE, slicename, errp);
583 if (slice == NULL)
584 goto out;
585
586 /*
587 * Get the list of slices be fetching the associated media, and then all
588 * associated slices.
589 */
590 media = dm_get_associated_descriptors(slice, DM_MEDIA, errp);
591 if (media == NULL || *media == NULL || *errp != 0)
592 goto out;
593
594 slices = dm_get_associated_descriptors(*media, DM_SLICE, errp);
595 if (slices == NULL || *slices == NULL || *errp != 0)
596 goto out;
597
598 media_attrs = dm_get_attributes(*media, errp);
599 if (media_attrs == NULL || *errp)
600 goto out;
601
602 *errp = nvlist_lookup_uint64(media_attrs, DM_NACCESSIBLE, &media_size);
603 if (*errp != 0)
604 goto out;
605
606 slice_attrs = dm_get_attributes(slice, errp);
607 if (slice_attrs == NULL || *errp != 0)
608 goto out;
609
610 *errp = nvlist_lookup_uint64(slice_attrs, DM_START, &start_block);
611 if (*errp != 0)
612 goto out;
613
614 *errp = nvlist_lookup_uint64(slice_attrs, DM_SIZE, &size);
615 if (*errp != 0)
616 goto out;
617
618 *errp = nvlist_lookup_uint32(slice_attrs, DM_INDEX, &in_snum);
619 if (*errp != 0)
620 goto out;
621
622 end_block = (start_block + size) - 1;
623
624 for (i = 0; slices[i]; i ++) {
625 uint64_t other_start;
626 uint64_t other_end;
627 uint64_t other_size;
628 uint32_t snum;
629
630 nvlist_t *other_attrs = dm_get_attributes(slices[i], errp);
631
632 if (other_attrs == NULL)
633 continue;
634
635 if (*errp != 0)
636 goto out;
637
638 *errp = nvlist_lookup_uint64(other_attrs, DM_START,
639 &other_start);
640 if (*errp) {
641 nvlist_free(other_attrs);
642 goto out;
643 }
644
645 *errp = nvlist_lookup_uint64(other_attrs, DM_SIZE,
646 &other_size);
647
648 if (*errp) {
649 nvlist_free(other_attrs);
650 ret = -1;
651 goto out;
652 }
653
654 other_end = (other_size + other_start) - 1;
655
656 *errp = nvlist_lookup_uint32(other_attrs, DM_INDEX,
657 &snum);
658
659 if (*errp) {
660 nvlist_free(other_attrs);
661 ret = -1;
662 goto out;
663 }
664
665 /*
666 * Check to see if there are > 2 overlapping regions
667 * on this media in the same region as this slice.
668 * This is done by assuming the following:
669 * Slice 2 is the backup slice if it is the size
670 * of the whole disk
671 * If slice 2 is the overlap and slice 2 is the size of
672 * the whole disk, continue. If another slice is found
673 * that overlaps with our slice, return it.
674 * There is the potential that there is more than one slice
675 * that our slice overlaps with, however, we only return
676 * the first overlapping slice we find.
677 *
678 */
679 if (start_block >= other_start && start_block <= other_end) {
680 if ((snum == 2 && (other_size == media_size)) ||
681 snum == in_snum) {
682 continue;
683 } else {
684 char *str = dm_get_name(slices[i], errp);
685 if (*errp != 0) {
686 nvlist_free(other_attrs);
687 ret = -1;
688 goto out;
689 }
690 *overlaps_with = strdup(str);
691 dm_free_name(str);
692 nvlist_free(other_attrs);
693 ret = 1;
694 goto out;
695 }
696 } else if (other_start >= start_block &&
697 other_start <= end_block) {
698 if ((snum == 2 && (other_size == media_size)) ||
699 snum == in_snum) {
700 continue;
701 } else {
702 char *str = dm_get_name(slices[i], errp);
703 if (*errp != 0) {
704 nvlist_free(other_attrs);
705 ret = -1;
706 goto out;
707 }
708 *overlaps_with = strdup(str);
709 dm_free_name(str);
710 nvlist_free(other_attrs);
711 ret = 1;
712 goto out;
713 }
714 }
715 nvlist_free(other_attrs);
716 }
717
718 out:
719 if (media_attrs)
720 nvlist_free(media_attrs);
721 if (slice_attrs)
722 nvlist_free(slice_attrs);
723
724 if (slices)
725 dm_free_descriptors(slices);
726 if (media)
727 dm_free_descriptors(media);
728 if (slice)
729 dm_free_descriptor(slice);
730
731 return (ret);
732 }
733
734 /*
735 * Get the full list of swap entries. Returns -1 on error, or >= 0 to
736 * indicate the number of entries in the list. Callers are responsible
737 * for calling dm_free_swapentries() to deallocate memory. If this
738 * returns 0, the swaptbl_t still needs to be freed.
739 */
740 int
dm_get_swapentries(swaptbl_t ** stp,int * errp)741 dm_get_swapentries(swaptbl_t **stp, int *errp)
742 {
743 int count, i;
744 swaptbl_t *tbl;
745 char *ptr;
746
747 *stp = NULL;
748
749 /* get number of swap entries */
750 if ((count = swapctl(SC_GETNSWP, NULL)) < 0) {
751 *errp = errno;
752 return (-1);
753 }
754
755 if (count == 0) {
756 return (0);
757 }
758
759 /* allocate space */
760 tbl = calloc(1, sizeof (int) + count * sizeof (swapent_t));
761 if (tbl == NULL) {
762 *errp = ENOMEM;
763 return (-1);
764 }
765
766 ptr = calloc(1, count * MAXPATHLEN);
767 if (ptr == NULL) {
768 *errp = ENOMEM;
769 free(tbl);
770 return (-1);
771 }
772
773 /* set up pointers to the pathnames */
774 tbl->swt_n = count;
775 for (i = 0; i < count; i++) {
776 tbl->swt_ent[i].ste_path = ptr;
777 ptr += MAXPATHLEN;
778 }
779
780 /* get list of swap paths */
781 count = swapctl(SC_LIST, tbl);
782 if (count < 0) {
783 *errp = errno;
784 free(ptr);
785 free(tbl);
786 return (-1);
787 }
788
789 *stp = tbl;
790 return (count);
791 }
792
793 /* ARGSUSED */
794 void
dm_free_swapentries(swaptbl_t * stp)795 dm_free_swapentries(swaptbl_t *stp)
796 {
797 ASSERT(stp != NULL);
798
799 free(stp->swt_ent[0].ste_path);
800 free(stp);
801 }
802
803 /*
804 * Check a slice to see if it's being used by swap.
805 */
806 int
dm_inuse_swap(const char * dev_name,int * errp)807 dm_inuse_swap(const char *dev_name, int *errp)
808 {
809 int count;
810 int found;
811 swaptbl_t *tbl = NULL;
812
813 *errp = 0;
814
815 count = dm_get_swapentries(&tbl, errp);
816 if (count < 0 || *errp) {
817 if (tbl)
818 dm_free_swapentries(tbl);
819 return (-1);
820 }
821
822 /* if there are no swap entries, we're done */
823 if (!count) {
824 return (0);
825 }
826
827 ASSERT(tbl != NULL);
828
829 found = 0;
830 while (count--) {
831 if (strcmp(dev_name, tbl->swt_ent[count].ste_path) == 0) {
832 found = 1;
833 break;
834 }
835 }
836
837 dm_free_swapentries(tbl);
838 return (found);
839 }
840
841 /*
842 * Returns 'in use' details, if found, about a specific dev_name,
843 * based on the caller(who). It is important to note that it is possible
844 * for there to be more than one 'in use' statistic regarding a dev_name.
845 * The **msg parameter returns a list of 'in use' details. This message
846 * is formatted via gettext().
847 */
848 int
dm_inuse(char * dev_name,char ** msg,dm_who_type_t who,int * errp)849 dm_inuse(char *dev_name, char **msg, dm_who_type_t who, int *errp)
850 {
851 nvlist_t *dev_stats = NULL;
852 char *by, *data;
853 nvpair_t *nvwhat = NULL;
854 nvpair_t *nvdesc = NULL;
855 int found = 0;
856 int err;
857 char *dname = NULL;
858
859 *errp = 0;
860 *msg = NULL;
861
862 /*
863 * If the user doesn't want to do in use checking, return.
864 */
865
866 if (NOINUSE_SET)
867 return (0);
868
869 dname = getfullblkname(dev_name);
870 /*
871 * If we cannot find the block name, we cannot check the device
872 * for in use statistics. So, return found, which is == 0.
873 */
874 if (dname == NULL || *dname == '\0') {
875 return (found);
876 }
877
878 /*
879 * Slice stats for swap devices are only returned if mounted
880 * (e.g. /tmp). Other devices or files being used for swap
881 * are ignored, so we add a special check here to use swapctl(2)
882 * to perform in-use checking.
883 */
884 if (ANY_ZPOOL_USE(who) && (err = dm_inuse_swap(dname, errp))) {
885
886 /* on error, dm_inuse_swap sets errp */
887 if (err < 0) {
888 free(dname);
889 return (err);
890 }
891
892 /* simulate a mounted swap device */
893 (void) build_usage_string(dname, DM_USE_MOUNT, "swap", msg,
894 &found, errp);
895
896 /* if this fails, dm_get_usage_string changed */
897 ASSERT(found == 1);
898
899 free(dname);
900 return (found);
901 }
902
903 dm_get_slice_stats(dname, &dev_stats, errp);
904 if (dev_stats == NULL) {
905 /*
906 * If there is an error, but it isn't a no device found error
907 * return the error as recorded. Otherwise, with a full
908 * block name, we might not be able to get the slice
909 * associated, and will get an ENODEV error. For example,
910 * an SVM metadevice will return a value from getfullblkname()
911 * but libdiskmgt won't be able to find this device for
912 * statistics gathering. This is expected and we should not
913 * report errnoneous errors.
914 */
915 if (*errp) {
916 if (*errp == ENODEV) {
917 *errp = 0;
918 }
919 }
920 free(dname);
921 return (found);
922 }
923
924 for (;;) {
925
926 nvwhat = nvlist_next_nvpair(dev_stats, nvdesc);
927 nvdesc = nvlist_next_nvpair(dev_stats, nvwhat);
928
929 /*
930 * End of the list found.
931 */
932 if (nvwhat == NULL || nvdesc == NULL) {
933 break;
934 }
935 /*
936 * Otherwise, we check to see if this client(who) cares
937 * about this in use scenario
938 */
939
940 ASSERT(strcmp(nvpair_name(nvwhat), DM_USED_BY) == 0);
941 ASSERT(strcmp(nvpair_name(nvdesc), DM_USED_NAME) == 0);
942 /*
943 * If we error getting the string value continue on
944 * to the next pair(if there is one)
945 */
946 if (nvpair_value_string(nvwhat, &by)) {
947 continue;
948 }
949 if (nvpair_value_string(nvdesc, &data)) {
950 continue;
951 }
952
953 switch (who) {
954 case DM_WHO_MKFS:
955 /*
956 * mkfs is not in use for these cases.
957 * All others are in use.
958 */
959 if (strcmp(by, DM_USE_LU) == 0 ||
960 strcmp(by, DM_USE_FS) == 0 ||
961 strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
962 break;
963 }
964 if (build_usage_string(dname,
965 by, data, msg, &found, errp) != 0) {
966 if (*errp) {
967 goto out;
968 }
969 }
970 break;
971 case DM_WHO_SWAP:
972 /*
973 * Not in use for this.
974 */
975 if (strcmp(by, DM_USE_DUMP) == 0 ||
976 strcmp(by, DM_USE_FS) == 0 ||
977 strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
978 break;
979 }
980 if (strcmp(by, DM_USE_LU) == 0 &&
981 strcmp(data, "-") == 0) {
982 break;
983 }
984 if (strcmp(by, DM_USE_VFSTAB) == 0 &&
985 strcmp(data, "") == 0) {
986 break;
987 }
988 if (build_usage_string(dname,
989 by, data, msg, &found, errp) != 0) {
990 if (*errp) {
991 goto out;
992 }
993 }
994 break;
995 case DM_WHO_DUMP:
996 /*
997 * Not in use for this.
998 */
999 if ((strcmp(by, DM_USE_MOUNT) == 0 &&
1000 strcmp(data, "swap") == 0) ||
1001 strcmp(by, DM_USE_DUMP) == 0 ||
1002 strcmp(by, DM_USE_FS) == 0 ||
1003 strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
1004 break;
1005 }
1006 if (build_usage_string(dname,
1007 by, data, msg, &found, errp)) {
1008 if (*errp) {
1009 goto out;
1010 }
1011 }
1012 break;
1013
1014 case DM_WHO_FORMAT:
1015 if (strcmp(by, DM_USE_FS) == 0 ||
1016 strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0)
1017 break;
1018 if (build_usage_string(dname,
1019 by, data, msg, &found, errp) != 0) {
1020 if (*errp) {
1021 goto out;
1022 }
1023 }
1024 break;
1025
1026 case DM_WHO_ZPOOL_FORCE:
1027 if (strcmp(by, DM_USE_FS) == 0 ||
1028 strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0)
1029 break;
1030 /* FALLTHROUGH */
1031 case DM_WHO_ZPOOL:
1032 if (build_usage_string(dname,
1033 by, data, msg, &found, errp) != 0) {
1034 if (*errp)
1035 goto out;
1036 }
1037 break;
1038
1039 case DM_WHO_ZPOOL_SPARE:
1040 if (strcmp(by, DM_USE_SPARE_ZPOOL) != 0) {
1041 if (build_usage_string(dname, by,
1042 data, msg, &found, errp) != 0) {
1043 if (*errp)
1044 goto out;
1045 }
1046 }
1047 break;
1048
1049 default:
1050 /*
1051 * nothing found in use for this client
1052 * of libdiskmgt. Default is 'not in use'.
1053 */
1054 break;
1055 }
1056 }
1057 out:
1058 if (dname != NULL)
1059 free(dname);
1060 if (dev_stats != NULL)
1061 nvlist_free(dev_stats);
1062
1063 return (found);
1064 }
1065
1066 void
dm_get_usage_string(char * what,char * how,char ** usage_string)1067 dm_get_usage_string(char *what, char *how, char **usage_string)
1068 {
1069
1070
1071 if (usage_string == NULL || what == NULL) {
1072 return;
1073 }
1074 *usage_string = NULL;
1075
1076 if (strcmp(what, DM_USE_MOUNT) == 0) {
1077 if (strcmp(how, "swap") == 0) {
1078 *usage_string = dgettext(TEXT_DOMAIN,
1079 "%s is currently used by swap. Please see swap(1M)."
1080 "\n");
1081 } else {
1082 *usage_string = dgettext(TEXT_DOMAIN,
1083 "%s is currently mounted on %s."
1084 " Please see umount(1M).\n");
1085 }
1086 } else if (strcmp(what, DM_USE_VFSTAB) == 0) {
1087 *usage_string = dgettext(TEXT_DOMAIN,
1088 "%s is normally mounted on %s according to /etc/vfstab. "
1089 "Please remove this entry to use this device.\n");
1090 } else if (strcmp(what, DM_USE_FS) == 0) {
1091 *usage_string = dgettext(TEXT_DOMAIN,
1092 "%s contains a %s filesystem.\n");
1093 } else if (strcmp(what, DM_USE_SVM) == 0) {
1094 if (strcmp(how, "mdb") == 0) {
1095 *usage_string = dgettext(TEXT_DOMAIN,
1096 "%s contains an SVM %s. Please see "
1097 "metadb(1M).\n");
1098 } else {
1099 *usage_string = dgettext(TEXT_DOMAIN,
1100 "%s is part of SVM volume %s. "
1101 "Please see metaclear(1M).\n");
1102 }
1103 } else if (strcmp(what, DM_USE_VXVM) == 0) {
1104 *usage_string = dgettext(TEXT_DOMAIN,
1105 "%s is part of VxVM volume %s.\n");
1106 } else if (strcmp(what, DM_USE_LU) == 0) {
1107 *usage_string = dgettext(TEXT_DOMAIN,
1108 "%s is in use for live upgrade %s. Please see ludelete(1M)."
1109 "\n");
1110 } else if (strcmp(what, DM_USE_DUMP) == 0) {
1111 *usage_string = dgettext(TEXT_DOMAIN,
1112 "%s is in use by %s. Please see dumpadm(1M)."
1113 "\n");
1114 } else if (strcmp(what, DM_USE_EXPORTED_ZPOOL) == 0) {
1115 *usage_string = dgettext(TEXT_DOMAIN,
1116 "%s is part of exported or potentially active ZFS pool %s. "
1117 "Please see zpool(1M).\n");
1118 } else if (strcmp(what, DM_USE_ACTIVE_ZPOOL) == 0) {
1119 *usage_string = dgettext(TEXT_DOMAIN,
1120 "%s is part of active ZFS pool %s. Please see zpool(1M)."
1121 "\n");
1122 } else if (strcmp(what, DM_USE_SPARE_ZPOOL) == 0) {
1123 *usage_string = dgettext(TEXT_DOMAIN,
1124 "%s is reserved as a hot spare for ZFS pool %s. Please "
1125 "see zpool(1M).\n");
1126 } else if (strcmp(what, DM_USE_L2CACHE_ZPOOL) == 0) {
1127 *usage_string = dgettext(TEXT_DOMAIN,
1128 "%s is in use as a cache device for ZFS pool %s. "
1129 "Please see zpool(1M).\n");
1130 }
1131 }
1132 void
libdiskmgt_add_str(nvlist_t * attrs,char * name,char * val,int * errp)1133 libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, int *errp)
1134 {
1135 if (*errp == 0) {
1136 *errp = nvlist_add_string(attrs, name, val);
1137 }
1138 }
1139
1140 descriptor_t **
libdiskmgt_empty_desc_array(int * errp)1141 libdiskmgt_empty_desc_array(int *errp)
1142 {
1143 descriptor_t **empty;
1144
1145 empty = (descriptor_t **)calloc(1, sizeof (descriptor_t *));
1146 if (empty == NULL) {
1147 *errp = ENOMEM;
1148 return (NULL);
1149 }
1150 empty[0] = NULL;
1151
1152 *errp = 0;
1153 return (empty);
1154 }
1155
1156 void
libdiskmgt_init_debug()1157 libdiskmgt_init_debug()
1158 {
1159 char *valp;
1160
1161 if ((valp = getenv(DM_DEBUG)) != NULL) {
1162 dm_debug = atoi(valp);
1163 }
1164 }
1165
1166 int
libdiskmgt_str_eq(char * nm1,char * nm2)1167 libdiskmgt_str_eq(char *nm1, char *nm2)
1168 {
1169 if (nm1 == NULL) {
1170 if (dm_debug) {
1171 (void) fprintf(stderr, "WARNING: str_eq nm1 NULL\n");
1172 }
1173
1174 if (nm2 == NULL) {
1175 return (1);
1176 } else {
1177 return (0);
1178 }
1179 }
1180
1181 /* nm1 != NULL */
1182
1183 if (nm2 == NULL) {
1184 if (dm_debug) {
1185 (void) fprintf(stderr, "WARNING: str_eq nm2 NULL\n");
1186 }
1187 return (0);
1188 }
1189
1190 if (strcmp(nm1, nm2) == 0) {
1191 return (1);
1192 }
1193
1194 return (0);
1195 }
1196
1197 /*ARGSUSED*/
1198 static descriptor_t **
desc_array_to_ptr_array(dm_descriptor_t * descs,int * errp)1199 desc_array_to_ptr_array(dm_descriptor_t *descs, int *errp)
1200 {
1201 #ifdef _LP64
1202 return ((descriptor_t **)descs);
1203 #else
1204 /* convert the 64 bit descriptors to 32 bit ptrs */
1205 int cnt;
1206 int i;
1207 descriptor_t **da;
1208
1209 for (cnt = 0; descs[cnt]; cnt++)
1210 ;
1211
1212 da = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
1213 if (da == NULL) {
1214 *errp = ENOMEM;
1215 return (NULL);
1216 }
1217
1218 for (i = 0; descs[i]; i++) {
1219 da[i] = (descriptor_t *)(uintptr_t)descs[i];
1220 }
1221 *errp = 0;
1222 free(descs);
1223
1224 return (da);
1225 #endif
1226 }
1227
1228 /*ARGSUSED*/
1229 static dm_descriptor_t *
ptr_array_to_desc_array(descriptor_t ** ptrs,int * errp)1230 ptr_array_to_desc_array(descriptor_t **ptrs, int *errp)
1231 {
1232 #ifdef _LP64
1233 return ((dm_descriptor_t *)ptrs);
1234 #else
1235 /* convert the 32 bit ptrs to the 64 bit descriptors */
1236 int cnt;
1237 int i;
1238 dm_descriptor_t *da;
1239
1240 if (*errp != 0 || ptrs == NULL) {
1241 return (NULL);
1242 }
1243
1244 for (cnt = 0; ptrs[cnt]; cnt++)
1245 ;
1246
1247 da = (dm_descriptor_t *)calloc(cnt + 1, sizeof (dm_descriptor_t));
1248 if (da == NULL) {
1249 *errp = ENOMEM;
1250 return (NULL);
1251 }
1252
1253 for (i = 0; ptrs[i]; i++) {
1254 da[i] = (uintptr_t)ptrs[i];
1255 }
1256 *errp = 0;
1257 free(ptrs);
1258
1259 return (da);
1260 #endif
1261 }
1262 /*
1263 * Build the usage string for the in use data. Return the build string in
1264 * the msg parameter. This function takes care of reallocing all the memory
1265 * for this usage string. Usage string is returned already formatted for
1266 * localization.
1267 */
1268 static int
build_usage_string(char * dname,char * by,char * data,char ** msg,int * found,int * errp)1269 build_usage_string(char *dname, char *by, char *data, char **msg,
1270 int *found, int *errp)
1271 {
1272 int len0;
1273 int len1;
1274 char *use;
1275 char *p;
1276
1277 *errp = 0;
1278
1279 dm_get_usage_string(by, data, &use);
1280 if (!use) {
1281 return (-1);
1282 }
1283
1284 if (*msg)
1285 len0 = strlen(*msg);
1286 else
1287 len0 = 0;
1288 /* LINTED */
1289 len1 = snprintf(NULL, 0, use, dname, data);
1290
1291 /*
1292 * If multiple in use details they
1293 * are listed 1 per line for ease of
1294 * reading. dm_find_usage_string
1295 * formats these appropriately.
1296 */
1297 if ((p = realloc(*msg, len0 + len1 + 1)) == NULL) {
1298 *errp = errno;
1299 free(*msg);
1300 return (-1);
1301 }
1302 *msg = p;
1303
1304 /* LINTED */
1305 (void) snprintf(*msg + len0, len1 + 1, use, dname, data);
1306 (*found)++;
1307 return (0);
1308 }
1309