xref: /illumos-gate/usr/src/cmd/format/menu_fdisk.c (revision 9742e5d31ea785b741c1dcd401242caf82abfbf1)
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  * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2016 Toomas Soome <tsoome@me.com>
24  */
25 
26 /*
27  * This file contains functions that implement the fdisk menu commands.
28  */
29 #include "global.h"
30 #include <errno.h>
31 #include <sys/time.h>
32 #include <sys/resource.h>
33 #include <sys/wait.h>
34 #include <signal.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <sys/dktp/fdisk.h>
39 #include <sys/stat.h>
40 #include <sys/dklabel.h>
41 #ifdef i386
42 #include <libfdisk.h>
43 #endif
44 
45 #include "main.h"
46 #include "analyze.h"
47 #include "menu.h"
48 #include "menu_command.h"
49 #include "menu_defect.h"
50 #include "menu_partition.h"
51 #include "menu_fdisk.h"
52 #include "param.h"
53 #include "misc.h"
54 #include "label.h"
55 #include "startup.h"
56 #include "partition.h"
57 #include "prompts.h"
58 #include "checkdev.h"
59 #include "io.h"
60 #include "ctlr_scsi.h"
61 #include "auto_sense.h"
62 
63 extern	struct menu_item menu_fdisk[];
64 struct mboot boot_sec;
65 uint_t	xstart;
66 
67 /*
68  * Byte swapping macros for accessing struct ipart
69  *	to resolve little endian on Sparc.
70  */
71 #if defined(sparc)
72 #define	les(val)	((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
73 #define	lel(val)	(((unsigned)(les((val)&0x0000FFFF))<<16) | \
74 			(les((unsigned)((val)&0xffff0000)>>16)))
75 
76 #elif	defined(i386)
77 
78 #define	les(val)	(val)
79 #define	lel(val)	(val)
80 
81 #else	/* defined(sparc) */
82 
83 #error	No Platform defined
84 
85 #endif	/* defined(sparc) */
86 
87 
88 /* Function prototypes */
89 
90 #if	defined(sparc)
91 
92 static int getbyte(uchar_t **);
93 static int getlong(uchar_t **);
94 
95 #endif	/* defined(sparc) */
96 
97 static int get_solaris_part(int fd, struct ipart *ipart);
98 
99 #ifdef i386
100 int extpart_init(ext_part_t **epp);
101 #endif
102 /*
103  * Handling the alignment problem of struct ipart.
104  */
105 static void
106 fill_ipart(char *bootptr, struct ipart *partp)
107 {
108 #if defined(sparc)
109 	/*
110 	 * Sparc platform:
111 	 *
112 	 * Packing short/word for struct ipart to resolve
113 	 *	little endian on Sparc since it is not
114 	 *	properly aligned on Sparc.
115 	 */
116 	partp->bootid = getbyte((uchar_t **)&bootptr);
117 	partp->beghead = getbyte((uchar_t **)&bootptr);
118 	partp->begsect = getbyte((uchar_t **)&bootptr);
119 	partp->begcyl = getbyte((uchar_t **)&bootptr);
120 	partp->systid = getbyte((uchar_t **)&bootptr);
121 	partp->endhead = getbyte((uchar_t **)&bootptr);
122 	partp->endsect = getbyte((uchar_t **)&bootptr);
123 	partp->endcyl = getbyte((uchar_t **)&bootptr);
124 	partp->relsect = getlong((uchar_t **)&bootptr);
125 	partp->numsect = getlong((uchar_t **)&bootptr);
126 #elif defined(i386)
127 	/*
128 	 * i386 platform:
129 	 *
130 	 * The fdisk table does not begin on a 4-byte boundary within
131 	 * the master boot record; so, we need to recopy its contents
132 	 * to another data structure to avoid an alignment exception.
133 	 */
134 	(void) bcopy(bootptr, partp, sizeof (struct ipart));
135 #else
136 #error  No Platform defined
137 #endif /* defined(sparc) */
138 }
139 
140 /*
141  * Get a correct byte/short/word routines for Sparc platform.
142  */
143 #if defined(sparc)
144 static int
145 getbyte(uchar_t **bp)
146 {
147 	int	b;
148 
149 	b = **bp;
150 	*bp = *bp + 1;
151 	return (b);
152 }
153 
154 #ifdef DEADCODE
155 static int
156 getshort(uchar_t **bp)
157 {
158 	int	b;
159 
160 	b = ((**bp) << 8) | *(*bp + 1);
161 	*bp += 2;
162 	return (b);
163 }
164 #endif /* DEADCODE */
165 
166 static int
167 getlong(uchar_t **bp)
168 {
169 	int	b, bh, bl;
170 
171 	bh = ((**bp) << 8) | *(*bp + 1);
172 	*bp += 2;
173 	bl = ((**bp) << 8) | *(*bp + 1);
174 	*bp += 2;
175 
176 	b = (bh << 16) | bl;
177 	return (b);
178 }
179 #endif /* defined(sparc) */
180 
181 #ifdef i386
182 /*
183  * Convert emcpowerN[a-p,p0,p1,p2,p3,p4] to emcpowerNp0 path,
184  * this is specific for emc powerpath driver.
185  */
186 static void
187 get_emcpower_pname(char *name, char *devname)
188 {
189 	char	*emcp = "emcpower";
190 	char	*npt = NULL;
191 	char	np[MAXNAMELEN];
192 	int	i = strlen(emcp);
193 
194 	(void) strcpy(np, devname);
195 	npt = strstr(np, emcp);
196 	while ((i < strlen(npt)) && (isdigit(npt[i])))
197 		i++;
198 	npt[i] = '\0';
199 	(void) snprintf(name, MAXNAMELEN, "/dev/rdsk/%sp0", npt);
200 }
201 #endif
202 
203 /*
204  * Convert cn[tn]dn to cn[tn]dns2 path
205  */
206 static void
207 get_sname(char *name)
208 {
209 	char		buf[MAXPATHLEN];
210 	char		*devp = "/dev/dsk";
211 	char		*rdevp = "/dev/rdsk";
212 	char		np[MAXNAMELEN];
213 	char		*npt;
214 
215 #ifdef i386
216 	if (emcpower_name(cur_disk->disk_name)) {
217 		get_emcpower_pname(name, cur_disk->disk_name);
218 		return;
219 	}
220 #endif
221 
222 	/*
223 	 * If it is a full path /dev/[r]dsk/cn[tn]dn, use this path
224 	 */
225 	(void) strcpy(np, cur_disk->disk_name);
226 	if (strncmp(rdevp, cur_disk->disk_name, strlen(rdevp)) == 0 ||
227 	    strncmp(devp, cur_disk->disk_name, strlen(devp)) == 0) {
228 		/*
229 		 * Skip if the path is already included with sN
230 		 */
231 		if (strchr(np, 's') == strrchr(np, 's')) {
232 			npt = strrchr(np, 'p');
233 			/* If pN is found, do not include it */
234 			if (npt != NULL) {
235 				*npt = '\0';
236 			}
237 			(void) snprintf(buf, sizeof (buf), "%ss2", np);
238 		} else {
239 			(void) snprintf(buf, sizeof (buf), "%s", np);
240 		}
241 	} else {
242 		(void) snprintf(buf, sizeof (buf), "/dev/rdsk/%ss2", np);
243 	}
244 	(void) strcpy(name, buf);
245 }
246 
247 /*
248  * Convert cn[tn]dnsn to cn[tn]dnp0 path
249  */
250 static void
251 get_pname(char *name)
252 {
253 	char		buf[MAXPATHLEN];
254 	char		*devp = "/dev/dsk";
255 	char		*rdevp = "/dev/rdsk";
256 	char		np[MAXNAMELEN];
257 	char		*npt;
258 
259 	/*
260 	 * If it is a full path /dev/[r]dsk/cn[tn]dnsn, use this path
261 	 */
262 	if (cur_disk == NULL) {
263 		(void) strcpy(np, x86_devname);
264 	} else {
265 		(void) strcpy(np, cur_disk->disk_name);
266 	}
267 
268 #ifdef i386
269 	if (emcpower_name(np)) {
270 		get_emcpower_pname(name, np);
271 		return;
272 	}
273 #endif
274 
275 	if (strncmp(rdevp, np, strlen(rdevp)) == 0 ||
276 	    strncmp(devp, np, strlen(devp)) == 0) {
277 		/*
278 		 * Skip if the path is already included with pN
279 		 */
280 		if (strchr(np, 'p') == NULL) {
281 			npt = strrchr(np, 's');
282 			/* If sN is found, do not include it */
283 			if (isdigit(*++npt)) {
284 				*--npt = '\0';
285 			}
286 			(void) snprintf(buf, sizeof (buf), "%sp0", np);
287 		} else {
288 			(void) snprintf(buf, sizeof (buf), "%s", np);
289 		}
290 	} else {
291 		(void) snprintf(buf, sizeof (buf), "/dev/rdsk/%sp0", np);
292 	}
293 	(void) strcpy(name, buf);
294 }
295 
296 /*
297  * Open file descriptor for current disk (cur_file)
298  *	with "p0" path or cur_disk->disk_path
299  */
300 void
301 open_cur_file(int mode)
302 {
303 	char	*dkpath;
304 	char	pbuf[MAXPATHLEN];
305 
306 	switch (mode) {
307 		case FD_USE_P0_PATH:
308 			(void) get_pname(&pbuf[0]);
309 			dkpath = pbuf;
310 			break;
311 		case FD_USE_CUR_DISK_PATH:
312 			if (cur_disk->fdisk_part.systid == SUNIXOS ||
313 			    cur_disk->fdisk_part.systid == SUNIXOS2) {
314 				(void) get_sname(&pbuf[0]);
315 				dkpath = pbuf;
316 			} else {
317 				dkpath = cur_disk->disk_path;
318 			}
319 			break;
320 		default:
321 			err_print("Error: Invalid mode option for opening "
322 			    "cur_file\n");
323 			fullabort();
324 	}
325 
326 	/* Close previous cur_file */
327 	(void) close(cur_file);
328 	/* Open cur_file with the required path dkpath */
329 	if ((cur_file = open_disk(dkpath, O_RDWR | O_NDELAY)) < 0) {
330 		err_print(
331 		    "Error: can't open selected disk '%s'.\n", dkpath);
332 		fullabort();
333 	}
334 }
335 
336 
337 /*
338  * This routine implements the 'fdisk' command.  It simply runs
339  * the fdisk command on the current disk.
340  * Use of this is restricted to interactive mode only.
341  */
342 int
343 c_fdisk(void)
344 {
345 
346 	char		buf[MAXPATHLEN];
347 	char		pbuf[MAXPATHLEN];
348 	struct stat	statbuf;
349 
350 	/*
351 	 * We must be in interactive mode to use the fdisk command
352 	 */
353 	if (option_f != NULL || isatty(0) != 1 || isatty(1) != 1) {
354 		err_print("Fdisk command is for interactive use only!\n");
355 		return (-1);
356 	}
357 
358 	/*
359 	 * There must be a current disk type and a current disk
360 	 */
361 	if (cur_dtype == NULL) {
362 		err_print("Current Disk Type is not set.\n");
363 		return (-1);
364 	}
365 
366 	/*
367 	 * Before running the fdisk command, get file status of
368 	 *	/dev/rdsk/cn[tn]dnp0 path to see if this disk
369 	 *	supports fixed disk partition table.
370 	 */
371 	(void) get_pname(&pbuf[0]);
372 	if (stat(pbuf, (struct stat *)&statbuf) == -1 ||
373 	    !S_ISCHR(statbuf.st_mode)) {
374 		err_print(
375 		"Disk does not support fixed disk partition table\n");
376 		return (0);
377 	}
378 
379 	/*
380 	 * Run the fdisk program.
381 	 */
382 	(void) snprintf(buf, sizeof (buf), "fdisk %s\n", pbuf);
383 	(void) system(buf);
384 
385 	/*
386 	 * Open cur_file with "p0" path for accessing the fdisk table
387 	 */
388 	(void) open_cur_file(FD_USE_P0_PATH);
389 
390 	/*
391 	 * Get solaris partition information in the fdisk partition table
392 	 */
393 	if (get_solaris_part(cur_file, &cur_disk->fdisk_part) == -1) {
394 		err_print("No fdisk solaris partition found\n");
395 		cur_disk->fdisk_part.numsect = 0;  /* No Solaris */
396 	}
397 
398 	/*
399 	 * Restore cur_file with cur_disk->disk_path
400 	 */
401 	(void) open_cur_file(FD_USE_CUR_DISK_PATH);
402 
403 	return (0);
404 }
405 
406 /*
407  * Read MBR on the disk
408  * if the Solaris partition has changed,
409  *	reread the vtoc
410  */
411 #ifdef DEADCODE
412 static void
413 update_cur_parts(void)
414 {
415 
416 	int i;
417 	register struct partition_info *parts;
418 
419 	for (i = 0; i < NDKMAP; i++) {
420 #if defined(_SUNOS_VTOC_16)
421 		if (cur_parts->vtoc.v_part[i].p_tag &&
422 		    cur_parts->vtoc.v_part[i].p_tag != V_ALTSCTR) {
423 			cur_parts->vtoc.v_part[i].p_start = 0;
424 			cur_parts->vtoc.v_part[i].p_size = 0;
425 
426 #endif
427 			cur_parts->pinfo_map[i].dkl_nblk = 0;
428 			cur_parts->pinfo_map[i].dkl_cylno = 0;
429 			cur_parts->vtoc.v_part[i].p_tag =
430 			    default_vtoc_map[i].p_tag;
431 			cur_parts->vtoc.v_part[i].p_flag =
432 			    default_vtoc_map[i].p_flag;
433 #if defined(_SUNOS_VTOC_16)
434 		}
435 #endif
436 	}
437 	cur_parts->pinfo_map[C_PARTITION].dkl_nblk = ncyl * spc();
438 
439 #if defined(_SUNOS_VTOC_16)
440 	/*
441 	 * Adjust for the boot partitions
442 	 */
443 	cur_parts->pinfo_map[I_PARTITION].dkl_nblk = spc();
444 	cur_parts->pinfo_map[I_PARTITION].dkl_cylno = 0;
445 	cur_parts->vtoc.v_part[C_PARTITION].p_start =
446 	    cur_parts->pinfo_map[C_PARTITION].dkl_cylno * nhead * nsect;
447 	cur_parts->vtoc.v_part[C_PARTITION].p_size =
448 	    cur_parts->pinfo_map[C_PARTITION].dkl_nblk;
449 
450 	cur_parts->vtoc.v_part[I_PARTITION].p_start =
451 	    cur_parts->pinfo_map[I_PARTITION].dkl_cylno;
452 	cur_parts->vtoc.v_part[I_PARTITION].p_size =
453 	    cur_parts->pinfo_map[I_PARTITION].dkl_nblk;
454 
455 #endif	/* defined(_SUNOS_VTOC_16) */
456 	parts = cur_dtype->dtype_plist;
457 	cur_dtype->dtype_ncyl = ncyl;
458 	cur_dtype->dtype_plist = cur_parts;
459 	parts->pinfo_name = cur_parts->pinfo_name;
460 	cur_disk->disk_parts = cur_parts;
461 	cur_ctype->ctype_dlist = cur_dtype;
462 
463 }
464 #endif /* DEADCODE */
465 
466 static int
467 get_solaris_part(int fd, struct ipart *ipart)
468 {
469 	int		i;
470 	struct ipart	ip;
471 	int		status;
472 	char		*mbr;
473 	char		*bootptr;
474 	struct dk_label	update_label;
475 	ushort_t	found = 0;
476 #ifdef i386
477 	uint32_t	relsec, numsec;
478 	int		pno, rval, ext_part_found = 0;
479 	ext_part_t	*epp;
480 #endif
481 
482 	(void) lseek(fd, 0, 0);
483 
484 	/*
485 	 * We may get mbr of different size, but the first 512 bytes
486 	 * are valid information.
487 	 */
488 	mbr = malloc(cur_blksz);
489 	if (mbr == NULL) {
490 		err_print("No memory available.\n");
491 		return (-1);
492 	}
493 	status = read(fd, mbr, cur_blksz);
494 
495 	if (status != cur_blksz) {
496 		err_print("Bad read of fdisk partition. Status = %x\n", status);
497 		err_print("Cannot read fdisk partition information.\n");
498 		free(mbr);
499 		return (-1);
500 	}
501 
502 	(void) memcpy(&boot_sec, mbr, sizeof (struct mboot));
503 	free(mbr);
504 
505 #ifdef i386
506 	(void) extpart_init(&epp);
507 #endif
508 	for (i = 0; i < FD_NUMPART; i++) {
509 		int	ipc;
510 
511 		ipc = i * sizeof (struct ipart);
512 
513 		/* Handling the alignment problem of struct ipart */
514 		bootptr = &boot_sec.parts[ipc];
515 		(void) fill_ipart(bootptr, &ip);
516 
517 #ifdef i386
518 		if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) {
519 			/* We support only one extended partition per disk */
520 			ext_part_found = 1;
521 			rval = fdisk_get_solaris_part(epp, &pno, &relsec,
522 			    &numsec);
523 			if (rval == FDISK_SUCCESS) {
524 				/*
525 				 * Found a solaris partition inside the
526 				 * extended partition. Update the statistics.
527 				 */
528 				if (nhead != 0 && nsect != 0) {
529 					pcyl = numsec / (nhead * nsect);
530 					xstart = relsec / (nhead * nsect);
531 					ncyl = pcyl - acyl;
532 				}
533 				solaris_offset = relsec;
534 				found = 2;
535 				ip.bootid = 0;
536 				ip.beghead = ip.begsect = ip.begcyl = 0xff;
537 				ip.endhead = ip.endsect = ip.endcyl = 0xff;
538 				ip.systid = SUNIXOS2;
539 				ip.relsect = relsec;
540 				ip.numsect = numsec;
541 				ipart->bootid = ip.bootid;
542 				status = bcmp(&ip, ipart,
543 				    sizeof (struct ipart));
544 				bcopy(&ip, ipart, sizeof (struct ipart));
545 			}
546 			continue;
547 		}
548 #endif
549 
550 		/*
551 		 * we are interested in Solaris and EFI partition types
552 		 */
553 #ifdef i386
554 		if ((ip.systid == SUNIXOS &&
555 		    (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) ||
556 		    ip.systid == SUNIXOS2 ||
557 		    ip.systid == EFI_PMBR) {
558 #else
559 		if (ip.systid == SUNIXOS ||
560 		    ip.systid == SUNIXOS2 ||
561 		    ip.systid == EFI_PMBR) {
562 #endif
563 			/*
564 			 * if the disk has an EFI label, nhead and nsect may
565 			 * be zero.  This test protects us from FPE's, and
566 			 * format still seems to work fine
567 			 */
568 			if (nhead != 0 && nsect != 0) {
569 				pcyl = lel(ip.numsect) / (nhead * nsect);
570 				xstart = lel(ip.relsect) / (nhead * nsect);
571 				ncyl = pcyl - acyl;
572 			}
573 #ifdef DEBUG
574 			else {
575 				err_print("Critical geometry values are zero:\n"
576 				    "\tnhead = %d; nsect = %d\n", nhead, nsect);
577 			}
578 #endif /* DEBUG */
579 
580 			solaris_offset = (uint_t)lel(ip.relsect);
581 			found = 1;
582 			break;
583 		}
584 	}
585 
586 #ifdef i386
587 	libfdisk_fini(&epp);
588 #endif
589 
590 	if (!found) {
591 		err_print("Solaris fdisk partition not found\n");
592 		return (-1);
593 	} else if (found == 1) {
594 		/*
595 		 * Found a primary solaris partition.
596 		 * compare the previous and current Solaris partition
597 		 * but don't use bootid in determination of Solaris partition
598 		 * changes
599 		 */
600 		ipart->bootid = ip.bootid;
601 		status = bcmp(&ip, ipart, sizeof (struct ipart));
602 
603 		bcopy(&ip, ipart, sizeof (struct ipart));
604 	}
605 
606 	/* if the disk partitioning has changed - get the VTOC */
607 	if (status) {
608 		struct extvtoc exvtoc;
609 		struct vtoc vtoc;
610 
611 		status = ioctl(fd, DKIOCGEXTVTOC, &exvtoc);
612 		if (status == -1) {
613 			i = errno;
614 			/* Try the old ioctl DKIOCGVTOC */
615 			status = ioctl(fd, DKIOCGVTOC, &vtoc);
616 			if (status == -1) {
617 				err_print("Bad ioctl DKIOCGEXTVTOC.\n");
618 				err_print("errno=%d %s\n", i, strerror(i));
619 				err_print("Cannot read vtoc information.\n");
620 				return (-1);
621 			}
622 		}
623 
624 		status = read_label(fd, &update_label);
625 		if (status == -1) {
626 			err_print("Cannot read label information.\n");
627 			return (-1);
628 		}
629 
630 		/* copy vtoc information */
631 		cur_parts->vtoc = update_label.dkl_vtoc;
632 
633 #if defined(_SUNOS_VTOC_16)
634 		/*
635 		 * this is to update the slice table on x86
636 		 * we don't care about VTOC8 here
637 		 */
638 		for (i = 0; i < NDKMAP; i ++) {
639 			cur_parts->pinfo_map[i].dkl_cylno =
640 			    update_label.dkl_vtoc.v_part[i].p_start /
641 			    ((int)(update_label.dkl_nhead *
642 			    update_label.dkl_nsect));
643 			cur_parts->pinfo_map[i].dkl_nblk =
644 			    update_label.dkl_vtoc.v_part[i].p_size;
645 		}
646 #endif /* defined(_SUNOS_VTOC_16) */
647 
648 		cur_dtype->dtype_ncyl = update_label.dkl_ncyl;
649 		cur_dtype->dtype_pcyl = update_label.dkl_pcyl;
650 		cur_dtype->dtype_acyl = update_label.dkl_acyl;
651 		cur_dtype->dtype_nhead = update_label.dkl_nhead;
652 		cur_dtype->dtype_nsect = update_label.dkl_nsect;
653 		ncyl = cur_dtype->dtype_ncyl;
654 		acyl = cur_dtype->dtype_acyl;
655 		pcyl = cur_dtype->dtype_pcyl;
656 		nsect = cur_dtype->dtype_nsect;
657 		nhead = cur_dtype->dtype_nhead;
658 	}
659 	return (0);
660 }
661 
662 
663 int
664 copy_solaris_part(struct ipart *ipart)
665 {
666 
667 	int		status, i, fd;
668 	struct mboot	mboot;
669 	char		*mbr;
670 	struct ipart	ip;
671 	char		buf[MAXPATHLEN];
672 	char		*bootptr;
673 	struct stat	statbuf;
674 #ifdef i386
675 	uint32_t	relsec, numsec;
676 	int		pno, rval, ext_part_found = 0;
677 	ext_part_t	*epp;
678 #endif
679 
680 	(void) get_pname(&buf[0]);
681 	if (stat(buf, &statbuf) == -1 ||
682 	    !S_ISCHR(statbuf.st_mode) ||
683 	    ((cur_label == L_TYPE_EFI) &&
684 	    (cur_disk->disk_flags & DSK_LABEL_DIRTY))) {
685 		/*
686 		 * Make sure to reset solaris_offset to zero if it is
687 		 *	previously set by a selected disk that
688 		 *	supports the fdisk table.
689 		 */
690 		solaris_offset = 0;
691 		/*
692 		 * Return if this disk does not support fdisk table or
693 		 * if it uses an EFI label but has not yet been labelled.
694 		 * If the EFI label has not been written then the open
695 		 * on the partition will fail.
696 		 */
697 		return (0);
698 	}
699 
700 	if ((fd = open(buf, O_RDONLY)) < 0) {
701 		/*
702 		 * Labeled device support in lofi provides p0 partition on
703 		 * both x86 and sparc. However, sparc does not implement fdisk
704 		 * partitioning. This workaround will allow format
705 		 * to ignore fdisk errors in case of lofi devices.
706 		 */
707 		if (strcmp(cur_disk->disk_dkinfo.dki_cname, "lofi") == 0) {
708 			solaris_offset = 0;
709 			return (0);
710 		}
711 		err_print("Error: can't open disk '%s'.\n", buf);
712 		return (-1);
713 	}
714 
715 	/*
716 	 * We may get mbr of different size, but the first 512 bytes
717 	 * are valid information.
718 	 */
719 	mbr = malloc(cur_blksz);
720 	if (mbr == NULL) {
721 		err_print("No memory available.\n");
722 		return (-1);
723 	}
724 	status = read(fd, mbr, cur_blksz);
725 
726 	if (status != cur_blksz) {
727 		err_print("Bad read of fdisk partition.\n");
728 		(void) close(fd);
729 		free(mbr);
730 		return (-1);
731 	}
732 
733 	(void) memcpy(&mboot, mbr, sizeof (struct mboot));
734 
735 #ifdef i386
736 	(void) extpart_init(&epp);
737 #endif
738 	for (i = 0; i < FD_NUMPART; i++) {
739 		int	ipc;
740 
741 		ipc = i * sizeof (struct ipart);
742 
743 		/* Handling the alignment problem of struct ipart */
744 		bootptr = &mboot.parts[ipc];
745 		(void) fill_ipart(bootptr, &ip);
746 
747 #ifdef i386
748 		if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) {
749 			/* We support only one extended partition per disk */
750 			ext_part_found = 1;
751 			rval = fdisk_get_solaris_part(epp, &pno, &relsec,
752 			    &numsec);
753 			if (rval == FDISK_SUCCESS) {
754 				/*
755 				 * Found a solaris partition inside the
756 				 * extended partition. Update the statistics.
757 				 */
758 				if (nhead != 0 && nsect != 0) {
759 					pcyl = numsec / (nhead * nsect);
760 					ncyl = pcyl - acyl;
761 				}
762 				solaris_offset = relsec;
763 				ip.bootid = 0;
764 				ip.beghead = ip.begsect = ip.begcyl = 0xff;
765 				ip.endhead = ip.endsect = ip.endcyl = 0xff;
766 				ip.systid = SUNIXOS2;
767 				ip.relsect = relsec;
768 				ip.numsect = numsec;
769 				bcopy(&ip, ipart, sizeof (struct ipart));
770 			}
771 			continue;
772 		}
773 #endif
774 
775 
776 #ifdef i386
777 		if ((ip.systid == SUNIXOS &&
778 		    (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) ||
779 		    ip.systid == SUNIXOS2 ||
780 		    ip.systid == EFI_PMBR) {
781 #else
782 		if (ip.systid == SUNIXOS ||
783 		    ip.systid == SUNIXOS2 ||
784 		    ip.systid == EFI_PMBR) {
785 #endif
786 			solaris_offset = lel(ip.relsect);
787 			bcopy(&ip, ipart, sizeof (struct ipart));
788 
789 			/*
790 			 * if the disk has an EFI label, we typically won't
791 			 * have values for nhead and nsect.  format seems to
792 			 * work without them, and we need to protect ourselves
793 			 * from FPE's
794 			 */
795 			if (nhead != 0 && nsect != 0) {
796 				pcyl = lel(ip.numsect) / (nhead * nsect);
797 				ncyl = pcyl - acyl;
798 			}
799 #ifdef DEBUG
800 			else {
801 				err_print("Critical geometry values are zero:\n"
802 				    "\tnhead = %d; nsect = %d\n", nhead, nsect);
803 			}
804 #endif /* DEBUG */
805 
806 			break;
807 		}
808 	}
809 #ifdef i386
810 	libfdisk_fini(&epp);
811 #endif
812 
813 	(void) close(fd);
814 	free(mbr);
815 	return (0);
816 }
817 
818 #if defined(_FIRMWARE_NEEDS_FDISK)
819 int
820 auto_solaris_part(struct dk_label *label)
821 {
822 
823 	int		status, i, fd;
824 	struct mboot	mboot;
825 	char		*mbr;
826 	struct ipart	ip;
827 	char		*bootptr;
828 	char		pbuf[MAXPATHLEN];
829 #ifdef i386
830 	uint32_t	relsec, numsec;
831 	int		pno, rval, ext_part_found = 0;
832 	ext_part_t	*epp;
833 #endif
834 
835 	(void) get_pname(&pbuf[0]);
836 	if ((fd = open_disk(pbuf, O_RDONLY)) < 0) {
837 		err_print("Error: can't open selected disk '%s'.\n", pbuf);
838 		return (-1);
839 	}
840 
841 	/*
842 	 * We may get mbr of different size, but the first 512 bytes
843 	 * are valid information.
844 	 */
845 	mbr = malloc(cur_blksz);
846 	if (mbr == NULL) {
847 		err_print("No memory available.\n");
848 		return (-1);
849 	}
850 	status = read(fd, mbr, cur_blksz);
851 
852 	if (status != cur_blksz) {
853 		err_print("Bad read of fdisk partition.\n");
854 		free(mbr);
855 		return (-1);
856 	}
857 
858 	(void) memcpy(&mboot, mbr, sizeof (struct mboot));
859 
860 #ifdef i386
861 	(void) extpart_init(&epp);
862 #endif
863 	for (i = 0; i < FD_NUMPART; i++) {
864 		int	ipc;
865 
866 		ipc = i * sizeof (struct ipart);
867 
868 		/* Handling the alignment problem of struct ipart */
869 		bootptr = &mboot.parts[ipc];
870 		(void) fill_ipart(bootptr, &ip);
871 
872 #ifdef i386
873 		if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) {
874 			/* We support only one extended partition per disk */
875 			ext_part_found = 1;
876 			rval = fdisk_get_solaris_part(epp, &pno, &relsec,
877 			    &numsec);
878 			if (rval == FDISK_SUCCESS) {
879 				/*
880 				 * Found a solaris partition inside the
881 				 * extended partition. Update the statistics.
882 				 */
883 				if ((label->dkl_nhead != 0) &&
884 				    (label->dkl_nsect != 0)) {
885 					label->dkl_pcyl =
886 					    numsec / (label->dkl_nhead *
887 					    label->dkl_nsect);
888 					label->dkl_ncyl = label->dkl_pcyl -
889 					    label->dkl_acyl;
890 				}
891 				solaris_offset = relsec;
892 			}
893 			continue;
894 		}
895 #endif
896 
897 		/*
898 		 * if the disk has an EFI label, the nhead and nsect fields
899 		 * the label may be zero.  This protects us from FPE's, and
900 		 * format still seems to work happily
901 		 */
902 
903 
904 #ifdef i386
905 		if ((ip.systid == SUNIXOS &&
906 		    (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) ||
907 		    ip.systid == SUNIXOS2 ||
908 		    ip.systid == EFI_PMBR) {
909 #else
910 		if (ip.systid == SUNIXOS ||
911 		    ip.systid == SUNIXOS2 ||
912 		    ip.systid == EFI_PMBR) {
913 #endif
914 			if ((label->dkl_nhead != 0) &&
915 			    (label->dkl_nsect != 0)) {
916 				label->dkl_pcyl = lel(ip.numsect) /
917 				    (label->dkl_nhead * label->dkl_nsect);
918 				label->dkl_ncyl = label->dkl_pcyl -
919 				    label->dkl_acyl;
920 			}
921 #ifdef DEBUG
922 			else {
923 				err_print("Critical label fields aren't "
924 				    "non-zero:\n"
925 				    "\tlabel->dkl_nhead = %d; "
926 				    "label->dkl_nsect = "
927 				    "%d\n", label->dkl_nhead,
928 				    label->dkl_nsect);
929 			}
930 #endif /* DEBUG */
931 
932 		solaris_offset = lel(ip.relsect);
933 		break;
934 		}
935 	}
936 
937 #ifdef i386
938 	libfdisk_fini(&epp);
939 #endif
940 	(void) close(fd);
941 	free(mbr);
942 	return (0);
943 }
944 #endif	/* defined(_FIRMWARE_NEEDS_FDISK) */
945 
946 
947 int
948 good_fdisk(void)
949 {
950 	char		buf[MAXPATHLEN];
951 	struct stat	statbuf;
952 
953 	(void) get_pname(&buf[0]);
954 	if (stat(buf, &statbuf) == -1 ||
955 	    !S_ISCHR(statbuf.st_mode) ||
956 	    cur_label == L_TYPE_EFI) {
957 		/*
958 		 * Return if this disk does not support fdisk table or
959 		 * if the disk is labeled with EFI.
960 		 */
961 		return (1);
962 	}
963 
964 	if (lel(cur_disk->fdisk_part.numsect) > 0) {
965 		return (1);
966 	} else {
967 		/*
968 		 * Labeled device support in lofi provides p0 partition on
969 		 * both x86 and sparc. However, sparc does not implement fdisk
970 		 * partitioning. This workaround will allow format
971 		 * to ignore fdisk errors in case of lofi devices.
972 		 */
973 		if (strcmp(cur_disk->disk_dkinfo.dki_cname, "lofi") == 0) {
974 			return (1);
975 		}
976 		err_print("WARNING - ");
977 		err_print("This disk may be in use by an application "
978 		    "that has\n\t  modified the fdisk table. Ensure "
979 		    "that this disk is\n\t  not currently in use "
980 		    "before proceeding to use fdisk.\n");
981 		return (0);
982 	}
983 }
984 
985 #ifdef i386
986 int
987 extpart_init(ext_part_t **epp)
988 {
989 	int		rval, lf_op_flag = 0;
990 	char		p0_path[MAXPATHLEN];
991 
992 	get_pname(&p0_path[0]);
993 	lf_op_flag |= FDISK_READ_DISK;
994 	if ((rval = libfdisk_init(epp, p0_path, NULL, lf_op_flag)) !=
995 	    FDISK_SUCCESS) {
996 		switch (rval) {
997 			/*
998 			 * FDISK_EBADLOGDRIVE, FDISK_ENOLOGDRIVE
999 			 * and FDISK_EBADMAGIC can be considered
1000 			 * as soft errors and hence we do not exit.
1001 			 */
1002 			case FDISK_EBADLOGDRIVE:
1003 				break;
1004 			case FDISK_ENOLOGDRIVE:
1005 				break;
1006 			case FDISK_EBADMAGIC:
1007 				break;
1008 			case FDISK_ENOVGEOM:
1009 				err_print("Could not get virtual geometry for"
1010 				    " this device\n");
1011 				fullabort();
1012 				break;
1013 			case FDISK_ENOPGEOM:
1014 				err_print("Could not get physical geometry for"
1015 				    " this device\n");
1016 				fullabort();
1017 				break;
1018 			case FDISK_ENOLGEOM:
1019 				err_print("Could not get label geometry for "
1020 				    " this device\n");
1021 				fullabort();
1022 				break;
1023 			default:
1024 				err_print("Failed to initialise libfdisk.\n");
1025 				fullabort();
1026 				break;
1027 		}
1028 	}
1029 	return (0);
1030 }
1031 #endif
1032