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