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