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 *
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 *
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 **
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
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 **
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
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
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 **
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
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
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 *
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
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
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
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
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
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
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
760 dmgt_set_error_handler(void (*func)(const char *, va_list))
761 {
762 	error_func = func;
763 }
764