* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "block/block_int.h"
#include "qemu/module.h"
+#include "qemu/bswap.h"
#include <zlib.h>
+/* Maximum compressed block size */
+#define MAX_BLOCK_SIZE (64 * 1024 * 1024)
+
typedef struct BDRVCloopState {
CoMutex lock;
uint32_t block_size;
return 0;
}
-static int cloop_open(BlockDriverState *bs, int flags)
+static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
+ Error **errp)
{
BDRVCloopState *s = bs->opaque;
uint32_t offsets_size, max_compressed_block_size = 1, i;
int ret;
bs->read_only = 1;
+ bs->request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O supported */
/* read header */
- ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
+ ret = bdrv_pread(bs->file->bs, 128, &s->block_size, 4);
if (ret < 0) {
return ret;
}
s->block_size = be32_to_cpu(s->block_size);
+ if (s->block_size % 512) {
+ error_setg(errp, "block_size %" PRIu32 " must be a multiple of 512",
+ s->block_size);
+ return -EINVAL;
+ }
+ if (s->block_size == 0) {
+ error_setg(errp, "block_size cannot be zero");
+ return -EINVAL;
+ }
+
+ /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but
+ * we can accept more. Prevent ridiculous values like 4 GB - 1 since we
+ * need a buffer this big.
+ */
+ if (s->block_size > MAX_BLOCK_SIZE) {
+ error_setg(errp, "block_size %" PRIu32 " must be %u MB or less",
+ s->block_size,
+ MAX_BLOCK_SIZE / (1024 * 1024));
+ return -EINVAL;
+ }
- ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
+ ret = bdrv_pread(bs->file->bs, 128 + 4, &s->n_blocks, 4);
if (ret < 0) {
return ret;
}
s->n_blocks = be32_to_cpu(s->n_blocks);
/* read offsets */
- offsets_size = s->n_blocks * sizeof(uint64_t);
- s->offsets = g_malloc(offsets_size);
+ if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) {
+ /* Prevent integer overflow */
+ error_setg(errp, "n_blocks %" PRIu32 " must be %zu or less",
+ s->n_blocks,
+ (UINT32_MAX - 1) / sizeof(uint64_t));
+ return -EINVAL;
+ }
+ offsets_size = (s->n_blocks + 1) * sizeof(uint64_t);
+ if (offsets_size > 512 * 1024 * 1024) {
+ /* Prevent ridiculous offsets_size which causes memory allocation to
+ * fail or overflows bdrv_pread() size. In practice the 512 MB
+ * offsets[] limit supports 16 TB images at 256 KB block size.
+ */
+ error_setg(errp, "image requires too many offsets, "
+ "try increasing block size");
+ return -EINVAL;
+ }
- ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
+ s->offsets = g_try_malloc(offsets_size);
+ if (s->offsets == NULL) {
+ error_setg(errp, "Could not allocate offsets table");
+ return -ENOMEM;
+ }
+
+ ret = bdrv_pread(bs->file->bs, 128 + 4 + 4, s->offsets, offsets_size);
if (ret < 0) {
goto fail;
}
- for(i=0;i<s->n_blocks;i++) {
+ for (i = 0; i < s->n_blocks + 1; i++) {
+ uint64_t size;
+
s->offsets[i] = be64_to_cpu(s->offsets[i]);
- if (i > 0) {
- uint32_t size = s->offsets[i] - s->offsets[i - 1];
- if (size > max_compressed_block_size) {
- max_compressed_block_size = size;
- }
+ if (i == 0) {
+ continue;
+ }
+
+ if (s->offsets[i] < s->offsets[i - 1]) {
+ error_setg(errp, "offsets not monotonically increasing at "
+ "index %" PRIu32 ", image file is corrupt", i);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ size = s->offsets[i] - s->offsets[i - 1];
+
+ /* Compressed blocks should be smaller than the uncompressed block size
+ * but maybe compression performed poorly so the compressed block is
+ * actually bigger. Clamp down on unrealistic values to prevent
+ * ridiculous s->compressed_block allocation.
+ */
+ if (size > 2 * MAX_BLOCK_SIZE) {
+ error_setg(errp, "invalid compressed block size at index %" PRIu32
+ ", image file is corrupt", i);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (size > max_compressed_block_size) {
+ max_compressed_block_size = size;
}
}
/* initialize zlib engine */
- s->compressed_block = g_malloc(max_compressed_block_size + 1);
- s->uncompressed_block = g_malloc(s->block_size);
+ s->compressed_block = g_try_malloc(max_compressed_block_size + 1);
+ if (s->compressed_block == NULL) {
+ error_setg(errp, "Could not allocate compressed_block");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ s->uncompressed_block = g_try_malloc(s->block_size);
+ if (s->uncompressed_block == NULL) {
+ error_setg(errp, "Could not allocate uncompressed_block");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
if (inflateInit(&s->zstream) != Z_OK) {
ret = -EINVAL;
goto fail;
int ret;
uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num];
- ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block,
- bytes);
+ ret = bdrv_pread(bs->file->bs, s->offsets[block_num],
+ s->compressed_block, bytes);
if (ret != bytes) {
return -1;
}
return 0;
}
-static int cloop_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
+static int coroutine_fn
+cloop_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
{
BDRVCloopState *s = bs->opaque;
- int i;
+ uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
+ int nb_sectors = bytes >> BDRV_SECTOR_BITS;
+ int ret, i;
+
+ assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
+
+ qemu_co_mutex_lock(&s->lock);
for (i = 0; i < nb_sectors; i++) {
+ void *data;
uint32_t sector_offset_in_block =
((sector_num + i) % s->sectors_per_block),
block_num = (sector_num + i) / s->sectors_per_block;
if (cloop_read_block(bs, block_num) != 0) {
- return -1;
+ ret = -EIO;
+ goto fail;
}
- memcpy(buf + i * 512,
- s->uncompressed_block + sector_offset_in_block * 512, 512);
+
+ data = s->uncompressed_block + sector_offset_in_block * 512;
+ qemu_iovec_from_buf(qiov, i * 512, data, 512);
}
- return 0;
-}
-static coroutine_fn int cloop_co_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
-{
- int ret;
- BDRVCloopState *s = bs->opaque;
- qemu_co_mutex_lock(&s->lock);
- ret = cloop_read(bs, sector_num, buf, nb_sectors);
+ ret = 0;
+fail:
qemu_co_mutex_unlock(&s->lock);
+
return ret;
}
static void cloop_close(BlockDriverState *bs)
{
BDRVCloopState *s = bs->opaque;
- if (s->n_blocks > 0) {
- g_free(s->offsets);
- }
+ g_free(s->offsets);
g_free(s->compressed_block);
g_free(s->uncompressed_block);
inflateEnd(&s->zstream);
.instance_size = sizeof(BDRVCloopState),
.bdrv_probe = cloop_probe,
.bdrv_open = cloop_open,
- .bdrv_read = cloop_co_read,
+ .bdrv_co_preadv = cloop_co_preadv,
.bdrv_close = cloop_close,
};