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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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);
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 				sap = (dmgt_slice_t **)realloc(sap,
279 				    (nslices + 2) * sizeof (dmgt_slice_t *));
280 				if (sap == NULL) {
281 					handle_error("out of memory");
282 					*error = -1;
283 				} else {
284 
285 					/* NULL-terminated array */
286 					sap[nslices] = slice;
287 					sap[nslices + 1] = NULL;
288 
289 					nslices++;
290 				}
291 			}
292 		}
293 
294 		dm_free_descriptors(slices);
295 	}
296 
297 	if (*error) {
298 		/* Normalize error */
299 		*error = -1;
300 	}
301 
302 	if (*error && sap != NULL) {
303 		zjni_free_array((void **)sap, (zjni_free_f)dmgt_free_slice);
304 		sap = NULL;
305 	}
306 
307 	return (sap);
308 }
309 
310 static void
311 remove_slice_from_list(dmgt_slice_t **slices, int index)
312 {
313 	int i;
314 	for (i = index; slices[i] != NULL; i++) {
315 		slices[i] = slices[i + 1];
316 	}
317 }
318 
319 static int
320 slices_overlap(dmgt_slice_t *slice1, dmgt_slice_t *slice2)
321 {
322 
323 	uint64_t start1 = slice1->start;
324 	uint64_t end1 = start1 + slice1->size - 1;
325 	uint64_t start2 = slice2->start;
326 	uint64_t end2 = start2 + slice2->size - 1;
327 
328 	int overlap = (start2 <= end1 && start1 <= end2);
329 
330 #ifdef DEBUG
331 	if (overlap) {
332 		(void) fprintf(stderr, "can't use %s: overlaps with %s\n",
333 		    slice2->name, slice1->name);
334 		(void) fprintf(stderr, "  1: start: %llu - %llu\n",
335 		    (unsigned long long)start1, (unsigned long long)end1);
336 		(void) fprintf(stderr, "  2: start: %llu - %llu\n",
337 		    (unsigned long long)start2, (unsigned long long)end2);
338 	}
339 #endif
340 
341 	return (overlap);
342 }
343 
344 /*
345  * Gets the slices for the given disk.
346  *
347  * Results:
348  *
349  *  1. Success: error is set to 0 and slices are returned
350  *
351  *  2. Failure: error is set to -1 and NULL is returned
352  */
353 static dmgt_slice_t **
354 get_disk_usable_slices(dm_descriptor_t media, const char *name,
355     uint32_t blocksize, int *in_use, int *error)
356 {
357 	dmgt_slice_t **slices = get_disk_slices(media, name, blocksize, error);
358 
359 	*in_use = 0;
360 
361 	if (!*error && slices != NULL) {
362 		int i, nslices;
363 
364 		for (nslices = 0; slices[nslices] != NULL; nslices++);
365 
366 		/* Prune slices based on use */
367 		for (i = nslices - 1; i >= 0; i--) {
368 			dmgt_slice_t *slice = slices[i];
369 			if (slice == NULL) {
370 				continue;
371 			}
372 
373 			if (slice_in_use(slice)) {
374 				int j;
375 				remove_slice_from_list(slices, i);
376 
377 				*in_use = 1;
378 
379 				/*
380 				 * Remove any slice that overlaps with this
381 				 * in-use slice
382 				 */
383 				for (j = nslices - 1; j >= 0; j--) {
384 					if (slices[j] == NULL) {
385 						continue;
386 					}
387 					if (slices_overlap(slice, slices[j])) {
388 						remove_slice_from_list(slices,
389 						    j);
390 					}
391 				}
392 			} else {
393 				if (slice_too_small(slice)) {
394 					remove_slice_from_list(slices, i);
395 				}
396 			}
397 		}
398 	}
399 
400 	return (slices);
401 }
402 
403 static void
404 get_disk_size(dm_descriptor_t media, char *name, uint64_t *size,
405     uint32_t *blocksize, int *error)
406 {
407 	nvlist_t *attrs;
408 
409 	*size = 0;
410 	*error = 0;
411 
412 	attrs = dm_get_attributes(media, error);
413 
414 	if (*error) {
415 		handle_error("could not get media attributes from disk: %s",
416 		    name);
417 	} else {
418 		/* Try to get the number of accessible blocks */
419 		nvpair_t *match = zjni_nvlist_walk_nvpair(
420 		    attrs, DM_NACCESSIBLE, DATA_TYPE_UINT64, NULL);
421 		if (match == NULL || nvpair_value_uint64(match, size)) {
422 
423 			/* Disk is probably not labeled, get raw size instead */
424 			match = zjni_nvlist_walk_nvpair(
425 			    attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
426 			if (match == NULL || nvpair_value_uint64(match, size)) {
427 				handle_error("could not get size of disk: %s",
428 				    name);
429 				*error = 1;
430 			}
431 		}
432 
433 		if (*error == 0) {
434 			match = zjni_nvlist_walk_nvpair(
435 			    attrs, DM_BLOCKSIZE, DATA_TYPE_UINT32, NULL);
436 			if (match == NULL ||
437 			    nvpair_value_uint32(match, blocksize)) {
438 				handle_error("could not get "
439 				    "block size of disk: %s", name);
440 				*error = 1;
441 			} else {
442 				*size *= *blocksize;
443 			}
444 		}
445 
446 		nvlist_free(attrs);
447 	}
448 }
449 
450 static void
451 get_slice_use(dm_descriptor_t slice, char *name, char **used_name,
452     char **used_by, int *error)
453 {
454 	/* Get slice use statistics */
455 	nvlist_t *stats = dm_get_stats(slice, DM_SLICE_STAT_USE, error);
456 	if (*error != 0) {
457 		handle_error("could not get stats of slice %s", name);
458 	} else {
459 
460 		*used_name = NULL;
461 		*used_by = NULL;
462 
463 		if (stats != NULL) {
464 			char *tmp;
465 			nvpair_t *match;
466 
467 			/* Get the type of usage for this slice */
468 			match = zjni_nvlist_walk_nvpair(
469 			    stats, DM_USED_BY, DATA_TYPE_STRING, NULL);
470 
471 			if (match != NULL &&
472 			    nvpair_value_string(match, &tmp) == 0) {
473 
474 				*used_name = strdup(tmp);
475 				if (*used_name == NULL) {
476 					*error = -1;
477 					handle_error("out of memory");
478 				} else {
479 
480 					/* Get the object using this slice */
481 					match =
482 					    zjni_nvlist_walk_nvpair(stats,
483 					    DM_USED_NAME, DATA_TYPE_STRING,
484 					    NULL);
485 
486 					if (match != NULL &&
487 					    nvpair_value_string(match, &tmp) ==
488 					    0) {
489 						*used_by = strdup(tmp);
490 						if (*used_by == NULL) {
491 							*error = -1;
492 							handle_error(
493 							    "out of memory");
494 						}
495 					}
496 				}
497 			}
498 			nvlist_free(stats);
499 		}
500 	}
501 }
502 
503 static dmgt_slice_t *
504 get_slice(dm_descriptor_t slice, uint32_t blocksize, int *error)
505 {
506 	dmgt_slice_t *sp;
507 	*error = 0;
508 	sp = (dmgt_slice_t *)calloc(1, sizeof (dmgt_slice_t));
509 	if (sp == NULL) {
510 		*error = -1;
511 		handle_error("out of memory");
512 	} else {
513 
514 		/* Get name */
515 		sp->name = get_device_name(slice, error);
516 		if (!*error) {
517 
518 			nvlist_t *attrs = dm_get_attributes(slice, error);
519 			if (*error) {
520 				handle_error("could not get "
521 				    "attributes from slice: %s", sp->name);
522 			} else {
523 				/* Get the size in blocks */
524 				nvpair_t *match = zjni_nvlist_walk_nvpair(
525 				    attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
526 				uint64_t size_blocks;
527 
528 				sp->size = 0;
529 
530 				if (match == NULL ||
531 				    nvpair_value_uint64(match, &size_blocks)) {
532 					handle_error("could not get "
533 					    "size of slice: %s", sp->name);
534 					*error = 1;
535 				} else {
536 					uint64_t start_blocks;
537 
538 					/* Convert to bytes */
539 					sp->size = blocksize * size_blocks;
540 
541 					/* Get the starting block */
542 					match = zjni_nvlist_walk_nvpair(
543 					    attrs, DM_START, DATA_TYPE_UINT64,
544 					    NULL);
545 
546 					if (match == NULL ||
547 					    nvpair_value_uint64(match,
548 					    &start_blocks)) {
549 						handle_error(
550 						    "could not get "
551 						    "start block of slice: %s",
552 						    sp->name);
553 						*error = 1;
554 					} else {
555 						/* Convert to bytes */
556 						sp->start =
557 						    blocksize * start_blocks;
558 
559 						/* Set slice use */
560 						get_slice_use(slice, sp->name,
561 						    &(sp->used_name),
562 						    &(sp->used_by), error);
563 					}
564 				}
565 			}
566 		}
567 	}
568 
569 	if (*error && sp != NULL) {
570 		dmgt_free_slice(sp);
571 	}
572 
573 	return (sp);
574 }
575 
576 static void
577 handle_error(const char *format, ...)
578 {
579 	va_list ap;
580 	va_start(ap, format);
581 
582 	if (error_func != NULL) {
583 		error_func(format, ap);
584 	}
585 
586 	va_end(ap);
587 }
588 
589 /* Should go away once 6285992 is fixed */
590 static int
591 slice_too_small(dmgt_slice_t *slice)
592 {
593 	/* Check size */
594 	if (slice->size < SPA_MINDEVSIZE) {
595 #ifdef DEBUG
596 		(void) fprintf(stderr, "can't use %s: slice too small: %llu\n",
597 			slice->name, (unsigned long long)slice->size);
598 #endif
599 		return (1);
600 	}
601 
602 	return (0);
603 }
604 
605 /* Should go away once 6285992 is fixed */
606 static int
607 slice_in_use(dmgt_slice_t *slice)
608 {
609 	int in_use = 0;
610 
611 	/* Check use */
612 	if (slice->used_name != NULL) {
613 
614 		in_use = 1;
615 
616 		/* If the slice contains an unmounted file system... */
617 		if (strcmp(DM_USE_FS, slice->used_name) == 0) {
618 
619 			/* Allow only if file system is not ZFS */
620 			if (strcmp(slice->used_by, "zfs") != 0) {
621 				in_use = 0;
622 			}
623 		} else
624 
625 			/* Uses that don't preclude slice from use by ZFS */
626 			if (strcmp(DM_USE_SVM, slice->used_name) == 0 ||
627 			    strcmp(DM_USE_VXVM, slice->used_name) == 0 ||
628 			    strcmp(DM_USE_LU, slice->used_name) == 0) {
629 				in_use = 0;
630 			}
631 	}
632 
633 #ifdef DEBUG
634 	if (in_use) {
635 		(void) fprintf(stderr,
636 		    "can't use %s: used name: %s: used by: %s\n",
637 		    slice->name, slice->used_name, slice->used_by);
638 	}
639 #endif
640 
641 	return (in_use);
642 }
643 
644 /*
645  * Extern functions
646  */
647 
648 /*
649  * Iterates through each available disk on the system.  For each free
650  * dmgt_disk_t *, runs the given function with the dmgt_disk_t * as
651  * the first arg and the given void * as the second arg.
652  */
653 int
654 dmgt_avail_disk_iter(dmgt_disk_iter_f func, void *data)
655 {
656 	int error = 0;
657 	int filter[] = { DM_DT_FIXED, -1 };
658 
659 	/* Search for fixed disks */
660 	dm_descriptor_t *disks = dm_get_descriptors(DM_DRIVE, filter, &error);
661 
662 	if (error) {
663 		handle_error("unable to communicate with libdiskmgt");
664 	} else {
665 		int i;
666 
667 		/* For each disk... */
668 		for (i = 0; disks != NULL && error == 0 && disks[i] != NULL;
669 		    i++) {
670 			/* Is this disk online? */
671 			dm_descriptor_t disk = (dm_descriptor_t)disks[i];
672 			int online = get_disk_online(disk, &error);
673 			if (!error && online) {
674 				dmgt_disk_t *dp = get_disk(disk, &error);
675 				if (!error) {
676 					/*
677 					 * If this disk or any of its
678 					 * slices is usable...
679 					 */
680 					if (!dp->in_use ||
681 					    zjni_count_elements(
682 					    (void **)dp->slices) != 0) {
683 
684 						/* Run the given function */
685 						if (func(dp, data)) {
686 							error = -1;
687 						}
688 						dmgt_free_disk(dp);
689 #ifdef DEBUG
690 					} else {
691 						(void) fprintf(stderr, "disk "
692 						    "has no available slices: "
693 						    "%s\n", dp->name);
694 #endif
695 					}
696 
697 				}
698 			}
699 		}
700 		dm_free_descriptors(disks);
701 	}
702 	return (error);
703 }
704 
705 void
706 dmgt_free_disk(dmgt_disk_t *disk)
707 {
708 	if (disk != NULL) {
709 		free(disk->name);
710 		zjni_free_array((void **)disk->aliases, free);
711 		zjni_free_array((void **)disk->slices,
712 		    (zjni_free_f)dmgt_free_slice);
713 		free(disk);
714 	}
715 }
716 
717 void
718 dmgt_free_slice(dmgt_slice_t *slice)
719 {
720 	if (slice != NULL) {
721 		free(slice->name);
722 		free(slice->used_name);
723 		free(slice->used_by);
724 		free(slice);
725 	}
726 }
727 
728 /*
729  * For clients that need to capture error output.
730  */
731 void
732 dmgt_set_error_handler(void (*func)(const char *, va_list))
733 {
734 	error_func = func;
735 }
736