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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <strings.h> 30 #include <unistd.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <ctype.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/systeminfo.h> 38 #include <sys/efi_partition.h> 39 #include <sys/byteorder.h> 40 41 #include <sys/vtoc.h> 42 #include <sys/tty.h> 43 #include <sys/dktp/fdisk.h> 44 #include <sys/dkio.h> 45 #include <sys/mnttab.h> 46 #include "libfdisk.h" 47 48 #define DEFAULT_PATH_PREFIX "/dev/rdsk/" 49 50 static void fdisk_free_ld_nodes(ext_part_t *epp); 51 static void fdisk_ext_place_in_sorted_list(ext_part_t *epp, 52 logical_drive_t *newld); 53 static void fdisk_ext_remove_from_sorted_list(ext_part_t *epp, 54 logical_drive_t *delld); 55 static int fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec, 56 uint32_t endsec); 57 static int fdisk_read_extpart(ext_part_t *epp); 58 static void fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part); 59 static int fdisk_init_master_part_table(ext_part_t *epp); 60 static struct ipart *fdisk_alloc_part_table(); 61 static int fdisk_read_master_part_table(ext_part_t *epp); 62 63 static int 64 fdisk_init_disk_geom(ext_part_t *epp) 65 { 66 struct dk_geom disk_geom; 67 struct dk_minfo disk_info; 68 int no_virtgeom_ioctl = 0, no_physgeom_ioctl = 0; 69 70 /* Get disk's HBA (virtual) geometry */ 71 errno = 0; 72 if (ioctl(epp->dev_fd, DKIOCG_VIRTGEOM, &disk_geom)) { 73 if (errno == ENOTTY) { 74 no_virtgeom_ioctl = 1; 75 } else if (errno == EINVAL) { 76 /* 77 * This means that the ioctl exists, but 78 * is invalid for this disk, meaning the 79 * disk doesn't have an HBA geometry 80 * (like, say, it's larger than 8GB). 81 */ 82 epp->disk_geom.virt_cyl = epp->disk_geom.virt_heads = 83 epp->disk_geom.virt_sec = 0; 84 } else { 85 return (FDISK_ENOVGEOM); 86 } 87 } else { 88 /* save virtual geometry values obtained by ioctl */ 89 epp->disk_geom.virt_cyl = disk_geom.dkg_ncyl; 90 epp->disk_geom.virt_heads = disk_geom.dkg_nhead; 91 epp->disk_geom.virt_sec = disk_geom.dkg_nsect; 92 } 93 94 errno = 0; 95 if (ioctl(epp->dev_fd, DKIOCG_PHYGEOM, &disk_geom)) { 96 if (errno == ENOTTY) { 97 no_physgeom_ioctl = 1; 98 } else { 99 return (FDISK_ENOPGEOM); 100 } 101 } 102 /* 103 * Call DKIOCGGEOM if the ioctls for physical and virtual 104 * geometry fail. Get both from this generic call. 105 */ 106 if (no_virtgeom_ioctl && no_physgeom_ioctl) { 107 errno = 0; 108 if (ioctl(epp->dev_fd, DKIOCGGEOM, &disk_geom)) { 109 return (FDISK_ENOLGEOM); 110 } 111 } 112 113 epp->disk_geom.phys_cyl = disk_geom.dkg_ncyl; 114 epp->disk_geom.phys_heads = disk_geom.dkg_nhead; 115 epp->disk_geom.phys_sec = disk_geom.dkg_nsect; 116 epp->disk_geom.alt_cyl = disk_geom.dkg_acyl; 117 118 /* 119 * If DKIOCGMEDIAINFO ioctl succeeds, set the dki_lbsize as the 120 * size of the sector, else default to 512 121 */ 122 if (ioctl(epp->dev_fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) < 0) { 123 /* ioctl failed, falling back to default value of 512 bytes */ 124 epp->disk_geom.sectsize = 512; 125 } else { 126 epp->disk_geom.sectsize = ((disk_info.dki_lbsize) ? 127 disk_info.dki_lbsize : 512); 128 } 129 130 /* 131 * if hba geometry was not set by DKIOC_VIRTGEOM 132 * or we got an invalid hba geometry 133 * then set hba geometry based on max values 134 */ 135 if (no_virtgeom_ioctl || disk_geom.dkg_ncyl == 0 || 136 disk_geom.dkg_nhead == 0 || disk_geom.dkg_nsect == 0 || 137 disk_geom.dkg_ncyl > MAX_CYL || disk_geom.dkg_nhead > MAX_HEAD || 138 disk_geom.dkg_nsect > MAX_SECT) { 139 epp->disk_geom.virt_sec = MAX_SECT; 140 epp->disk_geom.virt_heads = MAX_HEAD + 1; 141 epp->disk_geom.virt_cyl = (epp->disk_geom.phys_cyl * 142 epp->disk_geom.phys_heads * epp->disk_geom.phys_sec) / 143 (epp->disk_geom.virt_sec * epp->disk_geom.virt_heads); 144 } 145 return (FDISK_SUCCESS); 146 } 147 148 /* 149 * Initialise important members of the ext_part_t structure and 150 * other data structures vital to functionality of libfdisk 151 */ 152 int 153 libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab, int opflag) 154 { 155 ext_part_t *temp; 156 struct stat sbuf; 157 int rval = FDISK_SUCCESS; 158 159 if ((temp = calloc(1, sizeof (ext_part_t))) == NULL) { 160 return (ENOMEM); 161 } 162 163 (void) strncpy(temp->device_name, devstr, 164 sizeof (temp->device_name)); 165 166 /* Try to stat the node as provided */ 167 if (stat(temp->device_name, &sbuf) != 0) { 168 169 /* Prefix /dev/rdsk/ and stat again */ 170 (void) snprintf(temp->device_name, sizeof (temp->device_name), 171 "%s%s", DEFAULT_PATH_PREFIX, devstr); 172 173 if (stat(temp->device_name, &sbuf) != 0) { 174 175 /* 176 * In case of an EFI labeled disk, the device name 177 * could be cN[tN]dN. There is no pN. So we add "p0" 178 * at the end if we do not find it and stat again. 179 */ 180 if (strrchr(temp->device_name, 'p') == NULL) { 181 (void) strcat(temp->device_name, "p0"); 182 } 183 184 if (stat(temp->device_name, &sbuf) != 0) { 185 186 /* Failed all options, give up */ 187 free(temp); 188 return (EINVAL); 189 } 190 } 191 } 192 193 /* Make sure the device is a raw device */ 194 if ((sbuf.st_mode & S_IFMT) != S_IFCHR) { 195 return (EINVAL); 196 } 197 198 temp->ld_head = NULL; 199 temp->sorted_ld_head = NULL; 200 201 if ((temp->dev_fd = open(temp->device_name, O_RDWR, 0666)) < 0) { 202 free(temp); 203 return (EINVAL); 204 } 205 206 if ((temp->mtable = parttab) == NULL) { 207 if ((rval = fdisk_init_master_part_table(temp)) != 208 FDISK_SUCCESS) { 209 return (rval); 210 } 211 } 212 213 temp->op_flag = opflag; 214 215 if ((rval = fdisk_init_disk_geom(temp)) != FDISK_SUCCESS) { 216 return (rval); 217 } 218 219 *epp = temp; 220 221 if (opflag & FDISK_READ_DISK) { 222 rval = fdisk_read_extpart(*epp); 223 } 224 return (rval); 225 } 226 227 int 228 libfdisk_reset(ext_part_t *epp) 229 { 230 int rval = FDISK_SUCCESS; 231 232 fdisk_free_ld_nodes(epp); 233 epp->first_ebr_is_null = 1; 234 epp->corrupt_logical_drives = 0; 235 epp->logical_drive_count = 0; 236 epp->invalid_bb_sig[0] = 0; 237 if (epp->op_flag & FDISK_READ_DISK) { 238 rval = fdisk_read_extpart(epp); 239 } 240 return (rval); 241 } 242 243 void 244 libfdisk_fini(ext_part_t **epp) 245 { 246 fdisk_free_ld_nodes(*epp); 247 (void) close((*epp)->dev_fd); 248 free(*epp); 249 *epp = NULL; 250 } 251 252 int 253 fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, uint64_t *lsm_offset) 254 { 255 int i; 256 int rval = -1; 257 off_t seek_offset; 258 uint32_t linux_pg_size; 259 char *buf, *linux_swap_magic; 260 int sec_sz = fdisk_get_disk_geom(epp, PHYSGEOM, SSIZE); 261 off_t label_offset; 262 263 /* 264 * Known linux kernel page sizes 265 * The linux swap magic is found as the last 10 bytes of a disk chunk 266 * at the beginning of the linux swap partition whose size is that of 267 * kernel page size. 268 */ 269 uint32_t linux_pg_size_arr[] = {4096, }; 270 271 if ((buf = calloc(1, sec_sz)) == NULL) { 272 return (ENOMEM); 273 } 274 275 /* 276 * Check if there is a sane Solaris VTOC 277 * If there is a valid vtoc, no need to lookup 278 * for the linux swap signature. 279 */ 280 label_offset = (part_start + DK_LABEL_LOC) * sec_sz; 281 if ((rval = lseek(epp->dev_fd, label_offset, SEEK_SET)) < 0) 282 goto done; 283 284 if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) { 285 rval = EIO; 286 goto done; 287 } 288 289 290 if ((((struct dk_label *)buf)->dkl_magic == DKL_MAGIC) && 291 (((struct dk_label *)buf)->dkl_vtoc.v_sanity == VTOC_SANE)) { 292 rval = -1; 293 goto done; 294 } 295 296 /* No valid vtoc, so check for linux swap signature */ 297 linux_swap_magic = buf + sec_sz - LINUX_SWAP_MAGIC_LENGTH; 298 299 for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) { 300 linux_pg_size = linux_pg_size_arr[i]; 301 seek_offset = linux_pg_size/sec_sz - 1; 302 seek_offset += part_start; 303 seek_offset *= sec_sz; 304 305 if ((rval = lseek(epp->dev_fd, seek_offset, SEEK_SET)) < 0) { 306 break; 307 } 308 309 if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) { 310 rval = EIO; 311 break; 312 } 313 314 if ((strncmp(linux_swap_magic, "SWAP-SPACE", 315 LINUX_SWAP_MAGIC_LENGTH) == 0) || 316 (strncmp(linux_swap_magic, "SWAPSPACE2", 317 LINUX_SWAP_MAGIC_LENGTH) == 0)) { 318 /* Found a linux swap */ 319 rval = 0; 320 if (lsm_offset != NULL) 321 *lsm_offset = (uint64_t)seek_offset; 322 break; 323 } 324 } 325 326 done: 327 free(buf); 328 return (rval); 329 } 330 331 int 332 fdisk_get_solaris_part(ext_part_t *epp, int *pnum, uint32_t *begsec, 333 uint32_t *numsec) 334 { 335 logical_drive_t *temp = fdisk_get_ld_head(epp); 336 uint32_t part_start; 337 int pno; 338 int rval = -1; 339 340 for (pno = 5; temp != NULL; temp = temp->next, pno++) { 341 if (fdisk_is_solaris_part(LE_8(temp->parts[0].systid))) { 342 part_start = temp->abs_secnum + temp->logdrive_offset; 343 if ((temp->parts[0].systid == SUNIXOS) && 344 (fdisk_is_linux_swap(epp, part_start, 345 NULL) == 0)) { 346 continue; 347 } 348 *pnum = pno; 349 *begsec = part_start; 350 *numsec = temp->numsect; 351 rval = FDISK_SUCCESS; 352 } 353 } 354 return (rval); 355 } 356 357 int 358 fdisk_get_part_info(ext_part_t *epp, int pnum, uchar_t *sysid, uint32_t *begsec, 359 uint32_t *numsec) 360 { 361 logical_drive_t *temp = fdisk_get_ld_head(epp); 362 int pno; 363 364 if ((pnum < 5) || (pnum >= MAX_EXT_PARTS + 5)) { 365 return (EINVAL); 366 } 367 368 for (pno = 5; (pno < pnum) && (temp != NULL); temp = temp->next, pno++) 369 ; 370 371 if (temp == NULL) { 372 return (EINVAL); 373 } 374 375 *sysid = LE_8(temp->parts[0].systid); 376 *begsec = temp->abs_secnum + temp->logdrive_offset; 377 *numsec = temp->numsect; 378 return (FDISK_SUCCESS); 379 } 380 381 /* 382 * Allocate a node of type logical_drive_t and return the pointer to it 383 */ 384 static logical_drive_t * 385 fdisk_alloc_ld_node() 386 { 387 logical_drive_t *temp; 388 389 if ((temp = calloc(1, sizeof (logical_drive_t))) == NULL) { 390 return (NULL); 391 } 392 temp->next = NULL; 393 return (temp); 394 } 395 396 /* 397 * Free all the logical_drive_t's allocated during the run 398 */ 399 static void 400 fdisk_free_ld_nodes(ext_part_t *epp) 401 { 402 logical_drive_t *temp; 403 404 for (temp = epp->ld_head; temp != NULL; ) { 405 temp = epp->ld_head -> next; 406 free(epp->ld_head); 407 epp->ld_head = temp; 408 } 409 epp->ld_head = NULL; 410 epp->sorted_ld_head = NULL; 411 } 412 413 /* 414 * Find the first free sector within the extended partition 415 */ 416 int 417 fdisk_ext_find_first_free_sec(ext_part_t *epp, uint32_t *first_free_sec) 418 { 419 logical_drive_t *temp; 420 uint32_t last_free_sec; 421 422 *first_free_sec = epp->ext_beg_sec; 423 424 if (epp->ld_head == NULL) { 425 return (FDISK_SUCCESS); 426 } 427 428 /* 429 * When the first logical drive is out of order, we need to adjust 430 * first_free_sec accordingly. In this case, the first extended 431 * partition sector is not free even though the actual logical drive 432 * does not occupy space from the beginning of the extended partition. 433 * The next free sector would be the second sector of the extended 434 * partition. 435 */ 436 if (epp->ld_head->abs_secnum > epp->ext_beg_sec + 437 MAX_LOGDRIVE_OFFSET) { 438 (*first_free_sec)++; 439 } 440 441 while (*first_free_sec <= epp->ext_end_sec) { 442 for (temp = epp->sorted_ld_head; temp != NULL; temp = 443 temp->sorted_next) { 444 if (temp->abs_secnum == *first_free_sec) { 445 *first_free_sec = temp->abs_secnum + 446 temp->logdrive_offset + temp->numsect; 447 } 448 } 449 450 last_free_sec = fdisk_ext_find_last_free_sec(epp, 451 *first_free_sec); 452 453 if ((last_free_sec - *first_free_sec) < MAX_LOGDRIVE_OFFSET) { 454 /* 455 * Minimum size of a partition assumed to be atleast one 456 * sector. 457 */ 458 *first_free_sec = last_free_sec + 1; 459 continue; 460 } 461 462 break; 463 } 464 465 if (*first_free_sec > epp->ext_end_sec) { 466 return (FDISK_EOOBOUND); 467 } 468 469 return (FDISK_SUCCESS); 470 } 471 472 /* 473 * Find the last free sector within the extended partition given, a beginning 474 * sector (so that the range - "begsec to last_free_sec" is contiguous) 475 */ 476 uint32_t 477 fdisk_ext_find_last_free_sec(ext_part_t *epp, uint32_t begsec) 478 { 479 logical_drive_t *temp; 480 uint32_t last_free_sec; 481 482 last_free_sec = epp->ext_end_sec; 483 for (temp = epp->sorted_ld_head; temp != NULL; 484 temp = temp->sorted_next) { 485 if (temp->abs_secnum > begsec) { 486 last_free_sec = temp->abs_secnum - 1; 487 break; 488 } 489 } 490 return (last_free_sec); 491 } 492 493 /* 494 * Place the given ext_part_t structure in a sorted list, sorted in the 495 * ascending order of their beginning sectors. 496 */ 497 static void 498 fdisk_ext_place_in_sorted_list(ext_part_t *epp, logical_drive_t *newld) 499 { 500 logical_drive_t *pre, *cur; 501 502 if (newld->abs_secnum < epp->sorted_ld_head->abs_secnum) { 503 newld->sorted_next = epp->sorted_ld_head; 504 epp->sorted_ld_head = newld; 505 return; 506 } 507 pre = cur = epp->sorted_ld_head; 508 509 for (; cur != NULL; pre = cur, cur = cur->sorted_next) { 510 if (newld->abs_secnum < cur->abs_secnum) { 511 break; 512 } 513 } 514 515 newld->sorted_next = cur; 516 pre->sorted_next = newld; 517 } 518 519 static void 520 fdisk_ext_remove_from_sorted_list(ext_part_t *epp, logical_drive_t *delld) 521 { 522 logical_drive_t *pre, *cur; 523 524 if (delld == epp->sorted_ld_head) { 525 epp->sorted_ld_head = delld->sorted_next; 526 return; 527 } 528 529 pre = cur = epp->sorted_ld_head; 530 531 for (; cur != NULL; pre = cur, cur = cur->sorted_next) { 532 if (cur->abs_secnum == delld->abs_secnum) { 533 /* Found */ 534 break; 535 } 536 } 537 538 pre->sorted_next = cur->sorted_next; 539 } 540 541 static int 542 fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec, uint32_t endsec) 543 { 544 logical_drive_t *temp; 545 uint32_t firstsec, lastsec, last_free_sec; 546 547 for (temp = epp->ld_head; temp != NULL; temp = temp->next) { 548 firstsec = temp->abs_secnum; 549 lastsec = firstsec + temp->logdrive_offset + temp->numsect - 1; 550 if ((begsec >= firstsec) && 551 (begsec <= lastsec)) { 552 return (1); 553 } 554 } 555 556 /* 557 * Find the maximum possible end sector value 558 * given a beginning sector value 559 */ 560 last_free_sec = fdisk_ext_find_last_free_sec(epp, begsec); 561 562 if (endsec > last_free_sec) { 563 return (1); 564 } 565 return (0); 566 } 567 568 /* 569 * Check if the logical drive boundaries are sane 570 */ 571 int 572 fdisk_validate_logical_drive(ext_part_t *epp, uint32_t begsec, 573 uint32_t offset, uint32_t numsec) 574 { 575 uint32_t endsec; 576 577 endsec = begsec + offset + numsec - 1; 578 if (begsec < epp->ext_beg_sec || 579 begsec > epp->ext_end_sec || 580 endsec < epp->ext_beg_sec || 581 endsec > epp->ext_end_sec || 582 endsec < begsec || 583 fdisk_ext_overlapping_parts(epp, begsec, endsec)) { 584 return (1); 585 } 586 587 return (0); 588 } 589 590 /* 591 * Procedure to walk through the extended partitions and build a Singly 592 * Linked List out of the data. 593 */ 594 int 595 fdisk_read_extpart(ext_part_t *epp) 596 { 597 struct ipart *fdp, *ext_fdp; 598 int i = 0, j = 0, ext_part_found = 0, lpart = 5; 599 off_t secnum, offset; 600 logical_drive_t *temp, *ep_ptr; 601 unsigned char *ext_buf; 602 int sectsize = epp->disk_geom.sectsize; 603 604 if ((ext_buf = (uchar_t *)malloc(sectsize)) == NULL) { 605 return (ENOMEM); 606 } 607 fdp = epp->mtable; 608 609 for (i = 0; (i < FD_NUMPART) && (!ext_part_found); i++, fdp++) { 610 if (fdisk_is_dos_extended(LE_8(fdp->systid))) { 611 ext_part_found = 1; 612 secnum = LE_32(fdp->relsect); 613 offset = secnum * sectsize; 614 epp->ext_beg_sec = secnum; 615 epp->ext_end_sec = secnum + LE_32(fdp->numsect) - 1; 616 epp->ext_beg_cyl = 617 FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec); 618 epp->ext_end_cyl = 619 FDISK_SECT_TO_CYL(epp, epp->ext_end_sec); 620 621 /*LINTED*/ 622 while (B_TRUE) { 623 if (lseek(epp->dev_fd, offset, SEEK_SET) < 0) { 624 return (EIO); 625 } 626 if (read(epp->dev_fd, ext_buf, sectsize) < 627 sectsize) { 628 return (EIO); 629 } 630 /*LINTED*/ 631 ext_fdp = (struct ipart *) 632 (&ext_buf[FDISK_PART_TABLE_START]); 633 if ((LE_32(ext_fdp->relsect) == 0) && 634 (epp->logical_drive_count == 0)) { 635 /* No logical drives defined */ 636 epp->first_ebr_is_null = 0; 637 return (FDISK_ENOLOGDRIVE); 638 } 639 640 temp = fdisk_alloc_ld_node(); 641 temp->abs_secnum = secnum; 642 temp->logdrive_offset = 643 LE_32(ext_fdp->relsect); 644 temp ->numsect = LE_32(ext_fdp->numsect); 645 if (epp->ld_head == NULL) { 646 /* adding first logical drive */ 647 if (temp->logdrive_offset > 648 MAX_LOGDRIVE_OFFSET) { 649 /* out of order */ 650 temp->abs_secnum += 651 temp->logdrive_offset; 652 temp->logdrive_offset = 0; 653 } 654 } 655 temp->begcyl = 656 FDISK_SECT_TO_CYL(epp, temp->abs_secnum); 657 temp->endcyl = FDISK_SECT_TO_CYL(epp, 658 temp->abs_secnum + 659 temp->logdrive_offset + 660 temp->numsect - 1); 661 662 /* 663 * Check for sanity of logical drives 664 */ 665 if (fdisk_validate_logical_drive(epp, 666 temp->abs_secnum, temp->logdrive_offset, 667 temp->numsect)) { 668 epp->corrupt_logical_drives = 1; 669 free(temp); 670 return (FDISK_EBADLOGDRIVE); 671 } 672 673 temp->parts[0] = *ext_fdp; 674 ext_fdp++; 675 temp->parts[1] = *ext_fdp; 676 677 if (epp->ld_head == NULL) { 678 epp->ld_head = temp; 679 epp->sorted_ld_head = temp; 680 ep_ptr = temp; 681 epp->logical_drive_count = 1; 682 } else { 683 ep_ptr->next = temp; 684 ep_ptr = temp; 685 fdisk_ext_place_in_sorted_list(epp, 686 temp); 687 epp->logical_drive_count++; 688 } 689 690 /*LINTED*/ 691 if (LE_16((*(uint16_t *)&ext_buf[510])) != 692 MBB_MAGIC) { 693 epp->invalid_bb_sig[j++] = lpart; 694 temp->modified = FDISK_MINOR_WRITE; 695 } 696 697 if (LE_32(ext_fdp->relsect) == 0) 698 break; 699 else { 700 secnum = LE_32(fdp->relsect) + 701 LE_32(ext_fdp->relsect); 702 offset = secnum * sectsize; 703 } 704 lpart++; 705 } 706 } 707 } 708 return (FDISK_SUCCESS); 709 } 710 711 static int 712 fdisk_init_master_part_table(ext_part_t *epp) 713 { 714 int rval; 715 if ((epp->mtable = fdisk_alloc_part_table()) == NULL) { 716 return (ENOMEM); 717 } 718 rval = fdisk_read_master_part_table(epp); 719 if (rval) { 720 return (rval); 721 } 722 return (FDISK_SUCCESS); 723 } 724 725 static struct ipart * 726 fdisk_alloc_part_table() 727 { 728 int size = sizeof (struct ipart); 729 struct ipart *table; 730 731 if ((table = calloc(4, size)) == NULL) { 732 return (NULL); 733 } 734 735 return (table); 736 } 737 738 /* 739 * Reads the master fdisk partition table from the device assuming that it has 740 * a valid table. 741 * MBR is supposed to be of 512 bytes no matter what the device block size is. 742 */ 743 static int 744 fdisk_read_master_part_table(ext_part_t *epp) 745 { 746 uchar_t buf[512]; 747 int sectsize = 512; 748 int size = sizeof (struct ipart); 749 int cpcnt = FD_NUMPART * size; 750 751 if (lseek(epp->dev_fd, 0, SEEK_SET) < 0) { 752 return (EIO); 753 } 754 if (read(epp->dev_fd, buf, sectsize) < sectsize) { 755 return (EIO); 756 } 757 bcopy(&buf[FDISK_PART_TABLE_START], epp->mtable, cpcnt); 758 759 /*LINTED*/ 760 if (LE_16((*(uint16_t *)&buf[510])) != MBB_MAGIC) { 761 return (FDISK_EBADMAGIC); 762 } 763 764 return (FDISK_SUCCESS); 765 } 766 767 int 768 fdisk_ext_part_exists(ext_part_t *epp) 769 { 770 int i; 771 struct ipart *part_table = epp->mtable; 772 773 if (part_table == NULL) { 774 /* No extended partition found */ 775 return (0); 776 } 777 778 for (i = 0; i < FD_NUMPART; i++) { 779 if (fdisk_is_dos_extended(LE_8(part_table[i].systid))) { 780 break; 781 } 782 } 783 784 if (i == FD_NUMPART) { 785 /* No extended partition found */ 786 return (0); 787 } 788 return (1); 789 } 790 791 int 792 fdisk_ext_validate_part_start(ext_part_t *epp, uint32_t begcyl, 793 uint32_t *begsec) 794 { 795 logical_drive_t *temp; 796 uint32_t first_free_sec; 797 uint32_t first_free_cyl; 798 int rval; 799 800 rval = fdisk_ext_find_first_free_sec(epp, &first_free_sec); 801 if (rval != FDISK_SUCCESS) { 802 return (rval); 803 } 804 805 first_free_cyl = FDISK_SECT_TO_CYL(epp, first_free_sec); 806 if (begcyl == first_free_cyl) { 807 *begsec = first_free_sec; 808 return (FDISK_SUCCESS); 809 } 810 811 /* Check if the cylinder number is beyond the extended partition */ 812 if ((begcyl < epp->ext_beg_cyl) || (begcyl > epp->ext_end_cyl)) { 813 return (FDISK_EOOBOUND); 814 } 815 816 for (temp = epp->ld_head; temp != NULL; temp = temp->next) { 817 if ((begcyl >= temp->begcyl) && 818 (begcyl <= temp->endcyl)) { 819 return (FDISK_EOVERLAP); 820 } 821 } 822 *begsec = FDISK_CYL_TO_SECT(epp, begcyl); 823 824 return (FDISK_SUCCESS); 825 } 826 827 void 828 fdisk_change_logical_drive_id(ext_part_t *epp, int pno, uchar_t partid) 829 { 830 logical_drive_t *temp; 831 int i; 832 833 i = FD_NUMPART + 1; 834 for (temp = epp->ld_head; i < pno; temp = temp->next, i++) 835 ; 836 837 temp->parts[0].systid = LE_8(partid); 838 temp->modified = FDISK_MAJOR_WRITE; 839 } 840 841 /* 842 * A couple of special scenarios : 843 * 1. Since the first logical drive's EBR is always at the beginning of the 844 * extended partition, any specification that starts the first logical drive 845 * out of order will need to address the following issue : 846 * If the beginning of the drive is not coinciding with the beginning of the 847 * extended partition and : 848 * a) The start is within MAX_LOGDRIVE_OFFSET, the offset changes from the 849 * default of 63 to less than 63. 850 * logdrive_offset is updated to keep track of the space between 851 * the beginning of the logical drive and extended partition. abs_secnum 852 * points to the beginning of the extended partition. 853 * b) The start is greater than MAX_LOGDRIVE_OFFSET, the offset changes from 854 * the default of 63 to greater than 63. 855 * logdrive_offset is set to 0. abs_secnum points to the beginning of the 856 * logical drive, which is at an offset from the extended partition. 857 */ 858 void 859 fdisk_add_logical_drive(ext_part_t *epp, uint32_t begsec, uint32_t endsec, 860 uchar_t partid) 861 { 862 logical_drive_t *temp, *pre, *cur; 863 struct ipart *part; 864 865 temp = fdisk_alloc_ld_node(); 866 temp->abs_secnum = begsec; 867 temp->logdrive_offset = MAX_LOGDRIVE_OFFSET; 868 temp->numsect = endsec - begsec + 1 - MAX_LOGDRIVE_OFFSET; 869 temp->begcyl = FDISK_SECT_TO_CYL(epp, begsec); 870 temp->endcyl = FDISK_SECT_TO_CYL(epp, endsec); 871 temp->modified = FDISK_MAJOR_WRITE; 872 873 part = &temp->parts[0]; 874 part->bootid = 0; 875 part->systid = LE_8(partid); 876 part->relsect = MAX_LOGDRIVE_OFFSET; 877 part->numsect = LE_32(temp->numsect); 878 879 fdisk_set_CHS_values(epp, part); 880 881 if (epp->ld_head == NULL) { 882 epp->corrupt_logical_drives = 0; 883 if (begsec != epp->ext_beg_sec) { 884 part->relsect = LE_32(begsec - epp->ext_beg_sec); 885 temp->numsect = endsec - begsec + 1; 886 part->numsect = LE_32(temp->numsect); 887 if (LE_32(part->relsect) > MAX_LOGDRIVE_OFFSET) { 888 temp->logdrive_offset = 0; 889 } else { 890 temp->abs_secnum = epp->ext_beg_sec; 891 temp->logdrive_offset = LE_32(part->relsect); 892 } 893 } 894 epp->first_ebr_is_null = 0; 895 epp->ld_head = temp; 896 epp->sorted_ld_head = temp; 897 epp->logical_drive_count = 1; 898 return; 899 } 900 901 if (temp->abs_secnum == epp->ext_beg_sec) { 902 part->relsect = LE_32(LE_32(part->relsect) - 1); 903 temp->logdrive_offset--; 904 temp->abs_secnum++; 905 } 906 907 for (pre = cur = epp->ld_head; cur != NULL; pre = cur, cur = cur->next) 908 ; 909 910 part = &pre->parts[1]; 911 part->bootid = 0; 912 part->systid = LE_8(EXTDOS); 913 part->relsect = LE_32(temp->abs_secnum - epp->ext_beg_sec); 914 part->numsect = LE_32(temp->numsect + temp->logdrive_offset); 915 916 fdisk_set_CHS_values(epp, part); 917 918 pre->next = temp; 919 pre->modified = FDISK_MAJOR_WRITE; 920 epp->logical_drive_count++; 921 fdisk_ext_place_in_sorted_list(epp, temp); 922 } 923 924 /* 925 * There are 2 cases that need to be handled. 926 * 1. Deleting the first extended partition : 927 * The peculiarity of this case is that the offset of the first extended 928 * partition is always indicated by the entry in the master boot record. 929 * (MBR). This never changes, unless the extended partition itself is 930 * deleted. Hence, the location of the first EBR is fixed. 931 * It is only the logical drive which is deleted. This first EBR now gives 932 * information of the next logical drive and the info about the subsequent 933 * extended partition. Hence the "relsect" of the first EBR is modified to 934 * point to the next logical drive. 935 * 936 * 2. Deleting an intermediate extended partition. 937 * This is quite normal and follows the semantics of a normal linked list 938 * delete operation. The node being deleted has the information about the 939 * logical drive that it houses and the location and the size of the next 940 * extended partition. This informationis transferred to the node previous 941 * to the node being deleted. 942 * 943 */ 944 945 void 946 fdisk_delete_logical_drive(ext_part_t *epp, int pno) 947 { 948 logical_drive_t *pre, *cur; 949 int i; 950 951 i = FD_NUMPART + 1; 952 pre = cur = epp->ld_head; 953 for (; i < pno; i++) { 954 pre = cur; 955 cur = cur->next; 956 } 957 958 if (cur == epp->ld_head) { 959 /* Deleting the first logical drive */ 960 if (cur->next == NULL) { 961 /* Deleting the only logical drive left */ 962 free(cur); 963 epp->ld_head = NULL; 964 epp->sorted_ld_head = NULL; 965 epp->logical_drive_count = 0; 966 epp->first_ebr_is_null = 1; 967 } else { 968 pre = epp->ld_head; 969 cur = pre->next; 970 cur->parts[0].relsect = 971 LE_32(LE_32(cur->parts[0].relsect) + 972 LE_32(pre->parts[1].relsect)); 973 /* Corner case when partitions are out of order */ 974 if ((pre->abs_secnum != epp->ext_beg_sec) && 975 (cur->abs_secnum == epp->ext_beg_sec + 1)) { 976 cur->logdrive_offset++; 977 cur->abs_secnum = epp->ext_beg_sec; 978 } else { 979 cur->abs_secnum = LE_32(cur->parts[0].relsect) + 980 epp->ext_beg_sec; 981 cur->logdrive_offset = 0; 982 } 983 fdisk_ext_remove_from_sorted_list(epp, pre); 984 epp->ld_head = cur; 985 epp->ld_head->modified = FDISK_MAJOR_WRITE; 986 epp->logical_drive_count--; 987 free(pre); 988 } 989 } else { 990 pre->parts[1] = cur->parts[1]; 991 pre->next = cur->next; 992 fdisk_ext_remove_from_sorted_list(epp, cur); 993 pre->modified = FDISK_MAJOR_WRITE; 994 free(cur); 995 epp->logical_drive_count--; 996 } 997 } 998 999 static void 1000 fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part) 1001 { 1002 uint32_t lba, cy, hd, sc; 1003 uint32_t sectors = epp->disk_geom.virt_sec; 1004 uint32_t heads = epp->disk_geom.virt_heads; 1005 1006 lba = LE_32(part->relsect) + epp->ext_beg_sec; 1007 if (lba >= heads * sectors * MAX_CYL) { 1008 /* 1009 * the lba address cannot be expressed in CHS value 1010 * so store the maximum CHS field values in the CHS fields. 1011 */ 1012 cy = MAX_CYL + 1; 1013 hd = MAX_HEAD; 1014 sc = MAX_SECT; 1015 } else { 1016 cy = lba / sectors / heads; 1017 hd = lba / sectors % heads; 1018 sc = lba % sectors + 1; 1019 } 1020 1021 part->begcyl = cy & 0xff; 1022 part->beghead = (uchar_t)hd; 1023 part->begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc); 1024 1025 /* 1026 * This code is identical to the code above 1027 * except that it works on ending CHS values 1028 */ 1029 lba += LE_32(part->numsect - 1); 1030 if (lba >= heads * sectors * MAX_CYL) { 1031 cy = MAX_CYL + 1; 1032 hd = MAX_HEAD; 1033 sc = MAX_SECT; 1034 } else { 1035 cy = lba / sectors / heads; 1036 hd = lba / sectors % heads; 1037 sc = lba % sectors + 1; 1038 } 1039 part->endcyl = cy & 0xff; 1040 part->endhead = (uchar_t)hd; 1041 part->endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc); 1042 } 1043 1044 static int 1045 read_modify_write_ebr(ext_part_t *epp, unsigned char *ebr_buf, 1046 struct ipart *ebr_tab, uint32_t sec_offset) 1047 { 1048 off_t seek_offset; 1049 int sectsize = epp->disk_geom.sectsize; 1050 1051 seek_offset = (off_t)sec_offset * sectsize; 1052 1053 if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) { 1054 return (EIO); 1055 } 1056 if (read(epp->dev_fd, ebr_buf, sectsize) < sectsize) { 1057 return (EIO); 1058 } 1059 1060 bzero(&ebr_buf[FDISK_PART_TABLE_START], 4 * sizeof (struct ipart)); 1061 if (ebr_tab != NULL) { 1062 bcopy(ebr_tab, &ebr_buf[FDISK_PART_TABLE_START], 1063 2 * sizeof (struct ipart)); 1064 } 1065 ebr_buf[510] = 0x55; 1066 ebr_buf[511] = 0xAA; 1067 if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) { 1068 return (EIO); 1069 } 1070 if (write(epp->dev_fd, ebr_buf, sectsize) < sectsize) { 1071 return (EIO); 1072 } 1073 return (0); 1074 } 1075 1076 /* 1077 * XXX - ZFS mounts not detected. Needs to come in as a feature. 1078 * Currently only /etc/mnttab entries are being checked 1079 */ 1080 int 1081 fdisk_mounted_logical_drives(ext_part_t *epp) 1082 { 1083 char *part_str, *canonp; 1084 char compare_pdev_str[PATH_MAX]; 1085 char compare_sdev_str[PATH_MAX]; 1086 FILE *fp; 1087 struct mnttab mt; 1088 int part; 1089 int look_for_mounted_slices = 0; 1090 uint32_t begsec, numsec; 1091 1092 /* 1093 * Do not check for mounted logical drives for 1094 * devices other than /dev/rdsk/ 1095 */ 1096 if (strstr(epp->device_name, DEFAULT_PATH_PREFIX) == NULL) { 1097 return (0); 1098 } 1099 1100 if ((fp = fopen(MNTTAB, "r")) == NULL) { 1101 return (ENOENT); 1102 } 1103 1104 canonp = epp->device_name + strlen(DEFAULT_PATH_PREFIX); 1105 (void) snprintf(compare_pdev_str, PATH_MAX, "%s%s", "/dev/dsk/", 1106 canonp); 1107 part_str = strrchr(compare_pdev_str, 'p'); 1108 *(part_str + 1) = '\0'; 1109 (void) strcpy(compare_sdev_str, compare_pdev_str); 1110 part_str = strrchr(compare_sdev_str, 'p'); 1111 *part_str = 's'; 1112 1113 if (fdisk_get_solaris_part(epp, &part, &begsec, &numsec) == 1114 FDISK_SUCCESS) { 1115 if (part > FD_NUMPART) { 1116 /* 1117 * Solaris partition is on a logical drive. Look for 1118 * mounted slices. 1119 */ 1120 look_for_mounted_slices = 1; 1121 } 1122 } 1123 1124 while (getmntent(fp, &mt) == 0) { 1125 if (strstr(mt.mnt_special, compare_pdev_str) == NULL) { 1126 if (strstr(mt.mnt_special, compare_sdev_str) == NULL) { 1127 continue; 1128 } else { 1129 if (look_for_mounted_slices) { 1130 return (FDISK_EMOUNTED); 1131 } 1132 } 1133 } 1134 1135 /* 1136 * Get the partition number that is mounted, which would be 1137 * found just beyond the last 'p' in the device string. 1138 * For example, in /dev/dsk/c0t0d0p12, partition number 12 1139 * is just beyond the last 'p'. 1140 */ 1141 part_str = strrchr(mt.mnt_special, 'p'); 1142 if (part_str != NULL) { 1143 part_str++; 1144 part = atoi(part_str); 1145 /* Extended partition numbers start from 5 */ 1146 if (part >= 5) { 1147 return (FDISK_EMOUNTED); 1148 } 1149 } 1150 } 1151 return (0); 1152 } 1153 1154 int 1155 fdisk_commit_ext_part(ext_part_t *epp) 1156 { 1157 logical_drive_t *temp; 1158 int wflag = 0; /* write flag */ 1159 int rval; 1160 int sectsize = epp->disk_geom.sectsize; 1161 unsigned char *ebr_buf; 1162 int ld_count; 1163 uint32_t abs_secnum; 1164 int check_mounts = 0; 1165 1166 if ((ebr_buf = (unsigned char *)malloc(sectsize)) == NULL) { 1167 return (ENOMEM); 1168 } 1169 1170 if (epp->first_ebr_is_null) { 1171 /* 1172 * Indicator that the extended partition as a whole was 1173 * modifies (either created or deleted. Must check for mounts 1174 * and must commit 1175 */ 1176 check_mounts = 1; 1177 } 1178 1179 /* 1180 * Pass1 through the logical drives to make sure that commit of minor 1181 * written block dont get held up due to mounts. 1182 */ 1183 for (temp = epp->ld_head; temp != NULL; temp = temp->next) { 1184 if (temp == epp->ld_head) { 1185 abs_secnum = epp->ext_beg_sec; 1186 } else { 1187 abs_secnum = temp->abs_secnum; 1188 } 1189 if (temp->modified == FDISK_MINOR_WRITE) { 1190 rval = read_modify_write_ebr(epp, ebr_buf, 1191 temp->parts, abs_secnum); 1192 if (rval) { 1193 goto error; 1194 } 1195 temp->modified = 0; 1196 } else if (temp->modified == FDISK_MAJOR_WRITE) { 1197 check_mounts = 1; 1198 } 1199 } 1200 1201 if (!check_mounts) { 1202 goto skip_check_mounts; 1203 } 1204 1205 if ((rval = fdisk_mounted_logical_drives(epp)) != 0) { 1206 /* One/more extended partitions are mounted */ 1207 if (ebr_buf) { 1208 free(ebr_buf); 1209 } 1210 return (rval); 1211 } 1212 1213 skip_check_mounts: 1214 1215 if (epp->first_ebr_is_null) { 1216 rval = read_modify_write_ebr(epp, ebr_buf, NULL, 1217 epp->ext_beg_sec); 1218 if (rval) { 1219 goto error; 1220 } 1221 wflag = 1; 1222 ld_count = 0; 1223 } else { 1224 if (epp->logical_drive_count == 0) { 1225 /* 1226 * Can hit this case when there is just an extended 1227 * partition with no logical drives, and the user 1228 * committed without making any changes 1229 * We dont have anything to commit. Return success 1230 */ 1231 if (ebr_buf) { 1232 free(ebr_buf); 1233 } 1234 return (FDISK_SUCCESS); 1235 } 1236 1237 /* 1238 * Make sure that the first EBR is written with the first 1239 * logical drive's data, which might not be the first in disk 1240 * order. 1241 */ 1242 for (temp = epp->ld_head, ld_count = 0; temp != NULL; 1243 temp = temp->next, ld_count++) { 1244 if (ld_count == 0) { 1245 abs_secnum = epp->ext_beg_sec; 1246 } else { 1247 abs_secnum = temp->abs_secnum; 1248 } 1249 if (temp->modified) { 1250 rval = read_modify_write_ebr(epp, ebr_buf, 1251 temp->parts, abs_secnum); 1252 if (rval) { 1253 if (ld_count) { 1254 /* 1255 * There was atleast one 1256 * write to the disk before 1257 * this failure. Make sure that 1258 * the kernel is notified. 1259 * Issue the ioctl. 1260 */ 1261 break; 1262 } 1263 goto error; 1264 } 1265 if ((!wflag) && (temp->modified == 1266 FDISK_MAJOR_WRITE)) { 1267 wflag = 1; 1268 } 1269 } 1270 } 1271 1272 if (wflag == 0) { 1273 /* No changes made */ 1274 rval = FDISK_SUCCESS; 1275 goto error; 1276 } 1277 } 1278 1279 /* Issue ioctl to the driver to update extended partition info */ 1280 rval = ioctl(epp->dev_fd, DKIOCSETEXTPART); 1281 1282 /* 1283 * Certain devices ex:lofi do not support DKIOCSETEXTPART. 1284 * Extended partitions are still created on these devices. 1285 */ 1286 if (errno == ENOTTY) 1287 rval = FDISK_SUCCESS; 1288 1289 error: 1290 if (ebr_buf) { 1291 free(ebr_buf); 1292 } 1293 return (rval); 1294 } 1295 1296 int 1297 fdisk_init_ext_part(ext_part_t *epp, uint32_t rsect, uint32_t nsect) 1298 { 1299 epp->first_ebr_is_null = 1; 1300 epp->corrupt_logical_drives = 0; 1301 epp->logical_drive_count = 0; 1302 epp->ext_beg_sec = rsect; 1303 epp->ext_end_sec = rsect + nsect - 1; 1304 epp->ext_beg_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec); 1305 epp->ext_end_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_end_sec); 1306 epp->invalid_bb_sig[0] = 0; 1307 return (0); 1308 } 1309 1310 int 1311 fdisk_delete_ext_part(ext_part_t *epp) 1312 { 1313 epp->first_ebr_is_null = 1; 1314 /* Clear the logical drive information */ 1315 fdisk_free_ld_nodes(epp); 1316 epp->logical_drive_count = 0; 1317 epp->corrupt_logical_drives = 0; 1318 epp->invalid_bb_sig[0] = 0; 1319 return (0); 1320 } 1321 1322 int 1323 fdisk_get_disk_geom(ext_part_t *epp, int type, int what) 1324 { 1325 switch (type) { 1326 case PHYSGEOM: 1327 switch (what) { 1328 case NCYL: 1329 return ((int)epp->disk_geom.phys_cyl); 1330 case NHEADS: 1331 return ((int)epp->disk_geom.phys_heads); 1332 case NSECTPT: 1333 return ((int)epp->disk_geom.phys_sec); 1334 case SSIZE: 1335 return ((int)epp->disk_geom.sectsize); 1336 case ACYL: 1337 return ((int)epp->disk_geom.alt_cyl); 1338 default: 1339 return (EINVAL); 1340 } 1341 case VIRTGEOM: 1342 switch (what) { 1343 case NCYL: 1344 return ((int)epp->disk_geom.virt_cyl); 1345 case NHEADS: 1346 return ((int)epp->disk_geom.virt_heads); 1347 case NSECTPT: 1348 return ((int)epp->disk_geom.virt_sec); 1349 case SSIZE: 1350 return ((int)epp->disk_geom.sectsize); 1351 case ACYL: 1352 return ((int)epp->disk_geom.alt_cyl); 1353 default: 1354 return (EINVAL); 1355 } 1356 default: 1357 return (EINVAL); 1358 } 1359 } 1360 1361 int 1362 fdisk_invalid_bb_sig(ext_part_t *epp, uchar_t **bbsig_arr) 1363 { 1364 *bbsig_arr = &(epp->invalid_bb_sig[0]); 1365 return (epp->invalid_bb_sig[0]); 1366 } 1367