xref: /qemu/block/cloop.c (revision a4b740db)
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 
70a4b740dbSKevin Wolf     GLOBAL_STATE_CODE();
71a4b740dbSKevin 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 
84a4b740dbSKevin Wolf     GRAPH_RDLOCK_GUARD_MAINLOOP();
85a4b740dbSKevin 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