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 2004 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 <fcntl.h> 30 #include <libdevinfo.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <sys/sunddi.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <dirent.h> 38 #include <unistd.h> 39 #include <sys/dkio.h> 40 41 #include "libdiskmgt.h" 42 #include "disks_private.h" 43 #include "partition.h" 44 45 #ifdef sparc 46 #define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF)) 47 #define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \ 48 (les((unsigned)((val)&0xffff0000)>>16))) 49 #else 50 #define les(val) (val) 51 #define lel(val) (val) 52 #endif 53 54 #define ISIZE FD_NUMPART * sizeof (struct ipart) 55 56 static int desc_ok(descriptor_t *dp); 57 static int get_attrs(descriptor_t *dp, struct ipart *iparts, 58 nvlist_t *attrs); 59 static int get_parts(disk_t *disk, struct ipart *iparts, char *opath, 60 int opath_len); 61 static int open_disk(disk_t *diskp, char *opath, int len); 62 static int has_slices(descriptor_t *desc, int *errp); 63 64 descriptor_t ** 65 partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, 66 int *errp) 67 { 68 if (!desc_ok(desc)) { 69 *errp = ENODEV; 70 return (NULL); 71 } 72 73 switch (type) { 74 case DM_MEDIA: 75 return (media_get_assocs(desc, errp)); 76 case DM_SLICE: 77 if (!has_slices(desc, errp)) { 78 if (*errp != 0) { 79 return (NULL); 80 } 81 return (libdiskmgt_empty_desc_array(errp)); 82 } 83 return (slice_get_assocs(desc, errp)); 84 } 85 86 *errp = EINVAL; 87 return (NULL); 88 } 89 90 /* 91 * This is called by media/slice to get the associated partitions. 92 * For a media desc. we just get all the partitions, but for a slice desc. 93 * we just get the active solaris partition. 94 */ 95 descriptor_t ** 96 partition_get_assocs(descriptor_t *desc, int *errp) 97 { 98 descriptor_t **partitions; 99 int pos; 100 int i; 101 struct ipart iparts[FD_NUMPART]; 102 char pname[MAXPATHLEN]; 103 int conv_flag = 0; 104 105 if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) { 106 return (libdiskmgt_empty_desc_array(errp)); 107 } 108 109 /* allocate the array for the descriptors */ 110 partitions = (descriptor_t **)calloc(FD_NUMPART + 1, 111 sizeof (descriptor_t *)); 112 if (partitions == NULL) { 113 *errp = ENOMEM; 114 return (NULL); 115 } 116 117 #ifdef i386 118 { 119 /* convert part. name (e.g. c0d0p0) */ 120 int len; 121 122 len = strlen(pname); 123 if (len > 1 && *(pname + (len - 2)) == 'p') { 124 conv_flag = 1; 125 *(pname + (len - 1)) = 0; 126 } 127 } 128 #endif 129 130 /* 131 * If this is a slice desc. we need the first active solaris partition 132 * and if there isn't one then we need the first solaris partition. 133 */ 134 if (desc->type == DM_SLICE) { 135 for (i = 0; i < FD_NUMPART; i++) { 136 if (iparts[i].bootid == ACTIVE && 137 (iparts[i].systid == SUNIXOS || 138 iparts[i].systid == SUNIXOS2)) { 139 break; 140 } 141 } 142 143 /* no active solaris part., try to get the first solaris part. */ 144 if (i >= FD_NUMPART) { 145 for (i = 0; i < FD_NUMPART; i++) { 146 if (iparts[i].systid == SUNIXOS || 147 iparts[i].systid == SUNIXOS2) { 148 break; 149 } 150 } 151 } 152 153 if (i < FD_NUMPART) { 154 /* we found a solaris partition to use */ 155 char part_name[MAXPATHLEN]; 156 157 if (conv_flag) { 158 /* convert part. name (e.g. c0d0p0) */ 159 (void) snprintf(part_name, sizeof (part_name), "%s%d", 160 pname, i); 161 } else { 162 (void) snprintf(part_name, sizeof (part_name), "%d", i); 163 } 164 165 /* the media name comes from the slice desc. */ 166 partitions[0] = cache_get_desc(DM_PARTITION, desc->p.disk, 167 part_name, desc->secondary_name, errp); 168 if (*errp != 0) { 169 cache_free_descriptors(partitions); 170 return (NULL); 171 } 172 partitions[1] = NULL; 173 174 return (partitions); 175 176 } 177 178 return (libdiskmgt_empty_desc_array(errp)); 179 } 180 181 /* Must be for media, so get all the parts. */ 182 183 pos = 0; 184 for (i = 0; i < FD_NUMPART; i++) { 185 if (iparts[i].systid != 0) { 186 char part_name[MAXPATHLEN]; 187 188 if (conv_flag) { 189 /* convert part. name (e.g. c0d0p0) */ 190 (void) snprintf(part_name, sizeof (part_name), "%s%d", 191 pname, i); 192 } else { 193 (void) snprintf(part_name, sizeof (part_name), "%d", i); 194 } 195 196 /* the media name comes from the media desc. */ 197 partitions[pos] = cache_get_desc(DM_PARTITION, desc->p.disk, 198 part_name, desc->name, errp); 199 if (*errp != 0) { 200 cache_free_descriptors(partitions); 201 return (NULL); 202 } 203 204 pos++; 205 } 206 } 207 partitions[pos] = NULL; 208 209 *errp = 0; 210 return (partitions); 211 } 212 213 nvlist_t * 214 partition_get_attributes(descriptor_t *dp, int *errp) 215 { 216 nvlist_t *attrs = NULL; 217 struct ipart iparts[FD_NUMPART]; 218 219 if (!desc_ok(dp)) { 220 *errp = ENODEV; 221 return (NULL); 222 } 223 224 if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) { 225 return (NULL); 226 } 227 228 if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) { 229 *errp = ENOMEM; 230 return (NULL); 231 } 232 233 if ((*errp = get_attrs(dp, iparts, attrs)) != 0) { 234 nvlist_free(attrs); 235 attrs = NULL; 236 } 237 238 return (attrs); 239 } 240 241 /* 242 * Look for the partition by the partition number (which is not too useful). 243 */ 244 descriptor_t * 245 partition_get_descriptor_by_name(char *name, int *errp) 246 { 247 descriptor_t **partitions; 248 int i; 249 descriptor_t *partition = NULL; 250 251 partitions = cache_get_descriptors(DM_PARTITION, errp); 252 if (*errp != 0) { 253 return (NULL); 254 } 255 256 for (i = 0; partitions[i]; i++) { 257 if (libdiskmgt_str_eq(name, partitions[i]->name)) { 258 partition = partitions[i]; 259 } else { 260 /* clean up the unused descriptors */ 261 cache_free_descriptor(partitions[i]); 262 } 263 } 264 free(partitions); 265 266 if (partition == NULL) { 267 *errp = ENODEV; 268 } 269 270 return (partition); 271 } 272 273 /* ARGSUSED */ 274 descriptor_t ** 275 partition_get_descriptors(int filter[], int *errp) 276 { 277 return (cache_get_descriptors(DM_PARTITION, errp)); 278 } 279 280 char * 281 partition_get_name(descriptor_t *desc) 282 { 283 return (desc->name); 284 } 285 286 /* ARGSUSED */ 287 nvlist_t * 288 partition_get_stats(descriptor_t *dp, int stat_type, int *errp) 289 { 290 /* There are no stat types defined for partitions */ 291 *errp = EINVAL; 292 return (NULL); 293 } 294 295 /* ARGSUSED */ 296 int 297 partition_has_fdisk(disk_t *dp, int fd) 298 { 299 char bootsect[512 * 3]; /* 3 sectors to be safe */ 300 301 #ifdef sparc 302 if (dp->drv_type == DM_DT_FIXED) { 303 /* on sparc, only removable media can have fdisk parts. */ 304 return (0); 305 } 306 #endif 307 308 /* 309 * We assume the caller already made sure media was inserted and 310 * spun up. 311 */ 312 313 if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) { 314 return (0); 315 } 316 317 return (1); 318 } 319 320 /* 321 * A partition descriptor points to a disk, the name is the partition number 322 * and the secondary name is the media name. 323 */ 324 int 325 partition_make_descriptors() 326 { 327 int error; 328 disk_t *dp; 329 330 dp = cache_get_disklist(); 331 while (dp != NULL) { 332 struct ipart iparts[FD_NUMPART]; 333 char pname[MAXPATHLEN]; 334 335 if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) { 336 int i; 337 char mname[MAXPATHLEN]; 338 int conv_flag = 0; 339 340 #ifdef i386 341 /* convert part. name (e.g. c0d0p0) */ 342 int len; 343 344 len = strlen(pname); 345 if (len > 1 && *(pname + (len - 2)) == 'p') { 346 conv_flag = 1; 347 *(pname + (len - 1)) = 0; 348 } 349 #endif 350 351 mname[0] = 0; 352 (void) media_read_name(dp, mname, sizeof (mname)); 353 354 for (i = 0; i < FD_NUMPART; i++) { 355 if (iparts[i].systid != 0) { 356 char part_name[MAXPATHLEN]; 357 358 if (conv_flag) { 359 /* convert part. name (e.g. c0d0p0) */ 360 (void) snprintf(part_name, sizeof (part_name), 361 "%s%d", pname, i); 362 } else { 363 (void) snprintf(part_name, sizeof (part_name), 364 "%d", i); 365 } 366 367 cache_load_desc(DM_PARTITION, dp, part_name, mname, 368 &error); 369 if (error != 0) { 370 return (error); 371 } 372 } 373 } 374 } 375 dp = dp->next; 376 } 377 378 return (0); 379 } 380 381 static int 382 get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs) 383 { 384 char *p; 385 int part_num; 386 387 /* 388 * We already made sure the media was loaded and ready in the 389 * get_parts call within partition_get_attributes. 390 */ 391 392 p = strrchr(dp->name, 'p'); 393 if (p == NULL) { 394 p = dp->name; 395 } else { 396 p++; 397 } 398 part_num = atoi(p); 399 if (part_num >= FD_NUMPART || iparts[part_num].systid == 0) { 400 return (ENODEV); 401 } 402 403 /* we found the partition */ 404 405 if (nvlist_add_uint32(attrs, DM_BOOTID, 406 (unsigned int)iparts[part_num].bootid) != 0) { 407 return (ENOMEM); 408 } 409 410 if (nvlist_add_uint32(attrs, DM_PTYPE, 411 (unsigned int)iparts[part_num].systid) != 0) { 412 return (ENOMEM); 413 } 414 415 if (nvlist_add_uint32(attrs, DM_BHEAD, 416 (unsigned int)iparts[part_num].beghead) != 0) { 417 return (ENOMEM); 418 } 419 420 if (nvlist_add_uint32(attrs, DM_BSECT, 421 (unsigned int)((iparts[part_num].begsect) & 0x3f)) != 0) { 422 return (ENOMEM); 423 } 424 425 if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int) 426 ((iparts[part_num].begcyl & 0xff) | 427 ((iparts[part_num].begsect & 0xc0) << 2))) != 0) { 428 return (ENOMEM); 429 } 430 431 if (nvlist_add_uint32(attrs, DM_EHEAD, 432 (unsigned int)iparts[part_num].endhead) != 0) { 433 return (ENOMEM); 434 } 435 436 if (nvlist_add_uint32(attrs, DM_ESECT, 437 (unsigned int)((iparts[part_num].endsect) & 0x3f)) != 0) { 438 return (ENOMEM); 439 } 440 441 if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int) 442 ((iparts[part_num].endcyl & 0xff) | 443 ((iparts[part_num].endsect & 0xc0) << 2))) != 0) { 444 return (ENOMEM); 445 } 446 447 if (nvlist_add_uint32(attrs, DM_RELSECT, 448 (unsigned int)iparts[part_num].relsect) != 0) { 449 return (ENOMEM); 450 } 451 452 if (nvlist_add_uint32(attrs, DM_NSECTORS, 453 (unsigned int)iparts[part_num].numsect) != 0) { 454 return (ENOMEM); 455 } 456 457 return (0); 458 } 459 460 static int 461 get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len) 462 { 463 int fd; 464 struct dk_minfo minfo; 465 struct mboot bootblk; 466 char bootsect[512]; 467 int i; 468 469 /* Can't use drive_open_disk since we need the partition dev name. */ 470 if ((fd = open_disk(disk, opath, opath_len)) < 0) { 471 return (ENODEV); 472 } 473 474 /* First make sure media is inserted and spun up. */ 475 if (!media_read_info(fd, &minfo)) { 476 #ifdef i386 477 /* XXX Work around bug 4725434 */ 478 if (disk->removable) { 479 #endif 480 (void) close(fd); 481 return (ENODEV); 482 #ifdef i386 483 } 484 #endif 485 } 486 487 if (!partition_has_fdisk(disk, fd)) { 488 (void) close(fd); 489 return (ENOTTY); 490 } 491 492 if (lseek(fd, 0, 0) == -1) { 493 (void) close(fd); 494 return (ENODEV); 495 } 496 497 if (read(fd, bootsect, 512) != 512) { 498 (void) close(fd); 499 return (ENODEV); 500 } 501 (void) close(fd); 502 503 (void) memcpy(&bootblk, bootsect, sizeof (bootblk)); 504 505 if (les(bootblk.signature) != MBB_MAGIC) { 506 return (ENOTTY); 507 } 508 509 (void) memcpy(iparts, bootblk.parts, ISIZE); 510 511 for (i = 0; i < FD_NUMPART; i++) { 512 if (iparts[i].systid != 0) { 513 iparts[i].relsect = lel(iparts[i].relsect); 514 iparts[i].numsect = lel(iparts[i].numsect); 515 } 516 } 517 518 return (0); 519 } 520 /* return 1 if the partition descriptor is still valid, 0 if not. */ 521 static int 522 desc_ok(descriptor_t *dp) 523 { 524 /* First verify the media name for removable media */ 525 if (dp->p.disk->removable) { 526 char mname[MAXPATHLEN]; 527 528 if (!media_read_name(dp->p.disk, mname, sizeof (mname))) { 529 return (0); 530 } 531 532 if (mname[0] == 0) { 533 return (libdiskmgt_str_eq(dp->secondary_name, NULL)); 534 } else { 535 return (libdiskmgt_str_eq(dp->secondary_name, mname)); 536 } 537 } 538 539 /* 540 * We could verify the partition is still there but this is kind of 541 * expensive and other code down the line will do that (e.g. see 542 * get_attrs). 543 */ 544 545 return (1); 546 } 547 548 /* 549 * Return 1 if partition has slices, 0 if not. 550 */ 551 static int 552 has_slices(descriptor_t *desc, int *errp) 553 { 554 int pnum; 555 int i; 556 char *p; 557 struct ipart iparts[FD_NUMPART]; 558 559 if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) { 560 *errp = ENODEV; 561 return (0); 562 } 563 564 p = strrchr(desc->name, 'p'); 565 if (p == NULL) { 566 p = desc->name; 567 } else { 568 p++; 569 } 570 pnum = atoi(p); 571 572 /* 573 * Slices are associated with the active solaris partition or if there 574 * is no active solaris partition, then the first solaris partition. 575 */ 576 577 *errp = 0; 578 if (iparts[pnum].bootid == ACTIVE && 579 (iparts[pnum].systid == SUNIXOS || 580 iparts[pnum].systid == SUNIXOS2)) { 581 return (1); 582 } else { 583 int active = 0; 584 585 /* Check if there are no active solaris partitions. */ 586 for (i = 0; i < FD_NUMPART; i++) { 587 if (iparts[i].bootid == ACTIVE && 588 (iparts[i].systid == SUNIXOS || 589 iparts[i].systid == SUNIXOS2)) { 590 active = 1; 591 break; 592 } 593 } 594 595 if (!active) { 596 /* Check if this is the first solaris partition. */ 597 for (i = 0; i < FD_NUMPART; i++) { 598 if (iparts[i].systid == SUNIXOS || 599 iparts[i].systid == SUNIXOS2) { 600 break; 601 } 602 } 603 604 if (i < FD_NUMPART && i == pnum) { 605 return (1); 606 } 607 } 608 } 609 610 return (0); 611 } 612 613 static int 614 open_disk(disk_t *diskp, char *opath, int len) 615 { 616 char rmmedia_devpath[MAXPATHLEN]; 617 618 if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath, 619 sizeof (rmmedia_devpath))) { 620 621 int fd; 622 struct stat buf; 623 624 if (rmmedia_devpath[0] == 0) { 625 /* removable but no media */ 626 return (-1); 627 } 628 629 if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) { 630 return (-1); 631 } 632 633 if (fstat(fd, &buf) != 0) { 634 (void) close(fd); 635 return (-1); 636 } 637 638 if (buf.st_mode & S_IFCHR) { 639 /* opened, is device, so done */ 640 if (opath != NULL) { 641 (void) strlcpy(opath, rmmedia_devpath, len); 642 } 643 return (fd); 644 645 } else if (buf.st_mode & S_IFDIR) { 646 /* disk w/ slices so handle the directory */ 647 DIR *dirp; 648 struct dirent *dentp; 649 int dfd; 650 #ifdef _LP64 651 struct dirent *result; 652 #endif 653 654 /* each device file in the dir represents a slice */ 655 656 if ((dirp = fdopendir(fd)) == NULL) { 657 (void) close(fd); 658 return (-1); 659 } 660 661 if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) + 662 _PC_NAME_MAX + 1)) == NULL) { 663 /* out of memory */ 664 (void) close(fd); 665 return (-1); 666 } 667 #ifdef _LP64 668 while (readdir_r(dirp, dentp, &result) != NULL) { 669 #else 670 while (readdir_r(dirp, dentp) != NULL) { 671 #endif 672 char slice_path[MAXPATHLEN]; 673 674 if (libdiskmgt_str_eq(".", dentp->d_name) || 675 libdiskmgt_str_eq("..", dentp->d_name)) { 676 continue; 677 } 678 679 (void) snprintf(slice_path, sizeof (slice_path), "%s/%s", 680 rmmedia_devpath, dentp->d_name); 681 682 if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) { 683 continue; 684 } 685 686 if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) { 687 /* opened, is device, so done */ 688 free(dentp); 689 (void) close(fd); 690 if (opath != NULL) { 691 (void) strlcpy(opath, slice_path, len); 692 } 693 return (dfd); 694 } 695 696 /* not a device, keep looking */ 697 (void) close(dfd); 698 } 699 700 /* did not find a device under the rmmedia_path */ 701 free(dentp); 702 (void) close(fd); 703 } 704 705 /* didn't find a device under volume management control */ 706 return (-1); 707 } 708 709 /* 710 * Not removable media under volume management control so just open the 711 * first devpath. 712 */ 713 if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) { 714 #ifdef sparc 715 if (opath != NULL) { 716 (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len); 717 } 718 return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY)); 719 #else 720 /* On intel we need to open partition device (e.g. c0d0p0). */ 721 char part_dev[MAXPATHLEN]; 722 char *p; 723 724 (void) strlcpy(part_dev, diskp->aliases->devpaths->devpath, 725 sizeof (part_dev)); 726 p = strrchr(part_dev, '/'); 727 if (p == NULL) { 728 p = strrchr(part_dev, 's'); 729 if (p != NULL) { 730 *p = 'p'; 731 } 732 } else { 733 char *ps; 734 735 *p = 0; 736 ps = strrchr((p + 1), 's'); 737 if (ps != NULL) { 738 *ps = 'p'; 739 } 740 *p = '/'; 741 } 742 743 if (opath != NULL) { 744 (void) strlcpy(opath, part_dev, len); 745 } 746 return (open(part_dev, O_RDONLY|O_NDELAY)); 747 #endif 748 } 749 750 return (-1); 751 } 752