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