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