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