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