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