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