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