1019d6b8fSAnthony Liguori /*
2019d6b8fSAnthony Liguori * QEMU Block driver for CLOOP images
3019d6b8fSAnthony Liguori *
4019d6b8fSAnthony Liguori * Copyright (c) 2004 Johannes E. Schindelin
5019d6b8fSAnthony Liguori *
6019d6b8fSAnthony Liguori * Permission is hereby granted, free of charge, to any person obtaining a copy
7019d6b8fSAnthony Liguori * of this software and associated documentation files (the "Software"), to deal
8019d6b8fSAnthony Liguori * in the Software without restriction, including without limitation the rights
9019d6b8fSAnthony Liguori * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10019d6b8fSAnthony Liguori * copies of the Software, and to permit persons to whom the Software is
11019d6b8fSAnthony Liguori * furnished to do so, subject to the following conditions:
12019d6b8fSAnthony Liguori *
13019d6b8fSAnthony Liguori * The above copyright notice and this permission notice shall be included in
14019d6b8fSAnthony Liguori * all copies or substantial portions of the Software.
15019d6b8fSAnthony Liguori *
16019d6b8fSAnthony Liguori * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17019d6b8fSAnthony Liguori * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18019d6b8fSAnthony Liguori * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19019d6b8fSAnthony Liguori * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20019d6b8fSAnthony Liguori * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21019d6b8fSAnthony Liguori * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22019d6b8fSAnthony Liguori * THE SOFTWARE.
23019d6b8fSAnthony Liguori */
2480c71a24SPeter Maydell #include "qemu/osdep.h"
25da34e65cSMarkus Armbruster #include "qapi/error.h"
26398e6ad0SKevin Wolf #include "qemu/error-report.h"
27e2c1c34fSMarkus Armbruster #include "block/block-io.h"
28737e150eSPaolo Bonzini #include "block/block_int.h"
291de7afc9SPaolo Bonzini #include "qemu/module.h"
3058369e22SPaolo Bonzini #include "qemu/bswap.h"
31019d6b8fSAnthony Liguori #include <zlib.h>
32019d6b8fSAnthony Liguori
33d65f97a8SStefan Hajnoczi /* Maximum compressed block size */
34d65f97a8SStefan Hajnoczi #define MAX_BLOCK_SIZE (64 * 1024 * 1024)
35d65f97a8SStefan Hajnoczi
36019d6b8fSAnthony Liguori typedef struct BDRVCloopState {
37848c66e8SPaolo Bonzini CoMutex lock;
38019d6b8fSAnthony Liguori uint32_t block_size;
39019d6b8fSAnthony Liguori uint32_t n_blocks;
40019d6b8fSAnthony Liguori uint64_t *offsets;
41019d6b8fSAnthony Liguori uint32_t sectors_per_block;
42019d6b8fSAnthony Liguori uint32_t current_block;
43019d6b8fSAnthony Liguori uint8_t *compressed_block;
44019d6b8fSAnthony Liguori uint8_t *uncompressed_block;
45019d6b8fSAnthony Liguori z_stream zstream;
46019d6b8fSAnthony Liguori } BDRVCloopState;
47019d6b8fSAnthony Liguori
cloop_probe(const uint8_t * buf,int buf_size,const char * filename)48019d6b8fSAnthony Liguori static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
49019d6b8fSAnthony Liguori {
50019d6b8fSAnthony Liguori const char *magic_version_2_0 = "#!/bin/sh\n"
51019d6b8fSAnthony Liguori "#V2.0 Format\n"
52019d6b8fSAnthony Liguori "modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n";
53019d6b8fSAnthony Liguori int length = strlen(magic_version_2_0);
545b47b7c3SDong Xu Wang if (length > buf_size) {
55019d6b8fSAnthony Liguori length = buf_size;
565b47b7c3SDong Xu Wang }
575b47b7c3SDong Xu Wang if (!memcmp(magic_version_2_0, buf, length)) {
58019d6b8fSAnthony Liguori return 2;
595b47b7c3SDong Xu Wang }
60019d6b8fSAnthony Liguori return 0;
61019d6b8fSAnthony Liguori }
62019d6b8fSAnthony Liguori
cloop_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)63015a1036SMax Reitz static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
64015a1036SMax Reitz Error **errp)
65019d6b8fSAnthony Liguori {
66019d6b8fSAnthony Liguori BDRVCloopState *s = bs->opaque;
67019d6b8fSAnthony Liguori uint32_t offsets_size, max_compressed_block_size = 1, i;
681a60657fSKevin Wolf int ret;
69019d6b8fSAnthony Liguori
70*a4b740dbSKevin Wolf GLOBAL_STATE_CODE();
71*a4b740dbSKevin Wolf
72018f9deaSKevin Wolf bdrv_graph_rdlock_main_loop();
73eaa2410fSKevin Wolf ret = bdrv_apply_auto_read_only(bs, NULL, errp);
74018f9deaSKevin Wolf bdrv_graph_rdunlock_main_loop();
75eaa2410fSKevin Wolf if (ret < 0) {
76eaa2410fSKevin Wolf return ret;
77eaa2410fSKevin Wolf }
78eaa2410fSKevin Wolf
7983930780SVladimir Sementsov-Ogievskiy ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
8083930780SVladimir Sementsov-Ogievskiy if (ret < 0) {
8183930780SVladimir Sementsov-Ogievskiy return ret;
824e4bf5c4SKevin Wolf }
834e4bf5c4SKevin Wolf
84*a4b740dbSKevin Wolf GRAPH_RDLOCK_GUARD_MAINLOOP();
85*a4b740dbSKevin Wolf
86019d6b8fSAnthony Liguori /* read header */
8732cc71deSAlberto Faria ret = bdrv_pread(bs->file, 128, 4, &s->block_size, 0);
881a60657fSKevin Wolf if (ret < 0) {
891a60657fSKevin Wolf return ret;
90019d6b8fSAnthony Liguori }
91019d6b8fSAnthony Liguori s->block_size = be32_to_cpu(s->block_size);
92d65f97a8SStefan Hajnoczi if (s->block_size % 512) {
93370e6816SStefan Hajnoczi error_setg(errp, "block_size %" PRIu32 " must be a multiple of 512",
94d65f97a8SStefan Hajnoczi s->block_size);
95d65f97a8SStefan Hajnoczi return -EINVAL;
96d65f97a8SStefan Hajnoczi }
97d65f97a8SStefan Hajnoczi if (s->block_size == 0) {
98d65f97a8SStefan Hajnoczi error_setg(errp, "block_size cannot be zero");
99d65f97a8SStefan Hajnoczi return -EINVAL;
100d65f97a8SStefan Hajnoczi }
101d65f97a8SStefan Hajnoczi
102d65f97a8SStefan Hajnoczi /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but
103d65f97a8SStefan Hajnoczi * we can accept more. Prevent ridiculous values like 4 GB - 1 since we
104d65f97a8SStefan Hajnoczi * need a buffer this big.
105d65f97a8SStefan Hajnoczi */
106d65f97a8SStefan Hajnoczi if (s->block_size > MAX_BLOCK_SIZE) {
107370e6816SStefan Hajnoczi error_setg(errp, "block_size %" PRIu32 " must be %u MB or less",
108d65f97a8SStefan Hajnoczi s->block_size,
109d65f97a8SStefan Hajnoczi MAX_BLOCK_SIZE / (1024 * 1024));
110d65f97a8SStefan Hajnoczi return -EINVAL;
111d65f97a8SStefan Hajnoczi }
112c94304beSChristoph Hellwig
11332cc71deSAlberto Faria ret = bdrv_pread(bs->file, 128 + 4, 4, &s->n_blocks, 0);
1141a60657fSKevin Wolf if (ret < 0) {
1151a60657fSKevin Wolf return ret;
116c94304beSChristoph Hellwig }
117019d6b8fSAnthony Liguori s->n_blocks = be32_to_cpu(s->n_blocks);
118019d6b8fSAnthony Liguori
119019d6b8fSAnthony Liguori /* read offsets */
12042d43d35SStefan Hajnoczi if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) {
121509a41baSStefan Hajnoczi /* Prevent integer overflow */
122370e6816SStefan Hajnoczi error_setg(errp, "n_blocks %" PRIu32 " must be %zu or less",
123509a41baSStefan Hajnoczi s->n_blocks,
12442d43d35SStefan Hajnoczi (UINT32_MAX - 1) / sizeof(uint64_t));
125509a41baSStefan Hajnoczi return -EINVAL;
126509a41baSStefan Hajnoczi }
12742d43d35SStefan Hajnoczi offsets_size = (s->n_blocks + 1) * sizeof(uint64_t);
1287b103b36SStefan Hajnoczi if (offsets_size > 512 * 1024 * 1024) {
1297b103b36SStefan Hajnoczi /* Prevent ridiculous offsets_size which causes memory allocation to
1307b103b36SStefan Hajnoczi * fail or overflows bdrv_pread() size. In practice the 512 MB
1317b103b36SStefan Hajnoczi * offsets[] limit supports 16 TB images at 256 KB block size.
1327b103b36SStefan Hajnoczi */
1337b103b36SStefan Hajnoczi error_setg(errp, "image requires too many offsets, "
1347b103b36SStefan Hajnoczi "try increasing block size");
1357b103b36SStefan Hajnoczi return -EINVAL;
1367b103b36SStefan Hajnoczi }
1374ae7a52eSKevin Wolf
1384ae7a52eSKevin Wolf s->offsets = g_try_malloc(offsets_size);
1394ae7a52eSKevin Wolf if (s->offsets == NULL) {
1404ae7a52eSKevin Wolf error_setg(errp, "Could not allocate offsets table");
1414ae7a52eSKevin Wolf return -ENOMEM;
1424ae7a52eSKevin Wolf }
1431a60657fSKevin Wolf
14432cc71deSAlberto Faria ret = bdrv_pread(bs->file, 128 + 4 + 4, offsets_size, s->offsets, 0);
1451a60657fSKevin Wolf if (ret < 0) {
1461a60657fSKevin Wolf goto fail;
147c94304beSChristoph Hellwig }
1481a60657fSKevin Wolf
14942d43d35SStefan Hajnoczi for (i = 0; i < s->n_blocks + 1; i++) {
150f56b9bc3SStefan Hajnoczi uint64_t size;
151f56b9bc3SStefan Hajnoczi
152019d6b8fSAnthony Liguori s->offsets[i] = be64_to_cpu(s->offsets[i]);
153f56b9bc3SStefan Hajnoczi if (i == 0) {
154f56b9bc3SStefan Hajnoczi continue;
155f56b9bc3SStefan Hajnoczi }
156f56b9bc3SStefan Hajnoczi
157f56b9bc3SStefan Hajnoczi if (s->offsets[i] < s->offsets[i - 1]) {
158f56b9bc3SStefan Hajnoczi error_setg(errp, "offsets not monotonically increasing at "
159370e6816SStefan Hajnoczi "index %" PRIu32 ", image file is corrupt", i);
160f56b9bc3SStefan Hajnoczi ret = -EINVAL;
161f56b9bc3SStefan Hajnoczi goto fail;
162f56b9bc3SStefan Hajnoczi }
163f56b9bc3SStefan Hajnoczi
164f56b9bc3SStefan Hajnoczi size = s->offsets[i] - s->offsets[i - 1];
165f56b9bc3SStefan Hajnoczi
166f56b9bc3SStefan Hajnoczi /* Compressed blocks should be smaller than the uncompressed block size
167f56b9bc3SStefan Hajnoczi * but maybe compression performed poorly so the compressed block is
168f56b9bc3SStefan Hajnoczi * actually bigger. Clamp down on unrealistic values to prevent
169f56b9bc3SStefan Hajnoczi * ridiculous s->compressed_block allocation.
170f56b9bc3SStefan Hajnoczi */
171f56b9bc3SStefan Hajnoczi if (size > 2 * MAX_BLOCK_SIZE) {
172370e6816SStefan Hajnoczi error_setg(errp, "invalid compressed block size at index %" PRIu32
173370e6816SStefan Hajnoczi ", image file is corrupt", i);
174f56b9bc3SStefan Hajnoczi ret = -EINVAL;
175f56b9bc3SStefan Hajnoczi goto fail;
176f56b9bc3SStefan Hajnoczi }
177f56b9bc3SStefan Hajnoczi
1785b47b7c3SDong Xu Wang if (size > max_compressed_block_size) {
179019d6b8fSAnthony Liguori max_compressed_block_size = size;
180019d6b8fSAnthony Liguori }
181019d6b8fSAnthony Liguori }
182019d6b8fSAnthony Liguori
183019d6b8fSAnthony Liguori /* initialize zlib engine */
1844ae7a52eSKevin Wolf s->compressed_block = g_try_malloc(max_compressed_block_size + 1);
1854ae7a52eSKevin Wolf if (s->compressed_block == NULL) {
1864ae7a52eSKevin Wolf error_setg(errp, "Could not allocate compressed_block");
1874ae7a52eSKevin Wolf ret = -ENOMEM;
1884ae7a52eSKevin Wolf goto fail;
1894ae7a52eSKevin Wolf }
1904ae7a52eSKevin Wolf
1914ae7a52eSKevin Wolf s->uncompressed_block = g_try_malloc(s->block_size);
1924ae7a52eSKevin Wolf if (s->uncompressed_block == NULL) {
1934ae7a52eSKevin Wolf error_setg(errp, "Could not allocate uncompressed_block");
1944ae7a52eSKevin Wolf ret = -ENOMEM;
1954ae7a52eSKevin Wolf goto fail;
1964ae7a52eSKevin Wolf }
1974ae7a52eSKevin Wolf
1985b47b7c3SDong Xu Wang if (inflateInit(&s->zstream) != Z_OK) {
1991a60657fSKevin Wolf ret = -EINVAL;
2001a60657fSKevin Wolf goto fail;
2015b47b7c3SDong Xu Wang }
202019d6b8fSAnthony Liguori s->current_block = s->n_blocks;
203019d6b8fSAnthony Liguori
204019d6b8fSAnthony Liguori s->sectors_per_block = s->block_size/512;
205019d6b8fSAnthony Liguori bs->total_sectors = s->n_blocks * s->sectors_per_block;
206848c66e8SPaolo Bonzini qemu_co_mutex_init(&s->lock);
207019d6b8fSAnthony Liguori return 0;
208c94304beSChristoph Hellwig
2091a60657fSKevin Wolf fail:
2101a60657fSKevin Wolf g_free(s->offsets);
2111a60657fSKevin Wolf g_free(s->compressed_block);
2121a60657fSKevin Wolf g_free(s->uncompressed_block);
2131a60657fSKevin Wolf return ret;
214019d6b8fSAnthony Liguori }
215019d6b8fSAnthony Liguori
cloop_refresh_limits(BlockDriverState * bs,Error ** errp)216a6506481SEric Blake static void cloop_refresh_limits(BlockDriverState *bs, Error **errp)
217a6506481SEric Blake {
218a5b8dd2cSEric Blake bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */
219a6506481SEric Blake }
220a6506481SEric Blake
221cf8d4c58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
cloop_read_block(BlockDriverState * bs,int block_num)222cf8d4c58SPaolo Bonzini cloop_read_block(BlockDriverState *bs, int block_num)
223019d6b8fSAnthony Liguori {
22420be49e4SChristoph Hellwig BDRVCloopState *s = bs->opaque;
22520be49e4SChristoph Hellwig
226019d6b8fSAnthony Liguori if (s->current_block != block_num) {
227019d6b8fSAnthony Liguori int ret;
228019d6b8fSAnthony Liguori uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num];
229019d6b8fSAnthony Liguori
230cf8d4c58SPaolo Bonzini ret = bdrv_co_pread(bs->file, s->offsets[block_num], bytes,
23132cc71deSAlberto Faria s->compressed_block, 0);
232353a5d84SAlberto Faria if (ret < 0) {
233019d6b8fSAnthony Liguori return -1;
2345b47b7c3SDong Xu Wang }
235019d6b8fSAnthony Liguori
236019d6b8fSAnthony Liguori s->zstream.next_in = s->compressed_block;
237019d6b8fSAnthony Liguori s->zstream.avail_in = bytes;
238019d6b8fSAnthony Liguori s->zstream.next_out = s->uncompressed_block;
239019d6b8fSAnthony Liguori s->zstream.avail_out = s->block_size;
240019d6b8fSAnthony Liguori ret = inflateReset(&s->zstream);
2415b47b7c3SDong Xu Wang if (ret != Z_OK) {
242019d6b8fSAnthony Liguori return -1;
2435b47b7c3SDong Xu Wang }
244019d6b8fSAnthony Liguori ret = inflate(&s->zstream, Z_FINISH);
2455b47b7c3SDong Xu Wang if (ret != Z_STREAM_END || s->zstream.total_out != s->block_size) {
246019d6b8fSAnthony Liguori return -1;
2475b47b7c3SDong Xu Wang }
248019d6b8fSAnthony Liguori
249019d6b8fSAnthony Liguori s->current_block = block_num;
250019d6b8fSAnthony Liguori }
251019d6b8fSAnthony Liguori return 0;
252019d6b8fSAnthony Liguori }
253019d6b8fSAnthony Liguori
254cf8d4c58SPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
cloop_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)255f7ef38ddSVladimir Sementsov-Ogievskiy cloop_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
256f7ef38ddSVladimir Sementsov-Ogievskiy QEMUIOVector *qiov, BdrvRequestFlags flags)
257019d6b8fSAnthony Liguori {
258019d6b8fSAnthony Liguori BDRVCloopState *s = bs->opaque;
2595cd23081SKevin Wolf uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
2605cd23081SKevin Wolf int nb_sectors = bytes >> BDRV_SECTOR_BITS;
2615cd23081SKevin Wolf int ret, i;
2625cd23081SKevin Wolf
2631bbbf32dSNir Soffer assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
2641bbbf32dSNir Soffer assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
2655cd23081SKevin Wolf
2665cd23081SKevin Wolf qemu_co_mutex_lock(&s->lock);
267019d6b8fSAnthony Liguori
268019d6b8fSAnthony Liguori for (i = 0; i < nb_sectors; i++) {
2695cd23081SKevin Wolf void *data;
2705b47b7c3SDong Xu Wang uint32_t sector_offset_in_block =
2715b47b7c3SDong Xu Wang ((sector_num + i) % s->sectors_per_block),
272019d6b8fSAnthony Liguori block_num = (sector_num + i) / s->sectors_per_block;
2735b47b7c3SDong Xu Wang if (cloop_read_block(bs, block_num) != 0) {
2745cd23081SKevin Wolf ret = -EIO;
2755cd23081SKevin Wolf goto fail;
276019d6b8fSAnthony Liguori }
277019d6b8fSAnthony Liguori
2785cd23081SKevin Wolf data = s->uncompressed_block + sector_offset_in_block * 512;
2795cd23081SKevin Wolf qemu_iovec_from_buf(qiov, i * 512, data, 512);
2805cd23081SKevin Wolf }
2815cd23081SKevin Wolf
2825cd23081SKevin Wolf ret = 0;
2835cd23081SKevin Wolf fail:
2842914caa0SPaolo Bonzini qemu_co_mutex_unlock(&s->lock);
2855cd23081SKevin Wolf
2862914caa0SPaolo Bonzini return ret;
2872914caa0SPaolo Bonzini }
2882914caa0SPaolo Bonzini
cloop_close(BlockDriverState * bs)289019d6b8fSAnthony Liguori static void cloop_close(BlockDriverState *bs)
290019d6b8fSAnthony Liguori {
291019d6b8fSAnthony Liguori BDRVCloopState *s = bs->opaque;
292756f51e4SDong Xu Wang g_free(s->offsets);
293756f51e4SDong Xu Wang g_free(s->compressed_block);
294756f51e4SDong Xu Wang g_free(s->uncompressed_block);
295019d6b8fSAnthony Liguori inflateEnd(&s->zstream);
296019d6b8fSAnthony Liguori }
297019d6b8fSAnthony Liguori
298019d6b8fSAnthony Liguori static BlockDriver bdrv_cloop = {
299019d6b8fSAnthony Liguori .format_name = "cloop",
300019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVCloopState),
301019d6b8fSAnthony Liguori .bdrv_probe = cloop_probe,
30220be49e4SChristoph Hellwig .bdrv_open = cloop_open,
30369dca43dSMax Reitz .bdrv_child_perm = bdrv_default_perms,
304a6506481SEric Blake .bdrv_refresh_limits = cloop_refresh_limits,
3055cd23081SKevin Wolf .bdrv_co_preadv = cloop_co_preadv,
306019d6b8fSAnthony Liguori .bdrv_close = cloop_close,
307d67066d8SMax Reitz .is_format = true,
308019d6b8fSAnthony Liguori };
309019d6b8fSAnthony Liguori
bdrv_cloop_init(void)310019d6b8fSAnthony Liguori static void bdrv_cloop_init(void)
311019d6b8fSAnthony Liguori {
312019d6b8fSAnthony Liguori bdrv_register(&bdrv_cloop);
313019d6b8fSAnthony Liguori }
314019d6b8fSAnthony Liguori
315019d6b8fSAnthony Liguori block_init(bdrv_cloop_init);
316