]> git.proxmox.com Git - mirror_qemu.git/blob - block/parallels.c
block/parallels: create bat2sect helper
[mirror_qemu.git] / block / parallels.c
1 /*
2 * Block driver for Parallels disk image format
3 *
4 * Copyright (c) 2007 Alex Beregszaszi
5 * Copyright (c) 2015 Denis V. Lunev <den@openvz.org>
6 *
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
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 */
30 #include "qemu-common.h"
31 #include "block/block_int.h"
32 #include "qemu/module.h"
33
34 /**************************************************************/
35
36 #define HEADER_MAGIC "WithoutFreeSpace"
37 #define HEADER_MAGIC2 "WithouFreSpacExt"
38 #define HEADER_VERSION 2
39
40 #define DEFAULT_CLUSTER_SIZE 1048576 /* 1 MiB */
41
42
43 // always little-endian
44 typedef struct ParallelsHeader {
45 char magic[16]; // "WithoutFreeSpace"
46 uint32_t version;
47 uint32_t heads;
48 uint32_t cylinders;
49 uint32_t tracks;
50 uint32_t bat_entries;
51 uint64_t nb_sectors;
52 uint32_t inuse;
53 uint32_t data_off;
54 char padding[12];
55 } QEMU_PACKED ParallelsHeader;
56
57 typedef struct BDRVParallelsState {
58 /** Locking is conservative, the lock protects
59 * - image file extending (truncate, fallocate)
60 * - any access to block allocation table
61 */
62 CoMutex lock;
63
64 uint32_t *bat_bitmap;
65 unsigned int bat_size;
66
67 unsigned int tracks;
68
69 unsigned int off_multiplier;
70
71 bool has_truncate;
72 } BDRVParallelsState;
73
74 static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
75 {
76 const ParallelsHeader *ph = (const void *)buf;
77
78 if (buf_size < sizeof(ParallelsHeader))
79 return 0;
80
81 if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
82 !memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
83 (le32_to_cpu(ph->version) == HEADER_VERSION))
84 return 100;
85
86 return 0;
87 }
88
89 static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
90 Error **errp)
91 {
92 BDRVParallelsState *s = bs->opaque;
93 int i;
94 ParallelsHeader ph;
95 int ret;
96
97 ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph));
98 if (ret < 0) {
99 goto fail;
100 }
101
102 bs->total_sectors = le64_to_cpu(ph.nb_sectors);
103
104 if (le32_to_cpu(ph.version) != HEADER_VERSION) {
105 goto fail_format;
106 }
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 {
113 goto fail_format;
114 }
115
116 s->tracks = le32_to_cpu(ph.tracks);
117 if (s->tracks == 0) {
118 error_setg(errp, "Invalid image: Zero sectors per track");
119 ret = -EINVAL;
120 goto fail;
121 }
122 if (s->tracks > INT32_MAX/513) {
123 error_setg(errp, "Invalid image: Too big cluster");
124 ret = -EFBIG;
125 goto fail;
126 }
127
128 s->bat_size = le32_to_cpu(ph.bat_entries);
129 if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
130 error_setg(errp, "Catalog too large");
131 ret = -EFBIG;
132 goto fail;
133 }
134 s->bat_bitmap = g_try_new(uint32_t, s->bat_size);
135 if (s->bat_size && s->bat_bitmap == NULL) {
136 ret = -ENOMEM;
137 goto fail;
138 }
139
140 ret = bdrv_pread(bs->file, sizeof(ParallelsHeader),
141 s->bat_bitmap, s->bat_size * sizeof(uint32_t));
142 if (ret < 0) {
143 goto fail;
144 }
145
146 for (i = 0; i < s->bat_size; i++) {
147 le32_to_cpus(&s->bat_bitmap[i]);
148 }
149
150 s->has_truncate = bdrv_has_zero_init(bs->file) &&
151 bdrv_truncate(bs->file, bdrv_getlength(bs->file)) == 0;
152
153 qemu_co_mutex_init(&s->lock);
154 return 0;
155
156 fail_format:
157 error_setg(errp, "Image not in Parallels format");
158 ret = -EINVAL;
159 fail:
160 g_free(s->bat_bitmap);
161 return ret;
162 }
163
164
165 static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
166 {
167 return (uint64_t)s->bat_bitmap[idx] * s->off_multiplier;
168 }
169
170 static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num)
171 {
172 uint32_t index, offset;
173
174 index = sector_num / s->tracks;
175 offset = sector_num % s->tracks;
176
177 /* not allocated */
178 if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) {
179 return -1;
180 }
181 return bat2sect(s, index) + offset;
182 }
183
184 static 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
191 static 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
201 if (idx >= s->bat_size) {
202 return -EINVAL;
203 }
204 if (s->bat_bitmap[idx] != 0) {
205 return bat2sect(s, idx) + offset;
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
218 s->bat_bitmap[idx] = pos / s->off_multiplier;
219
220 tmp = cpu_to_le32(s->bat_bitmap[idx]);
221
222 ret = bdrv_pwrite(bs->file,
223 sizeof(ParallelsHeader) + idx * sizeof(tmp), &tmp, sizeof(tmp));
224 if (ret < 0) {
225 s->bat_bitmap[idx] = 0;
226 return ret;
227 }
228 return bat2sect(s, idx) + offset;
229 }
230
231 static 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
251 static 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
293 static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
294 int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
295 {
296 BDRVParallelsState *s = bs->opaque;
297 uint64_t bytes_done = 0;
298 QEMUIOVector hd_qiov;
299 int ret = 0;
300
301 qemu_iovec_init(&hd_qiov, qiov->niov);
302
303 while (nb_sectors > 0) {
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);
321 if (ret < 0) {
322 break;
323 }
324 }
325
326 nb_sectors -= n;
327 sector_num += n;
328 bytes_done += nbytes;
329 }
330
331 qemu_iovec_destroy(&hd_qiov);
332 return ret;
333 }
334
335 static 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;
341 uint32_t bat_entries, bat_sectors;
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
368 bat_entries = DIV_ROUND_UP(total_size, cl_size);
369 bat_sectors = DIV_ROUND_UP(bat_entries * sizeof(uint32_t) +
370 sizeof(ParallelsHeader), cl_size);
371 bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS;
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);
380 header.bat_entries = cpu_to_le32(bat_entries);
381 header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE));
382 header.data_off = cpu_to_le32(bat_sectors);
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 }
392 ret = bdrv_write_zeroes(file, 1, bat_sectors - 1, 0);
393 if (ret < 0) {
394 goto exit;
395 }
396 ret = 0;
397
398 done:
399 bdrv_unref(file);
400 return ret;
401
402 exit:
403 error_setg_errno(errp, -ret, "Failed to create Parallels image");
404 goto done;
405 }
406
407 static void parallels_close(BlockDriverState *bs)
408 {
409 BDRVParallelsState *s = bs->opaque;
410 g_free(s->bat_bitmap);
411 }
412
413 static 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
432 static BlockDriver bdrv_parallels = {
433 .format_name = "parallels",
434 .instance_size = sizeof(BDRVParallelsState),
435 .bdrv_probe = parallels_probe,
436 .bdrv_open = parallels_open,
437 .bdrv_close = parallels_close,
438 .bdrv_co_get_block_status = parallels_co_get_block_status,
439 .bdrv_has_zero_init = bdrv_has_zero_init_1,
440 .bdrv_co_readv = parallels_co_readv,
441 .bdrv_co_writev = parallels_co_writev,
442
443 .bdrv_create = parallels_create,
444 .create_opts = &parallels_create_opts,
445 };
446
447 static void bdrv_parallels_init(void)
448 {
449 bdrv_register(&bdrv_parallels);
450 }
451
452 block_init(bdrv_parallels_init);