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 }