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