diff options
-rw-r--r-- | block.c | 4 | ||||
-rw-r--r-- | block/raw.c | 130 | ||||
-rw-r--r-- | block_int.h | 1 |
3 files changed, 135 insertions, 0 deletions
@@ -511,6 +511,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, BlockDriver *drv) { int ret; + int probed = 0; if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; @@ -571,6 +572,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, /* Find the right image format driver */ if (!drv) { drv = find_image_format(filename); + probed = 1; } if (!drv) { @@ -584,6 +586,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, goto unlink_and_fail; } + bs->probed = probed; + /* If there is a backing file, use it */ if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') { char backing_filename[PATH_MAX]; diff --git a/block/raw.c b/block/raw.c index 4406b8c06b..1414e777b3 100644 --- a/block/raw.c +++ b/block/raw.c @@ -9,15 +9,82 @@ static int raw_open(BlockDriverState *bs, int flags) return 0; } +/* check for the user attempting to write something that looks like a + block format header to the beginning of the image and fail out. +*/ +static int check_for_block_signature(BlockDriverState *bs, const uint8_t *buf) +{ + static const uint8_t signatures[][4] = { + { 'Q', 'F', 'I', 0xfb }, /* qcow/qcow2 */ + { 'C', 'O', 'W', 'D' }, /* VMDK3 */ + { 'V', 'M', 'D', 'K' }, /* VMDK4 */ + { 'O', 'O', 'O', 'M' }, /* UML COW */ + {} + }; + int i; + + for (i = 0; signatures[i][0] != 0; i++) { + if (memcmp(buf, signatures[i], 4) == 0) { + return 1; + } + } + + return 0; +} + +static int check_write_unsafe(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + /* assume that if the user specifies the format explicitly, then assume + that they will continue to do so and provide no safety net */ + if (!bs->probed) { + return 0; + } + + if (sector_num == 0 && nb_sectors > 0) { + return check_for_block_signature(bs, buf); + } + + return 0; +} + static int raw_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { return bdrv_read(bs->file, sector_num, buf, nb_sectors); } +static int raw_write_scrubbed_bootsect(BlockDriverState *bs, + const uint8_t *buf) +{ + uint8_t bootsect[512]; + + /* scrub the dangerous signature */ + memcpy(bootsect, buf, 512); + memset(bootsect, 0, 4); + + return bdrv_write(bs->file, 0, bootsect, 1); +} + static int raw_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { + if (check_write_unsafe(bs, sector_num, buf, nb_sectors)) { + int ret; + + ret = raw_write_scrubbed_bootsect(bs, buf); + if (ret < 0) { + return ret; + } + + ret = bdrv_write(bs->file, 1, buf + 512, nb_sectors - 1); + if (ret < 0) { + return ret; + } + + return ret + 512; + } + return bdrv_write(bs->file, sector_num, buf, nb_sectors); } @@ -28,10 +95,73 @@ static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); } +typedef struct RawScrubberBounce +{ + BlockDriverCompletionFunc *cb; + void *opaque; + QEMUIOVector qiov; +} RawScrubberBounce; + +static void raw_aio_writev_scrubbed(void *opaque, int ret) +{ + RawScrubberBounce *b = opaque; + + if (ret < 0) { + b->cb(b->opaque, ret); + } else { + b->cb(b->opaque, ret + 512); + } + + qemu_iovec_destroy(&b->qiov); + qemu_free(b); +} + static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { + const uint8_t *first_buf; + int first_buf_index = 0, i; + + /* This is probably being paranoid, but handle cases of zero size + vectors. */ + for (i = 0; i < qiov->niov; i++) { + if (qiov->iov[i].iov_len) { + assert(qiov->iov[i].iov_len >= 512); + first_buf_index = i; + break; + } + } + + first_buf = qiov->iov[first_buf_index].iov_base; + + if (check_write_unsafe(bs, sector_num, first_buf, nb_sectors)) { + RawScrubberBounce *b; + int ret; + + /* write the first sector using sync I/O */ + ret = raw_write_scrubbed_bootsect(bs, first_buf); + if (ret < 0) { + return NULL; + } + + /* adjust request to be everything but first sector */ + + b = qemu_malloc(sizeof(*b)); + b->cb = cb; + b->opaque = opaque; + + qemu_iovec_init(&b->qiov, qiov->nalloc); + qemu_iovec_concat(&b->qiov, qiov, qiov->size); + + b->qiov.size -= 512; + b->qiov.iov[first_buf_index].iov_base += 512; + b->qiov.iov[first_buf_index].iov_len -= 512; + + return bdrv_aio_writev(bs->file, sector_num + 1, &b->qiov, + nb_sectors - 1, raw_aio_writev_scrubbed, b); + } + return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); } diff --git a/block_int.h b/block_int.h index 877e1e5943..96ff4cf1bc 100644 --- a/block_int.h +++ b/block_int.h @@ -144,6 +144,7 @@ struct BlockDriverState { int encrypted; /* if true, the media is encrypted */ int valid_key; /* if true, a valid encryption key has been set */ int sg; /* if true, the device is a /dev/sg* */ + int probed; /* if true, format was probed automatically */ /* event callback when inserting/removing */ void (*change_cb)(void *opaque); void *change_opaque; |