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