6039 installgrub MBR update response check should use locale specific yesexpr
[unleashed.git] / usr / src / cmd / boot / installgrub / installgrub.c
blob0ae6749bcc82f8c93c720792a5a9744f7a797b92
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <libgen.h>
30 #include <malloc.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <strings.h>
35 #include <libintl.h>
36 #include <locale.h>
37 #include <errno.h>
38 #include <libfdisk.h>
39 #include <stdarg.h>
40 #include <assert.h>
42 #include <sys/mount.h>
43 #include <sys/mnttab.h>
44 #include <sys/dktp/fdisk.h>
45 #include <sys/dkio.h>
46 #include <sys/vtoc.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/multiboot.h>
50 #include <sys/sysmacros.h>
51 #include <sys/efi_partition.h>
53 #include <libnvpair.h>
54 #include <libfstyp.h>
56 #include "message.h"
57 #include "installgrub.h"
58 #include "./../common/bblk_einfo.h"
59 #include "./../common/boot_utils.h"
60 #include "./../common/mboot_extra.h"
61 #include "getresponse.h"
63 #ifndef TEXT_DOMAIN
64 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
65 #endif
68 * Variables to track installgrub desired mode of operation.
69 * 'nowrite' and 'boot_debug' come from boot_common.h.
71 static boolean_t write_mbr = B_FALSE;
72 static boolean_t force_mbr = B_FALSE;
73 static boolean_t force_update = B_FALSE;
74 static boolean_t do_getinfo = B_FALSE;
75 static boolean_t do_version = B_FALSE;
76 static boolean_t do_mirror_bblk = B_FALSE;
77 static boolean_t strip = B_FALSE;
78 static boolean_t verbose_dump = B_FALSE;
80 /* Installing the bootblock is the default operation. */
81 static boolean_t do_install = B_TRUE;
83 /* Versioning string, if present. */
84 static char *update_str;
87 * Temporary buffer to store the first 32K of data looking for a multiboot
88 * signature.
90 char mboot_scan[MBOOT_SCAN_SIZE];
92 /* Function prototypes. */
93 static void check_options(char *);
94 static int handle_install(char *, char **);
95 static int handle_mirror(char *, char **);
96 static int handle_getinfo(char *, char **);
97 static int commit_to_disk(ig_data_t *, char *);
98 static int init_device(ig_device_t *, char *path);
99 static void cleanup_device(ig_device_t *);
100 static void cleanup_stage2(ig_stage2_t *);
101 static int get_start_sector(ig_device_t *);
102 static int get_disk_fd(ig_device_t *device);
103 static int get_raw_partition_fd(ig_device_t *);
104 static char *get_raw_partition_path(ig_device_t *);
105 static boolean_t gather_stage2_from_dev(ig_data_t *);
106 static int propagate_bootblock(ig_data_t *, ig_data_t *, char *);
107 static int find_x86_bootpar(struct mboot *, int *, uint32_t *);
108 static int copy_stage2_to_pcfs(ig_data_t *);
109 static int write_stage2(ig_data_t *);
110 static int write_stage1(ig_data_t *);
111 static void usage(char *);
112 static int read_stage1_from_file(char *, ig_data_t *);
113 static int read_stage2_from_file(char *, ig_data_t *);
114 static int read_stage1_from_disk(int, char *);
115 static int read_stage2_from_disk(int, ig_stage2_t *, int);
116 static int prepare_stage1(ig_data_t *);
117 static int prepare_stage2(ig_data_t *, char *);
118 static void prepare_fake_multiboot(ig_stage2_t *);
119 static void add_stage2_einfo(ig_stage2_t *, char *updt_str);
120 static boolean_t is_update_necessary(ig_data_t *, char *);
122 extern int read_stage2_blocklist(int, unsigned int *);
125 main(int argc, char *argv[])
127 int opt;
128 int params = 3;
129 int ret;
130 char **handle_args;
131 char *progname;
133 (void) setlocale(LC_ALL, "");
134 (void) textdomain(TEXT_DOMAIN);
135 if (init_yes() < 0) {
136 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
137 strerror(errno));
138 exit(BC_ERROR);
142 * retro-compatibility: installing the bootblock is the default
143 * and there is no switch for it.
145 do_install = B_TRUE;
147 while ((opt = getopt(argc, argv, "dVMFfmneiu:")) != EOF) {
148 switch (opt) {
149 case 'm':
150 write_mbr = B_TRUE;
151 break;
152 case 'n':
153 nowrite = B_TRUE;
154 break;
155 case 'f':
156 force_mbr = B_TRUE;
157 break;
158 case 'i':
159 do_getinfo = B_TRUE;
160 do_install = B_FALSE;
161 params = 1;
162 break;
163 case 'V':
164 verbose_dump = B_TRUE;
165 break;
166 case 'd':
167 boot_debug = B_TRUE;
168 break;
169 case 'F':
170 force_update = B_TRUE;
171 break;
172 case 'e':
173 strip = B_TRUE;
174 break;
175 case 'M':
176 do_mirror_bblk = B_TRUE;
177 do_install = B_FALSE;
178 params = 2;
179 break;
180 case 'u':
181 do_version = B_TRUE;
183 update_str = malloc(strlen(optarg) + 1);
184 if (update_str == NULL) {
185 (void) fprintf(stderr, gettext("Unable to "
186 "allocate memory\n"));
187 exit(BC_ERROR);
189 (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
190 break;
191 default:
192 /* fall through to process non-optional args */
193 break;
197 /* check arguments */
198 if (argc != optind + params) {
199 usage(argv[0]);
200 exit(BC_ERROR);
204 * clean up options (and bail out if an unrecoverable combination is
205 * requested.
207 progname = argv[0];
208 check_options(progname);
209 handle_args = argv + optind;
211 if (nowrite)
212 (void) fprintf(stdout, DRY_RUN);
214 if (do_getinfo) {
215 ret = handle_getinfo(progname, handle_args);
216 } else if (do_mirror_bblk) {
217 ret = handle_mirror(progname, handle_args);
218 } else {
219 ret = handle_install(progname, handle_args);
221 return (ret);
224 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
225 static void
226 check_options(char *progname)
228 if (do_getinfo && do_mirror_bblk) {
229 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
230 "specified at the same time\n"));
231 usage(progname);
232 exit(BC_ERROR);
235 if (do_mirror_bblk) {
237 * -u and -F may actually reflect a user intent that is not
238 * correct with this command (mirror can be interpreted
239 * "similar" to install. Emit a message and continue.
240 * -e and -V have no meaning, be quiet here and only report the
241 * incongruence if a debug output is requested.
243 if (do_version) {
244 (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
245 do_version = B_FALSE;
247 if (force_update) {
248 (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
249 force_update = B_FALSE;
251 if (strip || verbose_dump) {
252 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
253 strip = B_FALSE;
254 verbose_dump = B_FALSE;
258 if (do_getinfo) {
259 if (write_mbr || force_mbr || do_version || force_update) {
260 BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
261 write_mbr = force_mbr = do_version = B_FALSE;
262 force_update = B_FALSE;
268 * Install a new stage1/stage2 pair on the specified device. handle_install()
269 * expects argv to contain 3 parameters (the path to stage1, the path to stage2,
270 * the target device).
272 * Returns: BC_SUCCESS - if the installation is successful
273 * BC_ERROR - if the installation failed
274 * BC_NOUPDT - if no installation was performed because the GRUB
275 * version currently installed is more recent than the
276 * supplied one.
279 static int
280 handle_install(char *progname, char **argv)
282 ig_data_t install_data;
283 char *stage1_path = NULL;
284 char *stage2_path = NULL;
285 char *device_path = NULL;
286 int ret = BC_ERROR;
288 stage1_path = strdup(argv[0]);
289 stage2_path = strdup(argv[1]);
290 device_path = strdup(argv[2]);
292 bzero(&install_data, sizeof (ig_data_t));
294 if (!stage1_path || !stage2_path || !device_path) {
295 (void) fprintf(stderr, gettext("Missing parameter"));
296 usage(progname);
297 goto out;
300 BOOT_DEBUG("stage1 path: %s, stage2 path: %s, device: %s\n",
301 stage1_path, stage2_path, device_path);
303 if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
304 (void) fprintf(stderr, gettext("Unable to gather device "
305 "information for %s\n"), device_path);
306 goto out;
309 /* read in stage1 and stage2. */
310 if (read_stage1_from_file(stage1_path, &install_data) != BC_SUCCESS) {
311 (void) fprintf(stderr, gettext("Error opening %s\n"),
312 stage1_path);
313 goto out_dev;
316 if (read_stage2_from_file(stage2_path, &install_data) != BC_SUCCESS) {
317 (void) fprintf(stderr, gettext("Error opening %s\n"),
318 stage2_path);
319 goto out_dev;
322 /* We do not support versioning on PCFS. */
323 if (is_bootpar(install_data.device.type) && do_version)
324 do_version = B_FALSE;
327 * is_update_necessary() will take care of checking if versioning and/or
328 * forcing the update have been specified. It will also emit a warning
329 * if a non-versioned update is attempted over a versioned bootblock.
331 if (!is_update_necessary(&install_data, update_str)) {
332 (void) fprintf(stderr, gettext("GRUB version installed "
333 "on %s is more recent or identical\n"
334 "Use -F to override or install without the -u option\n"),
335 device_path);
336 ret = BC_NOUPDT;
337 goto out_dev;
340 * We get here if:
341 * - the installed GRUB version is older than the one about to be
342 * installed.
343 * - no versioning string has been passed through the command line.
344 * - a forced update is requested (-F).
346 BOOT_DEBUG("Ready to commit to disk\n");
347 ret = commit_to_disk(&install_data, update_str);
349 out_dev:
350 cleanup_device(&install_data.device);
351 out:
352 free(stage1_path);
353 free(stage2_path);
354 free(device_path);
355 return (ret);
359 * Retrieves from a device the extended information (einfo) associated to the
360 * installed stage2.
361 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
362 * Returns:
363 * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
364 * - BC_ERROR (on error)
365 * - BC_NOEINFO (no extended information available)
367 static int
368 handle_getinfo(char *progname, char **argv)
370 ig_data_t data;
371 ig_stage2_t *stage2 = &data.stage2;
372 ig_device_t *device = &data.device;
373 bblk_einfo_t *einfo;
374 uint8_t flags = 0;
375 uint32_t size;
376 char *device_path;
377 int retval = BC_ERROR;
378 int ret;
380 device_path = strdup(argv[0]);
381 if (!device_path) {
382 (void) fprintf(stderr, gettext("Missing parameter"));
383 usage(progname);
384 goto out;
387 bzero(&data, sizeof (ig_data_t));
388 BOOT_DEBUG("device path: %s\n", device_path);
390 if (init_device(device, device_path) != BC_SUCCESS) {
391 (void) fprintf(stderr, gettext("Unable to gather device "
392 "information for %s\n"), device_path);
393 goto out_dev;
396 if (is_bootpar(device->type)) {
397 (void) fprintf(stderr, gettext("Versioning not supported on "
398 "PCFS\n"));
399 goto out_dev;
402 ret = read_stage2_from_disk(device->part_fd, stage2, device->type);
403 if (ret == BC_ERROR) {
404 (void) fprintf(stderr, gettext("Error reading stage2 from "
405 "%s\n"), device_path);
406 goto out_dev;
409 if (ret == BC_NOEXTRA) {
410 (void) fprintf(stdout, gettext("No multiboot header found on "
411 "%s, unable to locate extra information area\n"),
412 device_path);
413 retval = BC_NOEINFO;
414 goto out_dev;
417 einfo = find_einfo(stage2->extra, stage2->extra_size);
418 if (einfo == NULL) {
419 retval = BC_NOEINFO;
420 (void) fprintf(stderr, gettext("No extended information "
421 "found\n"));
422 goto out_dev;
425 /* Print the extended information. */
426 if (strip)
427 flags |= EINFO_EASY_PARSE;
428 if (verbose_dump)
429 flags |= EINFO_PRINT_HEADER;
431 size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
432 print_einfo(flags, einfo, size);
433 retval = BC_SUCCESS;
435 out_dev:
436 cleanup_device(&data.device);
437 out:
438 free(device_path);
439 return (retval);
443 * Attempt to mirror (propagate) the current stage2 over the attaching disk.
445 * Returns:
446 * - BC_SUCCESS (a successful propagation happened)
447 * - BC_ERROR (an error occurred)
448 * - BC_NOEXTRA (it is not possible to dump the current bootblock since
449 * there is no multiboot information)
451 static int
452 handle_mirror(char *progname, char **argv)
454 ig_data_t curr_data;
455 ig_data_t attach_data;
456 ig_device_t *curr_device = &curr_data.device;
457 ig_device_t *attach_device = &attach_data.device;
458 ig_stage2_t *stage2_curr = &curr_data.stage2;
459 ig_stage2_t *stage2_attach = &attach_data.stage2;
460 bblk_einfo_t *einfo_curr = NULL;
461 char *curr_device_path;
462 char *attach_device_path;
463 char *updt_str = NULL;
464 int retval = BC_ERROR;
465 int ret;
467 curr_device_path = strdup(argv[0]);
468 attach_device_path = strdup(argv[1]);
470 if (!curr_device_path || !attach_device_path) {
471 (void) fprintf(stderr, gettext("Missing parameter"));
472 usage(progname);
473 goto out;
475 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
476 " %s\n", curr_device_path, attach_device_path);
478 bzero(&curr_data, sizeof (ig_data_t));
479 bzero(&attach_data, sizeof (ig_data_t));
481 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
482 (void) fprintf(stderr, gettext("Unable to gather device "
483 "information for %s (current device)\n"), curr_device_path);
484 goto out_currdev;
487 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
488 (void) fprintf(stderr, gettext("Unable to gather device "
489 "information for %s (attaching device)\n"),
490 attach_device_path);
491 goto out_devs;
494 if (is_bootpar(curr_device->type) || is_bootpar(attach_device->type)) {
495 (void) fprintf(stderr, gettext("boot block mirroring is not "
496 "supported on PCFS\n"));
497 goto out_devs;
500 ret = read_stage2_from_disk(curr_device->part_fd, stage2_curr,
501 curr_device->type);
502 if (ret == BC_ERROR) {
503 BOOT_DEBUG("Error reading first stage2 blocks from %s\n",
504 curr_device->path);
505 retval = BC_ERROR;
506 goto out_devs;
509 if (ret == BC_NOEXTRA) {
510 BOOT_DEBUG("No multiboot header found on %s, unable to grab "
511 "stage2\n", curr_device->path);
512 retval = BC_NOEXTRA;
513 goto out_devs;
516 einfo_curr = find_einfo(stage2_curr->extra, stage2_curr->extra_size);
517 if (einfo_curr != NULL)
518 updt_str = einfo_get_string(einfo_curr);
520 write_mbr = B_TRUE;
521 force_mbr = B_TRUE;
522 retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
523 cleanup_stage2(stage2_curr);
524 cleanup_stage2(stage2_attach);
526 out_devs:
527 cleanup_device(attach_device);
528 out_currdev:
529 cleanup_device(curr_device);
530 out:
531 free(curr_device_path);
532 free(attach_device_path);
533 return (retval);
536 static int
537 commit_to_disk(ig_data_t *install, char *updt_str)
539 assert(install != NULL);
541 * vanilla stage1 and stage2 need to be updated at runtime.
542 * Update stage2 before stage1 because stage1 needs to know the first
543 * sector stage2 will be written to.
545 if (prepare_stage2(install, updt_str) != BC_SUCCESS) {
546 (void) fprintf(stderr, gettext("Error building stage2\n"));
547 return (BC_ERROR);
549 if (prepare_stage1(install) != BC_SUCCESS) {
550 (void) fprintf(stderr, gettext("Error building stage1\n"));
551 return (BC_ERROR);
554 /* Write stage2 out to disk. */
555 if (write_stage2(install) != BC_SUCCESS) {
556 (void) fprintf(stderr, gettext("Error writing stage2 to "
557 "disk\n"));
558 return (BC_ERROR);
561 /* Write stage1 to disk and, if requested, to the MBR. */
562 if (write_stage1(install) != BC_SUCCESS) {
563 (void) fprintf(stderr, gettext("Error writing stage1 to "
564 "disk\n"));
565 return (BC_ERROR);
568 return (BC_SUCCESS);
572 * Propagate the bootblock on the source disk to the destination disk and
573 * version it with 'updt_str' in the process. Since we cannot trust any data
574 * on the attaching disk, we do not perform any specific check on a potential
575 * target extended information structure and we just blindly update.
577 static int
578 propagate_bootblock(ig_data_t *source, ig_data_t *target, char *updt_str)
580 ig_device_t *src_device = &source->device;
581 ig_device_t *dest_device = &target->device;
582 ig_stage2_t *src_stage2 = &source->stage2;
583 ig_stage2_t *dest_stage2 = &target->stage2;
584 uint32_t buf_size;
585 int retval;
587 assert(source != NULL);
588 assert(target != NULL);
590 /* read in stage1 from the source disk. */
591 if (read_stage1_from_disk(src_device->part_fd, target->stage1_buf)
592 != BC_SUCCESS)
593 return (BC_ERROR);
595 /* Prepare target stage2 for commit_to_disk. */
596 cleanup_stage2(dest_stage2);
598 if (updt_str != NULL)
599 do_version = B_TRUE;
600 else
601 do_version = B_FALSE;
603 buf_size = src_stage2->file_size + SECTOR_SIZE;
605 dest_stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
606 dest_stage2->buf = malloc(dest_stage2->buf_size);
607 if (dest_stage2->buf == NULL) {
608 perror(gettext("Memory allocation failed"));
609 return (BC_ERROR);
611 dest_stage2->file = dest_stage2->buf;
612 dest_stage2->file_size = src_stage2->file_size;
613 memcpy(dest_stage2->file, src_stage2->file, dest_stage2->file_size);
614 dest_stage2->extra = dest_stage2->buf +
615 P2ROUNDUP(dest_stage2->file_size, 8);
617 /* If we get down here we do have a mboot structure. */
618 assert(src_stage2->mboot);
620 dest_stage2->mboot_off = src_stage2->mboot_off;
621 dest_stage2->mboot = (multiboot_header_t *)(dest_stage2->buf +
622 dest_stage2->mboot_off);
624 (void) fprintf(stdout, gettext("Propagating %s stage1/stage2 to %s\n"),
625 src_device->path, dest_device->path);
626 retval = commit_to_disk(target, updt_str);
628 return (retval);
632 * open the device and fill the various members of ig_device_t.
634 static int
635 init_device(ig_device_t *device, char *path)
637 struct dk_gpt *vtoc;
638 fstyp_handle_t fhdl;
639 const char *fident;
641 bzero(device, sizeof (*device));
642 device->part_fd = -1;
643 device->disk_fd = -1;
644 device->path_p0 = NULL;
646 device->path = strdup(path);
647 if (device->path == NULL) {
648 perror(gettext("Memory allocation failed"));
649 return (BC_ERROR);
652 if (strstr(device->path, "diskette")) {
653 (void) fprintf(stderr, gettext("installing GRUB to a floppy "
654 "disk is no longer supported\n"));
655 return (BC_ERROR);
658 /* Detect if the target device is a pcfs partition. */
659 if (strstr(device->path, "p0:boot"))
660 device->type = IG_DEV_X86BOOTPAR;
662 if (get_disk_fd(device) != BC_SUCCESS)
663 return (BC_ERROR);
665 /* read in the device boot sector. */
666 if (read(device->disk_fd, device->boot_sector, SECTOR_SIZE)
667 != SECTOR_SIZE) {
668 (void) fprintf(stderr, gettext("Error reading boot sector\n"));
669 perror("read");
670 return (BC_ERROR);
673 if (efi_alloc_and_read(device->disk_fd, &vtoc) > 0) {
674 device->type = IG_DEV_EFI;
675 efi_free(vtoc);
678 if (get_raw_partition_fd(device) != BC_SUCCESS)
679 return (BC_ERROR);
681 if (is_efi(device->type)) {
682 if (fstyp_init(device->part_fd, 0, NULL, &fhdl) != 0)
683 return (BC_ERROR);
685 if (fstyp_ident(fhdl, "zfs", &fident) != 0) {
686 fstyp_fini(fhdl);
687 (void) fprintf(stderr, gettext("Booting of EFI labeled "
688 "disks is only supported with ZFS\n"));
689 return (BC_ERROR);
691 fstyp_fini(fhdl);
694 if (get_start_sector(device) != BC_SUCCESS)
695 return (BC_ERROR);
697 return (BC_SUCCESS);
700 static void
701 cleanup_device(ig_device_t *device)
703 if (device->path)
704 free(device->path);
705 if (device->path_p0)
706 free(device->path_p0);
708 if (device->part_fd != -1)
709 (void) close(device->part_fd);
710 if (device->disk_fd != -1)
711 (void) close(device->disk_fd);
713 bzero(device, sizeof (ig_device_t));
714 device->part_fd = -1;
715 device->disk_fd = -1;
718 static void
719 cleanup_stage2(ig_stage2_t *stage2)
721 if (stage2->buf)
722 free(stage2->buf);
723 bzero(stage2, sizeof (ig_stage2_t));
726 static int
727 get_start_sector(ig_device_t *device)
729 uint32_t secnum = 0, numsec = 0;
730 int i, pno, rval, log_part = 0;
731 struct mboot *mboot;
732 struct ipart *part;
733 ext_part_t *epp;
734 struct part_info dkpi;
735 struct extpart_info edkpi;
737 if (is_efi(device->type)) {
738 struct dk_gpt *vtoc;
740 if (efi_alloc_and_read(device->disk_fd, &vtoc) <= 0)
741 return (BC_ERROR);
743 device->start_sector = vtoc->efi_parts[device->slice].p_start;
744 /* GPT doesn't use traditional slice letters */
745 device->slice = 0xff;
746 device->partition = 0;
748 efi_free(vtoc);
749 goto found_part;
752 mboot = (struct mboot *)device->boot_sector;
754 if (is_bootpar(device->type)) {
755 if (find_x86_bootpar(mboot, &pno, &secnum) != BC_SUCCESS) {
756 (void) fprintf(stderr, NOBOOTPAR);
757 return (BC_ERROR);
758 } else {
759 device->start_sector = secnum;
760 device->partition = pno;
761 goto found_part;
766 * Search for Solaris fdisk partition
767 * Get the solaris partition information from the device
768 * and compare the offset of S2 with offset of solaris partition
769 * from fdisk partition table.
771 if (ioctl(device->part_fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
772 if (ioctl(device->part_fd, DKIOCPARTINFO, &dkpi) < 0) {
773 (void) fprintf(stderr, PART_FAIL);
774 return (BC_ERROR);
775 } else {
776 edkpi.p_start = dkpi.p_start;
780 for (i = 0; i < FD_NUMPART; i++) {
781 part = (struct ipart *)mboot->parts + i;
783 if (part->relsect == 0) {
784 (void) fprintf(stderr, BAD_PART, i);
785 return (BC_ERROR);
788 if (edkpi.p_start >= part->relsect &&
789 edkpi.p_start < (part->relsect + part->numsect)) {
790 /* Found the partition */
791 break;
795 if (i == FD_NUMPART) {
796 /* No solaris fdisk partitions (primary or logical) */
797 (void) fprintf(stderr, NOSOLPAR);
798 return (BC_ERROR);
802 * We have found a Solaris fdisk partition (primary or extended)
803 * Handle the simple case first: Solaris in a primary partition
805 if (!fdisk_is_dos_extended(part->systid)) {
806 device->start_sector = part->relsect;
807 device->partition = i;
808 goto found_part;
812 * Solaris in a logical partition. Find that partition in the
813 * extended part.
815 if ((rval = libfdisk_init(&epp, device->path_p0, NULL, FDISK_READ_DISK))
816 != FDISK_SUCCESS) {
817 switch (rval) {
819 * The first 3 cases are not an error per-se, just that
820 * there is no Solaris logical partition
822 case FDISK_EBADLOGDRIVE:
823 case FDISK_ENOLOGDRIVE:
824 case FDISK_EBADMAGIC:
825 (void) fprintf(stderr, NOSOLPAR);
826 return (BC_ERROR);
827 case FDISK_ENOVGEOM:
828 (void) fprintf(stderr, NO_VIRT_GEOM);
829 return (BC_ERROR);
830 case FDISK_ENOPGEOM:
831 (void) fprintf(stderr, NO_PHYS_GEOM);
832 return (BC_ERROR);
833 case FDISK_ENOLGEOM:
834 (void) fprintf(stderr, NO_LABEL_GEOM);
835 return (BC_ERROR);
836 default:
837 (void) fprintf(stderr, LIBFDISK_INIT_FAIL);
838 return (BC_ERROR);
842 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
843 libfdisk_fini(&epp);
844 if (rval != FDISK_SUCCESS) {
845 /* No solaris logical partition */
846 (void) fprintf(stderr, NOSOLPAR);
847 return (BC_ERROR);
850 device->start_sector = secnum;
851 device->partition = pno - 1;
852 log_part = 1;
854 found_part:
855 /* get confirmation for -m */
856 if (write_mbr && !force_mbr) {
857 (void) fprintf(stdout, MBOOT_PROMPT);
858 if (!yes()) {
859 write_mbr = 0;
860 (void) fprintf(stdout, MBOOT_NOT_UPDATED);
861 return (BC_ERROR);
866 * Currently if Solaris is in an extended partition we need to
867 * write GRUB to the MBR. Check for this.
869 if (log_part && !write_mbr) {
870 (void) fprintf(stdout, gettext("Installing Solaris on an "
871 "extended partition... forcing MBR update\n"));
872 write_mbr = 1;
876 * warn, if Solaris in primary partition and GRUB not in MBR and
877 * partition is not active
879 if (!log_part && part->bootid != 128 && !write_mbr) {
880 (void) fprintf(stdout, SOLPAR_INACTIVE, device->partition + 1);
883 return (BC_SUCCESS);
886 static int
887 get_disk_fd(ig_device_t *device)
889 int i;
890 char save[2];
891 char *end = NULL;
893 assert(device != NULL);
894 assert(device->path != NULL);
896 if (is_bootpar(device->type)) {
897 end = strstr(device->path, "p0:boot");
898 /* tested at the start of init_device() */
899 assert(end != NULL);
900 /* chop off :boot */
901 save[0] = end[2];
902 end[2] = '\0';
903 } else {
904 i = strlen(device->path);
905 save[0] = device->path[i - 2];
906 save[1] = device->path[i - 1];
907 device->path[i - 2] = 'p';
908 device->path[i - 1] = '0';
911 if (nowrite)
912 device->disk_fd = open(device->path, O_RDONLY);
913 else
914 device->disk_fd = open(device->path, O_RDWR);
916 device->path_p0 = strdup(device->path);
917 if (device->path_p0 == NULL) {
918 perror("strdup");
919 return (BC_ERROR);
922 if (is_bootpar(device->type)) {
923 end[2] = save[0];
924 } else {
925 device->path[i - 2] = save[0];
926 device->path[i - 1] = save[1];
929 if (device->disk_fd == -1) {
930 perror("open");
931 return (BC_ERROR);
934 return (BC_SUCCESS);
937 static void
938 prepare_fake_multiboot(ig_stage2_t *stage2)
940 multiboot_header_t *mboot;
942 assert(stage2 != NULL);
943 assert(stage2->mboot != NULL);
944 assert(stage2->buf != NULL);
946 mboot = stage2->mboot;
949 * Currently we expect find_multiboot() to have located a multiboot
950 * header with the AOUT kludge flag set.
952 assert(mboot->flags & BB_MBOOT_AOUT_FLAG);
954 /* Insert the information necessary to locate stage2. */
955 mboot->header_addr = stage2->mboot_off;
956 mboot->load_addr = 0;
957 mboot->load_end_addr = stage2->file_size;
960 static void
961 add_stage2_einfo(ig_stage2_t *stage2, char *updt_str)
963 bblk_hs_t hs;
964 uint32_t avail_space;
966 assert(stage2 != NULL);
968 /* Fill bootblock hashing source information. */
969 hs.src_buf = (unsigned char *)stage2->file;
970 hs.src_size = stage2->file_size;
971 /* How much space for the extended information structure? */
972 avail_space = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
973 add_einfo(stage2->extra, updt_str, &hs, avail_space);
977 static int
978 write_stage2(ig_data_t *install)
980 ig_device_t *device = &install->device;
981 ig_stage2_t *stage2 = &install->stage2;
982 off_t offset;
984 assert(install != NULL);
986 if (is_bootpar(device->type)) {
988 * stage2 is already on the filesystem, we only need to update
989 * the first two blocks (that we have modified during
990 * prepare_stage2())
992 if (write_out(device->part_fd, stage2->file, SECTOR_SIZE,
993 stage2->pcfs_first_sectors[0] * SECTOR_SIZE)
994 != BC_SUCCESS ||
995 write_out(device->part_fd, stage2->file + SECTOR_SIZE,
996 SECTOR_SIZE, stage2->pcfs_first_sectors[1] * SECTOR_SIZE)
997 != BC_SUCCESS) {
998 (void) fprintf(stderr, WRITE_FAIL_STAGE2);
999 return (BC_ERROR);
1001 (void) fprintf(stdout, WRITE_STAGE2_PCFS);
1002 return (BC_SUCCESS);
1006 * For disk, write stage2 starting at STAGE2_BLKOFF sector.
1007 * Note that we use stage2->buf rather than stage2->file, because we
1008 * may have extended information after the latter.
1010 * If we're writing to an EFI-labeled disk where stage2 lives in the
1011 * 3.5MB boot loader gap following the ZFS vdev labels, make sure the
1012 * size of the buffer doesn't exceed the size of the gap.
1014 if (is_efi(device->type) && stage2->buf_size > STAGE2_MAXSIZE) {
1015 (void) fprintf(stderr, WRITE_FAIL_STAGE2);
1016 return (BC_ERROR);
1019 offset = STAGE2_BLKOFF(device->type) * SECTOR_SIZE;
1021 if (write_out(device->part_fd, stage2->buf, stage2->buf_size,
1022 offset) != BC_SUCCESS) {
1023 perror("write");
1024 return (BC_ERROR);
1027 /* Simulate the "old" installgrub output. */
1028 (void) fprintf(stdout, WRITE_STAGE2_DISK, device->partition,
1029 (stage2->buf_size / SECTOR_SIZE) + 1, STAGE2_BLKOFF(device->type),
1030 stage2->first_sector);
1032 return (BC_SUCCESS);
1035 static int
1036 write_stage1(ig_data_t *install)
1038 ig_device_t *device = &install->device;
1040 assert(install != NULL);
1042 if (write_out(device->part_fd, install->stage1_buf,
1043 sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
1044 (void) fprintf(stdout, WRITE_FAIL_PBOOT);
1045 perror("write");
1046 return (BC_ERROR);
1049 /* Simulate "old" installgrub output. */
1050 (void) fprintf(stdout, WRITE_PBOOT, device->partition,
1051 device->start_sector);
1053 if (write_mbr) {
1054 if (write_out(device->disk_fd, install->stage1_buf,
1055 sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
1056 (void) fprintf(stdout, WRITE_FAIL_BOOTSEC);
1057 perror("write");
1058 return (BC_ERROR);
1060 /* Simulate "old" installgrub output. */
1061 (void) fprintf(stdout, WRITE_MBOOT);
1064 return (BC_SUCCESS);
1067 #define USAGE_STRING "%s [-m|-f|-n|-F|-u verstr] stage1 stage2 device\n" \
1068 "%s -M [-n] device1 device2\n" \
1069 "%s [-V|-e] -i device\n" \
1071 #define CANON_USAGE_STR gettext(USAGE_STRING)
1073 static void
1074 usage(char *progname)
1076 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
1080 static int
1081 read_stage1_from_file(char *path, ig_data_t *dest)
1083 int fd;
1085 assert(dest);
1087 /* read the stage1 file from filesystem */
1088 fd = open(path, O_RDONLY);
1089 if (fd == -1 ||
1090 read(fd, dest->stage1_buf, SECTOR_SIZE) != SECTOR_SIZE) {
1091 (void) fprintf(stderr, READ_FAIL_STAGE1, path);
1092 return (BC_ERROR);
1094 (void) close(fd);
1095 return (BC_SUCCESS);
1098 static int
1099 read_stage2_from_file(char *path, ig_data_t *dest)
1101 int fd;
1102 struct stat sb;
1103 ig_stage2_t *stage2 = &dest->stage2;
1104 ig_device_t *device = &dest->device;
1105 uint32_t buf_size;
1107 assert(dest);
1108 assert(stage2->buf == NULL);
1110 fd = open(path, O_RDONLY);
1111 if (fstat(fd, &sb) == -1) {
1112 perror("fstat");
1113 goto out;
1116 stage2->file_size = sb.st_size;
1118 if (!is_bootpar(device->type)) {
1120 * buffer size needs to account for stage2 plus the extra
1121 * versioning information at the end of it. We reserve one
1122 * extra sector (plus we round up to the next sector boundary).
1124 buf_size = stage2->file_size + SECTOR_SIZE;
1125 } else {
1126 /* In the PCFS case we only need to read in stage2. */
1127 buf_size = stage2->file_size;
1130 stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
1132 BOOT_DEBUG("stage2 buffer size = %d (%d sectors)\n", stage2->buf_size,
1133 stage2->buf_size / SECTOR_SIZE);
1135 stage2->buf = malloc(stage2->buf_size);
1136 if (stage2->buf == NULL) {
1137 perror(gettext("Memory allocation failed"));
1138 goto out_fd;
1141 stage2->file = stage2->buf;
1144 * Extra information (e.g. the versioning structure) is placed at the
1145 * end of stage2, aligned on a 8-byte boundary.
1147 if (!(is_bootpar(device->type)))
1148 stage2->extra = stage2->file + P2ROUNDUP(stage2->file_size, 8);
1150 if (lseek(fd, 0, SEEK_SET) == -1) {
1151 perror("lseek");
1152 goto out_alloc;
1155 if (read(fd, stage2->file, stage2->file_size) < 0) {
1156 perror(gettext("unable to read stage2"));
1157 goto out_alloc;
1160 (void) close(fd);
1161 return (BC_SUCCESS);
1163 out_alloc:
1164 free(stage2->buf);
1165 stage2->buf = NULL;
1166 out_fd:
1167 (void) close(fd);
1168 out:
1169 return (BC_ERROR);
1172 static int
1173 prepare_stage1(ig_data_t *install)
1175 ig_device_t *device = &install->device;
1177 assert(install != NULL);
1179 /* If PCFS add the BIOS Parameter Block. */
1180 if (is_bootpar(device->type)) {
1181 char bpb_sect[SECTOR_SIZE];
1183 if (pread(device->part_fd, bpb_sect, SECTOR_SIZE, 0)
1184 != SECTOR_SIZE) {
1185 (void) fprintf(stderr, READ_FAIL_BPB);
1186 return (BC_ERROR);
1188 bcopy(bpb_sect + STAGE1_BPB_OFFSET,
1189 install->stage1_buf + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
1192 /* copy MBR to stage1 in case of overwriting MBR sector. */
1193 bcopy(device->boot_sector + BOOTSZ, install->stage1_buf + BOOTSZ,
1194 SECTOR_SIZE - BOOTSZ);
1195 /* modify default stage1 file generated by GRUB. */
1196 *((unsigned char *)(install->stage1_buf + STAGE1_FORCE_LBA)) = 1;
1197 *((ulong_t *)(install->stage1_buf + STAGE1_STAGE2_SECTOR))
1198 = install->stage2.first_sector;
1199 *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_ADDRESS))
1200 = STAGE2_MEMADDR;
1201 *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_SEGMENT))
1202 = STAGE2_MEMADDR >> 4;
1204 return (BC_SUCCESS);
1208 * Grab stage1 from the specified device file descriptor.
1210 static int
1211 read_stage1_from_disk(int dev_fd, char *stage1_buf)
1213 assert(stage1_buf != NULL);
1215 if (read_in(dev_fd, stage1_buf, SECTOR_SIZE, 0) != BC_SUCCESS) {
1216 perror(gettext("Unable to read stage1 from disk"));
1217 return (BC_ERROR);
1219 return (BC_SUCCESS);
1222 static int
1223 read_stage2_from_disk(int dev_fd, ig_stage2_t *stage2, int type)
1225 uint32_t size;
1226 uint32_t buf_size;
1227 uint32_t mboot_off;
1228 multiboot_header_t *mboot;
1230 assert(stage2 != NULL);
1231 assert(dev_fd != -1);
1233 if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
1234 STAGE2_BLKOFF(type) * SECTOR_SIZE) != BC_SUCCESS) {
1235 perror(gettext("Error reading stage2 sectors"));
1236 return (BC_ERROR);
1239 /* No multiboot means no chance of knowing stage2 size */
1240 if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
1241 != BC_SUCCESS) {
1242 BOOT_DEBUG("Unable to find multiboot header\n");
1243 return (BC_NOEXTRA);
1245 mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
1248 * Unfilled mboot values mean an older version of installgrub installed
1249 * the stage2. Again we have no chance of knowing stage2 size.
1251 if (mboot->load_end_addr == 0 ||
1252 mboot->load_end_addr < mboot->load_addr)
1253 return (BC_NOEXTRA);
1256 * Currently, the amount of space reserved for extra information
1257 * is "fixed". We may have to scan for the terminating extra payload
1258 * in the future.
1260 size = mboot->load_end_addr - mboot->load_addr;
1261 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
1263 stage2->buf = malloc(buf_size);
1264 if (stage2->buf == NULL) {
1265 perror(gettext("Memory allocation failed"));
1266 return (BC_ERROR);
1268 stage2->buf_size = buf_size;
1270 if (read_in(dev_fd, stage2->buf, buf_size, STAGE2_BLKOFF(type) *
1271 SECTOR_SIZE) != BC_SUCCESS) {
1272 perror("read");
1273 free(stage2->buf);
1274 return (BC_ERROR);
1277 /* Update pointers. */
1278 stage2->file = stage2->buf;
1279 stage2->file_size = size;
1280 stage2->mboot_off = mboot_off;
1281 stage2->mboot = (multiboot_header_t *)(stage2->buf + stage2->mboot_off);
1282 stage2->extra = stage2->buf + P2ROUNDUP(stage2->file_size, 8);
1283 stage2->extra_size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
1285 return (BC_SUCCESS);
1288 static boolean_t
1289 is_update_necessary(ig_data_t *data, char *updt_str)
1291 bblk_einfo_t *einfo;
1292 bblk_hs_t stage2_hs;
1293 ig_stage2_t stage2_disk;
1294 ig_stage2_t *stage2_file = &data->stage2;
1295 ig_device_t *device = &data->device;
1296 int dev_fd = device->part_fd;
1298 assert(data != NULL);
1299 assert(device->part_fd != -1);
1301 bzero(&stage2_disk, sizeof (ig_stage2_t));
1303 /* Gather stage2 (if present) from the target device. */
1304 if (read_stage2_from_disk(dev_fd, &stage2_disk, device->type)
1305 != BC_SUCCESS) {
1306 BOOT_DEBUG("Unable to read stage2 from %s\n", device->path);
1307 BOOT_DEBUG("No multiboot wrapped stage2 on %s\n", device->path);
1308 return (B_TRUE);
1312 * Look for the extended information structure in the extra payload
1313 * area.
1315 einfo = find_einfo(stage2_disk.extra, stage2_disk.extra_size);
1316 if (einfo == NULL) {
1317 BOOT_DEBUG("No extended information available\n");
1318 return (B_TRUE);
1321 if (!do_version || updt_str == NULL) {
1322 (void) fprintf(stdout, "WARNING: target device %s has a "
1323 "versioned stage2 that is going to be overwritten by a non "
1324 "versioned one\n", device->path);
1325 return (B_TRUE);
1328 if (force_update) {
1329 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
1330 return (B_TRUE);
1333 /* Compare the two extended information structures. */
1334 stage2_hs.src_buf = (unsigned char *)stage2_file->file;
1335 stage2_hs.src_size = stage2_file->file_size;
1337 return (einfo_should_update(einfo, &stage2_hs, updt_str));
1341 #define START_BLOCK(pos) (*(ulong_t *)(pos))
1342 #define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4))
1343 #define START_SEG(pos) (*(ushort_t *)((pos) + 6))
1345 static int
1346 prepare_stage2(ig_data_t *install, char *updt_str)
1348 ig_device_t *device = &install->device;
1349 ig_stage2_t *stage2 = &install->stage2;
1350 uint32_t mboot_off = 0;
1352 assert(install != NULL);
1353 assert(stage2->file != NULL);
1355 /* New stage2 files come with an embedded stage2. */
1356 if (find_multiboot(stage2->file, stage2->file_size, &mboot_off)
1357 != BC_SUCCESS) {
1358 BOOT_DEBUG("WARNING: no multiboot structure found in stage2, "
1359 "are you using an old GRUB stage2?\n");
1360 if (do_version == B_TRUE) {
1361 (void) fprintf(stderr, gettext("Versioning requested "
1362 "but stage2 does not support it.. skipping.\n"));
1363 do_version = B_FALSE;
1365 } else {
1366 /* Keep track of where the multiboot header is. */
1367 stage2->mboot_off = mboot_off;
1368 stage2->mboot = (multiboot_header_t *)(stage2->file +
1369 mboot_off);
1370 if (do_version) {
1372 * Adding stage2 information needs to happen before
1373 * we modify the copy of stage2 we have in memory, so
1374 * that the hashing reflects the one of the file.
1375 * An error here is not fatal.
1377 add_stage2_einfo(stage2, updt_str);
1380 * Fill multiboot information. We add them even without
1381 * versioning to support as much as possible mirroring.
1383 prepare_fake_multiboot(stage2);
1386 if (is_bootpar(device->type)) {
1387 uint32_t blocklist[SECTOR_SIZE / sizeof (uint32_t)];
1388 uint32_t install_addr = STAGE2_MEMADDR + SECTOR_SIZE;
1389 int i = 0;
1390 uchar_t *pos;
1392 bzero(blocklist, sizeof (blocklist));
1393 if (read_stage2_blocklist(device->part_fd, blocklist) != 0) {
1394 (void) fprintf(stderr, gettext("Error reading pcfs "
1395 "stage2 blocklist\n"));
1396 return (BC_ERROR);
1399 pos = (uchar_t *)stage2->file + STAGE2_BLOCKLIST;
1400 stage2->first_sector = device->start_sector + blocklist[0];
1401 stage2->pcfs_first_sectors[0] = blocklist[0];
1402 BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
1405 if (blocklist[1] > 1) {
1406 blocklist[0]++;
1407 blocklist[1]--;
1408 } else {
1409 i += 2;
1412 stage2->pcfs_first_sectors[1] = blocklist[i];
1414 while (blocklist[i]) {
1415 if (START_BLOCK(pos - 8) != 0 &&
1416 START_BLOCK(pos - 8) != blocklist[i + 2]) {
1417 (void) fprintf(stderr, PCFS_FRAGMENTED);
1418 return (BC_ERROR);
1420 START_BLOCK(pos) = blocklist[i] + device->start_sector;
1421 START_SEG(pos) = (ushort_t)(install_addr >> 4);
1422 NUM_BLOCK(pos) = blocklist[i + 1];
1423 install_addr += blocklist[i + 1] * SECTOR_SIZE;
1424 pos -= 8;
1425 i += 2;
1427 } else {
1428 /* Solaris VTOC */
1429 stage2->first_sector = device->start_sector +
1430 STAGE2_BLKOFF(device->type);
1431 BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
1433 * In a solaris partition, stage2 is written to contiguous
1434 * blocks. So we update the starting block only.
1436 *((ulong_t *)(stage2->file + STAGE2_BLOCKLIST)) =
1437 stage2->first_sector + 1;
1440 /* force lba and set disk partition */
1441 *((unsigned char *) (stage2->file + STAGE2_FORCE_LBA)) = 1;
1442 *((long *)(stage2->file + STAGE2_INSTALLPART))
1443 = (device->partition << 16) | (device->slice << 8) | 0xff;
1445 return (BC_SUCCESS);
1448 static int
1449 find_x86_bootpar(struct mboot *mboot, int *part_num, uint32_t *start_sect)
1451 int i;
1453 for (i = 0; i < FD_NUMPART; i++) {
1454 struct ipart *part;
1456 part = (struct ipart *)mboot->parts + i;
1457 if (part->systid == 0xbe) {
1458 if (start_sect)
1459 *start_sect = part->relsect;
1460 if (part_num)
1461 *part_num = i;
1462 /* solaris boot part */
1463 return (BC_SUCCESS);
1466 return (BC_ERROR);
1469 static char *
1470 get_raw_partition_path(ig_device_t *device)
1472 char *raw;
1473 int len;
1475 if (is_bootpar(device->type)) {
1476 int part;
1477 struct mboot *mboot;
1479 mboot = (struct mboot *)device->boot_sector;
1480 if (find_x86_bootpar(mboot, &part, NULL) != BC_SUCCESS) {
1481 (void) fprintf(stderr, BOOTPAR_NOTFOUND,
1482 device->path_p0);
1483 return (NULL);
1486 raw = strdup(device->path_p0);
1487 if (raw == NULL) {
1488 perror(gettext("Memory allocation failed"));
1489 return (NULL);
1492 raw[strlen(raw) - 2] = '1' + part;
1493 return (raw);
1496 /* For disk, remember slice and return whole fdisk partition */
1497 raw = strdup(device->path);
1498 if (raw == NULL) {
1499 perror(gettext("Memory allocation failed"));
1500 return (NULL);
1503 len = strlen(raw);
1504 if (!is_efi(device->type) &&
1505 (raw[len - 2] != 's' || raw[len - 1] == '2')) {
1506 (void) fprintf(stderr, NOT_ROOT_SLICE);
1507 free(raw);
1508 return (NULL);
1510 device->slice = atoi(&raw[len - 1]);
1512 if (!is_efi(device->type)) {
1513 raw[len - 2] = 's';
1514 raw[len - 1] = '2';
1517 return (raw);
1520 static int
1521 get_raw_partition_fd(ig_device_t *device)
1523 struct stat stat = {0};
1524 char *raw;
1526 raw = get_raw_partition_path(device);
1527 if (raw == NULL)
1528 return (BC_ERROR);
1530 if (nowrite)
1531 device->part_fd = open(raw, O_RDONLY);
1532 else
1533 device->part_fd = open(raw, O_RDWR);
1535 if (device->part_fd < 0 || fstat(device->part_fd, &stat) != 0) {
1536 (void) fprintf(stderr, OPEN_FAIL, raw);
1537 free(raw);
1538 return (BC_ERROR);
1541 if (S_ISCHR(stat.st_mode) == 0) {
1542 (void) fprintf(stderr, NOT_RAW_DEVICE, raw);
1543 (void) close(device->part_fd);
1544 device->part_fd = -1;
1545 free(raw);
1546 return (BC_ERROR);
1549 free(raw);
1550 return (BC_SUCCESS);
1553 #define TMP_MNTPT "/tmp/installgrub_pcfs"
1554 static int
1555 copy_stage2_to_pcfs(ig_data_t *install)
1557 FILE *mntfp;
1558 int pcfs_fp;
1559 int status = BC_ERROR;
1560 char buf[SECTOR_SIZE];
1561 char *cp;
1562 struct mnttab mp = {0}, mpref = {0};
1563 ig_device_t *device = &install->device;
1564 ig_stage2_t *stage2 = &install->stage2;
1566 /* convert raw to block device name by removing the first 'r' */
1567 (void) strncpy(buf, device->path, sizeof (buf));
1568 buf[sizeof (buf) - 1] = 0;
1569 cp = strchr(buf, 'r');
1570 if (cp == NULL) {
1571 (void) fprintf(stderr, CONVERT_FAIL, device->path);
1572 return (BC_ERROR);
1574 do {
1575 *cp = *(cp + 1);
1576 } while (*(++cp));
1578 /* get the mount point, if any */
1579 mntfp = fopen("/etc/mnttab", "r");
1580 if (mntfp == NULL) {
1581 (void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab");
1582 return (BC_ERROR);
1585 mpref.mnt_special = buf;
1586 if (getmntany(mntfp, &mp, &mpref) != 0) {
1587 char cmd[128];
1589 /* not mounted, try remount */
1590 (void) mkdir(TMP_MNTPT, S_IRWXU);
1591 (void) snprintf(cmd, sizeof (cmd), "mount -F pcfs %s %s",
1592 buf, TMP_MNTPT);
1593 (void) system(cmd);
1594 rewind(mntfp);
1595 bzero(&mp, sizeof (mp));
1596 if (getmntany(mntfp, &mp, &mpref) != 0) {
1597 (void) fprintf(stderr, MOUNT_FAIL, buf);
1598 return (BC_ERROR);
1602 (void) snprintf(buf, sizeof (buf),
1603 "%s/boot", mp.mnt_mountp);
1604 (void) mkdir(buf, S_IRWXU);
1605 (void) strcat(buf, "/grub");
1606 (void) mkdir(buf, S_IRWXU);
1608 (void) strcat(buf, "/stage2");
1609 pcfs_fp = open(buf, O_WRONLY | O_CREAT, S_IRWXU);
1610 if (pcfs_fp == -1) {
1611 (void) fprintf(stderr, OPEN_FAIL_FILE, buf);
1612 perror("open:");
1613 goto out;
1616 /* write stage2 to the pcfs mounted filesystem. */
1617 if (write(pcfs_fp, stage2->file, stage2->file_size)
1618 != stage2->file_size) {
1619 perror(gettext("Error writing stage2"));
1620 goto out;
1623 status = BC_SUCCESS;
1624 out_fd:
1625 (void) close(pcfs_fp);
1626 out:
1627 (void) umount(TMP_MNTPT);
1628 (void) rmdir(TMP_MNTPT);
1629 return (status);