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 /*
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include "libzfs_jni_diskmgt.h"
30 #include "libzfs_jni_util.h"
31 #include <strings.h>
32 #include <libzfs.h>
33 #include <sys/mnttab.h>
34
35 /*
36 * Function prototypes
37 */
38
39 static char *get_device_name(dm_descriptor_t device, int *error);
40 static dmgt_disk_t *get_disk(dm_descriptor_t disk, int *error);
41 static char **get_disk_aliases(dm_descriptor_t disk, char *name, int *error);
42 static int get_disk_online(dm_descriptor_t disk, int *error);
43 static void remove_slice_from_list(dmgt_slice_t **slices, int index);
44 static dmgt_slice_t **get_disk_slices(dm_descriptor_t media,
45 const char *name, uint32_t blocksize, int *error);
46 static dmgt_slice_t **get_disk_usable_slices(dm_descriptor_t media,
47 const char *name, uint32_t blocksize, int *in_use, int *error);
48 static void get_disk_size(dm_descriptor_t media, char *name,
49 uint64_t *size, uint32_t *blocksize, int *error);
50 static void get_slice_use(dm_descriptor_t slice, char *name,
51 char **used_name, char **used_by, int *error);
52 static dmgt_slice_t *get_slice(
53 dm_descriptor_t slice, uint32_t blocksize, int *error);
54 static void handle_error(const char *format, ...);
55 static int slice_in_use(dmgt_slice_t *slice, int *error);
56 static int slice_too_small(dmgt_slice_t *slice);
57
58 /*
59 * Static data
60 */
61
62 static void (*error_func)(const char *, va_list);
63
64 /*
65 * Static functions
66 */
67
68 static char *
get_device_name(dm_descriptor_t device,int * error)69 get_device_name(dm_descriptor_t device, int *error)
70 {
71 char *dup;
72 char *name;
73
74 *error = 0;
75 name = dm_get_name(device, error);
76 if (*error) {
77 handle_error("could not determine name of device");
78 } else {
79 dup = strdup(name);
80 if (dup == NULL) {
81 handle_error("out of memory");
82 *error = -1;
83 }
84
85 dm_free_name(name);
86 }
87
88 return (dup);
89 }
90
91 /*
92 * Gets a dmgt_disk_t for the given disk dm_descriptor_t.
93 *
94 * Results:
95 *
96 * 1. Success: error is set to 0 and a dmgt_disk_t is returned
97 *
98 * 2. Failure: error is set to -1 and NULL is returned
99 */
100 static dmgt_disk_t *
get_disk(dm_descriptor_t disk,int * error)101 get_disk(dm_descriptor_t disk, int *error)
102 {
103 dmgt_disk_t *dp;
104 *error = 0;
105
106 dp = (dmgt_disk_t *)calloc(1, sizeof (dmgt_disk_t));
107 if (dp == NULL) {
108 handle_error("out of memory");
109 *error = -1;
110 } else {
111
112 /* Get name */
113 dp->name = get_device_name(disk, error);
114 if (!*error) {
115
116 /* Get aliases */
117 dp->aliases = get_disk_aliases(disk, dp->name, error);
118 if (!*error) {
119
120 /* Get media */
121 dm_descriptor_t *media =
122 dm_get_associated_descriptors(disk,
123 DM_MEDIA, error);
124 if (*error != 0 || media == NULL ||
125 *media == NULL) {
126 handle_error(
127 "could not get media from disk %s",
128 dp->name);
129 *error = -1;
130 } else {
131 /* Get size */
132 get_disk_size(media[0], dp->name,
133 &(dp->size), &(dp->blocksize),
134 error);
135 if (!*error) {
136 /* Get free slices */
137 dp->slices =
138 get_disk_usable_slices(
139 media[0], dp->name,
140 dp->blocksize,
141 &(dp->in_use), error);
142 }
143 dm_free_descriptors(media);
144 }
145 }
146 }
147 }
148
149 if (*error) {
150 /* Normalize error */
151 *error = -1;
152
153 if (dp != NULL) {
154 dmgt_free_disk(dp);
155 dp = NULL;
156 }
157 }
158
159 return (dp);
160 }
161
162 static char **
get_disk_aliases(dm_descriptor_t disk,char * name,int * error)163 get_disk_aliases(dm_descriptor_t disk, char *name, int *error)
164 {
165 char **names = NULL;
166 dm_descriptor_t *aliases;
167
168 *error = 0;
169 aliases = dm_get_associated_descriptors(disk, DM_ALIAS, error);
170 if (*error || aliases == NULL) {
171 *error = -1;
172 handle_error("could not get aliases for disk %s", name);
173 } else {
174
175 int j;
176
177 /* Count aliases */
178 for (j = 0; aliases[j] != NULL; j++);
179
180 names = (char **)calloc(j + 1, sizeof (char *));
181 if (names == NULL) {
182 *error = -1;
183 handle_error("out of memory");
184 } else {
185
186 /* For each alias... */
187 for (j = 0; *error == 0 && aliases[j] != NULL; j++) {
188
189 dm_descriptor_t alias = aliases[j];
190 char *aname = dm_get_name(alias, error);
191 if (*error) {
192 handle_error("could not get alias %d "
193 "for disk %s", (j + 1), name);
194 } else {
195 names[j] = strdup(aname);
196 if (names[j] == NULL) {
197 *error = -1;
198 handle_error("out of memory");
199 }
200
201 dm_free_name(aname);
202 }
203 }
204 }
205
206 dm_free_descriptors(aliases);
207 }
208
209 if (*error && names != NULL) {
210 /* Free previously-allocated names */
211 zjni_free_array((void **)names, free);
212 }
213
214 return (names);
215 }
216
217 static int
get_disk_online(dm_descriptor_t disk,int * error)218 get_disk_online(dm_descriptor_t disk, int *error)
219 {
220 uint32_t status = 0;
221
222 nvlist_t *attrs;
223 *error = 0;
224 attrs = dm_get_attributes(disk, error);
225 if (*error) {
226 handle_error("could not get disk attributes for disk");
227 } else {
228
229 /* Try to get the status */
230 nvpair_t *match = zjni_nvlist_walk_nvpair(
231 attrs, DM_STATUS, DATA_TYPE_UINT32, NULL);
232
233 if (match == NULL || nvpair_value_uint32(match, &status)) {
234
235 handle_error("could not get status of disk");
236 *error = 1;
237 }
238
239 nvlist_free(attrs);
240 }
241
242 return (status != 0);
243 }
244
245 /*
246 * Gets the slices for the given disk.
247 *
248 * Results:
249 *
250 * 1. Success: error is set to 0 and slices are returned
251 *
252 * 2. Failure: error is set to -1 and NULL is returned
253 */
254 static dmgt_slice_t **
get_disk_slices(dm_descriptor_t media,const char * name,uint32_t blocksize,int * error)255 get_disk_slices(dm_descriptor_t media, const char *name, uint32_t blocksize,
256 int *error)
257 {
258 dm_descriptor_t *slices;
259 dmgt_slice_t **sap = NULL;
260
261 *error = 0;
262 slices = dm_get_associated_descriptors(media, DM_SLICE, error);
263 if (*error != 0) {
264 handle_error("could not get slices of disk %s", name);
265 } else {
266 int j;
267 int nslices = 0;
268
269 /* For each slice... */
270 for (j = 0; *error == 0 &&
271 slices != NULL && slices[j] != NULL; j++) {
272
273 /* Get slice */
274 dmgt_slice_t *slice =
275 get_slice(slices[j], blocksize, error);
276 if (!*error) {
277
278 dmgt_slice_t **mem =
279 (dmgt_slice_t **)realloc(sap,
280 (nslices + 2) * sizeof (dmgt_slice_t *));
281
282 if (mem == NULL) {
283 handle_error("out of memory");
284 *error = -1;
285 } else {
286
287 sap = mem;
288
289 /* NULL-terminated array */
290 sap[nslices] = slice;
291 sap[nslices + 1] = NULL;
292
293 nslices++;
294 }
295 }
296 }
297
298 dm_free_descriptors(slices);
299 }
300
301 if (*error) {
302 /* Normalize error */
303 *error = -1;
304
305 if (sap != NULL) {
306 zjni_free_array((void **)sap,
307 (zjni_free_f)dmgt_free_slice);
308 sap = NULL;
309 }
310 }
311
312 return (sap);
313 }
314
315 static void
remove_slice_from_list(dmgt_slice_t ** slices,int index)316 remove_slice_from_list(dmgt_slice_t **slices, int index)
317 {
318 int i;
319 for (i = index; slices[i] != NULL; i++) {
320 slices[i] = slices[i + 1];
321 }
322 }
323
324 static int
slices_overlap(dmgt_slice_t * slice1,dmgt_slice_t * slice2)325 slices_overlap(dmgt_slice_t *slice1, dmgt_slice_t *slice2)
326 {
327
328 uint64_t start1 = slice1->start;
329 uint64_t end1 = start1 + slice1->size - 1;
330 uint64_t start2 = slice2->start;
331 uint64_t end2 = start2 + slice2->size - 1;
332
333 int overlap = (start2 <= end1 && start1 <= end2);
334
335 #ifdef DEBUG
336 if (overlap) {
337 (void) fprintf(stderr, "can't use %s: overlaps with %s\n",
338 slice2->name, slice1->name);
339 (void) fprintf(stderr, " 1: start: %llu - %llu\n",
340 (unsigned long long)start1, (unsigned long long)end1);
341 (void) fprintf(stderr, " 2: start: %llu - %llu\n",
342 (unsigned long long)start2, (unsigned long long)end2);
343 }
344 #endif
345
346 return (overlap);
347 }
348
349 /*
350 * Gets the slices for the given disk.
351 *
352 * Results:
353 *
354 * 1. Success: error is set to 0 and slices are returned
355 *
356 * 2. Failure: error is set to -1 and NULL is returned
357 */
358 static dmgt_slice_t **
get_disk_usable_slices(dm_descriptor_t media,const char * name,uint32_t blocksize,int * in_use,int * error)359 get_disk_usable_slices(dm_descriptor_t media, const char *name,
360 uint32_t blocksize, int *in_use, int *error)
361 {
362 dmgt_slice_t **slices = get_disk_slices(media, name, blocksize, error);
363 if (*error) {
364 slices = NULL;
365 }
366
367 *in_use = 0;
368
369 if (slices != NULL) {
370 int i, nslices;
371
372 for (nslices = 0; slices[nslices] != NULL; nslices++);
373
374 /* Prune slices based on use */
375 for (i = nslices - 1; i >= 0; i--) {
376 dmgt_slice_t *slice = slices[i];
377 int s_in_use;
378
379 /*
380 * Slice at this index could be NULL if
381 * removed in earlier iteration
382 */
383 if (slice == NULL) {
384 continue;
385 }
386
387 s_in_use = slice_in_use(slice, error);
388 if (*error) {
389 break;
390 }
391
392 if (s_in_use) {
393 int j;
394 remove_slice_from_list(slices, i);
395
396 /* Disk is in use */
397 *in_use = 1;
398
399 /*
400 * Remove any slice that overlaps with this
401 * in-use slice
402 */
403 for (j = nslices - 1; j >= 0; j--) {
404 dmgt_slice_t *slice2 = slices[j];
405
406 if (slice2 != NULL &&
407 slices_overlap(slice, slice2)) {
408 remove_slice_from_list(slices,
409 j);
410 dmgt_free_slice(slice2);
411 }
412 }
413
414 dmgt_free_slice(slice);
415 } else if (slice_too_small(slice)) {
416 remove_slice_from_list(slices, i);
417 dmgt_free_slice(slice);
418 }
419 }
420 }
421
422 if (*error) {
423 /* Normalize error */
424 *error = -1;
425
426 if (slices != NULL) {
427 zjni_free_array((void **)slices,
428 (zjni_free_f)dmgt_free_slice);
429 slices = NULL;
430 }
431 }
432
433 return (slices);
434 }
435
436 static void
get_disk_size(dm_descriptor_t media,char * name,uint64_t * size,uint32_t * blocksize,int * error)437 get_disk_size(dm_descriptor_t media, char *name, uint64_t *size,
438 uint32_t *blocksize, int *error)
439 {
440 nvlist_t *attrs;
441
442 *size = 0;
443 *error = 0;
444
445 attrs = dm_get_attributes(media, error);
446
447 if (*error) {
448 handle_error("could not get media attributes from disk: %s",
449 name);
450 } else {
451 /* Try to get the number of accessible blocks */
452 nvpair_t *match = zjni_nvlist_walk_nvpair(
453 attrs, DM_NACCESSIBLE, DATA_TYPE_UINT64, NULL);
454 if (match == NULL || nvpair_value_uint64(match, size)) {
455
456 /* Disk is probably not labeled, get raw size instead */
457 match = zjni_nvlist_walk_nvpair(
458 attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
459 if (match == NULL || nvpair_value_uint64(match, size)) {
460 handle_error("could not get size of disk: %s",
461 name);
462 *error = 1;
463 }
464 }
465
466 if (*error == 0) {
467 match = zjni_nvlist_walk_nvpair(
468 attrs, DM_BLOCKSIZE, DATA_TYPE_UINT32, NULL);
469 if (match == NULL ||
470 nvpair_value_uint32(match, blocksize)) {
471 handle_error("could not get "
472 "block size of disk: %s", name);
473 *error = 1;
474 } else {
475 *size *= *blocksize;
476 }
477 }
478
479 nvlist_free(attrs);
480 }
481 }
482
483 static void
get_slice_use(dm_descriptor_t slice,char * name,char ** used_name,char ** used_by,int * error)484 get_slice_use(dm_descriptor_t slice, char *name, char **used_name,
485 char **used_by, int *error)
486 {
487 /* Get slice use statistics */
488 nvlist_t *stats = dm_get_stats(slice, DM_SLICE_STAT_USE, error);
489 if (*error != 0) {
490 handle_error("could not get stats of slice %s", name);
491 } else {
492
493 *used_name = NULL;
494 *used_by = NULL;
495
496 if (stats != NULL) {
497 char *tmp;
498 nvpair_t *match;
499
500 /* Get the type of usage for this slice */
501 match = zjni_nvlist_walk_nvpair(
502 stats, DM_USED_BY, DATA_TYPE_STRING, NULL);
503
504 if (match != NULL &&
505 nvpair_value_string(match, &tmp) == 0) {
506
507 *used_name = strdup(tmp);
508 if (*used_name == NULL) {
509 *error = -1;
510 handle_error("out of memory");
511 } else {
512
513 /* Get the object using this slice */
514 match =
515 zjni_nvlist_walk_nvpair(stats,
516 DM_USED_NAME, DATA_TYPE_STRING,
517 NULL);
518
519 if (match != NULL &&
520 nvpair_value_string(match, &tmp) ==
521 0) {
522 *used_by = strdup(tmp);
523 if (*used_by == NULL) {
524 *error = -1;
525 handle_error(
526 "out of memory");
527 }
528 }
529 }
530 }
531 nvlist_free(stats);
532 }
533 }
534 }
535
536 static dmgt_slice_t *
get_slice(dm_descriptor_t slice,uint32_t blocksize,int * error)537 get_slice(dm_descriptor_t slice, uint32_t blocksize, int *error)
538 {
539 dmgt_slice_t *sp;
540 *error = 0;
541 sp = (dmgt_slice_t *)calloc(1, sizeof (dmgt_slice_t));
542 if (sp == NULL) {
543 *error = -1;
544 handle_error("out of memory");
545 } else {
546
547 /* Get name */
548 sp->name = get_device_name(slice, error);
549 if (!*error) {
550
551 nvlist_t *attrs = dm_get_attributes(slice, error);
552 if (*error) {
553 handle_error("could not get "
554 "attributes from slice: %s", sp->name);
555 } else {
556 /* Get the size in blocks */
557 nvpair_t *match = zjni_nvlist_walk_nvpair(
558 attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
559 uint64_t size_blocks;
560
561 sp->size = 0;
562
563 if (match == NULL ||
564 nvpair_value_uint64(match, &size_blocks)) {
565 handle_error("could not get "
566 "size of slice: %s", sp->name);
567 *error = 1;
568 } else {
569 uint64_t start_blocks;
570
571 /* Convert to bytes */
572 sp->size = blocksize * size_blocks;
573
574 /* Get the starting block */
575 match = zjni_nvlist_walk_nvpair(
576 attrs, DM_START, DATA_TYPE_UINT64,
577 NULL);
578
579 if (match == NULL ||
580 nvpair_value_uint64(match,
581 &start_blocks)) {
582 handle_error(
583 "could not get "
584 "start block of slice: %s",
585 sp->name);
586 *error = 1;
587 } else {
588 /* Convert to bytes */
589 sp->start =
590 blocksize * start_blocks;
591
592 /* Set slice use */
593 get_slice_use(slice, sp->name,
594 &(sp->used_name),
595 &(sp->used_by), error);
596 }
597 }
598 }
599 }
600 }
601
602 if (*error && sp != NULL) {
603 dmgt_free_slice(sp);
604 }
605
606 return (sp);
607 }
608
609 static void
handle_error(const char * format,...)610 handle_error(const char *format, ...)
611 {
612 va_list ap;
613 va_start(ap, format);
614
615 if (error_func != NULL) {
616 error_func(format, ap);
617 }
618
619 va_end(ap);
620 }
621
622 /* Should go away once 6285992 is fixed */
623 static int
slice_too_small(dmgt_slice_t * slice)624 slice_too_small(dmgt_slice_t *slice)
625 {
626 /* Check size */
627 if (slice->size < SPA_MINDEVSIZE) {
628 #ifdef DEBUG
629 (void) fprintf(stderr, "can't use %s: slice too small: %llu\n",
630 slice->name, (unsigned long long)slice->size);
631 #endif
632 return (1);
633 }
634
635 return (0);
636 }
637
638 static int
slice_in_use(dmgt_slice_t * slice,int * error)639 slice_in_use(dmgt_slice_t *slice, int *error)
640 {
641 char *msg = NULL;
642 int in_use;
643
644 /* Determine whether this slice could be passed to "zpool -f" */
645 in_use = dm_inuse(slice->name, &msg, DM_WHO_ZPOOL_FORCE, error);
646 if (*error) {
647 handle_error("%s: could not determine usage", slice->name);
648 }
649
650 #ifdef DEBUG
651 if (in_use) {
652 (void) fprintf(stderr,
653 "can't use %s: used name: %s: used by: %s\n message: %s\n",
654 slice->name, slice->used_name, slice->used_by, msg);
655 }
656 #endif
657
658 if (msg != NULL) {
659 free(msg);
660 }
661
662 return (in_use);
663 }
664
665 /*
666 * Extern functions
667 */
668
669 /*
670 * Iterates through each available disk on the system. For each free
671 * dmgt_disk_t *, runs the given function with the dmgt_disk_t * as
672 * the first arg and the given void * as the second arg.
673 */
674 int
dmgt_avail_disk_iter(dmgt_disk_iter_f func,void * data)675 dmgt_avail_disk_iter(dmgt_disk_iter_f func, void *data)
676 {
677 int error = 0;
678 int filter[] = { DM_DT_FIXED, -1 };
679
680 /* Search for fixed disks */
681 dm_descriptor_t *disks = dm_get_descriptors(DM_DRIVE, filter, &error);
682
683 if (error) {
684 handle_error("unable to communicate with libdiskmgt");
685 } else {
686 int i;
687
688 /* For each disk... */
689 for (i = 0; disks != NULL && disks[i] != NULL; i++) {
690 dm_descriptor_t disk = (dm_descriptor_t)disks[i];
691 int online;
692
693 /* Reset error flag for each disk */
694 error = 0;
695
696 /* Is this disk online? */
697 online = get_disk_online(disk, &error);
698 if (!error && online) {
699
700 /* Get a dmgt_disk_t for this dm_descriptor_t */
701 dmgt_disk_t *dp = get_disk(disk, &error);
702 if (!error) {
703
704 /*
705 * If this disk or any of its
706 * slices is usable...
707 */
708 if (!dp->in_use ||
709 zjni_count_elements(
710 (void **)dp->slices) != 0) {
711
712 /* Run the given function */
713 if (func(dp, data)) {
714 error = -1;
715 }
716 dmgt_free_disk(dp);
717 #ifdef DEBUG
718 } else {
719 (void) fprintf(stderr, "disk "
720 "has no available slices: "
721 "%s\n", dp->name);
722 #endif
723 }
724
725 }
726 }
727 }
728 dm_free_descriptors(disks);
729 }
730 return (error);
731 }
732
733 void
dmgt_free_disk(dmgt_disk_t * disk)734 dmgt_free_disk(dmgt_disk_t *disk)
735 {
736 if (disk != NULL) {
737 free(disk->name);
738 zjni_free_array((void **)disk->aliases, free);
739 zjni_free_array((void **)disk->slices,
740 (zjni_free_f)dmgt_free_slice);
741 free(disk);
742 }
743 }
744
745 void
dmgt_free_slice(dmgt_slice_t * slice)746 dmgt_free_slice(dmgt_slice_t *slice)
747 {
748 if (slice != NULL) {
749 free(slice->name);
750 free(slice->used_name);
751 free(slice->used_by);
752 free(slice);
753 }
754 }
755
756 /*
757 * For clients that need to capture error output.
758 */
759 void
dmgt_set_error_handler(void (* func)(const char *,va_list))760 dmgt_set_error_handler(void (*func)(const char *, va_list))
761 {
762 error_func = func;
763 }
764