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