xref: /illumos-gate/usr/src/cmd/boot/installgrub/installgrub.c (revision 69b3e10436272b0970d58743de375d0dd61046ce)
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