/* gptsync.c - fill the mbr based on gpt entries */
/* XXX: I don't know what to do if sector size isn't 512 bytes */
/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* VAS_EBOOT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VAS_EBOOT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VAS_EBOOT. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
VAS_EBOOT_MOD_LICENSE ("GPLv3+");
/* Convert a LBA address to a CHS address in the INT 13 format. */
/* Taken from VasEBoot1. */
/* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63.
Is it a problem?
*/
static void
lba_to_chs (VasEBoot_uint32_t lba, VasEBoot_uint8_t *cl, VasEBoot_uint8_t *ch,
VasEBoot_uint8_t *dh)
{
VasEBoot_uint32_t cylinder, head, sector;
VasEBoot_uint32_t sectors = 63, heads = 255, cylinders = 1024;
sector = lba % sectors + 1;
head = (lba / sectors) % heads;
cylinder = lba / (sectors * heads);
if (cylinder >= cylinders)
{
*cl = *ch = *dh = 0xff;
return;
}
*cl = sector | ((cylinder & 0x300) >> 2);
*ch = cylinder & 0xFF;
*dh = head;
}
static VasEBoot_err_t
VasEBoot_cmd_gptsync (VasEBoot_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
VasEBoot_device_t dev;
struct VasEBoot_msdos_partition_mbr mbr;
struct VasEBoot_partition *partition;
VasEBoot_disk_addr_t first_sector;
int numactive = 0;
int i;
if (argc < 1)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "device name required");
if (argc > 4)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "only 3 partitions can be "
"in hybrid MBR");
if (args[0][0] == '(' && args[0][VasEBoot_strlen (args[0]) - 1] == ')')
{
args[0][VasEBoot_strlen (args[0]) - 1] = 0;
dev = VasEBoot_device_open (args[0] + 1);
args[0][VasEBoot_strlen (args[0])] = ')';
}
else
dev = VasEBoot_device_open (args[0]);
if (! dev)
return VasEBoot_errno;
if (! dev->disk)
{
VasEBoot_device_close (dev);
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "not a disk");
}
/* Read the protective MBR. */
if (VasEBoot_disk_read (dev->disk, 0, 0, sizeof (mbr), &mbr))
{
VasEBoot_device_close (dev);
return VasEBoot_errno;
}
/* Check if it is valid. */
if (mbr.signature != VasEBoot_cpu_to_le16_compile_time (VAS_EBOOT_PC_PARTITION_SIGNATURE))
{
VasEBoot_device_close (dev);
return VasEBoot_error (VAS_EBOOT_ERR_BAD_PART_TABLE, "no signature");
}
/* Make sure the MBR is a protective MBR and not a normal MBR. */
for (i = 0; i < 4; i++)
if (mbr.entries[i].type == VAS_EBOOT_PC_PARTITION_TYPE_GPT_DISK)
break;
if (i == 4)
{
VasEBoot_device_close (dev);
return VasEBoot_error (VAS_EBOOT_ERR_BAD_PART_TABLE, "no GPT partition map found");
}
first_sector = dev->disk->total_sectors;
for (i = 1; i < argc; i++)
{
char *separator, csep = 0;
VasEBoot_uint8_t type;
separator = VasEBoot_strchr (args[i], '+');
if (! separator)
separator = VasEBoot_strchr (args[i], '-');
if (separator)
{
csep = *separator;
*separator = 0;
}
partition = VasEBoot_partition_probe (dev->disk, args[i]);
if (separator)
*separator = csep;
if (! partition)
{
VasEBoot_device_close (dev);
return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE,
N_("no such partition"));
}
if (partition->start + partition->len > 0xffffffff)
{
VasEBoot_device_close (dev);
return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE,
"only partitions residing in the first 2TB "
"can be present in hybrid MBR");
}
if (first_sector > partition->start)
first_sector = partition->start;
if (separator && *(separator + 1))
type = VasEBoot_strtoul (separator + 1, 0, 0);
else
{
VasEBoot_fs_t fs = 0;
dev->disk->partition = partition;
fs = VasEBoot_fs_probe (dev);
/* Unknown filesystem isn't fatal. */
if (VasEBoot_errno == VAS_EBOOT_ERR_UNKNOWN_FS)
{
fs = 0;
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
}
if (fs && VasEBoot_strcmp (fs->name, "ntfs") == 0)
type = VAS_EBOOT_PC_PARTITION_TYPE_NTFS;
else if (fs && VasEBoot_strcmp (fs->name, "fat") == 0)
/* FIXME: detect FAT16. */
type = VAS_EBOOT_PC_PARTITION_TYPE_FAT32_LBA;
else if (fs && (VasEBoot_strcmp (fs->name, "hfsplus") == 0
|| VasEBoot_strcmp (fs->name, "hfs") == 0))
type = VAS_EBOOT_PC_PARTITION_TYPE_HFS;
else
/* FIXME: detect more types. */
type = VAS_EBOOT_PC_PARTITION_TYPE_EXT2FS;
dev->disk->partition = 0;
}
mbr.entries[i].flag = (csep == '+') ? 0x80 : 0;
if (csep == '+')
{
numactive++;
if (numactive == 2)
{
VasEBoot_device_close (dev);
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT,
"only one partition can be active");
}
}
mbr.entries[i].type = type;
mbr.entries[i].start = VasEBoot_cpu_to_le32 (partition->start);
lba_to_chs (partition->start,
&(mbr.entries[i].start_sector),
&(mbr.entries[i].start_cylinder),
&(mbr.entries[i].start_head));
lba_to_chs (partition->start + partition->len - 1,
&(mbr.entries[i].end_sector),
&(mbr.entries[i].end_cylinder),
&(mbr.entries[i].end_head));
mbr.entries[i].length = VasEBoot_cpu_to_le32 (partition->len);
VasEBoot_free (partition);
}
for (; i < 4; i++)
VasEBoot_memset (&(mbr.entries[i]), 0, sizeof (mbr.entries[i]));
/* The protective partition. */
if (first_sector > 0xffffffff)
first_sector = 0xffffffff;
else
first_sector--;
mbr.entries[0].flag = 0;
mbr.entries[0].type = VAS_EBOOT_PC_PARTITION_TYPE_GPT_DISK;
mbr.entries[0].start = VasEBoot_cpu_to_le32_compile_time (1);
lba_to_chs (1,
&(mbr.entries[0].start_sector),
&(mbr.entries[0].start_cylinder),
&(mbr.entries[0].start_head));
lba_to_chs (first_sector,
&(mbr.entries[0].end_sector),
&(mbr.entries[0].end_cylinder),
&(mbr.entries[0].end_head));
mbr.entries[0].length = VasEBoot_cpu_to_le32 (first_sector);
mbr.signature = VasEBoot_cpu_to_le16_compile_time (VAS_EBOOT_PC_PARTITION_SIGNATURE);
if (VasEBoot_disk_write (dev->disk, 0, 0, sizeof (mbr), &mbr))
{
VasEBoot_device_close (dev);
return VasEBoot_errno;
}
VasEBoot_device_close (dev);
VasEBoot_printf_ (N_("New MBR is written to `%s'\n"), args[0]);
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_command_t cmd;
VAS_EBOOT_MOD_INIT(gptsync)
{
(void) mod; /* To stop warning. */
cmd = VasEBoot_register_command ("gptsync", VasEBoot_cmd_gptsync,
N_("DEVICE [PARTITION[+/-[TYPE]]] ..."),
/* TRANSLATORS: MBR type is one-byte partition
type id. */
N_("Fill hybrid MBR of GPT drive DEVICE. "
"Specified partitions will be a part "
"of hybrid MBR. Up to 3 partitions are "
"allowed. TYPE is an MBR type. "
"+ means that partition is active. "
"Only one partition can be active."));
}
VAS_EBOOT_MOD_FINI(gptsync)
{
VasEBoot_unregister_command (cmd);
}