Home | History | Annotate | Download | only in common
      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