xref: /illumos-gate/usr/src/cmd/boot/installgrub/installgrub.c (revision d8a7fe16f62711cdc5c4267da8b34ff24a6b668c)
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 2009 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 		libfdisk_fini(&epp);
315 		switch (rval) {
316 			/*
317 			 * The first 3 cases are not an error per-se, just that
318 			 * there is no Solaris logical partition
319 			 */
320 			case FDISK_EBADLOGDRIVE:
321 			case FDISK_ENOLOGDRIVE:
322 			case FDISK_EBADMAGIC:
323 				(void) fprintf(stderr, NOSOLPAR);
324 				exit(-1);
325 				/*NOTREACHED*/
326 			case FDISK_ENOVGEOM:
327 				(void) fprintf(stderr, NO_VIRT_GEOM);
328 				exit(1);
329 				break;
330 			case FDISK_ENOPGEOM:
331 				(void) fprintf(stderr, NO_PHYS_GEOM);
332 				exit(1);
333 				break;
334 			case FDISK_ENOLGEOM:
335 				(void) fprintf(stderr, NO_LABEL_GEOM);
336 				exit(1);
337 				break;
338 			default:
339 				(void) fprintf(stderr, LIBFDISK_INIT_FAIL);
340 				exit(1);
341 				break;
342 		}
343 	}
344 
345 	rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
346 	libfdisk_fini(&epp);
347 	if (rval != FDISK_SUCCESS) {
348 		/* No solaris logical partition */
349 		(void) fprintf(stderr, NOSOLPAR);
350 		exit(-1);
351 	}
352 
353 	start_sect = secnum;
354 	partition = pno - 1;
355 	log_part = 1;
356 
357 found_part:
358 	/* get confirmation for -m */
359 	if (write_mboot && !force_mboot) {
360 		(void) fprintf(stdout, MBOOT_PROMPT);
361 		if (getchar() != 'y') {
362 			write_mboot = 0;
363 			(void) fprintf(stdout, MBOOT_NOT_UPDATED);
364 		}
365 	}
366 
367 	/*
368 	 * Currently if Solaris is in an extended partition we need to
369 	 * write GRUB to the MBR. Check for this.
370 	 */
371 	if (log_part && !write_mboot) {
372 		(void) fprintf(stderr, EXTSOLPAR);
373 		exit(-1);
374 	}
375 
376 	/*
377 	 * warn, if Solaris in primary partition and GRUB not in MBR and
378 	 * partition is not active
379 	 */
380 	if (!log_part && part->bootid != 128 && !write_mboot) {
381 		(void) fprintf(stdout, SOLPAR_INACTIVE, partition + 1);
382 	}
383 
384 	return (start_sect);
385 }
386 
387 static void
388 usage(char *progname)
389 {
390 	(void) fprintf(stderr, USAGE, basename(progname));
391 	exit(-1);
392 }
393 
394 static int
395 open_device(char *device)
396 {
397 	int dev_fd;
398 	struct stat stat;
399 	char *raw_part;
400 
401 	is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) &&
402 	    strncmp(device, "/dev/dsk", strlen("/dev/dsk"));
403 
404 	/* handle boot partition specification */
405 	if (!is_floppy && strstr(device, "p0:boot")) {
406 		is_bootpar = 1;
407 	}
408 
409 	raw_part = get_raw_partition(device);
410 
411 	if (nowrite)
412 		dev_fd = open(raw_part, O_RDONLY);
413 	else
414 		dev_fd = open(raw_part, O_RDWR);
415 
416 	if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) {
417 		(void) fprintf(stderr, OPEN_FAIL, raw_part);
418 		exit(-1);
419 	}
420 	if (S_ISCHR(stat.st_mode) == 0) {
421 		(void) fprintf(stderr, NOT_RAW_DEVICE, raw_part);
422 		exit(-1);
423 	}
424 
425 	return (dev_fd);
426 }
427 
428 static void
429 read_stage1_stage2(char *stage1, char *stage2)
430 {
431 	int fd;
432 
433 	/* read the stage1 file from filesystem */
434 	fd = open(stage1, O_RDONLY);
435 	if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) {
436 		(void) fprintf(stderr, READ_FAIL_STAGE1, stage1);
437 		exit(-1);
438 	}
439 	(void) close(fd);
440 
441 	/* read first two blocks of stage 2 from filesystem */
442 	stage2_fd = open(stage2, O_RDONLY);
443 	if (stage2_fd == -1 ||
444 	    read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE)
445 	    != 2 * SECTOR_SIZE) {
446 		(void) fprintf(stderr, READ_FAIL_STAGE2, stage2);
447 		exit(-1);
448 	}
449 	/* leave the stage2 file open for later */
450 }
451 
452 static void
453 read_bpb_sect(int dev_fd)
454 {
455 	if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) {
456 		(void) fprintf(stderr, READ_FAIL_BPB);
457 		exit(-1);
458 	}
459 }
460 
461 static void
462 read_boot_sect(char *device)
463 {
464 	static int read_mbr = 0;
465 	int i, fd;
466 	char save[2];
467 
468 	if (read_mbr)
469 		return;
470 	read_mbr = 1;
471 
472 	/* get the whole disk (p0) */
473 	i = strlen(device);
474 	save[0] = device[i - 2];
475 	save[1] = device[i - 1];
476 	device[i - 2] = 'p';
477 	device[i - 1] = '0';
478 
479 	device_p0 = strdup(device);
480 	fd = open(device, O_RDONLY);
481 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
482 		(void) fprintf(stderr, READ_FAIL_MBR, device);
483 		if (fd == -1)
484 			perror("open");
485 		else
486 			perror("read");
487 		exit(-1);
488 	}
489 	(void) close(fd);
490 	device[i - 2] = save[0];
491 	device[i - 1] = save[1];
492 }
493 
494 static void
495 write_boot_sect(char *device)
496 {
497 	int fd, len;
498 	char *raw, *end;
499 	struct stat stat;
500 
501 	/* make a copy and chop off ":boot" */
502 	raw = strdup(device);
503 	end = strstr(raw, "p0:boot");
504 	if (end)
505 		end[2] = 0;
506 
507 	/* open p0 (whole disk) */
508 	len = strlen(raw);
509 	raw[len - 2] = 'p';
510 	raw[len - 1] = '0';
511 	fd = open(raw, O_WRONLY);
512 	if (fd == -1 || fstat(fd, &stat) != 0) {
513 		(void) fprintf(stderr, OPEN_FAIL, raw);
514 		exit(-1);
515 	}
516 	if (!nowrite &&
517 	    pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
518 		(void) fprintf(stderr, WRITE_FAIL_BOOTSEC);
519 		exit(-1);
520 	}
521 	(void) fprintf(stdout, WRITE_MBOOT);
522 	(void) close(fd);
523 }
524 
525 static void
526 modify_and_write_stage1(int dev_fd)
527 {
528 	if (is_floppy) {
529 		stage2_first_sector = blocklist[0];
530 		/* copy bios parameter block (for fat fs) */
531 		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
532 		    stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
533 	} else if (is_bootpar) {
534 		stage2_first_sector = get_start_sector(dev_fd) + blocklist[0];
535 		/* copy bios parameter block (for fat fs) and MBR */
536 		bcopy(bpb_sect + STAGE1_BPB_OFFSET,
537 		    stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
538 		bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
539 		*((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
540 	} else {
541 		stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF;
542 		/* copy MBR to stage1 in case of overwriting MBR sector */
543 		bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
544 		*((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
545 	}
546 
547 	/* modify default stage1 file generated by GRUB */
548 	*((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR))
549 	    = stage2_first_sector;
550 	*((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS))
551 	    = STAGE2_MEMADDR;
552 	*((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT))
553 	    = STAGE2_MEMADDR >> 4;
554 
555 	/*
556 	 * XXX the default grub distribution also:
557 	 * - Copy the possible MBR/extended part table
558 	 * - Set the boot drive of stage1
559 	 */
560 
561 	/* write stage1/pboot to 1st sector */
562 	if (!nowrite &&
563 	    pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
564 		(void) fprintf(stderr, WRITE_FAIL_PBOOT);
565 		exit(-1);
566 	}
567 
568 	if (is_floppy) {
569 		(void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY);
570 	} else {
571 		(void) fprintf(stdout, WRITE_PBOOT,
572 		    partition, get_start_sector(dev_fd));
573 	}
574 }
575 
576 static void check_extended_support(char *stage2)
577 {
578 	char	*cmp = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
579 
580 	if ((*cmp++ != '\xEE') && memcmp(cmp, extended_sig, HASH_SIZE) != 0) {
581 		fprintf(stderr, "%s does not support extended versioning\n",
582 		    stage2);
583 		do_version = 0;
584 	}
585 }
586 
587 
588 static void print_info()
589 {
590 	int	i;
591 
592 	if (strip) {
593 		fprintf(stdout, "%s\n", verstring);
594 	} else {
595 		fprintf(stdout, "Grub extended version information : %s\n",
596 		    verstring);
597 		fprintf(stdout, "Grub stage2 (MD5) signature : ");
598 	}
599 
600 	for (i = 0; i < HASH_SIZE; i++)
601 		fprintf(stdout, "%02x", (unsigned char)signature[i]);
602 
603 	fprintf(stdout, "\n");
604 }
605 
606 static int
607 read_stage2_info(int dev_fd)
608 {
609 	int 	ret;
610 	int	first_offset, second_offset;
611 	char	*sign;
612 
613 	if (is_floppy || is_bootpar) {
614 
615 		ret = pread(dev_fd, stage1_buffer, SECTOR_SIZE, 0);
616 		if (ret != SECTOR_SIZE) {
617 			perror("Error reading stage1 sector");
618 			return (1);
619 		}
620 
621 		first_offset = *((ulong_t *)(stage1_buffer +
622 		    STAGE1_STAGE2_SECTOR));
623 
624 		/* Start reading in the first sector of stage 2 */
625 
626 		ret = pread(dev_fd, stage2_buffer, SECTOR_SIZE, first_offset *
627 		    SECTOR_SIZE);
628 		if (ret != SECTOR_SIZE) {
629 			perror("Error reading stage2 first sector");
630 			return (1);
631 		}
632 
633 		/* From the block list section grab stage2 second sector */
634 
635 		second_offset = *((ulong_t *)(stage2_buffer +
636 		    STAGE2_BLOCKLIST));
637 
638 		ret = pread(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
639 		    second_offset * SECTOR_SIZE);
640 		if (ret != SECTOR_SIZE) {
641 			perror("Error reading stage2 second sector");
642 			return (1);
643 		}
644 	} else {
645 		ret = pread(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
646 		    STAGE2_BLKOFF * SECTOR_SIZE);
647 		if (ret != 2 * SECTOR_SIZE) {
648 			perror("Error reading stage2 sectors");
649 			return (1);
650 		}
651 	}
652 
653 	sign = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
654 	if (*sign++ != '\xEE')
655 		return (1);
656 	(void) memcpy(signature, sign, HASH_SIZE);
657 	sign = stage2_buffer + STAGE2_PKG_VERSION;
658 	(void) strncpy(verstring, sign, VERSION_SIZE);
659 	return (0);
660 }
661 
662 
663 static int
664 compute_and_write_md5hash(char *dest)
665 {
666 	struct stat	sb;
667 	char		*buffer;
668 
669 	if (fstat(stage2_fd, &sb) == -1)
670 		return (-1);
671 
672 	buffer = malloc(sb.st_size);
673 	if (buffer == NULL)
674 		return (-1);
675 
676 	if (lseek(stage2_fd, 0, SEEK_SET) == -1)
677 		return (-1);
678 	if (read(stage2_fd, buffer, sb.st_size) < 0)
679 		return (-1);
680 
681 	md5_calc(dest, buffer, sb.st_size);
682 	free(buffer);
683 	return (0);
684 }
685 
686 
687 #define	START_BLOCK(pos)	(*(ulong_t *)(pos))
688 #define	NUM_BLOCK(pos)		(*(ushort_t *)((pos) + 4))
689 #define	START_SEG(pos)		(*(ushort_t *)((pos) + 6))
690 
691 static void
692 modify_and_write_stage2(int dev_fd)
693 {
694 	int 	nrecord;
695 	off_t 	offset;
696 	char	*dest;
697 
698 	if (do_version) {
699 		dest = stage2_buffer + STAGE2_SIGN_OFFSET;
700 		if (compute_and_write_md5hash(dest) < 0)
701 			perror("MD5 operation");
702 		dest = stage2_buffer + STAGE2_PKG_VERSION;
703 		(void) strncpy(dest, verstring, VERSION_SIZE);
704 	}
705 
706 	if (is_floppy || is_bootpar) {
707 		int i = 0;
708 		uint32_t partition_offset;
709 		uint32_t install_addr = 0x8200;
710 		uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST;
711 
712 		stage2_first_sector = blocklist[0];
713 
714 		/* figure out the second sector */
715 		if (blocklist[1] > 1) {
716 			blocklist[0]++;
717 			blocklist[1]--;
718 		} else {
719 			i += 2;
720 		}
721 		stage2_second_sector = blocklist[i];
722 
723 		if (is_floppy)
724 			partition_offset = 0;
725 		else	/* solaris boot partition */
726 			partition_offset = get_start_sector(dev_fd);
727 
728 		/* install the blocklist at the end of stage2_buffer */
729 		while (blocklist[i]) {
730 			if (START_BLOCK(pos - 8) != 0 &&
731 			    START_BLOCK(pos - 8) != blocklist[i + 2]) {
732 				(void) fprintf(stderr, PCFS_FRAGMENTED);
733 				exit(-1);
734 			}
735 			START_BLOCK(pos) = blocklist[i] + partition_offset;
736 			START_SEG(pos) = (ushort_t)(install_addr >> 4);
737 			NUM_BLOCK(pos) = blocklist[i + 1];
738 			install_addr += blocklist[i + 1] * SECTOR_SIZE;
739 			pos -= 8;
740 			i += 2;
741 		}
742 
743 	} else {
744 		/*
745 		 * In a solaris partition, stage2 is written to contiguous
746 		 * blocks. So we update the starting block only.
747 		 */
748 		*((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) =
749 		    stage2_first_sector + 1;
750 	}
751 
752 	if (is_floppy) {
753 		/* modify the config file to add (fd0) */
754 		char *config_file = stage2_buffer + STAGE2_VER_STRING;
755 		while (*config_file++)
756 			;
757 		strcpy(config_file, "(fd0)/boot/grub/menu.lst");
758 	} else {
759 		/* force lba and set disk partition */
760 		*((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1;
761 		*((long *)(stage2_buffer + STAGE2_INSTALLPART))
762 		    = (partition << 16) | (slice << 8) | 0xff;
763 	}
764 
765 	/* modification done, now do the writing */
766 	if (is_floppy || is_bootpar) {
767 		/* we rewrite block 0 and 1 and that's it */
768 		if (!nowrite &&
769 		    (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
770 		    stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE ||
771 		    pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
772 		    stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) {
773 			(void) fprintf(stderr, WRITE_FAIL_STAGE2);
774 			exit(-1);
775 		}
776 		(void) fprintf(stdout, WRITE_STAGE2_PCFS);
777 		return;
778 	}
779 
780 	/* for disk, write stage2 starting at STAGE2_BLKOFF sector */
781 	offset = STAGE2_BLKOFF;
782 
783 	/* write the modified first two sectors */
784 	if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
785 	    offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) {
786 		(void) fprintf(stderr, WRITE_FAIL_STAGE2);
787 		exit(-1);
788 	}
789 
790 	/* write the remaining sectors */
791 	nrecord = 2;
792 	offset += 2;
793 	for (;;) {
794 		int nread, nwrite;
795 		nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE,
796 		    nrecord * SECTOR_SIZE);
797 		if (nread > 0 && !nowrite)
798 			nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
799 			    offset * SECTOR_SIZE);
800 		else
801 			nwrite = SECTOR_SIZE;
802 		if (nread < 0 || nwrite != SECTOR_SIZE) {
803 			(void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
804 			    nread, nwrite);
805 			break;
806 		}
807 		if (nread > 0) {
808 			nrecord ++;
809 			offset ++;
810 		}
811 		if (nread < SECTOR_SIZE)
812 			break;	/* end of file */
813 	}
814 	(void) fprintf(stdout, WRITE_STAGE2_DISK,
815 	    partition, nrecord, STAGE2_BLKOFF, stage2_first_sector);
816 }
817 
818 static char *
819 get_raw_partition(char *device)
820 {
821 	int len;
822 	struct mboot *mboot;
823 	static char *raw = NULL;
824 
825 	if (raw)
826 		return (raw);
827 	raw = strdup(device);
828 
829 	if (is_floppy)
830 		return (raw);
831 
832 	if (is_bootpar) {
833 		int i;
834 		char *end = strstr(raw, "p0:boot");
835 
836 		end[2] = 0;		/* chop off :boot */
837 		read_boot_sect(raw);
838 		mboot = (struct mboot *)boot_sect;
839 		for (i = 0; i < FD_NUMPART; i++) {
840 			struct ipart *part = (struct ipart *)mboot->parts + i;
841 			if (part->systid == 0xbe)	/* solaris boot part */
842 				break;
843 		}
844 
845 		if (i == FD_NUMPART) {
846 			(void) fprintf(stderr, BOOTPAR_NOTFOUND, device);
847 			exit(-1);
848 		}
849 		end[1] = '1' + i;	/* set partition name */
850 		return (raw);
851 	}
852 
853 	/* For disk, remember slice and return whole fdisk partition  */
854 	len = strlen(raw);
855 	if (raw[len - 2] != 's' || raw[len - 1] == '2') {
856 		(void) fprintf(stderr, NOT_ROOT_SLICE);
857 		exit(-1);
858 	}
859 	slice = atoi(&raw[len - 1]);
860 
861 	raw[len - 2] = 's';
862 	raw[len - 1] = '2';
863 	return (raw);
864 }
865 
866 #define	TMP_MNTPT	"/tmp/installgrub_pcfs"
867 static void
868 copy_stage2(int dev_fd, char *device)
869 {
870 	FILE *mntfp;
871 	int i, pcfs_fp;
872 	char buf[SECTOR_SIZE];
873 	char *cp;
874 	struct mnttab mp = {0}, mpref = {0};
875 
876 	/* convert raw to block device name by removing the first 'r' */
877 	(void) strncpy(buf, device, sizeof (buf));
878 	buf[sizeof (buf) - 1] = 0;
879 	cp = strchr(buf, 'r');
880 	if (cp == NULL) {
881 		(void) fprintf(stderr, CONVERT_FAIL, device);
882 		exit(-1);
883 	}
884 	do {
885 		*cp = *(cp + 1);
886 	} while (*(++cp));
887 
888 	/* get the mount point, if any */
889 	mntfp = fopen("/etc/mnttab", "r");
890 	if (mntfp == NULL) {
891 		(void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab");
892 		exit(-1);
893 	}
894 
895 	mpref.mnt_special = buf;
896 	if (getmntany(mntfp, &mp, &mpref) != 0) {
897 		char cmd[128];
898 
899 		/* not mounted, try remount */
900 		(void) mkdir(TMP_MNTPT, S_IRWXU);
901 		(void) snprintf(cmd, sizeof (cmd), "mount -F pcfs %s %s",
902 		    buf, TMP_MNTPT);
903 		(void) system(cmd);
904 		rewind(mntfp);
905 		bzero(&mp, sizeof (mp));
906 		if (getmntany(mntfp, &mp, &mpref) != 0) {
907 			(void) fprintf(stderr, MOUNT_FAIL, buf);
908 			exit(-1);
909 		}
910 	}
911 
912 	(void) snprintf(buf, sizeof (buf),
913 	    "%s/boot", mp.mnt_mountp);
914 	(void) mkdir(buf, S_IRWXU);
915 	(void) strcat(buf, "/grub");
916 	(void) mkdir(buf, S_IRWXU);
917 
918 	(void) strcat(buf, "/stage2");
919 	pcfs_fp = open(buf, O_WRONLY | O_CREAT, S_IRWXU);
920 	if (pcfs_fp == -1) {
921 		(void) fprintf(stderr, OPEN_FAIL_FILE, buf);
922 		perror("open:");
923 		(void) umount(TMP_MNTPT);
924 		exit(-1);
925 	}
926 
927 	/* write stage2 to pcfs */
928 	for (i = 0; ; i++) {
929 		int nread, nwrite;
930 		nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE);
931 		if (nowrite)
932 			nwrite = nread;
933 		else
934 			nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE);
935 		if (nread < 0 || nwrite != nread) {
936 			(void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
937 			    nread, nwrite);
938 			break;
939 		}
940 		if (nread < SECTOR_SIZE)
941 			break;	/* end of file */
942 	}
943 	(void) close(pcfs_fp);
944 	(void) umount(TMP_MNTPT);
945 
946 	/*
947 	 * Now, get the blocklist from the device.
948 	 */
949 	bzero(blocklist, sizeof (blocklist));
950 	if (read_stage2_blocklist(dev_fd, blocklist) != 0)
951 		exit(-1);
952 }
953