qemu/block/bochs.c
<<
>>
Prefs
   1/*
   2 * Block driver for the various disk image formats used by Bochs
   3 * Currently only for "growing" type in read-only mode
   4 *
   5 * Copyright (c) 2005 Alex Beregszaszi
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25#include "qemu-common.h"
  26#include "block/block_int.h"
  27#include "qemu/module.h"
  28
  29/**************************************************************/
  30
  31#define HEADER_MAGIC "Bochs Virtual HD Image"
  32#define HEADER_VERSION 0x00020000
  33#define HEADER_V1 0x00010000
  34#define HEADER_SIZE 512
  35
  36#define REDOLOG_TYPE "Redolog"
  37#define GROWING_TYPE "Growing"
  38
  39// not allocated: 0xffffffff
  40
  41// always little-endian
  42struct bochs_header_v1 {
  43    char magic[32]; // "Bochs Virtual HD Image"
  44    char type[16]; // "Redolog"
  45    char subtype[16]; // "Undoable" / "Volatile" / "Growing"
  46    uint32_t version;
  47    uint32_t header; // size of header
  48
  49    union {
  50        struct {
  51            uint32_t catalog; // num of entries
  52            uint32_t bitmap; // bitmap size
  53            uint32_t extent; // extent size
  54            uint64_t disk; // disk size
  55            char padding[HEADER_SIZE - 64 - 8 - 20];
  56        } redolog;
  57        char padding[HEADER_SIZE - 64 - 8];
  58    } extra;
  59};
  60
  61// always little-endian
  62struct bochs_header {
  63    char magic[32]; // "Bochs Virtual HD Image"
  64    char type[16]; // "Redolog"
  65    char subtype[16]; // "Undoable" / "Volatile" / "Growing"
  66    uint32_t version;
  67    uint32_t header; // size of header
  68
  69    union {
  70        struct {
  71            uint32_t catalog; // num of entries
  72            uint32_t bitmap; // bitmap size
  73            uint32_t extent; // extent size
  74            uint32_t reserved; // for ???
  75            uint64_t disk; // disk size
  76            char padding[HEADER_SIZE - 64 - 8 - 24];
  77        } redolog;
  78        char padding[HEADER_SIZE - 64 - 8];
  79    } extra;
  80};
  81
  82typedef struct BDRVBochsState {
  83    CoMutex lock;
  84    uint32_t *catalog_bitmap;
  85    int catalog_size;
  86
  87    int data_offset;
  88
  89    int bitmap_blocks;
  90    int extent_blocks;
  91    int extent_size;
  92} BDRVBochsState;
  93
  94static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
  95{
  96    const struct bochs_header *bochs = (const void *)buf;
  97
  98    if (buf_size < HEADER_SIZE)
  99        return 0;
 100
 101    if (!strcmp(bochs->magic, HEADER_MAGIC) &&
 102        !strcmp(bochs->type, REDOLOG_TYPE) &&
 103        !strcmp(bochs->subtype, GROWING_TYPE) &&
 104        ((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
 105        (le32_to_cpu(bochs->version) == HEADER_V1)))
 106        return 100;
 107
 108    return 0;
 109}
 110
 111static int bochs_open(BlockDriverState *bs, int flags)
 112{
 113    BDRVBochsState *s = bs->opaque;
 114    int i;
 115    struct bochs_header bochs;
 116    struct bochs_header_v1 header_v1;
 117    int ret;
 118
 119    bs->read_only = 1; // no write support yet
 120
 121    ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
 122    if (ret < 0) {
 123        return ret;
 124    }
 125
 126    if (strcmp(bochs.magic, HEADER_MAGIC) ||
 127        strcmp(bochs.type, REDOLOG_TYPE) ||
 128        strcmp(bochs.subtype, GROWING_TYPE) ||
 129        ((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
 130        (le32_to_cpu(bochs.version) != HEADER_V1))) {
 131        return -EMEDIUMTYPE;
 132    }
 133
 134    if (le32_to_cpu(bochs.version) == HEADER_V1) {
 135      memcpy(&header_v1, &bochs, sizeof(bochs));
 136      bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
 137    } else {
 138      bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
 139    }
 140
 141    s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
 142    s->catalog_bitmap = g_malloc(s->catalog_size * 4);
 143
 144    ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
 145                     s->catalog_size * 4);
 146    if (ret < 0) {
 147        goto fail;
 148    }
 149
 150    for (i = 0; i < s->catalog_size; i++)
 151        le32_to_cpus(&s->catalog_bitmap[i]);
 152
 153    s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
 154
 155    s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
 156    s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
 157
 158    s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
 159
 160    qemu_co_mutex_init(&s->lock);
 161    return 0;
 162
 163fail:
 164    g_free(s->catalog_bitmap);
 165    return ret;
 166}
 167
 168static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
 169{
 170    BDRVBochsState *s = bs->opaque;
 171    int64_t offset = sector_num * 512;
 172    int64_t extent_index, extent_offset, bitmap_offset;
 173    char bitmap_entry;
 174
 175    // seek to sector
 176    extent_index = offset / s->extent_size;
 177    extent_offset = (offset % s->extent_size) / 512;
 178
 179    if (s->catalog_bitmap[extent_index] == 0xffffffff) {
 180        return -1; /* not allocated */
 181    }
 182
 183    bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
 184        (s->extent_blocks + s->bitmap_blocks));
 185
 186    /* read in bitmap for current extent */
 187    if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
 188                   &bitmap_entry, 1) != 1) {
 189        return -1;
 190    }
 191
 192    if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
 193        return -1; /* not allocated */
 194    }
 195
 196    return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
 197}
 198
 199static int bochs_read(BlockDriverState *bs, int64_t sector_num,
 200                    uint8_t *buf, int nb_sectors)
 201{
 202    int ret;
 203
 204    while (nb_sectors > 0) {
 205        int64_t block_offset = seek_to_sector(bs, sector_num);
 206        if (block_offset >= 0) {
 207            ret = bdrv_pread(bs->file, block_offset, buf, 512);
 208            if (ret != 512) {
 209                return -1;
 210            }
 211        } else
 212            memset(buf, 0, 512);
 213        nb_sectors--;
 214        sector_num++;
 215        buf += 512;
 216    }
 217    return 0;
 218}
 219
 220static coroutine_fn int bochs_co_read(BlockDriverState *bs, int64_t sector_num,
 221                                      uint8_t *buf, int nb_sectors)
 222{
 223    int ret;
 224    BDRVBochsState *s = bs->opaque;
 225    qemu_co_mutex_lock(&s->lock);
 226    ret = bochs_read(bs, sector_num, buf, nb_sectors);
 227    qemu_co_mutex_unlock(&s->lock);
 228    return ret;
 229}
 230
 231static void bochs_close(BlockDriverState *bs)
 232{
 233    BDRVBochsState *s = bs->opaque;
 234    g_free(s->catalog_bitmap);
 235}
 236
 237static BlockDriver bdrv_bochs = {
 238    .format_name        = "bochs",
 239    .instance_size      = sizeof(BDRVBochsState),
 240    .bdrv_probe         = bochs_probe,
 241    .bdrv_open          = bochs_open,
 242    .bdrv_read          = bochs_co_read,
 243    .bdrv_close         = bochs_close,
 244};
 245
 246static void bdrv_bochs_init(void)
 247{
 248    bdrv_register(&bdrv_bochs);
 249}
 250
 251block_init(bdrv_bochs_init);
 252