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 == NULL) { 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] != NULL; 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] != NULL; 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] != NULL; 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] != NULL; 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