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