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