/* * Block driver to use partition images instead of whole hard disk images * * 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. */ #include "vl.h" #include "block_int.h" #ifdef __sun__ #include #endif typedef struct BDRVPartRawState { char mbr_data[63*512]; int fd; } BDRVPartRawState; static int part_raw_probe(const uint8_t *buf, int buf_size, const char *filename) { if (strstart(filename, "part:", NULL)) return 100; return 0; } static int part_raw_open(BlockDriverState *bs, const char *nfilename) { BDRVPartRawState *s = bs->opaque; int fd, boot_fd; int64_t size; #ifdef _BSD struct stat sb; #endif #ifdef __sun__ struct dk_minfo minfo; int rv; #endif int head, cylinder, sector; const char * filename = &(nfilename[5]); fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); if (fd < 0) { fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); if (fd < 0) return -1; bs->read_only = 1; } #ifdef _BSD if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { #ifdef DIOCGMEDIASIZE if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) #endif #ifdef CONFIG_COCOA size = LONG_LONG_MAX; #else size = lseek(fd, 0LL, SEEK_END); #endif } else #endif #ifdef __sun__ /* * use the DKIOCGMEDIAINFO ioctl to read the size. */ rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); if ( rv != -1 ) { size = minfo.dki_lbsize * minfo.dki_capacity; } else /* there are reports that lseek on some devices fails, but irc discussion said that contingency on contingency was overkill */ #endif { size = lseek(fd, 0, SEEK_END); } bs->total_sectors = (size / 512) + 63; s->fd = fd; /* set up c/h/s */ size = size+(63*512); cylinder = size/(63*16); /* FIXME */ cylinder = cylinder + 1; /* add a cylinder just in case partition extends beyond the edge of the last cylinder/head/track */ head = 16; sector = 63; /* some bit twiddling here */ sector = (((cylinder >> 8) & 3) << 6) + sector; /* set up fake MBR */ memset(s->mbr_data, 0, 63*512); boot_fd = open("bootmbr.bin", O_RDONLY); if (boot_fd == -1) { printf("Warning: failed to open bootsector.bin - MBR will not be bootbale\n"); } else { if (read(boot_fd, s->mbr_data, 512) == -1) { printf("Warning: failed to read bootsector.bin - MBR will not be bootbale\n"); } close(boot_fd); } /* first partition is bootable */ s->mbr_data[446] = 0x80; /* start head */ s->mbr_data[447] = 0x01; /* start sector - only first 6 bits */ s->mbr_data[448] = 0x01; /* start cylinder - this byte plus 2 bits from mbr_data[447] */ s->mbr_data[449] = 0x00; /* system ID */ s->mbr_data[450] = 0x0C; /* say we're win98 fat32 */ /* ending head */ s->mbr_data[451] = head; /* ending sector */ s->mbr_data[452] = sector; /* ending cylinder */ s->mbr_data[453] = cylinder; /* absolute start sector - 4 bytes/DWORD */ s->mbr_data[454] = 0x3F; // 3F = 63 /* absolute total number of sectors - 4 bytes/DWORD */ *((uint32_t*)(s->mbr_data+458)) = cpu_to_le32(bs->total_sectors - 63); /* leave the other partitions blank - we only support the first one */ /* set the MBR sector signature */ s->mbr_data[510] = 0x55; s->mbr_data[511] = 0xAA; return 0; } static int part_raw_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { BDRVPartRawState *s = bs->opaque; int ret,split; if (sector_num >= 63) { lseek(s->fd, (sector_num - 63) * 512, SEEK_SET); ret = read(s->fd, buf, nb_sectors * 512); if (ret != nb_sectors * 512) return -1; return 0; } else { if ((nb_sectors + sector_num) > 63) { /* ah hell - we have to do both the fake part and the real part */ split = nb_sectors + sector_num - 63; ret = part_raw_read(bs, 63, &buf[(nb_sectors-split)*512], split * 512); if (ret != split * 512) return -1; /* this will always return 0 */ ret = part_raw_read(bs, sector_num, buf, (nb_sectors - split) * 512); return 0; } else { memcpy(buf, &(s->mbr_data[sector_num*512]), nb_sectors*512); return 0; } } } static int part_raw_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { BDRVPartRawState *s = bs->opaque; int ret, split; if (sector_num >= 63) { lseek(s->fd, (sector_num - 63) * 512, SEEK_SET); ret = write(s->fd, buf, nb_sectors * 512); if (ret != nb_sectors * 512) return -1; return 0; } else { if ((nb_sectors + sector_num) > 63) { /* ah hell - we have to do both the fake part and the real part */ split = nb_sectors + sector_num - 63; ret = part_raw_write(bs, 63, &buf[(nb_sectors-split)*512], split * 512); if (ret != split * 512) return -1; /* this will always return 0 */ ret = part_raw_write(bs, sector_num, buf, (nb_sectors - split) * 512); return 0; } else { memcpy(&(s->mbr_data[sector_num*512]), buf, nb_sectors*512); return 0; } } } static void part_raw_close(BlockDriverState *bs) { BDRVPartRawState *s = bs->opaque; close(s->fd); } static int part_raw_create(const char *filename, int64_t total_size, const char *backing_file, int flags) { int fd; if (flags || backing_file) return -ENOTSUP; fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); if (fd < 0) return -EIO; ftruncate(fd, total_size * 512); close(fd); return 0; } BlockDriver bdrv_part_raw = { "part_raw", sizeof(BDRVPartRawState), part_raw_probe, part_raw_open, part_raw_read, part_raw_write, part_raw_close, part_raw_create, };