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