xref: /titanic_52/usr/src/cmd/fdisk/fdisk.c (revision 051aabe6136ff13e81542a427e9693ffe1503525)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
29 /*	  All Rights Reserved	*/
30 
31 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
32 /*	  All Rights Reserved	*/
33 
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 /*
37  * PROGRAM: fdisk(1M)
38  * This program reads the partition table on the specified device and
39  * also reads the drive parameters. The user can perform various
40  * operations from a supplied menu or from the command line. Diagnostic
41  * options are also available.
42  */
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <ctype.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53 #include <sys/param.h>
54 #include <sys/systeminfo.h>
55 #include <sys/efi_partition.h>
56 #include <sys/byteorder.h>
57 #include <sys/systeminfo.h>
58 
59 #include <sys/dktp/fdisk.h>
60 #include <sys/dkio.h>
61 #include <sys/vtoc.h>
62 
63 #define	CLR_SCR ""
64 #define	CLR_LIN ""
65 #define	HOME "" \
66 	""
67 #define	Q_LINE ""
68 #define	W_LINE ""
69 #define	E_LINE ""
70 #define	M_LINE "" \
71 	""
72 #define	T_LINE ""
73 
74 #define	DEFAULT_PATH	"/dev/rdsk/"
75 
76 /* XXX - should be in fdisk.h, used by sd as well */
77 
78 /*
79  * the MAX values are the maximum usable values for BIOS chs values
80  * The MAX_CYL value of 1022 is the maximum usable value
81  *   the value of 1023 is a fence value,
82  *   indicating no CHS geometry exists for the corresponding LBA value.
83  * HEAD range [ 0 .. MAX_HEAD ], so number of heads is (MAX_HEAD + 1)
84  * SECT range [ 1 .. MAX_SECT ], so number of sectors is (MAX_SECT)
85  */
86 #define	MAX_SECT	(63)
87 #define	MAX_CYL		(1022)
88 #define	MAX_HEAD	(254)
89 
90 /* for clear_vtoc() */
91 #define	OLD		0
92 #define	NEW		1
93 
94 /* readvtoc/writevtoc return codes */
95 #define	VTOC_OK		0	/* Good VTOC */
96 #define	VTOC_INVAL	1	/* invalid VTOC */
97 #define	VTOC_NOTSUP	2	/* operation not supported - EFI label */
98 #define	VTOC_RWERR	3	/* couldn't read or write VTOC */
99 
100 /*
101  * Support for fdisk(1M) on the SPARC platform
102  *	In order to convert little endian values to big endian for SPARC,
103  *	byte/short and long values must be swapped.
104  *	These swapping macros will be used to access information in the
105  *	mboot and ipart structures.
106  */
107 
108 #ifdef sparc
109 #define	les(val)	((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
110 #define	lel(val)	(((unsigned)(les((val)&0x0000FFFF))<<16) | \
111 			    (les((unsigned)((val)&0xffff0000)>>16)))
112 #else
113 #define	les(val)	(val)
114 #define	lel(val)	(val)
115 #endif
116 
117 #if defined(_SUNOS_VTOC_16)
118 #define	VTOC_OFFSET	1
119 #elif defined(_SUNOS_VTOC_8)
120 #define	VTOC_OFFSET	0
121 #else
122 #error No VTOC format defined.
123 #endif
124 
125 static char Usage[] = "Usage: fdisk\n"
126 "[ -A id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect ]\n"
127 "[ -b masterboot ]\n"
128 "[ -D id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect ]\n"
129 "[ -F fdisk_file ] [ -h ] [ -o offset ] [ -P fill_patt ] [ -s size ]\n"
130 "[ -S geom_file ] [ [ -v ] -W { creat_fdisk_file | - } ]\n"
131 "[ -w | r | d | n | I | B | E | g | G | R | t | T ] rdevice";
132 
133 static char Usage1[] = "    Partition options:\n"
134 "	-A id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect\n"
135 "		Create a partition with specific attributes:\n"
136 "		id      = system id number (fdisk.h) for the partition type\n"
137 "		act     = active partition flag (0 is off and 128 is on)\n"
138 "		bhead   = beginning head for start of partition\n"
139 "		bsect   = beginning sector for start of partition\n"
140 "		bcyl    = beginning cylinder for start of partition\n"
141 "		ehead   = ending head for end of partition\n"
142 "		esect   = ending sector for end of partition\n"
143 "		ecyl    = ending cylinder for end of partition\n"
144 "		rsect   = sector number from start of disk for\n"
145 "			  start of partition\n"
146 "		numsect = partition size in sectors\n"
147 "	-b master_boot\n"
148 "		Use master_boot as the master boot file.\n"
149 "	-B	Create one Solaris partition that uses the entire disk.\n"
150 "	-E	Create one EFI partition that uses the entire disk.\n"
151 "	-D id:act:bhead:bsect:bcyl:ehead:esect:ecyl:rsect:numsect\n"
152 "		Delete a partition. See attribute definitions for -A.\n"
153 "	-F fdisk_file\n"
154 "		Use fdisk_file to initialize on-line fdisk table.\n"
155 "	-I	Forego device checks. Generate a file image of what would go\n"
156 "		on a disk using the geometry specified with the -S option.\n"
157 "	-n	Do not run in interactive mode.\n"
158 "	-R	Open the disk device as read-only.\n"
159 "	-t	Check and adjust VTOC to be consistent with fdisk table.\n"
160 "		VTOC slices exceeding the partition size will be truncated.\n"
161 "	-T	Check and adjust VTOC to be consistent with fdisk table.\n"
162 "		VTOC slices exceeding the partition size will be removed.\n"
163 "	-W fdisk_file\n"
164 "		Write on-disk table to fdisk_file.\n"
165 "	-W -	Write on-disk table to standard output.\n"
166 "	-v	Display virtual geometry. Must be used with the -W option.\n"
167 "    Diagnostic options:\n"
168 "	-d	Activate debug information about progress.\n"
169 "	-g	Write label geometry to standard output:\n"
170 "		PCYL		number of physical cylinders\n"
171 "		NCYL		number of usable cylinders\n"
172 "		ACYL		number of alternate cylinders\n"
173 "		BCYL		cylinder offset\n"
174 "		NHEADS		number of heads\n"
175 "		NSECTORS	number of sectors per track\n"
176 "		SECTSIZ		size of a sector in bytes\n"
177 "	-G	Write physical geometry to standard output (see -g).\n"
178 "	-h	Issue this verbose help message.\n"
179 "	-o offset\n"
180 "		Block offset from start of disk (default 0). Ignored if\n"
181 "		-P # specified.\n"
182 "	-P fill_patt\n"
183 "		Fill disk with pattern fill_patt. fill_patt can be decimal or\n"
184 "		hexadecimal and is used as number for constant long word\n"
185 "		pattern. If fill_patt is \"#\" then pattern of block #\n"
186 "		for each block. Pattern is put in each block as long words\n"
187 "		and fills each block (see -o and -s).\n"
188 "	-r	Read from a disk to stdout (see -o and -s).\n"
189 "	-s size	Number of blocks on which to perform operation (see -o).\n"
190 "	-S geom_file\n"
191 "		Use geom_file to set the label geometry (see -g).\n"
192 "	-w	Write to a disk from stdin (see -o and -s).";
193 
194 static char Ostr[] = "Other OS";
195 static char Dstr[] = "DOS12";
196 static char D16str[] = "DOS16";
197 static char DDstr[] = "DOS-DATA";
198 static char EDstr[] = "EXT-DOS";
199 static char DBstr[] = "DOS-BIG";
200 static char PCstr[] = "PCIX";
201 static char Ustr[] = "UNIX System";
202 static char SUstr[] = "Solaris";
203 static char SU2str[] = "Solaris2";
204 static char X86str[] = "x86 Boot";
205 static char DIAGstr[] = "Diagnostic";
206 static char IFSstr[] = "IFS: NTFS";
207 static char AIXstr[] = "AIX Boot";
208 static char AIXDstr[] = "AIX Data";
209 static char OS2str[] = "OS/2 Boot";
210 static char WINstr[] = "Win95 FAT32";
211 static char EWINstr[] = "Ext Win95";
212 static char FAT95str[] = "FAT16 LBA";
213 static char EXTLstr[] = "EXT LBA";
214 static char LINUXstr[] = "Linux";
215 static char CPMstr[] = "CP/M";
216 static char NOVstr[] = "Netware 3.x+";
217 static char QNXstr[] = "QNX 4.x";
218 static char QNX2str[] = "QNX part 2";
219 static char QNX3str[] = "QNX part 3";
220 static char LINNATstr[] = "Linux native";
221 static char NTFSVOL1str[] = "NT volset 1";
222 static char NTFSVOL2str[] = "NT volset 2";
223 static char BSDstr[] = "BSD OS";
224 static char NEXTSTEPstr[] = "NeXTSTEP";
225 static char BSDIFSstr[] = "BSDI FS";
226 static char BSDISWAPstr[] = "BSDI swap";
227 static char Actvstr[] = "Active";
228 static char EFIstr[] = "EFI";
229 static char NAstr[] = "      ";
230 
231 /* All the user options and flags */
232 static char *Dfltdev;			/* name of fixed disk drive */
233 
234 /* Diagnostic options */
235 static int	io_wrt = 0;		/* write stdin to disk (-w) */
236 static int	io_rd = 0;		/* read disk and write stdout (-r) */
237 static char	*io_fatt;		/* user supplied pattern (-P pattern) */
238 static int	io_patt = 0;		/* write pattern to disk (-P pattern) */
239 static int	io_lgeom = 0;		/* get label geometry (-g) */
240 static int	io_pgeom = 0;		/* get drive physical geometry (-G) */
241 static char	*io_sgeom = 0;		/* set label geometry (-S geom_file) */
242 static int	io_readonly = 0;	/* do not write to disk (-R) */
243 
244 /* The -o offset and -s size options specify the area of the disk on */
245 /* which to perform the particular operation; i.e., -P, -r, or -w. */
246 static off_t	io_offset = 0;		/* offset sector (-o offset) */
247 static off_t	io_size = 0;		/* size in sectors (-s size) */
248 
249 /* Partition table flags */
250 static int	v_flag = 0;		/* virtual geometry-HBA flag (-v) */
251 static int 	stdo_flag = 0;		/* stdout flag (-W -) */
252 static int	io_fdisk = 0;		/* do fdisk operation */
253 static int	io_ifdisk = 0;		/* interactive partition */
254 static int	io_nifdisk = 0;		/* non-interactive partition (-n) */
255 
256 static int	io_adjt = 0;		/* check/adjust VTOC (truncate (-t)) */
257 static int	io_ADJT = 0;		/* check/adjust VTOC (delete (-T)) */
258 static char	*io_ffdisk = 0;		/* input fdisk file name (-F file) */
259 static char	*io_Wfdisk = 0;		/* output fdisk file name (-W file) */
260 static char	*io_Afdisk = 0;		/* add entry to partition table (-A) */
261 static char	*io_Dfdisk = 0;		/* delete entry from part. table (-D) */
262 
263 static char	*io_mboot = 0;		/* master boot record (-b boot_file) */
264 
265 static struct mboot BootCod;		/* buffer for master boot record */
266 
267 static int	io_wholedisk = 0;	/* use whole disk for Solaris (-B) */
268 static int	io_EFIdisk = 0;		/* use whole disk for EFI (-E) */
269 static int	io_debug = 0;		/* activate verbose mode (-d) */
270 static int	io_image = 0;		/* create image using geometry (-I) */
271 
272 static struct mboot *Bootblk;		/* pointer to cut/paste sector zero */
273 static char	*Bootsect;		/* pointer to sector zero buffer */
274 static char	*Nullsect;
275 static struct vtoc	disk_vtoc;	/* verify VTOC table */
276 static int	vt_inval = 0;
277 static int	no_virtgeom_ioctl = 0;	/* ioctl for virtual geometry failed */
278 static int	no_physgeom_ioctl = 0;	/* ioctl for physical geometry failed */
279 
280 static struct ipart	Table[FD_NUMPART];
281 static struct ipart	Old_Table[FD_NUMPART];
282 
283 /* Disk geometry information */
284 static struct dk_minfo	minfo;
285 static struct dk_geom	disk_geom;
286 
287 static diskaddr_t	dev_capacity;	/* number of blocks on device */
288 static diskaddr_t	chs_capacity;	/* Numcyl * heads * sectors */
289 
290 static int Dev;			/* fd for open device */
291 /* Physical geometry for the drive */
292 static int	Numcyl;			/* number of cylinders */
293 static int	heads;			/* number of heads */
294 static int	sectors;		/* number of sectors per track */
295 static int	acyl;			/* number of alternate sectors */
296 
297 /* HBA (virtual) geometry for the drive */
298 static int	hba_Numcyl;		/* number of cylinders */
299 static int	hba_heads;		/* number of heads */
300 static int	hba_sectors;		/* number of sectors per track */
301 
302 static int	sectsiz;		/* sector size */
303 
304 /* Load functions for fdisk table modification */
305 #define	LOADFILE	0	/* load fdisk from file */
306 #define	LOADDEL		1	/* delete an fdisk entry */
307 #define	LOADADD		2	/* add an fdisk entry */
308 
309 #define	CBUFLEN 80
310 static char s[CBUFLEN];
311 
312 static void update_disk_and_exit(boolean_t table_changed);
313 int main(int argc, char *argv[]);
314 static int read_geom(char *sgeom);
315 static void dev_mboot_read(void);
316 static void dev_mboot_write(off_t sect, char *buff, int bootsiz);
317 static void mboot_read(void);
318 static void fill_patt(void);
319 static void abs_read(void);
320 static void abs_write(void);
321 static void load(int funct, char *file);
322 static void Set_Table_CHS_Values(int ti);
323 static int insert_tbl(int id, int act,
324     int bhead, int bsect, int bcyl,
325     int ehead, int esect, int ecyl,
326     int rsect, int numsect);
327 static int verify_tbl(void);
328 static int pars_fdisk(char *line,
329     int *id, int *act,
330     int *bhead, int *bsect, int *bcyl,
331     int *ehead, int *esect, int *ecyl,
332     int *rsect, int *numsect);
333 static int validate_part(int id, int rsect, int numsect);
334 static void stage0(void);
335 static int pcreate(void);
336 static int specify(uchar_t tsystid);
337 static void dispmenu(void);
338 static int pchange(void);
339 static int ppartid(void);
340 static char pdelete(void);
341 static void rm_blanks(char *s);
342 static int getcyl(void);
343 static void disptbl(void);
344 static void print_Table(void);
345 static void copy_Table_to_Old_Table(void);
346 static void nulltbl(void);
347 static void copy_Bootblk_to_Table(void);
348 static void fill_ipart(char *bootptr, struct ipart *partp);
349 #ifdef sparc
350 uchar_t getbyte(char **bp);
351 uint32_t getlong(char **bp);
352 #endif
353 static void copy_Table_to_Bootblk(void);
354 static int TableChanged(void);
355 static void ffile_write(char *file);
356 static void fix_slice(void);
357 static int yesno(void);
358 static int readvtoc(void);
359 static int writevtoc(void);
360 static int efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc);
361 static int clear_efi(void);
362 static void clear_vtoc(int table, int part);
363 static int lecture_and_query(char *warning, char *devname);
364 static void sanity_check_provided_device(char *devname, int fd);
365 static char *get_node(char *devname);
366 
367 static void
368 update_disk_and_exit(boolean_t table_changed)
369 {
370 	if (table_changed) {
371 		/*
372 		 * Copy the new table back to the sector buffer
373 		 * and write it to disk
374 		 */
375 		copy_Table_to_Bootblk();
376 		dev_mboot_write(0, Bootsect, sectsiz);
377 	}
378 
379 	/* If the VTOC table is wrong fix it (truncation only) */
380 	if (io_adjt)
381 		fix_slice();
382 
383 	exit(0);
384 }
385 
386 
387 
388 /*
389  * main
390  * Process command-line options.
391  */
392 int
393 main(int argc, char *argv[])
394 {
395 	int c, i;
396 	extern	int optind;
397 	extern	char *optarg;
398 	int	errflg = 0;
399 	int	diag_cnt = 0;
400 	int openmode;
401 
402 	setbuf(stderr, 0);	/* so all output gets out on exit */
403 	setbuf(stdout, 0);
404 
405 	/* Process the options. */
406 	while ((c = getopt(argc, argv, "o:s:P:F:b:A:D:W:S:tTIhwvrndgGRBE"))
407 	    != EOF) {
408 		switch (c) {
409 
410 			case 'o':
411 				io_offset = (off_t)strtoull(optarg, 0, 0);
412 				continue;
413 			case 's':
414 				io_size = (off_t)strtoull(optarg, 0, 0);
415 				continue;
416 			case 'P':
417 				diag_cnt++;
418 				io_patt++;
419 				io_fatt = optarg;
420 				continue;
421 			case 'w':
422 				diag_cnt++;
423 				io_wrt++;
424 				continue;
425 			case 'r':
426 				diag_cnt++;
427 				io_rd++;
428 				continue;
429 			case 'd':
430 				io_debug++;
431 				continue;
432 			case 'I':
433 				io_image++;
434 				continue;
435 			case 'R':
436 				io_readonly++;
437 				continue;
438 			case 'S':
439 				diag_cnt++;
440 				io_sgeom = optarg;
441 				continue;
442 			case 'T':
443 				io_ADJT++;
444 				/* FALLTHRU */
445 			case 't':
446 				io_adjt++;
447 				continue;
448 			case 'B':
449 				io_wholedisk++;
450 				io_fdisk++;
451 				continue;
452 			case 'E':
453 				io_EFIdisk++;
454 				io_fdisk++;
455 				continue;
456 			case 'g':
457 				diag_cnt++;
458 				io_lgeom++;
459 				continue;
460 			case 'G':
461 				diag_cnt++;
462 				io_pgeom++;
463 				continue;
464 			case 'n':
465 				io_nifdisk++;
466 				io_fdisk++;
467 				continue;
468 			case 'F':
469 				io_fdisk++;
470 				io_ffdisk = optarg;
471 				continue;
472 			case 'b':
473 				io_mboot = optarg;
474 				continue;
475 			case 'W':
476 				/*
477 				 * If '-' is the -W argument, then write
478 				 * to standard output, otherwise write
479 				 * to the specified file.
480 				 */
481 				if (strncmp(optarg, "-", 1) == 0)
482 					stdo_flag = 1;
483 				else
484 					io_Wfdisk = optarg;
485 				io_fdisk++;
486 				continue;
487 			case 'A':
488 				io_fdisk++;
489 				io_Afdisk = optarg;
490 				continue;
491 			case 'D':
492 				io_fdisk++;
493 				io_Dfdisk = optarg;
494 				continue;
495 			case 'h':
496 				(void) fprintf(stderr, "%s\n", Usage);
497 				(void) fprintf(stderr, "%s\n", Usage1);
498 				exit(0);
499 				/* FALLTHRU */
500 			case 'v':
501 				v_flag = 1;
502 				continue;
503 			case '?':
504 				errflg++;
505 				break;
506 		}
507 		break;
508 	}
509 
510 	if (io_image && io_sgeom && diag_cnt == 1) {
511 		diag_cnt = 0;
512 	}
513 
514 	/* User option checking */
515 
516 	/* By default, run in interactive mode */
517 	if (!io_fdisk && !diag_cnt && !io_nifdisk) {
518 		io_ifdisk++;
519 		io_fdisk++;
520 	}
521 	if (((io_fdisk || io_adjt) && diag_cnt) || (diag_cnt > 1)) {
522 		errflg++;
523 	}
524 
525 	/* Was any error detected? */
526 	if (errflg || argc == optind) {
527 		(void) fprintf(stderr, "%s\n", Usage);
528 		(void) fprintf(stderr,
529 		    "\nDetailed help is available with the -h option.\n");
530 		exit(2);
531 	}
532 
533 
534 	/* Figure out the correct device node to open */
535 	Dfltdev = get_node(argv[optind]);
536 
537 	if (io_readonly)
538 		openmode = O_RDONLY;
539 	else
540 		openmode = O_RDWR|O_CREAT;
541 
542 	if ((Dev = open(Dfltdev, openmode, 0666)) == -1) {
543 		(void) fprintf(stderr,
544 		    "fdisk: Cannot open device %s.\n",
545 		    Dfltdev);
546 		exit(1);
547 	}
548 	/*
549 	 * not all disk (or disklike) drivers support DKIOCGMEDIAINFO
550 	 * in that case leave the minfo structure zeroed
551 	 */
552 	if (ioctl(Dev, DKIOCGMEDIAINFO, &minfo)) {
553 		memset(&minfo, 0, sizeof (minfo));
554 	}
555 
556 	/* Get the disk geometry */
557 	if (!io_image) {
558 		/* Get disk's HBA (virtual) geometry */
559 		errno = 0;
560 		if (ioctl(Dev, DKIOCG_VIRTGEOM, &disk_geom)) {
561 
562 			/*
563 			 * If ioctl isn't implemented on this platform, then
564 			 * turn off flag to print out virtual geometry (-v),
565 			 * otherwise use the virtual geometry.
566 			 */
567 
568 			if (errno == ENOTTY) {
569 				v_flag = 0;
570 				no_virtgeom_ioctl = 1;
571 			} else if (errno == EINVAL) {
572 				/*
573 				 * This means that the ioctl exists, but
574 				 * is invalid for this disk, meaning the
575 				 * disk doesn't have an HBA geometry
576 				 * (like, say, it's larger than 8GB).
577 				 */
578 				v_flag = 0;
579 				hba_Numcyl = hba_heads = hba_sectors = 0;
580 			} else {
581 				(void) fprintf(stderr,
582 				    "%s: Cannot get virtual disk geometry.\n",
583 				    argv[optind]);
584 				exit(1);
585 			}
586 		} else {
587 			/* save virtual geometry values obtained by ioctl */
588 			hba_Numcyl = disk_geom.dkg_ncyl;
589 			hba_heads = disk_geom.dkg_nhead;
590 			hba_sectors = disk_geom.dkg_nsect;
591 		}
592 
593 		errno = 0;
594 		if (ioctl(Dev, DKIOCG_PHYGEOM, &disk_geom)) {
595 			if (errno == ENOTTY) {
596 				no_physgeom_ioctl = 1;
597 			} else {
598 				(void) fprintf(stderr,
599 				    "%s: Cannot get physical disk geometry.\n",
600 				    argv[optind]);
601 				exit(1);
602 			}
603 
604 		}
605 		/*
606 		 * Call DKIOCGGEOM if the ioctls for physical and virtual
607 		 * geometry fail. Get both from this generic call.
608 		 */
609 		if (no_virtgeom_ioctl && no_physgeom_ioctl) {
610 			errno = 0;
611 			if (ioctl(Dev, DKIOCGGEOM, &disk_geom)) {
612 				(void) fprintf(stderr,
613 				    "%s: Cannot get disk label geometry.\n",
614 				    argv[optind]);
615 				exit(1);
616 			}
617 		}
618 
619 		Numcyl = disk_geom.dkg_ncyl;
620 		heads = disk_geom.dkg_nhead;
621 		sectors = disk_geom.dkg_nsect;
622 		sectsiz = 512;
623 		acyl = disk_geom.dkg_acyl;
624 
625 		/*
626 		 * if hba geometry was not set by DKIOC_VIRTGEOM
627 		 * or we got an invalid hba geometry
628 		 * then set hba geometry based on max values
629 		 */
630 		if (no_virtgeom_ioctl ||
631 		    disk_geom.dkg_ncyl == 0 ||
632 		    disk_geom.dkg_nhead == 0 ||
633 		    disk_geom.dkg_nsect == 0 ||
634 		    disk_geom.dkg_ncyl > MAX_CYL ||
635 		    disk_geom.dkg_nhead > MAX_HEAD ||
636 		    disk_geom.dkg_nsect > MAX_SECT) {
637 
638 			/*
639 			 * turn off flag to print out virtual geometry (-v)
640 			 */
641 			v_flag = 0;
642 			hba_sectors	= MAX_SECT;
643 			hba_heads	= MAX_HEAD + 1;
644 			hba_Numcyl	= (Numcyl * heads * sectors) /
645 			    (hba_sectors * hba_heads);
646 		}
647 
648 		if (io_debug) {
649 			(void) fprintf(stderr, "Physical Geometry:\n");
650 			(void) fprintf(stderr,
651 			    "  cylinders[%d] heads[%d] sectors[%d]\n"
652 			    "  sector size[%d] blocks[%d] mbytes[%d]\n",
653 			    Numcyl,
654 			    heads,
655 			    sectors,
656 			    sectsiz,
657 			    Numcyl * heads * sectors,
658 			    (Numcyl * heads * sectors * sectsiz) / 1048576);
659 			(void) fprintf(stderr, "Virtual (HBA) Geometry:\n");
660 			(void) fprintf(stderr,
661 			    "  cylinders[%d] heads[%d] sectors[%d]\n"
662 			    "  sector size[%d] blocks[%d] mbytes[%d]\n",
663 			    hba_Numcyl,
664 			    hba_heads,
665 			    hba_sectors,
666 			    sectsiz,
667 			    hba_Numcyl * hba_heads * hba_sectors,
668 			    (hba_Numcyl * hba_heads * hba_sectors * sectsiz) /
669 			    1048576);
670 		}
671 	}
672 
673 	/* If user has requested a geometry report just do it and exit */
674 	if (io_lgeom) {
675 		if (ioctl(Dev, DKIOCGGEOM, &disk_geom)) {
676 			(void) fprintf(stderr,
677 			    "%s: Cannot get disk label geometry.\n",
678 			    argv[optind]);
679 			exit(1);
680 		}
681 		Numcyl = disk_geom.dkg_ncyl;
682 		heads = disk_geom.dkg_nhead;
683 		sectors = disk_geom.dkg_nsect;
684 		sectsiz = 512;
685 		acyl = disk_geom.dkg_acyl;
686 		(void) printf("* Label geometry for device %s\n", Dfltdev);
687 		(void) printf(
688 		    "* PCYL     NCYL     ACYL     BCYL     NHEAD NSECT"
689 		    " SECSIZ\n");
690 		(void) printf("  %-8d %-8d %-8d %-8d %-5d %-5d %-6d\n",
691 		    Numcyl,
692 		    disk_geom.dkg_ncyl,
693 		    disk_geom.dkg_acyl,
694 		    disk_geom.dkg_bcyl,
695 		    heads,
696 		    sectors,
697 		    sectsiz);
698 		exit(0);
699 	} else if (io_pgeom) {
700 		if (ioctl(Dev, DKIOCG_PHYGEOM, &disk_geom)) {
701 			(void) fprintf(stderr,
702 			    "%s: Cannot get physical disk geometry.\n",
703 			    argv[optind]);
704 			exit(1);
705 		}
706 		(void) printf("* Physical geometry for device %s\n", Dfltdev);
707 		(void) printf(
708 		    "* PCYL     NCYL     ACYL     BCYL     NHEAD NSECT"
709 		    " SECSIZ\n");
710 		(void) printf("  %-8d %-8d %-8d %-8d %-5d %-5d %-6d\n",
711 		    disk_geom.dkg_pcyl,
712 		    disk_geom.dkg_ncyl,
713 		    disk_geom.dkg_acyl,
714 		    disk_geom.dkg_bcyl,
715 		    disk_geom.dkg_nhead,
716 		    disk_geom.dkg_nsect,
717 		    sectsiz);
718 		exit(0);
719 	} else if (io_sgeom) {
720 		if (read_geom(io_sgeom)) {
721 			exit(1);
722 		} else if (!io_image) {
723 			exit(0);
724 		}
725 	}
726 
727 	/*
728 	 * some drivers may not support DKIOCGMEDIAINFO
729 	 * in that case use CHS
730 	 */
731 	chs_capacity = Numcyl * heads * sectors;
732 	dev_capacity = chs_capacity;
733 	if (minfo.dki_capacity > 0)
734 		dev_capacity = minfo.dki_capacity;
735 
736 	/* Allocate memory to hold three complete sectors */
737 	Bootsect = (char *)malloc(3 * sectsiz);
738 	if (Bootsect == NULL) {
739 		(void) fprintf(stderr,
740 		    "fdisk: Unable to obtain enough buffer memory"
741 		    " (%d bytes).\n",
742 		    3 * sectsiz);
743 		exit(1);
744 	}
745 
746 	Nullsect = Bootsect + sectsiz;
747 	/* Zero out the "NULL" sector */
748 	for (i = 0; i < sectsiz; i++) {
749 		Nullsect[i] = 0;
750 	}
751 
752 	/* Find out what the user wants done */
753 	if (io_rd) {		/* abs disk read */
754 		abs_read();	/* will not return */
755 	} else if (io_wrt && !io_readonly) {
756 		abs_write();	/* will not return */
757 	} else if (io_patt && !io_readonly) {
758 		fill_patt();	/* will not return */
759 	}
760 
761 
762 	/* This is the fdisk edit, the real reason for the program.	*/
763 
764 	sanity_check_provided_device(Dfltdev, Dev);
765 
766 	/* Get the new BOOT program in case we write a new fdisk table */
767 	mboot_read();
768 
769 	/* Read from disk master boot */
770 	dev_mboot_read();
771 
772 	/*
773 	 * Verify and copy the device's fdisk table. This will be used
774 	 * as the prototype mboot if the device's mboot looks invalid.
775 	 */
776 	Bootblk = (struct mboot *)Bootsect;
777 	copy_Bootblk_to_Table();
778 
779 	/* save away a copy of Table in Old_Table for sensing changes */
780 	copy_Table_to_Old_Table();
781 
782 	/* Load fdisk table from specified file (-F fdisk_file) */
783 	if (io_ffdisk) {
784 		/* Load and verify user-specified table parameters */
785 		load(LOADFILE, io_ffdisk);
786 	}
787 
788 	/* Does user want to delete or add an entry? */
789 	if (io_Dfdisk) {
790 		load(LOADDEL, io_Dfdisk);
791 	}
792 	if (io_Afdisk) {
793 		load(LOADADD, io_Afdisk);
794 	}
795 
796 	if (!io_ffdisk && !io_Afdisk && !io_Dfdisk) {
797 		/* Check if there is no fdisk table */
798 		if (Table[0].systid == UNUSED || io_wholedisk || io_EFIdisk) {
799 			if (io_ifdisk && !io_wholedisk && !io_EFIdisk) {
800 				(void) printf(
801 				    "No fdisk table exists. The default"
802 				    " partition for the disk is:\n\n"
803 				    "  a 100%% \"SOLARIS System\" "
804 				    "partition\n\n"
805 				    "Type \"y\" to accept the default "
806 				    "partition,  otherwise type \"n\" to "
807 				    "edit the\n partition table.\n");
808 			}
809 
810 			/* Edit the partition table as directed */
811 			if (io_wholedisk ||(io_ifdisk && yesno())) {
812 
813 				/* Default scenario */
814 				nulltbl();
815 
816 				/* now set up UNIX System partition */
817 				Table[0].bootid = ACTIVE;
818 				Table[0].relsect = lel(heads * sectors);
819 				Table[0].numsect = lel((long)((Numcyl - 1) *
820 				    heads * sectors));
821 				Table[0].systid = SUNIXOS2;   /* Solaris */
822 
823 				/* calculate CHS values for table entry 0 */
824 				Set_Table_CHS_Values(0);
825 
826 				update_disk_and_exit(B_TRUE);
827 			} else if (io_EFIdisk) {
828 				/* create an EFI partition for the whole disk */
829 				nulltbl();
830 				i = insert_tbl(EFI_PMBR, 0, 0, 0, 0, 0, 0, 0, 1,
831 				    dev_capacity - 1);
832 				if (i != 0) {
833 					(void) fprintf(stderr,
834 					    "Error creating EFI partition\n");
835 					exit(1);
836 				}
837 				update_disk_and_exit(B_TRUE);
838 			}
839 		}
840 	}
841 
842 	/* Display complete fdisk table entries for debugging purposes */
843 	if (io_debug) {
844 		(void) fprintf(stderr, "Partition Table Entry Values:\n");
845 		print_Table();
846 		if (io_ifdisk) {
847 			(void) fprintf(stderr, "\n");
848 			(void) fprintf(stderr, "Press Enter to continue.\n");
849 			(void) gets(s);
850 		}
851 	}
852 
853 	/* Interactive fdisk mode */
854 	if (io_ifdisk) {
855 		(void) printf(CLR_SCR);
856 		disptbl();
857 		for (;;) {
858 			stage0();
859 			copy_Bootblk_to_Table();
860 			disptbl();
861 		}
862 	}
863 
864 	/* If user wants to write the table to a file, do it */
865 	if (io_Wfdisk)
866 		ffile_write(io_Wfdisk);
867 	else if (stdo_flag)
868 		ffile_write((char *)stdout);
869 
870 	update_disk_and_exit(TableChanged() == 1);
871 	return (0);
872 }
873 
874 /*
875  * read_geom
876  * Read geometry from specified file (-S).
877  */
878 
879 static int
880 read_geom(char *sgeom)
881 {
882 	char	line[256];
883 	FILE *fp;
884 
885 	/* open the prototype file */
886 	if ((fp = fopen(sgeom, "r")) == NULL) {
887 		(void) fprintf(stderr, "fdisk: Cannot open file %s.\n",
888 		    io_sgeom);
889 		return (1);
890 	}
891 
892 	/* Read a line from the file */
893 	while (fgets(line, sizeof (line) - 1, fp)) {
894 		if (line[0] == '\0' || line[0] == '\n' || line[0] == '*')
895 			continue;
896 		else {
897 			line[strlen(line)] = '\0';
898 			if (sscanf(line, "%hu %hu %hu %hu %hu %hu %d",
899 			    &disk_geom.dkg_pcyl,
900 			    &disk_geom.dkg_ncyl,
901 			    &disk_geom.dkg_acyl,
902 			    &disk_geom.dkg_bcyl,
903 			    &disk_geom.dkg_nhead,
904 			    &disk_geom.dkg_nsect,
905 			    &sectsiz) != 7) {
906 				(void) fprintf(stderr,
907 				    "Syntax error:\n	\"%s\".\n",
908 				    line);
909 				return (1);
910 			}
911 			break;
912 		} /* else */
913 	} /* while (fgets(line, sizeof (line) - 1, fp)) */
914 
915 	if (!io_image) {
916 		if (ioctl(Dev, DKIOCSGEOM, &disk_geom)) {
917 			(void) fprintf(stderr,
918 			    "fdisk: Cannot set label geometry.\n");
919 			return (1);
920 		}
921 	} else {
922 		Numcyl = hba_Numcyl = disk_geom.dkg_ncyl;
923 		heads = hba_heads = disk_geom.dkg_nhead;
924 		sectors = hba_sectors = disk_geom.dkg_nsect;
925 		acyl = disk_geom.dkg_acyl;
926 	}
927 
928 	(void) fclose(fp);
929 	return (0);
930 }
931 
932 /*
933  * dev_mboot_read
934  * Read the master boot sector from the device.
935  */
936 static void
937 dev_mboot_read(void)
938 {
939 	if ((ioctl(Dev, DKIOCGMBOOT, Bootsect) < 0) && (errno != ENOTTY)) {
940 		perror("Error in ioctl DKIOCGMBOOT");
941 	}
942 	if (errno == 0)
943 		return;
944 	if (lseek(Dev, 0, SEEK_SET) == -1) {
945 		(void) fprintf(stderr,
946 		    "fdisk: Error seeking to partition table on %s.\n",
947 		    Dfltdev);
948 		if (!io_image)
949 			exit(1);
950 	}
951 	if (read(Dev, Bootsect, sectsiz) != sectsiz) {
952 		(void) fprintf(stderr,
953 		    "fdisk: Error reading partition table from %s.\n",
954 		    Dfltdev);
955 		if (!io_image)
956 			exit(1);
957 	}
958 }
959 
960 /*
961  * dev_mboot_write
962  * Write the master boot sector to the device.
963  */
964 static void
965 dev_mboot_write(off_t sect, char *buff, int bootsiz)
966 {
967 	int 	new_pt, old_pt, error;
968 	int	clr_efi = -1;
969 
970 	if (io_readonly)
971 		return;
972 
973 	if (io_debug) {
974 		(void) fprintf(stderr, "About to write fdisk table:\n");
975 		print_Table();
976 		if (io_ifdisk) {
977 			(void) fprintf(stderr, "Press Enter to continue.\n");
978 			(void) gets(s);
979 		}
980 	}
981 
982 	/*
983 	 * If the new table has any Solaris partitions and the old
984 	 * table does not have an entry that describes it
985 	 * exactly then clear the old vtoc (if any).
986 	 */
987 	for (new_pt = 0; new_pt < FD_NUMPART; new_pt++) {
988 
989 		/* We only care about potential Solaris parts. */
990 		if (Table[new_pt].systid != SUNIXOS &&
991 		    Table[new_pt].systid != SUNIXOS2)
992 			continue;
993 
994 		/* Does the old table have an exact entry for the new entry? */
995 		for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) {
996 
997 			/* We only care about old Solaris partitions. */
998 			if ((Old_Table[old_pt].systid == SUNIXOS) ||
999 			    (Old_Table[old_pt].systid == SUNIXOS2)) {
1000 
1001 				/* Is this old one the same as a new one? */
1002 				if ((Old_Table[old_pt].relsect ==
1003 				    Table[new_pt].relsect) &&
1004 				    (Old_Table[old_pt].numsect ==
1005 				    Table[new_pt].numsect))
1006 					break; /* Yes */
1007 			}
1008 		}
1009 
1010 		/* Did a solaris partition change location or size? */
1011 		if (old_pt >= FD_NUMPART) {
1012 			/* Yes clear old vtoc */
1013 			if (io_debug) {
1014 				(void) fprintf(stderr,
1015 				    "Clearing VTOC labels from NEW"
1016 				    " table\n");
1017 			}
1018 			clear_vtoc(NEW, new_pt);
1019 		}
1020 	}
1021 
1022 
1023 	/* see if the old table had EFI */
1024 	for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) {
1025 		if (Old_Table[old_pt].systid == EFI_PMBR) {
1026 			clr_efi = old_pt;
1027 		}
1028 	}
1029 
1030 	/* look to see if a EFI partition changed in relsect/numsect */
1031 	for (new_pt = 0; new_pt < FD_NUMPART; new_pt++) {
1032 		if (Table[new_pt].systid != EFI_PMBR)
1033 			continue;
1034 		for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) {
1035 			if ((Old_Table[old_pt].systid ==
1036 			    Table[new_pt].systid) &&
1037 			    (Old_Table[old_pt].relsect ==
1038 			    Table[new_pt].relsect) &&
1039 			    (Old_Table[old_pt].numsect ==
1040 			    Table[new_pt].numsect))
1041 				break;
1042 		}
1043 
1044 		/*
1045 		 * if EFI partition changed, set the flag to clear
1046 		 * the EFI GPT
1047 		 */
1048 		if (old_pt == FD_NUMPART && Table[new_pt].begcyl != 0) {
1049 			clr_efi = 0;
1050 		}
1051 		break;
1052 	}
1053 
1054 	/* clear labels if necessary */
1055 	if (clr_efi >= 0) {
1056 		if (io_debug) {
1057 			(void) fprintf(stderr, "Clearing EFI labels\n");
1058 		}
1059 		if ((error = clear_efi()) != 0) {
1060 			if (io_debug) {
1061 				(void) fprintf(stderr,
1062 				    "\tError %d clearing EFI labels"
1063 				    " (probably no EFI labels exist)\n",
1064 				    error);
1065 			}
1066 		}
1067 	}
1068 
1069 	if ((ioctl(Dev, DKIOCSMBOOT, buff) == -1) && (errno != ENOTTY)) {
1070 		(void) fprintf(stderr,
1071 		    "fdisk: Error in ioctl DKIOCSMBOOT on %s.\n",
1072 		    Dfltdev);
1073 	}
1074 	if (errno == 0)
1075 		return;
1076 
1077 	/* write to disk drive */
1078 	if (lseek(Dev, sect, SEEK_SET) == -1) {
1079 		(void) fprintf(stderr,
1080 		    "fdisk: Error seeking to master boot record on %s.\n",
1081 		    Dfltdev);
1082 		exit(1);
1083 	}
1084 	if (write(Dev, buff, bootsiz) != bootsiz) {
1085 		(void) fprintf(stderr,
1086 		    "fdisk: Error writing master boot record to %s.\n",
1087 		    Dfltdev);
1088 		exit(1);
1089 	}
1090 }
1091 
1092 /*
1093  * mboot_read
1094  * Read the prototype boot records from the files.
1095  */
1096 static void
1097 mboot_read(void)
1098 {
1099 	int mDev, i;
1100 	struct ipart *part;
1101 
1102 #if defined(i386) || defined(sparc)
1103 	/*
1104 	 * If the master boot file hasn't been specified, use the
1105 	 * implementation architecture name to generate the default one.
1106 	 */
1107 	if (io_mboot == (char *)0) {
1108 		/*
1109 		 * Bug ID 1249035:
1110 		 *	The mboot file must be delivered on all platforms
1111 		 *	and installed in a non-platform-dependent
1112 		 *	directory; i.e., /usr/lib/fs/ufs.
1113 		 */
1114 		io_mboot = "/usr/lib/fs/ufs/mboot";
1115 	}
1116 
1117 	/* First read in the master boot record */
1118 
1119 	/* Open the master boot proto file */
1120 	if ((mDev = open(io_mboot, O_RDONLY, 0666)) == -1) {
1121 		(void) fprintf(stderr,
1122 		    "fdisk: Cannot open master boot file %s.\n",
1123 		    io_mboot);
1124 		exit(1);
1125 	}
1126 
1127 	/* Read the master boot program */
1128 	if (read(mDev, &BootCod, sizeof (struct mboot)) != sizeof
1129 	    (struct mboot)) {
1130 		(void) fprintf(stderr,
1131 		    "fdisk: Cannot read master boot file %s.\n",
1132 		    io_mboot);
1133 		exit(1);
1134 	}
1135 
1136 	/* Is this really a master boot record? */
1137 	if (les(BootCod.signature) != MBB_MAGIC) {
1138 		(void) fprintf(stderr,
1139 		    "fdisk: Invalid master boot file %s.\n", io_mboot);
1140 		(void) fprintf(stderr,
1141 		    "Bad magic number: is %x, but should be %x.\n",
1142 		    les(BootCod.signature), MBB_MAGIC);
1143 		exit(1);
1144 	}
1145 
1146 	(void) close(mDev);
1147 #else
1148 #error	fdisk needs to be ported to new architecture
1149 #endif
1150 
1151 	/* Zero out the partitions part of this record */
1152 	part = (struct ipart *)BootCod.parts;
1153 	for (i = 0; i < FD_NUMPART; i++, part++) {
1154 		(void) memset(part, 0, sizeof (struct ipart));
1155 	}
1156 
1157 }
1158 
1159 /*
1160  * fill_patt
1161  * Fill the disk with user/sector number pattern.
1162  */
1163 static void
1164 fill_patt(void)
1165 {
1166 	int	*buff_ptr, i;
1167 	off_t	*off_ptr;
1168 	int	io_fpatt = 0;
1169 	int	io_ipatt = 0;
1170 
1171 	if (strncmp(io_fatt, "#", 1) != 0) {
1172 		io_fpatt++;
1173 		io_ipatt = strtoul(io_fatt, 0, 0);
1174 		buff_ptr = (int *)Bootsect;
1175 		for (i = 0; i < sectsiz; i += 4, buff_ptr++)
1176 			*buff_ptr = io_ipatt;
1177 	}
1178 
1179 	/*
1180 	 * Fill disk with pattern based on block number.
1181 	 * Write to the disk at absolute relative block io_offset
1182 	 * for io_size blocks.
1183 	 */
1184 	while (io_size--) {
1185 		off_ptr = (off_t *)Bootsect;
1186 		if (!io_fpatt) {
1187 			for (i = 0; i < sectsiz;
1188 			    i += sizeof (off_t), off_ptr++)
1189 				*off_ptr = io_offset;
1190 		}
1191 		/* Write the data to disk */
1192 		if (lseek(Dev, (off_t)(sectsiz * io_offset++),
1193 		    SEEK_SET) == -1) {
1194 			(void) fprintf(stderr, "fdisk: Error seeking on %s.\n",
1195 			    Dfltdev);
1196 			exit(1);
1197 		}
1198 		if (write(Dev, Bootsect, sectsiz) != sectsiz) {
1199 			(void) fprintf(stderr, "fdisk: Error writing %s.\n",
1200 			    Dfltdev);
1201 			exit(1);
1202 		}
1203 	} /* while (--io_size); */
1204 }
1205 
1206 /*
1207  * abs_read
1208  * Read from the disk at absolute relative block io_offset for
1209  * io_size blocks. Write the data to standard ouput (-r).
1210  */
1211 static void
1212 abs_read(void)
1213 {
1214 	int c;
1215 
1216 	while (io_size--) {
1217 		if (lseek(Dev, (off_t)(sectsiz * io_offset++),
1218 		    SEEK_SET) == -1) {
1219 			(void) fprintf(stderr, "fdisk: Error seeking on %s.\n",
1220 			    Dfltdev);
1221 			exit(1);
1222 		}
1223 		if (read(Dev, Bootsect, sectsiz) != sectsiz) {
1224 			(void) fprintf(stderr, "fdisk: Error reading %s.\n",
1225 			    Dfltdev);
1226 			exit(1);
1227 		}
1228 
1229 		/* Write to standard ouptut */
1230 		if ((c = write(1, Bootsect, (unsigned)sectsiz)) != sectsiz) {
1231 			if (c >= 0) {
1232 				if (io_debug)
1233 					(void) fprintf(stderr,
1234 					    "fdisk: Output warning: %d of %d"
1235 					    " characters written.\n",
1236 					    c, sectsiz);
1237 				exit(2);
1238 			} else {
1239 				perror("write error on output file.");
1240 				exit(2);
1241 			}
1242 		} /* if ((c = write(1, Bootsect, (unsigned)sectsiz)) */
1243 			/* != sectsiz) */
1244 	} /* while (--io_size); */
1245 	exit(0);
1246 }
1247 
1248 /*
1249  * abs_write
1250  * Read the data from standard input. Write to the disk at
1251  * absolute relative block io_offset for io_size blocks (-w).
1252  */
1253 static void
1254 abs_write(void)
1255 {
1256 	int c, i;
1257 
1258 	while (io_size--) {
1259 		int part_exit = 0;
1260 		/* Read from standard input */
1261 		if ((c = read(0, Bootsect, (unsigned)sectsiz)) != sectsiz) {
1262 			if (c >= 0) {
1263 				if (io_debug)
1264 				(void) fprintf(stderr,
1265 				    "fdisk: WARNING: Incomplete read (%d of"
1266 				    " %d characters read) on input file.\n",
1267 				    c, sectsiz);
1268 				/* Fill pattern to mark partial sector in buf */
1269 				for (i = c; i < sectsiz; ) {
1270 					Bootsect[i++] = 0x41;
1271 					Bootsect[i++] = 0x62;
1272 					Bootsect[i++] = 0x65;
1273 					Bootsect[i++] = 0;
1274 				}
1275 				part_exit++;
1276 			} else {
1277 				perror("read error on input file.");
1278 				exit(2);
1279 			}
1280 
1281 		}
1282 		/* Write to disk drive */
1283 		if (lseek(Dev, (off_t)(sectsiz * io_offset++),
1284 		    SEEK_SET) == -1) {
1285 			(void) fprintf(stderr, "fdisk: Error seeking on %s.\n",
1286 			    Dfltdev);
1287 			exit(1);
1288 		}
1289 		if (write(Dev, Bootsect, sectsiz) != sectsiz) {
1290 			(void) fprintf(stderr, "fdisk: Error writing %s.\n",
1291 			    Dfltdev);
1292 			exit(1);
1293 		}
1294 		if (part_exit)
1295 		exit(0);
1296 	} /* while (--io_size); */
1297 	exit(1);
1298 }
1299 
1300 
1301 /*
1302  * load
1303  * Load will either read the fdisk table from a file or add or
1304  * delete an entry (-A, -D, -F).
1305  */
1306 
1307 static void
1308 load(int funct, char *file)
1309 {
1310 	int	id;
1311 	int	act;
1312 	int	bhead;
1313 	int	bsect;
1314 	int	bcyl;
1315 	int	ehead;
1316 	int	esect;
1317 	int	ecyl;
1318 	int	rsect;
1319 	int	numsect;
1320 	char	line[256];
1321 	int	i = 0;
1322 	int	j;
1323 	FILE *fp;
1324 
1325 	switch (funct) {
1326 
1327 	case LOADFILE:
1328 
1329 		/*
1330 		 * Zero out the table before loading it, which will
1331 		 * force it to be updated on disk later (-F
1332 		 * fdisk_file).
1333 		 */
1334 		nulltbl();
1335 
1336 		/* Open the prototype file */
1337 		if ((fp = fopen(file, "r")) == NULL) {
1338 			(void) fprintf(stderr,
1339 			    "fdisk: Cannot open prototype partition file %s.\n",
1340 			    file);
1341 			exit(1);
1342 		}
1343 
1344 		/* Read a line from the file */
1345 		while (fgets(line, sizeof (line) - 1, fp)) {
1346 			if (pars_fdisk(line, &id, &act, &bhead, &bsect,
1347 			    &bcyl, &ehead, &esect, &ecyl, &rsect, &numsect)) {
1348 				continue;
1349 			}
1350 
1351 			/*
1352 			 * Validate the partition. It cannot start at sector
1353 			 * 0 unless it is UNUSED or already exists
1354 			 */
1355 			if (validate_part(id, rsect, numsect) < 0) {
1356 				(void) fprintf(stderr,
1357 				    "fdisk: Error on entry \"%s\".\n",
1358 				    line);
1359 				exit(1);
1360 			}
1361 			/*
1362 			 * Find an unused entry to use and put the entry
1363 			 * in table
1364 			 */
1365 			if (insert_tbl(id, act, bhead, bsect, bcyl, ehead,
1366 			    esect, ecyl, rsect, numsect) < 0) {
1367 				(void) fprintf(stderr,
1368 				    "fdisk: Error on entry \"%s\".\n",
1369 				    line);
1370 				exit(1);
1371 			}
1372 		} /* while (fgets(line, sizeof (line) - 1, fp)) */
1373 
1374 		if (verify_tbl() < 0) {
1375 			(void) fprintf(stderr,
1376 			    "fdisk: Cannot create partition table\n");
1377 			exit(1);
1378 		}
1379 
1380 		(void) fclose(fp);
1381 		return;
1382 
1383 	case LOADDEL:
1384 
1385 		/* Parse the user-supplied deletion line (-D) */
1386 		if (pars_fdisk(file, &id, &act, &bhead, &bsect, &bcyl,
1387 		    &ehead, &esect, &ecyl, &rsect, &numsect)) {
1388 			(void) fprintf(stderr,
1389 			    "fdisk: Syntax error \"%s\"\n", file);
1390 			exit(1);
1391 		}
1392 
1393 		/* Find the exact entry in the table */
1394 		for (i = 0; i < FD_NUMPART; i++) {
1395 			if (Table[i].systid == id &&
1396 			    Table[i].bootid == act &&
1397 			    Table[i].beghead == bhead &&
1398 			    Table[i].begsect == ((bsect & 0x3f) |
1399 			    (uchar_t)((bcyl>>2) & 0xc0)) &&
1400 			    Table[i].begcyl == (uchar_t)(bcyl & 0xff) &&
1401 			    Table[i].endhead == ehead &&
1402 			    Table[i].endsect == ((esect & 0x3f) |
1403 			    (uchar_t)((ecyl>>2) & 0xc0)) &&
1404 			    Table[i].endcyl == (uchar_t)(ecyl & 0xff) &&
1405 			    Table[i].relsect == lel(rsect) &&
1406 			    Table[i].numsect == lel(numsect)) {
1407 
1408 				/*
1409 				 * Found the entry. Now move rest of
1410 				 * entries up toward the top of the
1411 				 * table, leaving available entries at
1412 				 * the end of the fdisk table.
1413 				 */
1414 				for (j = i; j < FD_NUMPART - 1; j++) {
1415 					Table[j].systid = Table[j + 1].systid;
1416 					Table[j].bootid = Table[j + 1].bootid;
1417 					Table[j].beghead = Table[j + 1].beghead;
1418 					Table[j].begsect = Table[j + 1].begsect;
1419 					Table[j].begcyl = Table[j + 1].begcyl;
1420 					Table[j].endhead = Table[j + 1].endhead;
1421 					Table[j].endsect = Table[j + 1].endsect;
1422 					Table[j].endcyl = Table[j + 1].endcyl;
1423 					Table[j].relsect = Table[j + 1].relsect;
1424 					Table[j].numsect = Table[j + 1].numsect;
1425 				}
1426 
1427 				/*
1428 				 * Mark the last entry as unused in case
1429 				 * all table entries were in use prior
1430 				 * to the deletion.
1431 				 */
1432 
1433 				Table[FD_NUMPART - 1].systid = UNUSED;
1434 				Table[FD_NUMPART - 1].bootid = 0;
1435 				return;
1436 			}
1437 		}
1438 		(void) fprintf(stderr,
1439 		    "fdisk: Entry does not match any existing partition:\n"
1440 		    "	\"%s\"\n",
1441 		    file);
1442 		exit(1);
1443 		/* FALLTHRU */
1444 
1445 	case LOADADD:
1446 
1447 		/* Parse the user-supplied addition line (-A) */
1448 		if (pars_fdisk(file, &id, &act, &bhead, &bsect, &bcyl, &ehead,
1449 		    &esect, &ecyl, &rsect, &numsect)) {
1450 			(void) fprintf(stderr,
1451 			    "fdisk: Syntax error \"%s\"\n", file);
1452 			exit(1);
1453 		}
1454 
1455 		/* Validate the partition. It cannot start at sector 0 */
1456 		if (rsect == 0) {
1457 			(void) fprintf(stderr,
1458 			    "fdisk: New partition cannot start at sector 0:\n"
1459 			    "   \"%s\".\n",
1460 			    file);
1461 			exit(1);
1462 		}
1463 
1464 		/*
1465 		 * if the user wishes to add an EFI partition, we need
1466 		 * more extensive validation.  rsect should be 1, and
1467 		 * numsect should equal the entire disk capacity - 1
1468 		 */
1469 
1470 		if (id == EFI_PMBR) {
1471 			if (rsect != 1) {
1472 				(void) fprintf(stderr,
1473 				    "fdisk: EFI partitions must start at sector"
1474 				    " 1 (input rsect = %d)\n", rsect);
1475 				exit(1);
1476 			}
1477 
1478 			if (numsect != dev_capacity - 1) {
1479 				(void) fprintf(stderr,
1480 				    "fdisk: EFI partitions must encompass the "
1481 				    "entire disk\n"
1482 				    "(input numsect: %d - avail: %llu)\n",
1483 				    numsect,
1484 				    dev_capacity - 1);
1485 				exit(1);
1486 			}
1487 		}
1488 
1489 		/* Find unused entry for use and put entry in table */
1490 		if (insert_tbl(id, act, bhead, bsect, bcyl, ehead, esect,
1491 		    ecyl, rsect, numsect) < 0) {
1492 			(void) fprintf(stderr,
1493 			    "fdisk: Invalid entry could not be inserted:\n"
1494 			    "	\"%s\"\n",
1495 			    file);
1496 			exit(1);
1497 		}
1498 
1499 		/* Make sure new entry does not overlap existing entry */
1500 		if (verify_tbl() < 0) {
1501 			(void) fprintf(stderr,
1502 			    "fdisk: Cannot create partition \"%s\"\n", file);
1503 			exit(1);
1504 		}
1505 	} /* switch funct */
1506 }
1507 
1508 /*
1509  * Set_Table_CHS_Values
1510  *
1511  * This will calculate the CHS values for beginning and ending CHS
1512  * for a single partition table entry (ti) based on the relsect
1513  * and numsect values contained in the partion table entry.
1514  *
1515  * hba_heads and hba_sectors contain the number of heads and sectors.
1516  *
1517  * If the number of cylinders exceeds the MAX_CYL,
1518  * then maximum values will be placed in the corresponding chs entry.
1519  */
1520 static void
1521 Set_Table_CHS_Values(int ti)
1522 {
1523 	uint32_t	lba, cy, hd, sc;
1524 
1525 	lba = (uint32_t)Table[ti].relsect;
1526 	if (lba >= hba_heads * hba_sectors * MAX_CYL) {
1527 		/*
1528 		 * the lba address cannot be expressed in CHS value
1529 		 * so store the maximum CHS field values in the CHS fields.
1530 		 */
1531 		cy = MAX_CYL + 1;
1532 		hd = MAX_HEAD;
1533 		sc = MAX_SECT;
1534 	} else {
1535 		cy = lba / hba_sectors / hba_heads;
1536 		hd = lba / hba_sectors % hba_heads;
1537 		sc = lba % hba_sectors + 1;
1538 	}
1539 	Table[ti].begcyl = cy & 0xff;
1540 	Table[ti].beghead = (uchar_t)hd;
1541 	Table[ti].begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
1542 
1543 	/*
1544 	 * This code is identical to the code above
1545 	 * except that it works on ending CHS values
1546 	 */
1547 	lba = (uint32_t)(Table[ti].relsect + Table[ti].numsect - 1);
1548 	if (lba >= hba_heads * hba_sectors * MAX_CYL) {
1549 		cy = MAX_CYL + 1;
1550 		hd = MAX_HEAD;
1551 		sc = MAX_SECT;
1552 	} else {
1553 		cy = lba / hba_sectors / hba_heads;
1554 		hd = lba / hba_sectors % hba_heads;
1555 		sc = lba % hba_sectors + 1;
1556 	}
1557 	Table[ti].endcyl = cy & 0xff;
1558 	Table[ti].endhead = (uchar_t)hd;
1559 	Table[ti].endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
1560 }
1561 
1562 /*
1563  * insert_tbl
1564  * 	Insert entry into fdisk table. Check all user-supplied values
1565  *	for the entry, but not the validity relative to other table
1566  *	entries!
1567  */
1568 static int
1569 insert_tbl(
1570     int id, int act,
1571     int bhead, int bsect, int bcyl,
1572     int ehead, int esect, int ecyl,
1573     int rsect, int numsect)
1574 {
1575 	int	i;
1576 
1577 	/* validate partition size */
1578 	if (rsect + numsect > dev_capacity) {
1579 		(void) fprintf(stderr,
1580 		    "fdisk: Partition table exceeds the size of the disk.\n");
1581 		return (-1);
1582 	}
1583 
1584 	/* find UNUSED partition table entry */
1585 	for (i = 0; i < FD_NUMPART; i++) {
1586 		if (Table[i].systid == UNUSED) {
1587 			break;
1588 		}
1589 	}
1590 	if (i >= FD_NUMPART) {
1591 		(void) fprintf(stderr, "fdisk: Partition table is full.\n");
1592 		return (-1);
1593 	}
1594 
1595 
1596 	Table[i].systid = (uchar_t)id;
1597 	Table[i].bootid = (uchar_t)act;
1598 	Table[i].numsect = lel(numsect);
1599 	Table[i].relsect = lel(rsect);
1600 
1601 	/*
1602 	 * If we have been called with a valid geometry, use it
1603 	 * valid means non-zero values that fit in the BIOS fields
1604 	 */
1605 	if (0 < bsect && bsect <= MAX_SECT &&
1606 	    0 <= bhead && bhead <= MAX_HEAD &&
1607 	    0 < esect && esect <= MAX_SECT &&
1608 	    0 <= ehead && ehead <= MAX_HEAD) {
1609 		if (bcyl > MAX_CYL)
1610 			bcyl = MAX_CYL + 1;
1611 		if (ecyl > MAX_CYL)
1612 			ecyl = MAX_CYL + 1;
1613 		Table[i].begcyl = bcyl & 0xff;
1614 		Table[i].endcyl = ecyl & 0xff;
1615 		Table[i].beghead = (uchar_t)bhead;
1616 		Table[i].endhead = (uchar_t)ehead;
1617 		Table[i].begsect = (uchar_t)(((bcyl >> 2) & 0xc0) | bsect);
1618 		Table[i].endsect = ((ecyl >> 2) & 0xc0) | esect;
1619 	} else {
1620 
1621 		/*
1622 		 * The specified values are invalid,
1623 		 * so calculate the values based on hba_heads, hba_sectors
1624 		 */
1625 		Set_Table_CHS_Values(i);
1626 	}
1627 
1628 	/*
1629 	 * return partition index
1630 	 */
1631 	return (i);
1632 }
1633 
1634 /*
1635  * verify_tbl
1636  * Verify that no partition entries overlap or exceed the size of
1637  * the disk.
1638  */
1639 static int
1640 verify_tbl(void)
1641 {
1642 	int	i, j, rsect, numsect;
1643 	int	noMoreParts = 0;
1644 	int	numParts = 0;
1645 
1646 	/* Make sure new entry does not overlap an existing entry */
1647 	for (i = 0; i < FD_NUMPART - 1; i++) {
1648 		if (Table[i].systid != UNUSED) {
1649 			numParts++;
1650 			/*
1651 			 * No valid partitions allowed after an UNUSED  or
1652 			 * EFI_PMBR part
1653 			 */
1654 			if (noMoreParts) {
1655 				return (-1);
1656 			}
1657 
1658 			/*
1659 			 * EFI_PMBR partitions must be the only partition
1660 			 * and must be Table entry 0
1661 			 */
1662 			if (Table[i].systid == EFI_PMBR) {
1663 				if (i == 0) {
1664 					noMoreParts = 1;
1665 				} else {
1666 					return (-1);
1667 				}
1668 
1669 				if (Table[i].relsect != 1) {
1670 					(void) fprintf(stderr, "ERROR: "
1671 					    "Invalid starting sector "
1672 					    "for EFI_PMBR partition:\n"
1673 					    "relsect %d "
1674 					    "(should be 1)\n",
1675 					    Table[i].relsect);
1676 
1677 					return (-1);
1678 				}
1679 
1680 				if (Table[i].numsect != dev_capacity - 1) {
1681 					(void) fprintf(stderr, "ERROR: "
1682 					    "EFI_PMBR partition must "
1683 					    "encompass the entire "
1684 					    "disk.\n numsect %d - "
1685 					    "actual %llu\n",
1686 					    Table[i].numsect,
1687 					    dev_capacity - 1);
1688 
1689 					return (-1);
1690 				}
1691 			}
1692 
1693 			/* make sure the partition isn't larger than the disk */
1694 			rsect = lel(Table[i].relsect);
1695 			numsect = lel(Table[i].numsect);
1696 			if ((rsect + numsect) > dev_capacity) {
1697 				return (-1);
1698 			}
1699 
1700 			for (j = i + 1; j < FD_NUMPART; j++) {
1701 				if (Table[j].systid != UNUSED) {
1702 					int t_relsect = lel(Table[j].relsect);
1703 					int t_numsect = lel(Table[j].numsect);
1704 
1705 					if (noMoreParts) {
1706 						(void) fprintf(stderr,
1707 						    "Cannot add partition to "
1708 						    "table; no more partitions "
1709 						    "allowed\n");
1710 
1711 						if (io_debug) {
1712 							(void) fprintf(stderr,
1713 							    "DEBUG: Current "
1714 							    "partition:\t"
1715 							    "%d:%d:%d:%d:%d:"
1716 							    "%d:%d:%d:%d:%d\n"
1717 							    "       Next "
1718 							    "partition:\t\t"
1719 							    "%d:%d:%d:%d:%d:"
1720 							    "%d:%d:%d:%d:%d\n",
1721 							    Table[i].systid,
1722 							    Table[i].bootid,
1723 							    Table[i].begcyl,
1724 							    Table[i].beghead,
1725 							    Table[i].begsect,
1726 							    Table[i].endcyl,
1727 							    Table[i].endhead,
1728 							    Table[i].endsect,
1729 							    Table[i].relsect,
1730 							    Table[i].numsect,
1731 							    Table[j].systid,
1732 							    Table[j].bootid,
1733 							    Table[j].begcyl,
1734 							    Table[j].beghead,
1735 							    Table[j].begsect,
1736 							    Table[j].endcyl,
1737 							    Table[j].endhead,
1738 							    Table[j].endsect,
1739 							    Table[j].relsect,
1740 							    Table[j].numsect);
1741 						}
1742 
1743 						return (-1);
1744 					}
1745 
1746 					if ((rsect >=
1747 					    (t_relsect + t_numsect)) ||
1748 					    ((rsect + numsect) <= t_relsect)) {
1749 						continue;
1750 					} else {
1751 						(void) fprintf(stderr, "ERROR: "
1752 						    "current partition overlaps"
1753 						    " following partition\n");
1754 
1755 						return (-1);
1756 					}
1757 				}
1758 			}
1759 		} else {
1760 			noMoreParts = 1;
1761 		}
1762 	}
1763 	if (Table[i].systid != UNUSED) {
1764 		if (noMoreParts ||
1765 		    ((lel(Table[i].relsect) + lel(Table[i].numsect)) >
1766 		    dev_capacity)) {
1767 			return (-1);
1768 		}
1769 	}
1770 
1771 	return (numParts);
1772 }
1773 
1774 /*
1775  * pars_fdisk
1776  * Parse user-supplied data to set up fdisk partitions
1777  * (-A, -D, -F).
1778  */
1779 static int
1780 pars_fdisk(
1781     char *line,
1782     int *id, int *act,
1783     int *bhead, int *bsect, int *bcyl,
1784     int *ehead, int *esect, int *ecyl,
1785     int *rsect, int *numsect)
1786 {
1787 	int	i;
1788 	if (line[0] == '\0' || line[0] == '\n' || line[0] == '*')
1789 		return (1);
1790 	line[strlen(line)] = '\0';
1791 	for (i = 0; i < strlen(line); i++) {
1792 		if (line[i] == '\0') {
1793 			break;
1794 		} else if (line[i] == ':') {
1795 			line[i] = ' ';
1796 		}
1797 	}
1798 	if (sscanf(line, "%d %d %d %d %d %d %d %d %d %d",
1799 	    id, act, bhead, bsect, bcyl, ehead, esect, ecyl,
1800 	    rsect, numsect) != 10) {
1801 		(void) fprintf(stderr, "Syntax error:\n	\"%s\".\n", line);
1802 		exit(1);
1803 	}
1804 	return (0);
1805 }
1806 
1807 /*
1808  * validate_part
1809  * Validate that a new partition does not start at sector 0. Only UNUSED
1810  * partitions and previously existing partitions are allowed to start at 0.
1811  */
1812 static int
1813 validate_part(int id, int rsect, int numsect)
1814 {
1815 	int i;
1816 	if ((id != UNUSED) && (rsect == 0)) {
1817 		for (i = 0; i < FD_NUMPART; i++) {
1818 			if ((Old_Table[i].systid == id) &&
1819 			    (Old_Table[i].relsect == lel(rsect)) &&
1820 			    (Old_Table[i].numsect == lel(numsect)))
1821 				return (0);
1822 		}
1823 		(void) fprintf(stderr,
1824 		    "New partition cannot start at sector 0\n");
1825 		return (-1);
1826 	}
1827 	return (0);
1828 }
1829 
1830 /*
1831  * stage0
1832  * Print out interactive menu and process user input.
1833  */
1834 static void
1835 stage0(void)
1836 {
1837 	dispmenu();
1838 	for (;;) {
1839 		(void) printf(Q_LINE);
1840 		(void) printf("Enter Selection: ");
1841 		(void) gets(s);
1842 		rm_blanks(s);
1843 		while (!((s[0] > '0') && (s[0] < '7') && (s[1] == 0))) {
1844 			(void) printf(E_LINE); /* Clear any previous error */
1845 			(void) printf(
1846 			    "Enter a one-digit number between 1 and 6.");
1847 			(void) printf(Q_LINE);
1848 			(void) printf("Enter Selection: ");
1849 			(void) gets(s);
1850 			rm_blanks(s);
1851 		}
1852 		(void) printf(E_LINE);
1853 		switch (s[0]) {
1854 			case '1':
1855 				if (pcreate() == -1)
1856 					return;
1857 				break;
1858 			case '2':
1859 				if (pchange() == -1)
1860 					return;
1861 				break;
1862 			case '3':
1863 				if (pdelete() == -1)
1864 					return;
1865 				break;
1866 			case '4':
1867 				if (ppartid() == -1)
1868 					return;
1869 				break;
1870 			case '5':
1871 				/* update disk partition table, if changed */
1872 				if (TableChanged() == 1) {
1873 					copy_Table_to_Bootblk();
1874 					dev_mboot_write(0, Bootsect, sectsiz);
1875 				}
1876 				/*
1877 				 * If the VTOC table is wrong fix it
1878 				 * (truncate only)
1879 				 */
1880 				if (io_adjt) {
1881 					fix_slice();
1882 				}
1883 				(void) close(Dev);
1884 				exit(0);
1885 				/* FALLTHRU */
1886 			case '6':
1887 				/*
1888 				 * If the VTOC table is wrong fix it
1889 				 * (truncate only)
1890 				 */
1891 				if (io_adjt) {
1892 					fix_slice();
1893 				}
1894 				(void) close(Dev);
1895 				exit(0);
1896 				/* FALLTHRU */
1897 			default:
1898 				break;
1899 		}
1900 		copy_Table_to_Bootblk();
1901 		disptbl();
1902 		dispmenu();
1903 	}
1904 }
1905 
1906 /*
1907  * pcreate
1908  * Create partition entry in the table (interactive mode).
1909  */
1910 static int
1911 pcreate(void)
1912 {
1913 	uchar_t tsystid = 'z';
1914 	int i, j;
1915 	int rsect = 1;
1916 	int retCode = 0;
1917 
1918 	i = 0;
1919 	for (;;) {
1920 		if (i == FD_NUMPART) {
1921 			(void) printf(E_LINE);
1922 			(void) printf(
1923 			    "The partition table is full!\n"
1924 			    "You must delete a partition before creating"
1925 			    " a new one.\n");
1926 			return (-1);
1927 		}
1928 		if (Table[i].systid == UNUSED) {
1929 			break;
1930 		}
1931 		i++;
1932 	}
1933 
1934 	j = 0;
1935 	for (i = 0; i < FD_NUMPART; i++) {
1936 		if (Table[i].systid != UNUSED) {
1937 			j += lel(Table[i].numsect);
1938 		}
1939 		if (j >= chs_capacity) {
1940 			(void) printf(E_LINE);
1941 			(void) printf("There is no more room on the disk for"
1942 			    " another partition.\n");
1943 			(void) printf(
1944 			    "You must delete a partition before creating"
1945 			    " a new one.\n");
1946 			return (-1);
1947 		}
1948 	}
1949 	while (tsystid == 'z') {
1950 		(void) printf(Q_LINE);
1951 		(void) printf(
1952 		    "Select the partition type to create:\n"
1953 		    "   1=SOLARIS2  2=UNIX        3=PCIXOS     4=Other\n"
1954 		    "   5=DOS12     6=DOS16       7=DOSEXT     8=DOSBIG\n"
1955 		    "   9=DOS16LBA  A=x86 Boot    B=Diagnostic C=FAT32\n"
1956 		    "   D=FAT32LBA  E=DOSEXTLBA   F=EFI        0=Exit? ");
1957 		(void) gets(s);
1958 		rm_blanks(s);
1959 		if (s[1] != 0) {
1960 			(void) printf(E_LINE);
1961 			(void) printf("Invalid selection, try again.");
1962 			continue;
1963 		}
1964 		switch (s[0]) {
1965 		case '0':		/* exit */
1966 			(void) printf(E_LINE);
1967 			return (-1);
1968 		case '1':		/* Solaris partition */
1969 			tsystid = SUNIXOS2;
1970 			break;
1971 		case '2':		/* UNIX partition */
1972 			tsystid = UNIXOS;
1973 			break;
1974 		case '3':		/* PCIXOS partition */
1975 			tsystid = PCIXOS;
1976 			break;
1977 		case '4':		/* OTHEROS System partition */
1978 			tsystid = OTHEROS;
1979 			break;
1980 		case '5':
1981 			tsystid = DOSOS12; /* DOS 12 bit fat */
1982 			break;
1983 		case '6':
1984 			tsystid = DOSOS16; /* DOS 16 bit fat */
1985 			break;
1986 		case '7':
1987 			tsystid = EXTDOS;
1988 			break;
1989 		case '8':
1990 			tsystid = DOSHUGE;
1991 			break;
1992 		case '9':
1993 			tsystid = FDISK_FAT95;  /* FAT16, need extended int13 */
1994 			break;
1995 		case 'a':		/* x86 Boot partition */
1996 		case 'A':
1997 			tsystid = X86BOOT;
1998 			break;
1999 		case 'b':		/* Diagnostic boot partition */
2000 		case 'B':
2001 			tsystid = DIAGPART;
2002 			break;
2003 		case 'c':		/* FAT32 */
2004 		case 'C':
2005 			tsystid = FDISK_WINDOWS;
2006 			break;
2007 		case 'd':		/* FAT32 and need extended int13 */
2008 		case 'D':
2009 			tsystid = FDISK_EXT_WIN;
2010 			break;
2011 		case 'e':	/* Extended partition, need extended int13 */
2012 		case 'E':
2013 			tsystid = FDISK_EXTLBA;
2014 			break;
2015 		case 'f':
2016 		case 'F':
2017 			tsystid = EFI_PMBR;
2018 			break;
2019 		default:
2020 			(void) printf(E_LINE);
2021 			(void) printf("Invalid selection, try again.");
2022 			continue;
2023 		}
2024 	}
2025 
2026 	(void) printf(E_LINE);
2027 
2028 	if (tsystid != EFI_PMBR) {
2029 		/* create the new partition */
2030 		i = specify(tsystid);
2031 
2032 		if (i != -1) {
2033 			/* see if it should be the active partition */
2034 			(void) printf(E_LINE);
2035 			(void) printf(Q_LINE);
2036 
2037 			(void) printf(
2038 			    "Should this become the active partition? If "
2039 			    "yes, it  will be activated\n"
2040 			    "each time the computer is reset or turned on.\n"
2041 			    "Please type \"y\" or \"n\". ");
2042 
2043 			if (yesno()) {
2044 				(void) printf(E_LINE);
2045 				for (j = 0; j < FD_NUMPART; j++) {
2046 					if (j == i) {
2047 						Table[j].bootid = ACTIVE;
2048 						(void) printf(E_LINE);
2049 						(void) printf(
2050 						    "Partition %d is now "
2051 						    "the active partition.",
2052 						    j + 1);
2053 					} else {
2054 						Table[j].bootid = 0;
2055 					}
2056 				}
2057 			} else {
2058 				Table[i].bootid = 0;
2059 			}
2060 
2061 			/* set up the return code */
2062 			i = 1;
2063 		}
2064 	} else {
2065 		/*
2066 		 * partitions of type EFI_PMBR must be the only partitions in
2067 		 * the table
2068 		 *
2069 		 * First, make sure there were no errors the table is
2070 		 * empty
2071 		 */
2072 		retCode = verify_tbl();
2073 
2074 		if (retCode < 0) {
2075 			(void) fprintf(stderr,
2076 			    "fdisk: Cannot create EFI partition table; \n"
2077 			    "current partition table is invalid.\n");
2078 			return (-1);
2079 		} else if (retCode > 0) {
2080 			(void) printf(
2081 			    "An EFI partition must be the only partition on "
2082 			    "disk.  You may manually delete existing\n"
2083 			    "partitions, or fdisk can do it.\n"
2084 			    "Do you want fdisk to destroy existing "
2085 			    "partitions?\n"
2086 			    "Please type \"y\" or \"n\". ");
2087 
2088 			if (yesno()) {
2089 				nulltbl();
2090 			} else {
2091 				return (-1);
2092 			}
2093 		}
2094 
2095 		/* create the table entry - i should be 0 */
2096 		i = insert_tbl(tsystid, 0, 0, 0, 0, 0, 0, 0, rsect,
2097 		    dev_capacity - rsect);
2098 
2099 		if (i != 0) {
2100 			(void) printf("Error creating EFI partition!!!\n");
2101 			i = -1;
2102 		} else {
2103 
2104 			/* EFI partitions are currently never active */
2105 			Table[i].bootid = 0;
2106 
2107 			/* set up the return code */
2108 			i = 1;
2109 		}
2110 	}
2111 
2112 	return (i);
2113 }
2114 
2115 /*
2116  * specify
2117  * Query the user to specify the size of the new partition in
2118  * terms of percentage of the disk or by specifying the starting
2119  * cylinder and length in cylinders.
2120  */
2121 static int
2122 specify(uchar_t tsystid)
2123 {
2124 	int	i, j, percent = -1;
2125 	int	cyl, cylen, first_free, size_free;
2126 	int	max_free;
2127 	int	cyl_size;
2128 	struct ipart *partition[FD_NUMPART];
2129 
2130 	cyl_size = heads * sectors;
2131 
2132 	/*
2133 	 * make a local copy of the partition table
2134 	 * and sort it into relsect order
2135 	 */
2136 	for (i = 0; i < FD_NUMPART; i++)
2137 		partition[i] = &Table[i];
2138 
2139 	for (i = 0; i < FD_NUMPART - 1; i++) {
2140 		if (partition[i]->systid == UNUSED)
2141 			break;
2142 		for (j = i + 1; j < FD_NUMPART; j++) {
2143 			if (partition[j]->systid == UNUSED)
2144 				break;
2145 			if (lel(partition[j]->relsect) <
2146 			    lel(partition[i]->relsect)) {
2147 				struct ipart *temp = partition[i];
2148 				partition[i] = partition[j];
2149 				partition[j] = temp;
2150 			}
2151 		}
2152 	}
2153 
2154 	(void) printf(Q_LINE);
2155 	(void) printf(
2156 	    "Specify the percentage of disk to use for this partition\n"
2157 	    "(or type \"c\" to specify the size in cylinders). ");
2158 	(void) gets(s);
2159 	rm_blanks(s);
2160 	if (s[0] != 'c') {	/* Specify size in percentage of disk */
2161 		i = 0;
2162 		while (s[i] != '\0') {
2163 			if (s[i] < '0' || s[i] > '9') {
2164 				(void) printf(E_LINE);
2165 				(void) printf("Invalid percentage value "
2166 				    "specified; retry the operation.");
2167 				return (-1);
2168 			}
2169 			i++;
2170 			if (i > 3) {
2171 				(void) printf(E_LINE);
2172 				(void) printf("Invalid percentage value "
2173 				    "specified; retry the operation.");
2174 				return (-1);
2175 			}
2176 		}
2177 		if ((percent = atoi(s)) > 100) {
2178 			(void) printf(E_LINE);
2179 			(void) printf(
2180 			    "Percentage value is too large. The value must be"
2181 			    " between 1 and 100;\nretry the operation.\n");
2182 			return (-1);
2183 		}
2184 		if (percent < 1) {
2185 			(void) printf(E_LINE);
2186 			(void) printf(
2187 			    "Percentage value is too small. The value must be"
2188 			    " between 1 and 100;\nretry the operation.\n");
2189 			return (-1);
2190 		}
2191 
2192 
2193 		if (percent == 100)
2194 			cylen = Numcyl - 1;
2195 		else
2196 			cylen = (Numcyl * percent) / 100;
2197 
2198 		/* Verify DOS12 partition doesn't exceed max size of 32MB. */
2199 		if ((tsystid == DOSOS12) &&
2200 		    ((long)((long)cylen * cyl_size) > MAXDOS)) {
2201 			int n;
2202 			n = MAXDOS * 100 / (int)(cyl_size) / Numcyl;
2203 			(void) printf(E_LINE);
2204 			(void) printf("Maximum size for a DOS partition "
2205 			    "is %d%%; retry the operation.",
2206 			    n <= 100 ? n : 100);
2207 			return (-1);
2208 		}
2209 
2210 
2211 		max_free = 0;
2212 		for (i = 0; i < FD_NUMPART; i++) {
2213 
2214 			/*
2215 			 * check for free space before partition i
2216 			 * where i varies from 0 to 3
2217 			 *
2218 			 * freespace after partition 3 is unusable
2219 			 * because there are no free partitions
2220 			 *
2221 			 * freespace begins at the end of previous partition
2222 			 * or cylinder 1
2223 			 */
2224 			if (i) {
2225 				/* Not an empty table */
2226 				first_free = lel(partition[i - 1]->relsect) +
2227 				    lel(partition[i - 1]->numsect);
2228 			} else {
2229 				first_free = cyl_size;
2230 			}
2231 
2232 			/*
2233 			 * freespace ends before the current partition
2234 			 * or the end of the disk (chs end)
2235 			 */
2236 			if (partition[i]->systid == UNUSED) {
2237 				size_free = chs_capacity - first_free;
2238 			} else {
2239 				size_free =
2240 				    lel(partition[i]->relsect) - first_free;
2241 			}
2242 
2243 			/* save largest free space */
2244 			if (max_free < size_free)
2245 				max_free = size_free;
2246 
2247 			if ((cylen * cyl_size) <= size_free) {
2248 				/* We found a place to use */
2249 				break;
2250 			}
2251 			if (partition[i]->systid == UNUSED) {
2252 				(void) printf(E_LINE);
2253 				max_free /= (cyl_size);
2254 				(void) fprintf(stderr, "fdisk: "
2255 				    "Maximum percentage available is %d\n",
2256 				    100 * max_free / Numcyl);
2257 				return (-1);
2258 			}
2259 		}
2260 
2261 		(void) printf(E_LINE);
2262 		if (i >= FD_NUMPART) {
2263 			(void) fprintf(stderr,
2264 			    "fdisk: Partition table is full.\n");
2265 			return (-1);
2266 		}
2267 
2268 		if ((i = insert_tbl(tsystid, 0, 0, 0, 0, 0, 0, 0,
2269 		    first_free, cylen * cyl_size)) >= 0)  {
2270 			return (i);
2271 		}
2272 		return (-1);
2273 	} else {
2274 
2275 		/* Specifying size in cylinders */
2276 		(void) printf(E_LINE);
2277 		(void) printf(Q_LINE);
2278 		(void) printf("Enter starting cylinder number: ");
2279 		if ((cyl = getcyl()) == -1) {
2280 			(void) printf(E_LINE);
2281 			(void) printf("Invalid number; retry the operation.");
2282 			return (-1);
2283 		}
2284 		if (cyl == 0) {
2285 			(void) printf(E_LINE);
2286 			(void) printf(
2287 			    "New partition cannot start at cylinder 0.\n");
2288 			return (-1);
2289 		}
2290 		if (cyl >= (unsigned int)Numcyl) {
2291 			(void) printf(E_LINE);
2292 			(void) printf(
2293 			    "Cylinder %d is out of bounds, "
2294 			    "the maximum is %d.\n",
2295 			    cyl, Numcyl - 1);
2296 			return (-1);
2297 		}
2298 		(void) printf(Q_LINE);
2299 		(void) printf("Enter partition size in cylinders: ");
2300 		if ((cylen = getcyl()) == -1) {
2301 			(void) printf(E_LINE);
2302 			(void) printf("Invalid number, retry the operation.");
2303 			return (-1);
2304 		}
2305 
2306 		for (i = 0; i < FD_NUMPART; i++) {
2307 			uint32_t	t_relsect, t_numsect;
2308 
2309 			if (partition[i]->systid == UNUSED)
2310 				break;
2311 			t_relsect = lel(partition[i]->relsect);
2312 			t_numsect = lel(partition[i]->numsect);
2313 
2314 			if (cyl * cyl_size >= t_relsect &&
2315 			    cyl * cyl_size < t_relsect + t_numsect) {
2316 				(void) printf(E_LINE);
2317 				(void) printf(
2318 				    "Cylinder %d is already allocated"
2319 				    "\nretry the operation.",
2320 				    cyl);
2321 				return (-1);
2322 			}
2323 
2324 			if (cyl * cyl_size < t_relsect &&
2325 			    (cyl + cylen - 1) * cyl_size > t_relsect) {
2326 				(void) printf(E_LINE);
2327 				(void) printf(
2328 				    "Maximum size for partition is %u cylinders"
2329 				    "\nretry the operation.",
2330 				    (t_relsect - cyl * cyl_size) / cyl_size);
2331 				return (-1);
2332 			}
2333 		}
2334 
2335 		/* Verify partition doesn't exceed disk size */
2336 		if (cyl + cylen > Numcyl) {
2337 			(void) printf(E_LINE);
2338 			(void) printf(
2339 			    "Maximum size for partition is %d cylinders;"
2340 			    "\nretry the operation.",
2341 			    Numcyl - cyl);
2342 			return (-1);
2343 		}
2344 
2345 		/* Verify DOS12 partition doesn't exceed max size of 32MB. */
2346 		if ((tsystid == DOSOS12) &&
2347 		    ((long)((long)cylen * cyl_size) > MAXDOS)) {
2348 			(void) printf(E_LINE);
2349 			(void) printf(
2350 			    "Maximum size for a %s partition is %ld cylinders;"
2351 			    "\nretry the operation.",
2352 			    Dstr, MAXDOS / (int)(cyl_size));
2353 			return (-1);
2354 		}
2355 
2356 		(void) printf(E_LINE);
2357 		i = insert_tbl(tsystid, 0, 0, 0, 0, 0, 0, 0,
2358 		    cyl * cyl_size, cylen * cyl_size);
2359 		if (i < 0)
2360 			return (-1);
2361 
2362 		if (verify_tbl() < 0) {
2363 			(void) printf(E_LINE);
2364 			(void) printf("fdisk: Cannot create partition table\n");
2365 			return (-1);
2366 		}
2367 
2368 		return (i);
2369 	}
2370 }
2371 
2372 /*
2373  * dispmenu
2374  * Display command menu (interactive mode).
2375  */
2376 static void
2377 dispmenu(void)
2378 {
2379 	(void) printf(M_LINE);
2380 	(void) printf(
2381 	    "SELECT ONE OF THE FOLLOWING:\n"
2382 	    "   1. Create a partition\n"
2383 	    "   2. Specify the active partition\n"
2384 	    "   3. Delete a partition\n"
2385 	    "   4. Change between Solaris and Solaris2 Partition IDs\n"
2386 	    "   5. Exit (update disk configuration and exit)\n"
2387 	    "   6. Cancel (exit without updating disk configuration)\n");
2388 }
2389 
2390 /*
2391  * pchange
2392  * Change the ACTIVE designation of a partition.
2393  */
2394 static int
2395 pchange(void)
2396 {
2397 	char s[80];
2398 	int i, j;
2399 
2400 	for (;;) {
2401 		(void) printf(Q_LINE);
2402 			{
2403 			(void) printf(
2404 			    "Specify the partition number to boot from"
2405 			    " (or specify 0 for none): ");
2406 			}
2407 		(void) gets(s);
2408 		rm_blanks(s);
2409 		if ((s[1] != 0) || (s[0] < '0') || (s[0] > '4')) {
2410 			(void) printf(E_LINE);
2411 			(void) printf(
2412 			    "Invalid response, please specify a number"
2413 			    " between 0 and 4.\n");
2414 		} else {
2415 			break;
2416 		}
2417 	}
2418 	if (s[0] == '0') {	/* No active partitions */
2419 		for (i = 0; i < FD_NUMPART; i++) {
2420 			if (Table[i].systid != UNUSED &&
2421 			    Table[i].bootid == ACTIVE)
2422 				Table[i].bootid = 0;
2423 		}
2424 		(void) printf(E_LINE);
2425 			(void) printf(
2426 			    "No partition is currently marked as active.");
2427 		return (0);
2428 	} else {	/* User has selected a partition to be active */
2429 		i = s[0] - '1';
2430 		if (Table[i].systid == UNUSED) {
2431 			(void) printf(E_LINE);
2432 			(void) printf("Partition does not exist.");
2433 			return (-1);
2434 		}
2435 		/* a DOS-DATA or EXT-DOS partition cannot be active */
2436 		else if ((Table[i].systid == DOSDATA) ||
2437 		    (Table[i].systid == EXTDOS) ||
2438 		    (Table[i].systid == FDISK_EXTLBA)) {
2439 			(void) printf(E_LINE);
2440 			(void) printf(
2441 			    "DOS-DATA, EXT_DOS and EXT_DOS_LBA partitions "
2442 			    "cannot be made active.\n");
2443 			(void) printf("Select another partition.");
2444 			return (-1);
2445 		}
2446 		Table[i].bootid = ACTIVE;
2447 		for (j = 0; j < FD_NUMPART; j++) {
2448 			if (j != i)
2449 			Table[j].bootid = 0;
2450 		}
2451 	}
2452 	(void) printf(E_LINE);
2453 		{
2454 		(void) printf(
2455 		    "Partition %d is now active. The system will start up"
2456 		    " from this\n", i + 1);
2457 		(void) printf("partition after the next reboot.");
2458 		}
2459 	return (1);
2460 }
2461 
2462 /*
2463  * Change between SOLARIS and SOLARIS2 partition id
2464  */
2465 static int
2466 ppartid(void)
2467 {
2468 	char	*p, s[80];
2469 	int	i;
2470 
2471 	for (;;) {
2472 		(void) printf(Q_LINE);
2473 		(void) printf("Specify the partition number to change"
2474 		    " (or enter 0 to exit): ");
2475 		if (!fgets(s, sizeof (s), stdin))
2476 			return (1);
2477 		i = strtol(s, &p, 10);
2478 
2479 		if (*p != '\n' || i < 0 || i > FD_NUMPART) {
2480 			(void) printf(E_LINE);
2481 			(void) printf(
2482 			    "Invalid response, retry the operation.\n");
2483 			continue;
2484 		}
2485 
2486 		if (i == 0) {
2487 			/* exit delete command */
2488 			(void) printf(E_LINE); /* clear error message */
2489 			return (1);
2490 		}
2491 
2492 		i -= 1;
2493 		if (Table[i].systid == SUNIXOS) {
2494 			Table[i].systid = SUNIXOS2;
2495 		} else if (Table[i].systid == SUNIXOS2) {
2496 			Table[i].systid = SUNIXOS;
2497 		} else {
2498 			(void) printf(E_LINE);
2499 			(void) printf(
2500 			    "Partition %d is not a Solaris partition.",
2501 			    i + 1);
2502 			continue;
2503 		}
2504 
2505 		(void) printf(E_LINE);
2506 		(void) printf("Partition %d has been changed.", i + 1);
2507 		return (1);
2508 	}
2509 }
2510 
2511 /*
2512  * pdelete
2513  * Remove partition entry from the table (interactive mode).
2514  */
2515 static char
2516 pdelete(void)
2517 {
2518 	char s[80];
2519 	int i, j;
2520 	char pactive;
2521 
2522 DEL1:	(void) printf(Q_LINE);
2523 	(void) printf("Specify the partition number to delete"
2524 	    " (or enter 0 to exit): ");
2525 	(void) gets(s);
2526 	rm_blanks(s);
2527 	if ((s[0] == '0')) {	/* exit delete command */
2528 		(void) printf(E_LINE);	/* clear error message */
2529 		return (1);
2530 	}
2531 	/* Accept only a single digit between 1 and 4 */
2532 	if (s[1] != 0 || (i = atoi(s)) < 1 || i > FD_NUMPART) {
2533 		(void) printf(E_LINE);
2534 		(void) printf("Invalid response, retry the operation.\n");
2535 		goto DEL1;
2536 	} else {		/* Found a digit between 1 and 4 */
2537 		--i;	/* Structure begins with element 0 */
2538 	}
2539 
2540 	if (Table[i].systid == UNUSED) {
2541 		(void) printf(E_LINE);
2542 		(void) printf("Partition %d does not exist.", i + 1);
2543 		return (-1);
2544 	}
2545 
2546 	(void) printf(Q_LINE);
2547 	(void) printf("Are you sure you want to delete partition %d?"
2548 	    " This will make all files and \n", i + 1);
2549 	(void) printf("programs in this partition inaccessible (type"
2550 	    " \"y\" or \"n\"). ");
2551 
2552 	(void) printf(E_LINE);
2553 	if (! yesno()) {
2554 		return (1);
2555 	}
2556 
2557 	if (Table[i].bootid == ACTIVE) {
2558 		pactive = 1;
2559 	} else {
2560 		pactive = 0;
2561 	}
2562 
2563 	for (j = i; j < FD_NUMPART - 1; j++) {
2564 		Table[j] = Table[j + 1];
2565 	}
2566 
2567 	Table[j].systid = UNUSED;
2568 	Table[j].numsect = 0;
2569 	Table[j].relsect = 0;
2570 	Table[j].bootid = 0;
2571 	(void) printf(E_LINE);
2572 	(void) printf("Partition %d has been deleted.", i + 1);
2573 
2574 	if (pactive) {
2575 		(void) printf(" This was the active partition.");
2576 	}
2577 
2578 	return (1);
2579 }
2580 
2581 /*
2582  * rm_blanks
2583  * Remove blanks from strings of user responses.
2584  */
2585 static void
2586 rm_blanks(char *s)
2587 {
2588 	register int i, j;
2589 
2590 	for (i = 0; i < CBUFLEN; i++) {
2591 		if ((s[i] == ' ') || (s[i] == '\t'))
2592 			continue;
2593 		else
2594 			/* Found first non-blank character of the string */
2595 			break;
2596 	}
2597 	for (j = 0; i < CBUFLEN; j++, i++) {
2598 		if ((s[j] = s[i]) == '\0') {
2599 			/* Reached end of string */
2600 			return;
2601 		}
2602 	}
2603 }
2604 
2605 /*
2606  * getcyl
2607  * Take the user-specified cylinder number and convert it from a
2608  * string to a decimal value.
2609  */
2610 static int
2611 getcyl(void)
2612 {
2613 int slen, i, j;
2614 unsigned int cyl;
2615 	(void) gets(s);
2616 	rm_blanks(s);
2617 	slen = strlen(s);
2618 	j = 1;
2619 	cyl = 0;
2620 	for (i = slen - 1; i >= 0; i--) {
2621 		if (s[i] < '0' || s[i] > '9') {
2622 			return (-1);
2623 		}
2624 		cyl += (j * (s[i] - '0'));
2625 		j *= 10;
2626 	}
2627 	return (cyl);
2628 }
2629 
2630 /*
2631  * disptbl
2632  * Display the current fdisk table; determine percentage
2633  * of the disk used for each partition.
2634  */
2635 static void
2636 disptbl(void)
2637 {
2638 	int i;
2639 	unsigned int startcyl, endcyl, length, percent, remainder;
2640 	char *stat, *type;
2641 
2642 	if ((heads == 0) || (sectors == 0)) {
2643 		(void) printf("WARNING: critical disk geometry information"
2644 		    " missing!\n");
2645 		(void) printf("\theads = %d, sectors = %d\n", heads, sectors);
2646 		exit(1);
2647 	}
2648 
2649 	(void) printf(HOME);
2650 	(void) printf(T_LINE);
2651 	(void) printf("             Total disk size is %d cylinders\n", Numcyl);
2652 	(void) printf("             Cylinder size is %d (512 byte) blocks\n\n",
2653 	    heads * sectors);
2654 	(void) printf(
2655 	    "                                               Cylinders\n");
2656 	(void) printf(
2657 	    "      Partition   Status    Type          Start   End   Length"
2658 	    "    %%\n");
2659 	(void) printf(
2660 	    "      =========   ======    ============  =====   ===   ======"
2661 	    "   ===");
2662 	for (i = 0; i < FD_NUMPART; i++) {
2663 		if (Table[i].systid == UNUSED) {
2664 			(void) printf("\n");
2665 			(void) printf(CLR_LIN);
2666 			continue;
2667 		}
2668 		if (Table[i].bootid == ACTIVE)
2669 			stat = Actvstr;
2670 		else
2671 			stat = NAstr;
2672 		switch (Table[i].systid) {
2673 		case UNIXOS:
2674 			type = Ustr;
2675 			break;
2676 		case SUNIXOS:
2677 			type = SUstr;
2678 			break;
2679 		case SUNIXOS2:
2680 			type = SU2str;
2681 			break;
2682 		case X86BOOT:
2683 			type = X86str;
2684 			break;
2685 		case DOSOS12:
2686 			type = Dstr;
2687 			break;
2688 		case DOSOS16:
2689 			type = D16str;
2690 			break;
2691 		case EXTDOS:
2692 			type = EDstr;
2693 			break;
2694 		case DOSDATA:
2695 			type = DDstr;
2696 			break;
2697 		case DOSHUGE:
2698 			type = DBstr;
2699 			break;
2700 		case PCIXOS:
2701 			type = PCstr;
2702 			break;
2703 		case DIAGPART:
2704 			type = DIAGstr;
2705 			break;
2706 		case FDISK_IFS:
2707 			type = IFSstr;
2708 			break;
2709 		case FDISK_AIXBOOT:
2710 			type = AIXstr;
2711 			break;
2712 		case FDISK_AIXDATA:
2713 			type = AIXDstr;
2714 			break;
2715 		case FDISK_OS2BOOT:
2716 			type = OS2str;
2717 			break;
2718 		case FDISK_WINDOWS:
2719 			type = WINstr;
2720 			break;
2721 		case FDISK_EXT_WIN:
2722 			type = EWINstr;
2723 			break;
2724 		case FDISK_FAT95:
2725 			type = FAT95str;
2726 			break;
2727 		case FDISK_EXTLBA:
2728 			type = EXTLstr;
2729 			break;
2730 		case FDISK_LINUX:
2731 			type = LINUXstr;
2732 			break;
2733 		case FDISK_CPM:
2734 			type = CPMstr;
2735 			break;
2736 		case FDISK_NOVELL3:
2737 			type = NOVstr;
2738 			break;
2739 		case FDISK_QNX4:
2740 			type = QNXstr;
2741 			break;
2742 		case FDISK_QNX42:
2743 			type = QNX2str;
2744 			break;
2745 		case FDISK_QNX43:
2746 			type = QNX3str;
2747 			break;
2748 		case FDISK_LINUXNAT:
2749 			type = LINNATstr;
2750 			break;
2751 		case FDISK_NTFSVOL1:
2752 			type = NTFSVOL1str;
2753 			break;
2754 		case FDISK_NTFSVOL2:
2755 			type = NTFSVOL2str;
2756 			break;
2757 		case FDISK_BSD:
2758 			type = BSDstr;
2759 			break;
2760 		case FDISK_NEXTSTEP:
2761 			type = NEXTSTEPstr;
2762 			break;
2763 		case FDISK_BSDIFS:
2764 			type = BSDIFSstr;
2765 			break;
2766 		case FDISK_BSDISWAP:
2767 			type = BSDISWAPstr;
2768 			break;
2769 		case EFI_PMBR:
2770 			type = EFIstr;
2771 			break;
2772 		default:
2773 			type = Ostr;
2774 			break;
2775 		}
2776 		startcyl = lel(Table[i].relsect) /
2777 		    (unsigned long)(heads * sectors);
2778 		length = lel(Table[i].numsect) /
2779 		    (unsigned long)(heads * sectors);
2780 		if (lel(Table[i].numsect) % (unsigned long)(heads * sectors))
2781 			length++;
2782 		endcyl = startcyl + length - 1;
2783 		percent = length * 100 / Numcyl;
2784 		if ((remainder = (length * 100 % Numcyl)) != 0) {
2785 			if ((remainder * 100 / Numcyl) > 50) {
2786 				/* round up */
2787 				percent++;
2788 			}
2789 			/* Else leave the percent as is since it's already */
2790 			/* rounded down */
2791 		}
2792 		if (percent > 100)
2793 			percent = 100;
2794 		(void) printf(
2795 		    "\n          %d       %s    %-12.12s   %4d  %4d    %4d"
2796 		    "    %3d",
2797 		    i + 1, stat, type, startcyl, endcyl, length, percent);
2798 	}
2799 	/* Print warning message if table is empty */
2800 	if (Table[0].systid == UNUSED) {
2801 		(void) printf(W_LINE);
2802 		(void) printf("WARNING: no partitions are defined!");
2803 	} else {
2804 		/* Clear the warning line */
2805 		(void) printf(W_LINE);
2806 	}
2807 }
2808 
2809 /*
2810  * print_Table
2811  * Write the detailed fdisk table to standard error for
2812  * the selected disk device.
2813  */
2814 static void
2815 print_Table(void)
2816 {
2817 	int i;
2818 
2819 	(void) fprintf(stderr,
2820 	    "  SYSID ACT BHEAD BSECT BEGCYL   EHEAD ESECT ENDCYL   RELSECT"
2821 	    "   NUMSECT\n");
2822 
2823 	for (i = 0; i < FD_NUMPART; i++) {
2824 		(void) fprintf(stderr, "  %-5d ", Table[i].systid);
2825 		(void) fprintf(stderr, "%-3d ", Table[i].bootid);
2826 		(void) fprintf(stderr, "%-5d ", Table[i].beghead);
2827 		(void) fprintf(stderr, "%-5d ", Table[i].begsect & 0x3f);
2828 		(void) fprintf(stderr, "%-8d ",
2829 		    (((uint_t)Table[i].begsect & 0xc0) << 2) + Table[i].begcyl);
2830 
2831 		(void) fprintf(stderr, "%-5d ", Table[i].endhead);
2832 		(void) fprintf(stderr, "%-5d ", Table[i].endsect & 0x3f);
2833 		(void) fprintf(stderr, "%-8d ",
2834 		    (((uint_t)Table[i].endsect & 0xc0) << 2) + Table[i].endcyl);
2835 		(void) fprintf(stderr, "%-9d ", lel(Table[i].relsect));
2836 		(void) fprintf(stderr, "%-9d\n", lel(Table[i].numsect));
2837 
2838 	}
2839 }
2840 
2841 /*
2842  * copy_Table_to_Old_Table
2843  * Copy Table into Old_Table. The function only copies the systid,
2844  * numsect, relsect, and bootid values because they are the only
2845  * ones compared when determining if Table has changed.
2846  */
2847 static void
2848 copy_Table_to_Old_Table(void)
2849 {
2850 	int i;
2851 	for (i = 0; i < FD_NUMPART; i++)  {
2852 		(void) memcpy(&Old_Table[i], &Table[i], sizeof (Table[0]));
2853 	}
2854 }
2855 
2856 /*
2857  * nulltbl
2858  * Zero out the systid, numsect, relsect, and bootid values in the
2859  * fdisk table.
2860  */
2861 static void
2862 nulltbl(void)
2863 {
2864 	int i;
2865 
2866 	for (i = 0; i < FD_NUMPART; i++)  {
2867 		Table[i].systid = UNUSED;
2868 		Table[i].numsect = lel(UNUSED);
2869 		Table[i].relsect = lel(UNUSED);
2870 		Table[i].bootid = 0;
2871 	}
2872 }
2873 
2874 /*
2875  * copy_Bootblk_to_Table
2876  * Copy the bytes from the boot record to an internal "Table".
2877  * All unused are padded with zeros starting at offset 446.
2878  */
2879 static void
2880 copy_Bootblk_to_Table(void)
2881 {
2882 	int i, j;
2883 	char *bootptr;
2884 	struct ipart iparts[FD_NUMPART];
2885 
2886 	/* Get an aligned copy of the partition tables */
2887 	(void) memcpy(iparts, Bootblk->parts, sizeof (iparts));
2888 	bootptr = (char *)iparts;	/* Points to start of partition table */
2889 	if (les(Bootblk->signature) != MBB_MAGIC)  {
2890 		/* Signature is missing */
2891 		nulltbl();
2892 		(void) memcpy(Bootblk->bootinst, &BootCod, BOOTSZ);
2893 		return;
2894 	}
2895 	/*
2896 	 * When the DOS fdisk command deletes a partition, it is not
2897 	 * recognized by the old algorithm.  The algorithm that
2898 	 * follows looks at each entry in the Bootrec and copies all
2899 	 * those that are valid.
2900 	 */
2901 	j = 0;
2902 	for (i = 0; i < FD_NUMPART; i++) {
2903 		if (iparts[i].systid == 0) {
2904 			/* Null entry */
2905 			bootptr += sizeof (struct ipart);
2906 		} else {
2907 			fill_ipart(bootptr, &Table[j]);
2908 			j++;
2909 			bootptr += sizeof (struct ipart);
2910 		}
2911 	}
2912 	for (i = j; i < FD_NUMPART; i++) {
2913 		Table[i].systid = UNUSED;
2914 		Table[i].numsect = lel(UNUSED);
2915 		Table[i].relsect = lel(UNUSED);
2916 		Table[i].bootid = 0;
2917 
2918 	}
2919 	/* For now, always replace the bootcode with ours */
2920 	(void) memcpy(Bootblk->bootinst, &BootCod, BOOTSZ);
2921 	copy_Table_to_Bootblk();
2922 }
2923 
2924 /*
2925  * fill_ipart
2926  * Initialize ipart structure values.
2927  */
2928 static void
2929 fill_ipart(char *bootptr, struct ipart *partp)
2930 {
2931 #ifdef sparc
2932 	/* Packing struct ipart for Sparc */
2933 	partp->bootid	= getbyte(&bootptr);
2934 	partp->beghead	= getbyte(&bootptr);
2935 	partp->begsect	= getbyte(&bootptr);
2936 	partp->begcyl	= getbyte(&bootptr);
2937 	partp->systid	= getbyte(&bootptr);
2938 	partp->endhead	= getbyte(&bootptr);
2939 	partp->endsect	= getbyte(&bootptr);
2940 	partp->endcyl	= getbyte(&bootptr);
2941 	partp->relsect	= (int32_t)getlong(&bootptr);
2942 	partp->numsect	= (int32_t)getlong(&bootptr);
2943 #else
2944 	*partp = *(struct ipart *)bootptr;
2945 #endif
2946 }
2947 
2948 /*
2949  * getbyte, getlong
2950  * 	Get a byte, a short, or a long (SPARC only).
2951  */
2952 #ifdef sparc
2953 uchar_t
2954 getbyte(char **bp)
2955 {
2956 	uchar_t	b;
2957 
2958 	b = (uchar_t)**bp;
2959 	*bp = *bp + 1;
2960 	return (b);
2961 }
2962 
2963 uint32_t
2964 getlong(char **bp)
2965 {
2966 	int32_t	b, bh, bl;
2967 
2968 	bh = ((**bp) << 8) | *(*bp + 1);
2969 	*bp += 2;
2970 	bl = ((**bp) << 8) | *(*bp + 1);
2971 	*bp += 2;
2972 
2973 	b = (bh << 16) | bl;
2974 	return ((uint32_t)b);
2975 }
2976 #endif
2977 
2978 /*
2979  * copy_Table_to_Bootblk
2980  * Copy the table into the 512 boot record. Note that the unused
2981  * entries will always be the last ones in the table and they are
2982  * marked with 100 in sysind. The the unused portion of the table
2983  * is padded with zeros in the bytes after the used entries.
2984  */
2985 static void
2986 copy_Table_to_Bootblk(void)
2987 {
2988 	struct ipart *boot_ptr, *tbl_ptr;
2989 
2990 	boot_ptr = (struct ipart *)Bootblk->parts;
2991 	tbl_ptr = (struct ipart *)&Table[0].bootid;
2992 	for (; tbl_ptr < (struct ipart *)&Table[FD_NUMPART].bootid;
2993 	    tbl_ptr++, boot_ptr++) {
2994 		if (tbl_ptr->systid == UNUSED)
2995 			(void) memset(boot_ptr, 0, sizeof (struct ipart));
2996 		else
2997 			(void) memcpy(boot_ptr, tbl_ptr, sizeof (struct ipart));
2998 	}
2999 	Bootblk->signature = les(MBB_MAGIC);
3000 }
3001 
3002 /*
3003  * TableChanged
3004  * 	Check for any changes in the partition table.
3005  */
3006 static int
3007 TableChanged(void)
3008 {
3009 	int i, changed;
3010 
3011 	changed = 0;
3012 	for (i = 0; i < FD_NUMPART; i++) {
3013 		if (memcmp(&Old_Table[i], &Table[i], sizeof (Table[0])) != 0) {
3014 			/* Partition table changed, write back to disk */
3015 			changed = 1;
3016 		}
3017 	}
3018 
3019 	return (changed);
3020 }
3021 
3022 /*
3023  * ffile_write
3024  * 	Display contents of partition table to standard output or
3025  *	another file name without writing it to the disk (-W file).
3026  */
3027 static void
3028 ffile_write(char *file)
3029 {
3030 	register int	i;
3031 	FILE *fp;
3032 
3033 	/*
3034 	 * If file isn't standard output, then it's a file name.
3035 	 * Open file and write it.
3036 	 */
3037 	if (file != (char *)stdout) {
3038 		if ((fp = fopen(file, "w")) == NULL) {
3039 			(void) fprintf(stderr,
3040 			    "fdisk: Cannot open output file %s.\n",
3041 			    file);
3042 			exit(1);
3043 		}
3044 	}
3045 	else
3046 		fp = stdout;
3047 
3048 	/*
3049 	 * Write the fdisk table information
3050 	 */
3051 	(void) fprintf(fp, "\n* %s default fdisk table\n", Dfltdev);
3052 	(void) fprintf(fp, "* Dimensions:\n");
3053 	(void) fprintf(fp, "*   %4d bytes/sector\n", sectsiz);
3054 	(void) fprintf(fp, "*   %4d sectors/track\n", sectors);
3055 	(void) fprintf(fp, "*   %4d tracks/cylinder\n", heads);
3056 	(void) fprintf(fp, "*   %4d cylinders\n", Numcyl);
3057 	(void) fprintf(fp, "*\n");
3058 	/* Write virtual (HBA) geometry, if required	*/
3059 	if (v_flag) {
3060 		(void) fprintf(fp, "* HBA Dimensions:\n");
3061 		(void) fprintf(fp, "*   %4d bytes/sector\n", sectsiz);
3062 		(void) fprintf(fp, "*   %4d sectors/track\n", hba_sectors);
3063 		(void) fprintf(fp, "*   %4d tracks/cylinder\n", hba_heads);
3064 		(void) fprintf(fp, "*   %4d cylinders\n", hba_Numcyl);
3065 		(void) fprintf(fp, "*\n");
3066 	}
3067 	(void) fprintf(fp, "* systid:\n");
3068 	(void) fprintf(fp, "*    1: DOSOS12\n");
3069 	(void) fprintf(fp, "*    2: PCIXOS\n");
3070 	(void) fprintf(fp, "*    4: DOSOS16\n");
3071 	(void) fprintf(fp, "*    5: EXTDOS\n");
3072 	(void) fprintf(fp, "*    6: DOSBIG\n");
3073 	(void) fprintf(fp, "*    7: FDISK_IFS\n");
3074 	(void) fprintf(fp, "*    8: FDISK_AIXBOOT\n");
3075 	(void) fprintf(fp, "*    9: FDISK_AIXDATA\n");
3076 	(void) fprintf(fp, "*   10: FDISK_0S2BOOT\n");
3077 	(void) fprintf(fp, "*   11: FDISK_WINDOWS\n");
3078 	(void) fprintf(fp, "*   12: FDISK_EXT_WIN\n");
3079 	(void) fprintf(fp, "*   14: FDISK_FAT95\n");
3080 	(void) fprintf(fp, "*   15: FDISK_EXTLBA\n");
3081 	(void) fprintf(fp, "*   18: DIAGPART\n");
3082 	(void) fprintf(fp, "*   65: FDISK_LINUX\n");
3083 	(void) fprintf(fp, "*   82: FDISK_CPM\n");
3084 	(void) fprintf(fp, "*   86: DOSDATA\n");
3085 	(void) fprintf(fp, "*   98: OTHEROS\n");
3086 	(void) fprintf(fp, "*   99: UNIXOS\n");
3087 	(void) fprintf(fp, "*  101: FDISK_NOVELL3\n");
3088 	(void) fprintf(fp, "*  119: FDISK_QNX4\n");
3089 	(void) fprintf(fp, "*  120: FDISK_QNX42\n");
3090 	(void) fprintf(fp, "*  121: FDISK_QNX43\n");
3091 	(void) fprintf(fp, "*  130: SUNIXOS\n");
3092 	(void) fprintf(fp, "*  131: FDISK_LINUXNAT\n");
3093 	(void) fprintf(fp, "*  134: FDISK_NTFSVOL1\n");
3094 	(void) fprintf(fp, "*  135: FDISK_NTFSVOL2\n");
3095 	(void) fprintf(fp, "*  165: FDISK_BSD\n");
3096 	(void) fprintf(fp, "*  167: FDISK_NEXTSTEP\n");
3097 	(void) fprintf(fp, "*  183: FDISK_BSDIFS\n");
3098 	(void) fprintf(fp, "*  184: FDISK_BSDISWAP\n");
3099 	(void) fprintf(fp, "*  190: X86BOOT\n");
3100 	(void) fprintf(fp, "*  191: SUNIXOS2\n");
3101 	(void) fprintf(fp, "*  238: EFI_PMBR\n");
3102 	(void) fprintf(fp, "*  239: EFI_FS\n");
3103 	(void) fprintf(fp, "*\n");
3104 	(void) fprintf(fp,
3105 	    "\n* Id    Act  Bhead  Bsect  Bcyl    Ehead  Esect  Ecyl"
3106 	    "    Rsect    Numsect\n");
3107 	for (i = 0; i < FD_NUMPART; i++) {
3108 		if (Table[i].systid != UNUSED)
3109 			(void) fprintf(fp,
3110 			    "  %-5d %-4d %-6d %-6d %-7d %-6d %-6d %-7d %-8d"
3111 			    " %-8d\n",
3112 			    Table[i].systid,
3113 			    Table[i].bootid,
3114 			    Table[i].beghead,
3115 			    Table[i].begsect & 0x3f,
3116 			    ((Table[i].begcyl & 0xff) | ((Table[i].begsect &
3117 			    0xc0) << 2)),
3118 			    Table[i].endhead,
3119 			    Table[i].endsect & 0x3f,
3120 			    ((Table[i].endcyl & 0xff) | ((Table[i].endsect &
3121 			    0xc0) << 2)),
3122 			    lel(Table[i].relsect),
3123 			    lel(Table[i].numsect));
3124 	}
3125 	if (fp != stdout)
3126 		(void) fclose(fp);
3127 }
3128 
3129 /*
3130  * fix_slice
3131  * 	Read the VTOC table on the Solaris partition and check that no
3132  *	slices exist that extend past the end of the Solaris partition.
3133  *	If no Solaris partition exists, nothing is done.
3134  */
3135 static void
3136 fix_slice(void)
3137 {
3138 	int	i;
3139 	int	numsect;
3140 
3141 	if (io_image) {
3142 		return;
3143 	}
3144 
3145 	for (i = 0; i < FD_NUMPART; i++) {
3146 		if (Table[i].systid == SUNIXOS || Table[i].systid == SUNIXOS2) {
3147 			/*
3148 			 * Only the size matters (not starting point), since
3149 			 * VTOC entries are relative to the start of
3150 			 * the partition.
3151 			 */
3152 			numsect = lel(Table[i].numsect);
3153 			break;
3154 		}
3155 	}
3156 
3157 	if (i >= FD_NUMPART) {
3158 		if (!io_nifdisk) {
3159 			(void) fprintf(stderr,
3160 			    "fdisk: No Solaris partition found - VTOC not"
3161 			    " checked.\n");
3162 		}
3163 		return;
3164 	}
3165 
3166 	if (readvtoc() != VTOC_OK) {
3167 		exit(1);		/* Failed to read the VTOC */
3168 	}
3169 	for (i = 0; i < V_NUMPAR; i++) {
3170 		/* Special case for slice two (entire disk) */
3171 		if (i == 2) {
3172 			if (disk_vtoc.v_part[i].p_start != 0) {
3173 				(void) fprintf(stderr,
3174 				    "slice %d starts at %ld, is not at"
3175 				    " start of partition",
3176 				    i, disk_vtoc.v_part[i].p_start);
3177 				if (!io_nifdisk) {
3178 					(void) printf(" adjust ?:");
3179 					if (yesno())
3180 						disk_vtoc.v_part[i].p_start = 0;
3181 				} else {
3182 					disk_vtoc.v_part[i].p_start = 0;
3183 					(void) fprintf(stderr, " adjusted!\n");
3184 				}
3185 
3186 			}
3187 			if (disk_vtoc.v_part[i].p_size != numsect) {
3188 				(void) fprintf(stderr,
3189 				    "slice %d size %ld does not cover"
3190 				    " complete partition",
3191 				    i, disk_vtoc.v_part[i].p_size);
3192 				if (!io_nifdisk) {
3193 					(void) printf(" adjust ?:");
3194 					if (yesno())
3195 						disk_vtoc.v_part[i].p_size =
3196 						    numsect;
3197 				} else {
3198 					disk_vtoc.v_part[i].p_size = numsect;
3199 					(void) fprintf(stderr, " adjusted!\n");
3200 				}
3201 			}
3202 			if (disk_vtoc.v_part[i].p_tag != V_BACKUP) {
3203 				(void) fprintf(stderr,
3204 				    "slice %d tag was %d should be %d",
3205 				    i, disk_vtoc.v_part[i].p_tag,
3206 				    V_BACKUP);
3207 				if (!io_nifdisk) {
3208 					(void) printf(" fix ?:");
3209 					if (yesno())
3210 						disk_vtoc.v_part[i].p_tag =
3211 						    V_BACKUP;
3212 				} else {
3213 					disk_vtoc.v_part[i].p_tag = V_BACKUP;
3214 					(void) fprintf(stderr, " fixed!\n");
3215 				}
3216 			}
3217 			continue;
3218 		}
3219 		if (io_ADJT) {
3220 			if (disk_vtoc.v_part[i].p_start > numsect ||
3221 			    disk_vtoc.v_part[i].p_start +
3222 			    disk_vtoc.v_part[i].p_size > numsect) {
3223 				(void) fprintf(stderr,
3224 				    "slice %d (start %ld, end %ld)"
3225 				    " is larger than the partition",
3226 				    i, disk_vtoc.v_part[i].p_start,
3227 				    disk_vtoc.v_part[i].p_start +
3228 				    disk_vtoc.v_part[i].p_size);
3229 				if (!io_nifdisk) {
3230 					(void) printf(" remove ?:");
3231 					if (yesno()) {
3232 						disk_vtoc.v_part[i].p_size = 0;
3233 						disk_vtoc.v_part[i].p_start = 0;
3234 						disk_vtoc.v_part[i].p_tag = 0;
3235 						disk_vtoc.v_part[i].p_flag = 0;
3236 					}
3237 				} else {
3238 					disk_vtoc.v_part[i].p_size = 0;
3239 					disk_vtoc.v_part[i].p_start = 0;
3240 					disk_vtoc.v_part[i].p_tag = 0;
3241 					disk_vtoc.v_part[i].p_flag = 0;
3242 					(void) fprintf(stderr,
3243 					    " removed!\n");
3244 				}
3245 			}
3246 			continue;
3247 		}
3248 		if (disk_vtoc.v_part[i].p_start > numsect) {
3249 			(void) fprintf(stderr,
3250 			    "slice %d (start %ld) is larger than the partition",
3251 			    i, disk_vtoc.v_part[i].p_start);
3252 			if (!io_nifdisk) {
3253 				(void) printf(" remove ?:");
3254 				if (yesno()) {
3255 					disk_vtoc.v_part[i].p_size = 0;
3256 					disk_vtoc.v_part[i].p_start = 0;
3257 					disk_vtoc.v_part[i].p_tag = 0;
3258 					disk_vtoc.v_part[i].p_flag = 0;
3259 				}
3260 			} else {
3261 				disk_vtoc.v_part[i].p_size = 0;
3262 				disk_vtoc.v_part[i].p_start = 0;
3263 				disk_vtoc.v_part[i].p_tag = 0;
3264 				disk_vtoc.v_part[i].p_flag = 0;
3265 				(void) fprintf(stderr,
3266 				" removed!\n");
3267 			}
3268 		} else if (disk_vtoc.v_part[i].p_start
3269 		    + disk_vtoc.v_part[i].p_size > numsect) {
3270 			(void) fprintf(stderr,
3271 			    "slice %d (end %ld) is larger"
3272 			    " than the partition",
3273 			    i,
3274 			    disk_vtoc.v_part[i].p_start +
3275 			    disk_vtoc.v_part[i].p_size);
3276 			if (!io_nifdisk) {
3277 				(void) printf(" adjust ?:");
3278 				if (yesno()) {
3279 					disk_vtoc.v_part[i].p_size = numsect;
3280 				}
3281 			} else {
3282 				disk_vtoc.v_part[i].p_size = numsect;
3283 				(void) fprintf(stderr, " adjusted!\n");
3284 			}
3285 		}
3286 	}
3287 #if 1		/* bh for now */
3288 	/* Make the VTOC look sane - ha ha */
3289 	disk_vtoc.v_version = V_VERSION;
3290 	disk_vtoc.v_sanity = VTOC_SANE;
3291 	disk_vtoc.v_nparts = V_NUMPAR;
3292 	if (disk_vtoc.v_sectorsz == 0)
3293 		disk_vtoc.v_sectorsz = NBPSCTR;
3294 #endif
3295 
3296 	/* Write the VTOC back to the disk */
3297 	if (!io_readonly)
3298 		(void) writevtoc();
3299 }
3300 
3301 /*
3302  * yesno
3303  * Get yes or no answer. Return 1 for yes and 0 for no.
3304  */
3305 
3306 static int
3307 yesno(void)
3308 {
3309 	char	s[80];
3310 
3311 	for (;;) {
3312 		(void) gets(s);
3313 		rm_blanks(s);
3314 		if ((s[1] != 0) || ((s[0] != 'y') && (s[0] != 'n'))) {
3315 			(void) printf(E_LINE);
3316 			(void) printf("Please answer with \"y\" or \"n\": ");
3317 			continue;
3318 		}
3319 		if (s[0] == 'y')
3320 			return (1);
3321 		else
3322 			return (0);
3323 	}
3324 }
3325 
3326 /*
3327  * readvtoc
3328  * 	Read the VTOC from the Solaris partition of the device.
3329  */
3330 static int
3331 readvtoc(void)
3332 {
3333 	int	i;
3334 	int	retval = VTOC_OK;
3335 
3336 	if ((i = read_vtoc(Dev, &disk_vtoc)) < VTOC_OK) {
3337 		if (i == VT_EINVAL) {
3338 			(void) fprintf(stderr, "fdisk: Invalid VTOC.\n");
3339 			vt_inval++;
3340 			retval = VTOC_INVAL;
3341 		} else if (i == VT_ENOTSUP) {
3342 			(void) fprintf(stderr, "fdisk: partition may have EFI "
3343 			    "GPT\n");
3344 			retval = VTOC_NOTSUP;
3345 		} else {
3346 			(void) fprintf(stderr, "fdisk: Cannot read VTOC.\n");
3347 			retval = VTOC_RWERR;
3348 		}
3349 	}
3350 	return (retval);
3351 }
3352 
3353 /*
3354  * writevtoc
3355  * 	Write the VTOC to the Solaris partition on the device.
3356  */
3357 static int
3358 writevtoc(void)
3359 {
3360 	int	i;
3361 	int	retval = 0;
3362 
3363 	if ((i = write_vtoc(Dev, &disk_vtoc)) != 0) {
3364 		if (i == VT_EINVAL) {
3365 			(void) fprintf(stderr,
3366 			    "fdisk: Invalid entry exists in VTOC.\n");
3367 			retval = VTOC_INVAL;
3368 		} else if (i == VT_ENOTSUP) {
3369 			(void) fprintf(stderr, "fdisk: partition may have EFI "
3370 			    "GPT\n");
3371 			retval = VTOC_NOTSUP;
3372 		} else {
3373 			(void) fprintf(stderr, "fdisk: Cannot write VTOC.\n");
3374 			retval = VTOC_RWERR;
3375 		}
3376 	}
3377 	return (retval);
3378 }
3379 
3380 /*
3381  * efi_ioctl
3382  * issues DKIOCSETEFI IOCTL
3383  * (duplicate of private efi_ioctl() in rdwr_efi.c
3384  */
3385 static int
3386 efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
3387 {
3388 	void *data = dk_ioc->dki_data;
3389 	int error;
3390 
3391 	dk_ioc->dki_data_64 = (uintptr_t)data;
3392 	error = ioctl(fd, cmd, (void *)dk_ioc);
3393 
3394 	return (error);
3395 }
3396 
3397 /*
3398  * clear_efi
3399  * Clear EFI labels from the EFI_PMBR partition on the device
3400  * This function is modeled on the libefi(3LIB) call efi_write()
3401  */
3402 static int
3403 clear_efi(void)
3404 {
3405 	struct dk_gpt	*efi_vtoc;
3406 	dk_efi_t	dk_ioc;
3407 
3408 	/*
3409 	 * see if we can read the EFI label
3410 	 */
3411 	if (efi_alloc_and_read(Dev, &efi_vtoc) < 0) {
3412 		return (VT_ERROR);
3413 	}
3414 
3415 	/*
3416 	 * set up the dk_ioc structure for writing
3417 	 */
3418 	dk_ioc.dki_lba = 1;
3419 	dk_ioc.dki_length = EFI_MIN_ARRAY_SIZE + efi_vtoc->efi_lbasize;
3420 
3421 	if ((dk_ioc.dki_data = calloc(dk_ioc.dki_length, 1)) == NULL) {
3422 		return (VT_ERROR);
3423 	}
3424 
3425 	/*
3426 	 * clear the primary label
3427 	 */
3428 	if (io_debug) {
3429 		(void) fprintf(stderr,
3430 		    "\tClearing primary EFI label at block %lld\n",
3431 		    dk_ioc.dki_lba);
3432 	}
3433 
3434 	if (efi_ioctl(Dev, DKIOCSETEFI, &dk_ioc) == -1) {
3435 		free(dk_ioc.dki_data);
3436 		switch (errno) {
3437 			case EIO:
3438 				return (VT_EIO);
3439 			case EINVAL:
3440 				return (VT_EINVAL);
3441 			default:
3442 				return (VT_ERROR);
3443 		}
3444 	}
3445 
3446 	/*
3447 	 * clear the backup partition table
3448 	 */
3449 	dk_ioc.dki_lba = efi_vtoc->efi_last_u_lba + 1;
3450 	dk_ioc.dki_length -= efi_vtoc->efi_lbasize;
3451 	dk_ioc.dki_data++;
3452 	if (io_debug) {
3453 		(void) fprintf(stderr,
3454 		    "\tClearing backup partition table at block %lld\n",
3455 		    dk_ioc.dki_lba);
3456 	}
3457 
3458 	if (efi_ioctl(Dev, DKIOCSETEFI, &dk_ioc) == -1) {
3459 		(void) fprintf(stderr, "\tUnable to clear backup EFI label at "
3460 		    "block %llu; errno %d\n", efi_vtoc->efi_last_u_lba + 1,
3461 		    errno);
3462 	}
3463 
3464 	/*
3465 	 * clear the backup label
3466 	 */
3467 	dk_ioc.dki_lba = efi_vtoc->efi_last_lba;
3468 	dk_ioc.dki_length = efi_vtoc->efi_lbasize;
3469 	dk_ioc.dki_data--;
3470 	if (io_debug) {
3471 		(void) fprintf(stderr, "\tClearing backup label at block "
3472 		    "%lld\n", dk_ioc.dki_lba);
3473 	}
3474 
3475 	if (efi_ioctl(Dev, DKIOCSETEFI, &dk_ioc) == -1) {
3476 		(void) fprintf(stderr,
3477 		    "\tUnable to clear backup EFI label at "
3478 		    "block %llu; errno %d\n",
3479 		    efi_vtoc->efi_last_lba,
3480 		    errno);
3481 	}
3482 
3483 	free(dk_ioc.dki_data);
3484 	efi_free(efi_vtoc);
3485 
3486 	return (0);
3487 }
3488 
3489 /*
3490  * clear_vtoc
3491  * 	Clear the VTOC from the current or previous Solaris partition on the
3492  *      device.
3493  */
3494 static void
3495 clear_vtoc(int table, int part)
3496 {
3497 	struct ipart *clr_table;
3498 	struct dk_label disk_label;
3499 	int pcyl, ncyl, count, bytes;
3500 	uint_t backup_block, solaris_offset;
3501 	off_t seek_byte;
3502 
3503 #ifdef DEBUG
3504 	struct dk_label	read_label;
3505 #endif /* DEBUG */
3506 
3507 	if (table == OLD) {
3508 		clr_table = &Old_Table[part];
3509 	} else {
3510 		clr_table = &Table[part];
3511 	}
3512 
3513 	(void) memset(&disk_label, 0, sizeof (struct dk_label));
3514 
3515 	seek_byte = (off_t)(lel(clr_table->relsect) + VTOC_OFFSET) * sectsiz;
3516 
3517 	if (io_debug) {
3518 		(void) fprintf(stderr,
3519 		    "\tClearing primary VTOC at byte %llu (block %llu)\n",
3520 		    (uint64_t)seek_byte,
3521 		    (uint64_t)(lel(clr_table->relsect) + VTOC_OFFSET));
3522 	}
3523 
3524 	if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3525 		(void) fprintf(stderr,
3526 		    "\tError seeking to primary label at byte %llu\n",
3527 		    (uint64_t)seek_byte);
3528 		return;
3529 	}
3530 
3531 	bytes = write(Dev, &disk_label, sizeof (struct dk_label));
3532 
3533 	if (bytes != sizeof (struct dk_label)) {
3534 		(void) fprintf(stderr,
3535 		    "\tWarning: only %d bytes written to clear primary VTOC!\n",
3536 		    bytes);
3537 	}
3538 
3539 #ifdef DEBUG
3540 	if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3541 		(void) fprintf(stderr,
3542 		    "DEBUG: Error seeking to primary label at byte %llu\n",
3543 		    (uint64_t)seek_byte);
3544 		return;
3545 	} else {
3546 		(void) fprintf(stderr,
3547 		    "DEBUG: Successful lseek() to byte %llu\n",
3548 		    (uint64_t)seek_byte);
3549 	}
3550 
3551 	bytes = read(Dev, &read_label, sizeof (struct dk_label));
3552 
3553 	if (bytes != sizeof (struct dk_label)) {
3554 		(void) fprintf(stderr,
3555 		    "DEBUG: Warning: only %d bytes read of label\n",
3556 		    bytes);
3557 	}
3558 
3559 	if (memcmp(&disk_label, &read_label, sizeof (struct dk_label)) != 0) {
3560 		(void) fprintf(stderr,
3561 		    "DEBUG: Warning: disk_label and read_label differ!!!\n");
3562 	} else {
3563 		(void) fprintf(stderr, "DEBUG Good compare of disk_label and "
3564 		    "read_label\n");
3565 	}
3566 #endif /* DEBUG */
3567 
3568 	/* Clear backup label */
3569 	pcyl = lel(clr_table->numsect) / (heads * sectors);
3570 	solaris_offset = lel(clr_table->relsect);
3571 	ncyl = pcyl - acyl;
3572 
3573 	backup_block = ((ncyl + acyl - 1) *
3574 	    (heads * sectors)) + ((heads - 1) * sectors) + 1;
3575 
3576 	for (count = 1; count < 6; count++) {
3577 		seek_byte = (off_t)(solaris_offset + backup_block) * 512;
3578 
3579 		if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3580 			(void) fprintf(stderr,
3581 			    "\tError seeking to backup label at byte %llu on "
3582 			    "%s.\n", (uint64_t)seek_byte, Dfltdev);
3583 			return;
3584 		}
3585 
3586 		if (io_debug) {
3587 			(void) fprintf(stderr, "\tClearing backup VTOC at"
3588 			    " byte %llu (block %llu)\n",
3589 			    (uint64_t)seek_byte,
3590 			    (uint64_t)(solaris_offset + backup_block));
3591 		}
3592 
3593 		bytes = write(Dev, &disk_label, sizeof (struct dk_label));
3594 
3595 		if (bytes != sizeof (struct dk_label)) {
3596 			(void) fprintf(stderr,
3597 			    "\t\tWarning: only %d bytes written to "
3598 			    "clear backup VTOC at block %llu!\n", bytes,
3599 			    (uint64_t)(solaris_offset + backup_block));
3600 		}
3601 
3602 #ifdef DEBUG
3603 	if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3604 		(void) fprintf(stderr,
3605 		    "DEBUG: Error seeking to backup label at byte %llu\n",
3606 		    (uint64_t)seek_byte);
3607 		return;
3608 	} else {
3609 		(void) fprintf(stderr,
3610 		    "DEBUG: Successful lseek() to byte %llu\n",
3611 		    (uint64_t)seek_byte);
3612 	}
3613 
3614 	bytes = read(Dev, &read_label, sizeof (struct dk_label));
3615 
3616 	if (bytes != sizeof (struct dk_label)) {
3617 		(void) fprintf(stderr,
3618 		    "DEBUG: Warning: only %d bytes read of backup label\n",
3619 		    bytes);
3620 	}
3621 
3622 	if (memcmp(&disk_label, &read_label, sizeof (struct dk_label)) != 0) {
3623 		(void) fprintf(stderr,
3624 		    "DEBUG: Warning: disk_label and read_label differ!!!\n");
3625 	} else {
3626 		(void) fprintf(stderr,
3627 		    "DEBUG: Good compare of disk_label and backup "
3628 		    "read_label\n");
3629 	}
3630 #endif /* DEBUG */
3631 
3632 		backup_block += 2;
3633 	}
3634 }
3635 
3636 #define	FDISK_STANDARD_LECTURE \
3637 	"Fdisk is normally used with the device that " \
3638 	"represents the entire fixed disk.\n" \
3639 	"(For example, /dev/rdsk/c0d0p0 on x86 or " \
3640 	"/dev/rdsk/c0t5d0s2 on sparc).\n"
3641 
3642 #define	FDISK_LECTURE_NOT_SECTOR_ZERO \
3643 	"The device does not appear to include absolute\n" \
3644 	"sector 0 of the PHYSICAL disk " \
3645 	"(the normal location for an fdisk table).\n"
3646 
3647 #define	FDISK_LECTURE_NOT_FULL \
3648 	"The device does not appear to encompass the entire PHYSICAL disk.\n"
3649 
3650 #define	FDISK_LECTURE_NO_VTOC \
3651 	"Unable to find a volume table of contents.\n" \
3652 	"Cannot verify the device encompasses the full PHYSICAL disk.\n"
3653 
3654 #define	FDISK_LECTURE_NO_GEOM \
3655 	"Unable to get geometry from device.\n" \
3656 	"Cannot verify the device encompasses the full PHYSICAL disk.\n"
3657 
3658 #define	FDISK_SHALL_I_CONTINUE \
3659 	"Are you sure you want to continue? (y/n) "
3660 
3661 /*
3662  *  lecture_and_query
3663  *	Called when a sanity check fails.  This routine gives a warning
3664  *	specific to the check that fails, followed by a generic lecture
3665  *	about the "right" device to supply as input.  Then, if appropriate,
3666  *	it will prompt the user on whether or not they want to continue.
3667  *	Inappropriate times for prompting are when the user has selected
3668  *	non-interactive mode or read-only mode.
3669  */
3670 static int
3671 lecture_and_query(char *warning, char *devname)
3672 {
3673 	if (io_nifdisk)
3674 		return (0);
3675 
3676 	(void) fprintf(stderr, "WARNING: Device %s: \n", devname);
3677 	(void) fprintf(stderr, "%s", warning);
3678 	(void) fprintf(stderr, FDISK_STANDARD_LECTURE);
3679 	(void) fprintf(stderr, FDISK_SHALL_I_CONTINUE);
3680 
3681 	return (yesno());
3682 }
3683 
3684 static void
3685 sanity_check_provided_device(char *devname, int fd)
3686 {
3687 	struct vtoc v;
3688 	struct dk_geom d;
3689 	struct part_info pi;
3690 	long totsize;
3691 	int idx = -1;
3692 
3693 	/*
3694 	 *  First try the PARTINFO ioctl.  If it works, we will be able
3695 	 *  to tell if they've specified the full disk partition by checking
3696 	 *  to see if they've specified a partition that starts at sector 0.
3697 	 */
3698 	if (ioctl(fd, DKIOCPARTINFO, &pi) != -1) {
3699 		if (pi.p_start != 0) {
3700 			if (!lecture_and_query(FDISK_LECTURE_NOT_SECTOR_ZERO,
3701 			    devname)) {
3702 				(void) close(fd);
3703 				exit(1);
3704 			}
3705 		}
3706 	} else {
3707 		if ((idx = read_vtoc(fd, &v)) < 0) {
3708 			if (!lecture_and_query(FDISK_LECTURE_NO_VTOC,
3709 			    devname)) {
3710 				(void) close(fd);
3711 				exit(1);
3712 			}
3713 			return;
3714 		}
3715 		if (ioctl(fd, DKIOCGGEOM, &d) == -1) {
3716 			perror(devname);
3717 			if (!lecture_and_query(FDISK_LECTURE_NO_GEOM,
3718 			    devname)) {
3719 				(void) close(fd);
3720 				exit(1);
3721 			}
3722 			return;
3723 		}
3724 		totsize = d.dkg_ncyl * d.dkg_nhead * d.dkg_nsect;
3725 		if (v.v_part[idx].p_size != totsize) {
3726 			if (!lecture_and_query(FDISK_LECTURE_NOT_FULL,
3727 			    devname)) {
3728 				(void) close(fd);
3729 				exit(1);
3730 			}
3731 		}
3732 	}
3733 }
3734 
3735 
3736 /*
3737  * get_node
3738  * Called from main to construct the name of the device node to open.
3739  * Initially tries to stat the node exactly as provided, if that fails
3740  * we prepend the default path (/dev/rdsk/).
3741  */
3742 static char *
3743 get_node(char *devname)
3744 {
3745 	char *node;
3746 	struct stat statbuf;
3747 	size_t space;
3748 
3749 	/* Don't do anything if we are skipping device checks */
3750 	if (io_image)
3751 		return (devname);
3752 
3753 	node = devname;
3754 
3755 	/* Try the node as provided first */
3756 	if (stat(node, (struct stat *)&statbuf) == -1) {
3757 		/*
3758 		 * Copy the passed in string to a new buffer, prepend the
3759 		 * default path and try again.
3760 		 */
3761 		space = strlen(DEFAULT_PATH) + strlen(devname) + 1;
3762 
3763 		if ((node = malloc(space)) == NULL) {
3764 			(void) fprintf(stderr, "fdisk: Unable to obtain memory "
3765 			    "for device node.\n");
3766 			exit(1);
3767 		}
3768 
3769 		/* Copy over the default path and the provided node */
3770 		(void) strncpy(node, DEFAULT_PATH, strlen(DEFAULT_PATH));
3771 		space -= strlen(DEFAULT_PATH);
3772 		(void) strlcpy(node + strlen(DEFAULT_PATH), devname, space);
3773 
3774 		/* Try to stat it again */
3775 		if (stat(node, (struct stat *)&statbuf) == -1) {
3776 			/* Failed all options, give up */
3777 			(void) fprintf(stderr,
3778 			    "fdisk: Cannot stat device %s.\n",
3779 			    devname);
3780 			exit(1);
3781 		}
3782 	}
3783 
3784 	/* Make sure the device specified is the raw device */
3785 	if ((statbuf.st_mode & S_IFMT) != S_IFCHR) {
3786 		(void) fprintf(stderr,
3787 		    "fdisk: %s must be a raw device.\n", node);
3788 		exit(1);
3789 	}
3790 
3791 	return (node);
3792 }
3793