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