8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / boot / installgrub / installgrub.c
blob9d95b37274858eee5a687b6e9dad11f95c47009a
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 2016 Toomas Soome <tsoome@me.com>
25 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <libgen.h>
31 #include <malloc.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <strings.h>
36 #include <libintl.h>
37 #include <locale.h>
38 #include <errno.h>
39 #include <libfdisk.h>
40 #include <stdarg.h>
41 #include <assert.h>
43 #include <sys/mount.h>
44 #include <sys/mnttab.h>
45 #include <sys/dktp/fdisk.h>
46 #include <sys/dkio.h>
47 #include <sys/vtoc.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/multiboot.h>
51 #include <sys/sysmacros.h>
52 #include <sys/efi_partition.h>
54 #include <libnvpair.h>
55 #include <libfstyp.h>
57 #include "message.h"
58 #include "installgrub.h"
59 #include "./../common/bblk_einfo.h"
60 #include "./../common/boot_utils.h"
61 #include "./../common/mboot_extra.h"
62 #include "getresponse.h"
64 #ifndef TEXT_DOMAIN
65 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
66 #endif
69 * Variables to track installgrub desired mode of operation.
70 * 'nowrite' and 'boot_debug' come from boot_common.h.
72 static boolean_t write_mbr = B_FALSE;
73 static boolean_t force_mbr = B_FALSE;
74 static boolean_t force_update = B_FALSE;
75 static boolean_t do_getinfo = B_FALSE;
76 static boolean_t do_version = B_FALSE;
77 static boolean_t do_mirror_bblk = B_FALSE;
78 static boolean_t strip = B_FALSE;
79 static boolean_t verbose_dump = B_FALSE;
81 /* Installing the bootblock is the default operation. */
82 static boolean_t do_install = B_TRUE;
84 /* Versioning string, if present. */
85 static char *update_str;
88 * Temporary buffer to store the first 32K of data looking for a multiboot
89 * signature.
91 char mboot_scan[MBOOT_SCAN_SIZE];
93 /* Function prototypes. */
94 static void check_options(char *);
95 static int handle_install(char *, char **);
96 static int handle_mirror(char *, char **);
97 static int handle_getinfo(char *, char **);
98 static int commit_to_disk(ig_data_t *, char *);
99 static int init_device(ig_device_t *, char *path);
100 static void cleanup_device(ig_device_t *);
101 static void cleanup_stage2(ig_stage2_t *);
102 static int get_start_sector(ig_device_t *);
103 static int get_disk_fd(ig_device_t *device);
104 static int get_raw_partition_fd(ig_device_t *);
105 static char *get_raw_partition_path(ig_device_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 write_stage2(ig_data_t *);
109 static int write_stage1(ig_data_t *);
110 static void usage(char *);
111 static int read_stage1_from_file(char *, ig_data_t *);
112 static int read_stage2_from_file(char *, ig_data_t *);
113 static int read_stage1_from_disk(int, char *);
114 static int read_stage2_from_disk(int, ig_stage2_t *, int);
115 static int prepare_stage1(ig_data_t *);
116 static int prepare_stage2(ig_data_t *, char *);
117 static void prepare_fake_multiboot(ig_stage2_t *);
118 static void add_stage2_einfo(ig_stage2_t *, char *updt_str);
119 static boolean_t is_update_necessary(ig_data_t *, char *);
121 extern int read_stage2_blocklist(int, unsigned int *);
124 main(int argc, char *argv[])
126 int opt;
127 int params = 3;
128 int ret;
129 char **handle_args;
130 char *progname;
132 (void) setlocale(LC_ALL, "");
133 (void) textdomain(TEXT_DOMAIN);
134 if (init_yes() < 0) {
135 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
136 strerror(errno));
137 exit(BC_ERROR);
141 * retro-compatibility: installing the bootblock is the default
142 * and there is no switch for it.
144 do_install = B_TRUE;
146 while ((opt = getopt(argc, argv, "dVMFfmneiu:")) != EOF) {
147 switch (opt) {
148 case 'm':
149 write_mbr = B_TRUE;
150 break;
151 case 'n':
152 nowrite = B_TRUE;
153 break;
154 case 'f':
155 force_mbr = B_TRUE;
156 break;
157 case 'i':
158 do_getinfo = B_TRUE;
159 do_install = B_FALSE;
160 params = 1;
161 break;
162 case 'V':
163 verbose_dump = B_TRUE;
164 break;
165 case 'd':
166 boot_debug = B_TRUE;
167 break;
168 case 'F':
169 force_update = B_TRUE;
170 break;
171 case 'e':
172 strip = B_TRUE;
173 break;
174 case 'M':
175 do_mirror_bblk = B_TRUE;
176 do_install = B_FALSE;
177 params = 2;
178 break;
179 case 'u':
180 do_version = B_TRUE;
182 update_str = malloc(strlen(optarg) + 1);
183 if (update_str == NULL) {
184 (void) fprintf(stderr, gettext("Unable to "
185 "allocate memory\n"));
186 exit(BC_ERROR);
188 (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
189 break;
190 default:
191 /* fall through to process non-optional args */
192 break;
196 /* check arguments */
197 if (argc != optind + params) {
198 usage(argv[0]);
199 exit(BC_ERROR);
203 * clean up options (and bail out if an unrecoverable combination is
204 * requested.
206 progname = argv[0];
207 check_options(progname);
208 handle_args = argv + optind;
210 if (nowrite)
211 (void) fprintf(stdout, DRY_RUN);
213 if (do_getinfo) {
214 ret = handle_getinfo(progname, handle_args);
215 } else if (do_mirror_bblk) {
216 ret = handle_mirror(progname, handle_args);
217 } else {
218 ret = handle_install(progname, handle_args);
220 return (ret);
223 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
224 static void
225 check_options(char *progname)
227 if (do_getinfo && do_mirror_bblk) {
228 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
229 "specified at the same time\n"));
230 usage(progname);
231 exit(BC_ERROR);
234 if (do_mirror_bblk) {
236 * -u and -F may actually reflect a user intent that is not
237 * correct with this command (mirror can be interpreted
238 * "similar" to install. Emit a message and continue.
239 * -e and -V have no meaning, be quiet here and only report the
240 * incongruence if a debug output is requested.
242 if (do_version) {
243 (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
244 do_version = B_FALSE;
246 if (force_update) {
247 (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
248 force_update = B_FALSE;
250 if (strip || verbose_dump) {
251 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
252 strip = B_FALSE;
253 verbose_dump = B_FALSE;
257 if (do_getinfo) {
258 if (write_mbr || force_mbr || do_version || force_update) {
259 BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
260 write_mbr = force_mbr = do_version = B_FALSE;
261 force_update = B_FALSE;
267 * Install a new stage1/stage2 pair on the specified device. handle_install()
268 * expects argv to contain 3 parameters (the path to stage1, the path to stage2,
269 * the target device).
271 * Returns: BC_SUCCESS - if the installation is successful
272 * BC_ERROR - if the installation failed
273 * BC_NOUPDT - if no installation was performed because the GRUB
274 * version currently installed is more recent than the
275 * supplied one.
278 static int
279 handle_install(char *progname, char **argv)
281 ig_data_t install_data;
282 char *stage1_path = NULL;
283 char *stage2_path = NULL;
284 char *device_path = NULL;
285 int ret = BC_ERROR;
287 stage1_path = strdup(argv[0]);
288 stage2_path = strdup(argv[1]);
289 device_path = strdup(argv[2]);
291 bzero(&install_data, sizeof (ig_data_t));
293 if (!stage1_path || !stage2_path || !device_path) {
294 (void) fprintf(stderr, gettext("Missing parameter"));
295 usage(progname);
296 goto out;
299 BOOT_DEBUG("stage1 path: %s, stage2 path: %s, device: %s\n",
300 stage1_path, stage2_path, device_path);
302 if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
303 (void) fprintf(stderr, gettext("Unable to gather device "
304 "information for %s\n"), device_path);
305 goto out;
308 /* read in stage1 and stage2. */
309 if (read_stage1_from_file(stage1_path, &install_data) != BC_SUCCESS) {
310 (void) fprintf(stderr, gettext("Error opening %s\n"),
311 stage1_path);
312 goto out_dev;
315 if (read_stage2_from_file(stage2_path, &install_data) != BC_SUCCESS) {
316 (void) fprintf(stderr, gettext("Error opening %s\n"),
317 stage2_path);
318 goto out_dev;
321 /* We do not support versioning on PCFS. */
322 if (is_bootpar(install_data.device.type) && do_version)
323 do_version = B_FALSE;
326 * is_update_necessary() will take care of checking if versioning and/or
327 * forcing the update have been specified. It will also emit a warning
328 * if a non-versioned update is attempted over a versioned bootblock.
330 if (!is_update_necessary(&install_data, update_str)) {
331 (void) fprintf(stderr, gettext("GRUB version installed "
332 "on %s is more recent or identical\n"
333 "Use -F to override or install without the -u option\n"),
334 device_path);
335 ret = BC_NOUPDT;
336 goto out_dev;
339 * We get here if:
340 * - the installed GRUB version is older than the one about to be
341 * installed.
342 * - no versioning string has been passed through the command line.
343 * - a forced update is requested (-F).
345 BOOT_DEBUG("Ready to commit to disk\n");
346 ret = commit_to_disk(&install_data, update_str);
348 out_dev:
349 cleanup_device(&install_data.device);
350 out:
351 free(stage1_path);
352 free(stage2_path);
353 free(device_path);
354 return (ret);
358 * Retrieves from a device the extended information (einfo) associated to the
359 * installed stage2.
360 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
361 * Returns:
362 * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
363 * - BC_ERROR (on error)
364 * - BC_NOEINFO (no extended information available)
366 static int
367 handle_getinfo(char *progname, char **argv)
369 ig_data_t data;
370 ig_stage2_t *stage2 = &data.stage2;
371 ig_device_t *device = &data.device;
372 bblk_einfo_t *einfo;
373 uint8_t flags = 0;
374 uint32_t size;
375 char *device_path;
376 int retval = BC_ERROR;
377 int ret;
379 device_path = strdup(argv[0]);
380 if (!device_path) {
381 (void) fprintf(stderr, gettext("Missing parameter"));
382 usage(progname);
383 goto out;
386 bzero(&data, sizeof (ig_data_t));
387 BOOT_DEBUG("device path: %s\n", device_path);
389 if (init_device(device, device_path) != BC_SUCCESS) {
390 (void) fprintf(stderr, gettext("Unable to gather device "
391 "information for %s\n"), device_path);
392 goto out_dev;
395 if (is_bootpar(device->type)) {
396 (void) fprintf(stderr, gettext("Versioning not supported on "
397 "PCFS\n"));
398 goto out_dev;
401 ret = read_stage2_from_disk(device->part_fd, stage2, device->type);
402 if (ret == BC_ERROR) {
403 (void) fprintf(stderr, gettext("Error reading stage2 from "
404 "%s\n"), device_path);
405 goto out_dev;
408 if (ret == BC_NOEXTRA) {
409 (void) fprintf(stdout, gettext("No multiboot header found on "
410 "%s, unable to locate extra information area\n"),
411 device_path);
412 retval = BC_NOEINFO;
413 goto out_dev;
416 einfo = find_einfo(stage2->extra, stage2->extra_size);
417 if (einfo == NULL) {
418 retval = BC_NOEINFO;
419 (void) fprintf(stderr, gettext("No extended information "
420 "found\n"));
421 goto out_dev;
424 /* Print the extended information. */
425 if (strip)
426 flags |= EINFO_EASY_PARSE;
427 if (verbose_dump)
428 flags |= EINFO_PRINT_HEADER;
430 size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
431 print_einfo(flags, einfo, size);
432 retval = BC_SUCCESS;
434 out_dev:
435 cleanup_device(&data.device);
436 out:
437 free(device_path);
438 return (retval);
442 * Attempt to mirror (propagate) the current stage2 over the attaching disk.
444 * Returns:
445 * - BC_SUCCESS (a successful propagation happened)
446 * - BC_ERROR (an error occurred)
447 * - BC_NOEXTRA (it is not possible to dump the current bootblock since
448 * there is no multiboot information)
450 static int
451 handle_mirror(char *progname, char **argv)
453 ig_data_t curr_data;
454 ig_data_t attach_data;
455 ig_device_t *curr_device = &curr_data.device;
456 ig_device_t *attach_device = &attach_data.device;
457 ig_stage2_t *stage2_curr = &curr_data.stage2;
458 ig_stage2_t *stage2_attach = &attach_data.stage2;
459 bblk_einfo_t *einfo_curr = NULL;
460 char *curr_device_path;
461 char *attach_device_path;
462 char *updt_str = NULL;
463 int retval = BC_ERROR;
464 int ret;
466 curr_device_path = strdup(argv[0]);
467 attach_device_path = strdup(argv[1]);
469 if (!curr_device_path || !attach_device_path) {
470 (void) fprintf(stderr, gettext("Missing parameter"));
471 usage(progname);
472 goto out;
474 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
475 " %s\n", curr_device_path, attach_device_path);
477 bzero(&curr_data, sizeof (ig_data_t));
478 bzero(&attach_data, sizeof (ig_data_t));
480 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
481 (void) fprintf(stderr, gettext("Unable to gather device "
482 "information for %s (current device)\n"), curr_device_path);
483 goto out_currdev;
486 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
487 (void) fprintf(stderr, gettext("Unable to gather device "
488 "information for %s (attaching device)\n"),
489 attach_device_path);
490 goto out_devs;
493 if (is_bootpar(curr_device->type) || is_bootpar(attach_device->type)) {
494 (void) fprintf(stderr, gettext("boot block mirroring is not "
495 "supported on PCFS\n"));
496 goto out_devs;
499 ret = read_stage2_from_disk(curr_device->part_fd, stage2_curr,
500 curr_device->type);
501 if (ret == BC_ERROR) {
502 BOOT_DEBUG("Error reading first stage2 blocks from %s\n",
503 curr_device->path);
504 retval = BC_ERROR;
505 goto out_devs;
508 if (ret == BC_NOEXTRA) {
509 BOOT_DEBUG("No multiboot header found on %s, unable to grab "
510 "stage2\n", curr_device->path);
511 retval = BC_NOEXTRA;
512 goto out_devs;
515 einfo_curr = find_einfo(stage2_curr->extra, stage2_curr->extra_size);
516 if (einfo_curr != NULL)
517 updt_str = einfo_get_string(einfo_curr);
519 write_mbr = B_TRUE;
520 force_mbr = B_TRUE;
521 retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
522 cleanup_stage2(stage2_curr);
523 cleanup_stage2(stage2_attach);
525 out_devs:
526 cleanup_device(attach_device);
527 out_currdev:
528 cleanup_device(curr_device);
529 out:
530 free(curr_device_path);
531 free(attach_device_path);
532 return (retval);
535 static int
536 commit_to_disk(ig_data_t *install, char *updt_str)
538 assert(install != NULL);
540 * vanilla stage1 and stage2 need to be updated at runtime.
541 * Update stage2 before stage1 because stage1 needs to know the first
542 * sector stage2 will be written to.
544 if (prepare_stage2(install, updt_str) != BC_SUCCESS) {
545 (void) fprintf(stderr, gettext("Error building stage2\n"));
546 return (BC_ERROR);
548 if (prepare_stage1(install) != BC_SUCCESS) {
549 (void) fprintf(stderr, gettext("Error building stage1\n"));
550 return (BC_ERROR);
553 /* Write stage2 out to disk. */
554 if (write_stage2(install) != BC_SUCCESS) {
555 (void) fprintf(stderr, gettext("Error writing stage2 to "
556 "disk\n"));
557 return (BC_ERROR);
560 /* Write stage1 to disk and, if requested, to the MBR. */
561 if (write_stage1(install) != BC_SUCCESS) {
562 (void) fprintf(stderr, gettext("Error writing stage1 to "
563 "disk\n"));
564 return (BC_ERROR);
567 return (BC_SUCCESS);
571 * Propagate the bootblock on the source disk to the destination disk and
572 * version it with 'updt_str' in the process. Since we cannot trust any data
573 * on the attaching disk, we do not perform any specific check on a potential
574 * target extended information structure and we just blindly update.
576 static int
577 propagate_bootblock(ig_data_t *source, ig_data_t *target, char *updt_str)
579 ig_device_t *src_device = &source->device;
580 ig_device_t *dest_device = &target->device;
581 ig_stage2_t *src_stage2 = &source->stage2;
582 ig_stage2_t *dest_stage2 = &target->stage2;
583 uint32_t buf_size;
584 int retval;
586 assert(source != NULL);
587 assert(target != NULL);
589 /* read in stage1 from the source disk. */
590 if (read_stage1_from_disk(src_device->part_fd, target->stage1_buf)
591 != BC_SUCCESS)
592 return (BC_ERROR);
594 /* Prepare target stage2 for commit_to_disk. */
595 cleanup_stage2(dest_stage2);
597 if (updt_str != NULL)
598 do_version = B_TRUE;
599 else
600 do_version = B_FALSE;
602 buf_size = src_stage2->file_size + SECTOR_SIZE;
604 dest_stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
605 dest_stage2->buf = malloc(dest_stage2->buf_size);
606 if (dest_stage2->buf == NULL) {
607 perror(gettext("Memory allocation failed"));
608 return (BC_ERROR);
610 dest_stage2->file = dest_stage2->buf;
611 dest_stage2->file_size = src_stage2->file_size;
612 memcpy(dest_stage2->file, src_stage2->file, dest_stage2->file_size);
613 dest_stage2->extra = dest_stage2->buf +
614 P2ROUNDUP(dest_stage2->file_size, 8);
616 /* If we get down here we do have a mboot structure. */
617 assert(src_stage2->mboot);
619 dest_stage2->mboot_off = src_stage2->mboot_off;
620 dest_stage2->mboot = (multiboot_header_t *)(dest_stage2->buf +
621 dest_stage2->mboot_off);
623 (void) fprintf(stdout, gettext("Propagating %s stage1/stage2 to %s\n"),
624 src_device->path, dest_device->path);
625 retval = commit_to_disk(target, updt_str);
627 return (retval);
631 * open the device and fill the various members of ig_device_t.
633 static int
634 init_device(ig_device_t *device, char *path)
636 struct dk_gpt *vtoc;
637 fstyp_handle_t fhdl;
638 const char *fident;
640 bzero(device, sizeof (*device));
641 device->part_fd = -1;
642 device->disk_fd = -1;
643 device->path_p0 = NULL;
645 device->path = strdup(path);
646 if (device->path == NULL) {
647 perror(gettext("Memory allocation failed"));
648 return (BC_ERROR);
651 if (strstr(device->path, "diskette")) {
652 (void) fprintf(stderr, gettext("installing GRUB to a floppy "
653 "disk is no longer supported\n"));
654 return (BC_ERROR);
657 /* Detect if the target device is a pcfs partition. */
658 if (strstr(device->path, "p0:boot"))
659 device->type = IG_DEV_X86BOOTPAR;
661 if (get_disk_fd(device) != BC_SUCCESS)
662 return (BC_ERROR);
664 /* read in the device boot sector. */
665 if (read(device->disk_fd, device->boot_sector, SECTOR_SIZE)
666 != SECTOR_SIZE) {
667 (void) fprintf(stderr, gettext("Error reading boot sector\n"));
668 perror("read");
669 return (BC_ERROR);
672 if (efi_alloc_and_read(device->disk_fd, &vtoc) >= 0) {
673 device->type = IG_DEV_EFI;
674 efi_free(vtoc);
677 if (get_raw_partition_fd(device) != BC_SUCCESS)
678 return (BC_ERROR);
680 if (is_efi(device->type)) {
681 if (fstyp_init(device->part_fd, 0, NULL, &fhdl) != 0)
682 return (BC_ERROR);
684 if (fstyp_ident(fhdl, "zfs", &fident) != 0) {
685 fstyp_fini(fhdl);
686 (void) fprintf(stderr, gettext("Booting of EFI labeled "
687 "disks is only supported with ZFS\n"));
688 return (BC_ERROR);
690 fstyp_fini(fhdl);
693 if (get_start_sector(device) != BC_SUCCESS)
694 return (BC_ERROR);
696 return (BC_SUCCESS);
699 static void
700 cleanup_device(ig_device_t *device)
702 if (device->path)
703 free(device->path);
704 if (device->path_p0)
705 free(device->path_p0);
707 if (device->part_fd != -1)
708 (void) close(device->part_fd);
709 if (device->disk_fd != -1)
710 (void) close(device->disk_fd);
712 bzero(device, sizeof (ig_device_t));
713 device->part_fd = -1;
714 device->disk_fd = -1;
717 static void
718 cleanup_stage2(ig_stage2_t *stage2)
720 if (stage2->buf)
721 free(stage2->buf);
722 bzero(stage2, sizeof (ig_stage2_t));
725 static int
726 get_start_sector(ig_device_t *device)
728 uint32_t secnum = 0, numsec = 0;
729 int i, pno, rval, log_part = 0;
730 struct mboot *mboot;
731 struct ipart *part = NULL;
732 ext_part_t *epp;
733 struct part_info dkpi;
734 struct extpart_info edkpi;
736 if (is_efi(device->type)) {
737 struct dk_gpt *vtoc;
739 if (efi_alloc_and_read(device->disk_fd, &vtoc) < 0)
740 return (BC_ERROR);
742 device->start_sector = vtoc->efi_parts[device->slice].p_start;
743 /* GPT doesn't use traditional slice letters */
744 device->slice = 0xff;
745 device->partition = 0;
747 efi_free(vtoc);
748 goto found_part;
751 mboot = (struct mboot *)device->boot_sector;
753 if (is_bootpar(device->type)) {
754 if (find_x86_bootpar(mboot, &pno, &secnum) != BC_SUCCESS) {
755 (void) fprintf(stderr, NOBOOTPAR);
756 return (BC_ERROR);
757 } else {
758 device->start_sector = secnum;
759 device->partition = pno;
760 goto found_part;
765 * Search for Solaris fdisk partition
766 * Get the solaris partition information from the device
767 * and compare the offset of S2 with offset of solaris partition
768 * from fdisk partition table.
770 if (ioctl(device->part_fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
771 if (ioctl(device->part_fd, DKIOCPARTINFO, &dkpi) < 0) {
772 (void) fprintf(stderr, PART_FAIL);
773 return (BC_ERROR);
774 } else {
775 edkpi.p_start = dkpi.p_start;
779 for (i = 0; i < FD_NUMPART; i++) {
780 part = (struct ipart *)mboot->parts + i;
782 if (part->relsect == 0) {
783 (void) fprintf(stderr, BAD_PART, i);
784 return (BC_ERROR);
787 if (edkpi.p_start >= part->relsect &&
788 edkpi.p_start < (part->relsect + part->numsect)) {
789 /* Found the partition */
790 break;
794 if (i == FD_NUMPART) {
795 /* No solaris fdisk partitions (primary or logical) */
796 (void) fprintf(stderr, NOSOLPAR);
797 return (BC_ERROR);
801 * We have found a Solaris fdisk partition (primary or extended)
802 * Handle the simple case first: Solaris in a primary partition
804 if (!fdisk_is_dos_extended(part->systid)) {
805 device->start_sector = part->relsect;
806 device->partition = i;
807 goto found_part;
811 * Solaris in a logical partition. Find that partition in the
812 * extended part.
814 if ((rval = libfdisk_init(&epp, device->path_p0, NULL, FDISK_READ_DISK))
815 != FDISK_SUCCESS) {
816 switch (rval) {
818 * The first 3 cases are not an error per-se, just that
819 * there is no Solaris logical partition
821 case FDISK_EBADLOGDRIVE:
822 case FDISK_ENOLOGDRIVE:
823 case FDISK_EBADMAGIC:
824 (void) fprintf(stderr, NOSOLPAR);
825 return (BC_ERROR);
826 case FDISK_ENOVGEOM:
827 (void) fprintf(stderr, NO_VIRT_GEOM);
828 return (BC_ERROR);
829 case FDISK_ENOPGEOM:
830 (void) fprintf(stderr, NO_PHYS_GEOM);
831 return (BC_ERROR);
832 case FDISK_ENOLGEOM:
833 (void) fprintf(stderr, NO_LABEL_GEOM);
834 return (BC_ERROR);
835 default:
836 (void) fprintf(stderr, LIBFDISK_INIT_FAIL);
837 return (BC_ERROR);
841 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
842 libfdisk_fini(&epp);
843 if (rval != FDISK_SUCCESS) {
844 /* No solaris logical partition */
845 (void) fprintf(stderr, NOSOLPAR);
846 return (BC_ERROR);
849 device->start_sector = secnum;
850 device->partition = pno - 1;
851 log_part = 1;
853 found_part:
854 /* get confirmation for -m */
855 if (write_mbr && !force_mbr) {
856 (void) fprintf(stdout, MBOOT_PROMPT);
857 if (!yes()) {
858 write_mbr = 0;
859 (void) fprintf(stdout, MBOOT_NOT_UPDATED);
860 return (BC_ERROR);
865 * Currently if Solaris is in an extended partition we need to
866 * write GRUB to the MBR. Check for this.
868 if (log_part && !write_mbr) {
869 (void) fprintf(stdout, gettext("Installing Solaris on an "
870 "extended partition... forcing MBR update\n"));
871 write_mbr = 1;
875 * warn, if Solaris in primary partition and GRUB not in MBR and
876 * partition is not active
878 if (part != NULL) {
879 if (!log_part && part->bootid != 128 && !write_mbr) {
880 (void) fprintf(stdout, SOLPAR_INACTIVE,
881 device->partition + 1);
885 return (BC_SUCCESS);
888 static int
889 get_disk_fd(ig_device_t *device)
891 int i = 0;
892 char save[2] = { '\0', '\0' };
893 char *end = NULL;
895 assert(device != NULL);
896 assert(device->path != NULL);
898 if (is_bootpar(device->type)) {
899 end = strstr(device->path, "p0:boot");
900 /* tested at the start of init_device() */
901 assert(end != NULL);
902 /* chop off :boot */
903 save[0] = end[2];
904 end[2] = '\0';
905 } else {
906 i = strlen(device->path);
907 save[0] = device->path[i - 2];
908 save[1] = device->path[i - 1];
909 device->path[i - 2] = 'p';
910 device->path[i - 1] = '0';
913 if (nowrite)
914 device->disk_fd = open(device->path, O_RDONLY);
915 else
916 device->disk_fd = open(device->path, O_RDWR);
918 device->path_p0 = strdup(device->path);
919 if (device->path_p0 == NULL) {
920 perror("strdup");
921 return (BC_ERROR);
924 if (is_bootpar(device->type)) {
925 end[2] = save[0];
926 } else {
927 device->path[i - 2] = save[0];
928 device->path[i - 1] = save[1];
931 if (device->disk_fd == -1) {
932 perror("open");
933 return (BC_ERROR);
936 return (BC_SUCCESS);
939 static void
940 prepare_fake_multiboot(ig_stage2_t *stage2)
942 multiboot_header_t *mboot;
944 assert(stage2 != NULL);
945 assert(stage2->mboot != NULL);
946 assert(stage2->buf != NULL);
948 mboot = stage2->mboot;
951 * Currently we expect find_multiboot() to have located a multiboot
952 * header with the AOUT kludge flag set.
954 assert(mboot->flags & BB_MBOOT_AOUT_FLAG);
956 /* Insert the information necessary to locate stage2. */
957 mboot->header_addr = stage2->mboot_off;
958 mboot->load_addr = 0;
959 mboot->load_end_addr = stage2->file_size;
962 static void
963 add_stage2_einfo(ig_stage2_t *stage2, char *updt_str)
965 bblk_hs_t hs;
966 uint32_t avail_space;
968 assert(stage2 != NULL);
970 /* Fill bootblock hashing source information. */
971 hs.src_buf = (unsigned char *)stage2->file;
972 hs.src_size = stage2->file_size;
973 /* How much space for the extended information structure? */
974 avail_space = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
975 add_einfo(stage2->extra, updt_str, &hs, avail_space);
979 static int
980 write_stage2(ig_data_t *install)
982 ig_device_t *device = &install->device;
983 ig_stage2_t *stage2 = &install->stage2;
984 off_t offset;
986 assert(install != NULL);
988 if (is_bootpar(device->type)) {
990 * stage2 is already on the filesystem, we only need to update
991 * the first two blocks (that we have modified during
992 * prepare_stage2())
994 if (write_out(device->part_fd, stage2->file, SECTOR_SIZE,
995 stage2->pcfs_first_sectors[0] * SECTOR_SIZE)
996 != BC_SUCCESS ||
997 write_out(device->part_fd, stage2->file + SECTOR_SIZE,
998 SECTOR_SIZE, stage2->pcfs_first_sectors[1] * SECTOR_SIZE)
999 != BC_SUCCESS) {
1000 (void) fprintf(stderr, WRITE_FAIL_STAGE2);
1001 return (BC_ERROR);
1003 (void) fprintf(stdout, WRITE_STAGE2_PCFS);
1004 return (BC_SUCCESS);
1008 * For disk, write stage2 starting at STAGE2_BLKOFF sector.
1009 * Note that we use stage2->buf rather than stage2->file, because we
1010 * may have extended information after the latter.
1012 * If we're writing to an EFI-labeled disk where stage2 lives in the
1013 * 3.5MB boot loader gap following the ZFS vdev labels, make sure the
1014 * size of the buffer doesn't exceed the size of the gap.
1016 if (is_efi(device->type) && stage2->buf_size > STAGE2_MAXSIZE) {
1017 (void) fprintf(stderr, WRITE_FAIL_STAGE2);
1018 return (BC_ERROR);
1021 offset = STAGE2_BLKOFF(device->type) * SECTOR_SIZE;
1023 if (write_out(device->part_fd, stage2->buf, stage2->buf_size,
1024 offset) != BC_SUCCESS) {
1025 perror("write");
1026 return (BC_ERROR);
1029 /* Simulate the "old" installgrub output. */
1030 (void) fprintf(stdout, WRITE_STAGE2_DISK, device->partition,
1031 (stage2->buf_size / SECTOR_SIZE) + 1, STAGE2_BLKOFF(device->type),
1032 stage2->first_sector);
1034 return (BC_SUCCESS);
1037 static int
1038 write_stage1(ig_data_t *install)
1040 ig_device_t *device = &install->device;
1042 assert(install != NULL);
1044 if (write_out(device->part_fd, install->stage1_buf,
1045 sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
1046 (void) fprintf(stdout, WRITE_FAIL_PBOOT);
1047 perror("write");
1048 return (BC_ERROR);
1051 /* Simulate "old" installgrub output. */
1052 (void) fprintf(stdout, WRITE_PBOOT, device->partition,
1053 device->start_sector);
1055 if (write_mbr) {
1056 if (write_out(device->disk_fd, install->stage1_buf,
1057 sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
1058 (void) fprintf(stdout, WRITE_FAIL_BOOTSEC);
1059 perror("write");
1060 return (BC_ERROR);
1062 /* Simulate "old" installgrub output. */
1063 (void) fprintf(stdout, WRITE_MBOOT);
1066 return (BC_SUCCESS);
1069 #define USAGE_STRING "%s [-m|-f|-n|-F|-u verstr] stage1 stage2 device\n" \
1070 "%s -M [-n] device1 device2\n" \
1071 "%s [-V|-e] -i device\n" \
1073 #define CANON_USAGE_STR gettext(USAGE_STRING)
1075 static void
1076 usage(char *progname)
1078 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
1082 static int
1083 read_stage1_from_file(char *path, ig_data_t *dest)
1085 int fd;
1087 assert(dest);
1089 /* read the stage1 file from filesystem */
1090 fd = open(path, O_RDONLY);
1091 if (fd == -1 ||
1092 read(fd, dest->stage1_buf, SECTOR_SIZE) != SECTOR_SIZE) {
1093 (void) fprintf(stderr, READ_FAIL_STAGE1, path);
1094 return (BC_ERROR);
1096 (void) close(fd);
1097 return (BC_SUCCESS);
1100 static int
1101 read_stage2_from_file(char *path, ig_data_t *dest)
1103 int fd;
1104 struct stat sb;
1105 ig_stage2_t *stage2 = &dest->stage2;
1106 ig_device_t *device = &dest->device;
1107 uint32_t buf_size;
1109 assert(dest);
1110 assert(stage2->buf == NULL);
1112 fd = open(path, O_RDONLY);
1113 if (fstat(fd, &sb) == -1) {
1114 perror("fstat");
1115 goto out;
1118 stage2->file_size = sb.st_size;
1120 if (!is_bootpar(device->type)) {
1122 * buffer size needs to account for stage2 plus the extra
1123 * versioning information at the end of it. We reserve one
1124 * extra sector (plus we round up to the next sector boundary).
1126 buf_size = stage2->file_size + SECTOR_SIZE;
1127 } else {
1128 /* In the PCFS case we only need to read in stage2. */
1129 buf_size = stage2->file_size;
1132 stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
1134 BOOT_DEBUG("stage2 buffer size = %d (%d sectors)\n", stage2->buf_size,
1135 stage2->buf_size / SECTOR_SIZE);
1137 stage2->buf = malloc(stage2->buf_size);
1138 if (stage2->buf == NULL) {
1139 perror(gettext("Memory allocation failed"));
1140 goto out_fd;
1143 stage2->file = stage2->buf;
1146 * Extra information (e.g. the versioning structure) is placed at the
1147 * end of stage2, aligned on a 8-byte boundary.
1149 if (!(is_bootpar(device->type)))
1150 stage2->extra = stage2->file + P2ROUNDUP(stage2->file_size, 8);
1152 if (lseek(fd, 0, SEEK_SET) == -1) {
1153 perror("lseek");
1154 goto out_alloc;
1157 if (read(fd, stage2->file, stage2->file_size) < 0) {
1158 perror(gettext("unable to read stage2"));
1159 goto out_alloc;
1162 (void) close(fd);
1163 return (BC_SUCCESS);
1165 out_alloc:
1166 free(stage2->buf);
1167 stage2->buf = NULL;
1168 out_fd:
1169 (void) close(fd);
1170 out:
1171 return (BC_ERROR);
1174 static int
1175 prepare_stage1(ig_data_t *install)
1177 ig_device_t *device = &install->device;
1179 assert(install != NULL);
1181 /* If PCFS add the BIOS Parameter Block. */
1182 if (is_bootpar(device->type)) {
1183 char bpb_sect[SECTOR_SIZE];
1185 if (pread(device->part_fd, bpb_sect, SECTOR_SIZE, 0)
1186 != SECTOR_SIZE) {
1187 (void) fprintf(stderr, READ_FAIL_BPB);
1188 return (BC_ERROR);
1190 bcopy(bpb_sect + STAGE1_BPB_OFFSET,
1191 install->stage1_buf + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
1194 /* copy MBR to stage1 in case of overwriting MBR sector. */
1195 bcopy(device->boot_sector + BOOTSZ, install->stage1_buf + BOOTSZ,
1196 SECTOR_SIZE - BOOTSZ);
1197 /* modify default stage1 file generated by GRUB. */
1198 *((unsigned char *)(install->stage1_buf + STAGE1_FORCE_LBA)) = 1;
1199 *((ulong_t *)(install->stage1_buf + STAGE1_STAGE2_SECTOR))
1200 = install->stage2.first_sector;
1201 *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_ADDRESS))
1202 = STAGE2_MEMADDR;
1203 *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_SEGMENT))
1204 = STAGE2_MEMADDR >> 4;
1206 return (BC_SUCCESS);
1210 * Grab stage1 from the specified device file descriptor.
1212 static int
1213 read_stage1_from_disk(int dev_fd, char *stage1_buf)
1215 assert(stage1_buf != NULL);
1217 if (read_in(dev_fd, stage1_buf, SECTOR_SIZE, 0) != BC_SUCCESS) {
1218 perror(gettext("Unable to read stage1 from disk"));
1219 return (BC_ERROR);
1221 return (BC_SUCCESS);
1224 static int
1225 read_stage2_from_disk(int dev_fd, ig_stage2_t *stage2, int type)
1227 uint32_t size;
1228 uint32_t buf_size;
1229 uint32_t mboot_off;
1230 multiboot_header_t *mboot;
1232 assert(stage2 != NULL);
1233 assert(dev_fd != -1);
1235 if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
1236 STAGE2_BLKOFF(type) * SECTOR_SIZE) != BC_SUCCESS) {
1237 perror(gettext("Error reading stage2 sectors"));
1238 return (BC_ERROR);
1241 /* No multiboot means no chance of knowing stage2 size */
1242 if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
1243 != BC_SUCCESS) {
1244 BOOT_DEBUG("Unable to find multiboot header\n");
1245 return (BC_NOEXTRA);
1247 mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
1250 * Unfilled mboot values mean an older version of installgrub installed
1251 * the stage2. Again we have no chance of knowing stage2 size.
1253 if (mboot->load_end_addr == 0 ||
1254 mboot->load_end_addr < mboot->load_addr)
1255 return (BC_NOEXTRA);
1258 * Currently, the amount of space reserved for extra information
1259 * is "fixed". We may have to scan for the terminating extra payload
1260 * in the future.
1262 size = mboot->load_end_addr - mboot->load_addr;
1263 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
1265 stage2->buf = malloc(buf_size);
1266 if (stage2->buf == NULL) {
1267 perror(gettext("Memory allocation failed"));
1268 return (BC_ERROR);
1270 stage2->buf_size = buf_size;
1272 if (read_in(dev_fd, stage2->buf, buf_size, STAGE2_BLKOFF(type) *
1273 SECTOR_SIZE) != BC_SUCCESS) {
1274 perror("read");
1275 free(stage2->buf);
1276 return (BC_ERROR);
1279 /* Update pointers. */
1280 stage2->file = stage2->buf;
1281 stage2->file_size = size;
1282 stage2->mboot_off = mboot_off;
1283 stage2->mboot = (multiboot_header_t *)(stage2->buf + stage2->mboot_off);
1284 stage2->extra = stage2->buf + P2ROUNDUP(stage2->file_size, 8);
1285 stage2->extra_size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
1287 return (BC_SUCCESS);
1290 static boolean_t
1291 is_update_necessary(ig_data_t *data, char *updt_str)
1293 bblk_einfo_t *einfo;
1294 bblk_hs_t stage2_hs;
1295 ig_stage2_t stage2_disk;
1296 ig_stage2_t *stage2_file = &data->stage2;
1297 ig_device_t *device = &data->device;
1298 int dev_fd = device->part_fd;
1300 assert(data != NULL);
1301 assert(device->part_fd != -1);
1303 bzero(&stage2_disk, sizeof (ig_stage2_t));
1305 /* Gather stage2 (if present) from the target device. */
1306 if (read_stage2_from_disk(dev_fd, &stage2_disk, device->type)
1307 != BC_SUCCESS) {
1308 BOOT_DEBUG("Unable to read stage2 from %s\n", device->path);
1309 BOOT_DEBUG("No multiboot wrapped stage2 on %s\n", device->path);
1310 return (B_TRUE);
1314 * Look for the extended information structure in the extra payload
1315 * area.
1317 einfo = find_einfo(stage2_disk.extra, stage2_disk.extra_size);
1318 if (einfo == NULL) {
1319 BOOT_DEBUG("No extended information available\n");
1320 return (B_TRUE);
1323 if (!do_version || updt_str == NULL) {
1324 (void) fprintf(stdout, "WARNING: target device %s has a "
1325 "versioned stage2 that is going to be overwritten by a non "
1326 "versioned one\n", device->path);
1327 return (B_TRUE);
1330 if (force_update) {
1331 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
1332 return (B_TRUE);
1335 /* Compare the two extended information structures. */
1336 stage2_hs.src_buf = (unsigned char *)stage2_file->file;
1337 stage2_hs.src_size = stage2_file->file_size;
1339 return (einfo_should_update(einfo, &stage2_hs, updt_str));
1343 #define START_BLOCK(pos) (*(ulong_t *)(pos))
1344 #define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4))
1345 #define START_SEG(pos) (*(ushort_t *)((pos) + 6))
1347 static int
1348 prepare_stage2(ig_data_t *install, char *updt_str)
1350 ig_device_t *device = &install->device;
1351 ig_stage2_t *stage2 = &install->stage2;
1352 uint32_t mboot_off = 0;
1354 assert(install != NULL);
1355 assert(stage2->file != NULL);
1357 /* New stage2 files come with an embedded stage2. */
1358 if (find_multiboot(stage2->file, stage2->file_size, &mboot_off)
1359 != BC_SUCCESS) {
1360 BOOT_DEBUG("WARNING: no multiboot structure found in stage2, "
1361 "are you using an old GRUB stage2?\n");
1362 if (do_version == B_TRUE) {
1363 (void) fprintf(stderr, gettext("Versioning requested "
1364 "but stage2 does not support it.. skipping.\n"));
1365 do_version = B_FALSE;
1367 } else {
1368 /* Keep track of where the multiboot header is. */
1369 stage2->mboot_off = mboot_off;
1370 stage2->mboot = (multiboot_header_t *)(stage2->file +
1371 mboot_off);
1372 if (do_version) {
1374 * Adding stage2 information needs to happen before
1375 * we modify the copy of stage2 we have in memory, so
1376 * that the hashing reflects the one of the file.
1377 * An error here is not fatal.
1379 add_stage2_einfo(stage2, updt_str);
1382 * Fill multiboot information. We add them even without
1383 * versioning to support as much as possible mirroring.
1385 prepare_fake_multiboot(stage2);
1388 if (is_bootpar(device->type)) {
1389 uint32_t blocklist[SECTOR_SIZE / sizeof (uint32_t)];
1390 uint32_t install_addr = STAGE2_MEMADDR + SECTOR_SIZE;
1391 int i = 0;
1392 uchar_t *pos;
1394 bzero(blocklist, sizeof (blocklist));
1395 if (read_stage2_blocklist(device->part_fd, blocklist) != 0) {
1396 (void) fprintf(stderr, gettext("Error reading pcfs "
1397 "stage2 blocklist\n"));
1398 return (BC_ERROR);
1401 pos = (uchar_t *)stage2->file + STAGE2_BLOCKLIST;
1402 stage2->first_sector = device->start_sector + blocklist[0];
1403 stage2->pcfs_first_sectors[0] = blocklist[0];
1404 BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
1407 if (blocklist[1] > 1) {
1408 blocklist[0]++;
1409 blocklist[1]--;
1410 } else {
1411 i += 2;
1414 stage2->pcfs_first_sectors[1] = blocklist[i];
1416 while (blocklist[i]) {
1417 if (START_BLOCK(pos - 8) != 0 &&
1418 START_BLOCK(pos - 8) != blocklist[i + 2]) {
1419 (void) fprintf(stderr, PCFS_FRAGMENTED);
1420 return (BC_ERROR);
1422 START_BLOCK(pos) = blocklist[i] + device->start_sector;
1423 START_SEG(pos) = (ushort_t)(install_addr >> 4);
1424 NUM_BLOCK(pos) = blocklist[i + 1];
1425 install_addr += blocklist[i + 1] * SECTOR_SIZE;
1426 pos -= 8;
1427 i += 2;
1429 } else {
1430 /* Solaris VTOC & EFI */
1431 if (device->start_sector >
1432 UINT32_MAX - STAGE2_BLKOFF(device->type)) {
1433 fprintf(stderr, gettext("Error: partition start sector "
1434 "must be less than %lld\n"),
1435 (uint64_t)UINT32_MAX - STAGE2_BLKOFF(device->type));
1436 return (BC_ERROR);
1438 stage2->first_sector = device->start_sector +
1439 STAGE2_BLKOFF(device->type);
1440 BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
1442 * In a solaris partition, stage2 is written to contiguous
1443 * blocks. So we update the starting block only.
1445 *((ulong_t *)(stage2->file + STAGE2_BLOCKLIST)) =
1446 stage2->first_sector + 1;
1449 /* force lba and set disk partition */
1450 *((unsigned char *) (stage2->file + STAGE2_FORCE_LBA)) = 1;
1451 *((long *)(stage2->file + STAGE2_INSTALLPART))
1452 = (device->partition << 16) | (device->slice << 8) | 0xff;
1454 return (BC_SUCCESS);
1457 static int
1458 find_x86_bootpar(struct mboot *mboot, int *part_num, uint32_t *start_sect)
1460 int i;
1462 for (i = 0; i < FD_NUMPART; i++) {
1463 struct ipart *part;
1465 part = (struct ipart *)mboot->parts + i;
1466 if (part->systid == 0xbe) {
1467 if (start_sect)
1468 *start_sect = part->relsect;
1469 if (part_num)
1470 *part_num = i;
1471 /* solaris boot part */
1472 return (BC_SUCCESS);
1475 return (BC_ERROR);
1478 static char *
1479 get_raw_partition_path(ig_device_t *device)
1481 char *raw;
1482 int len;
1484 if (is_bootpar(device->type)) {
1485 int part;
1486 struct mboot *mboot;
1488 mboot = (struct mboot *)device->boot_sector;
1489 if (find_x86_bootpar(mboot, &part, NULL) != BC_SUCCESS) {
1490 (void) fprintf(stderr, BOOTPAR_NOTFOUND,
1491 device->path_p0);
1492 return (NULL);
1495 raw = strdup(device->path_p0);
1496 if (raw == NULL) {
1497 perror(gettext("Memory allocation failed"));
1498 return (NULL);
1501 raw[strlen(raw) - 2] = '1' + part;
1502 return (raw);
1505 /* For disk, remember slice and return whole fdisk partition */
1506 raw = strdup(device->path);
1507 if (raw == NULL) {
1508 perror(gettext("Memory allocation failed"));
1509 return (NULL);
1512 len = strlen(raw);
1513 if (!is_efi(device->type) &&
1514 (raw[len - 2] != 's' || raw[len - 1] == '2')) {
1515 (void) fprintf(stderr, NOT_ROOT_SLICE);
1516 free(raw);
1517 return (NULL);
1519 device->slice = atoi(&raw[len - 1]);
1521 if (!is_efi(device->type)) {
1522 raw[len - 2] = 's';
1523 raw[len - 1] = '2';
1526 return (raw);
1529 static int
1530 get_raw_partition_fd(ig_device_t *device)
1532 struct stat stat = {0};
1533 char *raw;
1535 raw = get_raw_partition_path(device);
1536 if (raw == NULL)
1537 return (BC_ERROR);
1539 if (nowrite)
1540 device->part_fd = open(raw, O_RDONLY);
1541 else
1542 device->part_fd = open(raw, O_RDWR);
1544 if (device->part_fd < 0 || fstat(device->part_fd, &stat) != 0) {
1545 (void) fprintf(stderr, OPEN_FAIL, raw);
1546 free(raw);
1547 return (BC_ERROR);
1550 if (S_ISCHR(stat.st_mode) == 0) {
1551 (void) fprintf(stderr, NOT_RAW_DEVICE, raw);
1552 (void) close(device->part_fd);
1553 device->part_fd = -1;
1554 free(raw);
1555 return (BC_ERROR);
1558 free(raw);
1559 return (BC_SUCCESS);