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