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 * 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); 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 sap = (dmgt_slice_t **)realloc(sap, 279 (nslices + 2) * sizeof (dmgt_slice_t *)); 280 if (sap == NULL) { 281 handle_error("out of memory"); 282 *error = -1; 283 } else { 284 285 /* NULL-terminated array */ 286 sap[nslices] = slice; 287 sap[nslices + 1] = NULL; 288 289 nslices++; 290 } 291 } 292 } 293 294 dm_free_descriptors(slices); 295 } 296 297 if (*error) { 298 /* Normalize error */ 299 *error = -1; 300 } 301 302 if (*error && sap != NULL) { 303 zjni_free_array((void **)sap, (zjni_free_f)dmgt_free_slice); 304 sap = NULL; 305 } 306 307 return (sap); 308 } 309 310 static void 311 remove_slice_from_list(dmgt_slice_t **slices, int index) 312 { 313 int i; 314 for (i = index; slices[i] != NULL; i++) { 315 slices[i] = slices[i + 1]; 316 } 317 } 318 319 static int 320 slices_overlap(dmgt_slice_t *slice1, dmgt_slice_t *slice2) 321 { 322 323 uint64_t start1 = slice1->start; 324 uint64_t end1 = start1 + slice1->size - 1; 325 uint64_t start2 = slice2->start; 326 uint64_t end2 = start2 + slice2->size - 1; 327 328 int overlap = (start2 <= end1 && start1 <= end2); 329 330 #ifdef DEBUG 331 if (overlap) { 332 (void) fprintf(stderr, "can't use %s: overlaps with %s\n", 333 slice2->name, slice1->name); 334 (void) fprintf(stderr, " 1: start: %llu - %llu\n", 335 (unsigned long long)start1, (unsigned long long)end1); 336 (void) fprintf(stderr, " 2: start: %llu - %llu\n", 337 (unsigned long long)start2, (unsigned long long)end2); 338 } 339 #endif 340 341 return (overlap); 342 } 343 344 /* 345 * Gets the slices for the given disk. 346 * 347 * Results: 348 * 349 * 1. Success: error is set to 0 and slices are returned 350 * 351 * 2. Failure: error is set to -1 and NULL is returned 352 */ 353 static dmgt_slice_t ** 354 get_disk_usable_slices(dm_descriptor_t media, const char *name, 355 uint32_t blocksize, int *in_use, int *error) 356 { 357 dmgt_slice_t **slices = get_disk_slices(media, name, blocksize, error); 358 359 *in_use = 0; 360 361 if (!*error && slices != NULL) { 362 int i, nslices; 363 364 for (nslices = 0; slices[nslices] != NULL; nslices++); 365 366 /* Prune slices based on use */ 367 for (i = nslices - 1; i >= 0; i--) { 368 dmgt_slice_t *slice = slices[i]; 369 if (slice == NULL) { 370 continue; 371 } 372 373 if (slice_in_use(slice)) { 374 int j; 375 remove_slice_from_list(slices, i); 376 377 *in_use = 1; 378 379 /* 380 * Remove any slice that overlaps with this 381 * in-use slice 382 */ 383 for (j = nslices - 1; j >= 0; j--) { 384 if (slices[j] == NULL) { 385 continue; 386 } 387 if (slices_overlap(slice, slices[j])) { 388 remove_slice_from_list(slices, 389 j); 390 } 391 } 392 } else { 393 if (slice_too_small(slice)) { 394 remove_slice_from_list(slices, i); 395 } 396 } 397 } 398 } 399 400 return (slices); 401 } 402 403 static void 404 get_disk_size(dm_descriptor_t media, char *name, uint64_t *size, 405 uint32_t *blocksize, int *error) 406 { 407 nvlist_t *attrs; 408 409 *size = 0; 410 *error = 0; 411 412 attrs = dm_get_attributes(media, error); 413 414 if (*error) { 415 handle_error("could not get media attributes from disk: %s", 416 name); 417 } else { 418 /* Try to get the number of accessible blocks */ 419 nvpair_t *match = zjni_nvlist_walk_nvpair( 420 attrs, DM_NACCESSIBLE, DATA_TYPE_UINT64, NULL); 421 if (match == NULL || nvpair_value_uint64(match, size)) { 422 423 /* Disk is probably not labeled, get raw size instead */ 424 match = zjni_nvlist_walk_nvpair( 425 attrs, DM_SIZE, DATA_TYPE_UINT64, NULL); 426 if (match == NULL || nvpair_value_uint64(match, size)) { 427 handle_error("could not get size of disk: %s", 428 name); 429 *error = 1; 430 } 431 } 432 433 if (*error == 0) { 434 match = zjni_nvlist_walk_nvpair( 435 attrs, DM_BLOCKSIZE, DATA_TYPE_UINT32, NULL); 436 if (match == NULL || 437 nvpair_value_uint32(match, blocksize)) { 438 handle_error("could not get " 439 "block size of disk: %s", name); 440 *error = 1; 441 } else { 442 *size *= *blocksize; 443 } 444 } 445 446 nvlist_free(attrs); 447 } 448 } 449 450 static void 451 get_slice_use(dm_descriptor_t slice, char *name, char **used_name, 452 char **used_by, int *error) 453 { 454 /* Get slice use statistics */ 455 nvlist_t *stats = dm_get_stats(slice, DM_SLICE_STAT_USE, error); 456 if (*error != 0) { 457 handle_error("could not get stats of slice %s", name); 458 } else { 459 460 *used_name = NULL; 461 *used_by = NULL; 462 463 if (stats != NULL) { 464 char *tmp; 465 nvpair_t *match; 466 467 /* Get the type of usage for this slice */ 468 match = zjni_nvlist_walk_nvpair( 469 stats, DM_USED_BY, DATA_TYPE_STRING, NULL); 470 471 if (match != NULL && 472 nvpair_value_string(match, &tmp) == 0) { 473 474 *used_name = strdup(tmp); 475 if (*used_name == NULL) { 476 *error = -1; 477 handle_error("out of memory"); 478 } else { 479 480 /* Get the object using this slice */ 481 match = 482 zjni_nvlist_walk_nvpair(stats, 483 DM_USED_NAME, DATA_TYPE_STRING, 484 NULL); 485 486 if (match != NULL && 487 nvpair_value_string(match, &tmp) == 488 0) { 489 *used_by = strdup(tmp); 490 if (*used_by == NULL) { 491 *error = -1; 492 handle_error( 493 "out of memory"); 494 } 495 } 496 } 497 } 498 nvlist_free(stats); 499 } 500 } 501 } 502 503 static dmgt_slice_t * 504 get_slice(dm_descriptor_t slice, uint32_t blocksize, int *error) 505 { 506 dmgt_slice_t *sp; 507 *error = 0; 508 sp = (dmgt_slice_t *)calloc(1, sizeof (dmgt_slice_t)); 509 if (sp == NULL) { 510 *error = -1; 511 handle_error("out of memory"); 512 } else { 513 514 /* Get name */ 515 sp->name = get_device_name(slice, error); 516 if (!*error) { 517 518 nvlist_t *attrs = dm_get_attributes(slice, error); 519 if (*error) { 520 handle_error("could not get " 521 "attributes from slice: %s", sp->name); 522 } else { 523 /* Get the size in blocks */ 524 nvpair_t *match = zjni_nvlist_walk_nvpair( 525 attrs, DM_SIZE, DATA_TYPE_UINT64, NULL); 526 uint64_t size_blocks; 527 528 sp->size = 0; 529 530 if (match == NULL || 531 nvpair_value_uint64(match, &size_blocks)) { 532 handle_error("could not get " 533 "size of slice: %s", sp->name); 534 *error = 1; 535 } else { 536 uint64_t start_blocks; 537 538 /* Convert to bytes */ 539 sp->size = blocksize * size_blocks; 540 541 /* Get the starting block */ 542 match = zjni_nvlist_walk_nvpair( 543 attrs, DM_START, DATA_TYPE_UINT64, 544 NULL); 545 546 if (match == NULL || 547 nvpair_value_uint64(match, 548 &start_blocks)) { 549 handle_error( 550 "could not get " 551 "start block of slice: %s", 552 sp->name); 553 *error = 1; 554 } else { 555 /* Convert to bytes */ 556 sp->start = 557 blocksize * start_blocks; 558 559 /* Set slice use */ 560 get_slice_use(slice, sp->name, 561 &(sp->used_name), 562 &(sp->used_by), error); 563 } 564 } 565 } 566 } 567 } 568 569 if (*error && sp != NULL) { 570 dmgt_free_slice(sp); 571 } 572 573 return (sp); 574 } 575 576 static void 577 handle_error(const char *format, ...) 578 { 579 va_list ap; 580 va_start(ap, format); 581 582 if (error_func != NULL) { 583 error_func(format, ap); 584 } 585 586 va_end(ap); 587 } 588 589 /* Should go away once 6285992 is fixed */ 590 static int 591 slice_too_small(dmgt_slice_t *slice) 592 { 593 /* Check size */ 594 if (slice->size < SPA_MINDEVSIZE) { 595 #ifdef DEBUG 596 (void) fprintf(stderr, "can't use %s: slice too small: %llu\n", 597 slice->name, (unsigned long long)slice->size); 598 #endif 599 return (1); 600 } 601 602 return (0); 603 } 604 605 /* Should go away once 6285992 is fixed */ 606 static int 607 slice_in_use(dmgt_slice_t *slice) 608 { 609 int in_use = 0; 610 611 /* Check use */ 612 if (slice->used_name != NULL) { 613 614 in_use = 1; 615 616 /* If the slice contains an unmounted file system... */ 617 if (strcmp(DM_USE_FS, slice->used_name) == 0) { 618 619 /* Allow only if file system is not ZFS */ 620 if (strcmp(slice->used_by, "zfs") != 0) { 621 in_use = 0; 622 } 623 } else 624 625 /* Uses that don't preclude slice from use by ZFS */ 626 if (strcmp(DM_USE_SVM, slice->used_name) == 0 || 627 strcmp(DM_USE_VXVM, slice->used_name) == 0 || 628 strcmp(DM_USE_LU, slice->used_name) == 0) { 629 in_use = 0; 630 } 631 } 632 633 #ifdef DEBUG 634 if (in_use) { 635 (void) fprintf(stderr, 636 "can't use %s: used name: %s: used by: %s\n", 637 slice->name, slice->used_name, slice->used_by); 638 } 639 #endif 640 641 return (in_use); 642 } 643 644 /* 645 * Extern functions 646 */ 647 648 /* 649 * Iterates through each available disk on the system. For each free 650 * dmgt_disk_t *, runs the given function with the dmgt_disk_t * as 651 * the first arg and the given void * as the second arg. 652 */ 653 int 654 dmgt_avail_disk_iter(dmgt_disk_iter_f func, void *data) 655 { 656 int error = 0; 657 int filter[] = { DM_DT_FIXED, -1 }; 658 659 /* Search for fixed disks */ 660 dm_descriptor_t *disks = dm_get_descriptors(DM_DRIVE, filter, &error); 661 662 if (error) { 663 handle_error("unable to communicate with libdiskmgt"); 664 } else { 665 int i; 666 667 /* For each disk... */ 668 for (i = 0; disks != NULL && error == 0 && disks[i] != NULL; 669 i++) { 670 /* Is this disk online? */ 671 dm_descriptor_t disk = (dm_descriptor_t)disks[i]; 672 int online = get_disk_online(disk, &error); 673 if (!error && online) { 674 dmgt_disk_t *dp = get_disk(disk, &error); 675 if (!error) { 676 /* 677 * If this disk or any of its 678 * slices is usable... 679 */ 680 if (!dp->in_use || 681 zjni_count_elements( 682 (void **)dp->slices) != 0) { 683 684 /* Run the given function */ 685 if (func(dp, data)) { 686 error = -1; 687 } 688 dmgt_free_disk(dp); 689 #ifdef DEBUG 690 } else { 691 (void) fprintf(stderr, "disk " 692 "has no available slices: " 693 "%s\n", dp->name); 694 #endif 695 } 696 697 } 698 } 699 } 700 dm_free_descriptors(disks); 701 } 702 return (error); 703 } 704 705 void 706 dmgt_free_disk(dmgt_disk_t *disk) 707 { 708 if (disk != NULL) { 709 free(disk->name); 710 zjni_free_array((void **)disk->aliases, free); 711 zjni_free_array((void **)disk->slices, 712 (zjni_free_f)dmgt_free_slice); 713 free(disk); 714 } 715 } 716 717 void 718 dmgt_free_slice(dmgt_slice_t *slice) 719 { 720 if (slice != NULL) { 721 free(slice->name); 722 free(slice->used_name); 723 free(slice->used_by); 724 free(slice); 725 } 726 } 727 728 /* 729 * For clients that need to capture error output. 730 */ 731 void 732 dmgt_set_error_handler(void (*func)(const char *, va_list)) 733 { 734 error_func = func; 735 } 736