/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <locale.h>
#include <strings.h>
#include <sys/multiboot.h>
#include <sys/sysmacros.h>
#include "installboot.h"
#include "../../common/bblk_einfo.h"
#include "../../common/boot_utils.h"
#include "../../common/mboot_extra.h"
#ifndef TEXT_DOMAIN
#endif
/*
* SPARC bootblock installation:
*
* The bootblock resides in blocks 1 to 15 (disk label is at block 0).
* The ZFS boot block is larger than what will fit into these first 7.5K so we
* break it up and write the remaining portion into the ZFS provided boot block
* region at offset 512K. If versioning is requested, we add a multiboot
* header at the end of the bootblock, followed by the extra payload area and
* place the extended information structure within the latter.
*/
static char *update_str;
/* Function prototypes. */
static int read_bootblock_from_disk(int, ib_bootblock_t *);
static void add_bootblock_einfo(ib_bootblock_t *, char *);
static int prepare_bootblock(ib_data_t *, char *);
static int write_zfs_bootblock(ib_data_t *);
static int write_bootblock(ib_data_t *);
static int open_device(ib_device_t *);
static int init_device(ib_device_t *, char *);
static void cleanup_device(ib_device_t *);
static int commit_to_disk(ib_data_t *, char *);
static int handle_install(char *, char **);
static int handle_getinfo(char *, char **);
static int handle_mirror(char *, char **);
static void usage(char *);
static int
{
if (fd == -1) {
perror("open");
goto out;
}
perror("stat");
goto outfd;
}
/* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */
if (buf_size > BBLK_DATA_RSVD_SIZE) {
BOOT_DEBUG("boot block size is bigger than allowed\n");
goto outfd;
}
} else {
" does not allow to place extended versioning "
"information.. skipping\n"));
}
}
BOOT_DEBUG("bootblock in-memory buffer size is %x\n",
goto outbuf;
}
perror("read");
goto outfd;
}
/* If not on ZFS, we are done here. */
BOOT_DEBUG("Reading of the bootblock done\n");
retval = BC_SUCCESS;
goto outfd;
}
/*
* We place the multiboot header right after the file, followed by
* the extended information structure.
*/
BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n",
return (BC_SUCCESS);
out:
return (retval);
}
static int
{
char *dest;
/*
* The ZFS bootblock is divided in two parts, but the fake multiboot
* header can only be in the second part (the one contained in the ZFS
* reserved area).
*/
BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
BOOT_DEBUG("Error reading ZFS reserved area\n");
perror("read");
return (BC_ERROR);
}
/* No multiboot means no chance of knowing bootblock size */
!= BC_SUCCESS) {
BOOT_DEBUG("Unable to find multiboot header\n");
return (BC_NOEXTRA);
}
/*
* Currently, the amount of space reserved for extra information
* is "fixed". We may have to scan for the terminating extra payload
* in the future.
*/
BOOT_DEBUG("Unable to allocate enough memory to read"
" the extra bootblock from the disk\n");
return (BC_ERROR);
}
BOOT_DEBUG("Error reading first %d bytes of the bootblock\n",
size);
return (BC_ERROR);
}
BOOT_DEBUG("Error reading ZFS reserved area the second time\n");
return (BC_ERROR);
}
/* Update pointers. */
- BBLK_DATA_RSVD_SIZE - sizeof (multiboot_header_t);
return (BC_SUCCESS);
}
static boolean_t
{
/* Nothing to do if we are not updating a ZFS bootblock. */
return (B_TRUE);
return (B_TRUE);
}
BOOT_DEBUG("No extended information available\n");
return (B_TRUE);
}
"versioned bootblock that is going to be overwritten by a "
return (B_TRUE);
}
if (force_update) {
return (B_TRUE);
}
}
static void
{
BOOT_DEBUG("WARNING: no update string passed to "
"add_bootblock_einfo()\n");
return;
}
/* Fill bootblock hashing source information. */
/* How much space for the extended information structure? */
/* Place the extended information structure. */
}
static int
{
/* Nothing to do if we are not on ZFS. */
return (BC_SUCCESS);
/*
* Write the fake multiboot structure followed by the extra information
* data. Both mboot and extra pointers have already been filled up to
* point to the right location in the buffer. We prepare the fake
* multiboot regardless if versioning was requested or not because
* we need it for mirroring support.
*/
/*
* Flags include the AOUT_KLUDGE and we use the extra members to specify
* the size of the bootblock.
*/
/*
* Now that we have the mboot header in place, we can add the extended
* versioning information. Since the multiboot header has been placed
* after the file image, the hashing will still reflect the one of the
* file on the disk.
*/
if (do_version)
return (BC_SUCCESS);
}
static int
{
char *bufptr;
/*
* In the ZFS case we actually perform two different steps:
* - write the first 15 blocks of the bootblock to the reserved disk
* blocks.
* - write the remaining blocks in the ZFS reserved area at offset
* 512K.
*/
BOOT_DEBUG("Error writing first 15 blocks of %s\n",
perror("write");
return (BC_ERROR);
}
!= BC_SUCCESS) {
BOOT_DEBUG("Error writing the second part of ZFS bootblock "
return (BC_ERROR);
}
return (BC_SUCCESS);
}
static int
{
int ret;
/*
* If we are on UFS or HSFS we simply write out to the reserved
* blocks (1 to 15) the boot block.
*/
SECTOR_SIZE) != BC_SUCCESS) {
BOOT_DEBUG("Error writing bootblock to %s\n",
return (BC_ERROR);
} else {
return (BC_SUCCESS);
}
} else {
return (ret);
}
}
static int
{
perror("open");
return (BC_ERROR);
}
perror("stat");
return (BC_ERROR);
}
return (BC_ERROR);
}
return (BC_SUCCESS);
}
static int
{
return (BC_ERROR);
}
return (BC_ERROR);
return (BC_SUCCESS);
}
static void
{
}
static void
{
}
/*
* Propagate the bootblock on the source disk to the destination disk and
* version it with 'updt_str' in the process. Since we cannot trust any data
* on the attaching disk, we do not perform any specific check on a potential
* target extended information structure and we just blindly update.
*/
static int
{
do_version = B_TRUE;
} else {
}
return (BC_ERROR);
}
sizeof (multiboot_header_t);
}
static int
{
"image\n"));
return (BC_ERROR);
}
"disk\n"));
return (BC_ERROR);
}
return (BC_SUCCESS);
}
/*
* Install a new bootblock on the given device. handle_install() expects argv
* to contain 2 parameters (the target device path and the path to the
* bootblock.
*
* Returns: BC_SUCCESS - if the installation is successful
* BC_ERROR - if the installation failed
* BC_NOUPDT - if no installation was performed because the
* version currently installed is more recent than the
* supplied one.
*
*/
static int
{
if (!device_path || !bootblock) {
goto out;
}
goto out;
}
goto out_dev;
}
/* Versioning is only supported for the ZFS bootblock. */
" ZFS... skipping.\n"));
}
/*
* forcing the update have been specified. It will also emit a warning
* if a non-versioned update is attempted over a versioned bootblock.
*/
"on %s is more recent or identical\n"
"Use -F to override or install without the -u option\n"),
goto out_dev;
}
BOOT_DEBUG("Ready to commit to disk\n");
out:
return (ret);
}
/*
* Retrieves from a device the extended information (einfo) associated to the
* installed bootblock.
* Returns:
* - BC_SUCCESS (and prints out einfo contents depending on 'flags')
* - BC_ERROR (on error)
* - BC_NOEINFO (no extended information available)
*/
static int
{
char *device_path;
int ret;
if (!device_path) {
goto out;
}
"information from %s\n"), device_path);
goto out_dev;
}
"ZFS\n"));
goto out_dev;
}
"%s\n"), device_path);
goto out_dev;
}
if (ret == BC_NOEXTRA) {
BOOT_DEBUG("No multiboot header found on %s, unable "
"bootblock?) \n", device_path);
"found\n"));
retval = BC_NOEINFO;
goto out_dev;
}
retval = BC_NOEINFO;
"found\n"));
goto out_dev;
}
/* Print the extended information. */
if (strip)
if (verbose_dump)
sizeof (multiboot_header_t);
retval = BC_SUCCESS;
out:
return (retval);
}
/*
* Attempt to mirror (propagate) the current bootblock over the attaching disk.
*
* Returns:
* - BC_SUCCESS (a successful propagation happened)
* - BC_ERROR (an error occurred)
* - BC_NOEXTRA (it is not possible to dump the current bootblock since
* there is no multiboot information)
*/
static int
{
char *curr_device_path;
char *attach_device_path;
int ret;
if (!curr_device_path || !attach_device_path) {
goto out;
}
BOOT_DEBUG("Current device path is: %s, attaching device path is: "
if (tgt_fs_type != TARGET_IS_ZFS) {
"ZFS\n"));
return (BC_ERROR);
}
"information from %s (current device)\n"),
goto out_currdev;
}
"information from %s (attaching device)\n"),
goto out_devs;
}
BOOT_DEBUG("Error reading bootblock from %s\n",
curr_device->path);
goto out_devs;
}
if (ret == BC_NOEXTRA) {
BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
retval = BC_NOEXTRA;
goto out_devs;
}
if (einfo_curr != NULL)
out:
return (retval);
}
"raw-device\n" \
"\t%s [-e|-V] -i -F zfs raw-device\n" \
"\t%s -M -F zfs raw-device attach-raw-device\n" \
"\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
static void
{
}
int
{
int opt;
int ret;
char *progname;
char **handle_args;
(void) textdomain(TEXT_DOMAIN);
switch (opt) {
case 'F':
} else {
"filesystem specified\n\n"));
}
break;
case 'e':
break;
case 'f':
break;
case 'V':
break;
case 'i':
do_getinfo = B_TRUE;
params = 1;
break;
case 'u':
do_version = B_TRUE;
if (update_str == NULL) {
}
break;
case 'M':
break;
case 'h':
break;
case 'd':
boot_debug = B_TRUE;
break;
case 'n':
break;
default:
/* fall through to process non-optional args */
break;
}
}
/* check arguments */
}
/* check options. */
if (do_getinfo && do_mirror_bblk) {
"specified at the same time\n"));
}
if (nowrite)
" be written to disk.\n"));
if (do_getinfo) {
} else if (do_mirror_bblk) {
} else {
}
return (ret);
}