tools/mbrtool: A simple tool to create MBR partitions

Change-Id: I0f04b257ad49d07c03d630df47c4891c1fd7a954
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3570
Reviewed-by: Stephan Aßmus <superstippi@gmx.de>
This commit is contained in:
Alexander von Gluck IV 2020-12-30 12:29:48 -06:00 committed by Alex von Gluck IV
parent 242adb20f3
commit e9a5037fba
3 changed files with 216 additions and 0 deletions

View File

@ -106,6 +106,7 @@ SubInclude HAIKU_TOP src tools hvif2png ;
SubInclude HAIKU_TOP src tools keymap ;
SubInclude HAIKU_TOP src tools locale ;
SubInclude HAIKU_TOP src tools makebootable ;
SubInclude HAIKU_TOP src tools mbrtool ;
SubInclude HAIKU_TOP src tools opd_to_package_info ;
SubInclude HAIKU_TOP src tools package ;
SubInclude HAIKU_TOP src tools package_repo ;

View File

@ -0,0 +1,3 @@
SubDir HAIKU_TOP src tools mbrtool ;
BuildPlatformMain <build>mbrtool : mbrtool.cpp : $(HOST_LIBSUPC++) ;

View File

@ -0,0 +1,212 @@
/*
* Copyright 2020-2021 Haiku, Inc. All rights reserved.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck IV <kallisti5@unixzen.com>
*/
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#define EXIT_FAILURE 1
//#define DEBUG_MBRTOOL
#ifdef DEBUG_MBRTOOL
# define TRACE(x...) printf("mbrtool: " x)
#else
# define TRACE(x...) ;
#endif
#define INFO(x...) printf("mbrtool: " x)
// Disk sector size, 512 assumed!
static const size_t kSectorSize = 512;
static void
print_usage(bool error)
{
printf("\n");
printf("usage: mbrtool (options) <diskImage> <id> <type> <start> <len>\n");
printf(" <diskImage> Disk image to operate on\n");
printf(" <id> Partition ID (0-3)\n");
printf(" <type> Partition type id (hex)\n");
printf(" <start> Partition start offset (KiB)\n");
printf(" <len> Partition length (KiB)\n\n");
printf(" Options:\n");
printf(" -a, --active Partition boot flag\n");
printf("\nWarning: This tool requires precision!\n");
printf(" Inputs are only lightly validated!\n\n");
exit(error ? EXIT_FAILURE : 0);
}
static void
checkError(bool failed, const char *message)
{
if (!failed)
return;
if (errno != 0)
INFO("Error: %s: %s\n", message, strerror(errno));
else
INFO("Error: %s\n", message);
exit(1);
}
static ssize_t
mbrWipe(int handle)
{
// Blow away MBR while avoiding bootloader
uint8_t emptyMBR[66] = {};
// End of MBR marker
emptyMBR[64] = 0x55;
emptyMBR[65] = 0xAA;
return pwrite(handle, emptyMBR, 66, 0x1BE);
}
static bool
mbrValid(int handle)
{
// TODO: this is a really basic check and ignores invalid
// partition table entries
uint8_t mbrBytes[66] = {};
ssize_t read = pread(handle, mbrBytes, 66, 0x1BE);
checkError(read < 0, "failed to read MBR for validation");
return (mbrBytes[64] == 0x55 && mbrBytes[65] == 0xAA);
}
static void
createPartition(int handle, int index, bool active, uint8_t type,
uint64_t offset, uint64_t size)
{
uint8_t bootable = active ? 0x80 : 0x0;
uint8_t partition[16] = {
bootable, // bootable
0xff, 0xff, 0xff, // CHS first block (default to LBA)
type, // partition type
0xff, 0xff, 0xff, // CHS last block (default to LBA)
0x00, 0x00, 0x00, 0x00, // imageOffset in blocks (written below)
0x00, 0x00, 0x00, 0x00 // imageSize in blocks (written below)
};
// fill in LBA values
uint32_t partitionOffset = (uint32_t)(offset / kSectorSize);
((uint32_t *)partition)[2] = partitionOffset;
((uint32_t *)partition)[3] = (uint32_t)(size / kSectorSize);
TRACE("%s: #%d %c bytes: %u-%u, sectors: %u-%u\n", __func__, index,
active ? 'b' : '-', offset, offset + size, partitionOffset,
partitionOffset + uint32_t(size / kSectorSize));
ssize_t written = pwrite(handle, partition, 16, 512 - 2 - 16 * (4 - index));
checkError(written != 16, "failed to write partition entry");
if (active) {
// make it bootable
written = pwrite(handle, &partitionOffset, 4, offset + 512 - 2 - 4);
checkError(written != 4, "failed to make partition bootable");
}
return;
}
int
main(int argc, char *argv[])
{
const char *imageFile = NULL;
int partType = -1;
int partIndex = -1;
int64_t partStartOffset = -1;
int64_t partLength = -1;
bool partBootable = false;
while (1) {
int c;
static struct option long_options[] = {
{"active", no_argument, 0, 'a'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
opterr = 1; /* don't print errors */
c = getopt_long(argc, argv, "+ha", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 's':
print_usage(false);
break;
case 'a':
partBootable = true;
break;
default:
print_usage(true);
}
}
if ((argc - optind) != 5)
print_usage(true);
for (int index = optind; index < argc; index++) {
if (imageFile == NULL)
imageFile = argv[index];
else if (partIndex < 0)
partIndex = atoi(argv[index]);
else if (partType < 0)
partType = (int)strtol(argv[index], NULL, 0);
else if (partStartOffset < 0 ) {
partStartOffset = (int64_t)strtol(argv[index], NULL, 10);
partStartOffset *= 1024;
} else if (partLength < 0) {
partLength = (int64_t)strtol(argv[index], NULL, 10);
partLength *= 1024;
}
}
checkError(partIndex < 0 || partIndex > 3,
"invalid partition index, valid range is 0-3");
checkError(partType < 0, "Incorrect Partition Type!");
checkError((partStartOffset + partLength) > 2089072000000,
"partitions beyond 2TiB are not accepted!");
int imageFileHandle = open(imageFile, O_RDWR);
checkError(imageFileHandle < 0, "failed to open disk image file");
struct stat stat;
int result = fstat(imageFileHandle, &stat);
checkError(result != 0, "failed to stat image file");
off_t imageSize = stat.st_size;
if (!mbrValid(imageFileHandle)) {
INFO("MBR of image is invalid, creating a fresh one.\n");
mbrWipe(imageFileHandle);
}
// Just a warning. This is technically valid since MBR partition
// definitions are purely within the first 512 bytes
if (partStartOffset + partLength > imageSize)
INFO("Warning: Partition extends beyond end of file!\n");
createPartition(imageFileHandle, partIndex, partBootable, partType,
partStartOffset, partLength);
return 0;
}