/* * Block driver to use partitions as hard disks * * Copyright (c) 2007 Jim Brown * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* we need this in order to be able to figure out the sizes of the individual partitions */ #include "block_composite.h" /* in sectors */ #define MBR_SIZE 63 /* ideally this would be dynamically allocated */ #define MAX_PART_STRING 4096 typedef struct CompositeDriverState { BlockDriverState * bs; } CompositeDriverState; static int partition_probe(const uint8_t *buf, int buf_size, const char *filename) { if (strstart(filename, "partition:", NULL)) return 100; return 0; } static int partition_open(BlockDriverState *bs, const char *nfilename, int flags) { CompositeDriverState *s = bs->opaque; BlockDriverState * bbs; int64_t size, totalsectors; int boot_fd, i, bootid = 1, partition_count = 0; int head, cylinder, sector, oldstart; int oldhead, oldcylinder, oldsector; int sysid[10]; /* probably only need 4 */ const char * zfilename = &(nfilename[10]), * nptr = zfilename; char * filename, * strerr = NULL, * bootloader; char mbr_data[MBR_SIZE*512], partition_string[MAX_PART_STRING]; partition_string[0] = '\0'; strcat(partition_string, "composite:ram:63,"); /*63 == MBR_SIZE */ bootloader = strdup("bootmbr.bin"); bs->read_only = 0; int n = 0; while (nptr != NULL) { nptr = strchr(zfilename, ',')+1; if ((nptr-1) != NULL) { filename = strndup(zfilename, (size_t)((nptr-1)-zfilename)); zfilename = nptr; } else { filename = strdup(zfilename); nptr = NULL; } if (strncmp(filename, "sysid=", 6) == 0) { /* just pass in any sysid, e.g. 0x82 or 0x0C */ char ffd[strlen(filename)]; ffd[0] = ' '; ffd[1] = '\0'; strcat(ffd, filename+6); sysid[partition_count] = (int)strtol(ffd, &strerr, 0); if (*strerr != '\0') /* detect error in conversion */ sysid[partition_count-1] = 0x0C; /* default to win98 FAT32 */ } else if (strncmp(filename, "boot=", 5) == 0) { /* between 1 and 4, 1 selects the first partition, 4 selects the last partition (if you are making a hard disk with 4 partitions in it) */ char ffd[strlen(filename)]; int bootidf; ffd[0] = ' '; ffd[1] = '\0'; strcat(ffd, filename+5); bootidf = (int)strtol(ffd, &strerr, 0); if (*strerr == '\0') /* no detect error in conversion */ bootid = bootidf; } else if (strncmp(filename, "bootloader=", 11) == 0) { free(bootloader); bootloader = strdup(filename+11); } else { /* the string shouldn't start with a ',' */ if (n) strcat(partition_string, ","); else n = 1; strcat(partition_string, filename); partition_count ++; sysid[partition_count-1] = 0x0C; /* default to win98 FAT32 */ } free(filename); } s->bs = qemu_mallocz(sizeof(BlockDriverState)); if ((s->bs == NULL) || (bdrv_open2(s->bs, partition_string, 0, NULL) != 0)) { free(bootloader); return -1; } bs->total_sectors = s->bs->total_sectors; bs->read_only = s->bs->read_only; /* get the fake MBR */ memset(mbr_data, 0, MBR_SIZE*512); boot_fd = open(bootloader, O_RDONLY); if (boot_fd == -1) { printf("Warning: failed to open bootmbr.bin - MBR will not be bootable\n"); } else { if (read(boot_fd, mbr_data, MBR_SIZE*512) == -1) { printf("Warning: failed to read bootmbr.bin - MBR will not be bootable\n"); } close(boot_fd); } oldstart = 0x3F; //3F == 63 oldhead = 0x01; oldsector = 0x01; oldcylinder = 0x00; /* remember that the very first slave in the composite is the ram image ** * that we're using to store the MBR, so the second slave in the composite * is the first partition */ for (i = 1; i <= partition_count; i++) { /* set up c/h/s */ if (i == partition_count) { totalsectors = bs->total_sectors; } else { bbs = ((BDRVPartState*)s->bs->opaque)->slave_bs[i+1].bs; totalsectors = bbs->total_sectors; } size = totalsectors * 512; cylinder = size/(63*16); head = 16; sector = 63; /* some bit twiddling here */ sector = (((cylinder >> 8) & 3) << 6) + sector; /* set up fake MBR - each partition entry is 16 bytes long */ /* start 446 */ /* set which partition is meant to be bootable (that we'll boot from) */ if (i == bootid) mbr_data[446+((i-1)*16)] = 0x80; else mbr_data[446+((i-1)*16)] = 0x00; /* start head */ mbr_data[447+((i-1)*16)] = oldhead; /* start sector - only first 6 bits */ mbr_data[448+((i-1)*16)] = oldsector; /* start cylinder - this byte plus 2 bits from mbr_data[447] */ mbr_data[449+((i-1)*16)] = oldcylinder; /* system ID */ mbr_data[450+((i-1)*16)] = sysid[i-1]; /* ending head */ mbr_data[451+((i-1)*16)] = head; /* ending sector */ mbr_data[452+((i-1)*16)] = sector; /* ending cylinder */ mbr_data[453+((i-1)*16)] = cylinder; /* absolute start sector - 4 bytes/DWORD */ //mbr_data[454+(i*16)] = 0x3F; // 3F = 63 *((uint32_t*)(mbr_data+454+((i-1)*16))) = cpu_to_le32(oldstart); /* absolute total number of sectors - 4 bytes/DWORD */ *((uint32_t*)(mbr_data+458+((i-1)*16))) = cpu_to_le32(totalsectors); /* end 462 */ oldstart = oldstart + totalsectors; oldhead = head; oldcylinder = cylinder; oldsector = sector; oldsector++; if (oldsector > 63) { oldhead++; oldsector = 0x01; if (oldhead > 16) { oldcylinder++; oldhead = 0x01; } } } /* set the MBR sector signature */ mbr_data[510] = 0x55; mbr_data[511] = 0xAA; /* now write it to the ram image */ /* bs->boot_sector_enabled = 0; s->bs->boot_sector_enabled = 0; bbs = ((BDRVPartState*)s->bs->opaque)->slave_bs[0].bs; the ram image bbs->boot_sector_enabled = 0; */ if (bdrv_write(s->bs, 0, mbr_data, MBR_SIZE) != 0) printf("Warning: failed to commit MBR and partition table to fake hard disk\n"); return 0; } static int partition_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { CompositeDriverState *s = bs->opaque; return bdrv_read(s->bs, sector_num, buf, nb_sectors); } static int partition_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { CompositeDriverState *s = bs->opaque; return bdrv_write(s->bs, sector_num, buf, nb_sectors); } static void partition_close(BlockDriverState *bs) { CompositeDriverState *s = bs->opaque; bdrv_close(s->bs); qemu_free(s->bs); s->bs = NULL; } static int partition_create(const char *filename, int64_t total_size, const char *backing_file, int flags) { /* what would be the point... just make a raw or qcow */ return -ENOTSUP; } BlockDriver bdrv_partition = { "partition", sizeof(CompositeDriverState), partition_probe, partition_open, partition_read, partition_write, partition_close, partition_create, .protocol_name = "partition", };