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