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