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]
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2017 Toomas Soome <tsoome@me.com>
36 #include <sys/dktp/fdisk.h>
39 #include <sys/multiboot.h>
40 #include <sys/types.h>
42 #include <sys/sysmacros.h>
43 #include <sys/efi_partition.h>
45 #include <uuid/uuid.h>
47 #include "installboot.h"
48 #include "../../common/bblk_einfo.h"
49 #include "../../common/boot_utils.h"
50 #include "../../common/mboot_extra.h"
51 #include "getresponse.h"
54 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
58 * BIOS bootblock installation:
60 * 1. MBR is first sector of the disk. If the file system on target is
61 * ufs or zfs, the same MBR code is installed on first sector of the
62 * partition as well; this will allow to have real MBR sector to be
63 * replaced by some other boot loader and have illumos chainloaded.
65 * installboot will record the start LBA and size of stage2 code in MBR code.
66 * On boot, the MBR code will read the stage2 code and executes it.
68 * 2. Stage2 location depends on file system type;
69 * In case of zfs, installboot will store stage2 to zfs bootblk area,
70 * which is 512k bytes from partition start and size is 3.5MB.
72 * In case of ufs, the stage2 location is 50 512B sectors from
73 * Solaris2 MBR partition start, within boot slice, boot slice size is
76 * In case of pcfs, the stage2 location is 50 512B sectors from beginning
77 * of the disk, filling the space between MBR and first partition.
78 * This location assumes no other bootloader and the space is one cylinder,
79 * as first partition is starting from cylinder 1.
81 * In case of GPT partitioning and if file system is not zfs, the boot
82 * support is only possible with dedicated boot partition. For GPT,
83 * the current implementation is using BOOT partition, which must exist.
84 * BOOT partition does only contain raw boot blocks, without any file system.
86 * Loader stage2 is created with embedded version, by using fake multiboot (MB)
87 * header within first 32k and EINFO block is at the end of the actual
88 * boot block. MB header load_addr is set to 0 and load_end_addr is set to
89 * actual block end, so the EINFO size is (file size - load_end_addr).
90 * installboot does also store the illumos boot partition LBA to MB space,
91 * starting from bss_end_addr structure member location; stage2 will
92 * detect the partition and file system based on this value.
94 * Stored location values in MBR/stage2 also mean the bootblocks must be
95 * reinstalled in case the partition content is relocated.
98 static boolean_t write_mbr
= B_FALSE
;
99 static boolean_t force_mbr
= B_FALSE
;
100 static boolean_t force_update
= B_FALSE
;
101 static boolean_t do_getinfo
= B_FALSE
;
102 static boolean_t do_version
= B_FALSE
;
103 static boolean_t do_mirror_bblk
= B_FALSE
;
104 static boolean_t strip
= B_FALSE
;
105 static boolean_t verbose_dump
= B_FALSE
;
107 /* Versioning string, if present. */
108 static char *update_str
;
111 * Temporary buffer to store the first 32K of data looking for a multiboot
114 char mboot_scan
[MBOOT_SCAN_SIZE
];
116 /* Function prototypes. */
117 static void check_options(char *);
118 static int get_start_sector(ib_device_t
*);
120 static int read_stage1_from_file(char *, ib_data_t
*);
121 static int read_bootblock_from_file(char *, ib_bootblock_t
*);
122 static int read_bootblock_from_disk(ib_device_t
*, ib_bootblock_t
*, char **);
123 static void add_bootblock_einfo(ib_bootblock_t
*, char *);
124 static int prepare_stage1(ib_data_t
*);
125 static int prepare_bootblock(ib_data_t
*, char *);
126 static int write_stage1(ib_data_t
*);
127 static int write_bootblock(ib_data_t
*);
128 static int init_device(ib_device_t
*, char *);
129 static void cleanup_device(ib_device_t
*);
130 static int commit_to_disk(ib_data_t
*, char *);
131 static int handle_install(char *, char **);
132 static int handle_getinfo(char *, char **);
133 static int handle_mirror(char *, char **);
134 static boolean_t
is_update_necessary(ib_data_t
*, char *);
135 static int propagate_bootblock(ib_data_t
*, ib_data_t
*, char *);
136 static void usage(char *);
139 read_stage1_from_file(char *path
, ib_data_t
*dest
)
143 assert(dest
!= NULL
);
145 /* read the stage1 file from filesystem */
146 fd
= open(path
, O_RDONLY
);
148 read(fd
, dest
->stage1
, SECTOR_SIZE
) != SECTOR_SIZE
) {
149 (void) fprintf(stderr
, gettext("cannot read stage1 file %s\n"),
158 read_bootblock_from_file(char *file
, ib_bootblock_t
*bblock
)
164 int retval
= BC_ERROR
;
166 assert(bblock
!= NULL
);
167 assert(file
!= NULL
);
169 fd
= open(file
, O_RDONLY
);
171 BOOT_DEBUG("Error opening %s\n", file
);
176 if (fstat(fd
, &sb
) == -1) {
177 BOOT_DEBUG("Error getting information (stat) about %s", file
);
182 /* loader bootblock has version built in */
183 buf_size
= sb
.st_size
;
185 bblock
->buf_size
= buf_size
;
186 BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
189 bblock
->buf
= malloc(buf_size
);
190 if (bblock
->buf
== NULL
) {
191 perror(gettext("Memory allocation failure"));
194 bblock
->file
= bblock
->buf
;
196 if (read(fd
, bblock
->file
, bblock
->buf_size
) != bblock
->buf_size
) {
197 BOOT_DEBUG("Read from %s failed\n", file
);
202 if (find_multiboot(bblock
->file
, MBOOT_SCAN_SIZE
, &mboot_off
)
204 (void) fprintf(stderr
,
205 gettext("Unable to find multiboot header\n"));
209 bblock
->mboot
= (multiboot_header_t
*)(bblock
->file
+ mboot_off
);
210 bblock
->mboot_off
= mboot_off
;
213 bblock
->mboot
->load_end_addr
- bblock
->mboot
->load_addr
;
214 BOOT_DEBUG("bootblock file size is %d\n", bblock
->file_size
);
216 bblock
->extra
= bblock
->buf
+ P2ROUNDUP(bblock
->file_size
, 8);
217 bblock
->extra_size
= bblock
->buf_size
- P2ROUNDUP(bblock
->file_size
, 8);
219 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
220 "(size=%d)\n", bblock
->mboot
, bblock
->mboot_off
, bblock
->extra
,
221 bblock
->extra_size
, bblock
->buf
, bblock
->buf_size
);
227 (void) free(bblock
->buf
);
236 read_bootblock_from_disk(ib_device_t
*device
, ib_bootblock_t
*bblock
,
240 uint32_t size
, offset
;
243 multiboot_header_t
*mboot
;
245 assert(device
!= NULL
);
246 assert(bblock
!= NULL
);
248 if (device
->target
.fstype
== IG_FS_ZFS
) {
249 dev_fd
= device
->target
.fd
;
250 offset
= BBLK_ZFS_BLK_OFF
* SECTOR_SIZE
;
251 *path
= device
->target
.path
;
253 dev_fd
= device
->stage
.fd
;
254 offset
= device
->stage
.offset
* SECTOR_SIZE
;
255 *path
= device
->stage
.path
;
258 if (read_in(dev_fd
, mboot_scan
, sizeof (mboot_scan
), offset
)
260 BOOT_DEBUG("Error reading bootblock area\n");
265 /* No multiboot means no chance of knowing bootblock size */
266 if (find_multiboot(mboot_scan
, sizeof (mboot_scan
), &mboot_off
)
268 BOOT_DEBUG("Unable to find multiboot header\n");
271 mboot
= (multiboot_header_t
*)(mboot_scan
+ mboot_off
);
274 * make sure mboot has sane values
276 if (mboot
->load_end_addr
== 0 ||
277 mboot
->load_end_addr
< mboot
->load_addr
)
281 * Currently, the amount of space reserved for extra information
282 * is "fixed". We may have to scan for the terminating extra payload
285 size
= mboot
->load_end_addr
- mboot
->load_addr
;
286 buf_size
= P2ROUNDUP(size
+ SECTOR_SIZE
, SECTOR_SIZE
);
287 bblock
->file_size
= size
;
289 bblock
->buf
= malloc(buf_size
);
290 if (bblock
->buf
== NULL
) {
291 BOOT_DEBUG("Unable to allocate enough memory to read"
292 " the extra bootblock from the disk\n");
293 perror(gettext("Memory allocation failure"));
296 bblock
->buf_size
= buf_size
;
298 if (read_in(dev_fd
, bblock
->buf
, buf_size
, offset
) != BC_SUCCESS
) {
299 BOOT_DEBUG("Error reading the bootblock\n");
300 (void) free(bblock
->buf
);
305 /* Update pointers. */
306 bblock
->file
= bblock
->buf
;
307 bblock
->mboot_off
= mboot_off
;
308 bblock
->mboot
= (multiboot_header_t
*)(bblock
->buf
+ bblock
->mboot_off
);
309 bblock
->extra
= bblock
->buf
+ P2ROUNDUP(bblock
->file_size
, 8);
310 bblock
->extra_size
= bblock
->buf_size
- P2ROUNDUP(bblock
->file_size
, 8);
312 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
313 "(size=%d)\n", bblock
->mboot
, bblock
->mboot_off
, bblock
->extra
,
314 bblock
->extra_size
, bblock
->buf
, bblock
->buf_size
);
320 is_update_necessary(ib_data_t
*data
, char *updt_str
)
323 bblk_einfo_t
*einfo_file
;
325 ib_bootblock_t bblock_disk
;
326 ib_bootblock_t
*bblock_file
= &data
->bootblock
;
327 ib_device_t
*device
= &data
->device
;
331 assert(data
!= NULL
);
333 bzero(&bblock_disk
, sizeof (ib_bootblock_t
));
335 ret
= read_bootblock_from_disk(device
, &bblock_disk
, &path
);
336 if (ret
!= BC_SUCCESS
) {
337 BOOT_DEBUG("Unable to read bootblock from %s\n", path
);
341 einfo
= find_einfo(bblock_disk
.extra
, bblock_disk
.extra_size
);
343 BOOT_DEBUG("No extended information available on disk\n");
347 einfo_file
= find_einfo(bblock_file
->extra
, bblock_file
->extra_size
);
348 if (einfo_file
== NULL
) {
350 * loader bootblock is versioned. missing version means
351 * probably incompatible block. installboot can not install
354 (void) fprintf(stderr
,
355 gettext("ERROR: non versioned bootblock in file\n"));
358 if (updt_str
== NULL
) {
359 updt_str
= einfo_get_string(einfo_file
);
364 if (!do_version
|| updt_str
== NULL
) {
365 (void) fprintf(stderr
,
366 gettext("WARNING: target device %s has a "
367 "versioned bootblock that is going to be overwritten by a "
368 "non versioned one\n"), device
->path
);
373 BOOT_DEBUG("Forcing update of %s bootblock\n", device
->path
);
377 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str
);
379 bblock_hs
.src_buf
= (unsigned char *)bblock_file
->file
;
380 bblock_hs
.src_size
= bblock_file
->file_size
;
382 return (einfo_should_update(einfo
, &bblock_hs
, updt_str
));
386 add_bootblock_einfo(ib_bootblock_t
*bblock
, char *updt_str
)
389 uint32_t avail_space
;
391 assert(bblock
!= NULL
);
393 if (updt_str
== NULL
) {
394 BOOT_DEBUG("WARNING: no update string passed to "
395 "add_bootblock_einfo()\n");
399 /* Fill bootblock hashing source information. */
400 hs
.src_buf
= (unsigned char *)bblock
->file
;
401 hs
.src_size
= bblock
->file_size
;
402 /* How much space for the extended information structure? */
403 avail_space
= bblock
->buf_size
- P2ROUNDUP(bblock
->file_size
, 8);
404 /* Place the extended information structure. */
405 add_einfo(bblock
->extra
, updt_str
, &hs
, avail_space
);
409 * set up data for case stage1 is installed as MBR
410 * set up location and size of bootblock
411 * set disk guid to provide unique information for biosdev command
414 prepare_stage1(ib_data_t
*data
)
418 assert(data
!= NULL
);
419 device
= &data
->device
;
422 bcopy(device
->mbr
+ STAGE1_BPB_OFFSET
,
423 data
->stage1
+ STAGE1_BPB_OFFSET
, STAGE1_BPB_SIZE
);
426 /* copy MBR, note STAGE1_SIG == BOOTSZ */
427 bcopy(device
->mbr
+ STAGE1_SIG
, data
->stage1
+ STAGE1_SIG
,
428 SECTOR_SIZE
- STAGE1_SIG
);
430 /* set stage2 size */
431 *((uint16_t *)(data
->stage1
+ STAGE1_STAGE2_SIZE
)) =
432 (uint16_t)(data
->bootblock
.buf_size
/ SECTOR_SIZE
);
435 * set stage2 location.
436 * for zfs always use zfs embedding, for ufs/pcfs use partition_start
437 * as base for stage2 location, for ufs/pcfs in MBR partition, use
438 * free space after MBR record.
440 if (device
->target
.fstype
== IG_FS_ZFS
)
441 *((uint64_t *)(data
->stage1
+ STAGE1_STAGE2_LBA
)) =
442 device
->target
.start
+ device
->target
.offset
;
444 *((uint64_t *)(data
->stage1
+ STAGE1_STAGE2_LBA
)) =
445 device
->stage
.start
+ device
->stage
.offset
;
449 * set disk uuid. we only need reasonable amount of uniqueness
450 * to allow biosdev to identify disk based on mbr differences.
452 uuid_generate(data
->stage1
+ STAGE1_STAGE2_UUID
);
458 prepare_bootblock(ib_data_t
*data
, char *updt_str
)
460 ib_bootblock_t
*bblock
;
464 assert(data
!= NULL
);
466 bblock
= &data
->bootblock
;
467 device
= &data
->device
;
469 ptr
= (uint64_t *)(&bblock
->mboot
->bss_end_addr
);
470 *ptr
= device
->target
.start
;
473 * the loader bootblock has built in version, if custom
474 * version was provided, update it.
477 add_bootblock_einfo(bblock
, updt_str
);
483 write_bootblock(ib_data_t
*data
)
485 ib_device_t
*device
= &data
->device
;
486 ib_bootblock_t
*bblock
= &data
->bootblock
;
492 assert(data
!= NULL
);
495 * ZFS bootblock area is 3.5MB, make sure we can fit.
496 * buf_size is size of bootblk+EINFO.
498 if (bblock
->buf_size
> BBLK_ZFS_BLK_SIZE
) {
499 (void) fprintf(stderr
, gettext("bootblock is too large\n"));
503 if (device
->target
.fstype
== IG_FS_ZFS
) {
504 dev_fd
= device
->target
.fd
;
505 abs
= device
->target
.start
+ device
->target
.offset
;
506 offset
= BBLK_ZFS_BLK_OFF
* SECTOR_SIZE
;
507 path
= device
->target
.path
;
509 dev_fd
= device
->stage
.fd
;
510 abs
= device
->stage
.start
+ device
->stage
.offset
;
511 offset
= device
->stage
.offset
* SECTOR_SIZE
;
512 path
= device
->stage
.path
;
513 if (bblock
->buf_size
>
514 (device
->stage
.size
- device
->stage
.offset
) * SECTOR_SIZE
) {
515 (void) fprintf(stderr
, gettext("Device %s is "
516 "too small to fit the stage2\n"), path
);
520 ret
= write_out(dev_fd
, bblock
->buf
, bblock
->buf_size
, offset
);
521 if (ret
!= BC_SUCCESS
) {
522 BOOT_DEBUG("Error writing the ZFS bootblock "
523 "to %s at offset %d\n", path
, offset
);
527 (void) fprintf(stdout
, gettext("bootblock written for %s,"
528 " %d sectors starting at %d (abs %lld)\n"), path
,
529 (bblock
->buf_size
/ SECTOR_SIZE
) + 1, offset
/ SECTOR_SIZE
, abs
);
535 * Partition boot block or volume boot record (VBR). The VBR is
536 * stored on partition relative sector 0 and allows chainloading
537 * to read boot program from partition.
539 * As the VBR will use the first sector of the partition,
540 * this means, we need to be sure the space is not used.
541 * We do support three partitioning chemes:
542 * 1. GPT: zfs and ufs have reserved space for first 8KB, but
543 * only zfs does have space for boot2. The pcfs has support
544 * for VBR, but no space for boot2. So with GPT, to support
545 * ufs or pcfs boot, we must have separate dedicated boot
546 * partition and we will store VBR on it.
547 * 2. MBR: we have almost the same situation as with GPT, except that
548 * if the partitions start from cylinder 1, we will have space
549 * between MBR and cylinder 0. If so, we do not require separate
551 * 3. MBR+VTOC: with this combination we store VBR in sector 0 of the
552 * solaris2 MBR partition. The slice 0 will start from cylinder 1,
553 * and we do have space for boot2, so we do not require separate
557 write_stage1(ib_data_t
*data
)
559 ib_device_t
*device
= &data
->device
;
562 assert(data
!= NULL
);
565 * We have separate partition for boot programs and the stage1
566 * location is not absolute sector 0.
567 * We will write VBR and trigger MBR to read 1 sector from VBR.
568 * This case does also cover MBR+VTOC case, as the solaris 2 partition
569 * name and the root file system slice names are different.
571 if (device
->stage
.start
!= 0 &&
572 strcmp(device
->target
.path
, device
->stage
.path
)) {
573 /* we got separate stage area, use it */
574 if (write_out(device
->stage
.fd
, data
->stage1
,
575 sizeof (data
->stage1
), 0) != BC_SUCCESS
) {
576 (void) fprintf(stdout
, gettext("cannot write "
577 "partition boot sector\n"));
582 (void) fprintf(stdout
, gettext("stage1 written to "
583 "%s %d sector 0 (abs %d)\n"),
584 device
->devtype
== IG_DEV_MBR
? "partition":"slice",
585 device
->stage
.id
, device
->stage
.start
);
586 start
= device
->stage
.start
;
590 * We have either GPT or MBR (without VTOC) and if the root
591 * file system is not pcfs, we can store VBR. Also trigger
592 * MBR to read 1 sector from VBR.
594 if (device
->devtype
!= IG_DEV_VTOC
&&
595 device
->target
.fstype
!= IG_FS_PCFS
) {
596 if (write_out(device
->target
.fd
, data
->stage1
,
597 sizeof (data
->stage1
), 0) != BC_SUCCESS
) {
598 (void) fprintf(stdout
, gettext("cannot write "
599 "partition boot sector\n"));
604 (void) fprintf(stdout
, gettext("stage1 written to "
605 "%s %d sector 0 (abs %d)\n"),
606 device
->devtype
== IG_DEV_MBR
? "partition":"slice",
607 device
->target
.id
, device
->target
.start
);
608 start
= device
->target
.start
;
613 * If we did write partition boot block, update MBR to
614 * read partition boot block, not boot2.
617 *((uint16_t *)(data
->stage1
+ STAGE1_STAGE2_SIZE
)) = 1;
618 *((uint64_t *)(data
->stage1
+ STAGE1_STAGE2_LBA
)) =
621 if (write_out(device
->fd
, data
->stage1
,
622 sizeof (data
->stage1
), 0) != BC_SUCCESS
) {
623 (void) fprintf(stdout
,
624 gettext("cannot write master boot sector\n"));
628 (void) fprintf(stdout
,
629 gettext("stage1 written to master boot sector\n"));
636 * find partition/slice start sector. will be recorded in stage2 and used
637 * by stage2 to identify partition with boot file system.
640 get_start_sector(ib_device_t
*device
)
642 uint32_t secnum
= 0, numsec
= 0;
643 int i
, pno
, rval
, log_part
= 0;
645 struct ipart
*part
= NULL
;
647 struct part_info dkpi
;
648 struct extpart_info edkpi
;
650 if (device
->devtype
== IG_DEV_EFI
) {
653 if (efi_alloc_and_read(device
->fd
, &vtoc
) < 0)
656 if (device
->stage
.start
== 0) {
657 /* zero size means the fstype must be zfs */
658 assert(device
->target
.fstype
== IG_FS_ZFS
);
660 device
->stage
.start
=
661 vtoc
->efi_parts
[device
->stage
.id
].p_start
;
663 vtoc
->efi_parts
[device
->stage
.id
].p_size
;
664 device
->stage
.offset
= BBLK_ZFS_BLK_OFF
;
665 device
->target
.offset
= BBLK_ZFS_BLK_OFF
;
668 device
->target
.start
=
669 vtoc
->efi_parts
[device
->target
.id
].p_start
;
670 device
->target
.size
=
671 vtoc
->efi_parts
[device
->target
.id
].p_size
;
673 /* with pcfs we always write MBR */
674 if (device
->target
.fstype
== IG_FS_PCFS
) {
683 mboot
= (struct mboot
*)device
->mbr
;
685 /* For MBR we have device->stage filled already. */
686 if (device
->devtype
== IG_DEV_MBR
) {
687 /* MBR partition starts from 0 */
688 pno
= device
->target
.id
- 1;
689 part
= (struct ipart
*)mboot
->parts
+ pno
;
691 if (part
->relsect
== 0) {
692 (void) fprintf(stderr
, gettext("Partition %d of the "
693 "disk has an incorrect offset\n"),
697 device
->target
.start
= part
->relsect
;
698 device
->target
.size
= part
->numsect
;
700 /* with pcfs we always write MBR */
701 if (device
->target
.fstype
== IG_FS_PCFS
) {
705 if (device
->target
.fstype
== IG_FS_ZFS
)
706 device
->target
.offset
= BBLK_ZFS_BLK_OFF
;
712 * Search for Solaris fdisk partition
713 * Get the solaris partition information from the device
714 * and compare the offset of S2 with offset of solaris partition
715 * from fdisk partition table.
717 if (ioctl(device
->target
.fd
, DKIOCEXTPARTINFO
, &edkpi
) < 0) {
718 if (ioctl(device
->target
.fd
, DKIOCPARTINFO
, &dkpi
) < 0) {
719 (void) fprintf(stderr
, gettext("cannot get the "
720 "slice information of the disk\n"));
723 edkpi
.p_start
= dkpi
.p_start
;
724 edkpi
.p_length
= dkpi
.p_length
;
728 device
->target
.start
= edkpi
.p_start
;
729 device
->target
.size
= edkpi
.p_length
;
730 if (device
->target
.fstype
== IG_FS_ZFS
)
731 device
->target
.offset
= BBLK_ZFS_BLK_OFF
;
733 for (i
= 0; i
< FD_NUMPART
; i
++) {
734 part
= (struct ipart
*)mboot
->parts
+ i
;
736 if (part
->relsect
== 0) {
737 (void) fprintf(stderr
, gettext("Partition %d of the "
738 "disk has an incorrect offset\n"), i
+1);
742 if (edkpi
.p_start
>= part
->relsect
&&
743 edkpi
.p_start
< (part
->relsect
+ part
->numsect
)) {
744 /* Found the partition */
749 if (i
== FD_NUMPART
) {
750 /* No solaris fdisk partitions (primary or logical) */
751 (void) fprintf(stderr
, gettext("Solaris partition not found. "
752 "Aborting operation.\n"));
757 * We have found a Solaris fdisk partition (primary or extended)
758 * Handle the simple case first: Solaris in a primary partition
760 if (!fdisk_is_dos_extended(part
->systid
)) {
761 device
->stage
.start
= part
->relsect
;
762 device
->stage
.size
= part
->numsect
;
763 if (device
->target
.fstype
== IG_FS_ZFS
)
764 device
->stage
.offset
= BBLK_ZFS_BLK_OFF
;
766 device
->stage
.offset
= BBLK_BLKLIST_OFF
;
767 device
->stage
.id
= i
+ 1;
772 * Solaris in a logical partition. Find that partition in the
776 if ((rval
= libfdisk_init(&epp
, device
->path
, NULL
, FDISK_READ_DISK
))
780 * The first 3 cases are not an error per-se, just that
781 * there is no Solaris logical partition
783 case FDISK_EBADLOGDRIVE
:
784 case FDISK_ENOLOGDRIVE
:
785 case FDISK_EBADMAGIC
:
786 (void) fprintf(stderr
, gettext("Solaris "
787 "partition not found. "
788 "Aborting operation.\n"));
791 (void) fprintf(stderr
, gettext("Could not get "
792 "virtual geometry\n"));
795 (void) fprintf(stderr
, gettext("Could not get "
796 "physical geometry\n"));
799 (void) fprintf(stderr
, gettext("Could not get "
800 "label geometry\n"));
803 (void) fprintf(stderr
, gettext("Failed to "
804 "initialize libfdisk.\n"));
809 rval
= fdisk_get_solaris_part(epp
, &pno
, &secnum
, &numsec
);
811 if (rval
!= FDISK_SUCCESS
) {
812 /* No solaris logical partition */
813 (void) fprintf(stderr
, gettext("Solaris partition not found. "
814 "Aborting operation.\n"));
818 device
->stage
.start
= secnum
;
819 device
->stage
.size
= numsec
;
820 device
->stage
.id
= pno
;
824 /* get confirmation for -m */
825 if (write_mbr
&& !force_mbr
) {
826 (void) fprintf(stdout
, gettext("Updating master boot sector "
827 "destroys existing boot managers (if any).\n"
828 "continue (y/n)? "));
831 (void) fprintf(stdout
, gettext("master boot sector "
838 * warn, if illumos in primary partition and loader not in MBR and
839 * partition is not active
841 if (device
->devtype
!= IG_DEV_EFI
) {
842 if (!log_part
&& part
->bootid
!= 128 && !write_mbr
) {
843 (void) fprintf(stdout
, gettext("Solaris fdisk "
844 "partition is inactive.\n"), device
->stage
.id
);
852 open_device(char *path
)
854 struct stat statbuf
= {0};
858 fd
= open(path
, O_RDONLY
);
860 fd
= open(path
, O_RDWR
);
863 BOOT_DEBUG("Unable to open %s\n", path
);
868 if (fstat(fd
, &statbuf
) != 0) {
869 BOOT_DEBUG("Unable to stat %s\n", path
);
875 if (S_ISCHR(statbuf
.st_mode
) == 0) {
876 (void) fprintf(stderr
, gettext("%s: Not a character device\n"),
886 get_boot_partition(ib_device_t
*device
, struct mboot
*mbr
)
892 part
= (struct ipart
*)mbr
->parts
;
893 for (i
= 0; i
< FD_NUMPART
; i
++) {
894 if (part
[i
].systid
== X86BOOT
)
898 /* no X86BOOT, try to use space between MBR and first partition */
899 if (i
== FD_NUMPART
) {
900 device
->stage
.path
= strdup(device
->path
);
901 if (device
->stage
.path
== NULL
) {
902 perror(gettext("Memory allocation failure"));
905 device
->stage
.fd
= dup(device
->fd
);
906 device
->stage
.id
= 0;
907 device
->stage
.devtype
= IG_DEV_MBR
;
908 device
->stage
.fstype
= IG_FS_NONE
;
909 device
->stage
.start
= 0;
910 device
->stage
.size
= part
[0].relsect
;
911 device
->stage
.offset
= BBLK_BLKLIST_OFF
;
915 if ((path
= strdup(device
->path
)) == NULL
) {
916 perror(gettext("Memory allocation failure"));
920 ptr
= strrchr(path
, 'p');
923 (void) asprintf(&ptr
, "%s%d", path
, i
+1); /* partitions are p1..p4 */
926 perror(gettext("Memory allocation failure"));
929 device
->stage
.path
= ptr
;
930 device
->stage
.fd
= open_device(ptr
);
931 device
->stage
.id
= i
+ 1;
932 device
->stage
.devtype
= IG_DEV_MBR
;
933 device
->stage
.fstype
= IG_FS_NONE
;
934 device
->stage
.start
= part
[i
].relsect
;
935 device
->stage
.size
= part
[i
].numsect
;
936 device
->stage
.offset
= 1; /* leave sector 0 for VBR */
941 get_boot_slice(ib_device_t
*device
, struct dk_gpt
*vtoc
)
946 for (i
= 0; i
< vtoc
->efi_nparts
; i
++) {
947 if (vtoc
->efi_parts
[i
].p_tag
== V_BOOT
) {
948 if ((path
= strdup(device
->target
.path
)) == NULL
) {
949 perror(gettext("Memory allocation failure"));
952 ptr
= strrchr(path
, 's');
955 (void) asprintf(&ptr
, "%s%d", path
, i
);
958 perror(gettext("Memory allocation failure"));
961 device
->stage
.path
= ptr
;
962 device
->stage
.fd
= open_device(ptr
);
963 device
->stage
.id
= i
;
964 device
->stage
.devtype
= IG_DEV_EFI
;
965 device
->stage
.fstype
= IG_FS_NONE
;
966 device
->stage
.start
= vtoc
->efi_parts
[i
].p_start
;
967 device
->stage
.size
= vtoc
->efi_parts
[i
].p_size
;
968 device
->stage
.offset
= 1; /* leave sector 0 for VBR */
976 init_device(ib_device_t
*device
, char *path
)
982 int pathlen
= strlen(path
);
985 bzero(device
, sizeof (*device
));
986 device
->fd
= -1; /* whole disk fd */
987 device
->stage
.fd
= -1; /* bootblock partition fd */
988 device
->target
.fd
= -1; /* target fs partition fd */
990 /* basic check, whole disk is not allowed */
991 if ((p
= strrchr(path
, '/')) == NULL
)
993 if ((strrchr(p
, 'p') == NULL
&& strrchr(p
, 's') == NULL
) ||
994 (path
[pathlen
-2] == 'p' && path
[pathlen
-1] == '0')) {
995 (void) fprintf(stderr
, gettext("installing loader to "
996 "whole disk device is not supported\n"));
999 device
->target
.path
= strdup(path
);
1000 if (device
->target
.path
== NULL
) {
1001 perror(gettext("Memory allocation failure"));
1004 device
->path
= strdup(path
);
1005 if (device
->path
== NULL
) {
1006 perror(gettext("Memory allocation failure"));
1010 /* change device name to p0 */
1011 device
->path
[pathlen
- 2] = 'p';
1012 device
->path
[pathlen
- 1] = '0';
1014 if (strstr(device
->target
.path
, "diskette")) {
1015 (void) fprintf(stderr
, gettext("installing loader to a floppy "
1016 "disk is not supported\n"));
1020 /* Detect if the target device is a pcfs partition. */
1021 if (strstr(device
->target
.path
, "p0:boot")) {
1022 (void) fprintf(stderr
, gettext("installing loader to x86 boot "
1023 "partition is not supported\n"));
1027 if ((device
->fd
= open_device(device
->path
)) == -1)
1030 /* read in the device boot sector. */
1031 if (read(device
->fd
, device
->mbr
, SECTOR_SIZE
) != SECTOR_SIZE
) {
1032 (void) fprintf(stderr
, gettext("Error reading boot sector\n"));
1037 device
->devtype
= IG_DEV_VTOC
;
1038 if (efi_alloc_and_read(device
->fd
, &vtoc
) >= 0) {
1039 ret
= get_boot_slice(device
, vtoc
);
1040 device
->devtype
= IG_DEV_EFI
;
1042 if (ret
== BC_ERROR
)
1044 } else if (device
->target
.path
[pathlen
- 2] == 'p') {
1045 device
->devtype
= IG_DEV_MBR
;
1046 ret
= get_boot_partition(device
, (struct mboot
*)device
->mbr
);
1047 if (ret
== BC_ERROR
)
1049 } else if (device
->target
.path
[pathlen
- 1] == '2') {
1051 * NOTE: we could relax there and allow zfs boot on
1052 * slice 2 for instance, but lets keep traditional limits.
1054 (void) fprintf(stderr
,
1055 gettext("raw device must be a root slice (not s2)\n"));
1059 /* fill stage partition for case there is no boot partition */
1060 if (device
->stage
.path
== NULL
) {
1061 if ((device
->stage
.path
= strdup(path
)) == NULL
) {
1062 perror(gettext("Memory allocation failure"));
1065 if (device
->devtype
== IG_DEV_VTOC
) {
1067 device
->stage
.path
[pathlen
- 2] = 's';
1068 device
->stage
.path
[pathlen
- 1] = '2';
1069 device
->stage
.id
= 2;
1071 p
= strrchr(device
->stage
.path
, 'p');
1073 p
= strrchr(device
->stage
.path
, 's');
1074 device
->stage
.id
= atoi(++p
);
1076 device
->stage
.devtype
= device
->devtype
;
1077 device
->stage
.fd
= open_device(device
->stage
.path
);
1080 p
= strrchr(device
->target
.path
, 'p');
1082 p
= strrchr(device
->target
.path
, 's');
1083 device
->target
.id
= atoi(++p
);
1085 if (strcmp(device
->stage
.path
, device
->target
.path
) == 0)
1086 device
->target
.fd
= dup(device
->stage
.fd
);
1088 device
->target
.fd
= open_device(device
->target
.path
);
1090 if (fstyp_init(device
->target
.fd
, 0, NULL
, &fhdl
) != 0)
1093 if (fstyp_ident(fhdl
, NULL
, &fident
) != 0) {
1095 (void) fprintf(stderr
, gettext("Failed to detect file "
1100 /* at this moment non-boot partition has no size set, use this fact */
1101 if (device
->devtype
== IG_DEV_EFI
&& strcmp(fident
, "zfs") &&
1102 device
->stage
.size
== 0) {
1104 (void) fprintf(stderr
, gettext("Booting %s of EFI labeled "
1105 "disks requires the boot partition.\n"), fident
);
1108 if (strcmp(fident
, "zfs") == 0)
1109 device
->target
.fstype
= IG_FS_ZFS
;
1110 else if (strcmp(fident
, "ufs") == 0) {
1111 device
->target
.fstype
= IG_FS_UFS
;
1112 } else if (strcmp(fident
, "pcfs") == 0) {
1113 device
->target
.fstype
= IG_FS_PCFS
;
1115 (void) fprintf(stderr
, gettext("File system %s is not "
1116 "supported by loader\n"), fident
);
1122 /* check for boot partition content */
1123 if (device
->stage
.size
) {
1124 if (fstyp_init(device
->stage
.fd
, 0, NULL
, &fhdl
) != 0)
1127 if (fstyp_ident(fhdl
, NULL
, &fident
) == 0) {
1128 (void) fprintf(stderr
, gettext("Unexpected %s file "
1129 "system on boot partition\n"), fident
);
1135 return (get_start_sector(device
));
1139 cleanup_device(ib_device_t
*device
)
1143 if (device
->stage
.path
)
1144 free(device
->stage
.path
);
1145 if (device
->target
.path
)
1146 free(device
->target
.path
);
1148 if (device
->fd
!= -1)
1149 (void) close(device
->fd
);
1150 if (device
->stage
.fd
!= -1)
1151 (void) close(device
->stage
.fd
);
1152 if (device
->target
.fd
!= -1)
1153 (void) close(device
->target
.fd
);
1154 bzero(device
, sizeof (*device
));
1158 cleanup_bootblock(ib_bootblock_t
*bblock
)
1161 bzero(bblock
, sizeof (ib_bootblock_t
));
1165 * Propagate the bootblock on the source disk to the destination disk and
1166 * version it with 'updt_str' in the process. Since we cannot trust any data
1167 * on the attaching disk, we do not perform any specific check on a potential
1168 * target extended information structure and we just blindly update.
1171 propagate_bootblock(ib_data_t
*src
, ib_data_t
*dest
, char *updt_str
)
1173 ib_bootblock_t
*src_bblock
= &src
->bootblock
;
1174 ib_bootblock_t
*dest_bblock
= &dest
->bootblock
;
1176 assert(src
!= NULL
);
1177 assert(dest
!= NULL
);
1179 /* read the stage1 file from source disk */
1180 if (read(src
->device
.fd
, dest
->stage1
, SECTOR_SIZE
) != SECTOR_SIZE
) {
1181 (void) fprintf(stderr
, gettext("cannot read stage1 from %s\n"),
1186 cleanup_bootblock(dest_bblock
);
1188 dest_bblock
->buf_size
= src_bblock
->buf_size
;
1189 dest_bblock
->buf
= malloc(dest_bblock
->buf_size
);
1190 if (dest_bblock
->buf
== NULL
) {
1191 perror(gettext("Memory Allocation Failure"));
1194 dest_bblock
->file
= dest_bblock
->buf
;
1195 dest_bblock
->file_size
= src_bblock
->file_size
;
1196 (void) memcpy(dest_bblock
->buf
, src_bblock
->buf
,
1197 dest_bblock
->buf_size
);
1199 dest_bblock
->mboot
= (multiboot_header_t
*)(dest_bblock
->file
+
1200 src_bblock
->mboot_off
);
1201 dest_bblock
->mboot_off
= src_bblock
->mboot_off
;
1202 dest_bblock
->extra
= (char *)dest_bblock
->file
+
1203 P2ROUNDUP(dest_bblock
->file_size
, 8);
1204 dest_bblock
->extra_size
= src_bblock
->extra_size
;
1206 (void) fprintf(stdout
, gettext("Propagating %s bootblock to %s\n"),
1207 src
->device
.path
, dest
->device
.path
);
1209 return (commit_to_disk(dest
, updt_str
));
1213 commit_to_disk(ib_data_t
*data
, char *update_str
)
1215 assert(data
!= NULL
);
1217 if (prepare_bootblock(data
, update_str
) != BC_SUCCESS
) {
1218 (void) fprintf(stderr
, gettext("Error updating the bootblock "
1223 if (prepare_stage1(data
) != BC_SUCCESS
) {
1224 (void) fprintf(stderr
, gettext("Error updating the stage1 "
1229 if (write_bootblock(data
) != BC_SUCCESS
) {
1230 (void) fprintf(stderr
, gettext("Error writing bootblock to "
1235 return (write_stage1(data
));
1239 * Install a new bootblock on the given device. handle_install() expects argv
1240 * to contain 3 parameters (the target device path and the path to the
1243 * Returns: BC_SUCCESS - if the installation is successful
1244 * BC_ERROR - if the installation failed
1245 * BC_NOUPDT - if no installation was performed because the
1246 * version currently installed is more recent than the
1251 handle_install(char *progname
, char **argv
)
1253 ib_data_t install_data
;
1254 ib_bootblock_t
*bblock
= &install_data
.bootblock
;
1255 char *stage1
= NULL
;
1256 char *bootblock
= NULL
;
1257 char *device_path
= NULL
;
1260 stage1
= strdup(argv
[0]);
1261 bootblock
= strdup(argv
[1]);
1262 device_path
= strdup(argv
[2]);
1264 if (!device_path
|| !bootblock
|| !stage1
) {
1265 (void) fprintf(stderr
, gettext("Missing parameter"));
1270 BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
1271 device_path
, stage1
, bootblock
);
1272 bzero(&install_data
, sizeof (ib_data_t
));
1274 if (init_device(&install_data
.device
, device_path
) != BC_SUCCESS
) {
1275 (void) fprintf(stderr
, gettext("Unable to open device %s\n"),
1280 if (read_stage1_from_file(stage1
, &install_data
) != BC_SUCCESS
) {
1281 (void) fprintf(stderr
, gettext("Error opening %s\n"), stage1
);
1285 if (read_bootblock_from_file(bootblock
, bblock
) != BC_SUCCESS
) {
1286 (void) fprintf(stderr
, gettext("Error reading %s\n"),
1292 * is_update_necessary() will take care of checking if versioning and/or
1293 * forcing the update have been specified. It will also emit a warning
1294 * if a non-versioned update is attempted over a versioned bootblock.
1296 if (!is_update_necessary(&install_data
, update_str
)) {
1297 (void) fprintf(stderr
, gettext("bootblock version installed "
1298 "on %s is more recent or identical\n"
1299 "Use -F to override or install without the -u option\n"),
1305 BOOT_DEBUG("Ready to commit to disk\n");
1306 ret
= commit_to_disk(&install_data
, update_str
);
1309 cleanup_device(&install_data
.device
);
1318 * Retrieves from a device the extended information (einfo) associated to the
1319 * file or installed stage2.
1320 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0
1323 * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
1324 * - BC_ERROR (on error)
1325 * - BC_NOEINFO (no extended information available)
1328 handle_getinfo(char *progname
, char **argv
)
1331 ib_bootblock_t bblock
;
1333 bblk_einfo_t
*einfo
;
1335 char *device_path
, *path
;
1336 int retval
= BC_ERROR
;
1339 device_path
= strdup(argv
[0]);
1341 (void) fprintf(stderr
, gettext("Missing parameter"));
1346 if (stat(device_path
, &sb
) == -1) {
1351 bzero(&bblock
, sizeof (bblock
));
1352 bzero(&device
, sizeof (device
));
1353 BOOT_DEBUG("device path: %s\n", device_path
);
1355 if (S_ISREG(sb
.st_mode
) != 0) {
1357 ret
= read_bootblock_from_file(device_path
, &bblock
);
1359 if (init_device(&device
, device_path
) != BC_SUCCESS
) {
1360 (void) fprintf(stderr
, gettext("Unable to gather "
1361 "device information from %s\n"), device_path
);
1364 ret
= read_bootblock_from_disk(&device
, &bblock
, &path
);
1367 if (ret
== BC_ERROR
) {
1368 (void) fprintf(stderr
, gettext("Error reading bootblock from "
1373 if (ret
== BC_NOEXTRA
) {
1374 BOOT_DEBUG("No multiboot header found on %s, unable "
1375 "to locate extra information area (old/non versioned "
1376 "bootblock?) \n", device_path
);
1377 (void) fprintf(stderr
, gettext("No extended information "
1379 retval
= BC_NOEINFO
;
1383 einfo
= find_einfo(bblock
.extra
, bblock
.extra_size
);
1384 if (einfo
== NULL
) {
1385 retval
= BC_NOEINFO
;
1386 (void) fprintf(stderr
, gettext("No extended information "
1391 /* Print the extended information. */
1393 flags
|= EINFO_EASY_PARSE
;
1395 flags
|= EINFO_PRINT_HEADER
;
1397 print_einfo(flags
, einfo
, bblock
.extra_size
);
1398 retval
= BC_SUCCESS
;
1401 if (S_ISREG(sb
.st_mode
) == 0)
1402 cleanup_device(&device
);
1409 * Attempt to mirror (propagate) the current bootblock over the attaching disk.
1412 * - BC_SUCCESS (a successful propagation happened)
1413 * - BC_ERROR (an error occurred)
1414 * - BC_NOEXTRA (it is not possible to dump the current bootblock since
1415 * there is no multiboot information)
1418 handle_mirror(char *progname
, char **argv
)
1420 ib_data_t curr_data
;
1421 ib_data_t attach_data
;
1422 ib_device_t
*curr_device
= &curr_data
.device
;
1423 ib_device_t
*attach_device
= &attach_data
.device
;
1424 ib_bootblock_t
*bblock_curr
= &curr_data
.bootblock
;
1425 ib_bootblock_t
*bblock_attach
= &attach_data
.bootblock
;
1426 bblk_einfo_t
*einfo_curr
= NULL
;
1427 char *curr_device_path
;
1428 char *attach_device_path
;
1429 char *updt_str
= NULL
;
1431 int retval
= BC_ERROR
;
1434 curr_device_path
= strdup(argv
[0]);
1435 attach_device_path
= strdup(argv
[1]);
1437 if (!curr_device_path
|| !attach_device_path
) {
1438 (void) fprintf(stderr
, gettext("Missing parameter"));
1442 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
1443 " %s\n", curr_device_path
, attach_device_path
);
1445 bzero(&curr_data
, sizeof (ib_data_t
));
1446 bzero(&attach_data
, sizeof (ib_data_t
));
1448 if (init_device(curr_device
, curr_device_path
) != BC_SUCCESS
) {
1449 (void) fprintf(stderr
, gettext("Unable to gather device "
1450 "information from %s (current device)\n"),
1455 if (init_device(attach_device
, attach_device_path
) != BC_SUCCESS
) {
1456 (void) fprintf(stderr
, gettext("Unable to gather device "
1457 "information from %s (attaching device)\n"),
1458 attach_device_path
);
1462 ret
= read_bootblock_from_disk(curr_device
, bblock_curr
, &path
);
1463 if (ret
== BC_ERROR
) {
1464 BOOT_DEBUG("Error reading bootblock from %s\n", path
);
1469 if (ret
== BC_NOEXTRA
) {
1470 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
1471 " the bootblock\n", path
);
1472 retval
= BC_NOEXTRA
;
1478 einfo_curr
= find_einfo(bblock_curr
->extra
, bblock_curr
->extra_size
);
1479 if (einfo_curr
!= NULL
)
1480 updt_str
= einfo_get_string(einfo_curr
);
1482 retval
= propagate_bootblock(&curr_data
, &attach_data
, updt_str
);
1483 cleanup_bootblock(bblock_curr
);
1484 cleanup_bootblock(bblock_attach
);
1486 cleanup_device(attach_device
);
1488 cleanup_device(curr_device
);
1490 free(curr_device_path
);
1491 free(attach_device_path
);
1495 #define USAGE_STRING "Usage:\t%s [-h|-m|-f|-n|-F|-u verstr] stage1 stage2 " \
1497 "\t%s -M [-n] raw-device attach-raw-device\n" \
1498 "\t%s [-e|-V] -i raw-device | file\n"
1500 #define CANON_USAGE_STR gettext(USAGE_STRING)
1503 usage(char *progname
)
1505 (void) fprintf(stdout
, CANON_USAGE_STR
, progname
, progname
, progname
);
1509 main(int argc
, char **argv
)
1517 (void) setlocale(LC_ALL
, "");
1518 (void) textdomain(TEXT_DOMAIN
);
1519 if (init_yes() < 0) {
1520 (void) fprintf(stderr
, gettext(ERR_MSG_INIT_YES
),
1525 while ((opt
= getopt(argc
, argv
, "deFfhiMmnu:V")) != EOF
) {
1528 boot_debug
= B_TRUE
;
1534 force_update
= B_TRUE
;
1544 do_getinfo
= B_TRUE
;
1548 do_mirror_bblk
= B_TRUE
;
1558 do_version
= B_TRUE
;
1560 update_str
= malloc(strlen(optarg
) + 1);
1561 if (update_str
== NULL
) {
1562 perror(gettext("Memory allocation failure"));
1565 (void) strlcpy(update_str
, optarg
, strlen(optarg
) + 1);
1568 verbose_dump
= B_TRUE
;
1571 /* fall through to process non-optional args */
1576 /* check arguments */
1577 if (argc
!= optind
+ params
) {
1582 check_options(progname
);
1583 handle_args
= argv
+ optind
;
1586 (void) fprintf(stdout
, gettext("Dry run requested. Nothing will"
1587 " be written to disk.\n"));
1590 ret
= handle_getinfo(progname
, handle_args
);
1591 } else if (do_mirror_bblk
) {
1592 ret
= handle_mirror(progname
, handle_args
);
1594 ret
= handle_install(progname
, handle_args
);
1599 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
1601 check_options(char *progname
)
1603 if (do_getinfo
&& do_mirror_bblk
) {
1604 (void) fprintf(stderr
, gettext("Only one of -M and -i can be "
1605 "specified at the same time\n"));
1610 if (do_mirror_bblk
) {
1612 * -u and -F may actually reflect a user intent that is not
1613 * correct with this command (mirror can be interpreted
1614 * "similar" to install. Emit a message and continue.
1615 * -e and -V have no meaning, be quiet here and only report the
1616 * incongruence if a debug output is requested.
1619 (void) fprintf(stderr
, MEANINGLESS_OPT
, "-u");
1620 do_version
= B_FALSE
;
1623 (void) fprintf(stderr
, MEANINGLESS_OPT
, "-F");
1624 force_update
= B_FALSE
;
1626 if (strip
|| verbose_dump
) {
1627 BOOT_DEBUG(MEANINGLESS_OPT
, "-e|-V");
1629 verbose_dump
= B_FALSE
;
1634 if (write_mbr
|| force_mbr
|| do_version
|| force_update
) {
1635 BOOT_DEBUG(MEANINGLESS_OPT
, "-m|-f|-u|-F");
1636 write_mbr
= force_mbr
= do_version
= B_FALSE
;
1637 force_update
= B_FALSE
;