]> git.proxmox.com Git - mirror_qemu.git/blame - block/parallels.c
block/parallels: create bat2sect helper
[mirror_qemu.git] / block / parallels.c
CommitLineData
6ada7453
TS
1/*
2 * Block driver for Parallels disk image format
3 *
4 * Copyright (c) 2007 Alex Beregszaszi
cc5690f2 5 * Copyright (c) 2015 Denis V. Lunev <den@openvz.org>
6ada7453 6 *
cc5690f2
DL
7 * This code was originally based on comparing different disk images created
8 * by Parallels. Currently it is based on opened OpenVZ sources
9 * available at
10 * http://git.openvz.org/?p=ploop;a=summary
6ada7453
TS
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a copy
13 * of this software and associated documentation files (the "Software"), to deal
14 * in the Software without restriction, including without limitation the rights
15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 * copies of the Software, and to permit persons to whom the Software is
17 * furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included in
20 * all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 * THE SOFTWARE.
29 */
faf07963 30#include "qemu-common.h"
737e150e 31#include "block/block_int.h"
1de7afc9 32#include "qemu/module.h"
6ada7453
TS
33
34/**************************************************************/
35
36#define HEADER_MAGIC "WithoutFreeSpace"
d25d5980 37#define HEADER_MAGIC2 "WithouFreSpacExt"
6ada7453 38#define HEADER_VERSION 2
6ada7453 39
74cf6c50
DL
40#define DEFAULT_CLUSTER_SIZE 1048576 /* 1 MiB */
41
42
6ada7453 43// always little-endian
07898904 44typedef struct ParallelsHeader {
6ada7453
TS
45 char magic[16]; // "WithoutFreeSpace"
46 uint32_t version;
47 uint32_t heads;
48 uint32_t cylinders;
49 uint32_t tracks;
369f7de9 50 uint32_t bat_entries;
8c27d54f
DL
51 uint64_t nb_sectors;
52 uint32_t inuse;
53 uint32_t data_off;
54 char padding[12];
07898904 55} QEMU_PACKED ParallelsHeader;
6ada7453
TS
56
57typedef struct BDRVParallelsState {
481fb9cf
DL
58 /** Locking is conservative, the lock protects
59 * - image file extending (truncate, fallocate)
60 * - any access to block allocation table
61 */
848c66e8 62 CoMutex lock;
6ada7453 63
369f7de9
DL
64 uint32_t *bat_bitmap;
65 unsigned int bat_size;
6ada7453 66
9302e863 67 unsigned int tracks;
d25d5980
DL
68
69 unsigned int off_multiplier;
5a41e1fa
DL
70
71 bool has_truncate;
6ada7453
TS
72} BDRVParallelsState;
73
74static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
75{
07898904 76 const ParallelsHeader *ph = (const void *)buf;
6ada7453 77
912f3128 78 if (buf_size < sizeof(ParallelsHeader))
f08e2f84 79 return 0;
6ada7453 80
d25d5980
DL
81 if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
82 !memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
f08e2f84
DL
83 (le32_to_cpu(ph->version) == HEADER_VERSION))
84 return 100;
6ada7453
TS
85
86 return 0;
87}
88
015a1036
HR
89static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
90 Error **errp)
6ada7453
TS
91{
92 BDRVParallelsState *s = bs->opaque;
1dec5a70 93 int i;
07898904 94 ParallelsHeader ph;
46536235 95 int ret;
6ada7453 96
46536235
KW
97 ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
98 if (ret < 0) {
6ada7453 99 goto fail;
46536235 100 }
6ada7453 101
d25d5980
DL
102 bs->total_sectors = le64_to_cpu(ph.nb_sectors);
103
418a7adb
DL
104 if (le32_to_cpu(ph.version) != HEADER_VERSION) {
105 goto fail_format;
106 }
d25d5980
DL
107 if (!memcmp(ph.magic, HEADER_MAGIC, 16)) {
108 s->off_multiplier = 1;
109 bs->total_sectors = 0xffffffff & bs->total_sectors;
110 } else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) {
111 s->off_multiplier = le32_to_cpu(ph.tracks);
112 } else {
418a7adb 113 goto fail_format;
6ada7453
TS
114 }
115
6ada7453 116 s->tracks = le32_to_cpu(ph.tracks);
9302e863
KW
117 if (s->tracks == 0) {
118 error_setg(errp, "Invalid image: Zero sectors per track");
119 ret = -EINVAL;
120 goto fail;
121 }
d25d5980
DL
122 if (s->tracks > INT32_MAX/513) {
123 error_setg(errp, "Invalid image: Too big cluster");
124 ret = -EFBIG;
125 goto fail;
126 }
6ada7453 127
369f7de9
DL
128 s->bat_size = le32_to_cpu(ph.bat_entries);
129 if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
afbcc40b
KW
130 error_setg(errp, "Catalog too large");
131 ret = -EFBIG;
132 goto fail;
133 }
369f7de9
DL
134 s->bat_bitmap = g_try_new(uint32_t, s->bat_size);
135 if (s->bat_size && s->bat_bitmap == NULL) {
f7b593d9
KW
136 ret = -ENOMEM;
137 goto fail;
138 }
46536235 139
912f3128 140 ret = bdrv_pread(bs->file, sizeof(ParallelsHeader),
369f7de9 141 s->bat_bitmap, s->bat_size * sizeof(uint32_t));
46536235
KW
142 if (ret < 0) {
143 goto fail;
144 }
145
369f7de9
DL
146 for (i = 0; i < s->bat_size; i++) {
147 le32_to_cpus(&s->bat_bitmap[i]);
148 }
6ada7453 149
5a41e1fa
DL
150 s->has_truncate = bdrv_has_zero_init(bs->file) &&
151 bdrv_truncate(bs->file, bdrv_getlength(bs->file)) == 0;
152
848c66e8 153 qemu_co_mutex_init(&s->lock);
6ada7453 154 return 0;
46536235 155
418a7adb
DL
156fail_format:
157 error_setg(errp, "Image not in Parallels format");
158 ret = -EINVAL;
6ada7453 159fail:
369f7de9 160 g_free(s->bat_bitmap);
46536235 161 return ret;
6ada7453
TS
162}
163
555cc9d9
DL
164
165static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
166{
167 return (uint64_t)s->bat_bitmap[idx] * s->off_multiplier;
168}
169
29442569 170static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num)
6ada7453 171{
c34d2451 172 uint32_t index, offset;
6ada7453
TS
173
174 index = sector_num / s->tracks;
175 offset = sector_num % s->tracks;
176
9d8b88f6 177 /* not allocated */
369f7de9 178 if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) {
f08e2f84 179 return -1;
369f7de9 180 }
555cc9d9 181 return bat2sect(s, index) + offset;
6ada7453
TS
182}
183
9de9da17
RK
184static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num,
185 int nb_sectors)
186{
187 int ret = s->tracks - sector_num % s->tracks;
188 return MIN(nb_sectors, ret);
189}
190
5a41e1fa
DL
191static int64_t allocate_cluster(BlockDriverState *bs, int64_t sector_num)
192{
193 BDRVParallelsState *s = bs->opaque;
194 uint32_t idx, offset, tmp;
195 int64_t pos;
196 int ret;
197
198 idx = sector_num / s->tracks;
199 offset = sector_num % s->tracks;
200
369f7de9 201 if (idx >= s->bat_size) {
5a41e1fa
DL
202 return -EINVAL;
203 }
369f7de9 204 if (s->bat_bitmap[idx] != 0) {
555cc9d9 205 return bat2sect(s, idx) + offset;
5a41e1fa
DL
206 }
207
208 pos = bdrv_getlength(bs->file) >> BDRV_SECTOR_BITS;
209 if (s->has_truncate) {
210 ret = bdrv_truncate(bs->file, (pos + s->tracks) << BDRV_SECTOR_BITS);
211 } else {
212 ret = bdrv_write_zeroes(bs->file, pos, s->tracks, 0);
213 }
214 if (ret < 0) {
215 return ret;
216 }
217
369f7de9 218 s->bat_bitmap[idx] = pos / s->off_multiplier;
5a41e1fa 219
369f7de9 220 tmp = cpu_to_le32(s->bat_bitmap[idx]);
5a41e1fa
DL
221
222 ret = bdrv_pwrite(bs->file,
223 sizeof(ParallelsHeader) + idx * sizeof(tmp), &tmp, sizeof(tmp));
224 if (ret < 0) {
369f7de9 225 s->bat_bitmap[idx] = 0;
5a41e1fa
DL
226 return ret;
227 }
555cc9d9 228 return bat2sect(s, idx) + offset;
5a41e1fa
DL
229}
230
dd3bed16
RK
231static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
232 int64_t sector_num, int nb_sectors, int *pnum)
233{
234 BDRVParallelsState *s = bs->opaque;
235 int64_t offset;
236
237 qemu_co_mutex_lock(&s->lock);
238 offset = seek_to_sector(s, sector_num);
239 qemu_co_mutex_unlock(&s->lock);
240
241 *pnum = cluster_remainder(s, sector_num, nb_sectors);
242
243 if (offset < 0) {
244 return 0;
245 }
246
247 return (offset << BDRV_SECTOR_BITS) |
248 BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
249}
250
5a41e1fa
DL
251static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
252 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
253{
254 BDRVParallelsState *s = bs->opaque;
255 uint64_t bytes_done = 0;
256 QEMUIOVector hd_qiov;
257 int ret = 0;
258
259 qemu_iovec_init(&hd_qiov, qiov->niov);
260
261 while (nb_sectors > 0) {
262 int64_t position;
263 int n, nbytes;
264
265 qemu_co_mutex_lock(&s->lock);
266 position = allocate_cluster(bs, sector_num);
267 qemu_co_mutex_unlock(&s->lock);
268 if (position < 0) {
269 ret = (int)position;
270 break;
271 }
272
273 n = cluster_remainder(s, sector_num, nb_sectors);
274 nbytes = n << BDRV_SECTOR_BITS;
275
276 qemu_iovec_reset(&hd_qiov);
277 qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
278
279 ret = bdrv_co_writev(bs->file, position, n, &hd_qiov);
280 if (ret < 0) {
281 break;
282 }
283
284 nb_sectors -= n;
285 sector_num += n;
286 bytes_done += nbytes;
287 }
288
289 qemu_iovec_destroy(&hd_qiov);
290 return ret;
291}
292
481fb9cf
DL
293static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
294 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
6ada7453 295{
29442569 296 BDRVParallelsState *s = bs->opaque;
481fb9cf
DL
297 uint64_t bytes_done = 0;
298 QEMUIOVector hd_qiov;
299 int ret = 0;
300
301 qemu_iovec_init(&hd_qiov, qiov->niov);
29442569 302
6ada7453 303 while (nb_sectors > 0) {
481fb9cf
DL
304 int64_t position;
305 int n, nbytes;
306
307 qemu_co_mutex_lock(&s->lock);
308 position = seek_to_sector(s, sector_num);
309 qemu_co_mutex_unlock(&s->lock);
310
311 n = cluster_remainder(s, sector_num, nb_sectors);
312 nbytes = n << BDRV_SECTOR_BITS;
313
314 if (position < 0) {
315 qemu_iovec_memset(qiov, bytes_done, 0, nbytes);
316 } else {
317 qemu_iovec_reset(&hd_qiov);
318 qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
319
320 ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
29442569 321 if (ret < 0) {
481fb9cf 322 break;
29442569 323 }
9d8b88f6 324 }
481fb9cf 325
9de9da17
RK
326 nb_sectors -= n;
327 sector_num += n;
481fb9cf 328 bytes_done += nbytes;
6ada7453 329 }
6ada7453 330
481fb9cf 331 qemu_iovec_destroy(&hd_qiov);
2914caa0
PB
332 return ret;
333}
334
74cf6c50
DL
335static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
336{
337 int64_t total_size, cl_size;
338 uint8_t tmp[BDRV_SECTOR_SIZE];
339 Error *local_err = NULL;
340 BlockDriverState *file;
369f7de9 341 uint32_t bat_entries, bat_sectors;
74cf6c50
DL
342 ParallelsHeader header;
343 int ret;
344
345 total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
346 BDRV_SECTOR_SIZE);
347 cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
348 DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
349
350 ret = bdrv_create_file(filename, opts, &local_err);
351 if (ret < 0) {
352 error_propagate(errp, local_err);
353 return ret;
354 }
355
356 file = NULL;
357 ret = bdrv_open(&file, filename, NULL, NULL,
358 BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
359 if (ret < 0) {
360 error_propagate(errp, local_err);
361 return ret;
362 }
363 ret = bdrv_truncate(file, 0);
364 if (ret < 0) {
365 goto exit;
366 }
367
369f7de9
DL
368 bat_entries = DIV_ROUND_UP(total_size, cl_size);
369 bat_sectors = DIV_ROUND_UP(bat_entries * sizeof(uint32_t) +
74cf6c50 370 sizeof(ParallelsHeader), cl_size);
369f7de9 371 bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS;
74cf6c50
DL
372
373 memset(&header, 0, sizeof(header));
374 memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic));
375 header.version = cpu_to_le32(HEADER_VERSION);
376 /* don't care much about geometry, it is not used on image level */
377 header.heads = cpu_to_le32(16);
378 header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE / 16 / 32);
379 header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS);
369f7de9 380 header.bat_entries = cpu_to_le32(bat_entries);
74cf6c50 381 header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE));
369f7de9 382 header.data_off = cpu_to_le32(bat_sectors);
74cf6c50
DL
383
384 /* write all the data */
385 memset(tmp, 0, sizeof(tmp));
386 memcpy(tmp, &header, sizeof(header));
387
388 ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
389 if (ret < 0) {
390 goto exit;
391 }
369f7de9 392 ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0);
74cf6c50
DL
393 if (ret < 0) {
394 goto exit;
395 }
396 ret = 0;
397
398done:
399 bdrv_unref(file);
400 return ret;
401
402exit:
403 error_setg_errno(errp, -ret, "Failed to create Parallels image");
404 goto done;
405}
406
6ada7453
TS
407static void parallels_close(BlockDriverState *bs)
408{
409 BDRVParallelsState *s = bs->opaque;
369f7de9 410 g_free(s->bat_bitmap);
6ada7453
TS
411}
412
74cf6c50
DL
413static QemuOptsList parallels_create_opts = {
414 .name = "parallels-create-opts",
415 .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
416 .desc = {
417 {
418 .name = BLOCK_OPT_SIZE,
419 .type = QEMU_OPT_SIZE,
420 .help = "Virtual disk size",
421 },
422 {
423 .name = BLOCK_OPT_CLUSTER_SIZE,
424 .type = QEMU_OPT_SIZE,
425 .help = "Parallels image cluster size",
426 .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
427 },
428 { /* end of list */ }
429 }
430};
431
5efa9d5a 432static BlockDriver bdrv_parallels = {
e60f469c
AJ
433 .format_name = "parallels",
434 .instance_size = sizeof(BDRVParallelsState),
435 .bdrv_probe = parallels_probe,
1dec5a70 436 .bdrv_open = parallels_open,
e60f469c 437 .bdrv_close = parallels_close,
dd3bed16 438 .bdrv_co_get_block_status = parallels_co_get_block_status,
d0e61ce5 439 .bdrv_has_zero_init = bdrv_has_zero_init_1,
481fb9cf 440 .bdrv_co_readv = parallels_co_readv,
5a41e1fa 441 .bdrv_co_writev = parallels_co_writev,
74cf6c50
DL
442
443 .bdrv_create = parallels_create,
444 .create_opts = &parallels_create_opts,
6ada7453 445};
5efa9d5a
AL
446
447static void bdrv_parallels_init(void)
448{
449 bdrv_register(&bdrv_parallels);
450}
451
452block_init(bdrv_parallels_init);