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