block/parallels: _co_writev callback for Parallels format
[qemu/ar7.git] / block / parallels.c
blob8d738033c392a631eaa75413835e5d1b06c92a75
1 /*
2 * Block driver for Parallels disk image format
4 * Copyright (c) 2007 Alex Beregszaszi
6 * This code is based on comparing different disk images created by Parallels.
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
26 #include "qemu-common.h"
27 #include "block/block_int.h"
28 #include "qemu/module.h"
30 /**************************************************************/
32 #define HEADER_MAGIC "WithoutFreeSpace"
33 #define HEADER_MAGIC2 "WithouFreSpacExt"
34 #define HEADER_VERSION 2
36 // always little-endian
37 typedef struct ParallelsHeader {
38 char magic[16]; // "WithoutFreeSpace"
39 uint32_t version;
40 uint32_t heads;
41 uint32_t cylinders;
42 uint32_t tracks;
43 uint32_t catalog_entries;
44 uint64_t nb_sectors;
45 uint32_t inuse;
46 uint32_t data_off;
47 char padding[12];
48 } QEMU_PACKED ParallelsHeader;
50 typedef struct BDRVParallelsState {
51 /** Locking is conservative, the lock protects
52 * - image file extending (truncate, fallocate)
53 * - any access to block allocation table
55 CoMutex lock;
57 uint32_t *catalog_bitmap;
58 unsigned int catalog_size;
60 unsigned int tracks;
62 unsigned int off_multiplier;
64 bool has_truncate;
65 } BDRVParallelsState;
67 static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
69 const ParallelsHeader *ph = (const void *)buf;
71 if (buf_size < sizeof(ParallelsHeader))
72 return 0;
74 if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
75 !memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
76 (le32_to_cpu(ph->version) == HEADER_VERSION))
77 return 100;
79 return 0;
82 static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
83 Error **errp)
85 BDRVParallelsState *s = bs->opaque;
86 int i;
87 ParallelsHeader ph;
88 int ret;
90 ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
91 if (ret < 0) {
92 goto fail;
95 bs->total_sectors = le64_to_cpu(ph.nb_sectors);
97 if (le32_to_cpu(ph.version) != HEADER_VERSION) {
98 goto fail_format;
100 if (!memcmp(ph.magic, HEADER_MAGIC, 16)) {
101 s->off_multiplier = 1;
102 bs->total_sectors = 0xffffffff & bs->total_sectors;
103 } else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) {
104 s->off_multiplier = le32_to_cpu(ph.tracks);
105 } else {
106 goto fail_format;
109 s->tracks = le32_to_cpu(ph.tracks);
110 if (s->tracks == 0) {
111 error_setg(errp, "Invalid image: Zero sectors per track");
112 ret = -EINVAL;
113 goto fail;
115 if (s->tracks > INT32_MAX/513) {
116 error_setg(errp, "Invalid image: Too big cluster");
117 ret = -EFBIG;
118 goto fail;
121 s->catalog_size = le32_to_cpu(ph.catalog_entries);
122 if (s->catalog_size > INT_MAX / sizeof(uint32_t)) {
123 error_setg(errp, "Catalog too large");
124 ret = -EFBIG;
125 goto fail;
127 s->catalog_bitmap = g_try_new(uint32_t, s->catalog_size);
128 if (s->catalog_size && s->catalog_bitmap == NULL) {
129 ret = -ENOMEM;
130 goto fail;
133 ret = bdrv_pread(bs->file, sizeof(ParallelsHeader),
134 s->catalog_bitmap, s->catalog_size * sizeof(uint32_t));
135 if (ret < 0) {
136 goto fail;
139 for (i = 0; i < s->catalog_size; i++)
140 le32_to_cpus(&s->catalog_bitmap[i]);
142 s->has_truncate = bdrv_has_zero_init(bs->file) &&
143 bdrv_truncate(bs->file, bdrv_getlength(bs->file)) == 0;
145 qemu_co_mutex_init(&s->lock);
146 return 0;
148 fail_format:
149 error_setg(errp, "Image not in Parallels format");
150 ret = -EINVAL;
151 fail:
152 g_free(s->catalog_bitmap);
153 return ret;
156 static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num)
158 uint32_t index, offset;
160 index = sector_num / s->tracks;
161 offset = sector_num % s->tracks;
163 /* not allocated */
164 if ((index >= s->catalog_size) || (s->catalog_bitmap[index] == 0))
165 return -1;
166 return (uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset;
169 static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num,
170 int nb_sectors)
172 int ret = s->tracks - sector_num % s->tracks;
173 return MIN(nb_sectors, ret);
176 static int64_t allocate_cluster(BlockDriverState *bs, int64_t sector_num)
178 BDRVParallelsState *s = bs->opaque;
179 uint32_t idx, offset, tmp;
180 int64_t pos;
181 int ret;
183 idx = sector_num / s->tracks;
184 offset = sector_num % s->tracks;
186 if (idx >= s->catalog_size) {
187 return -EINVAL;
189 if (s->catalog_bitmap[idx] != 0) {
190 return (uint64_t)s->catalog_bitmap[idx] * s->off_multiplier + offset;
193 pos = bdrv_getlength(bs->file) >> BDRV_SECTOR_BITS;
194 if (s->has_truncate) {
195 ret = bdrv_truncate(bs->file, (pos + s->tracks) << BDRV_SECTOR_BITS);
196 } else {
197 ret = bdrv_write_zeroes(bs->file, pos, s->tracks, 0);
199 if (ret < 0) {
200 return ret;
203 s->catalog_bitmap[idx] = pos / s->off_multiplier;
205 tmp = cpu_to_le32(s->catalog_bitmap[idx]);
207 ret = bdrv_pwrite(bs->file,
208 sizeof(ParallelsHeader) + idx * sizeof(tmp), &tmp, sizeof(tmp));
209 if (ret < 0) {
210 s->catalog_bitmap[idx] = 0;
211 return ret;
213 return (uint64_t)s->catalog_bitmap[idx] * s->off_multiplier + offset;
216 static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
217 int64_t sector_num, int nb_sectors, int *pnum)
219 BDRVParallelsState *s = bs->opaque;
220 int64_t offset;
222 qemu_co_mutex_lock(&s->lock);
223 offset = seek_to_sector(s, sector_num);
224 qemu_co_mutex_unlock(&s->lock);
226 *pnum = cluster_remainder(s, sector_num, nb_sectors);
228 if (offset < 0) {
229 return 0;
232 return (offset << BDRV_SECTOR_BITS) |
233 BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
236 static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
237 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
239 BDRVParallelsState *s = bs->opaque;
240 uint64_t bytes_done = 0;
241 QEMUIOVector hd_qiov;
242 int ret = 0;
244 qemu_iovec_init(&hd_qiov, qiov->niov);
246 while (nb_sectors > 0) {
247 int64_t position;
248 int n, nbytes;
250 qemu_co_mutex_lock(&s->lock);
251 position = allocate_cluster(bs, sector_num);
252 qemu_co_mutex_unlock(&s->lock);
253 if (position < 0) {
254 ret = (int)position;
255 break;
258 n = cluster_remainder(s, sector_num, nb_sectors);
259 nbytes = n << BDRV_SECTOR_BITS;
261 qemu_iovec_reset(&hd_qiov);
262 qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
264 ret = bdrv_co_writev(bs->file, position, n, &hd_qiov);
265 if (ret < 0) {
266 break;
269 nb_sectors -= n;
270 sector_num += n;
271 bytes_done += nbytes;
274 qemu_iovec_destroy(&hd_qiov);
275 return ret;
278 static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
279 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
281 BDRVParallelsState *s = bs->opaque;
282 uint64_t bytes_done = 0;
283 QEMUIOVector hd_qiov;
284 int ret = 0;
286 qemu_iovec_init(&hd_qiov, qiov->niov);
288 while (nb_sectors > 0) {
289 int64_t position;
290 int n, nbytes;
292 qemu_co_mutex_lock(&s->lock);
293 position = seek_to_sector(s, sector_num);
294 qemu_co_mutex_unlock(&s->lock);
296 n = cluster_remainder(s, sector_num, nb_sectors);
297 nbytes = n << BDRV_SECTOR_BITS;
299 if (position < 0) {
300 qemu_iovec_memset(qiov, bytes_done, 0, nbytes);
301 } else {
302 qemu_iovec_reset(&hd_qiov);
303 qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
305 ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
306 if (ret < 0) {
307 break;
311 nb_sectors -= n;
312 sector_num += n;
313 bytes_done += nbytes;
316 qemu_iovec_destroy(&hd_qiov);
317 return ret;
320 static void parallels_close(BlockDriverState *bs)
322 BDRVParallelsState *s = bs->opaque;
323 g_free(s->catalog_bitmap);
326 static BlockDriver bdrv_parallels = {
327 .format_name = "parallels",
328 .instance_size = sizeof(BDRVParallelsState),
329 .bdrv_probe = parallels_probe,
330 .bdrv_open = parallels_open,
331 .bdrv_close = parallels_close,
332 .bdrv_co_get_block_status = parallels_co_get_block_status,
333 .bdrv_has_zero_init = bdrv_has_zero_init_1,
334 .bdrv_co_readv = parallels_co_readv,
335 .bdrv_co_writev = parallels_co_writev,
338 static void bdrv_parallels_init(void)
340 bdrv_register(&bdrv_parallels);
343 block_init(bdrv_parallels_init);