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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 2017 Nexenta Systems, Inc. 29 */ 30 31 #include <fcntl.h> 32 #include <libdevinfo.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <dirent.h> 37 #include <sys/dkio.h> 38 #include <sys/stat.h> 39 #include <sys/sunddi.h> 40 #include <sys/types.h> 41 #include <sys/vtoc.h> 42 #include <unistd.h> 43 #include <devid.h> 44 #include <dirent.h> 45 #include <sys/dktp/fdisk.h> 46 #include <sys/efi_partition.h> 47 48 #include "libdiskmgt.h" 49 #include "disks_private.h" 50 #include "partition.h" 51 #ifndef VT_ENOTSUP 52 #define VT_ENOTSUP (-5) 53 #endif 54 55 #define FMT_UNKNOWN 0 56 #define FMT_VTOC 1 57 #define FMT_EFI 2 58 59 typedef int (*detectorp)(char *, nvlist_t *, int *); 60 61 static detectorp detectors[] = { 62 inuse_mnt, 63 inuse_active_zpool, 64 inuse_lu, 65 inuse_dump, 66 inuse_vxvm, 67 inuse_exported_zpool, 68 inuse_fs, /* fs should always be last */ 69 NULL 70 }; 71 72 static int add_inuse(char *name, nvlist_t *attrs); 73 static int desc_ok(descriptor_t *dp); 74 static void dsk2rdsk(char *dsk, char *rdsk, int size); 75 static int get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs); 76 static descriptor_t **get_fixed_assocs(descriptor_t *desc, int *errp); 77 static int get_slice_num(slice_t *devp); 78 static int match_fixed_name(disk_t *dp, char *name, int *errp); 79 static int make_fixed_descriptors(disk_t *dp); 80 81 descriptor_t ** 82 slice_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, 83 int *errp) 84 { 85 if (!desc_ok(desc)) { 86 *errp = ENODEV; 87 return (NULL); 88 } 89 90 switch (type) { 91 case DM_MEDIA: 92 return (media_get_assocs(desc, errp)); 93 case DM_PARTITION: 94 return (partition_get_assocs(desc, errp)); 95 } 96 97 *errp = EINVAL; 98 return (NULL); 99 } 100 101 /* 102 * This is called by media/partition to get the slice descriptors for the given 103 * media/partition descriptor. 104 * For media, just get the slices, but for a partition, it must be a solaris 105 * partition and if there are active partitions, it must be the active one. 106 */ 107 descriptor_t ** 108 slice_get_assocs(descriptor_t *desc, int *errp) 109 { 110 /* Just check the first drive name. */ 111 if (desc->p.disk->aliases == NULL) { 112 *errp = 0; 113 return (libdiskmgt_empty_desc_array(errp)); 114 } 115 116 return (get_fixed_assocs(desc, errp)); 117 } 118 119 nvlist_t * 120 slice_get_attributes(descriptor_t *dp, int *errp) 121 { 122 nvlist_t *attrs = NULL; 123 int fd; 124 char devpath[MAXPATHLEN]; 125 126 if (!desc_ok(dp)) { 127 *errp = ENODEV; 128 return (NULL); 129 } 130 131 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { 132 *errp = ENOMEM; 133 return (NULL); 134 } 135 136 /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */ 137 dsk2rdsk(dp->name, devpath, sizeof (devpath)); 138 fd = open(devpath, O_RDONLY|O_NDELAY); 139 140 if ((*errp = get_attrs(dp, fd, attrs)) != 0) { 141 nvlist_free(attrs); 142 attrs = NULL; 143 } 144 145 if (fd >= 0) { 146 (void) close(fd); 147 } 148 149 return (attrs); 150 } 151 152 /* 153 * Look for the slice by the slice devpath. 154 */ 155 descriptor_t * 156 slice_get_descriptor_by_name(char *name, int *errp) 157 { 158 int found = 0; 159 disk_t *dp; 160 161 for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) { 162 found = match_fixed_name(dp, name, errp); 163 164 if (found) { 165 char mname[MAXPATHLEN]; 166 167 if (*errp != 0) { 168 return (NULL); 169 } 170 171 mname[0] = 0; 172 (void) media_read_name(dp, mname, sizeof (mname)); 173 174 return (cache_get_desc(DM_SLICE, dp, name, mname, 175 errp)); 176 } 177 } 178 179 *errp = ENODEV; 180 return (NULL); 181 } 182 183 /* ARGSUSED */ 184 descriptor_t ** 185 slice_get_descriptors(int filter[], int *errp) 186 { 187 return (cache_get_descriptors(DM_SLICE, errp)); 188 } 189 190 char * 191 slice_get_name(descriptor_t *desc) 192 { 193 return (desc->name); 194 } 195 196 nvlist_t * 197 slice_get_stats(descriptor_t *dp, int stat_type, int *errp) 198 { 199 nvlist_t *stats; 200 201 if (stat_type != DM_SLICE_STAT_USE) { 202 *errp = EINVAL; 203 return (NULL); 204 } 205 206 *errp = 0; 207 208 if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) { 209 *errp = ENOMEM; 210 return (NULL); 211 } 212 213 if ((*errp = add_inuse(dp->name, stats)) != 0) { 214 nvlist_free(stats); 215 return (NULL); 216 } 217 218 return (stats); 219 } 220 221 /* 222 * A slice descriptor points to a disk, the name is the devpath and the 223 * secondary name is the media name. 224 */ 225 int 226 slice_make_descriptors() 227 { 228 disk_t *dp; 229 230 dp = cache_get_disklist(); 231 while (dp != NULL) { 232 int error; 233 234 error = make_fixed_descriptors(dp); 235 if (error != 0) { 236 return (error); 237 } 238 239 dp = dp->next; 240 } 241 242 return (0); 243 } 244 245 /* convert rdsk paths to dsk paths */ 246 void 247 slice_rdsk2dsk(char *rdsk, char *dsk, int size) 248 { 249 char *strp; 250 251 (void) strlcpy(dsk, rdsk, size); 252 253 if ((strp = strstr(dsk, "/rdsk/")) == NULL) { 254 /* not rdsk, check for floppy */ 255 strp = strstr(dsk, "/rdiskette"); 256 } 257 258 if (strp != NULL) { 259 strp++; /* move ptr to the r in rdsk or rdiskette */ 260 261 /* move the succeeding chars over by one */ 262 do { 263 *strp = *(strp + 1); 264 strp++; 265 } while (*strp); 266 } 267 } 268 269 /* 270 * Check if/how the slice is used. 271 */ 272 static int 273 add_inuse(char *name, nvlist_t *attrs) 274 { 275 int i; 276 int error; 277 278 for (i = 0; detectors[i] != NULL; i ++) { 279 if (detectors[i](name, attrs, &error) || error != 0) { 280 if (error != 0) { 281 return (error); 282 } 283 break; 284 } 285 } 286 287 return (0); 288 } 289 290 /* return 1 if the slice descriptor is still valid, 0 if not. */ 291 static int 292 desc_ok(descriptor_t *dp) 293 { 294 /* First verify the media name for removable media */ 295 if (dp->p.disk->removable) { 296 char mname[MAXPATHLEN]; 297 298 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { 299 return (0); 300 } 301 302 if (mname[0] == 0) { 303 return (libdiskmgt_str_eq(dp->secondary_name, NULL)); 304 } else { 305 return (libdiskmgt_str_eq(dp->secondary_name, mname)); 306 } 307 } 308 309 /* 310 * We could verify the slice is still there, but other code down the 311 * line already does these checks (e.g. see get_attrs). 312 */ 313 314 return (1); 315 } 316 317 /* convert dsk paths to rdsk paths */ 318 static void 319 dsk2rdsk(char *dsk, char *rdsk, int size) 320 { 321 char *slashp; 322 size_t len; 323 324 (void) strlcpy(rdsk, dsk, size); 325 326 /* make sure there is enough room to add the r to dsk */ 327 len = strlen(dsk); 328 if (len + 2 > size) { 329 return; 330 } 331 332 if ((slashp = strstr(rdsk, "/dsk/")) == NULL) { 333 /* not dsk, check for floppy */ 334 slashp = strstr(rdsk, "/diskette"); 335 } 336 337 if (slashp != NULL) { 338 char *endp; 339 340 endp = rdsk + len; /* point to terminating 0 */ 341 /* move the succeeding chars over by one */ 342 do { 343 *(endp + 1) = *endp; 344 endp--; 345 } while (endp != slashp); 346 347 *(endp + 1) = 'r'; 348 } 349 } 350 351 static int 352 get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs) 353 { 354 struct dk_minfo minfo; 355 int status; 356 int data_format = FMT_UNKNOWN; 357 int snum = -1; 358 int error; 359 struct extvtoc vtoc; 360 struct dk_gpt *efip; 361 struct dk_cinfo dkinfo; 362 int cooked_fd; 363 struct stat buf; 364 365 if (fd < 0) { 366 return (ENODEV); 367 } 368 369 /* First make sure media is inserted and spun up. */ 370 if (!media_read_info(fd, &minfo)) { 371 return (ENODEV); 372 } 373 374 if ((status = read_extvtoc(fd, &vtoc)) >= 0) { 375 data_format = FMT_VTOC; 376 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { 377 data_format = FMT_EFI; 378 if (nvlist_add_boolean(attrs, DM_EFI) != 0) { 379 efi_free(efip); 380 return (ENOMEM); 381 } 382 } 383 384 if (data_format == FMT_UNKNOWN) { 385 return (ENODEV); 386 } 387 388 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { 389 snum = dkinfo.dki_partition; 390 } 391 392 /* check the slice */ 393 if (data_format == FMT_VTOC) { 394 if (snum < 0 || snum >= vtoc.v_nparts || 395 vtoc.v_part[snum].p_size == 0) { 396 return (ENODEV); 397 } 398 } else { /* data_format == FMT_EFI */ 399 if (snum < 0 || snum >= efip->efi_nparts || 400 efip->efi_parts[snum].p_size == 0) { 401 efi_free(efip); 402 return (ENODEV); 403 } 404 } 405 406 /* the slice exists */ 407 408 if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) { 409 if (data_format == FMT_EFI) { 410 efi_free(efip); 411 } 412 return (ENOMEM); 413 } 414 415 if (data_format == FMT_VTOC) { 416 if (nvlist_add_uint64(attrs, DM_START, 417 vtoc.v_part[snum].p_start) != 0) { 418 return (ENOMEM); 419 } 420 421 if (nvlist_add_uint64(attrs, DM_SIZE, 422 vtoc.v_part[snum].p_size) != 0) { 423 return (ENOMEM); 424 } 425 426 if (nvlist_add_uint32(attrs, DM_TAG, 427 vtoc.v_part[snum].p_tag) != 0) { 428 return (ENOMEM); 429 } 430 431 if (nvlist_add_uint32(attrs, DM_FLAG, 432 vtoc.v_part[snum].p_flag) != 0) { 433 return (ENOMEM); 434 } 435 } else { /* data_format == FMT_EFI */ 436 if (nvlist_add_uint64(attrs, DM_START, 437 efip->efi_parts[snum].p_start) != 0) { 438 efi_free(efip); 439 return (ENOMEM); 440 } 441 442 if (nvlist_add_uint64(attrs, DM_SIZE, 443 efip->efi_parts[snum].p_size) != 0) { 444 efi_free(efip); 445 return (ENOMEM); 446 } 447 448 if (efip->efi_parts[snum].p_name[0] != 0) { 449 char label[EFI_PART_NAME_LEN + 1]; 450 451 (void) snprintf(label, sizeof (label), "%.*s", 452 EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name); 453 if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) { 454 efi_free(efip); 455 return (ENOMEM); 456 } 457 } 458 } 459 460 if (data_format == FMT_EFI) { 461 efi_free(efip); 462 } 463 464 if (inuse_mnt(dp->name, attrs, &error)) { 465 if (error != 0) 466 return (error); 467 } 468 469 if (fstat(fd, &buf) != -1) { 470 if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) { 471 return (ENOMEM); 472 } 473 } 474 475 /* 476 * We need to open the cooked slice (not the raw one) to get the 477 * correct devid. 478 */ 479 cooked_fd = open(dp->name, O_RDONLY|O_NDELAY); 480 481 if (cooked_fd >= 0) { 482 int no_mem = 0; 483 ddi_devid_t devid; 484 485 if (devid_get(cooked_fd, &devid) == 0) { 486 char *minor; 487 488 if (devid_get_minor_name(cooked_fd, &minor) == 0) { 489 char *devidstr; 490 491 devidstr = devid_str_encode(devid, minor); 492 if (devidstr != NULL) { 493 494 if (nvlist_add_string(attrs, 495 DM_DEVICEID, devidstr) != 0) { 496 no_mem = 1; 497 } 498 499 devid_str_free(devidstr); 500 } 501 devid_str_free(minor); 502 } 503 devid_free(devid); 504 } 505 (void) close(cooked_fd); 506 507 if (no_mem) { 508 return (ENOMEM); 509 } 510 } 511 512 return (0); 513 } 514 515 static descriptor_t ** 516 get_fixed_assocs(descriptor_t *desc, int *errp) 517 { 518 int fd; 519 int status; 520 int data_format = FMT_UNKNOWN; 521 int cnt; 522 struct extvtoc vtoc; 523 struct dk_gpt *efip; 524 int pos; 525 char *media_name = NULL; 526 slice_t *devp; 527 descriptor_t **slices; 528 529 if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) { 530 *errp = ENODEV; 531 return (NULL); 532 } 533 534 if ((status = read_extvtoc(fd, &vtoc)) >= 0) { 535 data_format = FMT_VTOC; 536 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { 537 data_format = FMT_EFI; 538 } else { 539 (void) close(fd); 540 *errp = 0; 541 return (libdiskmgt_empty_desc_array(errp)); 542 } 543 (void) close(fd); 544 545 /* count the number of slices */ 546 devp = desc->p.disk->aliases->devpaths; 547 for (cnt = 0; devp != NULL; devp = devp->next) 548 cnt++; 549 550 /* allocate the array for the descriptors */ 551 slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 552 if (slices == NULL) { 553 if (data_format == FMT_EFI) { 554 efi_free(efip); 555 } 556 *errp = ENOMEM; 557 return (NULL); 558 } 559 560 /* get the media name from the descriptor */ 561 if (desc->type == DM_MEDIA) { 562 media_name = desc->name; 563 } else { 564 /* must be a DM_PARTITION */ 565 media_name = desc->secondary_name; 566 } 567 568 pos = 0; 569 for (devp = desc->p.disk->aliases->devpaths; devp != NULL; 570 devp = devp->next) { 571 572 int slice_num; 573 char devpath[MAXPATHLEN]; 574 575 slice_num = get_slice_num(devp); 576 /* can't get slicenum, so no need to keep trying the drive */ 577 if (slice_num == -1) { 578 break; 579 } 580 581 if (data_format == FMT_VTOC) { 582 if (slice_num >= vtoc.v_nparts || 583 vtoc.v_part[slice_num].p_size == 0) { 584 continue; 585 } 586 } else { /* data_format == FMT_EFI */ 587 if (slice_num >= efip->efi_nparts || 588 efip->efi_parts[slice_num].p_size == 0) { 589 continue; 590 } 591 } 592 593 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); 594 slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath, 595 media_name, errp); 596 if (*errp != 0) { 597 cache_free_descriptors(slices); 598 if (data_format == FMT_EFI) { 599 efi_free(efip); 600 } 601 return (NULL); 602 } 603 pos++; 604 } 605 slices[pos] = NULL; 606 607 if (data_format == FMT_EFI) { 608 efi_free(efip); 609 } 610 611 *errp = 0; 612 return (slices); 613 } 614 615 static int 616 get_slice_num(slice_t *devp) 617 { 618 /* check if we already determined the devpath slice number */ 619 if (devp->slice_num == -1) { 620 int fd; 621 622 if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) { 623 struct dk_cinfo dkinfo; 624 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { 625 devp->slice_num = dkinfo.dki_partition; 626 } 627 (void) close(fd); 628 } 629 } 630 631 return (devp->slice_num); 632 } 633 634 static int 635 make_fixed_descriptors(disk_t *dp) 636 { 637 int error = 0; 638 alias_t *ap; 639 slice_t *devp; 640 char mname[MAXPATHLEN]; 641 int data_format = FMT_UNKNOWN; 642 struct extvtoc vtoc; 643 struct dk_gpt *efip; 644 645 /* Just check the first drive name. */ 646 if ((ap = dp->aliases) == NULL) { 647 return (0); 648 } 649 650 mname[0] = 0; 651 (void) media_read_name(dp, mname, sizeof (mname)); 652 653 for (devp = ap->devpaths; devp != NULL; devp = devp->next) { 654 int slice_num; 655 char devpath[MAXPATHLEN]; 656 657 slice_num = get_slice_num(devp); 658 /* can't get slicenum, so no need to keep trying the drive */ 659 if (slice_num == -1) { 660 break; 661 } 662 663 if (data_format == FMT_UNKNOWN) { 664 int fd; 665 int status; 666 667 if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { 668 if ((status = read_extvtoc(fd, &vtoc)) >= 0) { 669 data_format = FMT_VTOC; 670 } else if (status == VT_ENOTSUP && 671 efi_alloc_and_read(fd, &efip) >= 0) { 672 data_format = FMT_EFI; 673 } 674 (void) close(fd); 675 } 676 } 677 678 /* can't get slice data, so no need to keep trying the drive */ 679 if (data_format == FMT_UNKNOWN) { 680 break; 681 } 682 683 if (data_format == FMT_VTOC) { 684 if (slice_num >= vtoc.v_nparts || 685 vtoc.v_part[slice_num].p_size == 0) { 686 continue; 687 } 688 } else { /* data_format == FMT_EFI */ 689 if (slice_num >= efip->efi_nparts || 690 efip->efi_parts[slice_num].p_size == 0) { 691 continue; 692 } 693 } 694 695 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); 696 cache_load_desc(DM_SLICE, dp, devpath, mname, &error); 697 if (error != 0) { 698 break; 699 } 700 } 701 702 if (data_format == FMT_EFI) { 703 efi_free(efip); 704 } 705 706 return (error); 707 } 708 709 /* 710 * Just look for the name on the devpaths we have cached. Return 1 if we 711 * find the name and the size of that slice is non-zero. 712 */ 713 static int 714 match_fixed_name(disk_t *diskp, char *name, int *errp) 715 { 716 slice_t *dp = NULL; 717 alias_t *ap; 718 int slice_num; 719 int fd; 720 int status; 721 int data_format = FMT_UNKNOWN; 722 struct extvtoc vtoc; 723 struct dk_gpt *efip; 724 725 ap = diskp->aliases; 726 while (ap != NULL) { 727 slice_t *devp; 728 729 devp = ap->devpaths; 730 while (devp != NULL) { 731 char path[MAXPATHLEN]; 732 733 slice_rdsk2dsk(devp->devpath, path, sizeof (path)); 734 if (libdiskmgt_str_eq(path, name)) { 735 /* found it */ 736 dp = devp; 737 break; 738 } 739 740 devp = devp->next; 741 } 742 743 if (dp != NULL) { 744 break; 745 } 746 747 ap = ap->next; 748 } 749 750 if (dp == NULL) { 751 *errp = 0; 752 return (0); 753 } 754 755 /* 756 * If we found a match on the name we now have to check that this 757 * slice really exists (non-0 size). 758 */ 759 760 slice_num = get_slice_num(dp); 761 /* can't get slicenum, so no slice */ 762 if (slice_num == -1) { 763 *errp = ENODEV; 764 return (1); 765 } 766 767 if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) { 768 *errp = ENODEV; 769 return (1); 770 } 771 772 if ((status = read_extvtoc(fd, &vtoc)) >= 0) { 773 data_format = FMT_VTOC; 774 } else if (status == VT_ENOTSUP) { 775 status = efi_alloc_and_read(fd, &efip); 776 if (status >= 0) { 777 data_format = FMT_EFI; 778 } else if (status == VT_ERROR && errno == ENOTTY) { 779 *errp = 0; 780 return (1); 781 } 782 } else { 783 (void) close(fd); 784 *errp = ENODEV; 785 return (1); 786 } 787 (void) close(fd); 788 789 if (data_format == FMT_VTOC) { 790 if (slice_num < vtoc.v_nparts && 791 vtoc.v_part[slice_num].p_size > 0) { 792 *errp = 0; 793 return (1); 794 } 795 } else { /* data_format == FMT_EFI */ 796 if (slice_num < efip->efi_nparts && 797 efip->efi_parts[slice_num].p_size > 0) { 798 efi_free(efip); 799 *errp = 0; 800 return (1); 801 } 802 efi_free(efip); 803 } 804 805 *errp = ENODEV; 806 return (1); 807 } 808