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
50static void fdisk_free_ld_nodes(ext_part_t *epp);
51static void fdisk_ext_place_in_sorted_list(ext_part_t *epp,
52    logical_drive_t *newld);
53static void fdisk_ext_remove_from_sorted_list(ext_part_t *epp,
54    logical_drive_t *delld);
55static int fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec,
56    uint32_t endsec);
57static int fdisk_read_extpart(ext_part_t *epp);
58static void fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part);
59static int fdisk_init_master_part_table(ext_part_t *epp);
60static struct ipart *fdisk_alloc_part_table();
61static int fdisk_read_master_part_table(ext_part_t *epp);
62
63static int
64fdisk_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 */
152int
153libfdisk_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
241fail:
242	*epp = NULL;
243	free(temp);
244	return (rval);
245}
246
247int
248libfdisk_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
263void
264libfdisk_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
275int
276fdisk_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
352done:
353	free(buf);
354	return (rval);
355}
356
357int
358fdisk_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
383int
384fdisk_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 */
410static logical_drive_t *
411fdisk_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 */
425static void
426fdisk_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 */
442int
443fdisk_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 */
502uint32_t
503fdisk_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 */
523static void
524fdisk_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
545static void
546fdisk_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
567static int
568fdisk_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 */
597int
598fdisk_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 */
620static int
621fdisk_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
737static int
738fdisk_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
751static struct ipart *
752fdisk_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 */
769static int
770fdisk_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
815int
816fdisk_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
839int
840fdisk_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
875void
876fdisk_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 */
906void
907fdisk_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
993void
994fdisk_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
1047static void
1048fdisk_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
1092static int
1093read_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 */
1128int
1129fdisk_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
1202int
1203fdisk_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
1261skip_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
1337error:
1338	if (ebr_buf) {
1339		free(ebr_buf);
1340	}
1341	return (rval);
1342}
1343
1344int
1345fdisk_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
1358int
1359fdisk_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
1370int
1371fdisk_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
1409int
1410fdisk_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