xref: /illumos-gate/usr/src/cmd/fdisk/fdisk.c (revision 1237e847)
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 2007 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, old_solaris = -1, new_solaris = -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 	/* see if the old table had EFI or Solaris partitions */
983 	for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) {
984 		if (Old_Table[old_pt].systid == SUNIXOS ||
985 		    Old_Table[old_pt].systid == SUNIXOS2) {
986 			old_solaris = old_pt;
987 		} else if (Old_Table[old_pt].systid == EFI_PMBR) {
988 			clr_efi = old_pt;
989 		}
990 	}
991 
992 	/* look to see if Solaris partition changed in relsect/numsect */
993 	for (new_pt = 0; new_pt < FD_NUMPART; new_pt++) {
994 
995 		/*
996 		 * if this is not a Solaris partition, ignore it
997 		 */
998 		if (Table[new_pt].systid != SUNIXOS &&
999 		    Table[new_pt].systid != SUNIXOS2)
1000 			continue;
1001 
1002 		/*
1003 		 * if there was no previous Solaris partition
1004 		 * or if the old partition was in a different place
1005 		 * or if the old partition was a different size
1006 		 * then this must be a new Solaris partition
1007 		 */
1008 		if (old_solaris == -1 ||
1009 		    Old_Table[old_solaris].relsect != Table[new_pt].relsect ||
1010 		    Old_Table[old_solaris].numsect != Table[new_pt].numsect) {
1011 			new_solaris = new_pt;
1012 			break;
1013 		}
1014 	}
1015 
1016 	/* look to see if a EFI partition changed in relsect/numsect */
1017 	for (new_pt = 0; new_pt < FD_NUMPART; new_pt++) {
1018 		if (Table[new_pt].systid != EFI_PMBR)
1019 			continue;
1020 		for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) {
1021 			if ((Old_Table[old_pt].systid ==
1022 			    Table[new_pt].systid) &&
1023 			    (Old_Table[old_pt].relsect ==
1024 			    Table[new_pt].relsect) &&
1025 			    (Old_Table[old_pt].numsect ==
1026 			    Table[new_pt].numsect))
1027 				break;
1028 		}
1029 
1030 		/*
1031 		 * if EFI partition changed, set the flag to clear
1032 		 * the EFI GPT
1033 		 */
1034 		if (old_pt == FD_NUMPART && Table[new_pt].begcyl != 0) {
1035 			clr_efi = 0;
1036 		}
1037 		break;
1038 	}
1039 
1040 	/* clear labels if necessary */
1041 	if (clr_efi >= 0) {
1042 		if (io_debug) {
1043 			(void) fprintf(stderr, "Clearing EFI labels\n");
1044 		}
1045 		if ((error = clear_efi()) != 0) {
1046 			if (io_debug) {
1047 				(void) fprintf(stderr,
1048 				    "\tError %d clearing EFI labels"
1049 				    " (probably no EFI labels exist)\n",
1050 				    error);
1051 			}
1052 		}
1053 	}
1054 
1055 	if (new_solaris >= 0) {
1056 		if (io_debug) {
1057 			(void) fprintf(stderr, "Clearing VTOC labels from NEW"
1058 			    " table\n");
1059 		}
1060 		clear_vtoc(NEW, new_solaris);
1061 	}
1062 
1063 	if ((ioctl(Dev, DKIOCSMBOOT, buff) == -1) && (errno != ENOTTY)) {
1064 		(void) fprintf(stderr,
1065 		    "fdisk: Error in ioctl DKIOCSMBOOT on %s.\n",
1066 		    Dfltdev);
1067 	}
1068 	if (errno == 0)
1069 		return;
1070 
1071 	/* write to disk drive */
1072 	if (lseek(Dev, sect, SEEK_SET) == -1) {
1073 		(void) fprintf(stderr,
1074 		    "fdisk: Error seeking to master boot record on %s.\n",
1075 		    Dfltdev);
1076 		exit(1);
1077 	}
1078 	if (write(Dev, buff, bootsiz) != bootsiz) {
1079 		(void) fprintf(stderr,
1080 		    "fdisk: Error writing master boot record to %s.\n",
1081 		    Dfltdev);
1082 		exit(1);
1083 	}
1084 }
1085 
1086 /*
1087  * mboot_read
1088  * Read the prototype boot records from the files.
1089  */
1090 static void
1091 mboot_read(void)
1092 {
1093 	int mDev, i;
1094 	struct ipart *part;
1095 
1096 #if defined(i386) || defined(sparc)
1097 	/*
1098 	 * If the master boot file hasn't been specified, use the
1099 	 * implementation architecture name to generate the default one.
1100 	 */
1101 	if (io_mboot == (char *)0) {
1102 		/*
1103 		 * Bug ID 1249035:
1104 		 *	The mboot file must be delivered on all platforms
1105 		 *	and installed in a non-platform-dependent
1106 		 *	directory; i.e., /usr/lib/fs/ufs.
1107 		 */
1108 		io_mboot = "/usr/lib/fs/ufs/mboot";
1109 	}
1110 
1111 	/* First read in the master boot record */
1112 
1113 	/* Open the master boot proto file */
1114 	if ((mDev = open(io_mboot, O_RDONLY, 0666)) == -1) {
1115 		(void) fprintf(stderr,
1116 		    "fdisk: Cannot open master boot file %s.\n",
1117 		    io_mboot);
1118 		exit(1);
1119 	}
1120 
1121 	/* Read the master boot program */
1122 	if (read(mDev, &BootCod, sizeof (struct mboot)) != sizeof
1123 	    (struct mboot)) {
1124 		(void) fprintf(stderr,
1125 		    "fdisk: Cannot read master boot file %s.\n",
1126 		    io_mboot);
1127 		exit(1);
1128 	}
1129 
1130 	/* Is this really a master boot record? */
1131 	if (les(BootCod.signature) != MBB_MAGIC) {
1132 		(void) fprintf(stderr,
1133 		    "fdisk: Invalid master boot file %s.\n", io_mboot);
1134 		(void) fprintf(stderr,
1135 		    "Bad magic number: is %x, but should be %x.\n",
1136 		    les(BootCod.signature), MBB_MAGIC);
1137 		exit(1);
1138 	}
1139 
1140 	(void) close(mDev);
1141 #else
1142 #error	fdisk needs to be ported to new architecture
1143 #endif
1144 
1145 	/* Zero out the partitions part of this record */
1146 	part = (struct ipart *)BootCod.parts;
1147 	for (i = 0; i < FD_NUMPART; i++, part++) {
1148 		(void) memset(part, 0, sizeof (struct ipart));
1149 	}
1150 
1151 }
1152 
1153 /*
1154  * fill_patt
1155  * Fill the disk with user/sector number pattern.
1156  */
1157 static void
1158 fill_patt(void)
1159 {
1160 	int	*buff_ptr, i;
1161 	int	io_fpatt = 0;
1162 	int	io_ipatt = 0;
1163 
1164 	if (strncmp(io_fatt, "#", 1) != 0) {
1165 		io_fpatt++;
1166 		io_ipatt = strtoul(io_fatt, 0, 0);
1167 		buff_ptr = (int *)Bootsect;
1168 		for (i = 0; i < sectsiz; i += 4, buff_ptr++)
1169 			*buff_ptr = io_ipatt;
1170 	}
1171 
1172 	/*
1173 	 * Fill disk with pattern based on block number.
1174 	 * Write to the disk at absolute relative block io_offset
1175 	 * for io_size blocks.
1176 	 */
1177 	while (io_size--) {
1178 		buff_ptr = (int *)Bootsect;
1179 		if (!io_fpatt) {
1180 			for (i = 0; i < sectsiz; i += 4, buff_ptr++)
1181 				*buff_ptr = io_offset;
1182 		}
1183 		/* Write the data to disk */
1184 		if (lseek(Dev, sectsiz * io_offset++, SEEK_SET) == -1) {
1185 			(void) fprintf(stderr, "fdisk: Error seeking on %s.\n",
1186 			    Dfltdev);
1187 			exit(1);
1188 		}
1189 		if (write(Dev, Bootsect, sectsiz) != sectsiz) {
1190 			(void) fprintf(stderr, "fdisk: Error writing %s.\n",
1191 			    Dfltdev);
1192 			exit(1);
1193 		}
1194 	} /* while (--io_size); */
1195 }
1196 
1197 /*
1198  * abs_read
1199  * Read from the disk at absolute relative block io_offset for
1200  * io_size blocks. Write the data to standard ouput (-r).
1201  */
1202 static void
1203 abs_read(void)
1204 {
1205 	int c;
1206 
1207 	while (io_size--) {
1208 		if (lseek(Dev, sectsiz * io_offset++, SEEK_SET) == -1) {
1209 			(void) fprintf(stderr, "fdisk: Error seeking on %s.\n",
1210 			    Dfltdev);
1211 			exit(1);
1212 		}
1213 		if (read(Dev, Bootsect, sectsiz) != sectsiz) {
1214 			(void) fprintf(stderr, "fdisk: Error reading %s.\n",
1215 			    Dfltdev);
1216 			exit(1);
1217 		}
1218 
1219 		/* Write to standard ouptut */
1220 		if ((c = write(1, Bootsect, (unsigned)sectsiz)) != sectsiz) {
1221 			if (c >= 0) {
1222 				if (io_debug)
1223 					(void) fprintf(stderr,
1224 					    "fdisk: Output warning: %d of %d"
1225 					    " characters written.\n",
1226 					    c, sectsiz);
1227 				exit(2);
1228 			} else {
1229 				perror("write error on output file.");
1230 				exit(2);
1231 			}
1232 		} /* if ((c = write(1, Bootsect, (unsigned)sectsiz)) */
1233 			/* != sectsiz) */
1234 	} /* while (--io_size); */
1235 	exit(0);
1236 }
1237 
1238 /*
1239  * abs_write
1240  * Read the data from standard input. Write to the disk at
1241  * absolute relative block io_offset for io_size blocks (-w).
1242  */
1243 static void
1244 abs_write(void)
1245 {
1246 	int c, i;
1247 
1248 	while (io_size--) {
1249 		int part_exit = 0;
1250 		/* Read from standard input */
1251 		if ((c = read(0, Bootsect, (unsigned)sectsiz)) != sectsiz) {
1252 			if (c >= 0) {
1253 				if (io_debug)
1254 				(void) fprintf(stderr,
1255 				    "fdisk: WARNING: Incomplete read (%d of"
1256 				    " %d characters read) on input file.\n",
1257 				    c, sectsiz);
1258 				/* Fill pattern to mark partial sector in buf */
1259 				for (i = c; i < sectsiz; ) {
1260 					Bootsect[i++] = 0x41;
1261 					Bootsect[i++] = 0x62;
1262 					Bootsect[i++] = 0x65;
1263 					Bootsect[i++] = 0;
1264 				}
1265 				part_exit++;
1266 			} else {
1267 				perror("read error on input file.");
1268 				exit(2);
1269 			}
1270 
1271 		}
1272 		/* Write to disk drive */
1273 		if (lseek(Dev, sectsiz * io_offset++, SEEK_SET) == -1) {
1274 			(void) fprintf(stderr, "fdisk: Error seeking on %s.\n",
1275 			    Dfltdev);
1276 			exit(1);
1277 		}
1278 		if (write(Dev, Bootsect, sectsiz) != sectsiz) {
1279 			(void) fprintf(stderr, "fdisk: Error writing %s.\n",
1280 			    Dfltdev);
1281 			exit(1);
1282 		}
1283 		if (part_exit)
1284 		exit(0);
1285 	} /* while (--io_size); */
1286 	exit(1);
1287 }
1288 
1289 
1290 /*
1291  * load
1292  * Load will either read the fdisk table from a file or add or
1293  * delete an entry (-A, -D, -F).
1294  */
1295 
1296 static void
1297 load(int funct, char *file)
1298 {
1299 	int	id;
1300 	int	act;
1301 	int	bhead;
1302 	int	bsect;
1303 	int	bcyl;
1304 	int	ehead;
1305 	int	esect;
1306 	int	ecyl;
1307 	int	rsect;
1308 	int	numsect;
1309 	char	line[256];
1310 	int	i = 0;
1311 	int	j;
1312 	FILE *fp;
1313 
1314 	switch (funct) {
1315 
1316 	case LOADFILE:
1317 
1318 		/*
1319 		 * Zero out the table before loading it, which will
1320 		 * force it to be updated on disk later (-F
1321 		 * fdisk_file).
1322 		 */
1323 		nulltbl();
1324 
1325 		/* Open the prototype file */
1326 		if ((fp = fopen(file, "r")) == NULL) {
1327 			(void) fprintf(stderr,
1328 			    "fdisk: Cannot open prototype partition file %s.\n",
1329 			    file);
1330 			exit(1);
1331 		}
1332 
1333 		/* Read a line from the file */
1334 		while (fgets(line, sizeof (line) - 1, fp)) {
1335 			if (pars_fdisk(line, &id, &act, &bhead, &bsect,
1336 			    &bcyl, &ehead, &esect, &ecyl, &rsect, &numsect)) {
1337 				continue;
1338 			}
1339 
1340 			/*
1341 			 * Validate the partition. It cannot start at sector
1342 			 * 0 unless it is UNUSED or already exists
1343 			 */
1344 			if (validate_part(id, rsect, numsect) < 0) {
1345 				(void) fprintf(stderr,
1346 				    "fdisk: Error on entry \"%s\".\n",
1347 				    line);
1348 				exit(1);
1349 			}
1350 			/*
1351 			 * Find an unused entry to use and put the entry
1352 			 * in table
1353 			 */
1354 			if (insert_tbl(id, act, bhead, bsect, bcyl, ehead,
1355 			    esect, ecyl, rsect, numsect) < 0) {
1356 				(void) fprintf(stderr,
1357 				    "fdisk: Error on entry \"%s\".\n",
1358 				    line);
1359 				exit(1);
1360 			}
1361 		} /* while (fgets(line, sizeof (line) - 1, fp)) */
1362 
1363 		if (verify_tbl() < 0) {
1364 			(void) fprintf(stderr,
1365 			    "fdisk: Cannot create partition table\n");
1366 			exit(1);
1367 		}
1368 
1369 		(void) fclose(fp);
1370 		return;
1371 
1372 	case LOADDEL:
1373 
1374 		/* Parse the user-supplied deletion line (-D) */
1375 		if (pars_fdisk(file, &id, &act, &bhead, &bsect, &bcyl,
1376 		    &ehead, &esect, &ecyl, &rsect, &numsect)) {
1377 			(void) fprintf(stderr,
1378 			    "fdisk: Syntax error \"%s\"\n", file);
1379 			exit(1);
1380 		}
1381 
1382 		/* Find the exact entry in the table */
1383 		for (i = 0; i < FD_NUMPART; i++) {
1384 			if (Table[i].systid == id &&
1385 			    Table[i].bootid == act &&
1386 			    Table[i].beghead == bhead &&
1387 			    Table[i].begsect == ((bsect & 0x3f) |
1388 			    (uchar_t)((bcyl>>2) & 0xc0)) &&
1389 			    Table[i].begcyl == (uchar_t)(bcyl & 0xff) &&
1390 			    Table[i].endhead == ehead &&
1391 			    Table[i].endsect == ((esect & 0x3f) |
1392 			    (uchar_t)((ecyl>>2) & 0xc0)) &&
1393 			    Table[i].endcyl == (uchar_t)(ecyl & 0xff) &&
1394 			    Table[i].relsect == lel(rsect) &&
1395 			    Table[i].numsect == lel(numsect)) {
1396 
1397 				/*
1398 				 * Found the entry. Now move rest of
1399 				 * entries up toward the top of the
1400 				 * table, leaving available entries at
1401 				 * the end of the fdisk table.
1402 				 */
1403 				for (j = i; j < FD_NUMPART - 1; j++) {
1404 					Table[j].systid = Table[j + 1].systid;
1405 					Table[j].bootid = Table[j + 1].bootid;
1406 					Table[j].beghead = Table[j + 1].beghead;
1407 					Table[j].begsect = Table[j + 1].begsect;
1408 					Table[j].begcyl = Table[j + 1].begcyl;
1409 					Table[j].endhead = Table[j + 1].endhead;
1410 					Table[j].endsect = Table[j + 1].endsect;
1411 					Table[j].endcyl = Table[j + 1].endcyl;
1412 					Table[j].relsect = Table[j + 1].relsect;
1413 					Table[j].numsect = Table[j + 1].numsect;
1414 				}
1415 
1416 				/*
1417 				 * Mark the last entry as unused in case
1418 				 * all table entries were in use prior
1419 				 * to the deletion.
1420 				 */
1421 
1422 				Table[FD_NUMPART - 1].systid = UNUSED;
1423 				Table[FD_NUMPART - 1].bootid = 0;
1424 				return;
1425 			}
1426 		}
1427 		(void) fprintf(stderr,
1428 		    "fdisk: Entry does not match any existing partition:\n"
1429 		    "	\"%s\"\n",
1430 		    file);
1431 		exit(1);
1432 		/* FALLTHRU */
1433 
1434 	case LOADADD:
1435 
1436 		/* Parse the user-supplied addition line (-A) */
1437 		if (pars_fdisk(file, &id, &act, &bhead, &bsect, &bcyl, &ehead,
1438 		    &esect, &ecyl, &rsect, &numsect)) {
1439 			(void) fprintf(stderr,
1440 			    "fdisk: Syntax error \"%s\"\n", file);
1441 			exit(1);
1442 		}
1443 
1444 		/* Validate the partition. It cannot start at sector 0 */
1445 		if (rsect == 0) {
1446 			(void) fprintf(stderr,
1447 			    "fdisk: New partition cannot start at sector 0:\n"
1448 			    "   \"%s\".\n",
1449 			    file);
1450 			exit(1);
1451 		}
1452 
1453 		/*
1454 		 * if the user wishes to add an EFI partition, we need
1455 		 * more extensive validation.  rsect should be 1, and
1456 		 * numsect should equal the entire disk capacity - 1
1457 		 */
1458 
1459 		if (id == EFI_PMBR) {
1460 			if (rsect != 1) {
1461 				(void) fprintf(stderr,
1462 				    "fdisk: EFI partitions must start at sector"
1463 				    " 1 (input rsect = %d)\n", rsect);
1464 				exit(1);
1465 			}
1466 
1467 			if (numsect != dev_capacity - 1) {
1468 				(void) fprintf(stderr,
1469 				    "fdisk: EFI partitions must encompass the "
1470 				    "entire disk\n"
1471 				    "(input numsect: %d - avail: %llu)\n",
1472 				    numsect,
1473 				    dev_capacity - 1);
1474 				exit(1);
1475 			}
1476 		}
1477 
1478 		/* Find unused entry for use and put entry in table */
1479 		if (insert_tbl(id, act, bhead, bsect, bcyl, ehead, esect,
1480 		    ecyl, rsect, numsect) < 0) {
1481 			(void) fprintf(stderr,
1482 			    "fdisk: Invalid entry could not be inserted:\n"
1483 			    "	\"%s\"\n",
1484 			    file);
1485 			exit(1);
1486 		}
1487 
1488 		/* Make sure new entry does not overlap existing entry */
1489 		if (verify_tbl() < 0) {
1490 			(void) fprintf(stderr,
1491 			    "fdisk: Cannot create partition \"%s\"\n", file);
1492 			exit(1);
1493 		}
1494 	} /* switch funct */
1495 }
1496 
1497 /*
1498  * Set_Table_CHS_Values
1499  *
1500  * This will calculate the CHS values for beginning and ending CHS
1501  * for a single partition table entry (ti) based on the relsect
1502  * and numsect values contained in the partion table entry.
1503  *
1504  * hba_heads and hba_sectors contain the number of heads and sectors.
1505  *
1506  * If the number of cylinders exceeds the MAX_CYL,
1507  * then maximum values will be placed in the corresponding chs entry.
1508  */
1509 static void
1510 Set_Table_CHS_Values(int ti)
1511 {
1512 	uint32_t	lba, cy, hd, sc;
1513 
1514 	lba = (uint32_t)Table[ti].relsect;
1515 	if (lba >= hba_heads * hba_sectors * MAX_CYL) {
1516 		/*
1517 		 * the lba address cannot be expressed in CHS value
1518 		 * so store the maximum CHS field values in the CHS fields.
1519 		 */
1520 		cy = MAX_CYL + 1;
1521 		hd = MAX_HEAD;
1522 		sc = MAX_SECT;
1523 	} else {
1524 		cy = lba / hba_sectors / hba_heads;
1525 		hd = lba / hba_sectors % hba_heads;
1526 		sc = lba % hba_sectors + 1;
1527 	}
1528 	Table[ti].begcyl = cy & 0xff;
1529 	Table[ti].beghead = (uchar_t)hd;
1530 	Table[ti].begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
1531 
1532 	/*
1533 	 * This code is identical to the code above
1534 	 * except that it works on ending CHS values
1535 	 */
1536 	lba = (uint32_t)(Table[ti].relsect + Table[ti].numsect - 1);
1537 	if (lba >= hba_heads * hba_sectors * MAX_CYL) {
1538 		cy = MAX_CYL + 1;
1539 		hd = MAX_HEAD;
1540 		sc = MAX_SECT;
1541 	} else {
1542 		cy = lba / hba_sectors / hba_heads;
1543 		hd = lba / hba_sectors % hba_heads;
1544 		sc = lba % hba_sectors + 1;
1545 	}
1546 	Table[ti].endcyl = cy & 0xff;
1547 	Table[ti].endhead = (uchar_t)hd;
1548 	Table[ti].endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
1549 }
1550 
1551 /*
1552  * insert_tbl
1553  * 	Insert entry into fdisk table. Check all user-supplied values
1554  *	for the entry, but not the validity relative to other table
1555  *	entries!
1556  */
1557 static int
1558 insert_tbl(
1559     int id, int act,
1560     int bhead, int bsect, int bcyl,
1561     int ehead, int esect, int ecyl,
1562     int rsect, int numsect)
1563 {
1564 	int	i;
1565 
1566 	/* validate partition size */
1567 	if (rsect + numsect > dev_capacity) {
1568 		(void) fprintf(stderr,
1569 		    "fdisk: Partition table exceeds the size of the disk.\n");
1570 		return (-1);
1571 	}
1572 
1573 	/* find UNUSED partition table entry */
1574 	for (i = 0; i < FD_NUMPART; i++) {
1575 		if (Table[i].systid == UNUSED) {
1576 			break;
1577 		}
1578 	}
1579 	if (i >= FD_NUMPART) {
1580 		(void) fprintf(stderr, "fdisk: Partition table is full.\n");
1581 		return (-1);
1582 	}
1583 
1584 
1585 	Table[i].systid = (uchar_t)id;
1586 	Table[i].bootid = (uchar_t)act;
1587 	Table[i].numsect = lel(numsect);
1588 	Table[i].relsect = lel(rsect);
1589 
1590 	/*
1591 	 * If we have been called with a valid geometry, use it
1592 	 * valid means non-zero values that fit in the BIOS fields
1593 	 */
1594 	if (0 < bsect && bsect <= MAX_SECT &&
1595 	    0 <= bhead && bhead <= MAX_HEAD &&
1596 	    0 < esect && esect <= MAX_SECT &&
1597 	    0 <= ehead && ehead <= MAX_HEAD) {
1598 		if (bcyl > MAX_CYL)
1599 			bcyl = MAX_CYL + 1;
1600 		if (ecyl > MAX_CYL)
1601 			ecyl = MAX_CYL + 1;
1602 		Table[i].begcyl = bcyl & 0xff;
1603 		Table[i].endcyl = ecyl & 0xff;
1604 		Table[i].beghead = (uchar_t)bhead;
1605 		Table[i].endhead = (uchar_t)ehead;
1606 		Table[i].begsect = (uchar_t)(((bcyl >> 2) & 0xc0) | bsect);
1607 		Table[i].endsect = ((ecyl >> 2) & 0xc0) | esect;
1608 	} else {
1609 
1610 		/*
1611 		 * The specified values are invalid,
1612 		 * so calculate the values based on hba_heads, hba_sectors
1613 		 */
1614 		Set_Table_CHS_Values(i);
1615 	}
1616 
1617 	/*
1618 	 * return partition index
1619 	 */
1620 	return (i);
1621 }
1622 
1623 /*
1624  * verify_tbl
1625  * Verify that no partition entries overlap or exceed the size of
1626  * the disk.
1627  */
1628 static int
1629 verify_tbl(void)
1630 {
1631 	int	i, j, rsect, numsect;
1632 	int	noMoreParts = 0;
1633 	int	numParts = 0;
1634 
1635 	/* Make sure new entry does not overlap an existing entry */
1636 	for (i = 0; i < FD_NUMPART - 1; i++) {
1637 		if (Table[i].systid != UNUSED) {
1638 			numParts++;
1639 			/*
1640 			 * No valid partitions allowed after an UNUSED  or
1641 			 * EFI_PMBR part
1642 			 */
1643 			if (noMoreParts) {
1644 				return (-1);
1645 			}
1646 
1647 			/*
1648 			 * EFI_PMBR partitions must be the only partition
1649 			 * and must be Table entry 0
1650 			 */
1651 			if (Table[i].systid == EFI_PMBR) {
1652 				if (i == 0) {
1653 					noMoreParts = 1;
1654 				} else {
1655 					return (-1);
1656 				}
1657 
1658 				if (Table[i].relsect != 1) {
1659 					(void) fprintf(stderr, "ERROR: "
1660 					    "Invalid starting sector "
1661 					    "for EFI_PMBR partition:\n"
1662 					    "relsect %d "
1663 					    "(should be 1)\n",
1664 					    Table[i].relsect);
1665 
1666 					return (-1);
1667 				}
1668 
1669 				if (Table[i].numsect != dev_capacity - 1) {
1670 					(void) fprintf(stderr, "ERROR: "
1671 					    "EFI_PMBR partition must "
1672 					    "encompass the entire "
1673 					    "disk.\n numsect %d - "
1674 					    "actual %llu\n",
1675 					    Table[i].numsect,
1676 					    dev_capacity - 1);
1677 
1678 					return (-1);
1679 				}
1680 			}
1681 
1682 			/* make sure the partition isn't larger than the disk */
1683 			rsect = lel(Table[i].relsect);
1684 			numsect = lel(Table[i].numsect);
1685 			if ((rsect + numsect) > dev_capacity) {
1686 				return (-1);
1687 			}
1688 
1689 			for (j = i + 1; j < FD_NUMPART; j++) {
1690 				if (Table[j].systid != UNUSED) {
1691 					int t_relsect = lel(Table[j].relsect);
1692 					int t_numsect = lel(Table[j].numsect);
1693 
1694 					if (noMoreParts) {
1695 						(void) fprintf(stderr,
1696 						    "Cannot add partition to "
1697 						    "table; no more partitions "
1698 						    "allowed\n");
1699 
1700 						if (io_debug) {
1701 							(void) fprintf(stderr,
1702 							    "DEBUG: Current "
1703 							    "partition:\t"
1704 							    "%d:%d:%d:%d:%d:"
1705 							    "%d:%d:%d:%d:%d\n"
1706 							    "       Next "
1707 							    "partition:\t\t"
1708 							    "%d:%d:%d:%d:%d:"
1709 							    "%d:%d:%d:%d:%d\n",
1710 							    Table[i].systid,
1711 							    Table[i].bootid,
1712 							    Table[i].begcyl,
1713 							    Table[i].beghead,
1714 							    Table[i].begsect,
1715 							    Table[i].endcyl,
1716 							    Table[i].endhead,
1717 							    Table[i].endsect,
1718 							    Table[i].relsect,
1719 							    Table[i].numsect,
1720 							    Table[j].systid,
1721 							    Table[j].bootid,
1722 							    Table[j].begcyl,
1723 							    Table[j].beghead,
1724 							    Table[j].begsect,
1725 							    Table[j].endcyl,
1726 							    Table[j].endhead,
1727 							    Table[j].endsect,
1728 							    Table[j].relsect,
1729 							    Table[j].numsect);
1730 						}
1731 
1732 						return (-1);
1733 					}
1734 
1735 					if ((rsect >=
1736 					    (t_relsect + t_numsect)) ||
1737 					    ((rsect + numsect) <= t_relsect)) {
1738 						continue;
1739 					} else {
1740 						(void) fprintf(stderr, "ERROR: "
1741 						    "current partition overlaps"
1742 						    " following partition\n");
1743 
1744 						return (-1);
1745 					}
1746 				}
1747 			}
1748 		} else {
1749 			noMoreParts = 1;
1750 		}
1751 	}
1752 	if (Table[i].systid != UNUSED) {
1753 		if (noMoreParts ||
1754 		    ((lel(Table[i].relsect) + lel(Table[i].numsect)) >
1755 		    dev_capacity)) {
1756 			return (-1);
1757 		}
1758 	}
1759 
1760 	return (numParts);
1761 }
1762 
1763 /*
1764  * pars_fdisk
1765  * Parse user-supplied data to set up fdisk partitions
1766  * (-A, -D, -F).
1767  */
1768 static int
1769 pars_fdisk(
1770     char *line,
1771     int *id, int *act,
1772     int *bhead, int *bsect, int *bcyl,
1773     int *ehead, int *esect, int *ecyl,
1774     int *rsect, int *numsect)
1775 {
1776 	int	i;
1777 	if (line[0] == '\0' || line[0] == '\n' || line[0] == '*')
1778 		return (1);
1779 	line[strlen(line)] = '\0';
1780 	for (i = 0; i < strlen(line); i++) {
1781 		if (line[i] == '\0') {
1782 			break;
1783 		} else if (line[i] == ':') {
1784 			line[i] = ' ';
1785 		}
1786 	}
1787 	if (sscanf(line, "%d %d %d %d %d %d %d %d %d %d",
1788 	    id, act, bhead, bsect, bcyl, ehead, esect, ecyl,
1789 	    rsect, numsect) != 10) {
1790 		(void) fprintf(stderr, "Syntax error:\n	\"%s\".\n", line);
1791 		exit(1);
1792 	}
1793 	return (0);
1794 }
1795 
1796 /*
1797  * validate_part
1798  * Validate that a new partition does not start at sector 0. Only UNUSED
1799  * partitions and previously existing partitions are allowed to start at 0.
1800  */
1801 static int
1802 validate_part(int id, int rsect, int numsect)
1803 {
1804 	int i;
1805 	if ((id != UNUSED) && (rsect == 0)) {
1806 		for (i = 0; i < FD_NUMPART; i++) {
1807 			if ((Old_Table[i].systid == id) &&
1808 			    (Old_Table[i].relsect == lel(rsect)) &&
1809 			    (Old_Table[i].numsect == lel(numsect)))
1810 				return (0);
1811 		}
1812 		(void) fprintf(stderr,
1813 		    "New partition cannot start at sector 0\n");
1814 		return (-1);
1815 	}
1816 	return (0);
1817 }
1818 
1819 /*
1820  * stage0
1821  * Print out interactive menu and process user input.
1822  */
1823 static void
1824 stage0(void)
1825 {
1826 	dispmenu();
1827 	for (;;) {
1828 		(void) printf(Q_LINE);
1829 		(void) printf("Enter Selection: ");
1830 		(void) gets(s);
1831 		rm_blanks(s);
1832 		while (!((s[0] > '0') && (s[0] < '7') && (s[1] == 0))) {
1833 			(void) printf(E_LINE); /* Clear any previous error */
1834 			(void) printf(
1835 			    "Enter a one-digit number between 1 and 6.");
1836 			(void) printf(Q_LINE);
1837 			(void) printf("Enter Selection: ");
1838 			(void) gets(s);
1839 			rm_blanks(s);
1840 		}
1841 		(void) printf(E_LINE);
1842 		switch (s[0]) {
1843 			case '1':
1844 				if (pcreate() == -1)
1845 					return;
1846 				break;
1847 			case '2':
1848 				if (pchange() == -1)
1849 					return;
1850 				break;
1851 			case '3':
1852 				if (pdelete() == -1)
1853 					return;
1854 				break;
1855 			case '4':
1856 				if (ppartid() == -1)
1857 					return;
1858 				break;
1859 			case '5':
1860 				/* update disk partition table, if changed */
1861 				if (TableChanged() == 1) {
1862 					copy_Table_to_Bootblk();
1863 					dev_mboot_write(0, Bootsect, sectsiz);
1864 				}
1865 				/*
1866 				 * If the VTOC table is wrong fix it
1867 				 * (truncate only)
1868 				 */
1869 				if (io_adjt) {
1870 					fix_slice();
1871 				}
1872 				(void) close(Dev);
1873 				exit(0);
1874 				/* FALLTHRU */
1875 			case '6':
1876 				/*
1877 				 * If the VTOC table is wrong fix it
1878 				 * (truncate only)
1879 				 */
1880 				if (io_adjt) {
1881 					fix_slice();
1882 				}
1883 				(void) close(Dev);
1884 				exit(0);
1885 				/* FALLTHRU */
1886 			default:
1887 				break;
1888 		}
1889 		copy_Table_to_Bootblk();
1890 		disptbl();
1891 		dispmenu();
1892 	}
1893 }
1894 
1895 /*
1896  * pcreate
1897  * Create partition entry in the table (interactive mode).
1898  */
1899 static int
1900 pcreate(void)
1901 {
1902 	uchar_t tsystid = 'z';
1903 	int i, j;
1904 	int rsect = 1;
1905 	int retCode = 0;
1906 
1907 	i = 0;
1908 	for (;;) {
1909 		if (i == FD_NUMPART) {
1910 			(void) printf(E_LINE);
1911 			(void) printf(
1912 			    "The partition table is full!\n"
1913 			    "You must delete a partition before creating"
1914 			    " a new one.\n");
1915 			return (-1);
1916 		}
1917 		if (Table[i].systid == UNUSED) {
1918 			break;
1919 		}
1920 		i++;
1921 	}
1922 
1923 	j = 0;
1924 	for (i = 0; i < FD_NUMPART; i++) {
1925 		if (Table[i].systid != UNUSED) {
1926 			j += lel(Table[i].numsect);
1927 		}
1928 		if (j >= chs_capacity) {
1929 			(void) printf(E_LINE);
1930 			(void) printf("There is no more room on the disk for"
1931 			    " another partition.\n");
1932 			(void) printf(
1933 			    "You must delete a partition before creating"
1934 			    " a new one.\n");
1935 			return (-1);
1936 		}
1937 	}
1938 	while (tsystid == 'z') {
1939 		(void) printf(Q_LINE);
1940 		(void) printf(
1941 		    "Select the partition type to create:\n"
1942 		    "   1=SOLARIS2  2=UNIX        3=PCIXOS     4=Other\n"
1943 		    "   5=DOS12     6=DOS16       7=DOSEXT     8=DOSBIG\n"
1944 		    "   9=DOS16LBA  A=x86 Boot    B=Diagnostic C=FAT32\n"
1945 		    "   D=FAT32LBA  E=DOSEXTLBA   F=EFI        0=Exit? ");
1946 		(void) gets(s);
1947 		rm_blanks(s);
1948 		if (s[1] != 0) {
1949 			(void) printf(E_LINE);
1950 			(void) printf("Invalid selection, try again.");
1951 			continue;
1952 		}
1953 		switch (s[0]) {
1954 		case '0':		/* exit */
1955 			(void) printf(E_LINE);
1956 			return (-1);
1957 		case '1':		/* Solaris partition */
1958 			tsystid = SUNIXOS2;
1959 			break;
1960 		case '2':		/* UNIX partition */
1961 			tsystid = UNIXOS;
1962 			break;
1963 		case '3':		/* PCIXOS partition */
1964 			tsystid = PCIXOS;
1965 			break;
1966 		case '4':		/* OTHEROS System partition */
1967 			tsystid = OTHEROS;
1968 			break;
1969 		case '5':
1970 			tsystid = DOSOS12; /* DOS 12 bit fat */
1971 			break;
1972 		case '6':
1973 			tsystid = DOSOS16; /* DOS 16 bit fat */
1974 			break;
1975 		case '7':
1976 			tsystid = EXTDOS;
1977 			break;
1978 		case '8':
1979 			tsystid = DOSHUGE;
1980 			break;
1981 		case '9':
1982 			tsystid = FDISK_FAT95;  /* FAT16, need extended int13 */
1983 			break;
1984 		case 'a':		/* x86 Boot partition */
1985 		case 'A':
1986 			tsystid = X86BOOT;
1987 			break;
1988 		case 'b':		/* Diagnostic boot partition */
1989 		case 'B':
1990 			tsystid = DIAGPART;
1991 			break;
1992 		case 'c':		/* FAT32 */
1993 		case 'C':
1994 			tsystid = FDISK_WINDOWS;
1995 			break;
1996 		case 'd':		/* FAT32 and need extended int13 */
1997 		case 'D':
1998 			tsystid = FDISK_EXT_WIN;
1999 			break;
2000 		case 'e':	/* Extended partition, need extended int13 */
2001 		case 'E':
2002 			tsystid = FDISK_EXTLBA;
2003 			break;
2004 		case 'f':
2005 		case 'F':
2006 			tsystid = EFI_PMBR;
2007 			break;
2008 		default:
2009 			(void) printf(E_LINE);
2010 			(void) printf("Invalid selection, try again.");
2011 			continue;
2012 		}
2013 	}
2014 
2015 	(void) printf(E_LINE);
2016 
2017 	if (tsystid != EFI_PMBR) {
2018 		/* create the new partition */
2019 		i = specify(tsystid);
2020 
2021 		if (i != -1) {
2022 			/* see if it should be the active partition */
2023 			(void) printf(E_LINE);
2024 			(void) printf(Q_LINE);
2025 
2026 			(void) printf(
2027 			    "Should this become the active partition? If "
2028 			    "yes, it  will be activated\n"
2029 			    "each time the computer is reset or turned on.\n"
2030 			    "Please type \"y\" or \"n\". ");
2031 
2032 			if (yesno()) {
2033 				(void) printf(E_LINE);
2034 				for (j = 0; j < FD_NUMPART; j++) {
2035 					if (j == i) {
2036 						Table[j].bootid = ACTIVE;
2037 						(void) printf(E_LINE);
2038 						(void) printf(
2039 						    "Partition %d is now "
2040 						    "the active partition.",
2041 						    j + 1);
2042 					} else {
2043 						Table[j].bootid = 0;
2044 					}
2045 				}
2046 			} else {
2047 				Table[i].bootid = 0;
2048 			}
2049 
2050 			/* set up the return code */
2051 			i = 1;
2052 		}
2053 	} else {
2054 		/*
2055 		 * partitions of type EFI_PMBR must be the only partitions in
2056 		 * the table
2057 		 *
2058 		 * First, make sure there were no errors the table is
2059 		 * empty
2060 		 */
2061 		retCode = verify_tbl();
2062 
2063 		if (retCode < 0) {
2064 			(void) fprintf(stderr,
2065 			    "fdisk: Cannot create EFI partition table; \n"
2066 			    "current partition table is invalid.\n");
2067 			return (-1);
2068 		} else if (retCode > 0) {
2069 			(void) printf(
2070 			    "An EFI partition must be the only partition on "
2071 			    "disk.  You may manually delete existing\n"
2072 			    "partitions, or fdisk can do it.\n"
2073 			    "Do you want fdisk to destroy existing "
2074 			    "partitions?\n"
2075 			    "Please type \"y\" or \"n\". ");
2076 
2077 			if (yesno()) {
2078 				nulltbl();
2079 			} else {
2080 				return (-1);
2081 			}
2082 		}
2083 
2084 		/* create the table entry - i should be 0 */
2085 		i = insert_tbl(tsystid, 0, 0, 0, 0, 0, 0, 0, rsect,
2086 		    dev_capacity - rsect);
2087 
2088 		if (i != 0) {
2089 			(void) printf("Error creating EFI partition!!!\n");
2090 			i = -1;
2091 		} else {
2092 
2093 			/* EFI partitions are currently never active */
2094 			Table[i].bootid = 0;
2095 
2096 			/* set up the return code */
2097 			i = 1;
2098 		}
2099 	}
2100 
2101 	return (i);
2102 }
2103 
2104 /*
2105  * specify
2106  * Query the user to specify the size of the new partition in
2107  * terms of percentage of the disk or by specifying the starting
2108  * cylinder and length in cylinders.
2109  */
2110 static int
2111 specify(uchar_t tsystid)
2112 {
2113 	int	i, j, percent = -1;
2114 	int	cyl, cylen, first_free, size_free;
2115 	int	max_free;
2116 	int	cyl_size;
2117 	struct ipart *partition[FD_NUMPART];
2118 
2119 	cyl_size = heads * sectors;
2120 
2121 	/*
2122 	 * make a local copy of the partition table
2123 	 * and sort it into relsect order
2124 	 */
2125 	for (i = 0; i < FD_NUMPART; i++)
2126 		partition[i] = &Table[i];
2127 
2128 	for (i = 0; i < FD_NUMPART - 1; i++) {
2129 		if (partition[i]->systid == UNUSED)
2130 			break;
2131 		for (j = i + 1; j < FD_NUMPART; j++) {
2132 			if (partition[j]->systid == UNUSED)
2133 				break;
2134 			if (lel(partition[j]->relsect) <
2135 			    lel(partition[i]->relsect)) {
2136 				struct ipart *temp = partition[i];
2137 				partition[i] = partition[j];
2138 				partition[j] = temp;
2139 			}
2140 		}
2141 	}
2142 
2143 	(void) printf(Q_LINE);
2144 	(void) printf(
2145 	    "Specify the percentage of disk to use for this partition\n"
2146 	    "(or type \"c\" to specify the size in cylinders). ");
2147 	(void) gets(s);
2148 	rm_blanks(s);
2149 	if (s[0] != 'c') {	/* Specify size in percentage of disk */
2150 		i = 0;
2151 		while (s[i] != '\0') {
2152 			if (s[i] < '0' || s[i] > '9') {
2153 				(void) printf(E_LINE);
2154 				(void) printf("Invalid percentage value "
2155 				    "specified; retry the operation.");
2156 				return (-1);
2157 			}
2158 			i++;
2159 			if (i > 3) {
2160 				(void) printf(E_LINE);
2161 				(void) printf("Invalid percentage value "
2162 				    "specified; retry the operation.");
2163 				return (-1);
2164 			}
2165 		}
2166 		if ((percent = atoi(s)) > 100) {
2167 			(void) printf(E_LINE);
2168 			(void) printf(
2169 			    "Percentage value is too large. The value must be"
2170 			    " between 1 and 100;\nretry the operation.\n");
2171 			return (-1);
2172 		}
2173 		if (percent < 1) {
2174 			(void) printf(E_LINE);
2175 			(void) printf(
2176 			    "Percentage value is too small. The value must be"
2177 			    " between 1 and 100;\nretry the operation.\n");
2178 			return (-1);
2179 		}
2180 
2181 
2182 		if (percent == 100)
2183 			cylen = Numcyl - 1;
2184 		else
2185 			cylen = (Numcyl * percent) / 100;
2186 
2187 		/* Verify DOS12 partition doesn't exceed max size of 32MB. */
2188 		if ((tsystid == DOSOS12) &&
2189 		    ((long)((long)cylen * cyl_size) > MAXDOS)) {
2190 			int n;
2191 			n = MAXDOS * 100 / (int)(cyl_size) / Numcyl;
2192 			(void) printf(E_LINE);
2193 			(void) printf("Maximum size for a DOS partition "
2194 			    "is %d%%; retry the operation.",
2195 			    n <= 100 ? n : 100);
2196 			return (-1);
2197 		}
2198 
2199 
2200 		max_free = 0;
2201 		for (i = 0; i < FD_NUMPART; i++) {
2202 
2203 			/*
2204 			 * check for free space before partition i
2205 			 * where i varies from 0 to 3
2206 			 *
2207 			 * freespace after partition 3 is unusable
2208 			 * because there are no free partitions
2209 			 *
2210 			 * freespace begins at the end of previous partition
2211 			 * or cylinder 1
2212 			 */
2213 			if (i) {
2214 				/* Not an empty table */
2215 				first_free = lel(partition[i - 1]->relsect) +
2216 				    lel(partition[i - 1]->numsect);
2217 			} else {
2218 				first_free = cyl_size;
2219 			}
2220 
2221 			/*
2222 			 * freespace ends before the current partition
2223 			 * or the end of the disk (chs end)
2224 			 */
2225 			if (partition[i]->systid == UNUSED) {
2226 				size_free = chs_capacity - first_free;
2227 			} else {
2228 				size_free =
2229 				    lel(partition[i]->relsect) - first_free;
2230 			}
2231 
2232 			/* save largest free space */
2233 			if (max_free < size_free)
2234 				max_free = size_free;
2235 
2236 			if ((cylen * cyl_size) <= size_free) {
2237 				/* We found a place to use */
2238 				break;
2239 			}
2240 			if (partition[i]->systid == UNUSED) {
2241 				(void) printf(E_LINE);
2242 				max_free /= (cyl_size);
2243 				(void) fprintf(stderr, "fdisk: "
2244 				    "Maximum percentage available is %d\n",
2245 				    100 * max_free / Numcyl);
2246 				return (-1);
2247 			}
2248 		}
2249 
2250 		(void) printf(E_LINE);
2251 		if (i >= FD_NUMPART) {
2252 			(void) fprintf(stderr,
2253 			    "fdisk: Partition table is full.\n");
2254 			return (-1);
2255 		}
2256 
2257 		if ((i = insert_tbl(tsystid, 0, 0, 0, 0, 0, 0, 0,
2258 		    first_free, cylen * cyl_size)) >= 0)  {
2259 			return (i);
2260 		}
2261 		return (-1);
2262 	} else {
2263 
2264 		/* Specifying size in cylinders */
2265 		(void) printf(E_LINE);
2266 		(void) printf(Q_LINE);
2267 		(void) printf("Enter starting cylinder number: ");
2268 		if ((cyl = getcyl()) == -1) {
2269 			(void) printf(E_LINE);
2270 			(void) printf("Invalid number; retry the operation.");
2271 			return (-1);
2272 		}
2273 		if (cyl == 0) {
2274 			(void) printf(E_LINE);
2275 			(void) printf(
2276 			    "New partition cannot start at cylinder 0.\n");
2277 			return (-1);
2278 		}
2279 		if (cyl >= (unsigned int)Numcyl) {
2280 			(void) printf(E_LINE);
2281 			(void) printf(
2282 			    "Cylinder %d is out of bounds, "
2283 			    "the maximum is %d.\n",
2284 			    cyl, Numcyl - 1);
2285 			return (-1);
2286 		}
2287 		(void) printf(Q_LINE);
2288 		(void) printf("Enter partition size in cylinders: ");
2289 		if ((cylen = getcyl()) == -1) {
2290 			(void) printf(E_LINE);
2291 			(void) printf("Invalid number, retry the operation.");
2292 			return (-1);
2293 		}
2294 
2295 		for (i = 0; i < FD_NUMPART; i++) {
2296 			uint32_t	t_relsect, t_numsect;
2297 
2298 			if (partition[i]->systid == UNUSED)
2299 				break;
2300 			t_relsect = lel(partition[i]->relsect);
2301 			t_numsect = lel(partition[i]->numsect);
2302 
2303 			if (cyl * cyl_size >= t_relsect &&
2304 			    cyl * cyl_size < t_relsect + t_numsect) {
2305 				(void) printf(E_LINE);
2306 				(void) printf(
2307 				    "Cylinder %d is already allocated"
2308 				    "\nretry the operation.",
2309 				    cyl);
2310 				return (-1);
2311 			}
2312 
2313 			if (cyl * cyl_size < t_relsect &&
2314 			    (cyl + cylen - 1) * cyl_size > t_relsect) {
2315 				(void) printf(E_LINE);
2316 				(void) printf(
2317 				    "Maximum size for partition is %u cylinders"
2318 				    "\nretry the operation.",
2319 				    (t_relsect - cyl * cyl_size) / cyl_size);
2320 				return (-1);
2321 			}
2322 		}
2323 
2324 		/* Verify partition doesn't exceed disk size */
2325 		if (cyl + cylen > Numcyl) {
2326 			(void) printf(E_LINE);
2327 			(void) printf(
2328 			    "Maximum size for partition is %d cylinders;"
2329 			    "\nretry the operation.",
2330 			    Numcyl - cyl);
2331 			return (-1);
2332 		}
2333 
2334 		/* Verify DOS12 partition doesn't exceed max size of 32MB. */
2335 		if ((tsystid == DOSOS12) &&
2336 		    ((long)((long)cylen * cyl_size) > MAXDOS)) {
2337 			(void) printf(E_LINE);
2338 			(void) printf(
2339 			    "Maximum size for a %s partition is %ld cylinders;"
2340 			    "\nretry the operation.",
2341 			    Dstr, MAXDOS / (int)(cyl_size));
2342 			return (-1);
2343 		}
2344 
2345 		(void) printf(E_LINE);
2346 		i = insert_tbl(tsystid, 0, 0, 0, 0, 0, 0, 0,
2347 		    cyl * cyl_size, cylen * cyl_size);
2348 		if (i < 0)
2349 			return (-1);
2350 
2351 		if (verify_tbl() < 0) {
2352 			(void) printf(E_LINE);
2353 			(void) printf("fdisk: Cannot create partition table\n");
2354 			return (-1);
2355 		}
2356 
2357 		return (i);
2358 	}
2359 }
2360 
2361 /*
2362  * dispmenu
2363  * Display command menu (interactive mode).
2364  */
2365 static void
2366 dispmenu(void)
2367 {
2368 	(void) printf(M_LINE);
2369 	(void) printf(
2370 	    "SELECT ONE OF THE FOLLOWING:\n"
2371 	    "   1. Create a partition\n"
2372 	    "   2. Specify the active partition\n"
2373 	    "   3. Delete a partition\n"
2374 	    "   4. Change between Solaris and Solaris2 Partition IDs\n"
2375 	    "   5. Exit (update disk configuration and exit)\n"
2376 	    "   6. Cancel (exit without updating disk configuration)\n");
2377 }
2378 
2379 /*
2380  * pchange
2381  * Change the ACTIVE designation of a partition.
2382  */
2383 static int
2384 pchange(void)
2385 {
2386 	char s[80];
2387 	int i, j;
2388 
2389 	for (;;) {
2390 		(void) printf(Q_LINE);
2391 			{
2392 			(void) printf(
2393 			    "Specify the partition number to boot from"
2394 			    " (or specify 0 for none): ");
2395 			}
2396 		(void) gets(s);
2397 		rm_blanks(s);
2398 		if ((s[1] != 0) || (s[0] < '0') || (s[0] > '4')) {
2399 			(void) printf(E_LINE);
2400 			(void) printf(
2401 			    "Invalid response, please specify a number"
2402 			    " between 0 and 4.\n");
2403 		} else {
2404 			break;
2405 		}
2406 	}
2407 	if (s[0] == '0') {	/* No active partitions */
2408 		for (i = 0; i < FD_NUMPART; i++) {
2409 			if (Table[i].systid != UNUSED &&
2410 			    Table[i].bootid == ACTIVE)
2411 				Table[i].bootid = 0;
2412 		}
2413 		(void) printf(E_LINE);
2414 			(void) printf(
2415 			    "No partition is currently marked as active.");
2416 		return (0);
2417 	} else {	/* User has selected a partition to be active */
2418 		i = s[0] - '1';
2419 		if (Table[i].systid == UNUSED) {
2420 			(void) printf(E_LINE);
2421 			(void) printf("Partition does not exist.");
2422 			return (-1);
2423 		}
2424 		/* a DOS-DATA or EXT-DOS partition cannot be active */
2425 		else if ((Table[i].systid == DOSDATA) ||
2426 		    (Table[i].systid == EXTDOS) ||
2427 		    (Table[i].systid == FDISK_EXTLBA)) {
2428 			(void) printf(E_LINE);
2429 			(void) printf(
2430 			    "DOS-DATA, EXT_DOS and EXT_DOS_LBA partitions "
2431 			    "cannot be made active.\n");
2432 			(void) printf("Select another partition.");
2433 			return (-1);
2434 		}
2435 		Table[i].bootid = ACTIVE;
2436 		for (j = 0; j < FD_NUMPART; j++) {
2437 			if (j != i)
2438 			Table[j].bootid = 0;
2439 		}
2440 	}
2441 	(void) printf(E_LINE);
2442 		{
2443 		(void) printf(
2444 		    "Partition %d is now active. The system will start up"
2445 		    " from this\n", i + 1);
2446 		(void) printf("partition after the next reboot.");
2447 		}
2448 	return (1);
2449 }
2450 
2451 /*
2452  * Change between SOLARIS and SOLARIS2 partition id
2453  */
2454 static int
2455 ppartid(void)
2456 {
2457 	char	*p, s[80];
2458 	int	i;
2459 
2460 	for (;;) {
2461 		(void) printf(Q_LINE);
2462 		(void) printf("Specify the partition number to change"
2463 		    " (or enter 0 to exit): ");
2464 		if (!fgets(s, sizeof (s), stdin))
2465 			return (1);
2466 		i = strtol(s, &p, 10);
2467 
2468 		if (*p != '\n' || i < 0 || i > FD_NUMPART) {
2469 			(void) printf(E_LINE);
2470 			(void) printf(
2471 			    "Invalid response, retry the operation.\n");
2472 			continue;
2473 		}
2474 
2475 		if (i == 0) {
2476 			/* exit delete command */
2477 			(void) printf(E_LINE); /* clear error message */
2478 			return (1);
2479 		}
2480 
2481 		i -= 1;
2482 		if (Table[i].systid == SUNIXOS) {
2483 			Table[i].systid = SUNIXOS2;
2484 		} else if (Table[i].systid == SUNIXOS2) {
2485 			Table[i].systid = SUNIXOS;
2486 		} else {
2487 			(void) printf(E_LINE);
2488 			(void) printf(
2489 			    "Partition %d is not a Solaris partition.",
2490 			    i + 1);
2491 			continue;
2492 		}
2493 
2494 		(void) printf(E_LINE);
2495 		(void) printf("Partition %d has been changed.", i + 1);
2496 		return (1);
2497 	}
2498 }
2499 
2500 /*
2501  * pdelete
2502  * Remove partition entry from the table (interactive mode).
2503  */
2504 static char
2505 pdelete(void)
2506 {
2507 	char s[80];
2508 	int i, j;
2509 	char pactive;
2510 
2511 DEL1:	(void) printf(Q_LINE);
2512 	(void) printf("Specify the partition number to delete"
2513 	    " (or enter 0 to exit): ");
2514 	(void) gets(s);
2515 	rm_blanks(s);
2516 	if ((s[0] == '0')) {	/* exit delete command */
2517 		(void) printf(E_LINE);	/* clear error message */
2518 		return (1);
2519 	}
2520 	/* Accept only a single digit between 1 and 4 */
2521 	if (s[1] != 0 || (i = atoi(s)) < 1 || i > FD_NUMPART) {
2522 		(void) printf(E_LINE);
2523 		(void) printf("Invalid response, retry the operation.\n");
2524 		goto DEL1;
2525 	} else {		/* Found a digit between 1 and 4 */
2526 		--i;	/* Structure begins with element 0 */
2527 	}
2528 
2529 	if (Table[i].systid == UNUSED) {
2530 		(void) printf(E_LINE);
2531 		(void) printf("Partition %d does not exist.", i + 1);
2532 		return (-1);
2533 	}
2534 
2535 	(void) printf(Q_LINE);
2536 	(void) printf("Are you sure you want to delete partition %d?"
2537 	    " This will make all files and \n", i + 1);
2538 	(void) printf("programs in this partition inaccessible (type"
2539 	    " \"y\" or \"n\"). ");
2540 
2541 	(void) printf(E_LINE);
2542 	if (! yesno()) {
2543 		return (1);
2544 	}
2545 
2546 	if (Table[i].bootid == ACTIVE) {
2547 		pactive = 1;
2548 	} else {
2549 		pactive = 0;
2550 	}
2551 
2552 	for (j = i; j < FD_NUMPART - 1; j++) {
2553 		Table[j] = Table[j + 1];
2554 	}
2555 
2556 	Table[j].systid = UNUSED;
2557 	Table[j].numsect = 0;
2558 	Table[j].relsect = 0;
2559 	Table[j].bootid = 0;
2560 	(void) printf(E_LINE);
2561 	(void) printf("Partition %d has been deleted.", i + 1);
2562 
2563 	if (pactive) {
2564 		(void) printf(" This was the active partition.");
2565 	}
2566 
2567 	return (1);
2568 }
2569 
2570 /*
2571  * rm_blanks
2572  * Remove blanks from strings of user responses.
2573  */
2574 static void
2575 rm_blanks(char *s)
2576 {
2577 	register int i, j;
2578 
2579 	for (i = 0; i < CBUFLEN; i++) {
2580 		if ((s[i] == ' ') || (s[i] == '\t'))
2581 			continue;
2582 		else
2583 			/* Found first non-blank character of the string */
2584 			break;
2585 	}
2586 	for (j = 0; i < CBUFLEN; j++, i++) {
2587 		if ((s[j] = s[i]) == '\0') {
2588 			/* Reached end of string */
2589 			return;
2590 		}
2591 	}
2592 }
2593 
2594 /*
2595  * getcyl
2596  * Take the user-specified cylinder number and convert it from a
2597  * string to a decimal value.
2598  */
2599 static int
2600 getcyl(void)
2601 {
2602 int slen, i, j;
2603 unsigned int cyl;
2604 	(void) gets(s);
2605 	rm_blanks(s);
2606 	slen = strlen(s);
2607 	j = 1;
2608 	cyl = 0;
2609 	for (i = slen - 1; i >= 0; i--) {
2610 		if (s[i] < '0' || s[i] > '9') {
2611 			return (-1);
2612 		}
2613 		cyl += (j * (s[i] - '0'));
2614 		j *= 10;
2615 	}
2616 	return (cyl);
2617 }
2618 
2619 /*
2620  * disptbl
2621  * Display the current fdisk table; determine percentage
2622  * of the disk used for each partition.
2623  */
2624 static void
2625 disptbl(void)
2626 {
2627 	int i;
2628 	unsigned int startcyl, endcyl, length, percent, remainder;
2629 	char *stat, *type;
2630 
2631 	if ((heads == 0) || (sectors == 0)) {
2632 		(void) printf("WARNING: critical disk geometry information"
2633 		    " missing!\n");
2634 		(void) printf("\theads = %d, sectors = %d\n", heads, sectors);
2635 		exit(1);
2636 	}
2637 
2638 	(void) printf(HOME);
2639 	(void) printf(T_LINE);
2640 	(void) printf("             Total disk size is %d cylinders\n", Numcyl);
2641 	(void) printf("             Cylinder size is %d (512 byte) blocks\n\n",
2642 	    heads * sectors);
2643 	(void) printf(
2644 	    "                                               Cylinders\n");
2645 	(void) printf(
2646 	    "      Partition   Status    Type          Start   End   Length"
2647 	    "    %%\n");
2648 	(void) printf(
2649 	    "      =========   ======    ============  =====   ===   ======"
2650 	    "   ===");
2651 	for (i = 0; i < FD_NUMPART; i++) {
2652 		if (Table[i].systid == UNUSED) {
2653 			(void) printf("\n");
2654 			(void) printf(CLR_LIN);
2655 			continue;
2656 		}
2657 		if (Table[i].bootid == ACTIVE)
2658 			stat = Actvstr;
2659 		else
2660 			stat = NAstr;
2661 		switch (Table[i].systid) {
2662 		case UNIXOS:
2663 			type = Ustr;
2664 			break;
2665 		case SUNIXOS:
2666 			type = SUstr;
2667 			break;
2668 		case SUNIXOS2:
2669 			type = SU2str;
2670 			break;
2671 		case X86BOOT:
2672 			type = X86str;
2673 			break;
2674 		case DOSOS12:
2675 			type = Dstr;
2676 			break;
2677 		case DOSOS16:
2678 			type = D16str;
2679 			break;
2680 		case EXTDOS:
2681 			type = EDstr;
2682 			break;
2683 		case DOSDATA:
2684 			type = DDstr;
2685 			break;
2686 		case DOSHUGE:
2687 			type = DBstr;
2688 			break;
2689 		case PCIXOS:
2690 			type = PCstr;
2691 			break;
2692 		case DIAGPART:
2693 			type = DIAGstr;
2694 			break;
2695 		case FDISK_IFS:
2696 			type = IFSstr;
2697 			break;
2698 		case FDISK_AIXBOOT:
2699 			type = AIXstr;
2700 			break;
2701 		case FDISK_AIXDATA:
2702 			type = AIXDstr;
2703 			break;
2704 		case FDISK_OS2BOOT:
2705 			type = OS2str;
2706 			break;
2707 		case FDISK_WINDOWS:
2708 			type = WINstr;
2709 			break;
2710 		case FDISK_EXT_WIN:
2711 			type = EWINstr;
2712 			break;
2713 		case FDISK_FAT95:
2714 			type = FAT95str;
2715 			break;
2716 		case FDISK_EXTLBA:
2717 			type = EXTLstr;
2718 			break;
2719 		case FDISK_LINUX:
2720 			type = LINUXstr;
2721 			break;
2722 		case FDISK_CPM:
2723 			type = CPMstr;
2724 			break;
2725 		case FDISK_NOVELL3:
2726 			type = NOVstr;
2727 			break;
2728 		case FDISK_QNX4:
2729 			type = QNXstr;
2730 			break;
2731 		case FDISK_QNX42:
2732 			type = QNX2str;
2733 			break;
2734 		case FDISK_QNX43:
2735 			type = QNX3str;
2736 			break;
2737 		case FDISK_LINUXNAT:
2738 			type = LINNATstr;
2739 			break;
2740 		case FDISK_NTFSVOL1:
2741 			type = NTFSVOL1str;
2742 			break;
2743 		case FDISK_NTFSVOL2:
2744 			type = NTFSVOL2str;
2745 			break;
2746 		case FDISK_BSD:
2747 			type = BSDstr;
2748 			break;
2749 		case FDISK_NEXTSTEP:
2750 			type = NEXTSTEPstr;
2751 			break;
2752 		case FDISK_BSDIFS:
2753 			type = BSDIFSstr;
2754 			break;
2755 		case FDISK_BSDISWAP:
2756 			type = BSDISWAPstr;
2757 			break;
2758 		case EFI_PMBR:
2759 			type = EFIstr;
2760 			break;
2761 		default:
2762 			type = Ostr;
2763 			break;
2764 		}
2765 		startcyl = lel(Table[i].relsect) / (heads * sectors);
2766 		length = lel(Table[i].numsect) / (long)(heads * sectors);
2767 		if (lel(Table[i].numsect) % (long)(heads * sectors))
2768 			length++;
2769 		endcyl = startcyl + length - 1;
2770 		percent = length * 100 / Numcyl;
2771 		if ((remainder = (length * 100 % Numcyl)) != 0) {
2772 			if ((remainder * 100 / Numcyl) > 50) {
2773 				/* round up */
2774 				percent++;
2775 			}
2776 			/* Else leave the percent as is since it's already */
2777 			/* rounded down */
2778 		}
2779 		if (percent > 100)
2780 			percent = 100;
2781 		(void) printf(
2782 		    "\n          %d       %s    %-12.12s   %4d  %4d    %4d"
2783 		    "    %3d",
2784 		    i + 1, stat, type, startcyl, endcyl, length, percent);
2785 	}
2786 	/* Print warning message if table is empty */
2787 	if (Table[0].systid == UNUSED) {
2788 		(void) printf(W_LINE);
2789 		(void) printf("WARNING: no partitions are defined!");
2790 	} else {
2791 		/* Clear the warning line */
2792 		(void) printf(W_LINE);
2793 	}
2794 }
2795 
2796 /*
2797  * print_Table
2798  * Write the detailed fdisk table to standard error for
2799  * the selected disk device.
2800  */
2801 static void
2802 print_Table(void)
2803 {
2804 	int i;
2805 
2806 	(void) fprintf(stderr,
2807 	    "  SYSID ACT BHEAD BSECT BEGCYL   EHEAD ESECT ENDCYL   RELSECT"
2808 	    "   NUMSECT\n");
2809 
2810 	for (i = 0; i < FD_NUMPART; i++) {
2811 		(void) fprintf(stderr, "  %-5d ", Table[i].systid);
2812 		(void) fprintf(stderr, "%-3d ", Table[i].bootid);
2813 		(void) fprintf(stderr, "%-5d ", Table[i].beghead);
2814 		(void) fprintf(stderr, "%-5d ", Table[i].begsect & 0x3f);
2815 		(void) fprintf(stderr, "%-8d ",
2816 		    (((uint_t)Table[i].begsect & 0xc0) << 2) + Table[i].begcyl);
2817 
2818 		(void) fprintf(stderr, "%-5d ", Table[i].endhead);
2819 		(void) fprintf(stderr, "%-5d ", Table[i].endsect & 0x3f);
2820 		(void) fprintf(stderr, "%-8d ",
2821 		    (((uint_t)Table[i].endsect & 0xc0) << 2) + Table[i].endcyl);
2822 		(void) fprintf(stderr, "%-9d ", lel(Table[i].relsect));
2823 		(void) fprintf(stderr, "%-9d\n", lel(Table[i].numsect));
2824 
2825 	}
2826 }
2827 
2828 /*
2829  * copy_Table_to_Old_Table
2830  * Copy Table into Old_Table. The function only copies the systid,
2831  * numsect, relsect, and bootid values because they are the only
2832  * ones compared when determining if Table has changed.
2833  */
2834 static void
2835 copy_Table_to_Old_Table(void)
2836 {
2837 	int i;
2838 	for (i = 0; i < FD_NUMPART; i++)  {
2839 		(void) memcpy(&Old_Table[i], &Table[i], sizeof (Table[0]));
2840 	}
2841 }
2842 
2843 /*
2844  * nulltbl
2845  * Zero out the systid, numsect, relsect, and bootid values in the
2846  * fdisk table.
2847  */
2848 static void
2849 nulltbl(void)
2850 {
2851 	int i;
2852 
2853 	for (i = 0; i < FD_NUMPART; i++)  {
2854 		Table[i].systid = UNUSED;
2855 		Table[i].numsect = lel(UNUSED);
2856 		Table[i].relsect = lel(UNUSED);
2857 		Table[i].bootid = 0;
2858 	}
2859 }
2860 
2861 /*
2862  * copy_Bootblk_to_Table
2863  * Copy the bytes from the boot record to an internal "Table".
2864  * All unused are padded with zeros starting at offset 446.
2865  */
2866 static void
2867 copy_Bootblk_to_Table(void)
2868 {
2869 	int i, j;
2870 	char *bootptr;
2871 	struct ipart iparts[FD_NUMPART];
2872 
2873 	/* Get an aligned copy of the partition tables */
2874 	(void) memcpy(iparts, Bootblk->parts, sizeof (iparts));
2875 	bootptr = (char *)iparts;	/* Points to start of partition table */
2876 	if (les(Bootblk->signature) != MBB_MAGIC)  {
2877 		/* Signature is missing */
2878 		nulltbl();
2879 		(void) memcpy(Bootblk->bootinst, &BootCod, BOOTSZ);
2880 		return;
2881 	}
2882 	/*
2883 	 * When the DOS fdisk command deletes a partition, it is not
2884 	 * recognized by the old algorithm.  The algorithm that
2885 	 * follows looks at each entry in the Bootrec and copies all
2886 	 * those that are valid.
2887 	 */
2888 	j = 0;
2889 	for (i = 0; i < FD_NUMPART; i++) {
2890 		if (iparts[i].systid == 0) {
2891 			/* Null entry */
2892 			bootptr += sizeof (struct ipart);
2893 		} else {
2894 			fill_ipart(bootptr, &Table[j]);
2895 			j++;
2896 			bootptr += sizeof (struct ipart);
2897 		}
2898 	}
2899 	for (i = j; i < FD_NUMPART; i++) {
2900 		Table[i].systid = UNUSED;
2901 		Table[i].numsect = lel(UNUSED);
2902 		Table[i].relsect = lel(UNUSED);
2903 		Table[i].bootid = 0;
2904 
2905 	}
2906 	/* For now, always replace the bootcode with ours */
2907 	(void) memcpy(Bootblk->bootinst, &BootCod, BOOTSZ);
2908 	copy_Table_to_Bootblk();
2909 }
2910 
2911 /*
2912  * fill_ipart
2913  * Initialize ipart structure values.
2914  */
2915 static void
2916 fill_ipart(char *bootptr, struct ipart *partp)
2917 {
2918 #ifdef sparc
2919 	/* Packing struct ipart for Sparc */
2920 	partp->bootid	= getbyte(&bootptr);
2921 	partp->beghead	= getbyte(&bootptr);
2922 	partp->begsect	= getbyte(&bootptr);
2923 	partp->begcyl	= getbyte(&bootptr);
2924 	partp->systid	= getbyte(&bootptr);
2925 	partp->endhead	= getbyte(&bootptr);
2926 	partp->endsect	= getbyte(&bootptr);
2927 	partp->endcyl	= getbyte(&bootptr);
2928 	partp->relsect	= (int32_t)getlong(&bootptr);
2929 	partp->numsect	= (int32_t)getlong(&bootptr);
2930 #else
2931 	*partp = *(struct ipart *)bootptr;
2932 #endif
2933 }
2934 
2935 /*
2936  * getbyte, getlong
2937  * 	Get a byte, a short, or a long (SPARC only).
2938  */
2939 #ifdef sparc
2940 uchar_t
2941 getbyte(char **bp)
2942 {
2943 	uchar_t	b;
2944 
2945 	b = (uchar_t)**bp;
2946 	*bp = *bp + 1;
2947 	return (b);
2948 }
2949 
2950 uint32_t
2951 getlong(char **bp)
2952 {
2953 	int32_t	b, bh, bl;
2954 
2955 	bh = ((**bp) << 8) | *(*bp + 1);
2956 	*bp += 2;
2957 	bl = ((**bp) << 8) | *(*bp + 1);
2958 	*bp += 2;
2959 
2960 	b = (bh << 16) | bl;
2961 	return ((uint32_t)b);
2962 }
2963 #endif
2964 
2965 /*
2966  * copy_Table_to_Bootblk
2967  * Copy the table into the 512 boot record. Note that the unused
2968  * entries will always be the last ones in the table and they are
2969  * marked with 100 in sysind. The the unused portion of the table
2970  * is padded with zeros in the bytes after the used entries.
2971  */
2972 static void
2973 copy_Table_to_Bootblk(void)
2974 {
2975 	struct ipart *boot_ptr, *tbl_ptr;
2976 
2977 	boot_ptr = (struct ipart *)Bootblk->parts;
2978 	tbl_ptr = (struct ipart *)&Table[0].bootid;
2979 	for (; tbl_ptr < (struct ipart *)&Table[FD_NUMPART].bootid;
2980 	    tbl_ptr++, boot_ptr++) {
2981 		if (tbl_ptr->systid == UNUSED)
2982 			(void) memset(boot_ptr, 0, sizeof (struct ipart));
2983 		else
2984 			(void) memcpy(boot_ptr, tbl_ptr, sizeof (struct ipart));
2985 	}
2986 	Bootblk->signature = les(MBB_MAGIC);
2987 }
2988 
2989 /*
2990  * TableChanged
2991  * 	Check for any changes in the partition table.
2992  */
2993 static int
2994 TableChanged(void)
2995 {
2996 	int i, changed;
2997 
2998 	changed = 0;
2999 	for (i = 0; i < FD_NUMPART; i++) {
3000 		if (memcmp(&Old_Table[i], &Table[i], sizeof (Table[0])) != 0) {
3001 			/* Partition table changed, write back to disk */
3002 			changed = 1;
3003 		}
3004 	}
3005 
3006 	return (changed);
3007 }
3008 
3009 /*
3010  * ffile_write
3011  * 	Display contents of partition table to standard output or
3012  *	another file name without writing it to the disk (-W file).
3013  */
3014 static void
3015 ffile_write(char *file)
3016 {
3017 	register int	i;
3018 	FILE *fp;
3019 
3020 	/*
3021 	 * If file isn't standard output, then it's a file name.
3022 	 * Open file and write it.
3023 	 */
3024 	if (file != (char *)stdout) {
3025 		if ((fp = fopen(file, "w")) == NULL) {
3026 			(void) fprintf(stderr,
3027 			    "fdisk: Cannot open output file %s.\n",
3028 			    file);
3029 			exit(1);
3030 		}
3031 	}
3032 	else
3033 		fp = stdout;
3034 
3035 	/*
3036 	 * Write the fdisk table information
3037 	 */
3038 	(void) fprintf(fp, "\n* %s default fdisk table\n", Dfltdev);
3039 	(void) fprintf(fp, "* Dimensions:\n");
3040 	(void) fprintf(fp, "*   %4d bytes/sector\n", sectsiz);
3041 	(void) fprintf(fp, "*   %4d sectors/track\n", sectors);
3042 	(void) fprintf(fp, "*   %4d tracks/cylinder\n", heads);
3043 	(void) fprintf(fp, "*   %4d cylinders\n", Numcyl);
3044 	(void) fprintf(fp, "*\n");
3045 	/* Write virtual (HBA) geometry, if required	*/
3046 	if (v_flag) {
3047 		(void) fprintf(fp, "* HBA Dimensions:\n");
3048 		(void) fprintf(fp, "*   %4d bytes/sector\n", sectsiz);
3049 		(void) fprintf(fp, "*   %4d sectors/track\n", hba_sectors);
3050 		(void) fprintf(fp, "*   %4d tracks/cylinder\n", hba_heads);
3051 		(void) fprintf(fp, "*   %4d cylinders\n", hba_Numcyl);
3052 		(void) fprintf(fp, "*\n");
3053 	}
3054 	(void) fprintf(fp, "* systid:\n");
3055 	(void) fprintf(fp, "*    1: DOSOS12\n");
3056 	(void) fprintf(fp, "*    2: PCIXOS\n");
3057 	(void) fprintf(fp, "*    4: DOSOS16\n");
3058 	(void) fprintf(fp, "*    5: EXTDOS\n");
3059 	(void) fprintf(fp, "*    6: DOSBIG\n");
3060 	(void) fprintf(fp, "*    7: FDISK_IFS\n");
3061 	(void) fprintf(fp, "*    8: FDISK_AIXBOOT\n");
3062 	(void) fprintf(fp, "*    9: FDISK_AIXDATA\n");
3063 	(void) fprintf(fp, "*   10: FDISK_0S2BOOT\n");
3064 	(void) fprintf(fp, "*   11: FDISK_WINDOWS\n");
3065 	(void) fprintf(fp, "*   12: FDISK_EXT_WIN\n");
3066 	(void) fprintf(fp, "*   14: FDISK_FAT95\n");
3067 	(void) fprintf(fp, "*   15: FDISK_EXTLBA\n");
3068 	(void) fprintf(fp, "*   18: DIAGPART\n");
3069 	(void) fprintf(fp, "*   65: FDISK_LINUX\n");
3070 	(void) fprintf(fp, "*   82: FDISK_CPM\n");
3071 	(void) fprintf(fp, "*   86: DOSDATA\n");
3072 	(void) fprintf(fp, "*   98: OTHEROS\n");
3073 	(void) fprintf(fp, "*   99: UNIXOS\n");
3074 	(void) fprintf(fp, "*  101: FDISK_NOVELL3\n");
3075 	(void) fprintf(fp, "*  119: FDISK_QNX4\n");
3076 	(void) fprintf(fp, "*  120: FDISK_QNX42\n");
3077 	(void) fprintf(fp, "*  121: FDISK_QNX43\n");
3078 	(void) fprintf(fp, "*  130: SUNIXOS\n");
3079 	(void) fprintf(fp, "*  131: FDISK_LINUXNAT\n");
3080 	(void) fprintf(fp, "*  134: FDISK_NTFSVOL1\n");
3081 	(void) fprintf(fp, "*  135: FDISK_NTFSVOL2\n");
3082 	(void) fprintf(fp, "*  165: FDISK_BSD\n");
3083 	(void) fprintf(fp, "*  167: FDISK_NEXTSTEP\n");
3084 	(void) fprintf(fp, "*  183: FDISK_BSDIFS\n");
3085 	(void) fprintf(fp, "*  184: FDISK_BSDISWAP\n");
3086 	(void) fprintf(fp, "*  190: X86BOOT\n");
3087 	(void) fprintf(fp, "*  191: SUNIXOS2\n");
3088 	(void) fprintf(fp, "*  238: EFI_PMBR\n");
3089 	(void) fprintf(fp, "*  239: EFI_FS\n");
3090 	(void) fprintf(fp, "*\n");
3091 	(void) fprintf(fp,
3092 	    "\n* Id    Act  Bhead  Bsect  Bcyl    Ehead  Esect  Ecyl"
3093 	    "    Rsect    Numsect\n");
3094 	for (i = 0; i < FD_NUMPART; i++) {
3095 		if (Table[i].systid != UNUSED)
3096 			(void) fprintf(fp,
3097 			    "  %-5d %-4d %-6d %-6d %-7d %-6d %-6d %-7d %-8d"
3098 			    " %-8d\n",
3099 			    Table[i].systid,
3100 			    Table[i].bootid,
3101 			    Table[i].beghead,
3102 			    Table[i].begsect & 0x3f,
3103 			    ((Table[i].begcyl & 0xff) | ((Table[i].begsect &
3104 			    0xc0) << 2)),
3105 			    Table[i].endhead,
3106 			    Table[i].endsect & 0x3f,
3107 			    ((Table[i].endcyl & 0xff) | ((Table[i].endsect &
3108 			    0xc0) << 2)),
3109 			    lel(Table[i].relsect),
3110 			    lel(Table[i].numsect));
3111 	}
3112 	if (fp != stdout)
3113 		(void) fclose(fp);
3114 }
3115 
3116 /*
3117  * fix_slice
3118  * 	Read the VTOC table on the Solaris partition and check that no
3119  *	slices exist that extend past the end of the Solaris partition.
3120  *	If no Solaris partition exists, nothing is done.
3121  */
3122 static void
3123 fix_slice(void)
3124 {
3125 	int	i;
3126 	int	numsect;
3127 
3128 	if (io_image) {
3129 		return;
3130 	}
3131 
3132 	for (i = 0; i < FD_NUMPART; i++) {
3133 		if (Table[i].systid == SUNIXOS || Table[i].systid == SUNIXOS2) {
3134 			/*
3135 			 * Only the size matters (not starting point), since
3136 			 * VTOC entries are relative to the start of
3137 			 * the partition.
3138 			 */
3139 			numsect = lel(Table[i].numsect);
3140 			break;
3141 		}
3142 	}
3143 
3144 	if (i >= FD_NUMPART) {
3145 		if (!io_nifdisk) {
3146 			(void) fprintf(stderr,
3147 			    "fdisk: No Solaris partition found - VTOC not"
3148 			    " checked.\n");
3149 		}
3150 		return;
3151 	}
3152 
3153 	if (readvtoc() != VTOC_OK) {
3154 		exit(1);		/* Failed to read the VTOC */
3155 	}
3156 	for (i = 0; i < V_NUMPAR; i++) {
3157 		/* Special case for slice two (entire disk) */
3158 		if (i == 2) {
3159 			if (disk_vtoc.v_part[i].p_start != 0) {
3160 				(void) fprintf(stderr,
3161 				    "slice %d starts at %ld, is not at"
3162 				    " start of partition",
3163 				    i, disk_vtoc.v_part[i].p_start);
3164 				if (!io_nifdisk) {
3165 					(void) printf(" adjust ?:");
3166 					if (yesno())
3167 						disk_vtoc.v_part[i].p_start = 0;
3168 				} else {
3169 					disk_vtoc.v_part[i].p_start = 0;
3170 					(void) fprintf(stderr, " adjusted!\n");
3171 				}
3172 
3173 			}
3174 			if (disk_vtoc.v_part[i].p_size != numsect) {
3175 				(void) fprintf(stderr,
3176 				    "slice %d size %ld does not cover"
3177 				    " complete partition",
3178 				    i, disk_vtoc.v_part[i].p_size);
3179 				if (!io_nifdisk) {
3180 					(void) printf(" adjust ?:");
3181 					if (yesno())
3182 						disk_vtoc.v_part[i].p_size =
3183 						    numsect;
3184 				} else {
3185 					disk_vtoc.v_part[i].p_size = numsect;
3186 					(void) fprintf(stderr, " adjusted!\n");
3187 				}
3188 			}
3189 			if (disk_vtoc.v_part[i].p_tag != V_BACKUP) {
3190 				(void) fprintf(stderr,
3191 				    "slice %d tag was %d should be %d",
3192 				    i, disk_vtoc.v_part[i].p_tag,
3193 				    V_BACKUP);
3194 				if (!io_nifdisk) {
3195 					(void) printf(" fix ?:");
3196 					if (yesno())
3197 						disk_vtoc.v_part[i].p_tag =
3198 						    V_BACKUP;
3199 				} else {
3200 					disk_vtoc.v_part[i].p_tag = V_BACKUP;
3201 					(void) fprintf(stderr, " fixed!\n");
3202 				}
3203 			}
3204 			continue;
3205 		}
3206 		if (io_ADJT) {
3207 			if (disk_vtoc.v_part[i].p_start > numsect ||
3208 			    disk_vtoc.v_part[i].p_start +
3209 			    disk_vtoc.v_part[i].p_size > numsect) {
3210 				(void) fprintf(stderr,
3211 				    "slice %d (start %ld, end %ld)"
3212 				    " is larger than the partition",
3213 				    i, disk_vtoc.v_part[i].p_start,
3214 				    disk_vtoc.v_part[i].p_start +
3215 				    disk_vtoc.v_part[i].p_size);
3216 				if (!io_nifdisk) {
3217 					(void) printf(" remove ?:");
3218 					if (yesno()) {
3219 						disk_vtoc.v_part[i].p_size = 0;
3220 						disk_vtoc.v_part[i].p_start = 0;
3221 						disk_vtoc.v_part[i].p_tag = 0;
3222 						disk_vtoc.v_part[i].p_flag = 0;
3223 					}
3224 				} else {
3225 					disk_vtoc.v_part[i].p_size = 0;
3226 					disk_vtoc.v_part[i].p_start = 0;
3227 					disk_vtoc.v_part[i].p_tag = 0;
3228 					disk_vtoc.v_part[i].p_flag = 0;
3229 					(void) fprintf(stderr,
3230 					    " removed!\n");
3231 				}
3232 			}
3233 			continue;
3234 		}
3235 		if (disk_vtoc.v_part[i].p_start > numsect) {
3236 			(void) fprintf(stderr,
3237 			    "slice %d (start %ld) is larger than the partition",
3238 			    i, disk_vtoc.v_part[i].p_start);
3239 			if (!io_nifdisk) {
3240 				(void) printf(" remove ?:");
3241 				if (yesno()) {
3242 					disk_vtoc.v_part[i].p_size = 0;
3243 					disk_vtoc.v_part[i].p_start = 0;
3244 					disk_vtoc.v_part[i].p_tag = 0;
3245 					disk_vtoc.v_part[i].p_flag = 0;
3246 				}
3247 			} else {
3248 				disk_vtoc.v_part[i].p_size = 0;
3249 				disk_vtoc.v_part[i].p_start = 0;
3250 				disk_vtoc.v_part[i].p_tag = 0;
3251 				disk_vtoc.v_part[i].p_flag = 0;
3252 				(void) fprintf(stderr,
3253 				" removed!\n");
3254 			}
3255 		} else if (disk_vtoc.v_part[i].p_start
3256 		    + disk_vtoc.v_part[i].p_size > numsect) {
3257 			(void) fprintf(stderr,
3258 			    "slice %d (end %ld) is larger"
3259 			    " than the partition",
3260 			    i,
3261 			    disk_vtoc.v_part[i].p_start +
3262 			    disk_vtoc.v_part[i].p_size);
3263 			if (!io_nifdisk) {
3264 				(void) printf(" adjust ?:");
3265 				if (yesno()) {
3266 					disk_vtoc.v_part[i].p_size = numsect;
3267 				}
3268 			} else {
3269 				disk_vtoc.v_part[i].p_size = numsect;
3270 				(void) fprintf(stderr, " adjusted!\n");
3271 			}
3272 		}
3273 	}
3274 #if 1		/* bh for now */
3275 	/* Make the VTOC look sane - ha ha */
3276 	disk_vtoc.v_version = V_VERSION;
3277 	disk_vtoc.v_sanity = VTOC_SANE;
3278 	disk_vtoc.v_nparts = V_NUMPAR;
3279 	if (disk_vtoc.v_sectorsz == 0)
3280 		disk_vtoc.v_sectorsz = NBPSCTR;
3281 #endif
3282 
3283 	/* Write the VTOC back to the disk */
3284 	if (!io_readonly)
3285 		(void) writevtoc();
3286 }
3287 
3288 /*
3289  * yesno
3290  * Get yes or no answer. Return 1 for yes and 0 for no.
3291  */
3292 
3293 static int
3294 yesno(void)
3295 {
3296 	char	s[80];
3297 
3298 	for (;;) {
3299 		(void) gets(s);
3300 		rm_blanks(s);
3301 		if ((s[1] != 0) || ((s[0] != 'y') && (s[0] != 'n'))) {
3302 			(void) printf(E_LINE);
3303 			(void) printf("Please answer with \"y\" or \"n\": ");
3304 			continue;
3305 		}
3306 		if (s[0] == 'y')
3307 			return (1);
3308 		else
3309 			return (0);
3310 	}
3311 }
3312 
3313 /*
3314  * readvtoc
3315  * 	Read the VTOC from the Solaris partition of the device.
3316  */
3317 static int
3318 readvtoc(void)
3319 {
3320 	int	i;
3321 	int	retval = VTOC_OK;
3322 
3323 	if ((i = read_vtoc(Dev, &disk_vtoc)) < VTOC_OK) {
3324 		if (i == VT_EINVAL) {
3325 			(void) fprintf(stderr, "fdisk: Invalid VTOC.\n");
3326 			vt_inval++;
3327 			retval = VTOC_INVAL;
3328 		} else if (i == VT_ENOTSUP) {
3329 			(void) fprintf(stderr, "fdisk: partition may have EFI "
3330 			    "GPT\n");
3331 			retval = VTOC_NOTSUP;
3332 		} else {
3333 			(void) fprintf(stderr, "fdisk: Cannot read VTOC.\n");
3334 			retval = VTOC_RWERR;
3335 		}
3336 	}
3337 	return (retval);
3338 }
3339 
3340 /*
3341  * writevtoc
3342  * 	Write the VTOC to the Solaris partition on the device.
3343  */
3344 static int
3345 writevtoc(void)
3346 {
3347 	int	i;
3348 	int	retval = 0;
3349 
3350 	if ((i = write_vtoc(Dev, &disk_vtoc)) != 0) {
3351 		if (i == VT_EINVAL) {
3352 			(void) fprintf(stderr,
3353 			    "fdisk: Invalid entry exists in VTOC.\n");
3354 			retval = VTOC_INVAL;
3355 		} else if (i == VT_ENOTSUP) {
3356 			(void) fprintf(stderr, "fdisk: partition may have EFI "
3357 			    "GPT\n");
3358 			retval = VTOC_NOTSUP;
3359 		} else {
3360 			(void) fprintf(stderr, "fdisk: Cannot write VTOC.\n");
3361 			retval = VTOC_RWERR;
3362 		}
3363 	}
3364 	return (retval);
3365 }
3366 
3367 /*
3368  * efi_ioctl
3369  * issues DKIOCSETEFI IOCTL
3370  * (duplicate of private efi_ioctl() in rdwr_efi.c
3371  */
3372 static int
3373 efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
3374 {
3375 	void *data = dk_ioc->dki_data;
3376 	int error;
3377 
3378 	dk_ioc->dki_data_64 = (uintptr_t)data;
3379 	error = ioctl(fd, cmd, (void *)dk_ioc);
3380 
3381 	return (error);
3382 }
3383 
3384 /*
3385  * clear_efi
3386  * Clear EFI labels from the EFI_PMBR partition on the device
3387  * This function is modeled on the libefi(3LIB) call efi_write()
3388  */
3389 static int
3390 clear_efi(void)
3391 {
3392 	struct dk_gpt	*efi_vtoc;
3393 	dk_efi_t	dk_ioc;
3394 
3395 	/*
3396 	 * see if we can read the EFI label
3397 	 */
3398 	if (efi_alloc_and_read(Dev, &efi_vtoc) < 0) {
3399 		return (VT_ERROR);
3400 	}
3401 
3402 	/*
3403 	 * set up the dk_ioc structure for writing
3404 	 */
3405 	dk_ioc.dki_lba = 1;
3406 	dk_ioc.dki_length = EFI_MIN_ARRAY_SIZE + efi_vtoc->efi_lbasize;
3407 
3408 	if ((dk_ioc.dki_data = calloc(dk_ioc.dki_length, 1)) == NULL) {
3409 		return (VT_ERROR);
3410 	}
3411 
3412 	/*
3413 	 * clear the primary label
3414 	 */
3415 	if (io_debug) {
3416 		(void) fprintf(stderr,
3417 		    "\tClearing primary EFI label at block %lld\n",
3418 		    dk_ioc.dki_lba);
3419 	}
3420 
3421 	if (efi_ioctl(Dev, DKIOCSETEFI, &dk_ioc) == -1) {
3422 		free(dk_ioc.dki_data);
3423 		switch (errno) {
3424 			case EIO:
3425 				return (VT_EIO);
3426 			case EINVAL:
3427 				return (VT_EINVAL);
3428 			default:
3429 				return (VT_ERROR);
3430 		}
3431 	}
3432 
3433 	/*
3434 	 * clear the backup partition table
3435 	 */
3436 	dk_ioc.dki_lba = efi_vtoc->efi_last_u_lba + 1;
3437 	dk_ioc.dki_length -= efi_vtoc->efi_lbasize;
3438 	dk_ioc.dki_data++;
3439 	if (io_debug) {
3440 		(void) fprintf(stderr,
3441 		    "\tClearing backup partition table at block %lld\n",
3442 		    dk_ioc.dki_lba);
3443 	}
3444 
3445 	if (efi_ioctl(Dev, DKIOCSETEFI, &dk_ioc) == -1) {
3446 		(void) fprintf(stderr, "\tUnable to clear backup EFI label at "
3447 		    "block %llu; errno %d\n", efi_vtoc->efi_last_u_lba + 1,
3448 		    errno);
3449 	}
3450 
3451 	/*
3452 	 * clear the backup label
3453 	 */
3454 	dk_ioc.dki_lba = efi_vtoc->efi_last_lba;
3455 	dk_ioc.dki_length = efi_vtoc->efi_lbasize;
3456 	dk_ioc.dki_data--;
3457 	if (io_debug) {
3458 		(void) fprintf(stderr, "\tClearing backup label at block "
3459 		    "%lld\n", dk_ioc.dki_lba);
3460 	}
3461 
3462 	if (efi_ioctl(Dev, DKIOCSETEFI, &dk_ioc) == -1) {
3463 		(void) fprintf(stderr,
3464 		    "\tUnable to clear backup EFI label at "
3465 		    "block %llu; errno %d\n",
3466 		    efi_vtoc->efi_last_lba,
3467 		    errno);
3468 	}
3469 
3470 	free(dk_ioc.dki_data);
3471 	efi_free(efi_vtoc);
3472 
3473 	return (0);
3474 }
3475 
3476 /*
3477  * clear_vtoc
3478  * 	Clear the VTOC from the current or previous Solaris partition on the
3479  *      device.
3480  */
3481 static void
3482 clear_vtoc(int table, int part)
3483 {
3484 	struct ipart *clr_table;
3485 	struct dk_label disk_label;
3486 	int pcyl, ncyl, backup_block, solaris_offset, count, bytes, seek_byte;
3487 
3488 #ifdef DEBUG
3489 	struct dk_label	read_label;
3490 #endif /* DEBUG */
3491 
3492 	if (table == OLD) {
3493 		clr_table = &Old_Table[part];
3494 	} else {
3495 		clr_table = &Table[part];
3496 	}
3497 
3498 	(void) memset(&disk_label, 0, sizeof (struct dk_label));
3499 
3500 	seek_byte = (lel(clr_table->relsect) * sectsiz) + VTOC_OFFSET;
3501 
3502 	if (io_debug) {
3503 		(void) fprintf(stderr, "\tClearing primary VTOC at byte %d\n",
3504 		    seek_byte);
3505 	}
3506 
3507 	if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3508 		(void) fprintf(stderr,
3509 		    "\tError seeking to primary label at byte %d\n",
3510 		    seek_byte);
3511 		return;
3512 	}
3513 
3514 	bytes = write(Dev, &disk_label, sizeof (struct dk_label));
3515 
3516 	if (bytes != sizeof (struct dk_label)) {
3517 		(void) fprintf(stderr,
3518 		    "\tWarning: only %d bytes written to clear primary VTOC!\n",
3519 		    bytes);
3520 	}
3521 
3522 #ifdef DEBUG
3523 	if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3524 		(void) fprintf(stderr,
3525 		    "DEBUG: Error seeking to primary label at byte %d\n",
3526 		    seek_byte);
3527 		return;
3528 	} else {
3529 		(void) fprintf(stderr, "DEBUG: Successful lseek() to byte %d\n",
3530 		    seek_byte);
3531 	}
3532 
3533 	bytes = read(Dev, &read_label, sizeof (struct dk_label));
3534 
3535 	if (bytes != sizeof (struct dk_label)) {
3536 		(void) fprintf(stderr,
3537 		    "DEBUG: Warning: only %d bytes read of label\n",
3538 		    bytes);
3539 	}
3540 
3541 	if (memcmp(&disk_label, &read_label, sizeof (struct dk_label)) != 0) {
3542 		(void) fprintf(stderr,
3543 		    "DEBUG: Warning: disk_label and read_label differ!!!\n");
3544 	} else {
3545 		(void) fprintf(stderr, "DEBUG Good compare of disk_label and "
3546 		    "read_label\n");
3547 	}
3548 #endif /* DEBUG */
3549 
3550 	/* Clear backup label */
3551 	pcyl = lel(clr_table->numsect) / (heads * sectors);
3552 	solaris_offset = lel(clr_table->relsect);
3553 	ncyl = pcyl - acyl;
3554 
3555 	backup_block = ((ncyl + acyl - 1) *
3556 	    (heads * sectors)) + ((heads - 1) * sectors) + 1;
3557 
3558 	for (count = 1; count < 6; count++) {
3559 		seek_byte = (solaris_offset + backup_block) * 512;
3560 
3561 		if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3562 			(void) fprintf(stderr,
3563 			    "\tError seeking to backup label at byte %d on "
3564 			    "%s.\n", seek_byte, Dfltdev);
3565 			return;
3566 		}
3567 
3568 		if (io_debug) {
3569 			(void) fprintf(stderr, "\tClearing backup VTOC at"
3570 			    " byte %d (block %d)\n",
3571 			    (solaris_offset + backup_block) * 512,
3572 			    (solaris_offset + backup_block));
3573 		}
3574 
3575 		bytes = write(Dev, &disk_label, sizeof (struct dk_label));
3576 
3577 		if (bytes != sizeof (struct dk_label)) {
3578 			(void) fprintf(stderr,
3579 			    "\t\tWarning: only %d bytes written to "
3580 			    "clear backup VTOC at block %d!\n", bytes,
3581 			    (solaris_offset + backup_block));
3582 		}
3583 
3584 #ifdef DEBUG
3585 	if (lseek(Dev, seek_byte, SEEK_SET) == -1) {
3586 		(void) fprintf(stderr,
3587 		    "DEBUG: Error seeking to backup label at byte %d\n",
3588 		    seek_byte);
3589 		return;
3590 	} else {
3591 		(void) fprintf(stderr, "DEBUG: Successful lseek() to byte %d\n",
3592 		    seek_byte);
3593 	}
3594 
3595 	bytes = read(Dev, &read_label, sizeof (struct dk_label));
3596 
3597 	if (bytes != sizeof (struct dk_label)) {
3598 		(void) fprintf(stderr,
3599 		    "DEBUG: Warning: only %d bytes read of backup label\n",
3600 		    bytes);
3601 	}
3602 
3603 	if (memcmp(&disk_label, &read_label, sizeof (struct dk_label)) != 0) {
3604 		(void) fprintf(stderr,
3605 		    "DEBUG: Warning: disk_label and read_label differ!!!\n");
3606 	} else {
3607 		(void) fprintf(stderr,
3608 		    "DEBUG: Good compare of disk_label and backup "
3609 		    "read_label\n");
3610 	}
3611 #endif /* DEBUG */
3612 
3613 		backup_block += 2;
3614 	}
3615 }
3616 
3617 #define	FDISK_STANDARD_LECTURE \
3618 	"Fdisk is normally used with the device that " \
3619 	"represents the entire fixed disk.\n" \
3620 	"(For example, /dev/rdsk/c0d0p0 on x86 or " \
3621 	"/dev/rdsk/c0t5d0s2 on sparc).\n"
3622 
3623 #define	FDISK_LECTURE_NOT_SECTOR_ZERO \
3624 	"The device does not appear to include absolute\n" \
3625 	"sector 0 of the PHYSICAL disk " \
3626 	"(the normal location for an fdisk table).\n"
3627 
3628 #define	FDISK_LECTURE_NOT_FULL \
3629 	"The device does not appear to encompass the entire PHYSICAL disk.\n"
3630 
3631 #define	FDISK_LECTURE_NO_VTOC \
3632 	"Unable to find a volume table of contents.\n" \
3633 	"Cannot verify the device encompasses the full PHYSICAL disk.\n"
3634 
3635 #define	FDISK_LECTURE_NO_GEOM \
3636 	"Unable to get geometry from device.\n" \
3637 	"Cannot verify the device encompasses the full PHYSICAL disk.\n"
3638 
3639 #define	FDISK_SHALL_I_CONTINUE \
3640 	"Are you sure you want to continue? (y/n) "
3641 
3642 /*
3643  *  lecture_and_query
3644  *	Called when a sanity check fails.  This routine gives a warning
3645  *	specific to the check that fails, followed by a generic lecture
3646  *	about the "right" device to supply as input.  Then, if appropriate,
3647  *	it will prompt the user on whether or not they want to continue.
3648  *	Inappropriate times for prompting are when the user has selected
3649  *	non-interactive mode or read-only mode.
3650  */
3651 static int
3652 lecture_and_query(char *warning, char *devname)
3653 {
3654 	if (io_nifdisk)
3655 		return (0);
3656 
3657 	(void) fprintf(stderr, "WARNING: Device %s: \n", devname);
3658 	(void) fprintf(stderr, "%s", warning);
3659 	(void) fprintf(stderr, FDISK_STANDARD_LECTURE);
3660 	(void) fprintf(stderr, FDISK_SHALL_I_CONTINUE);
3661 
3662 	return (yesno());
3663 }
3664 
3665 static void
3666 sanity_check_provided_device(char *devname, int fd)
3667 {
3668 	struct vtoc v;
3669 	struct dk_geom d;
3670 	struct part_info pi;
3671 	long totsize;
3672 	int idx = -1;
3673 
3674 	/*
3675 	 *  First try the PARTINFO ioctl.  If it works, we will be able
3676 	 *  to tell if they've specified the full disk partition by checking
3677 	 *  to see if they've specified a partition that starts at sector 0.
3678 	 */
3679 	if (ioctl(fd, DKIOCPARTINFO, &pi) != -1) {
3680 		if (pi.p_start != 0) {
3681 			if (!lecture_and_query(FDISK_LECTURE_NOT_SECTOR_ZERO,
3682 			    devname)) {
3683 				(void) close(fd);
3684 				exit(1);
3685 			}
3686 		}
3687 	} else {
3688 		if ((idx = read_vtoc(fd, &v)) < 0) {
3689 			if (!lecture_and_query(FDISK_LECTURE_NO_VTOC,
3690 			    devname)) {
3691 				(void) close(fd);
3692 				exit(1);
3693 			}
3694 			return;
3695 		}
3696 		if (ioctl(fd, DKIOCGGEOM, &d) == -1) {
3697 			perror(devname);
3698 			if (!lecture_and_query(FDISK_LECTURE_NO_GEOM,
3699 			    devname)) {
3700 				(void) close(fd);
3701 				exit(1);
3702 			}
3703 			return;
3704 		}
3705 		totsize = d.dkg_ncyl * d.dkg_nhead * d.dkg_nsect;
3706 		if (v.v_part[idx].p_size != totsize) {
3707 			if (!lecture_and_query(FDISK_LECTURE_NOT_FULL,
3708 			    devname)) {
3709 				(void) close(fd);
3710 				exit(1);
3711 			}
3712 		}
3713 	}
3714 }
3715 
3716 
3717 /*
3718  * get_node
3719  * Called from main to construct the name of the device node to open.
3720  * Initially tries to stat the node exactly as provided, if that fails
3721  * we prepend the default path (/dev/rdsk/).
3722  */
3723 static char *
3724 get_node(char *devname)
3725 {
3726 	char *node;
3727 	struct stat statbuf;
3728 	size_t space;
3729 
3730 	/* Don't do anything if we are skipping device checks */
3731 	if (io_image)
3732 		return (devname);
3733 
3734 	node = devname;
3735 
3736 	/* Try the node as provided first */
3737 	if (stat(node, (struct stat *)&statbuf) == -1) {
3738 		/*
3739 		 * Copy the passed in string to a new buffer, prepend the
3740 		 * default path and try again.
3741 		 */
3742 		space = strlen(DEFAULT_PATH) + strlen(devname) + 1;
3743 
3744 		if ((node = malloc(space)) == NULL) {
3745 			(void) fprintf(stderr, "fdisk: Unable to obtain memory "
3746 			    "for device node.\n");
3747 			exit(1);
3748 		}
3749 
3750 		/* Copy over the default path and the provided node */
3751 		(void) strncpy(node, DEFAULT_PATH, strlen(DEFAULT_PATH));
3752 		space -= strlen(DEFAULT_PATH);
3753 		(void) strlcpy(node + strlen(DEFAULT_PATH), devname, space);
3754 
3755 		/* Try to stat it again */
3756 		if (stat(node, (struct stat *)&statbuf) == -1) {
3757 			/* Failed all options, give up */
3758 			(void) fprintf(stderr,
3759 			    "fdisk: Cannot stat device %s.\n",
3760 			    devname);
3761 			exit(1);
3762 		}
3763 	}
3764 
3765 	/* Make sure the device specified is the raw device */
3766 	if ((statbuf.st_mode & S_IFMT) != S_IFCHR) {
3767 		(void) fprintf(stderr,
3768 		    "fdisk: %s must be a raw device.\n", node);
3769 		exit(1);
3770 	}
3771 
3772 	return (node);
3773 }
3774