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