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, vtoc.v_part[snum].p_start) 417 != 0) { 418 return (ENOMEM); 419 } 420 421 if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size) 422 != 0) { 423 return (ENOMEM); 424 } 425 426 if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag) 427 != 0) { 428 return (ENOMEM); 429 } 430 431 if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag) 432 != 0) { 433 return (ENOMEM); 434 } 435 436 } else { /* data_format == FMT_EFI */ 437 if (nvlist_add_uint64(attrs, DM_START, 438 efip->efi_parts[snum].p_start) != 0) { 439 efi_free(efip); 440 return (ENOMEM); 441 } 442 443 if (nvlist_add_uint64(attrs, DM_SIZE, efip->efi_parts[snum].p_size) 444 != 0) { 445 efi_free(efip); 446 return (ENOMEM); 447 } 448 449 if (efip->efi_parts[snum].p_name[0] != 0) { 450 char label[EFI_PART_NAME_LEN + 1]; 451 452 (void) snprintf(label, sizeof (label), "%.*s", 453 EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name); 454 if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) { 455 efi_free(efip); 456 return (ENOMEM); 457 } 458 } 459 } 460 461 if (data_format == FMT_EFI) { 462 efi_free(efip); 463 } 464 465 if (inuse_mnt(dp->name, attrs, &error)) { 466 if (error != 0) 467 return (error); 468 } 469 470 if (fstat(fd, &buf) != -1) { 471 if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) { 472 return (ENOMEM); 473 } 474 } 475 476 /* 477 * We need to open the cooked slice (not the raw one) to get the 478 * correct devid. 479 */ 480 cooked_fd = open(dp->name, O_RDONLY|O_NDELAY); 481 482 if (cooked_fd >= 0) { 483 int no_mem = 0; 484 ddi_devid_t devid; 485 486 if (devid_get(cooked_fd, &devid) == 0) { 487 char *minor; 488 489 if (devid_get_minor_name(cooked_fd, &minor) == 0) { 490 char *devidstr; 491 492 if ((devidstr = devid_str_encode(devid, minor)) != 0) { 493 494 if (nvlist_add_string(attrs, DM_DEVICEID, devidstr) 495 != 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 for (cnt = 0, devp = desc->p.disk->aliases->devpaths; devp != NULL; 547 devp = devp->next, cnt++); 548 549 /* allocate the array for the descriptors */ 550 slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *)); 551 if (slices == NULL) { 552 if (data_format == FMT_EFI) { 553 efi_free(efip); 554 } 555 *errp = ENOMEM; 556 return (NULL); 557 } 558 559 /* get the media name from the descriptor */ 560 if (desc->type == DM_MEDIA) { 561 media_name = desc->name; 562 } else { 563 /* must be a DM_PARTITION */ 564 media_name = desc->secondary_name; 565 } 566 567 pos = 0; 568 for (devp = desc->p.disk->aliases->devpaths; devp != NULL; 569 devp = devp->next) { 570 571 int slice_num; 572 char devpath[MAXPATHLEN]; 573 574 slice_num = get_slice_num(devp); 575 /* can't get slicenum, so no need to keep trying the drive */ 576 if (slice_num == -1) { 577 break; 578 } 579 580 if (data_format == FMT_VTOC) { 581 if (slice_num >= vtoc.v_nparts || 582 vtoc.v_part[slice_num].p_size == 0) { 583 continue; 584 } 585 } else { /* data_format == FMT_EFI */ 586 if (slice_num >= efip->efi_nparts || 587 efip->efi_parts[slice_num].p_size == 0) { 588 continue; 589 } 590 } 591 592 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); 593 slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath, 594 media_name, errp); 595 if (*errp != 0) { 596 cache_free_descriptors(slices); 597 if (data_format == FMT_EFI) { 598 efi_free(efip); 599 } 600 return (NULL); 601 } 602 pos++; 603 } 604 slices[pos] = NULL; 605 606 if (data_format == FMT_EFI) { 607 efi_free(efip); 608 } 609 610 *errp = 0; 611 return (slices); 612 } 613 614 static int 615 get_slice_num(slice_t *devp) 616 { 617 /* check if we already determined the devpath slice number */ 618 if (devp->slice_num == -1) { 619 int fd; 620 621 if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) { 622 struct dk_cinfo dkinfo; 623 if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { 624 devp->slice_num = dkinfo.dki_partition; 625 } 626 (void) close(fd); 627 } 628 } 629 630 return (devp->slice_num); 631 } 632 633 static int 634 make_fixed_descriptors(disk_t *dp) 635 { 636 int error = 0; 637 alias_t *ap; 638 slice_t *devp; 639 char mname[MAXPATHLEN]; 640 int data_format = FMT_UNKNOWN; 641 struct extvtoc vtoc; 642 struct dk_gpt *efip; 643 644 /* Just check the first drive name. */ 645 if ((ap = dp->aliases) == NULL) { 646 return (0); 647 } 648 649 mname[0] = 0; 650 (void) media_read_name(dp, mname, sizeof (mname)); 651 652 for (devp = ap->devpaths; devp != NULL; devp = devp->next) { 653 int slice_num; 654 char devpath[MAXPATHLEN]; 655 656 slice_num = get_slice_num(devp); 657 /* can't get slicenum, so no need to keep trying the drive */ 658 if (slice_num == -1) { 659 break; 660 } 661 662 if (data_format == FMT_UNKNOWN) { 663 int fd; 664 int status; 665 666 if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) { 667 if ((status = read_extvtoc(fd, &vtoc)) >= 0) { 668 data_format = FMT_VTOC; 669 } else if (status == VT_ENOTSUP && 670 efi_alloc_and_read(fd, &efip) >= 0) { 671 data_format = FMT_EFI; 672 } 673 (void) close(fd); 674 } 675 } 676 677 /* can't get slice data, so no need to keep trying the drive */ 678 if (data_format == FMT_UNKNOWN) { 679 break; 680 } 681 682 if (data_format == FMT_VTOC) { 683 if (slice_num >= vtoc.v_nparts || 684 vtoc.v_part[slice_num].p_size == 0) { 685 continue; 686 } 687 } else { /* data_format == FMT_EFI */ 688 if (slice_num >= efip->efi_nparts || 689 efip->efi_parts[slice_num].p_size == 0) { 690 continue; 691 } 692 } 693 694 slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath)); 695 cache_load_desc(DM_SLICE, dp, devpath, mname, &error); 696 if (error != 0) { 697 break; 698 } 699 } 700 701 if (data_format == FMT_EFI) { 702 efi_free(efip); 703 } 704 705 return (error); 706 } 707 708 /* 709 * Just look for the name on the devpaths we have cached. Return 1 if we 710 * find the name and the size of that slice is non-zero. 711 */ 712 static int 713 match_fixed_name(disk_t *diskp, char *name, int *errp) 714 { 715 slice_t *dp = NULL; 716 alias_t *ap; 717 int slice_num; 718 int fd; 719 int status; 720 int data_format = FMT_UNKNOWN; 721 struct extvtoc vtoc; 722 struct dk_gpt *efip; 723 724 ap = diskp->aliases; 725 while (ap != NULL) { 726 slice_t *devp; 727 728 devp = ap->devpaths; 729 while (devp != NULL) { 730 char path[MAXPATHLEN]; 731 732 slice_rdsk2dsk(devp->devpath, path, sizeof (path)); 733 if (libdiskmgt_str_eq(path, name)) { 734 /* found it */ 735 dp = devp; 736 break; 737 } 738 739 devp = devp->next; 740 } 741 742 if (dp != NULL) { 743 break; 744 } 745 746 ap = ap->next; 747 } 748 749 if (dp == NULL) { 750 *errp = 0; 751 return (0); 752 } 753 754 /* 755 * If we found a match on the name we now have to check that this 756 * slice really exists (non-0 size). 757 */ 758 759 slice_num = get_slice_num(dp); 760 /* can't get slicenum, so no slice */ 761 if (slice_num == -1) { 762 *errp = ENODEV; 763 return (1); 764 } 765 766 if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) { 767 *errp = ENODEV; 768 return (1); 769 } 770 771 if ((status = read_extvtoc(fd, &vtoc)) >= 0) { 772 data_format = FMT_VTOC; 773 } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { 774 data_format = FMT_EFI; 775 } else { 776 (void) close(fd); 777 *errp = ENODEV; 778 return (1); 779 } 780 (void) close(fd); 781 782 if (data_format == FMT_VTOC) { 783 if (slice_num < vtoc.v_nparts && 784 vtoc.v_part[slice_num].p_size > 0) { 785 *errp = 0; 786 return (1); 787 } 788 } else { /* data_format == FMT_EFI */ 789 if (slice_num < efip->efi_nparts && 790 efip->efi_parts[slice_num].p_size > 0) { 791 efi_free(efip); 792 *errp = 0; 793 return (1); 794 } 795 efi_free(efip); 796 } 797 798 *errp = ENODEV; 799 return (1); 800 } 801