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