xref: /illumos-gate/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 *
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 *
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 **
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
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 **
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
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
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 **
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
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
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 *
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
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
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
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
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
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
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
764 dmgt_set_error_handler(void (*func)(const char *, va_list))
765 {
766 	error_func = func;
767 }
768