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