Home | History | Annotate | Download | only in installgrub
      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 2010 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <libgen.h>
     29 #include <malloc.h>
     30 #include <string.h>
     31 #include <sys/types.h>
     32 #include <sys/stat.h>
     33 #include <fcntl.h>
     34 #include <unistd.h>
     35 #include <strings.h>
     36 #include <sys/mount.h>
     37 #include <sys/mnttab.h>
     38 #include <sys/dktp/fdisk.h>
     39 #include <sys/dkio.h>
     40 #include <sys/vtoc.h>
     41 
     42 #include <libintl.h>
     43 #include <locale.h>
     44 #include "message.h"
     45 #include <errno.h>
     46 #include <libfdisk.h>
     47 #include <md5.h>
     48 
     49 #ifndef	TEXT_DOMAIN
     50 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
     51 #endif
     52 
     53 #define	SECTOR_SIZE	0x200
     54 #define	HASH_SIZE	0x10
     55 #define	VERSION_SIZE	0x50
     56 #define	STAGE2_MEMADDR	0x8000	/* loading addr of stage2 */
     57 
     58 #define	STAGE1_BPB_OFFSET	0x3
     59 #define	STAGE1_BPB_SIZE		0x3B
     60 #define	STAGE1_BOOT_DRIVE	0x40
     61 #define	STAGE1_FORCE_LBA	0x41
     62 #define	STAGE1_STAGE2_ADDRESS	0x42
     63 #define	STAGE1_STAGE2_SECTOR	0x44
     64 #define	STAGE1_STAGE2_SEGMENT	0x48
     65 
     66 #define	STAGE2_BLOCKLIST	(SECTOR_SIZE - 0x8)
     67 #define	STAGE2_INSTALLPART	(SECTOR_SIZE + 0x8)
     68 #define	STAGE2_FORCE_LBA	(SECTOR_SIZE + 0x11)
     69 #define	STAGE2_VER_STRING	(SECTOR_SIZE + 0x12)
     70 #define	STAGE2_SIGN_OFFSET	(SECTOR_SIZE + 0x60)
     71 #define	STAGE2_PKG_VERSION	(SECTOR_SIZE + 0x70)
     72 #define	STAGE2_BLKOFF		50	/* offset from start of fdisk part */
     73 
     74 static char extended_sig[] = "\xCC\xCC\xCC\xCC\xAA\xAA\xAA\xAA\xBB\xBB\xBB\xBB"
     75 "\xBB\xBB\xBB\xBB";
     76 
     77 static int nowrite = 0;
     78 static int write_mboot = 0;
     79 static int force_mboot = 0;
     80 static int getinfo = 0;
     81 static int do_version = 0;
     82 static int is_floppy = 0;
     83 static int is_bootpar = 0;
     84 static int strip = 0;
     85 static int stage2_fd;
     86 static int partition, slice = 0xff;
     87 static char *device_p0;
     88 static uint32_t stage2_first_sector, stage2_second_sector;
     89 
     90 
     91 static char bpb_sect[SECTOR_SIZE];
     92 static char boot_sect[SECTOR_SIZE];
     93 static char stage1_buffer[SECTOR_SIZE];
     94 static char stage2_buffer[2 * SECTOR_SIZE];
     95 static char signature[HASH_SIZE];
     96 static char verstring[VERSION_SIZE];
     97 static unsigned int blocklist[SECTOR_SIZE / sizeof (unsigned int)];
     98 
     99 static int open_device(char *);
    100 static void read_bpb_sect(int);
    101 static void read_boot_sect(char *);
    102 static void write_boot_sect(char *);
    103 static void read_stage1_stage2(char *, char *);
    104 static void modify_and_write_stage1(int);
    105 static void modify_and_write_stage2(int);
    106 static unsigned int get_start_sector(int);
    107 static void copy_stage2(int, char *);
    108 static char *get_raw_partition(char *);
    109 static void usage(char *);
    110 static void print_info();
    111 static int read_stage2_info(int);
    112 static void check_extended_support();
    113 
    114 extern int read_stage2_blocklist(int, unsigned int *);
    115 
    116 int
    117 main(int argc, char *argv[])
    118 {
    119 	int dev_fd, opt, params = 3;
    120 	char *stage1, *stage2, *device;
    121 
    122 	(void) setlocale(LC_ALL, "");
    123 	(void) textdomain(TEXT_DOMAIN);
    124 
    125 	while ((opt = getopt(argc, argv, "fmneis:")) != EOF) {
    126 		switch (opt) {
    127 		case 'm':
    128 			write_mboot = 1;
    129 			break;
    130 		case 'n':
    131 			nowrite = 1;
    132 			break;
    133 		case 'f':
    134 			force_mboot = 1;
    135 			break;
    136 		case 'i':
    137 			getinfo = 1;
    138 			params = 1;
    139 			break;
    140 		case 'e':
    141 			strip = 1;
    142 			break;
    143 		case 's':
    144 			do_version = 1;
    145 			(void) snprintf(verstring, sizeof (verstring), "%s",
    146 			    optarg);
    147 			break;
    148 		default:
    149 			/* fall through to process non-optional args */
    150 			break;
    151 		}
    152 	}
    153 
    154 	/* check arguments */
    155 	if (argc != optind + params) {
    156 		usage(argv[0]);
    157 	}
    158 
    159 	if (nowrite) {
    160 		(void) fprintf(stdout, DRY_RUN);
    161 	}
    162 
    163 	if (params == 1) {
    164 		device = strdup(argv[optind]);
    165 		if (!device) {
    166 			usage(argv[0]);
    167 		}
    168 	} else if (params == 3) {
    169 		stage1 = strdup(argv[optind]);
    170 		stage2 = strdup(argv[optind + 1]);
    171 		device = strdup(argv[optind + 2]);
    172 
    173 		if (!stage1 || !stage2 || !device) {
    174 			usage(argv[0]);
    175 		}
    176 	}
    177 
    178 	/* open and check device type */
    179 	dev_fd = open_device(device);
    180 
    181 	if (getinfo) {
    182 		if (read_stage2_info(dev_fd) != 0) {
    183 			fprintf(stderr, "Unable to read extended information"
    184 			    " from %s\n", device);
    185 			exit(1);
    186 		}
    187 		print_info();
    188 		(void) free(device);
    189 		(void) close(dev_fd);
    190 		return (0);
    191 	}
    192 
    193 	/* read in stage1 and stage2 into buffer */
    194 	read_stage1_stage2(stage1, stage2);
    195 
    196 	/* check if stage2 supports extended versioning */
    197 	if (do_version)
    198 		check_extended_support(stage2);
    199 
    200 	/* In the pcfs case, write a fresh stage2 */
    201 	if (is_floppy || is_bootpar) {
    202 		copy_stage2(dev_fd, device);
    203 		read_bpb_sect(dev_fd);
    204 	}
    205 
    206 	/* read in boot sector */
    207 	if (!is_floppy)
    208 		read_boot_sect(device);
    209 
    210 	/* modify stage1 based on grub needs */
    211 	modify_and_write_stage1(dev_fd);
    212 
    213 	/* modify stage2 and write to media */
    214 	modify_and_write_stage2(dev_fd);
    215 
    216 	if (!is_floppy && write_mboot)
    217 		write_boot_sect(device);
    218 
    219 	(void) close(dev_fd);
    220 	free(device);
    221 	free(stage1);
    222 	free(stage2);
    223 
    224 	return (0);
    225 }
    226 
    227 static unsigned int
    228 get_start_sector(int fd)
    229 {
    230 	static unsigned int start_sect = 0;
    231 	uint32_t secnum = 0, numsec = 0;
    232 	int i, pno, rval, log_part = 0;
    233 	struct mboot *mboot;
    234 	struct ipart *part;
    235 	ext_part_t *epp;
    236 	struct part_info dkpi;
    237 	struct extpart_info edkpi;
    238 
    239 	if (start_sect)
    240 		return (start_sect);
    241 
    242 	mboot = (struct mboot *)boot_sect;
    243 	for (i = 0; i < FD_NUMPART; i++) {
    244 		part = (struct ipart *)mboot->parts + i;
    245 		if (is_bootpar) {
    246 			if (part->systid == 0xbe) {
    247 				start_sect = part->relsect;
    248 				partition = i;
    249 				goto found_part;
    250 			}
    251 		}
    252 	}
    253 
    254 	/*
    255 	 * We will not support x86 boot partition on extended partitions
    256 	 */
    257 	if (is_bootpar) {
    258 		(void) fprintf(stderr, NOBOOTPAR);
    259 		exit(-1);
    260 	}
    261 
    262 	/*
    263 	 * Not an x86 boot partition. Search for Solaris fdisk partition
    264 	 * Get the solaris partition information from the device
    265 	 * and compare the offset of S2 with offset of solaris partition
    266 	 * from fdisk partition table.
    267 	 */
    268 	if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
    269 		if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
    270 			(void) fprintf(stderr, PART_FAIL);
    271 			exit(-1);
    272 		} else {
    273 			edkpi.p_start = dkpi.p_start;
    274 		}
    275 	}
    276 
    277 	for (i = 0; i < FD_NUMPART; i++) {
    278 		part = (struct ipart *)mboot->parts + i;
    279 
    280 		if (part->relsect == 0) {
    281 			(void) fprintf(stderr, BAD_PART, i);
    282 			exit(-1);
    283 		}
    284 
    285 		if (edkpi.p_start >= part->relsect &&
    286 		    edkpi.p_start < (part->relsect + part->numsect)) {
    287 			/* Found the partition */
    288 			break;
    289 		}
    290 	}
    291 
    292 	if (i == FD_NUMPART) {
    293 		/* No solaris fdisk partitions (primary or logical) */
    294 		(void) fprintf(stderr, NOSOLPAR);
    295 		exit(-1);
    296 	}
    297 
    298 	/*
    299 	 * We have found a Solaris fdisk partition (primary or extended)
    300 	 * Handle the simple case first: Solaris in a primary partition
    301 	 */
    302 	if (!fdisk_is_dos_extended(part->systid)) {
    303 		start_sect = part->relsect;
    304 		partition = i;
    305 		goto found_part;
    306 	}
    307 
    308 	/*
    309 	 * Solaris in a logical partition. Find that partition in the
    310 	 * extended part.
    311 	 */
    312 	if ((rval = libfdisk_init(&epp, device_p0, NULL, FDISK_READ_DISK))
    313 	    != FDISK_SUCCESS) {
    314 		switch (rval) {
    315 			/*
    316 			 * The first 3 cases are not an error per-se, just that
    317 			 * there is no Solaris logical partition
    318 			 */
    319 			case FDISK_EBADLOGDRIVE:
    320 			case FDISK_ENOLOGDRIVE:
    321 			case FDISK_EBADMAGIC:
    322 				(void) fprintf(stderr, NOSOLPAR);
    323 				exit(-1);
    324 				/*NOTREACHED*/
    325 			case FDISK_ENOVGEOM:
    326 				(void) fprintf(stderr, NO_VIRT_GEOM);
    327 				exit(1);
    328 				break;
    329 			case FDISK_ENOPGEOM:
    330 				(void) fprintf(stderr, NO_PHYS_GEOM);
    331 				exit(1);
    332 				break;
    333 			case FDISK_ENOLGEOM:
    334 				(void) fprintf(stderr, NO_LABEL_GEOM);
    335 				exit(1);
    336 				break;
    337 			default:
    338 				(void) fprintf(stderr, LIBFDISK_INIT_FAIL);
    339 				exit(1);
    340 				break;
    341 		}
    342 	}
    343 
    344 	rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
    345 	libfdisk_fini(&epp);
    346 	if (rval != FDISK_SUCCESS) {
    347 		/* No solaris logical partition */
    348 		(void) fprintf(stderr, NOSOLPAR);
    349 		exit(-1);
    350 	}
    351 
    352 	start_sect = secnum;
    353 	partition = pno - 1;
    354 	log_part = 1;
    355 
    356 found_part:
    357 	/* get confirmation for -m */
    358 	if (write_mboot && !force_mboot) {
    359 		(void) fprintf(stdout, MBOOT_PROMPT);
    360 		if (getchar() != 'y') {
    361 			write_mboot = 0;
    362 			(void) fprintf(stdout, MBOOT_NOT_UPDATED);
    363 		}
    364 	}
    365 
    366 	/*
    367 	 * Currently if Solaris is in an extended partition we need to
    368 	 * write GRUB to the MBR. Check for this.
    369 	 */
    370 	if (log_part && !write_mboot) {
    371 		(void) fprintf(stderr, EXTSOLPAR);
    372 		exit(-1);
    373 	}
    374 
    375 	/*
    376 	 * warn, if Solaris in primary partition and GRUB not in MBR and
    377 	 * partition is not active
    378 	 */
    379 	if (!log_part && part->bootid != 128 && !write_mboot) {
    380 		(void) fprintf(stdout, SOLPAR_INACTIVE, partition + 1);
    381 	}
    382 
    383 	return (start_sect);
    384 }
    385 
    386 static void
    387 usage(char *progname)
    388 {
    389 	(void) fprintf(stderr, USAGE, basename(progname));
    390 	exit(-1);
    391 }
    392 
    393 static int
    394 open_device(char *device)
    395 {
    396 	int dev_fd;
    397 	struct stat stat;
    398 	char *raw_part;
    399 
    400 	is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) &&
    401 	    strncmp(device, "/dev/dsk", strlen("/dev/dsk"));
    402 
    403 	/* handle boot partition specification */
    404 	if (!is_floppy && strstr(device, "p0:boot")) {
    405 		is_bootpar = 1;
    406 	}
    407 
    408 	raw_part = get_raw_partition(device);
    409 
    410 	if (nowrite)
    411 		dev_fd = open(raw_part, O_RDONLY);
    412 	else
    413 		dev_fd = open(raw_part, O_RDWR);
    414 
    415 	if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) {
    416 		(void) fprintf(stderr, OPEN_FAIL, raw_part);
    417 		exit(-1);
    418 	}
    419 	if (S_ISCHR(stat.st_mode) == 0) {
    420 		(void) fprintf(stderr, NOT_RAW_DEVICE, raw_part);
    421 		exit(-1);
    422 	}
    423 
    424 	return (dev_fd);
    425 }
    426 
    427 static void
    428 read_stage1_stage2(char *stage1, char *stage2)
    429 {
    430 	int fd;
    431 
    432 	/* read the stage1 file from filesystem */
    433 	fd = open(stage1, O_RDONLY);
    434 	if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) {
    435 		(void) fprintf(stderr, READ_FAIL_STAGE1, stage1);
    436 		exit(-1);
    437 	}
    438 	(void) close(fd);
    439 
    440 	/* read first two blocks of stage 2 from filesystem */
    441 	stage2_fd = open(stage2, O_RDONLY);
    442 	if (stage2_fd == -1 ||
    443 	    read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE)
    444 	    != 2 * SECTOR_SIZE) {
    445 		(void) fprintf(stderr, READ_FAIL_STAGE2, stage2);
    446 		exit(-1);
    447 	}
    448 	/* leave the stage2 file open for later */
    449 }
    450 
    451 static void
    452 read_bpb_sect(int dev_fd)
    453 {
    454 	if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) {
    455 		(void) fprintf(stderr, READ_FAIL_BPB);
    456 		exit(-1);
    457 	}
    458 }
    459 
    460 static void
    461 read_boot_sect(char *device)
    462 {
    463 	static int read_mbr = 0;
    464 	int i, fd;
    465 	char save[2];
    466 
    467 	if (read_mbr)
    468 		return;
    469 	read_mbr = 1;
    470 
    471 	/* get the whole disk (p0) */
    472 	i = strlen(device);
    473 	save[0] = device[i - 2];
    474 	save[1] = device[i - 1];
    475 	device[i - 2] = 'p';
    476 	device[i - 1] = '0';
    477 
    478 	device_p0 = strdup(device);
    479 	fd = open(device, O_RDONLY);
    480 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
    481 		(void) fprintf(stderr, READ_FAIL_MBR, device);
    482 		if (fd == -1)
    483 			perror("open");
    484 		else
    485 			perror("read");
    486 		exit(-1);
    487 	}
    488 	(void) close(fd);
    489 	device[i - 2] = save[0];
    490 	device[i - 1] = save[1];
    491 }
    492 
    493 static void
    494 write_boot_sect(char *device)
    495 {
    496 	int fd, len;
    497 	char *raw, *end;
    498 	struct stat stat;
    499 
    500 	/* make a copy and chop off ":boot" */
    501 	raw = strdup(device);
    502 	end = strstr(raw, "p0:boot");
    503 	if (end)
    504 		end[2] = 0;
    505 
    506 	/* open p0 (whole disk) */
    507 	len = strlen(raw);
    508 	raw[len - 2] = 'p';
    509 	raw[len - 1] = '0';
    510 	fd = open(raw, O_WRONLY);
    511 	if (fd == -1 || fstat(fd, &stat) != 0) {
    512 		(void) fprintf(stderr, OPEN_FAIL, raw);
    513 		exit(-1);
    514 	}
    515 	if (!nowrite &&
    516 	    pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
    517 		(void) fprintf(stderr, WRITE_FAIL_BOOTSEC);
    518 		exit(-1);
    519 	}
    520 	(void) fprintf(stdout, WRITE_MBOOT);
    521 	(void) close(fd);
    522 }
    523 
    524 static void
    525 modify_and_write_stage1(int dev_fd)
    526 {
    527 	if (is_floppy) {
    528 		stage2_first_sector = blocklist[0];
    529 		/* copy bios parameter block (for fat fs) */
    530 		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
    531 		    stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
    532 	} else if (is_bootpar) {
    533 		stage2_first_sector = get_start_sector(dev_fd) + blocklist[0];
    534 		/* copy bios parameter block (for fat fs) and MBR */
    535 		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
    536 		    stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
    537 		bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
    538 		*((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
    539 	} else {
    540 		stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF;
    541 		/* copy MBR to stage1 in case of overwriting MBR sector */
    542 		bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
    543 		*((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
    544 	}
    545 
    546 	/* modify default stage1 file generated by GRUB */
    547 	*((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR))
    548 	    = stage2_first_sector;
    549 	*((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS))
    550 	    = STAGE2_MEMADDR;
    551 	*((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT))
    552 	    = STAGE2_MEMADDR >> 4;
    553 
    554 	/*
    555 	 * XXX the default grub distribution also:
    556 	 * - Copy the possible MBR/extended part table
    557 	 * - Set the boot drive of stage1
    558 	 */
    559 
    560 	/* write stage1/pboot to 1st sector */
    561 	if (!nowrite &&
    562 	    pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
    563 		(void) fprintf(stderr, WRITE_FAIL_PBOOT);
    564 		exit(-1);
    565 	}
    566 
    567 	if (is_floppy) {
    568 		(void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY);
    569 	} else {
    570 		(void) fprintf(stdout, WRITE_PBOOT,
    571 		    partition, get_start_sector(dev_fd));
    572 	}
    573 }
    574 
    575 static void check_extended_support(char *stage2)
    576 {
    577 	char	*cmp = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
    578 
    579 	if ((*cmp++ != '\xEE') && memcmp(cmp, extended_sig, HASH_SIZE) != 0) {
    580 		fprintf(stderr, "%s does not support extended versioning\n",
    581 		    stage2);
    582 		do_version = 0;
    583 	}
    584 }
    585 
    586 
    587 static void print_info()
    588 {
    589 	int	i;
    590 
    591 	if (strip) {
    592 		fprintf(stdout, "%s\n", verstring);
    593 	} else {
    594 		fprintf(stdout, "Grub extended version information : %s\n",
    595 		    verstring);
    596 		fprintf(stdout, "Grub stage2 (MD5) signature : ");
    597 	}
    598 
    599 	for (i = 0; i < HASH_SIZE; i++)
    600 		fprintf(stdout, "%02x", (unsigned char)signature[i]);
    601 
    602 	fprintf(stdout, "\n");
    603 }
    604 
    605 static int
    606 read_stage2_info(int dev_fd)
    607 {
    608 	int 	ret;
    609 	int	first_offset, second_offset;
    610 	char	*sign;
    611 
    612 	if (is_floppy || is_bootpar) {
    613 
    614 		ret = pread(dev_fd, stage1_buffer, SECTOR_SIZE, 0);
    615 		if (ret != SECTOR_SIZE) {
    616 			perror("Error reading stage1 sector");
    617 			return (1);
    618 		}
    619 
    620 		first_offset = *((ulong_t *)(stage1_buffer +
    621 		    STAGE1_STAGE2_SECTOR));
    622 
    623 		/* Start reading in the first sector of stage 2 */
    624 
    625 		ret = pread(dev_fd, stage2_buffer, SECTOR_SIZE, first_offset *
    626 		    SECTOR_SIZE);
    627 		if (ret != SECTOR_SIZE) {
    628 			perror("Error reading stage2 first sector");
    629 			return (1);
    630 		}
    631 
    632 		/* From the block list section grab stage2 second sector */
    633 
    634 		second_offset = *((ulong_t *)(stage2_buffer +
    635 		    STAGE2_BLOCKLIST));
    636 
    637 		ret = pread(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
    638 		    second_offset * SECTOR_SIZE);
    639 		if (ret != SECTOR_SIZE) {
    640 			perror("Error reading stage2 second sector");
    641 			return (1);
    642 		}
    643 	} else {
    644 		ret = pread(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
    645 		    STAGE2_BLKOFF * SECTOR_SIZE);
    646 		if (ret != 2 * SECTOR_SIZE) {
    647 			perror("Error reading stage2 sectors");
    648 			return (1);
    649 		}
    650 	}
    651 
    652 	sign = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
    653 	if (*sign++ != '\xEE')
    654 		return (1);
    655 	(void) memcpy(signature, sign, HASH_SIZE);
    656 	sign = stage2_buffer + STAGE2_PKG_VERSION;
    657 	(void) strncpy(verstring, sign, VERSION_SIZE);
    658 	return (0);
    659 }
    660 
    661 
    662 static int
    663 compute_and_write_md5hash(char *dest)
    664 {
    665 	struct stat	sb;
    666 	char		*buffer;
    667 
    668 	if (fstat(stage2_fd, &sb) == -1)
    669 		return (-1);
    670 
    671 	buffer = malloc(sb.st_size);
    672 	if (buffer == NULL)
    673 		return (-1);
    674 
    675 	if (lseek(stage2_fd, 0, SEEK_SET) == -1)
    676 		return (-1);
    677 	if (read(stage2_fd, buffer, sb.st_size) < 0)
    678 		return (-1);
    679 
    680 	md5_calc(dest, buffer, sb.st_size);
    681 	free(buffer);
    682 	return (0);
    683 }
    684 
    685 
    686 #define	START_BLOCK(pos)	(*(ulong_t *)(pos))
    687 #define	NUM_BLOCK(pos)		(*(ushort_t *)((pos) + 4))
    688 #define	START_SEG(pos)		(*(ushort_t *)((pos) + 6))
    689 
    690 static void
    691 modify_and_write_stage2(int dev_fd)
    692 {
    693 	int 	nrecord;
    694 	off_t 	offset;
    695 	char	*dest;
    696 
    697 	if (do_version) {
    698 		dest = stage2_buffer + STAGE2_SIGN_OFFSET;
    699 		if (compute_and_write_md5hash(dest) < 0)
    700 			perror("MD5 operation");
    701 		dest = stage2_buffer + STAGE2_PKG_VERSION;
    702 		(void) strncpy(dest, verstring, VERSION_SIZE);
    703 	}
    704 
    705 	if (is_floppy || is_bootpar) {
    706 		int i = 0;
    707 		uint32_t partition_offset;
    708 		uint32_t install_addr = 0x8200;
    709 		uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST;
    710 
    711 		stage2_first_sector = blocklist[0];
    712 
    713 		/* figure out the second sector */
    714 		if (blocklist[1] > 1) {
    715 			blocklist[0]++;
    716 			blocklist[1]--;
    717 		} else {
    718 			i += 2;
    719 		}
    720 		stage2_second_sector = blocklist[i];
    721 
    722 		if (is_floppy)
    723 			partition_offset = 0;
    724 		else	/* solaris boot partition */
    725 			partition_offset = get_start_sector(dev_fd);
    726 
    727 		/* install the blocklist at the end of stage2_buffer */
    728 		while (blocklist[i]) {
    729 			if (START_BLOCK(pos - 8) != 0 &&
    730 			    START_BLOCK(pos - 8) != blocklist[i + 2]) {
    731 				(void) fprintf(stderr, PCFS_FRAGMENTED);
    732 				exit(-1);
    733 			}
    734 			START_BLOCK(pos) = blocklist[i] + partition_offset;
    735 			START_SEG(pos) = (ushort_t)(install_addr >> 4);
    736 			NUM_BLOCK(pos) = blocklist[i + 1];
    737 			install_addr += blocklist[i + 1] * SECTOR_SIZE;
    738 			pos -= 8;
    739 			i += 2;
    740 		}
    741 
    742 	} else {
    743 		/*
    744 		 * In a solaris partition, stage2 is written to contiguous
    745 		 * blocks. So we update the starting block only.
    746 		 */
    747 		*((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) =
    748 		    stage2_first_sector + 1;
    749 	}
    750 
    751 	if (is_floppy) {
    752 		/* modify the config file to add (fd0) */
    753 		char *config_file = stage2_buffer + STAGE2_VER_STRING;
    754 		while (*config_file++)
    755 			;
    756 		strcpy(config_file, "(fd0)/boot/grub/menu.lst");
    757 	} else {
    758 		/* force lba and set disk partition */
    759 		*((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1;
    760 		*((long *)(stage2_buffer + STAGE2_INSTALLPART))
    761 		    = (partition << 16) | (slice << 8) | 0xff;
    762 	}
    763 
    764 	/* modification done, now do the writing */
    765 	if (is_floppy || is_bootpar) {
    766 		/* we rewrite block 0 and 1 and that's it */
    767 		if (!nowrite &&
    768 		    (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
    769 		    stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE ||
    770 		    pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
    771 		    stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) {
    772 			(void) fprintf(stderr, WRITE_FAIL_STAGE2);
    773 			exit(-1);
    774 		}
    775 		(void) fprintf(stdout, WRITE_STAGE2_PCFS);
    776 		return;
    777 	}
    778 
    779 	/* for disk, write stage2 starting at STAGE2_BLKOFF sector */
    780 	offset = STAGE2_BLKOFF;
    781 
    782 	/* write the modified first two sectors */
    783 	if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
    784 	    offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) {
    785 		(void) fprintf(stderr, WRITE_FAIL_STAGE2);
    786 		exit(-1);
    787 	}
    788 
    789 	/* write the remaining sectors */
    790 	nrecord = 2;
    791 	offset += 2;
    792 	for (;;) {
    793 		int nread, nwrite;
    794 		nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE,
    795 		    nrecord * SECTOR_SIZE);
    796 		if (nread > 0 && !nowrite)
    797 			nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
    798 			    offset * SECTOR_SIZE);
    799 		else
    800 			nwrite = SECTOR_SIZE;
    801 		if (nread < 0 || nwrite != SECTOR_SIZE) {
    802 			(void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
    803 			    nread, nwrite);
    804 			break;
    805 		}
    806 		if (nread > 0) {
    807 			nrecord ++;
    808 			offset ++;
    809 		}
    810 		if (nread < SECTOR_SIZE)
    811 			break;	/* end of file */
    812 	}
    813 	(void) fprintf(stdout, WRITE_STAGE2_DISK,
    814 	    partition, nrecord, STAGE2_BLKOFF, stage2_first_sector);
    815 }
    816 
    817 static char *
    818 get_raw_partition(char *device)
    819 {
    820 	int len;
    821 	struct mboot *mboot;
    822 	static char *raw = NULL;
    823 
    824 	if (raw)
    825 		return (raw);
    826 	raw = strdup(device);
    827 
    828 	if (is_floppy)
    829 		return (raw);
    830 
    831 	if (is_bootpar) {
    832 		int i;
    833 		char *end = strstr(raw, "p0:boot");
    834 
    835 		end[2] = 0;		/* chop off :boot */
    836 		read_boot_sect(raw);
    837 		mboot = (struct mboot *)boot_sect;
    838 		for (i = 0; i < FD_NUMPART; i++) {
    839 			struct ipart *part = (struct ipart *)mboot->parts + i;
    840 			if (part->systid == 0xbe)	/* solaris boot part */
    841 				break;
    842 		}
    843 
    844 		if (i == FD_NUMPART) {
    845 			(void) fprintf(stderr, BOOTPAR_NOTFOUND, device);
    846 			exit(-1);
    847 		}
    848 		end[1] = '1' + i;	/* set partition name */
    849 		return (raw);
    850 	}
    851 
    852 	/* For disk, remember slice and return whole fdisk partition  */
    853 	len = strlen(raw);
    854 	if (raw[len - 2] != 's' || raw[len - 1] == '2') {
    855 		(void) fprintf(stderr, NOT_ROOT_SLICE);
    856 		exit(-1);
    857 	}
    858 	slice = atoi(&raw[len - 1]);
    859 
    860 	raw[len - 2] = 's';
    861 	raw[len - 1] = '2';
    862 	return (raw);
    863 }
    864 
    865 #define	TMP_MNTPT	"/tmp/installgrub_pcfs"
    866 static void
    867 copy_stage2(int dev_fd, char *device)
    868 {
    869 	FILE *mntfp;
    870 	int i, pcfs_fp;
    871 	char buf[SECTOR_SIZE];
    872 	char *cp;
    873 	struct mnttab mp = {0}, mpref = {0};
    874 
    875 	/* convert raw to block device name by removing the first 'r' */
    876 	(void) strncpy(buf, device, sizeof (buf));
    877 	buf[sizeof (buf) - 1] = 0;
    878 	cp = strchr(buf, 'r');
    879 	if (cp == NULL) {
    880 		(void) fprintf(stderr, CONVERT_FAIL, device);
    881 		exit(-1);
    882 	}
    883 	do {
    884 		*cp = *(cp + 1);
    885 	} while (*(++cp));
    886 
    887 	/* get the mount point, if any */
    888 	mntfp = fopen("/etc/mnttab", "r");
    889 	if (mntfp == NULL) {
    890 		(void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab");
    891 		exit(-1);
    892 	}
    893 
    894 	mpref.mnt_special = buf;
    895 	if (getmntany(mntfp, &mp, &mpref) != 0) {
    896 		char cmd[128];
    897 
    898 		/* not mounted, try remount */
    899 		(void) mkdir(TMP_MNTPT, S_IRWXU);
    900 		(void) snprintf(cmd, sizeof (cmd), "mount -F pcfs %s %s",
    901 		    buf, TMP_MNTPT);
    902 		(void) system(cmd);
    903 		rewind(mntfp);
    904 		bzero(&mp, sizeof (mp));
    905 		if (getmntany(mntfp, &mp, &mpref) != 0) {
    906 			(void) fprintf(stderr, MOUNT_FAIL, buf);
    907 			exit(-1);
    908 		}
    909 	}
    910 
    911 	(void) snprintf(buf, sizeof (buf),
    912 	    "%s/boot", mp.mnt_mountp);
    913 	(void) mkdir(buf, S_IRWXU);
    914 	(void) strcat(buf, "/grub");
    915 	(void) mkdir(buf, S_IRWXU);
    916 
    917 	(void) strcat(buf, "/stage2");
    918 	pcfs_fp = open(buf, O_WRONLY | O_CREAT, S_IRWXU);
    919 	if (pcfs_fp == -1) {
    920 		(void) fprintf(stderr, OPEN_FAIL_FILE, buf);
    921 		perror("open:");
    922 		(void) umount(TMP_MNTPT);
    923 		exit(-1);
    924 	}
    925 
    926 	/* write stage2 to pcfs */
    927 	for (i = 0; ; i++) {
    928 		int nread, nwrite;
    929 		nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE);
    930 		if (nowrite)
    931 			nwrite = nread;
    932 		else
    933 			nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE);
    934 		if (nread < 0 || nwrite != nread) {
    935 			(void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
    936 			    nread, nwrite);
    937 			break;
    938 		}
    939 		if (nread < SECTOR_SIZE)
    940 			break;	/* end of file */
    941 	}
    942 	(void) close(pcfs_fp);
    943 	(void) umount(TMP_MNTPT);
    944 
    945 	/*
    946 	 * Now, get the blocklist from the device.
    947 	 */
    948 	bzero(blocklist, sizeof (blocklist));
    949 	if (read_stage2_blocklist(dev_fd, blocklist) != 0)
    950 		exit(-1);
    951 }
    952