]> git.proxmox.com Git - mirror_qemu.git/blame - block/parallels.c
block/parallels: implement parallels_check method of block driver
[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
9eae9cca
DL
64 ParallelsHeader *header;
65 uint32_t header_size;
369f7de9
DL
66 uint32_t *bat_bitmap;
67 unsigned int bat_size;
6ada7453 68
9302e863 69 unsigned int tracks;
d25d5980
DL
70
71 unsigned int off_multiplier;
5a41e1fa
DL
72
73 bool has_truncate;
6ada7453
TS
74} BDRVParallelsState;
75
555cc9d9
DL
76
77static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
78{
dd97cdc0 79 return (uint64_t)le32_to_cpu(s->bat_bitmap[idx]) * s->off_multiplier;
555cc9d9
DL
80}
81
29442569 82static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num)
6ada7453 83{
c34d2451 84 uint32_t index, offset;
6ada7453
TS
85
86 index = sector_num / s->tracks;
87 offset = sector_num % s->tracks;
88
9d8b88f6 89 /* not allocated */
369f7de9 90 if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) {
f08e2f84 91 return -1;
369f7de9 92 }
555cc9d9 93 return bat2sect(s, index) + offset;
6ada7453
TS
94}
95
9de9da17
RK
96static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num,
97 int nb_sectors)
98{
99 int ret = s->tracks - sector_num % s->tracks;
100 return MIN(nb_sectors, ret);
101}
102
5a41e1fa
DL
103static int64_t allocate_cluster(BlockDriverState *bs, int64_t sector_num)
104{
105 BDRVParallelsState *s = bs->opaque;
dd97cdc0 106 uint32_t idx, offset;
5a41e1fa
DL
107 int64_t pos;
108 int ret;
109
110 idx = sector_num / s->tracks;
111 offset = sector_num % s->tracks;
112
369f7de9 113 if (idx >= s->bat_size) {
5a41e1fa
DL
114 return -EINVAL;
115 }
369f7de9 116 if (s->bat_bitmap[idx] != 0) {
555cc9d9 117 return bat2sect(s, idx) + offset;
5a41e1fa
DL
118 }
119
120 pos = bdrv_getlength(bs->file) >> BDRV_SECTOR_BITS;
121 if (s->has_truncate) {
122 ret = bdrv_truncate(bs->file, (pos + s->tracks) << BDRV_SECTOR_BITS);
123 } else {
124 ret = bdrv_write_zeroes(bs->file, pos, s->tracks, 0);
125 }
126 if (ret < 0) {
127 return ret;
128 }
129
dd97cdc0 130 s->bat_bitmap[idx] = cpu_to_le32(pos / s->off_multiplier);
5a41e1fa 131 ret = bdrv_pwrite(bs->file,
dd97cdc0
DL
132 sizeof(ParallelsHeader) + idx * sizeof(s->bat_bitmap[idx]),
133 s->bat_bitmap + idx, sizeof(s->bat_bitmap[idx]));
5a41e1fa 134 if (ret < 0) {
369f7de9 135 s->bat_bitmap[idx] = 0;
5a41e1fa
DL
136 return ret;
137 }
555cc9d9 138 return bat2sect(s, idx) + offset;
5a41e1fa
DL
139}
140
dd3bed16
RK
141static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
142 int64_t sector_num, int nb_sectors, int *pnum)
143{
144 BDRVParallelsState *s = bs->opaque;
145 int64_t offset;
146
147 qemu_co_mutex_lock(&s->lock);
148 offset = seek_to_sector(s, sector_num);
149 qemu_co_mutex_unlock(&s->lock);
150
151 *pnum = cluster_remainder(s, sector_num, nb_sectors);
152
153 if (offset < 0) {
154 return 0;
155 }
156
157 return (offset << BDRV_SECTOR_BITS) |
158 BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
159}
160
5a41e1fa
DL
161static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
162 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
163{
164 BDRVParallelsState *s = bs->opaque;
165 uint64_t bytes_done = 0;
166 QEMUIOVector hd_qiov;
167 int ret = 0;
168
169 qemu_iovec_init(&hd_qiov, qiov->niov);
170
171 while (nb_sectors > 0) {
172 int64_t position;
173 int n, nbytes;
174
175 qemu_co_mutex_lock(&s->lock);
176 position = allocate_cluster(bs, sector_num);
177 qemu_co_mutex_unlock(&s->lock);
178 if (position < 0) {
179 ret = (int)position;
180 break;
181 }
182
183 n = cluster_remainder(s, sector_num, nb_sectors);
184 nbytes = n << BDRV_SECTOR_BITS;
185
186 qemu_iovec_reset(&hd_qiov);
187 qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
188
189 ret = bdrv_co_writev(bs->file, position, n, &hd_qiov);
190 if (ret < 0) {
191 break;
192 }
193
194 nb_sectors -= n;
195 sector_num += n;
196 bytes_done += nbytes;
197 }
198
199 qemu_iovec_destroy(&hd_qiov);
200 return ret;
201}
202
481fb9cf
DL
203static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
204 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
6ada7453 205{
29442569 206 BDRVParallelsState *s = bs->opaque;
481fb9cf
DL
207 uint64_t bytes_done = 0;
208 QEMUIOVector hd_qiov;
209 int ret = 0;
210
211 qemu_iovec_init(&hd_qiov, qiov->niov);
29442569 212
6ada7453 213 while (nb_sectors > 0) {
481fb9cf
DL
214 int64_t position;
215 int n, nbytes;
216
217 qemu_co_mutex_lock(&s->lock);
218 position = seek_to_sector(s, sector_num);
219 qemu_co_mutex_unlock(&s->lock);
220
221 n = cluster_remainder(s, sector_num, nb_sectors);
222 nbytes = n << BDRV_SECTOR_BITS;
223
224 if (position < 0) {
225 qemu_iovec_memset(qiov, bytes_done, 0, nbytes);
226 } else {
227 qemu_iovec_reset(&hd_qiov);
228 qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
229
230 ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
29442569 231 if (ret < 0) {
481fb9cf 232 break;
29442569 233 }
9d8b88f6 234 }
481fb9cf 235
9de9da17
RK
236 nb_sectors -= n;
237 sector_num += n;
481fb9cf 238 bytes_done += nbytes;
6ada7453 239 }
6ada7453 240
481fb9cf 241 qemu_iovec_destroy(&hd_qiov);
2914caa0
PB
242 return ret;
243}
244
49ad6467
DL
245
246static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
247 BdrvCheckMode fix)
248{
249 BDRVParallelsState *s = bs->opaque;
250 int64_t size, prev_off, high_off;
251 int ret;
252 uint32_t i;
253 bool flush_bat = false;
254 int cluster_size = s->tracks << BDRV_SECTOR_BITS;
255
256 size = bdrv_getlength(bs->file);
257 if (size < 0) {
258 res->check_errors++;
259 return size;
260 }
261
262 res->bfi.total_clusters = s->bat_size;
263 res->bfi.compressed_clusters = 0; /* compression is not supported */
264
265 high_off = 0;
266 prev_off = 0;
267 for (i = 0; i < s->bat_size; i++) {
268 int64_t off = bat2sect(s, i) << BDRV_SECTOR_BITS;
269 if (off == 0) {
270 prev_off = 0;
271 continue;
272 }
273
274 /* cluster outside the image */
275 if (off > size) {
276 fprintf(stderr, "%s cluster %u is outside image\n",
277 fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
278 res->corruptions++;
279 if (fix & BDRV_FIX_ERRORS) {
280 prev_off = 0;
281 s->bat_bitmap[i] = 0;
282 res->corruptions_fixed++;
283 flush_bat = true;
284 continue;
285 }
286 }
287
288 res->bfi.allocated_clusters++;
289 if (off > high_off) {
290 high_off = off;
291 }
292
293 if (prev_off != 0 && (prev_off + cluster_size) != off) {
294 res->bfi.fragmented_clusters++;
295 }
296 prev_off = off;
297 }
298
299 if (flush_bat) {
300 ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size);
301 if (ret < 0) {
302 res->check_errors++;
303 return ret;
304 }
305 }
306
307 res->image_end_offset = high_off + cluster_size;
308 if (size > res->image_end_offset) {
309 int64_t count;
310 count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size);
311 fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n",
312 fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR",
313 size - res->image_end_offset);
314 res->leaks += count;
315 if (fix & BDRV_FIX_LEAKS) {
316 ret = bdrv_truncate(bs->file, res->image_end_offset);
317 if (ret < 0) {
318 res->check_errors++;
319 return ret;
320 }
321 res->leaks_fixed += count;
322 }
323 }
324
325 return 0;
326}
327
328
74cf6c50
DL
329static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
330{
331 int64_t total_size, cl_size;
332 uint8_t tmp[BDRV_SECTOR_SIZE];
333 Error *local_err = NULL;
334 BlockDriverState *file;
369f7de9 335 uint32_t bat_entries, bat_sectors;
74cf6c50
DL
336 ParallelsHeader header;
337 int ret;
338
339 total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
340 BDRV_SECTOR_SIZE);
341 cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
342 DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
343
344 ret = bdrv_create_file(filename, opts, &local_err);
345 if (ret < 0) {
346 error_propagate(errp, local_err);
347 return ret;
348 }
349
350 file = NULL;
351 ret = bdrv_open(&file, filename, NULL, NULL,
352 BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
353 if (ret < 0) {
354 error_propagate(errp, local_err);
355 return ret;
356 }
357 ret = bdrv_truncate(file, 0);
358 if (ret < 0) {
359 goto exit;
360 }
361
369f7de9
DL
362 bat_entries = DIV_ROUND_UP(total_size, cl_size);
363 bat_sectors = DIV_ROUND_UP(bat_entries * sizeof(uint32_t) +
74cf6c50 364 sizeof(ParallelsHeader), cl_size);
369f7de9 365 bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS;
74cf6c50
DL
366
367 memset(&header, 0, sizeof(header));
368 memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic));
369 header.version = cpu_to_le32(HEADER_VERSION);
370 /* don't care much about geometry, it is not used on image level */
371 header.heads = cpu_to_le32(16);
372 header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE / 16 / 32);
373 header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS);
369f7de9 374 header.bat_entries = cpu_to_le32(bat_entries);
74cf6c50 375 header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE));
369f7de9 376 header.data_off = cpu_to_le32(bat_sectors);
74cf6c50
DL
377
378 /* write all the data */
379 memset(tmp, 0, sizeof(tmp));
380 memcpy(tmp, &header, sizeof(header));
381
382 ret = bdrv_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE);
383 if (ret < 0) {
384 goto exit;
385 }
369f7de9 386 ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0);
74cf6c50
DL
387 if (ret < 0) {
388 goto exit;
389 }
390 ret = 0;
391
392done:
393 bdrv_unref(file);
394 return ret;
395
396exit:
397 error_setg_errno(errp, -ret, "Failed to create Parallels image");
398 goto done;
399}
400
23d6bd3b
DL
401
402static int parallels_probe(const uint8_t *buf, int buf_size,
403 const char *filename)
404{
405 const ParallelsHeader *ph = (const void *)buf;
406
407 if (buf_size < sizeof(ParallelsHeader)) {
408 return 0;
409 }
410
411 if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
412 !memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
413 (le32_to_cpu(ph->version) == HEADER_VERSION)) {
414 return 100;
415 }
416
417 return 0;
418}
419
420static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
421 Error **errp)
422{
423 BDRVParallelsState *s = bs->opaque;
424 ParallelsHeader ph;
425 int ret, size;
426
427 ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
428 if (ret < 0) {
429 goto fail;
430 }
431
432 bs->total_sectors = le64_to_cpu(ph.nb_sectors);
433
434 if (le32_to_cpu(ph.version) != HEADER_VERSION) {
435 goto fail_format;
436 }
437 if (!memcmp(ph.magic, HEADER_MAGIC, 16)) {
438 s->off_multiplier = 1;
439 bs->total_sectors = 0xffffffff & bs->total_sectors;
440 } else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) {
441 s->off_multiplier = le32_to_cpu(ph.tracks);
442 } else {
443 goto fail_format;
444 }
445
446 s->tracks = le32_to_cpu(ph.tracks);
447 if (s->tracks == 0) {
448 error_setg(errp, "Invalid image: Zero sectors per track");
449 ret = -EINVAL;
450 goto fail;
451 }
452 if (s->tracks > INT32_MAX/513) {
453 error_setg(errp, "Invalid image: Too big cluster");
454 ret = -EFBIG;
455 goto fail;
456 }
457
458 s->bat_size = le32_to_cpu(ph.bat_entries);
459 if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
460 error_setg(errp, "Catalog too large");
461 ret = -EFBIG;
462 goto fail;
463 }
464
465 size = sizeof(ParallelsHeader) + sizeof(uint32_t) * s->bat_size;
466 s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file));
467 s->header = qemu_try_blockalign(bs->file, s->header_size);
468 if (s->header == NULL) {
469 ret = -ENOMEM;
470 goto fail;
471 }
472 if (le32_to_cpu(ph.data_off) < s->header_size) {
473 /* there is not enough unused space to fit to block align between BAT
474 and actual data. We can't avoid read-modify-write... */
475 s->header_size = size;
476 }
477
478 ret = bdrv_pread(bs->file, 0, s->header, s->header_size);
479 if (ret < 0) {
480 goto fail;
481 }
482 s->bat_bitmap = (uint32_t *)(s->header + 1);
483
484 s->has_truncate = bdrv_has_zero_init(bs->file) &&
485 bdrv_truncate(bs->file, bdrv_getlength(bs->file)) == 0;
486
487 qemu_co_mutex_init(&s->lock);
488 return 0;
489
490fail_format:
491 error_setg(errp, "Image not in Parallels format");
492 ret = -EINVAL;
493fail:
494 qemu_vfree(s->header);
495 return ret;
496}
497
498
6ada7453
TS
499static void parallels_close(BlockDriverState *bs)
500{
501 BDRVParallelsState *s = bs->opaque;
9eae9cca 502 qemu_vfree(s->header);
6ada7453
TS
503}
504
74cf6c50
DL
505static QemuOptsList parallels_create_opts = {
506 .name = "parallels-create-opts",
507 .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
508 .desc = {
509 {
510 .name = BLOCK_OPT_SIZE,
511 .type = QEMU_OPT_SIZE,
512 .help = "Virtual disk size",
513 },
514 {
515 .name = BLOCK_OPT_CLUSTER_SIZE,
516 .type = QEMU_OPT_SIZE,
517 .help = "Parallels image cluster size",
518 .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
519 },
520 { /* end of list */ }
521 }
522};
523
5efa9d5a 524static BlockDriver bdrv_parallels = {
e60f469c
AJ
525 .format_name = "parallels",
526 .instance_size = sizeof(BDRVParallelsState),
527 .bdrv_probe = parallels_probe,
1dec5a70 528 .bdrv_open = parallels_open,
e60f469c 529 .bdrv_close = parallels_close,
dd3bed16 530 .bdrv_co_get_block_status = parallels_co_get_block_status,
d0e61ce5 531 .bdrv_has_zero_init = bdrv_has_zero_init_1,
481fb9cf 532 .bdrv_co_readv = parallels_co_readv,
5a41e1fa 533 .bdrv_co_writev = parallels_co_writev,
74cf6c50
DL
534
535 .bdrv_create = parallels_create,
49ad6467 536 .bdrv_check = parallels_check,
74cf6c50 537 .create_opts = &parallels_create_opts,
6ada7453 538};
5efa9d5a
AL
539
540static void bdrv_parallels_init(void)
541{
542 bdrv_register(&bdrv_parallels);
543}
544
545block_init(bdrv_parallels_init);