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