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