Commit ea2384d36e1e5f6dfd44b748d290762181c38350
1 parent
e4d4fe3c
new disk image layer
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1037 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
7 changed files
with
2372 additions
and
410 deletions
block-cow.c
0 → 100644
| 1 | +/* | |
| 2 | + * Block driver for the COW format | |
| 3 | + * | |
| 4 | + * Copyright (c) 2004 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#ifndef _WIN32 | |
| 25 | +#include "vl.h" | |
| 26 | +#include "block_int.h" | |
| 27 | +#include <sys/mman.h> | |
| 28 | + | |
| 29 | +/**************************************************************/ | |
| 30 | +/* COW block driver using file system holes */ | |
| 31 | + | |
| 32 | +/* user mode linux compatible COW file */ | |
| 33 | +#define COW_MAGIC 0x4f4f4f4d /* MOOO */ | |
| 34 | +#define COW_VERSION 2 | |
| 35 | + | |
| 36 | +struct cow_header_v2 { | |
| 37 | + uint32_t magic; | |
| 38 | + uint32_t version; | |
| 39 | + char backing_file[1024]; | |
| 40 | + int32_t mtime; | |
| 41 | + uint64_t size; | |
| 42 | + uint32_t sectorsize; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +typedef struct BDRVCowState { | |
| 46 | + int fd; | |
| 47 | + uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ | |
| 48 | + uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ | |
| 49 | + int cow_bitmap_size; | |
| 50 | + int64_t cow_sectors_offset; | |
| 51 | +} BDRVCowState; | |
| 52 | + | |
| 53 | +static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) | |
| 54 | +{ | |
| 55 | + const struct cow_header_v2 *cow_header = (const void *)buf; | |
| 56 | + | |
| 57 | + if (be32_to_cpu(cow_header->magic) == COW_MAGIC && | |
| 58 | + be32_to_cpu(cow_header->version) == COW_VERSION) | |
| 59 | + return 100; | |
| 60 | + else | |
| 61 | + return 0; | |
| 62 | +} | |
| 63 | + | |
| 64 | +static int cow_open(BlockDriverState *bs, const char *filename) | |
| 65 | +{ | |
| 66 | + BDRVCowState *s = bs->opaque; | |
| 67 | + int fd; | |
| 68 | + struct cow_header_v2 cow_header; | |
| 69 | + int64_t size; | |
| 70 | + | |
| 71 | + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | |
| 72 | + if (fd < 0) { | |
| 73 | + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | |
| 74 | + if (fd < 0) | |
| 75 | + return -1; | |
| 76 | + } | |
| 77 | + s->fd = fd; | |
| 78 | + /* see if it is a cow image */ | |
| 79 | + if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { | |
| 80 | + goto fail; | |
| 81 | + } | |
| 82 | + | |
| 83 | + if (be32_to_cpu(cow_header.magic) != COW_MAGIC || | |
| 84 | + be32_to_cpu(cow_header.version) != COW_VERSION) { | |
| 85 | + goto fail; | |
| 86 | + } | |
| 87 | + | |
| 88 | + /* cow image found */ | |
| 89 | + size = be64_to_cpu(cow_header.size); | |
| 90 | + bs->total_sectors = size / 512; | |
| 91 | + | |
| 92 | + pstrcpy(bs->backing_file, sizeof(bs->backing_file), | |
| 93 | + cow_header.backing_file); | |
| 94 | + | |
| 95 | +#if 0 | |
| 96 | + if (cow_header.backing_file[0] != '\0') { | |
| 97 | + if (stat(cow_header.backing_file, &st) != 0) { | |
| 98 | + fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file); | |
| 99 | + goto fail; | |
| 100 | + } | |
| 101 | + if (st.st_mtime != be32_to_cpu(cow_header.mtime)) { | |
| 102 | + fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file); | |
| 103 | + goto fail; | |
| 104 | + } | |
| 105 | + fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE); | |
| 106 | + if (fd < 0) | |
| 107 | + goto fail; | |
| 108 | + bs->fd = fd; | |
| 109 | + } | |
| 110 | +#endif | |
| 111 | + /* mmap the bitmap */ | |
| 112 | + s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); | |
| 113 | + s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size), | |
| 114 | + s->cow_bitmap_size, | |
| 115 | + PROT_READ | PROT_WRITE, | |
| 116 | + MAP_SHARED, s->fd, 0); | |
| 117 | + if (s->cow_bitmap_addr == MAP_FAILED) | |
| 118 | + goto fail; | |
| 119 | + s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); | |
| 120 | + s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; | |
| 121 | + return 0; | |
| 122 | + fail: | |
| 123 | + close(fd); | |
| 124 | + return -1; | |
| 125 | +} | |
| 126 | + | |
| 127 | +static inline void set_bit(uint8_t *bitmap, int64_t bitnum) | |
| 128 | +{ | |
| 129 | + bitmap[bitnum / 8] |= (1 << (bitnum%8)); | |
| 130 | +} | |
| 131 | + | |
| 132 | +static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) | |
| 133 | +{ | |
| 134 | + return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); | |
| 135 | +} | |
| 136 | + | |
| 137 | + | |
| 138 | +/* Return true if first block has been changed (ie. current version is | |
| 139 | + * in COW file). Set the number of continuous blocks for which that | |
| 140 | + * is true. */ | |
| 141 | +static inline int is_changed(uint8_t *bitmap, | |
| 142 | + int64_t sector_num, int nb_sectors, | |
| 143 | + int *num_same) | |
| 144 | +{ | |
| 145 | + int changed; | |
| 146 | + | |
| 147 | + if (!bitmap || nb_sectors == 0) { | |
| 148 | + *num_same = nb_sectors; | |
| 149 | + return 0; | |
| 150 | + } | |
| 151 | + | |
| 152 | + changed = is_bit_set(bitmap, sector_num); | |
| 153 | + for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { | |
| 154 | + if (is_bit_set(bitmap, sector_num + *num_same) != changed) | |
| 155 | + break; | |
| 156 | + } | |
| 157 | + | |
| 158 | + return changed; | |
| 159 | +} | |
| 160 | + | |
| 161 | +static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, | |
| 162 | + int nb_sectors, int *pnum) | |
| 163 | +{ | |
| 164 | + BDRVCowState *s = bs->opaque; | |
| 165 | + return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); | |
| 166 | +} | |
| 167 | + | |
| 168 | +static int cow_read(BlockDriverState *bs, int64_t sector_num, | |
| 169 | + uint8_t *buf, int nb_sectors) | |
| 170 | +{ | |
| 171 | + BDRVCowState *s = bs->opaque; | |
| 172 | + int ret, n; | |
| 173 | + | |
| 174 | + while (nb_sectors > 0) { | |
| 175 | + if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) { | |
| 176 | + lseek64(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | |
| 177 | + ret = read(s->fd, buf, n * 512); | |
| 178 | + if (ret != n * 512) | |
| 179 | + return -1; | |
| 180 | + } else { | |
| 181 | + memset(buf, 0, n * 512); | |
| 182 | + } | |
| 183 | + nb_sectors -= n; | |
| 184 | + sector_num += n; | |
| 185 | + buf += n * 512; | |
| 186 | + } | |
| 187 | + return 0; | |
| 188 | +} | |
| 189 | + | |
| 190 | +static int cow_write(BlockDriverState *bs, int64_t sector_num, | |
| 191 | + const uint8_t *buf, int nb_sectors) | |
| 192 | +{ | |
| 193 | + BDRVCowState *s = bs->opaque; | |
| 194 | + int ret, i; | |
| 195 | + | |
| 196 | + lseek64(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | |
| 197 | + ret = write(s->fd, buf, nb_sectors * 512); | |
| 198 | + if (ret != nb_sectors * 512) | |
| 199 | + return -1; | |
| 200 | + for (i = 0; i < nb_sectors; i++) | |
| 201 | + set_bit(s->cow_bitmap, sector_num + i); | |
| 202 | + return 0; | |
| 203 | +} | |
| 204 | + | |
| 205 | +static int cow_close(BlockDriverState *bs) | |
| 206 | +{ | |
| 207 | + BDRVCowState *s = bs->opaque; | |
| 208 | + munmap(s->cow_bitmap_addr, s->cow_bitmap_size); | |
| 209 | + close(s->fd); | |
| 210 | +} | |
| 211 | + | |
| 212 | +static int cow_create(const char *filename, int64_t image_sectors, | |
| 213 | + const char *image_filename, int flags) | |
| 214 | +{ | |
| 215 | + int fd, cow_fd; | |
| 216 | + struct cow_header_v2 cow_header; | |
| 217 | + struct stat st; | |
| 218 | + | |
| 219 | + if (flags) | |
| 220 | + return -ENOTSUP; | |
| 221 | + | |
| 222 | + cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | |
| 223 | + 0644); | |
| 224 | + if (cow_fd < 0) | |
| 225 | + return -1; | |
| 226 | + memset(&cow_header, 0, sizeof(cow_header)); | |
| 227 | + cow_header.magic = cpu_to_be32(COW_MAGIC); | |
| 228 | + cow_header.version = cpu_to_be32(COW_VERSION); | |
| 229 | + if (image_filename) { | |
| 230 | + fd = open(image_filename, O_RDONLY | O_BINARY); | |
| 231 | + if (fd < 0) { | |
| 232 | + close(cow_fd); | |
| 233 | + return -1; | |
| 234 | + } | |
| 235 | + if (fstat(fd, &st) != 0) { | |
| 236 | + close(fd); | |
| 237 | + return -1; | |
| 238 | + } | |
| 239 | + close(fd); | |
| 240 | + cow_header.mtime = cpu_to_be32(st.st_mtime); | |
| 241 | + realpath(image_filename, cow_header.backing_file); | |
| 242 | + } | |
| 243 | + cow_header.sectorsize = cpu_to_be32(512); | |
| 244 | + cow_header.size = cpu_to_be64(image_sectors * 512); | |
| 245 | + write(cow_fd, &cow_header, sizeof(cow_header)); | |
| 246 | + /* resize to include at least all the bitmap */ | |
| 247 | + ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); | |
| 248 | + close(cow_fd); | |
| 249 | + return 0; | |
| 250 | +} | |
| 251 | + | |
| 252 | +BlockDriver bdrv_cow = { | |
| 253 | + "cow", | |
| 254 | + sizeof(BDRVCowState), | |
| 255 | + cow_probe, | |
| 256 | + cow_open, | |
| 257 | + cow_read, | |
| 258 | + cow_write, | |
| 259 | + cow_close, | |
| 260 | + cow_create, | |
| 261 | + cow_is_allocated, | |
| 262 | +}; | |
| 263 | +#endif | ... | ... |
block-qcow.c
0 → 100644
| 1 | +/* | |
| 2 | + * Block driver for the QCOW format | |
| 3 | + * | |
| 4 | + * Copyright (c) 2004 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#include "vl.h" | |
| 25 | +#include "block_int.h" | |
| 26 | +#include "zlib.h" | |
| 27 | +#include "aes.h" | |
| 28 | + | |
| 29 | +/**************************************************************/ | |
| 30 | +/* QEMU COW block driver with compression and encryption support */ | |
| 31 | + | |
| 32 | +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) | |
| 33 | +#define QCOW_VERSION 1 | |
| 34 | + | |
| 35 | +#define QCOW_CRYPT_NONE 0 | |
| 36 | +#define QCOW_CRYPT_AES 1 | |
| 37 | + | |
| 38 | +#define QCOW_OFLAG_COMPRESSED (1LL << 63) | |
| 39 | + | |
| 40 | +typedef struct QCowHeader { | |
| 41 | + uint32_t magic; | |
| 42 | + uint32_t version; | |
| 43 | + uint64_t backing_file_offset; | |
| 44 | + uint32_t backing_file_size; | |
| 45 | + uint32_t mtime; | |
| 46 | + uint64_t size; /* in bytes */ | |
| 47 | + uint8_t cluster_bits; | |
| 48 | + uint8_t l2_bits; | |
| 49 | + uint32_t crypt_method; | |
| 50 | + uint64_t l1_table_offset; | |
| 51 | +} QCowHeader; | |
| 52 | + | |
| 53 | +#define L2_CACHE_SIZE 16 | |
| 54 | + | |
| 55 | +typedef struct BDRVQcowState { | |
| 56 | + int fd; | |
| 57 | + int cluster_bits; | |
| 58 | + int cluster_size; | |
| 59 | + int cluster_sectors; | |
| 60 | + int l2_bits; | |
| 61 | + int l2_size; | |
| 62 | + int l1_size; | |
| 63 | + uint64_t cluster_offset_mask; | |
| 64 | + uint64_t l1_table_offset; | |
| 65 | + uint64_t *l1_table; | |
| 66 | + uint64_t *l2_cache; | |
| 67 | + uint64_t l2_cache_offsets[L2_CACHE_SIZE]; | |
| 68 | + uint32_t l2_cache_counts[L2_CACHE_SIZE]; | |
| 69 | + uint8_t *cluster_cache; | |
| 70 | + uint8_t *cluster_data; | |
| 71 | + uint64_t cluster_cache_offset; | |
| 72 | + uint32_t crypt_method; /* current crypt method, 0 if no key yet */ | |
| 73 | + uint32_t crypt_method_header; | |
| 74 | + AES_KEY aes_encrypt_key; | |
| 75 | + AES_KEY aes_decrypt_key; | |
| 76 | +} BDRVQcowState; | |
| 77 | + | |
| 78 | +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); | |
| 79 | + | |
| 80 | +static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | |
| 81 | +{ | |
| 82 | + const QCowHeader *cow_header = (const void *)buf; | |
| 83 | + | |
| 84 | + if (be32_to_cpu(cow_header->magic) == QCOW_MAGIC && | |
| 85 | + be32_to_cpu(cow_header->version) == QCOW_VERSION) | |
| 86 | + return 100; | |
| 87 | + else | |
| 88 | + return 0; | |
| 89 | +} | |
| 90 | + | |
| 91 | +static int qcow_open(BlockDriverState *bs, const char *filename) | |
| 92 | +{ | |
| 93 | + BDRVQcowState *s = bs->opaque; | |
| 94 | + int fd, len, i, shift; | |
| 95 | + QCowHeader header; | |
| 96 | + | |
| 97 | + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | |
| 98 | + if (fd < 0) { | |
| 99 | + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | |
| 100 | + if (fd < 0) | |
| 101 | + return -1; | |
| 102 | + } | |
| 103 | + s->fd = fd; | |
| 104 | + if (read(fd, &header, sizeof(header)) != sizeof(header)) | |
| 105 | + goto fail; | |
| 106 | + be32_to_cpus(&header.magic); | |
| 107 | + be32_to_cpus(&header.version); | |
| 108 | + be64_to_cpus(&header.backing_file_offset); | |
| 109 | + be32_to_cpus(&header.backing_file_size); | |
| 110 | + be32_to_cpus(&header.mtime); | |
| 111 | + be64_to_cpus(&header.size); | |
| 112 | + be32_to_cpus(&header.crypt_method); | |
| 113 | + be64_to_cpus(&header.l1_table_offset); | |
| 114 | + | |
| 115 | + if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) | |
| 116 | + goto fail; | |
| 117 | + if (header.size <= 1 || header.cluster_bits < 9) | |
| 118 | + goto fail; | |
| 119 | + if (header.crypt_method > QCOW_CRYPT_AES) | |
| 120 | + goto fail; | |
| 121 | + s->crypt_method_header = header.crypt_method; | |
| 122 | + if (s->crypt_method_header) | |
| 123 | + bs->encrypted = 1; | |
| 124 | + s->cluster_bits = header.cluster_bits; | |
| 125 | + s->cluster_size = 1 << s->cluster_bits; | |
| 126 | + s->cluster_sectors = 1 << (s->cluster_bits - 9); | |
| 127 | + s->l2_bits = header.l2_bits; | |
| 128 | + s->l2_size = 1 << s->l2_bits; | |
| 129 | + bs->total_sectors = header.size / 512; | |
| 130 | + s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1; | |
| 131 | + | |
| 132 | + /* read the level 1 table */ | |
| 133 | + shift = s->cluster_bits + s->l2_bits; | |
| 134 | + s->l1_size = (header.size + (1LL << shift) - 1) >> shift; | |
| 135 | + | |
| 136 | + s->l1_table_offset = header.l1_table_offset; | |
| 137 | + s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); | |
| 138 | + if (!s->l1_table) | |
| 139 | + goto fail; | |
| 140 | + lseek64(fd, s->l1_table_offset, SEEK_SET); | |
| 141 | + if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) != | |
| 142 | + s->l1_size * sizeof(uint64_t)) | |
| 143 | + goto fail; | |
| 144 | + for(i = 0;i < s->l1_size; i++) { | |
| 145 | + be64_to_cpus(&s->l1_table[i]); | |
| 146 | + } | |
| 147 | + /* alloc L2 cache */ | |
| 148 | + s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); | |
| 149 | + if (!s->l2_cache) | |
| 150 | + goto fail; | |
| 151 | + s->cluster_cache = qemu_malloc(s->cluster_size); | |
| 152 | + if (!s->cluster_cache) | |
| 153 | + goto fail; | |
| 154 | + s->cluster_data = qemu_malloc(s->cluster_size); | |
| 155 | + if (!s->cluster_data) | |
| 156 | + goto fail; | |
| 157 | + s->cluster_cache_offset = -1; | |
| 158 | + | |
| 159 | + /* read the backing file name */ | |
| 160 | + if (header.backing_file_offset != 0) { | |
| 161 | + len = header.backing_file_size; | |
| 162 | + if (len > 1023) | |
| 163 | + len = 1023; | |
| 164 | + lseek64(fd, header.backing_file_offset, SEEK_SET); | |
| 165 | + if (read(fd, bs->backing_file, len) != len) | |
| 166 | + goto fail; | |
| 167 | + bs->backing_file[len] = '\0'; | |
| 168 | + } | |
| 169 | + return 0; | |
| 170 | + | |
| 171 | + fail: | |
| 172 | + qemu_free(s->l1_table); | |
| 173 | + qemu_free(s->l2_cache); | |
| 174 | + qemu_free(s->cluster_cache); | |
| 175 | + qemu_free(s->cluster_data); | |
| 176 | + close(fd); | |
| 177 | + return -1; | |
| 178 | +} | |
| 179 | + | |
| 180 | +static int qcow_set_key(BlockDriverState *bs, const char *key) | |
| 181 | +{ | |
| 182 | + BDRVQcowState *s = bs->opaque; | |
| 183 | + uint8_t keybuf[16]; | |
| 184 | + int len, i; | |
| 185 | + | |
| 186 | + memset(keybuf, 0, 16); | |
| 187 | + len = strlen(key); | |
| 188 | + if (len > 16) | |
| 189 | + len = 16; | |
| 190 | + /* XXX: we could compress the chars to 7 bits to increase | |
| 191 | + entropy */ | |
| 192 | + for(i = 0;i < len;i++) { | |
| 193 | + keybuf[i] = key[i]; | |
| 194 | + } | |
| 195 | + s->crypt_method = s->crypt_method_header; | |
| 196 | + | |
| 197 | + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) | |
| 198 | + return -1; | |
| 199 | + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) | |
| 200 | + return -1; | |
| 201 | +#if 0 | |
| 202 | + /* test */ | |
| 203 | + { | |
| 204 | + uint8_t in[16]; | |
| 205 | + uint8_t out[16]; | |
| 206 | + uint8_t tmp[16]; | |
| 207 | + for(i=0;i<16;i++) | |
| 208 | + in[i] = i; | |
| 209 | + AES_encrypt(in, tmp, &s->aes_encrypt_key); | |
| 210 | + AES_decrypt(tmp, out, &s->aes_decrypt_key); | |
| 211 | + for(i = 0; i < 16; i++) | |
| 212 | + printf(" %02x", tmp[i]); | |
| 213 | + printf("\n"); | |
| 214 | + for(i = 0; i < 16; i++) | |
| 215 | + printf(" %02x", out[i]); | |
| 216 | + printf("\n"); | |
| 217 | + } | |
| 218 | +#endif | |
| 219 | + return 0; | |
| 220 | +} | |
| 221 | + | |
| 222 | +/* The crypt function is compatible with the linux cryptoloop | |
| 223 | + algorithm for < 4 GB images. NOTE: out_buf == in_buf is | |
| 224 | + supported */ | |
| 225 | +static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | |
| 226 | + uint8_t *out_buf, const uint8_t *in_buf, | |
| 227 | + int nb_sectors, int enc, | |
| 228 | + const AES_KEY *key) | |
| 229 | +{ | |
| 230 | + union { | |
| 231 | + uint64_t ll[2]; | |
| 232 | + uint8_t b[16]; | |
| 233 | + } ivec; | |
| 234 | + int i; | |
| 235 | + | |
| 236 | + for(i = 0; i < nb_sectors; i++) { | |
| 237 | + ivec.ll[0] = cpu_to_le64(sector_num); | |
| 238 | + ivec.ll[1] = 0; | |
| 239 | + AES_cbc_encrypt(in_buf, out_buf, 512, key, | |
| 240 | + ivec.b, enc); | |
| 241 | + sector_num++; | |
| 242 | + in_buf += 512; | |
| 243 | + out_buf += 512; | |
| 244 | + } | |
| 245 | +} | |
| 246 | + | |
| 247 | +/* 'allocate' is: | |
| 248 | + * | |
| 249 | + * 0 to not allocate. | |
| 250 | + * | |
| 251 | + * 1 to allocate a normal cluster (for sector indexes 'n_start' to | |
| 252 | + * 'n_end') | |
| 253 | + * | |
| 254 | + * 2 to allocate a compressed cluster of size | |
| 255 | + * 'compressed_size'. 'compressed_size' must be > 0 and < | |
| 256 | + * cluster_size | |
| 257 | + * | |
| 258 | + * return 0 if not allocated. | |
| 259 | + */ | |
| 260 | +static uint64_t get_cluster_offset(BlockDriverState *bs, | |
| 261 | + uint64_t offset, int allocate, | |
| 262 | + int compressed_size, | |
| 263 | + int n_start, int n_end) | |
| 264 | +{ | |
| 265 | + BDRVQcowState *s = bs->opaque; | |
| 266 | + int min_index, i, j, l1_index, l2_index; | |
| 267 | + uint64_t l2_offset, *l2_table, cluster_offset, tmp; | |
| 268 | + uint32_t min_count; | |
| 269 | + int new_l2_table; | |
| 270 | + | |
| 271 | + l1_index = offset >> (s->l2_bits + s->cluster_bits); | |
| 272 | + l2_offset = s->l1_table[l1_index]; | |
| 273 | + new_l2_table = 0; | |
| 274 | + if (!l2_offset) { | |
| 275 | + if (!allocate) | |
| 276 | + return 0; | |
| 277 | + /* allocate a new l2 entry */ | |
| 278 | + l2_offset = lseek64(s->fd, 0, SEEK_END); | |
| 279 | + /* round to cluster size */ | |
| 280 | + l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); | |
| 281 | + /* update the L1 entry */ | |
| 282 | + s->l1_table[l1_index] = l2_offset; | |
| 283 | + tmp = cpu_to_be64(l2_offset); | |
| 284 | + lseek64(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET); | |
| 285 | + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) | |
| 286 | + return 0; | |
| 287 | + new_l2_table = 1; | |
| 288 | + } | |
| 289 | + for(i = 0; i < L2_CACHE_SIZE; i++) { | |
| 290 | + if (l2_offset == s->l2_cache_offsets[i]) { | |
| 291 | + /* increment the hit count */ | |
| 292 | + if (++s->l2_cache_counts[i] == 0xffffffff) { | |
| 293 | + for(j = 0; j < L2_CACHE_SIZE; j++) { | |
| 294 | + s->l2_cache_counts[j] >>= 1; | |
| 295 | + } | |
| 296 | + } | |
| 297 | + l2_table = s->l2_cache + (i << s->l2_bits); | |
| 298 | + goto found; | |
| 299 | + } | |
| 300 | + } | |
| 301 | + /* not found: load a new entry in the least used one */ | |
| 302 | + min_index = 0; | |
| 303 | + min_count = 0xffffffff; | |
| 304 | + for(i = 0; i < L2_CACHE_SIZE; i++) { | |
| 305 | + if (s->l2_cache_counts[i] < min_count) { | |
| 306 | + min_count = s->l2_cache_counts[i]; | |
| 307 | + min_index = i; | |
| 308 | + } | |
| 309 | + } | |
| 310 | + l2_table = s->l2_cache + (min_index << s->l2_bits); | |
| 311 | + lseek(s->fd, l2_offset, SEEK_SET); | |
| 312 | + if (new_l2_table) { | |
| 313 | + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | |
| 314 | + if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != | |
| 315 | + s->l2_size * sizeof(uint64_t)) | |
| 316 | + return 0; | |
| 317 | + } else { | |
| 318 | + if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != | |
| 319 | + s->l2_size * sizeof(uint64_t)) | |
| 320 | + return 0; | |
| 321 | + } | |
| 322 | + s->l2_cache_offsets[min_index] = l2_offset; | |
| 323 | + s->l2_cache_counts[min_index] = 1; | |
| 324 | + found: | |
| 325 | + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); | |
| 326 | + cluster_offset = be64_to_cpu(l2_table[l2_index]); | |
| 327 | + if (!cluster_offset || | |
| 328 | + ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) { | |
| 329 | + if (!allocate) | |
| 330 | + return 0; | |
| 331 | + /* allocate a new cluster */ | |
| 332 | + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && | |
| 333 | + (n_end - n_start) < s->cluster_sectors) { | |
| 334 | + /* if the cluster is already compressed, we must | |
| 335 | + decompress it in the case it is not completely | |
| 336 | + overwritten */ | |
| 337 | + if (decompress_cluster(s, cluster_offset) < 0) | |
| 338 | + return 0; | |
| 339 | + cluster_offset = lseek64(s->fd, 0, SEEK_END); | |
| 340 | + cluster_offset = (cluster_offset + s->cluster_size - 1) & | |
| 341 | + ~(s->cluster_size - 1); | |
| 342 | + /* write the cluster content */ | |
| 343 | + lseek64(s->fd, cluster_offset, SEEK_SET); | |
| 344 | + if (write(s->fd, s->cluster_cache, s->cluster_size) != | |
| 345 | + s->cluster_size) | |
| 346 | + return -1; | |
| 347 | + } else { | |
| 348 | + cluster_offset = lseek64(s->fd, 0, SEEK_END); | |
| 349 | + if (allocate == 1) { | |
| 350 | + /* round to cluster size */ | |
| 351 | + cluster_offset = (cluster_offset + s->cluster_size - 1) & | |
| 352 | + ~(s->cluster_size - 1); | |
| 353 | + ftruncate(s->fd, cluster_offset + s->cluster_size); | |
| 354 | + /* if encrypted, we must initialize the cluster | |
| 355 | + content which won't be written */ | |
| 356 | + if (s->crypt_method && | |
| 357 | + (n_end - n_start) < s->cluster_sectors) { | |
| 358 | + uint64_t start_sect; | |
| 359 | + start_sect = (offset & ~(s->cluster_size - 1)) >> 9; | |
| 360 | + memset(s->cluster_data + 512, 0xaa, 512); | |
| 361 | + for(i = 0; i < s->cluster_sectors; i++) { | |
| 362 | + if (i < n_start || i >= n_end) { | |
| 363 | + encrypt_sectors(s, start_sect + i, | |
| 364 | + s->cluster_data, | |
| 365 | + s->cluster_data + 512, 1, 1, | |
| 366 | + &s->aes_encrypt_key); | |
| 367 | + lseek64(s->fd, cluster_offset + i * 512, SEEK_SET); | |
| 368 | + if (write(s->fd, s->cluster_data, 512) != 512) | |
| 369 | + return -1; | |
| 370 | + } | |
| 371 | + } | |
| 372 | + } | |
| 373 | + } else { | |
| 374 | + cluster_offset |= QCOW_OFLAG_COMPRESSED | | |
| 375 | + (uint64_t)compressed_size << (63 - s->cluster_bits); | |
| 376 | + } | |
| 377 | + } | |
| 378 | + /* update L2 table */ | |
| 379 | + tmp = cpu_to_be64(cluster_offset); | |
| 380 | + l2_table[l2_index] = tmp; | |
| 381 | + lseek64(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET); | |
| 382 | + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) | |
| 383 | + return 0; | |
| 384 | + } | |
| 385 | + return cluster_offset; | |
| 386 | +} | |
| 387 | + | |
| 388 | +static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num, | |
| 389 | + int nb_sectors, int *pnum) | |
| 390 | +{ | |
| 391 | + BDRVQcowState *s = bs->opaque; | |
| 392 | + int index_in_cluster, n; | |
| 393 | + uint64_t cluster_offset; | |
| 394 | + | |
| 395 | + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); | |
| 396 | + index_in_cluster = sector_num & (s->cluster_sectors - 1); | |
| 397 | + n = s->cluster_sectors - index_in_cluster; | |
| 398 | + if (n > nb_sectors) | |
| 399 | + n = nb_sectors; | |
| 400 | + *pnum = n; | |
| 401 | + return (cluster_offset != 0); | |
| 402 | +} | |
| 403 | + | |
| 404 | +static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | |
| 405 | + const uint8_t *buf, int buf_size) | |
| 406 | +{ | |
| 407 | + z_stream strm1, *strm = &strm1; | |
| 408 | + int ret, out_len; | |
| 409 | + | |
| 410 | + memset(strm, 0, sizeof(*strm)); | |
| 411 | + | |
| 412 | + strm->next_in = (uint8_t *)buf; | |
| 413 | + strm->avail_in = buf_size; | |
| 414 | + strm->next_out = out_buf; | |
| 415 | + strm->avail_out = out_buf_size; | |
| 416 | + | |
| 417 | + ret = inflateInit2(strm, -12); | |
| 418 | + if (ret != Z_OK) | |
| 419 | + return -1; | |
| 420 | + ret = inflate(strm, Z_FINISH); | |
| 421 | + out_len = strm->next_out - out_buf; | |
| 422 | + if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || | |
| 423 | + out_len != out_buf_size) { | |
| 424 | + inflateEnd(strm); | |
| 425 | + return -1; | |
| 426 | + } | |
| 427 | + inflateEnd(strm); | |
| 428 | + return 0; | |
| 429 | +} | |
| 430 | + | |
| 431 | +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) | |
| 432 | +{ | |
| 433 | + int ret, csize; | |
| 434 | + uint64_t coffset; | |
| 435 | + | |
| 436 | + coffset = cluster_offset & s->cluster_offset_mask; | |
| 437 | + if (s->cluster_cache_offset != coffset) { | |
| 438 | + csize = cluster_offset >> (63 - s->cluster_bits); | |
| 439 | + csize &= (s->cluster_size - 1); | |
| 440 | + lseek64(s->fd, coffset, SEEK_SET); | |
| 441 | + ret = read(s->fd, s->cluster_data, csize); | |
| 442 | + if (ret != csize) | |
| 443 | + return -1; | |
| 444 | + if (decompress_buffer(s->cluster_cache, s->cluster_size, | |
| 445 | + s->cluster_data, csize) < 0) { | |
| 446 | + return -1; | |
| 447 | + } | |
| 448 | + s->cluster_cache_offset = coffset; | |
| 449 | + } | |
| 450 | + return 0; | |
| 451 | +} | |
| 452 | + | |
| 453 | +static int qcow_read(BlockDriverState *bs, int64_t sector_num, | |
| 454 | + uint8_t *buf, int nb_sectors) | |
| 455 | +{ | |
| 456 | + BDRVQcowState *s = bs->opaque; | |
| 457 | + int ret, index_in_cluster, n; | |
| 458 | + uint64_t cluster_offset; | |
| 459 | + | |
| 460 | + while (nb_sectors > 0) { | |
| 461 | + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); | |
| 462 | + index_in_cluster = sector_num & (s->cluster_sectors - 1); | |
| 463 | + n = s->cluster_sectors - index_in_cluster; | |
| 464 | + if (n > nb_sectors) | |
| 465 | + n = nb_sectors; | |
| 466 | + if (!cluster_offset) { | |
| 467 | + memset(buf, 0, 512 * n); | |
| 468 | + } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | |
| 469 | + if (decompress_cluster(s, cluster_offset) < 0) | |
| 470 | + return -1; | |
| 471 | + memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); | |
| 472 | + } else { | |
| 473 | + lseek64(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); | |
| 474 | + ret = read(s->fd, buf, n * 512); | |
| 475 | + if (ret != n * 512) | |
| 476 | + return -1; | |
| 477 | + if (s->crypt_method) { | |
| 478 | + encrypt_sectors(s, sector_num, buf, buf, n, 0, | |
| 479 | + &s->aes_decrypt_key); | |
| 480 | + } | |
| 481 | + } | |
| 482 | + nb_sectors -= n; | |
| 483 | + sector_num += n; | |
| 484 | + buf += n * 512; | |
| 485 | + } | |
| 486 | + return 0; | |
| 487 | +} | |
| 488 | + | |
| 489 | +static int qcow_write(BlockDriverState *bs, int64_t sector_num, | |
| 490 | + const uint8_t *buf, int nb_sectors) | |
| 491 | +{ | |
| 492 | + BDRVQcowState *s = bs->opaque; | |
| 493 | + int ret, index_in_cluster, n; | |
| 494 | + uint64_t cluster_offset; | |
| 495 | + | |
| 496 | + while (nb_sectors > 0) { | |
| 497 | + index_in_cluster = sector_num & (s->cluster_sectors - 1); | |
| 498 | + n = s->cluster_sectors - index_in_cluster; | |
| 499 | + if (n > nb_sectors) | |
| 500 | + n = nb_sectors; | |
| 501 | + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, | |
| 502 | + index_in_cluster, | |
| 503 | + index_in_cluster + n); | |
| 504 | + if (!cluster_offset) | |
| 505 | + return -1; | |
| 506 | + lseek64(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); | |
| 507 | + if (s->crypt_method) { | |
| 508 | + encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, | |
| 509 | + &s->aes_encrypt_key); | |
| 510 | + ret = write(s->fd, s->cluster_data, n * 512); | |
| 511 | + } else { | |
| 512 | + ret = write(s->fd, buf, n * 512); | |
| 513 | + } | |
| 514 | + if (ret != n * 512) | |
| 515 | + return -1; | |
| 516 | + nb_sectors -= n; | |
| 517 | + sector_num += n; | |
| 518 | + buf += n * 512; | |
| 519 | + } | |
| 520 | + s->cluster_cache_offset = -1; /* disable compressed cache */ | |
| 521 | + return 0; | |
| 522 | +} | |
| 523 | + | |
| 524 | +static int qcow_close(BlockDriverState *bs) | |
| 525 | +{ | |
| 526 | + BDRVQcowState *s = bs->opaque; | |
| 527 | + qemu_free(s->l1_table); | |
| 528 | + qemu_free(s->l2_cache); | |
| 529 | + qemu_free(s->cluster_cache); | |
| 530 | + qemu_free(s->cluster_data); | |
| 531 | + close(s->fd); | |
| 532 | +} | |
| 533 | + | |
| 534 | +static int qcow_create(const char *filename, int64_t total_size, | |
| 535 | + const char *backing_file, int flags) | |
| 536 | +{ | |
| 537 | + int fd, header_size, backing_filename_len, l1_size, i, shift; | |
| 538 | + QCowHeader header; | |
| 539 | + char backing_filename[1024]; | |
| 540 | + uint64_t tmp; | |
| 541 | + struct stat st; | |
| 542 | + | |
| 543 | + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | |
| 544 | + 0644); | |
| 545 | + if (fd < 0) | |
| 546 | + return -1; | |
| 547 | + memset(&header, 0, sizeof(header)); | |
| 548 | + header.magic = cpu_to_be32(QCOW_MAGIC); | |
| 549 | + header.version = cpu_to_be32(QCOW_VERSION); | |
| 550 | + header.size = cpu_to_be64(total_size * 512); | |
| 551 | + header_size = sizeof(header); | |
| 552 | + backing_filename_len = 0; | |
| 553 | + if (backing_file) { | |
| 554 | + realpath(backing_file, backing_filename); | |
| 555 | + if (stat(backing_filename, &st) != 0) { | |
| 556 | + return -1; | |
| 557 | + } | |
| 558 | + header.mtime = cpu_to_be32(st.st_mtime); | |
| 559 | + header.backing_file_offset = cpu_to_be64(header_size); | |
| 560 | + backing_filename_len = strlen(backing_filename); | |
| 561 | + header.backing_file_size = cpu_to_be32(backing_filename_len); | |
| 562 | + header_size += backing_filename_len; | |
| 563 | + header.cluster_bits = 9; /* 512 byte cluster to avoid copying | |
| 564 | + unmodifyed sectors */ | |
| 565 | + header.l2_bits = 12; /* 32 KB L2 tables */ | |
| 566 | + } else { | |
| 567 | + header.cluster_bits = 12; /* 4 KB clusters */ | |
| 568 | + header.l2_bits = 9; /* 4 KB L2 tables */ | |
| 569 | + } | |
| 570 | + header_size = (header_size + 7) & ~7; | |
| 571 | + shift = header.cluster_bits + header.l2_bits; | |
| 572 | + l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift; | |
| 573 | + | |
| 574 | + header.l1_table_offset = cpu_to_be64(header_size); | |
| 575 | + if (flags) { | |
| 576 | + header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); | |
| 577 | + } else { | |
| 578 | + header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); | |
| 579 | + } | |
| 580 | + | |
| 581 | + /* write all the data */ | |
| 582 | + write(fd, &header, sizeof(header)); | |
| 583 | + if (backing_file) { | |
| 584 | + write(fd, backing_filename, backing_filename_len); | |
| 585 | + } | |
| 586 | + lseek(fd, header_size, SEEK_SET); | |
| 587 | + tmp = 0; | |
| 588 | + for(i = 0;i < l1_size; i++) { | |
| 589 | + write(fd, &tmp, sizeof(tmp)); | |
| 590 | + } | |
| 591 | + close(fd); | |
| 592 | + return 0; | |
| 593 | +} | |
| 594 | + | |
| 595 | +int qcow_get_cluster_size(BlockDriverState *bs) | |
| 596 | +{ | |
| 597 | + BDRVQcowState *s = bs->opaque; | |
| 598 | + if (bs->drv != &bdrv_qcow) | |
| 599 | + return -1; | |
| 600 | + return s->cluster_size; | |
| 601 | +} | |
| 602 | + | |
| 603 | +/* XXX: put compressed sectors first, then all the cluster aligned | |
| 604 | + tables to avoid losing bytes in alignment */ | |
| 605 | +int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, | |
| 606 | + const uint8_t *buf) | |
| 607 | +{ | |
| 608 | + BDRVQcowState *s = bs->opaque; | |
| 609 | + z_stream strm; | |
| 610 | + int ret, out_len; | |
| 611 | + uint8_t *out_buf; | |
| 612 | + uint64_t cluster_offset; | |
| 613 | + | |
| 614 | + if (bs->drv != &bdrv_qcow) | |
| 615 | + return -1; | |
| 616 | + | |
| 617 | + out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); | |
| 618 | + if (!out_buf) | |
| 619 | + return -1; | |
| 620 | + | |
| 621 | + /* best compression, small window, no zlib header */ | |
| 622 | + memset(&strm, 0, sizeof(strm)); | |
| 623 | + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, | |
| 624 | + Z_DEFLATED, -12, | |
| 625 | + 9, Z_DEFAULT_STRATEGY); | |
| 626 | + if (ret != 0) { | |
| 627 | + qemu_free(out_buf); | |
| 628 | + return -1; | |
| 629 | + } | |
| 630 | + | |
| 631 | + strm.avail_in = s->cluster_size; | |
| 632 | + strm.next_in = (uint8_t *)buf; | |
| 633 | + strm.avail_out = s->cluster_size; | |
| 634 | + strm.next_out = out_buf; | |
| 635 | + | |
| 636 | + ret = deflate(&strm, Z_FINISH); | |
| 637 | + if (ret != Z_STREAM_END && ret != Z_OK) { | |
| 638 | + qemu_free(out_buf); | |
| 639 | + deflateEnd(&strm); | |
| 640 | + return -1; | |
| 641 | + } | |
| 642 | + out_len = strm.next_out - out_buf; | |
| 643 | + | |
| 644 | + deflateEnd(&strm); | |
| 645 | + | |
| 646 | + if (ret != Z_STREAM_END || out_len >= s->cluster_size) { | |
| 647 | + /* could not compress: write normal cluster */ | |
| 648 | + qcow_write(bs, sector_num, buf, s->cluster_sectors); | |
| 649 | + } else { | |
| 650 | + cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, | |
| 651 | + out_len, 0, 0); | |
| 652 | + cluster_offset &= s->cluster_offset_mask; | |
| 653 | + lseek64(s->fd, cluster_offset, SEEK_SET); | |
| 654 | + if (write(s->fd, out_buf, out_len) != out_len) { | |
| 655 | + qemu_free(out_buf); | |
| 656 | + return -1; | |
| 657 | + } | |
| 658 | + } | |
| 659 | + | |
| 660 | + qemu_free(out_buf); | |
| 661 | + return 0; | |
| 662 | +} | |
| 663 | + | |
| 664 | +BlockDriver bdrv_qcow = { | |
| 665 | + "qcow", | |
| 666 | + sizeof(BDRVQcowState), | |
| 667 | + qcow_probe, | |
| 668 | + qcow_open, | |
| 669 | + qcow_read, | |
| 670 | + qcow_write, | |
| 671 | + qcow_close, | |
| 672 | + qcow_create, | |
| 673 | + qcow_is_allocated, | |
| 674 | + qcow_set_key, | |
| 675 | +}; | |
| 676 | + | |
| 677 | + | ... | ... |
block-vmdk.c
0 → 100644
| 1 | +/* | |
| 2 | + * Block driver for the VMDK format | |
| 3 | + * | |
| 4 | + * Copyright (c) 2004 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#include "vl.h" | |
| 25 | +#include "block_int.h" | |
| 26 | + | |
| 27 | +/* XXX: this code is untested */ | |
| 28 | +/* XXX: add write support */ | |
| 29 | + | |
| 30 | +#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') | |
| 31 | +#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') | |
| 32 | + | |
| 33 | +typedef struct { | |
| 34 | + uint32_t version; | |
| 35 | + uint32_t flags; | |
| 36 | + uint32_t disk_sectors; | |
| 37 | + uint32_t granularity; | |
| 38 | + uint32_t l1dir_offset; | |
| 39 | + uint32_t l1dir_size; | |
| 40 | + uint32_t file_sectors; | |
| 41 | + uint32_t cylinders; | |
| 42 | + uint32_t heads; | |
| 43 | + uint32_t sectors_per_track; | |
| 44 | +} VMDK3Header; | |
| 45 | + | |
| 46 | +typedef struct { | |
| 47 | + uint32_t version; | |
| 48 | + uint32_t flags; | |
| 49 | + int64_t capacity; | |
| 50 | + int64_t granularity; | |
| 51 | + int64_t desc_offset; | |
| 52 | + int64_t desc_size; | |
| 53 | + int32_t num_gtes_per_gte; | |
| 54 | + int64_t rgd_offset; | |
| 55 | + int64_t gd_offset; | |
| 56 | + int64_t grain_offset; | |
| 57 | + char filler[1]; | |
| 58 | + char check_bytes[4]; | |
| 59 | +} VMDK4Header; | |
| 60 | + | |
| 61 | +#define L2_CACHE_SIZE 16 | |
| 62 | + | |
| 63 | +typedef struct BDRVVmdkState { | |
| 64 | + int fd; | |
| 65 | + int64_t l1_table_offset; | |
| 66 | + uint32_t *l1_table; | |
| 67 | + unsigned int l1_size; | |
| 68 | + uint32_t l1_entry_sectors; | |
| 69 | + | |
| 70 | + unsigned int l2_size; | |
| 71 | + uint32_t *l2_cache; | |
| 72 | + uint32_t l2_cache_offsets[L2_CACHE_SIZE]; | |
| 73 | + uint32_t l2_cache_counts[L2_CACHE_SIZE]; | |
| 74 | + | |
| 75 | + unsigned int cluster_sectors; | |
| 76 | +} BDRVVmdkState; | |
| 77 | + | |
| 78 | +static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) | |
| 79 | +{ | |
| 80 | + uint32_t magic; | |
| 81 | + | |
| 82 | + if (buf_size < 4) | |
| 83 | + return 0; | |
| 84 | + magic = be32_to_cpu(*(uint32_t *)buf); | |
| 85 | + if (magic == VMDK3_MAGIC || | |
| 86 | + magic == VMDK4_MAGIC) | |
| 87 | + return 100; | |
| 88 | + else | |
| 89 | + return 0; | |
| 90 | +} | |
| 91 | + | |
| 92 | +static int vmdk_open(BlockDriverState *bs, const char *filename) | |
| 93 | +{ | |
| 94 | + BDRVVmdkState *s = bs->opaque; | |
| 95 | + int fd, i; | |
| 96 | + uint32_t magic; | |
| 97 | + int l1_size; | |
| 98 | + | |
| 99 | + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | |
| 100 | + if (fd < 0) | |
| 101 | + return -1; | |
| 102 | + if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) | |
| 103 | + goto fail; | |
| 104 | + magic = le32_to_cpu(magic); | |
| 105 | + | |
| 106 | + if (magic == VMDK3_MAGIC) { | |
| 107 | + VMDK3Header header; | |
| 108 | + if (read(fd, &header, sizeof(header)) != | |
| 109 | + sizeof(header)) | |
| 110 | + goto fail; | |
| 111 | + s->cluster_sectors = le32_to_cpu(header.granularity); | |
| 112 | + s->l2_size = 1 << 9; | |
| 113 | + s->l1_size = 1 << 6; | |
| 114 | + bs->total_sectors = le32_to_cpu(header.disk_sectors); | |
| 115 | + s->l1_table_offset = le32_to_cpu(header.l1dir_offset) * 512; | |
| 116 | + s->l1_entry_sectors = s->l2_size * s->cluster_sectors; | |
| 117 | + } else if (magic == VMDK4_MAGIC) { | |
| 118 | + VMDK4Header header; | |
| 119 | + | |
| 120 | + if (read(fd, &header, sizeof(header)) != sizeof(header)) | |
| 121 | + goto fail; | |
| 122 | + bs->total_sectors = le32_to_cpu(header.capacity); | |
| 123 | + s->cluster_sectors = le32_to_cpu(header.granularity); | |
| 124 | + s->l2_size = le32_to_cpu(header.num_gtes_per_gte); | |
| 125 | + s->l1_entry_sectors = s->l2_size * s->cluster_sectors; | |
| 126 | + if (s->l1_entry_sectors <= 0) | |
| 127 | + goto fail; | |
| 128 | + s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) | |
| 129 | + / s->l1_entry_sectors; | |
| 130 | + s->l1_table_offset = le64_to_cpu(header.rgd_offset) * 512; | |
| 131 | + } else { | |
| 132 | + goto fail; | |
| 133 | + } | |
| 134 | + /* read the L1 table */ | |
| 135 | + l1_size = s->l1_size * sizeof(uint32_t); | |
| 136 | + s->l1_table = qemu_malloc(l1_size); | |
| 137 | + if (!s->l1_table) | |
| 138 | + goto fail; | |
| 139 | + if (read(s->fd, s->l1_table, l1_size) != l1_size) | |
| 140 | + goto fail; | |
| 141 | + for(i = 0; i < s->l1_size; i++) { | |
| 142 | + le32_to_cpus(&s->l1_table[i]); | |
| 143 | + } | |
| 144 | + | |
| 145 | + s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); | |
| 146 | + if (!s->l2_cache) | |
| 147 | + goto fail; | |
| 148 | + s->fd = fd; | |
| 149 | + /* XXX: currently only read only */ | |
| 150 | + bs->read_only = 1; | |
| 151 | + return 0; | |
| 152 | + fail: | |
| 153 | + qemu_free(s->l1_table); | |
| 154 | + qemu_free(s->l2_cache); | |
| 155 | + close(fd); | |
| 156 | + return -1; | |
| 157 | +} | |
| 158 | + | |
| 159 | +static uint64_t get_cluster_offset(BlockDriverState *bs, | |
| 160 | + uint64_t offset) | |
| 161 | +{ | |
| 162 | + BDRVVmdkState *s = bs->opaque; | |
| 163 | + unsigned int l1_index, l2_offset, l2_index; | |
| 164 | + int min_index, i, j; | |
| 165 | + uint32_t min_count, *l2_table; | |
| 166 | + uint64_t cluster_offset; | |
| 167 | + | |
| 168 | + l1_index = (offset >> 9) / s->l1_entry_sectors; | |
| 169 | + if (l1_index >= s->l1_size) | |
| 170 | + return 0; | |
| 171 | + l2_offset = s->l1_table[l1_index]; | |
| 172 | + if (!l2_offset) | |
| 173 | + return 0; | |
| 174 | + | |
| 175 | + for(i = 0; i < L2_CACHE_SIZE; i++) { | |
| 176 | + if (l2_offset == s->l2_cache_offsets[i]) { | |
| 177 | + /* increment the hit count */ | |
| 178 | + if (++s->l2_cache_counts[i] == 0xffffffff) { | |
| 179 | + for(j = 0; j < L2_CACHE_SIZE; j++) { | |
| 180 | + s->l2_cache_counts[j] >>= 1; | |
| 181 | + } | |
| 182 | + } | |
| 183 | + l2_table = s->l2_cache + (i * s->l2_size); | |
| 184 | + goto found; | |
| 185 | + } | |
| 186 | + } | |
| 187 | + /* not found: load a new entry in the least used one */ | |
| 188 | + min_index = 0; | |
| 189 | + min_count = 0xffffffff; | |
| 190 | + for(i = 0; i < L2_CACHE_SIZE; i++) { | |
| 191 | + if (s->l2_cache_counts[i] < min_count) { | |
| 192 | + min_count = s->l2_cache_counts[i]; | |
| 193 | + min_index = i; | |
| 194 | + } | |
| 195 | + } | |
| 196 | + l2_table = s->l2_cache + (min_index * s->l2_size); | |
| 197 | + lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET); | |
| 198 | + if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != | |
| 199 | + s->l2_size * sizeof(uint32_t)) | |
| 200 | + return 0; | |
| 201 | + s->l2_cache_offsets[min_index] = l2_offset; | |
| 202 | + s->l2_cache_counts[min_index] = 1; | |
| 203 | + found: | |
| 204 | + l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; | |
| 205 | + cluster_offset = le32_to_cpu(l2_table[l2_index]); | |
| 206 | + cluster_offset <<= 9; | |
| 207 | + return cluster_offset; | |
| 208 | +} | |
| 209 | + | |
| 210 | +static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, | |
| 211 | + int nb_sectors, int *pnum) | |
| 212 | +{ | |
| 213 | + BDRVVmdkState *s = bs->opaque; | |
| 214 | + int index_in_cluster, n; | |
| 215 | + uint64_t cluster_offset; | |
| 216 | + | |
| 217 | + cluster_offset = get_cluster_offset(bs, sector_num << 9); | |
| 218 | + index_in_cluster = sector_num % s->cluster_sectors; | |
| 219 | + n = s->cluster_sectors - index_in_cluster; | |
| 220 | + if (n > nb_sectors) | |
| 221 | + n = nb_sectors; | |
| 222 | + *pnum = n; | |
| 223 | + return (cluster_offset != 0); | |
| 224 | +} | |
| 225 | + | |
| 226 | +static int vmdk_read(BlockDriverState *bs, int64_t sector_num, | |
| 227 | + uint8_t *buf, int nb_sectors) | |
| 228 | +{ | |
| 229 | + BDRVVmdkState *s = bs->opaque; | |
| 230 | + int ret, index_in_cluster, n; | |
| 231 | + uint64_t cluster_offset; | |
| 232 | + | |
| 233 | + while (nb_sectors > 0) { | |
| 234 | + cluster_offset = get_cluster_offset(bs, sector_num << 9); | |
| 235 | + index_in_cluster = sector_num % s->cluster_sectors; | |
| 236 | + n = s->cluster_sectors - index_in_cluster; | |
| 237 | + if (n > nb_sectors) | |
| 238 | + n = nb_sectors; | |
| 239 | + if (!cluster_offset) { | |
| 240 | + memset(buf, 0, 512 * n); | |
| 241 | + } else { | |
| 242 | + lseek64(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); | |
| 243 | + ret = read(s->fd, buf, n * 512); | |
| 244 | + if (ret != n * 512) | |
| 245 | + return -1; | |
| 246 | + } | |
| 247 | + nb_sectors -= n; | |
| 248 | + sector_num += n; | |
| 249 | + buf += n * 512; | |
| 250 | + } | |
| 251 | + return 0; | |
| 252 | +} | |
| 253 | + | |
| 254 | +static int vmdk_write(BlockDriverState *bs, int64_t sector_num, | |
| 255 | + const uint8_t *buf, int nb_sectors) | |
| 256 | +{ | |
| 257 | + return -1; | |
| 258 | +} | |
| 259 | + | |
| 260 | +static int vmdk_close(BlockDriverState *bs) | |
| 261 | +{ | |
| 262 | + BDRVVmdkState *s = bs->opaque; | |
| 263 | + qemu_free(s->l1_table); | |
| 264 | + qemu_free(s->l2_cache); | |
| 265 | + close(s->fd); | |
| 266 | +} | |
| 267 | + | |
| 268 | +BlockDriver bdrv_vmdk = { | |
| 269 | + "vmdk", | |
| 270 | + sizeof(BDRVVmdkState), | |
| 271 | + vmdk_probe, | |
| 272 | + vmdk_open, | |
| 273 | + vmdk_read, | |
| 274 | + vmdk_write, | |
| 275 | + vmdk_close, | |
| 276 | + NULL, /* no create yet */ | |
| 277 | + vmdk_is_allocated, | |
| 278 | +}; | ... | ... |
block.c
| ... | ... | @@ -22,43 +22,16 @@ |
| 22 | 22 | * THE SOFTWARE. |
| 23 | 23 | */ |
| 24 | 24 | #include "vl.h" |
| 25 | - | |
| 26 | -#ifndef _WIN32 | |
| 27 | -#include <sys/mman.h> | |
| 28 | -#endif | |
| 29 | - | |
| 30 | -#include "cow.h" | |
| 31 | - | |
| 32 | -struct BlockDriverState { | |
| 33 | - int fd; /* if -1, only COW mappings */ | |
| 34 | - int64_t total_sectors; | |
| 35 | - int read_only; /* if true, the media is read only */ | |
| 36 | - int inserted; /* if true, the media is present */ | |
| 37 | - int removable; /* if true, the media can be removed */ | |
| 38 | - int locked; /* if true, the media cannot temporarily be ejected */ | |
| 39 | - /* event callback when inserting/removing */ | |
| 40 | - void (*change_cb)(void *opaque); | |
| 41 | - void *change_opaque; | |
| 42 | - | |
| 43 | - uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ | |
| 44 | - uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ | |
| 45 | - int cow_bitmap_size; | |
| 46 | - int cow_fd; | |
| 47 | - int64_t cow_sectors_offset; | |
| 48 | - int boot_sector_enabled; | |
| 49 | - uint8_t boot_sector_data[512]; | |
| 50 | - | |
| 51 | - char filename[1024]; | |
| 52 | - | |
| 53 | - /* NOTE: the following infos are only hints for real hardware | |
| 54 | - drivers. They are not used by the block driver */ | |
| 55 | - int cyls, heads, secs; | |
| 56 | - int type; | |
| 57 | - char device_name[32]; | |
| 58 | - BlockDriverState *next; | |
| 59 | -}; | |
| 25 | +#include "block_int.h" | |
| 60 | 26 | |
| 61 | 27 | static BlockDriverState *bdrv_first; |
| 28 | +static BlockDriver *first_drv; | |
| 29 | + | |
| 30 | +void bdrv_register(BlockDriver *bdrv) | |
| 31 | +{ | |
| 32 | + bdrv->next = first_drv; | |
| 33 | + first_drv = bdrv; | |
| 34 | +} | |
| 62 | 35 | |
| 63 | 36 | /* create a new block device (by default it is empty) */ |
| 64 | 37 | BlockDriverState *bdrv_new(const char *device_name) |
| ... | ... | @@ -69,126 +42,149 @@ BlockDriverState *bdrv_new(const char *device_name) |
| 69 | 42 | if(!bs) |
| 70 | 43 | return NULL; |
| 71 | 44 | pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); |
| 72 | - /* insert at the end */ | |
| 73 | - pbs = &bdrv_first; | |
| 74 | - while (*pbs != NULL) | |
| 75 | - pbs = &(*pbs)->next; | |
| 76 | - *pbs = bs; | |
| 45 | + if (device_name[0] != '\0') { | |
| 46 | + /* insert at the end */ | |
| 47 | + pbs = &bdrv_first; | |
| 48 | + while (*pbs != NULL) | |
| 49 | + pbs = &(*pbs)->next; | |
| 50 | + *pbs = bs; | |
| 51 | + } | |
| 77 | 52 | return bs; |
| 78 | 53 | } |
| 79 | 54 | |
| 80 | -int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) | |
| 55 | +BlockDriver *bdrv_find_format(const char *format_name) | |
| 56 | +{ | |
| 57 | + BlockDriver *drv1; | |
| 58 | + for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { | |
| 59 | + if (!strcmp(drv1->format_name, format_name)) | |
| 60 | + return drv1; | |
| 61 | + } | |
| 62 | + return NULL; | |
| 63 | +} | |
| 64 | + | |
| 65 | +int bdrv_create(BlockDriver *drv, | |
| 66 | + const char *filename, int64_t size_in_sectors, | |
| 67 | + const char *backing_file, int flags) | |
| 68 | +{ | |
| 69 | + if (!drv->bdrv_create) | |
| 70 | + return -ENOTSUP; | |
| 71 | + return drv->bdrv_create(filename, size_in_sectors, backing_file, flags); | |
| 72 | +} | |
| 73 | + | |
| 74 | +/* XXX: race condition possible */ | |
| 75 | +static void get_tmp_filename(char *filename, int size) | |
| 81 | 76 | { |
| 82 | 77 | int fd; |
| 83 | - int64_t size; | |
| 84 | - struct cow_header_v2 cow_header; | |
| 85 | -#ifndef _WIN32 | |
| 86 | - char template[] = "/tmp/vl.XXXXXX"; | |
| 87 | - int cow_fd; | |
| 88 | - struct stat st; | |
| 89 | -#endif | |
| 78 | + pstrcpy(filename, size, "/tmp/vl.XXXXXX"); | |
| 79 | + fd = mkstemp(filename); | |
| 80 | + close(fd); | |
| 81 | +} | |
| 90 | 82 | |
| 91 | - bs->read_only = 0; | |
| 92 | - bs->fd = -1; | |
| 93 | - bs->cow_fd = -1; | |
| 94 | - bs->cow_bitmap = NULL; | |
| 95 | - pstrcpy(bs->filename, sizeof(bs->filename), filename); | |
| 83 | +static BlockDriver *find_image_format(const char *filename) | |
| 84 | +{ | |
| 85 | + int fd, ret, score, score_max; | |
| 86 | + BlockDriver *drv1, *drv; | |
| 87 | + uint8_t buf[1024]; | |
| 96 | 88 | |
| 97 | - /* open standard HD image */ | |
| 98 | -#ifdef _WIN32 | |
| 99 | - fd = open(filename, O_RDWR | O_BINARY); | |
| 100 | -#else | |
| 101 | - fd = open(filename, O_RDWR | O_LARGEFILE); | |
| 102 | -#endif | |
| 103 | - if (fd < 0) { | |
| 104 | - /* read only image on disk */ | |
| 105 | -#ifdef _WIN32 | |
| 106 | - fd = open(filename, O_RDONLY | O_BINARY); | |
| 107 | -#else | |
| 108 | - fd = open(filename, O_RDONLY | O_LARGEFILE); | |
| 109 | -#endif | |
| 110 | - if (fd < 0) { | |
| 111 | - perror(filename); | |
| 112 | - goto fail; | |
| 89 | + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | |
| 90 | + if (fd < 0) | |
| 91 | + return NULL; | |
| 92 | + ret = read(fd, buf, sizeof(buf)); | |
| 93 | + if (ret < 0) { | |
| 94 | + close(fd); | |
| 95 | + return NULL; | |
| 96 | + } | |
| 97 | + close(fd); | |
| 98 | + | |
| 99 | + drv = NULL; | |
| 100 | + score_max = 0; | |
| 101 | + for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { | |
| 102 | + score = drv1->bdrv_probe(buf, ret, filename); | |
| 103 | + if (score > score_max) { | |
| 104 | + score_max = score; | |
| 105 | + drv = drv1; | |
| 113 | 106 | } |
| 114 | - if (!snapshot) | |
| 115 | - bs->read_only = 1; | |
| 116 | 107 | } |
| 117 | - bs->fd = fd; | |
| 108 | + return drv; | |
| 109 | +} | |
| 110 | + | |
| 111 | +int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) | |
| 112 | +{ | |
| 113 | + return bdrv_open2(bs, filename, snapshot, NULL); | |
| 114 | +} | |
| 115 | + | |
| 116 | +int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, | |
| 117 | + BlockDriver *drv) | |
| 118 | +{ | |
| 119 | + int ret; | |
| 120 | + char tmp_filename[1024]; | |
| 121 | + | |
| 122 | + bs->read_only = 0; | |
| 123 | + bs->is_temporary = 0; | |
| 124 | + bs->encrypted = 0; | |
| 125 | + | |
| 126 | + if (snapshot) { | |
| 127 | + BlockDriverState *bs1; | |
| 128 | + int64_t total_size; | |
| 129 | + | |
| 130 | + /* if snapshot, we create a temporary backing file and open it | |
| 131 | + instead of opening 'filename' directly */ | |
| 118 | 132 | |
| 119 | - /* see if it is a cow image */ | |
| 120 | - if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { | |
| 121 | - fprintf(stderr, "%s: could not read header\n", filename); | |
| 122 | - goto fail; | |
| 133 | + /* if there is a backing file, use it */ | |
| 134 | + bs1 = bdrv_new(""); | |
| 135 | + if (!bs1) { | |
| 136 | + return -1; | |
| 137 | + } | |
| 138 | + if (bdrv_open(bs1, filename, 0) < 0) { | |
| 139 | + bdrv_delete(bs1); | |
| 140 | + return -1; | |
| 141 | + } | |
| 142 | + total_size = bs1->total_sectors; | |
| 143 | + bdrv_delete(bs1); | |
| 144 | + | |
| 145 | + get_tmp_filename(tmp_filename, sizeof(tmp_filename)); | |
| 146 | + /* XXX: use cow for linux as it is more efficient ? */ | |
| 147 | + if (bdrv_create(&bdrv_qcow, tmp_filename, | |
| 148 | + total_size, filename, 0) < 0) { | |
| 149 | + return -1; | |
| 150 | + } | |
| 151 | + filename = tmp_filename; | |
| 152 | + bs->is_temporary = 1; | |
| 153 | + } | |
| 154 | + | |
| 155 | + pstrcpy(bs->filename, sizeof(bs->filename), filename); | |
| 156 | + if (!drv) { | |
| 157 | + drv = find_image_format(filename); | |
| 158 | + if (!drv) | |
| 159 | + return -1; | |
| 160 | + } | |
| 161 | + bs->drv = drv; | |
| 162 | + bs->opaque = qemu_mallocz(drv->instance_size); | |
| 163 | + if (bs->opaque == NULL && drv->instance_size > 0) | |
| 164 | + return -1; | |
| 165 | + | |
| 166 | + ret = drv->bdrv_open(bs, filename); | |
| 167 | + if (ret < 0) { | |
| 168 | + qemu_free(bs->opaque); | |
| 169 | + return -1; | |
| 123 | 170 | } |
| 124 | 171 | #ifndef _WIN32 |
| 125 | - if (be32_to_cpu(cow_header.magic) == COW_MAGIC && | |
| 126 | - be32_to_cpu(cow_header.version) == COW_VERSION) { | |
| 127 | - /* cow image found */ | |
| 128 | - size = cow_header.size; | |
| 129 | -#ifndef WORDS_BIGENDIAN | |
| 130 | - size = bswap64(size); | |
| 131 | -#endif | |
| 132 | - bs->total_sectors = size / 512; | |
| 133 | - | |
| 134 | - bs->cow_fd = fd; | |
| 135 | - bs->fd = -1; | |
| 136 | - if (cow_header.backing_file[0] != '\0') { | |
| 137 | - if (stat(cow_header.backing_file, &st) != 0) { | |
| 138 | - fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file); | |
| 139 | - goto fail; | |
| 140 | - } | |
| 141 | - if (st.st_mtime != be32_to_cpu(cow_header.mtime)) { | |
| 142 | - fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file); | |
| 143 | - goto fail; | |
| 144 | - } | |
| 145 | - fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE); | |
| 146 | - if (fd < 0) | |
| 147 | - goto fail; | |
| 148 | - bs->fd = fd; | |
| 172 | + if (bs->is_temporary) { | |
| 173 | + unlink(filename); | |
| 174 | + } | |
| 175 | +#endif | |
| 176 | + if (bs->backing_file[0] != '\0' && drv->bdrv_is_allocated) { | |
| 177 | + /* if there is a backing file, use it */ | |
| 178 | + bs->backing_hd = bdrv_new(""); | |
| 179 | + if (!bs->backing_hd) { | |
| 180 | + fail: | |
| 181 | + bdrv_close(bs); | |
| 182 | + return -1; | |
| 149 | 183 | } |
| 150 | - /* mmap the bitmap */ | |
| 151 | - bs->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); | |
| 152 | - bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size), | |
| 153 | - bs->cow_bitmap_size, | |
| 154 | - PROT_READ | PROT_WRITE, | |
| 155 | - MAP_SHARED, bs->cow_fd, 0); | |
| 156 | - if (bs->cow_bitmap_addr == MAP_FAILED) | |
| 184 | + if (bdrv_open(bs->backing_hd, bs->backing_file, 0) < 0) | |
| 157 | 185 | goto fail; |
| 158 | - bs->cow_bitmap = bs->cow_bitmap_addr + sizeof(cow_header); | |
| 159 | - bs->cow_sectors_offset = (bs->cow_bitmap_size + 511) & ~511; | |
| 160 | - snapshot = 0; | |
| 161 | - } else | |
| 162 | -#endif | |
| 163 | - { | |
| 164 | - /* standard raw image */ | |
| 165 | - size = lseek64(fd, 0, SEEK_END); | |
| 166 | - bs->total_sectors = size / 512; | |
| 167 | - bs->fd = fd; | |
| 168 | 186 | } |
| 169 | 187 | |
| 170 | -#ifndef _WIN32 | |
| 171 | - if (snapshot) { | |
| 172 | - /* create a temporary COW file */ | |
| 173 | - cow_fd = mkstemp64(template); | |
| 174 | - if (cow_fd < 0) | |
| 175 | - goto fail; | |
| 176 | - bs->cow_fd = cow_fd; | |
| 177 | - unlink(template); | |
| 178 | - | |
| 179 | - /* just need to allocate bitmap */ | |
| 180 | - bs->cow_bitmap_size = (bs->total_sectors + 7) >> 3; | |
| 181 | - bs->cow_bitmap_addr = mmap(get_mmap_addr(bs->cow_bitmap_size), | |
| 182 | - bs->cow_bitmap_size, | |
| 183 | - PROT_READ | PROT_WRITE, | |
| 184 | - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| 185 | - if (bs->cow_bitmap_addr == MAP_FAILED) | |
| 186 | - goto fail; | |
| 187 | - bs->cow_bitmap = bs->cow_bitmap_addr; | |
| 188 | - bs->cow_sectors_offset = 0; | |
| 189 | - } | |
| 190 | -#endif | |
| 191 | - | |
| 192 | 188 | bs->inserted = 1; |
| 193 | 189 | |
| 194 | 190 | /* call the change callback */ |
| ... | ... | @@ -196,23 +192,22 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) |
| 196 | 192 | bs->change_cb(bs->change_opaque); |
| 197 | 193 | |
| 198 | 194 | return 0; |
| 199 | - fail: | |
| 200 | - bdrv_close(bs); | |
| 201 | - return -1; | |
| 202 | 195 | } |
| 203 | 196 | |
| 204 | 197 | void bdrv_close(BlockDriverState *bs) |
| 205 | 198 | { |
| 206 | 199 | if (bs->inserted) { |
| 207 | -#ifndef _WIN32 | |
| 208 | - /* we unmap the mapping so that it is written to the COW file */ | |
| 209 | - if (bs->cow_bitmap_addr) | |
| 210 | - munmap(bs->cow_bitmap_addr, bs->cow_bitmap_size); | |
| 200 | + if (bs->backing_hd) | |
| 201 | + bdrv_delete(bs->backing_hd); | |
| 202 | + bs->drv->bdrv_close(bs); | |
| 203 | + qemu_free(bs->opaque); | |
| 204 | +#ifdef _WIN32 | |
| 205 | + if (bs->is_temporary) { | |
| 206 | + unlink(bs->filename); | |
| 207 | + } | |
| 211 | 208 | #endif |
| 212 | - if (bs->cow_fd >= 0) | |
| 213 | - close(bs->cow_fd); | |
| 214 | - if (bs->fd >= 0) | |
| 215 | - close(bs->fd); | |
| 209 | + bs->opaque = NULL; | |
| 210 | + bs->drv = NULL; | |
| 216 | 211 | bs->inserted = 0; |
| 217 | 212 | |
| 218 | 213 | /* call the change callback */ |
| ... | ... | @@ -223,85 +218,45 @@ void bdrv_close(BlockDriverState *bs) |
| 223 | 218 | |
| 224 | 219 | void bdrv_delete(BlockDriverState *bs) |
| 225 | 220 | { |
| 221 | + /* XXX: remove the driver list */ | |
| 226 | 222 | bdrv_close(bs); |
| 227 | 223 | qemu_free(bs); |
| 228 | 224 | } |
| 229 | 225 | |
| 230 | -static inline void set_bit(uint8_t *bitmap, int64_t bitnum) | |
| 231 | -{ | |
| 232 | - bitmap[bitnum / 8] |= (1 << (bitnum%8)); | |
| 233 | -} | |
| 234 | - | |
| 235 | -static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) | |
| 236 | -{ | |
| 237 | - return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); | |
| 238 | -} | |
| 239 | - | |
| 240 | - | |
| 241 | -/* Return true if first block has been changed (ie. current version is | |
| 242 | - * in COW file). Set the number of continuous blocks for which that | |
| 243 | - * is true. */ | |
| 244 | -static int is_changed(uint8_t *bitmap, | |
| 245 | - int64_t sector_num, int nb_sectors, | |
| 246 | - int *num_same) | |
| 247 | -{ | |
| 248 | - int changed; | |
| 249 | - | |
| 250 | - if (!bitmap || nb_sectors == 0) { | |
| 251 | - *num_same = nb_sectors; | |
| 252 | - return 0; | |
| 253 | - } | |
| 254 | - | |
| 255 | - changed = is_bit_set(bitmap, sector_num); | |
| 256 | - for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { | |
| 257 | - if (is_bit_set(bitmap, sector_num + *num_same) != changed) | |
| 258 | - break; | |
| 259 | - } | |
| 260 | - | |
| 261 | - return changed; | |
| 262 | -} | |
| 263 | - | |
| 264 | 226 | /* commit COW file into the raw image */ |
| 265 | 227 | int bdrv_commit(BlockDriverState *bs) |
| 266 | 228 | { |
| 267 | 229 | int64_t i; |
| 268 | - uint8_t *cow_bitmap; | |
| 230 | + int n, j; | |
| 231 | + unsigned char sector[512]; | |
| 269 | 232 | |
| 270 | 233 | if (!bs->inserted) |
| 271 | - return -1; | |
| 272 | - | |
| 273 | - if (!bs->cow_bitmap) { | |
| 274 | - fprintf(stderr, "Already committed to %s\n", bs->filename); | |
| 275 | - return 0; | |
| 276 | - } | |
| 234 | + return -ENOENT; | |
| 277 | 235 | |
| 278 | 236 | if (bs->read_only) { |
| 279 | - fprintf(stderr, "Can't commit to %s: read-only\n", bs->filename); | |
| 280 | - return -1; | |
| 237 | + return -EACCES; | |
| 281 | 238 | } |
| 282 | 239 | |
| 283 | - cow_bitmap = bs->cow_bitmap; | |
| 284 | - for (i = 0; i < bs->total_sectors; i++) { | |
| 285 | - if (is_bit_set(cow_bitmap, i)) { | |
| 286 | - unsigned char sector[512]; | |
| 287 | - if (bdrv_read(bs, i, sector, 1) != 0) { | |
| 288 | - fprintf(stderr, "Error reading sector %lli: aborting commit\n", | |
| 289 | - (long long)i); | |
| 290 | - return -1; | |
| 291 | - } | |
| 240 | + if (!bs->backing_hd) { | |
| 241 | + return -ENOTSUP; | |
| 242 | + } | |
| 292 | 243 | |
| 293 | - /* Make bdrv_write write to real file for a moment. */ | |
| 294 | - bs->cow_bitmap = NULL; | |
| 295 | - if (bdrv_write(bs, i, sector, 1) != 0) { | |
| 296 | - fprintf(stderr, "Error writing sector %lli: aborting commit\n", | |
| 297 | - (long long)i); | |
| 298 | - bs->cow_bitmap = cow_bitmap; | |
| 299 | - return -1; | |
| 244 | + for (i = 0; i < bs->total_sectors;) { | |
| 245 | + if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) { | |
| 246 | + for(j = 0; j < n; j++) { | |
| 247 | + if (bdrv_read(bs, i, sector, 1) != 0) { | |
| 248 | + return -EIO; | |
| 249 | + } | |
| 250 | + | |
| 251 | + if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) { | |
| 252 | + return -EIO; | |
| 253 | + } | |
| 254 | + i++; | |
| 300 | 255 | } |
| 301 | - bs->cow_bitmap = cow_bitmap; | |
| 302 | - } | |
| 256 | + } else { | |
| 257 | + i += n; | |
| 258 | + } | |
| 303 | 259 | } |
| 304 | - fprintf(stderr, "Committed snapshot to %s\n", bs->filename); | |
| 305 | 260 | return 0; |
| 306 | 261 | } |
| 307 | 262 | |
| ... | ... | @@ -309,37 +264,34 @@ int bdrv_commit(BlockDriverState *bs) |
| 309 | 264 | int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
| 310 | 265 | uint8_t *buf, int nb_sectors) |
| 311 | 266 | { |
| 312 | - int ret, n, fd; | |
| 313 | - int64_t offset; | |
| 314 | - | |
| 267 | + int ret, n; | |
| 268 | + BlockDriver *drv = bs->drv; | |
| 269 | + | |
| 315 | 270 | if (!bs->inserted) |
| 316 | 271 | return -1; |
| 317 | 272 | |
| 318 | 273 | while (nb_sectors > 0) { |
| 319 | - if (is_changed(bs->cow_bitmap, sector_num, nb_sectors, &n)) { | |
| 320 | - fd = bs->cow_fd; | |
| 321 | - offset = bs->cow_sectors_offset; | |
| 322 | - } else if (sector_num == 0 && bs->boot_sector_enabled) { | |
| 274 | + if (sector_num == 0 && bs->boot_sector_enabled) { | |
| 323 | 275 | memcpy(buf, bs->boot_sector_data, 512); |
| 324 | 276 | n = 1; |
| 325 | - goto next; | |
| 326 | - } else { | |
| 327 | - fd = bs->fd; | |
| 328 | - offset = 0; | |
| 329 | - } | |
| 330 | - | |
| 331 | - if (fd < 0) { | |
| 332 | - /* no file, just return empty sectors */ | |
| 333 | - memset(buf, 0, n * 512); | |
| 277 | + } else if (bs->backing_hd) { | |
| 278 | + if (drv->bdrv_is_allocated(bs, sector_num, nb_sectors, &n)) { | |
| 279 | + ret = drv->bdrv_read(bs, sector_num, buf, n); | |
| 280 | + if (ret < 0) | |
| 281 | + return -1; | |
| 282 | + } else { | |
| 283 | + /* read from the base image */ | |
| 284 | + ret = bdrv_read(bs->backing_hd, sector_num, buf, n); | |
| 285 | + if (ret < 0) | |
| 286 | + return -1; | |
| 287 | + } | |
| 334 | 288 | } else { |
| 335 | - offset += sector_num * 512; | |
| 336 | - lseek64(fd, offset, SEEK_SET); | |
| 337 | - ret = read(fd, buf, n * 512); | |
| 338 | - if (ret != n * 512) { | |
| 289 | + ret = drv->bdrv_read(bs, sector_num, buf, nb_sectors); | |
| 290 | + if (ret < 0) | |
| 339 | 291 | return -1; |
| 340 | - } | |
| 292 | + /* no need to loop */ | |
| 293 | + break; | |
| 341 | 294 | } |
| 342 | - next: | |
| 343 | 295 | nb_sectors -= n; |
| 344 | 296 | sector_num += n; |
| 345 | 297 | buf += n * 512; |
| ... | ... | @@ -351,37 +303,11 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
| 351 | 303 | int bdrv_write(BlockDriverState *bs, int64_t sector_num, |
| 352 | 304 | const uint8_t *buf, int nb_sectors) |
| 353 | 305 | { |
| 354 | - int ret, fd, i; | |
| 355 | - int64_t offset, retl; | |
| 356 | - | |
| 357 | 306 | if (!bs->inserted) |
| 358 | 307 | return -1; |
| 359 | 308 | if (bs->read_only) |
| 360 | 309 | return -1; |
| 361 | - | |
| 362 | - if (bs->cow_bitmap) { | |
| 363 | - fd = bs->cow_fd; | |
| 364 | - offset = bs->cow_sectors_offset; | |
| 365 | - } else { | |
| 366 | - fd = bs->fd; | |
| 367 | - offset = 0; | |
| 368 | - } | |
| 369 | - | |
| 370 | - offset += sector_num * 512; | |
| 371 | - retl = lseek64(fd, offset, SEEK_SET); | |
| 372 | - if (retl == -1) { | |
| 373 | - return -1; | |
| 374 | - } | |
| 375 | - ret = write(fd, buf, nb_sectors * 512); | |
| 376 | - if (ret != nb_sectors * 512) { | |
| 377 | - return -1; | |
| 378 | - } | |
| 379 | - | |
| 380 | - if (bs->cow_bitmap) { | |
| 381 | - for (i = 0; i < nb_sectors; i++) | |
| 382 | - set_bit(bs->cow_bitmap, sector_num + i); | |
| 383 | - } | |
| 384 | - return 0; | |
| 310 | + return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors); | |
| 385 | 311 | } |
| 386 | 312 | |
| 387 | 313 | void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) |
| ... | ... | @@ -459,6 +385,47 @@ void bdrv_set_change_cb(BlockDriverState *bs, |
| 459 | 385 | bs->change_opaque = opaque; |
| 460 | 386 | } |
| 461 | 387 | |
| 388 | +int bdrv_is_encrypted(BlockDriverState *bs) | |
| 389 | +{ | |
| 390 | + if (bs->backing_hd && bs->backing_hd->encrypted) | |
| 391 | + return 1; | |
| 392 | + return bs->encrypted; | |
| 393 | +} | |
| 394 | + | |
| 395 | +int bdrv_set_key(BlockDriverState *bs, const char *key) | |
| 396 | +{ | |
| 397 | + int ret; | |
| 398 | + if (bs->backing_hd && bs->backing_hd->encrypted) { | |
| 399 | + ret = bdrv_set_key(bs->backing_hd, key); | |
| 400 | + if (ret < 0) | |
| 401 | + return ret; | |
| 402 | + if (!bs->encrypted) | |
| 403 | + return 0; | |
| 404 | + } | |
| 405 | + if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key) | |
| 406 | + return -1; | |
| 407 | + return bs->drv->bdrv_set_key(bs, key); | |
| 408 | +} | |
| 409 | + | |
| 410 | +void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size) | |
| 411 | +{ | |
| 412 | + if (!bs->inserted || !bs->drv) { | |
| 413 | + buf[0] = '\0'; | |
| 414 | + } else { | |
| 415 | + pstrcpy(buf, buf_size, bs->drv->format_name); | |
| 416 | + } | |
| 417 | +} | |
| 418 | + | |
| 419 | +void bdrv_iterate_format(void (*it)(void *opaque, const char *name), | |
| 420 | + void *opaque) | |
| 421 | +{ | |
| 422 | + BlockDriver *drv; | |
| 423 | + | |
| 424 | + for (drv = first_drv; drv != NULL; drv = drv->next) { | |
| 425 | + it(opaque, drv->format_name); | |
| 426 | + } | |
| 427 | +} | |
| 428 | + | |
| 462 | 429 | BlockDriverState *bdrv_find(const char *name) |
| 463 | 430 | { |
| 464 | 431 | BlockDriverState *bs; |
| ... | ... | @@ -479,6 +446,11 @@ void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque) |
| 479 | 446 | } |
| 480 | 447 | } |
| 481 | 448 | |
| 449 | +const char *bdrv_get_device_name(BlockDriverState *bs) | |
| 450 | +{ | |
| 451 | + return bs->device_name; | |
| 452 | +} | |
| 453 | + | |
| 482 | 454 | void bdrv_info(void) |
| 483 | 455 | { |
| 484 | 456 | BlockDriverState *bs; |
| ... | ... | @@ -503,10 +475,117 @@ void bdrv_info(void) |
| 503 | 475 | } |
| 504 | 476 | if (bs->inserted) { |
| 505 | 477 | term_printf(" file=%s", bs->filename); |
| 478 | + if (bs->backing_file[0] != '\0') | |
| 479 | + term_printf(" backing_file=%s", bs->backing_file); | |
| 506 | 480 | term_printf(" ro=%d", bs->read_only); |
| 481 | + term_printf(" drv=%s", bs->drv->format_name); | |
| 482 | + if (bs->encrypted) | |
| 483 | + term_printf(" encrypted"); | |
| 507 | 484 | } else { |
| 508 | 485 | term_printf(" [not inserted]"); |
| 509 | 486 | } |
| 510 | 487 | term_printf("\n"); |
| 511 | 488 | } |
| 512 | 489 | } |
| 490 | + | |
| 491 | + | |
| 492 | +/**************************************************************/ | |
| 493 | +/* RAW block driver */ | |
| 494 | + | |
| 495 | +typedef struct BDRVRawState { | |
| 496 | + int fd; | |
| 497 | +} BDRVRawState; | |
| 498 | + | |
| 499 | +static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) | |
| 500 | +{ | |
| 501 | + return 1; /* maybe */ | |
| 502 | +} | |
| 503 | + | |
| 504 | +static int raw_open(BlockDriverState *bs, const char *filename) | |
| 505 | +{ | |
| 506 | + BDRVRawState *s = bs->opaque; | |
| 507 | + int fd; | |
| 508 | + int64_t size; | |
| 509 | + | |
| 510 | + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | |
| 511 | + if (fd < 0) { | |
| 512 | + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | |
| 513 | + if (fd < 0) | |
| 514 | + return -1; | |
| 515 | + bs->read_only = 1; | |
| 516 | + } | |
| 517 | + size = lseek64(fd, 0, SEEK_END); | |
| 518 | + bs->total_sectors = size / 512; | |
| 519 | + s->fd = fd; | |
| 520 | + return 0; | |
| 521 | +} | |
| 522 | + | |
| 523 | +static int raw_read(BlockDriverState *bs, int64_t sector_num, | |
| 524 | + uint8_t *buf, int nb_sectors) | |
| 525 | +{ | |
| 526 | + BDRVRawState *s = bs->opaque; | |
| 527 | + int ret; | |
| 528 | + | |
| 529 | + lseek64(s->fd, sector_num * 512, SEEK_SET); | |
| 530 | + ret = read(s->fd, buf, nb_sectors * 512); | |
| 531 | + if (ret != nb_sectors * 512) | |
| 532 | + return -1; | |
| 533 | + return 0; | |
| 534 | +} | |
| 535 | + | |
| 536 | +static int raw_write(BlockDriverState *bs, int64_t sector_num, | |
| 537 | + const uint8_t *buf, int nb_sectors) | |
| 538 | +{ | |
| 539 | + BDRVRawState *s = bs->opaque; | |
| 540 | + int ret; | |
| 541 | + | |
| 542 | + lseek64(s->fd, sector_num * 512, SEEK_SET); | |
| 543 | + ret = write(s->fd, buf, nb_sectors * 512); | |
| 544 | + if (ret != nb_sectors * 512) | |
| 545 | + return -1; | |
| 546 | + return 0; | |
| 547 | +} | |
| 548 | + | |
| 549 | +static int raw_close(BlockDriverState *bs) | |
| 550 | +{ | |
| 551 | + BDRVRawState *s = bs->opaque; | |
| 552 | + close(s->fd); | |
| 553 | +} | |
| 554 | + | |
| 555 | +static int raw_create(const char *filename, int64_t total_size, | |
| 556 | + const char *backing_file, int flags) | |
| 557 | +{ | |
| 558 | + int fd; | |
| 559 | + | |
| 560 | + if (flags || backing_file) | |
| 561 | + return -ENOTSUP; | |
| 562 | + | |
| 563 | + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | |
| 564 | + 0644); | |
| 565 | + if (fd < 0) | |
| 566 | + return -EIO; | |
| 567 | + ftruncate64(fd, total_size * 512); | |
| 568 | + close(fd); | |
| 569 | + return 0; | |
| 570 | +} | |
| 571 | + | |
| 572 | +BlockDriver bdrv_raw = { | |
| 573 | + "raw", | |
| 574 | + sizeof(BDRVRawState), | |
| 575 | + raw_probe, | |
| 576 | + raw_open, | |
| 577 | + raw_read, | |
| 578 | + raw_write, | |
| 579 | + raw_close, | |
| 580 | + raw_create, | |
| 581 | +}; | |
| 582 | + | |
| 583 | +void bdrv_init(void) | |
| 584 | +{ | |
| 585 | + bdrv_register(&bdrv_raw); | |
| 586 | +#ifndef _WIN32 | |
| 587 | + bdrv_register(&bdrv_cow); | |
| 588 | +#endif | |
| 589 | + bdrv_register(&bdrv_qcow); | |
| 590 | + bdrv_register(&bdrv_vmdk); | |
| 591 | +} | ... | ... |
block_int.h
0 → 100644
| 1 | +/* | |
| 2 | + * QEMU System Emulator block driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2003 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#ifndef BLOCK_INT_H | |
| 25 | +#define BLOCK_INT_H | |
| 26 | + | |
| 27 | +struct BlockDriver { | |
| 28 | + const char *format_name; | |
| 29 | + int instance_size; | |
| 30 | + int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); | |
| 31 | + int (*bdrv_open)(BlockDriverState *bs, const char *filename); | |
| 32 | + int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, | |
| 33 | + uint8_t *buf, int nb_sectors); | |
| 34 | + int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, | |
| 35 | + const uint8_t *buf, int nb_sectors); | |
| 36 | + int (*bdrv_close)(BlockDriverState *bs); | |
| 37 | + int (*bdrv_create)(const char *filename, int64_t total_sectors, | |
| 38 | + const char *backing_file, int flags); | |
| 39 | + int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num, | |
| 40 | + int nb_sectors, int *pnum); | |
| 41 | + int (*bdrv_set_key)(BlockDriverState *bs, const char *key); | |
| 42 | + struct BlockDriver *next; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +struct BlockDriverState { | |
| 46 | + int64_t total_sectors; | |
| 47 | + int read_only; /* if true, the media is read only */ | |
| 48 | + int inserted; /* if true, the media is present */ | |
| 49 | + int removable; /* if true, the media can be removed */ | |
| 50 | + int locked; /* if true, the media cannot temporarily be ejected */ | |
| 51 | + int encrypted; /* if true, the media is encrypted */ | |
| 52 | + /* event callback when inserting/removing */ | |
| 53 | + void (*change_cb)(void *opaque); | |
| 54 | + void *change_opaque; | |
| 55 | + | |
| 56 | + BlockDriver *drv; | |
| 57 | + void *opaque; | |
| 58 | + | |
| 59 | + int boot_sector_enabled; | |
| 60 | + uint8_t boot_sector_data[512]; | |
| 61 | + | |
| 62 | + char filename[1024]; | |
| 63 | + char backing_file[1024]; /* if non zero, the image is a diff of | |
| 64 | + this file image */ | |
| 65 | + int is_temporary; | |
| 66 | + | |
| 67 | + BlockDriverState *backing_hd; | |
| 68 | + | |
| 69 | + /* NOTE: the following infos are only hints for real hardware | |
| 70 | + drivers. They are not used by the block driver */ | |
| 71 | + int cyls, heads, secs; | |
| 72 | + int type; | |
| 73 | + char device_name[32]; | |
| 74 | + BlockDriverState *next; | |
| 75 | +}; | |
| 76 | + | |
| 77 | +#endif /* BLOCK_INT_H */ | ... | ... |
qemu-img.c
0 → 100644
| 1 | +/* | |
| 2 | + * create a COW disk image | |
| 3 | + * | |
| 4 | + * Copyright (c) 2003 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#include "vl.h" | |
| 25 | + | |
| 26 | +void *get_mmap_addr(unsigned long size) | |
| 27 | +{ | |
| 28 | + return NULL; | |
| 29 | +} | |
| 30 | + | |
| 31 | +void qemu_free(void *ptr) | |
| 32 | +{ | |
| 33 | + free(ptr); | |
| 34 | +} | |
| 35 | + | |
| 36 | +void *qemu_malloc(size_t size) | |
| 37 | +{ | |
| 38 | + return malloc(size); | |
| 39 | +} | |
| 40 | + | |
| 41 | +void *qemu_mallocz(size_t size) | |
| 42 | +{ | |
| 43 | + void *ptr; | |
| 44 | + ptr = qemu_malloc(size); | |
| 45 | + if (!ptr) | |
| 46 | + return NULL; | |
| 47 | + memset(ptr, 0, size); | |
| 48 | + return ptr; | |
| 49 | +} | |
| 50 | + | |
| 51 | +char *qemu_strdup(const char *str) | |
| 52 | +{ | |
| 53 | + char *ptr; | |
| 54 | + ptr = qemu_malloc(strlen(str) + 1); | |
| 55 | + if (!ptr) | |
| 56 | + return NULL; | |
| 57 | + strcpy(ptr, str); | |
| 58 | + return ptr; | |
| 59 | +} | |
| 60 | + | |
| 61 | +void pstrcpy(char *buf, int buf_size, const char *str) | |
| 62 | +{ | |
| 63 | + int c; | |
| 64 | + char *q = buf; | |
| 65 | + | |
| 66 | + if (buf_size <= 0) | |
| 67 | + return; | |
| 68 | + | |
| 69 | + for(;;) { | |
| 70 | + c = *str++; | |
| 71 | + if (c == 0 || q >= buf + buf_size - 1) | |
| 72 | + break; | |
| 73 | + *q++ = c; | |
| 74 | + } | |
| 75 | + *q = '\0'; | |
| 76 | +} | |
| 77 | + | |
| 78 | +/* strcat and truncate. */ | |
| 79 | +char *pstrcat(char *buf, int buf_size, const char *s) | |
| 80 | +{ | |
| 81 | + int len; | |
| 82 | + len = strlen(buf); | |
| 83 | + if (len < buf_size) | |
| 84 | + pstrcpy(buf + len, buf_size - len, s); | |
| 85 | + return buf; | |
| 86 | +} | |
| 87 | + | |
| 88 | +int strstart(const char *str, const char *val, const char **ptr) | |
| 89 | +{ | |
| 90 | + const char *p, *q; | |
| 91 | + p = str; | |
| 92 | + q = val; | |
| 93 | + while (*q != '\0') { | |
| 94 | + if (*p != *q) | |
| 95 | + return 0; | |
| 96 | + p++; | |
| 97 | + q++; | |
| 98 | + } | |
| 99 | + if (ptr) | |
| 100 | + *ptr = p; | |
| 101 | + return 1; | |
| 102 | +} | |
| 103 | + | |
| 104 | +void term_printf(const char *fmt, ...) | |
| 105 | +{ | |
| 106 | + va_list ap; | |
| 107 | + va_start(ap, fmt); | |
| 108 | + vprintf(fmt, ap); | |
| 109 | + va_end(ap); | |
| 110 | +} | |
| 111 | + | |
| 112 | +void __attribute__((noreturn)) error(const char *fmt, ...) | |
| 113 | +{ | |
| 114 | + va_list ap; | |
| 115 | + va_start(ap, fmt); | |
| 116 | + fprintf(stderr, "qemuimg: "); | |
| 117 | + vfprintf(stderr, fmt, ap); | |
| 118 | + fprintf(stderr, "\n"); | |
| 119 | + exit(1); | |
| 120 | + va_end(ap); | |
| 121 | +} | |
| 122 | + | |
| 123 | +static void format_print(void *opaque, const char *name) | |
| 124 | +{ | |
| 125 | + printf(" %s", name); | |
| 126 | +} | |
| 127 | + | |
| 128 | +void help(void) | |
| 129 | +{ | |
| 130 | + printf("qemuimg version " QEMU_VERSION ", Copyright (c) 2004 Fabrice Bellard\n" | |
| 131 | + "usage: qemuimg command [command options]\n" | |
| 132 | + "QEMU disk image utility\n" | |
| 133 | + "\n" | |
| 134 | + "Command syntax:\n" | |
| 135 | + " create [-e] [-b base_image] [-f fmt] filename [size]\n" | |
| 136 | + " commit [-f fmt] filename\n" | |
| 137 | + " convert [-c] [-e] [-f fmt] filename [-O output_fmt] output_filename\n" | |
| 138 | + " info [-f fmt] filename\n" | |
| 139 | + "\n" | |
| 140 | + "Command parameters:\n" | |
| 141 | + " 'filename' is a disk image filename\n" | |
| 142 | + " 'base_image' is the read-only disk image which is used as base for a copy on\n" | |
| 143 | + " write image; the copy on write image only stores the modified data\n" | |
| 144 | + " 'fmt' is the disk image format. It is guessed automatically in most cases\n" | |
| 145 | + " 'size' is the disk image size in kilobytes. Optional suffixes 'M' (megabyte)\n" | |
| 146 | + " and 'G' (gigabyte) are supported\n" | |
| 147 | + " 'output_filename' is the destination disk image filename\n" | |
| 148 | + " 'output_fmt' is the destination format\n" | |
| 149 | + " '-c' indicates that target image must be compressed (qcow format only)\n" | |
| 150 | + " '-e' indicates that the target image must be encrypted (qcow format only)\n" | |
| 151 | + ); | |
| 152 | + printf("\nSupported format:"); | |
| 153 | + bdrv_iterate_format(format_print, NULL); | |
| 154 | + printf("\n"); | |
| 155 | + exit(1); | |
| 156 | +} | |
| 157 | + | |
| 158 | + | |
| 159 | +#define NB_SUFFIXES 4 | |
| 160 | + | |
| 161 | +static void get_human_readable_size(char *buf, int buf_size, int64_t size) | |
| 162 | +{ | |
| 163 | + char suffixes[NB_SUFFIXES] = "KMGT"; | |
| 164 | + int64_t base; | |
| 165 | + int i; | |
| 166 | + | |
| 167 | + if (size <= 999) { | |
| 168 | + snprintf(buf, buf_size, "%lld", size); | |
| 169 | + } else { | |
| 170 | + base = 1024; | |
| 171 | + for(i = 0; i < NB_SUFFIXES; i++) { | |
| 172 | + if (size < (10 * base)) { | |
| 173 | + snprintf(buf, buf_size, "%0.1f%c", | |
| 174 | + (double)size / base, | |
| 175 | + suffixes[i]); | |
| 176 | + break; | |
| 177 | + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { | |
| 178 | + snprintf(buf, buf_size, "%lld%c", | |
| 179 | + (size + (base >> 1)) / base, | |
| 180 | + suffixes[i]); | |
| 181 | + break; | |
| 182 | + } | |
| 183 | + base = base * 1024; | |
| 184 | + } | |
| 185 | + } | |
| 186 | +} | |
| 187 | + | |
| 188 | +#if defined(WIN32) | |
| 189 | +/* XXX: put correct support for win32 */ | |
| 190 | +static int read_password(char *buf, int buf_size) | |
| 191 | +{ | |
| 192 | + int c, i; | |
| 193 | + printf("Password: "); | |
| 194 | + fflush(stdout); | |
| 195 | + i = 0; | |
| 196 | + for(;;) { | |
| 197 | + c = getchar(); | |
| 198 | + if (c == '\n') | |
| 199 | + break; | |
| 200 | + if (i < (buf_size - 1)) | |
| 201 | + buf[i++] = c; | |
| 202 | + } | |
| 203 | + buf[i] = '\0'; | |
| 204 | + return 0; | |
| 205 | +} | |
| 206 | + | |
| 207 | +#else | |
| 208 | + | |
| 209 | +#include <termios.h> | |
| 210 | + | |
| 211 | +static struct termios oldtty; | |
| 212 | + | |
| 213 | +static void term_exit(void) | |
| 214 | +{ | |
| 215 | + tcsetattr (0, TCSANOW, &oldtty); | |
| 216 | +} | |
| 217 | + | |
| 218 | +static void term_init(void) | |
| 219 | +{ | |
| 220 | + struct termios tty; | |
| 221 | + | |
| 222 | + tcgetattr (0, &tty); | |
| 223 | + oldtty = tty; | |
| 224 | + | |
| 225 | + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP | |
| 226 | + |INLCR|IGNCR|ICRNL|IXON); | |
| 227 | + tty.c_oflag |= OPOST; | |
| 228 | + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); | |
| 229 | + tty.c_cflag &= ~(CSIZE|PARENB); | |
| 230 | + tty.c_cflag |= CS8; | |
| 231 | + tty.c_cc[VMIN] = 1; | |
| 232 | + tty.c_cc[VTIME] = 0; | |
| 233 | + | |
| 234 | + tcsetattr (0, TCSANOW, &tty); | |
| 235 | + | |
| 236 | + atexit(term_exit); | |
| 237 | +} | |
| 238 | + | |
| 239 | +int read_password(char *buf, int buf_size) | |
| 240 | +{ | |
| 241 | + uint8_t ch; | |
| 242 | + int i, ret; | |
| 243 | + | |
| 244 | + printf("password: "); | |
| 245 | + fflush(stdout); | |
| 246 | + term_init(); | |
| 247 | + i = 0; | |
| 248 | + for(;;) { | |
| 249 | + ret = read(0, &ch, 1); | |
| 250 | + if (ret == -1) { | |
| 251 | + if (errno == EAGAIN || errno == EINTR) { | |
| 252 | + continue; | |
| 253 | + } else { | |
| 254 | + ret = -1; | |
| 255 | + break; | |
| 256 | + } | |
| 257 | + } else if (ret == 0) { | |
| 258 | + ret = -1; | |
| 259 | + break; | |
| 260 | + } else { | |
| 261 | + if (ch == '\r') { | |
| 262 | + ret = 0; | |
| 263 | + break; | |
| 264 | + } | |
| 265 | + if (i < (buf_size - 1)) | |
| 266 | + buf[i++] = ch; | |
| 267 | + } | |
| 268 | + } | |
| 269 | + term_exit(); | |
| 270 | + buf[i] = '\0'; | |
| 271 | + printf("\n"); | |
| 272 | + return ret; | |
| 273 | +} | |
| 274 | +#endif | |
| 275 | + | |
| 276 | +static int img_create(int argc, char **argv) | |
| 277 | +{ | |
| 278 | + int c, ret, encrypted; | |
| 279 | + const char *fmt = "raw"; | |
| 280 | + const char *filename; | |
| 281 | + const char *base_filename = NULL; | |
| 282 | + int64_t size; | |
| 283 | + const char *p; | |
| 284 | + BlockDriver *drv; | |
| 285 | + | |
| 286 | + encrypted = 0; | |
| 287 | + for(;;) { | |
| 288 | + c = getopt(argc, argv, "b:f:he"); | |
| 289 | + if (c == -1) | |
| 290 | + break; | |
| 291 | + switch(c) { | |
| 292 | + case 'h': | |
| 293 | + help(); | |
| 294 | + break; | |
| 295 | + case 'b': | |
| 296 | + base_filename = optarg; | |
| 297 | + break; | |
| 298 | + case 'f': | |
| 299 | + fmt = optarg; | |
| 300 | + break; | |
| 301 | + case 'e': | |
| 302 | + encrypted = 1; | |
| 303 | + break; | |
| 304 | + } | |
| 305 | + } | |
| 306 | + optind++; | |
| 307 | + if (optind >= argc) | |
| 308 | + help(); | |
| 309 | + filename = argv[optind++]; | |
| 310 | + size = 0; | |
| 311 | + if (!base_filename) { | |
| 312 | + if (optind >= argc) | |
| 313 | + help(); | |
| 314 | + p = argv[optind]; | |
| 315 | + size = strtoul(p, (char **)&p, 0); | |
| 316 | + if (*p == 'M') { | |
| 317 | + size *= 1024 * 1024; | |
| 318 | + } else if (*p == 'G') { | |
| 319 | + size *= 1024 * 1024 * 1024; | |
| 320 | + } else if (*p == 'k' || *p == 'K' || *p == '\0') { | |
| 321 | + size *= 1024; | |
| 322 | + } else { | |
| 323 | + help(); | |
| 324 | + } | |
| 325 | + } | |
| 326 | + drv = bdrv_find_format(fmt); | |
| 327 | + if (!drv) | |
| 328 | + error("Unknown file format '%s'", fmt); | |
| 329 | + printf("Formating '%s', fmt=%s", | |
| 330 | + filename, fmt); | |
| 331 | + if (encrypted) | |
| 332 | + printf(", encrypted"); | |
| 333 | + if (base_filename) | |
| 334 | + printf(", backing_file=%s\n", | |
| 335 | + base_filename); | |
| 336 | + else | |
| 337 | + printf(", size=%lld kB\n", size / 1024); | |
| 338 | + ret = bdrv_create(drv, filename, size / 512, base_filename, encrypted); | |
| 339 | + if (ret < 0) { | |
| 340 | + if (ret == -ENOTSUP) { | |
| 341 | + error("Formatting or formatting option not suppored for file format '%s'", fmt); | |
| 342 | + } else { | |
| 343 | + error("Error while formatting"); | |
| 344 | + } | |
| 345 | + } | |
| 346 | + return 0; | |
| 347 | +} | |
| 348 | + | |
| 349 | +static int img_commit(int argc, char **argv) | |
| 350 | +{ | |
| 351 | + int c, ret; | |
| 352 | + const char *filename, *fmt; | |
| 353 | + BlockDriver *drv; | |
| 354 | + BlockDriverState *bs; | |
| 355 | + | |
| 356 | + fmt = NULL; | |
| 357 | + for(;;) { | |
| 358 | + c = getopt(argc, argv, "f:h"); | |
| 359 | + if (c == -1) | |
| 360 | + break; | |
| 361 | + switch(c) { | |
| 362 | + case 'h': | |
| 363 | + help(); | |
| 364 | + break; | |
| 365 | + case 'f': | |
| 366 | + fmt = optarg; | |
| 367 | + break; | |
| 368 | + } | |
| 369 | + } | |
| 370 | + optind++; | |
| 371 | + if (optind >= argc) | |
| 372 | + help(); | |
| 373 | + filename = argv[optind++]; | |
| 374 | + | |
| 375 | + bs = bdrv_new(""); | |
| 376 | + if (!bs) | |
| 377 | + error("Not enough memory"); | |
| 378 | + if (fmt) { | |
| 379 | + drv = bdrv_find_format(fmt); | |
| 380 | + if (!drv) | |
| 381 | + error("Unknown file format '%s'", fmt); | |
| 382 | + } else { | |
| 383 | + drv = NULL; | |
| 384 | + } | |
| 385 | + if (bdrv_open2(bs, filename, 0, drv) < 0) { | |
| 386 | + error("Could not open '%s'", filename); | |
| 387 | + } | |
| 388 | + ret = bdrv_commit(bs); | |
| 389 | + switch(ret) { | |
| 390 | + case 0: | |
| 391 | + printf("Image committed.\n"); | |
| 392 | + break; | |
| 393 | + case -ENOENT: | |
| 394 | + error("No disk inserted"); | |
| 395 | + break; | |
| 396 | + case -EACCES: | |
| 397 | + error("Image is read-only"); | |
| 398 | + break; | |
| 399 | + case -ENOTSUP: | |
| 400 | + error("Image is already committed"); | |
| 401 | + break; | |
| 402 | + default: | |
| 403 | + error("Error while committing image"); | |
| 404 | + break; | |
| 405 | + } | |
| 406 | + | |
| 407 | + bdrv_delete(bs); | |
| 408 | + return 0; | |
| 409 | +} | |
| 410 | + | |
| 411 | +static int is_not_zero(const uint8_t *sector, int len) | |
| 412 | +{ | |
| 413 | + int i; | |
| 414 | + len >>= 2; | |
| 415 | + for(i = 0;i < len; i++) { | |
| 416 | + if (((uint32_t *)sector)[i] != 0) | |
| 417 | + return 1; | |
| 418 | + } | |
| 419 | + return 0; | |
| 420 | +} | |
| 421 | + | |
| 422 | +static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) | |
| 423 | +{ | |
| 424 | + int v, i; | |
| 425 | + | |
| 426 | + if (n <= 0) { | |
| 427 | + *pnum = 0; | |
| 428 | + return 0; | |
| 429 | + } | |
| 430 | + v = is_not_zero(buf, 512); | |
| 431 | + for(i = 1; i < n; i++) { | |
| 432 | + buf += 512; | |
| 433 | + if (v != is_not_zero(buf, 512)) | |
| 434 | + break; | |
| 435 | + } | |
| 436 | + *pnum = i; | |
| 437 | + return v; | |
| 438 | +} | |
| 439 | + | |
| 440 | +static BlockDriverState *bdrv_new_open(const char *filename, | |
| 441 | + const char *fmt) | |
| 442 | +{ | |
| 443 | + BlockDriverState *bs; | |
| 444 | + BlockDriver *drv; | |
| 445 | + char password[256]; | |
| 446 | + | |
| 447 | + bs = bdrv_new(""); | |
| 448 | + if (!bs) | |
| 449 | + error("Not enough memory"); | |
| 450 | + if (fmt) { | |
| 451 | + drv = bdrv_find_format(fmt); | |
| 452 | + if (!drv) | |
| 453 | + error("Unknown file format '%s'", fmt); | |
| 454 | + } else { | |
| 455 | + drv = NULL; | |
| 456 | + } | |
| 457 | + if (bdrv_open2(bs, filename, 0, drv) < 0) { | |
| 458 | + error("Could not open '%s'", filename); | |
| 459 | + } | |
| 460 | + if (bdrv_is_encrypted(bs)) { | |
| 461 | + printf("Disk image '%s' is encrypted.\n", filename); | |
| 462 | + if (read_password(password, sizeof(password)) < 0) | |
| 463 | + error("No password given"); | |
| 464 | + if (bdrv_set_key(bs, password) < 0) | |
| 465 | + error("invalid password"); | |
| 466 | + } | |
| 467 | + return bs; | |
| 468 | +} | |
| 469 | + | |
| 470 | +#define IO_BUF_SIZE 65536 | |
| 471 | + | |
| 472 | +static int img_convert(int argc, char **argv) | |
| 473 | +{ | |
| 474 | + int c, ret, n, n1, compress, cluster_size, cluster_sectors, encrypt; | |
| 475 | + const char *filename, *fmt, *out_fmt, *out_filename; | |
| 476 | + BlockDriver *drv; | |
| 477 | + BlockDriverState *bs, *out_bs; | |
| 478 | + int64_t total_sectors, nb_sectors, sector_num; | |
| 479 | + uint8_t buf[IO_BUF_SIZE]; | |
| 480 | + const uint8_t *buf1; | |
| 481 | + | |
| 482 | + fmt = NULL; | |
| 483 | + out_fmt = "raw"; | |
| 484 | + compress = 0; | |
| 485 | + encrypt = 0; | |
| 486 | + for(;;) { | |
| 487 | + c = getopt(argc, argv, "f:O:hce"); | |
| 488 | + if (c == -1) | |
| 489 | + break; | |
| 490 | + switch(c) { | |
| 491 | + case 'h': | |
| 492 | + help(); | |
| 493 | + break; | |
| 494 | + case 'f': | |
| 495 | + fmt = optarg; | |
| 496 | + break; | |
| 497 | + case 'O': | |
| 498 | + out_fmt = optarg; | |
| 499 | + break; | |
| 500 | + case 'c': | |
| 501 | + compress = 1; | |
| 502 | + break; | |
| 503 | + case 'e': | |
| 504 | + encrypt = 1; | |
| 505 | + break; | |
| 506 | + } | |
| 507 | + } | |
| 508 | + optind++; | |
| 509 | + if (optind >= argc) | |
| 510 | + help(); | |
| 511 | + filename = argv[optind++]; | |
| 512 | + if (optind >= argc) | |
| 513 | + help(); | |
| 514 | + out_filename = argv[optind++]; | |
| 515 | + | |
| 516 | + bs = bdrv_new_open(filename, fmt); | |
| 517 | + | |
| 518 | + drv = bdrv_find_format(out_fmt); | |
| 519 | + if (!drv) | |
| 520 | + error("Unknown file format '%s'", fmt); | |
| 521 | + if (compress && drv != &bdrv_qcow) | |
| 522 | + error("Compression not supported for this file format"); | |
| 523 | + if (encrypt && drv != &bdrv_qcow) | |
| 524 | + error("Encryption not supported for this file format"); | |
| 525 | + if (compress && encrypt) | |
| 526 | + error("Compression and encryption not supported at the same time"); | |
| 527 | + bdrv_get_geometry(bs, &total_sectors); | |
| 528 | + ret = bdrv_create(drv, out_filename, total_sectors, NULL, encrypt); | |
| 529 | + if (ret < 0) { | |
| 530 | + if (ret == -ENOTSUP) { | |
| 531 | + error("Formatting not suppored for file format '%s'", fmt); | |
| 532 | + } else { | |
| 533 | + error("Error while formatting '%s'", out_filename); | |
| 534 | + } | |
| 535 | + } | |
| 536 | + | |
| 537 | + out_bs = bdrv_new_open(out_filename, out_fmt); | |
| 538 | + | |
| 539 | + if (compress) { | |
| 540 | + cluster_size = qcow_get_cluster_size(out_bs); | |
| 541 | + if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) | |
| 542 | + error("invalid cluster size"); | |
| 543 | + cluster_sectors = cluster_size >> 9; | |
| 544 | + sector_num = 0; | |
| 545 | + for(;;) { | |
| 546 | + nb_sectors = total_sectors - sector_num; | |
| 547 | + if (nb_sectors <= 0) | |
| 548 | + break; | |
| 549 | + if (nb_sectors >= cluster_sectors) | |
| 550 | + n = cluster_sectors; | |
| 551 | + else | |
| 552 | + n = nb_sectors; | |
| 553 | + if (bdrv_read(bs, sector_num, buf, n) < 0) | |
| 554 | + error("error while reading"); | |
| 555 | + if (n < cluster_sectors) | |
| 556 | + memset(buf + n * 512, 0, cluster_size - n * 512); | |
| 557 | + if (is_not_zero(buf, cluster_size)) { | |
| 558 | + if (qcow_compress_cluster(out_bs, sector_num, buf) != 0) | |
| 559 | + error("error while compressing sector %lld", sector_num); | |
| 560 | + } | |
| 561 | + sector_num += n; | |
| 562 | + } | |
| 563 | + } else { | |
| 564 | + sector_num = 0; | |
| 565 | + for(;;) { | |
| 566 | + nb_sectors = total_sectors - sector_num; | |
| 567 | + if (nb_sectors <= 0) | |
| 568 | + break; | |
| 569 | + if (nb_sectors >= (IO_BUF_SIZE / 512)) | |
| 570 | + n = (IO_BUF_SIZE / 512); | |
| 571 | + else | |
| 572 | + n = nb_sectors; | |
| 573 | + if (bdrv_read(bs, sector_num, buf, n) < 0) | |
| 574 | + error("error while reading"); | |
| 575 | + /* NOTE: at the same time we convert, we do not write zero | |
| 576 | + sectors to have a chance to compress the image. Ideally, we | |
| 577 | + should add a specific call to have the info to go faster */ | |
| 578 | + buf1 = buf; | |
| 579 | + while (n > 0) { | |
| 580 | + if (is_allocated_sectors(buf1, n, &n1)) { | |
| 581 | + if (bdrv_write(out_bs, sector_num, buf1, n1) < 0) | |
| 582 | + error("error while writing"); | |
| 583 | + } | |
| 584 | + sector_num += n1; | |
| 585 | + n -= n1; | |
| 586 | + buf1 += n1 * 512; | |
| 587 | + } | |
| 588 | + } | |
| 589 | + } | |
| 590 | + bdrv_delete(out_bs); | |
| 591 | + bdrv_delete(bs); | |
| 592 | + return 0; | |
| 593 | +} | |
| 594 | + | |
| 595 | +static int img_info(int argc, char **argv) | |
| 596 | +{ | |
| 597 | + int c; | |
| 598 | + const char *filename, *fmt; | |
| 599 | + BlockDriver *drv; | |
| 600 | + BlockDriverState *bs; | |
| 601 | + char fmt_name[128], size_buf[128], dsize_buf[128]; | |
| 602 | + int64_t total_sectors; | |
| 603 | + struct stat st; | |
| 604 | + | |
| 605 | + fmt = NULL; | |
| 606 | + for(;;) { | |
| 607 | + c = getopt(argc, argv, "f:h"); | |
| 608 | + if (c == -1) | |
| 609 | + break; | |
| 610 | + switch(c) { | |
| 611 | + case 'h': | |
| 612 | + help(); | |
| 613 | + break; | |
| 614 | + case 'f': | |
| 615 | + fmt = optarg; | |
| 616 | + break; | |
| 617 | + } | |
| 618 | + } | |
| 619 | + optind++; | |
| 620 | + if (optind >= argc) | |
| 621 | + help(); | |
| 622 | + filename = argv[optind++]; | |
| 623 | + | |
| 624 | + bs = bdrv_new(""); | |
| 625 | + if (!bs) | |
| 626 | + error("Not enough memory"); | |
| 627 | + if (fmt) { | |
| 628 | + drv = bdrv_find_format(fmt); | |
| 629 | + if (!drv) | |
| 630 | + error("Unknown file format '%s'", fmt); | |
| 631 | + } else { | |
| 632 | + drv = NULL; | |
| 633 | + } | |
| 634 | + if (bdrv_open2(bs, filename, 0, drv) < 0) { | |
| 635 | + error("Could not open '%s'", filename); | |
| 636 | + } | |
| 637 | + bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); | |
| 638 | + bdrv_get_geometry(bs, &total_sectors); | |
| 639 | + get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); | |
| 640 | + if (stat(filename, &st) < 0) | |
| 641 | + error("Could not stat '%s'", filename); | |
| 642 | + get_human_readable_size(dsize_buf, sizeof(dsize_buf), | |
| 643 | + (int64_t)st.st_blocks * 512); | |
| 644 | + printf("image: %s\n" | |
| 645 | + "file format: %s\n" | |
| 646 | + "virtual size: %s (%lld bytes)\n" | |
| 647 | + "disk size: %s\n", | |
| 648 | + filename, fmt_name, size_buf, | |
| 649 | + total_sectors * 512, | |
| 650 | + dsize_buf); | |
| 651 | + if (bdrv_is_encrypted(bs)) | |
| 652 | + printf("encrypted: yes\n"); | |
| 653 | + bdrv_delete(bs); | |
| 654 | + return 0; | |
| 655 | +} | |
| 656 | + | |
| 657 | +int main(int argc, char **argv) | |
| 658 | +{ | |
| 659 | + const char *cmd; | |
| 660 | + | |
| 661 | + bdrv_init(); | |
| 662 | + if (argc < 2) | |
| 663 | + help(); | |
| 664 | + cmd = argv[1]; | |
| 665 | + if (!strcmp(cmd, "create")) { | |
| 666 | + img_create(argc, argv); | |
| 667 | + } else if (!strcmp(cmd, "commit")) { | |
| 668 | + img_commit(argc, argv); | |
| 669 | + } else if (!strcmp(cmd, "convert")) { | |
| 670 | + img_convert(argc, argv); | |
| 671 | + } else if (!strcmp(cmd, "info")) { | |
| 672 | + img_info(argc, argv); | |
| 673 | + } else { | |
| 674 | + help(); | |
| 675 | + } | |
| 676 | + return 0; | |
| 677 | +} | ... | ... |
vl.h
| ... | ... | @@ -48,8 +48,21 @@ |
| 48 | 48 | #define lseek64 _lseeki64 |
| 49 | 49 | #endif |
| 50 | 50 | |
| 51 | +#ifdef QEMU_TOOL | |
| 52 | + | |
| 53 | +/* we use QEMU_TOOL in the command line tools which do not depend on | |
| 54 | + the target CPU type */ | |
| 55 | +#include "config-host.h" | |
| 56 | +#include <setjmp.h> | |
| 57 | +#include "osdep.h" | |
| 58 | +#include "bswap.h" | |
| 59 | + | |
| 60 | +#else | |
| 61 | + | |
| 51 | 62 | #include "cpu.h" |
| 52 | 63 | |
| 64 | +#endif /* !defined(QEMU_TOOL) */ | |
| 65 | + | |
| 53 | 66 | #ifndef glue |
| 54 | 67 | #define xglue(x, y) x ## y |
| 55 | 68 | #define glue(x, y) xglue(x, y) |
| ... | ... | @@ -57,153 +70,6 @@ |
| 57 | 70 | #define tostring(s) #s |
| 58 | 71 | #endif |
| 59 | 72 | |
| 60 | -#if defined(WORDS_BIGENDIAN) | |
| 61 | -static inline uint32_t be32_to_cpu(uint32_t v) | |
| 62 | -{ | |
| 63 | - return v; | |
| 64 | -} | |
| 65 | - | |
| 66 | -static inline uint16_t be16_to_cpu(uint16_t v) | |
| 67 | -{ | |
| 68 | - return v; | |
| 69 | -} | |
| 70 | - | |
| 71 | -static inline uint32_t cpu_to_be32(uint32_t v) | |
| 72 | -{ | |
| 73 | - return v; | |
| 74 | -} | |
| 75 | - | |
| 76 | -static inline uint16_t cpu_to_be16(uint16_t v) | |
| 77 | -{ | |
| 78 | - return v; | |
| 79 | -} | |
| 80 | - | |
| 81 | -static inline uint32_t le32_to_cpu(uint32_t v) | |
| 82 | -{ | |
| 83 | - return bswap32(v); | |
| 84 | -} | |
| 85 | - | |
| 86 | -static inline uint16_t le16_to_cpu(uint16_t v) | |
| 87 | -{ | |
| 88 | - return bswap16(v); | |
| 89 | -} | |
| 90 | - | |
| 91 | -static inline uint32_t cpu_to_le32(uint32_t v) | |
| 92 | -{ | |
| 93 | - return bswap32(v); | |
| 94 | -} | |
| 95 | - | |
| 96 | -static inline uint16_t cpu_to_le16(uint16_t v) | |
| 97 | -{ | |
| 98 | - return bswap16(v); | |
| 99 | -} | |
| 100 | - | |
| 101 | -#else | |
| 102 | - | |
| 103 | -static inline uint32_t be32_to_cpu(uint32_t v) | |
| 104 | -{ | |
| 105 | - return bswap32(v); | |
| 106 | -} | |
| 107 | - | |
| 108 | -static inline uint16_t be16_to_cpu(uint16_t v) | |
| 109 | -{ | |
| 110 | - return bswap16(v); | |
| 111 | -} | |
| 112 | - | |
| 113 | -static inline uint32_t cpu_to_be32(uint32_t v) | |
| 114 | -{ | |
| 115 | - return bswap32(v); | |
| 116 | -} | |
| 117 | - | |
| 118 | -static inline uint16_t cpu_to_be16(uint16_t v) | |
| 119 | -{ | |
| 120 | - return bswap16(v); | |
| 121 | -} | |
| 122 | - | |
| 123 | -static inline uint32_t le32_to_cpu(uint32_t v) | |
| 124 | -{ | |
| 125 | - return v; | |
| 126 | -} | |
| 127 | - | |
| 128 | -static inline uint16_t le16_to_cpu(uint16_t v) | |
| 129 | -{ | |
| 130 | - return v; | |
| 131 | -} | |
| 132 | - | |
| 133 | -static inline uint32_t cpu_to_le32(uint32_t v) | |
| 134 | -{ | |
| 135 | - return v; | |
| 136 | -} | |
| 137 | - | |
| 138 | -static inline uint16_t cpu_to_le16(uint16_t v) | |
| 139 | -{ | |
| 140 | - return v; | |
| 141 | -} | |
| 142 | -#endif | |
| 143 | - | |
| 144 | -static inline void cpu_to_le16w(uint16_t *p, uint16_t v) | |
| 145 | -{ | |
| 146 | - *p = cpu_to_le16(v); | |
| 147 | -} | |
| 148 | - | |
| 149 | -static inline void cpu_to_le32w(uint32_t *p, uint32_t v) | |
| 150 | -{ | |
| 151 | - *p = cpu_to_le32(v); | |
| 152 | -} | |
| 153 | - | |
| 154 | -static inline uint16_t le16_to_cpup(const uint16_t *p) | |
| 155 | -{ | |
| 156 | - return le16_to_cpu(*p); | |
| 157 | -} | |
| 158 | - | |
| 159 | -static inline uint32_t le32_to_cpup(const uint32_t *p) | |
| 160 | -{ | |
| 161 | - return le32_to_cpu(*p); | |
| 162 | -} | |
| 163 | - | |
| 164 | -/* unaligned versions (optimized for frequent unaligned accesses)*/ | |
| 165 | - | |
| 166 | -#if defined(__i386__) || defined(__powerpc__) | |
| 167 | - | |
| 168 | -#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v) | |
| 169 | -#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v) | |
| 170 | -#define le16_to_cpupu(p) le16_to_cpup(p) | |
| 171 | -#define le32_to_cpupu(p) le32_to_cpup(p) | |
| 172 | - | |
| 173 | -#else | |
| 174 | - | |
| 175 | -static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) | |
| 176 | -{ | |
| 177 | - uint8_t *p1 = (uint8_t *)p; | |
| 178 | - | |
| 179 | - p1[0] = v; | |
| 180 | - p1[1] = v >> 8; | |
| 181 | -} | |
| 182 | - | |
| 183 | -static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) | |
| 184 | -{ | |
| 185 | - uint8_t *p1 = (uint8_t *)p; | |
| 186 | - | |
| 187 | - p1[0] = v; | |
| 188 | - p1[1] = v >> 8; | |
| 189 | - p1[2] = v >> 16; | |
| 190 | - p1[3] = v >> 24; | |
| 191 | -} | |
| 192 | - | |
| 193 | -static inline uint16_t le16_to_cpupu(const uint16_t *p) | |
| 194 | -{ | |
| 195 | - const uint8_t *p1 = (const uint8_t *)p; | |
| 196 | - return p1[0] | (p1[1] << 8); | |
| 197 | -} | |
| 198 | - | |
| 199 | -static inline uint32_t le32_to_cpupu(const uint32_t *p) | |
| 200 | -{ | |
| 201 | - const uint8_t *p1 = (const uint8_t *)p; | |
| 202 | - return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24); | |
| 203 | -} | |
| 204 | - | |
| 205 | -#endif | |
| 206 | - | |
| 207 | 73 | /* vl.c */ |
| 208 | 74 | uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); |
| 209 | 75 | |
| ... | ... | @@ -233,6 +99,8 @@ void qemu_register_reset(QEMUResetHandler *func, void *opaque); |
| 233 | 99 | void qemu_system_reset_request(void); |
| 234 | 100 | void qemu_system_shutdown_request(void); |
| 235 | 101 | |
| 102 | +void main_loop_wait(int timeout); | |
| 103 | + | |
| 236 | 104 | extern int audio_enabled; |
| 237 | 105 | extern int ram_size; |
| 238 | 106 | extern int bios_size; |
| ... | ... | @@ -301,6 +169,7 @@ void qemu_del_fd_read_handler(int fd); |
| 301 | 169 | /* character device */ |
| 302 | 170 | |
| 303 | 171 | #define CHR_EVENT_BREAK 0 /* serial break char */ |
| 172 | +#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */ | |
| 304 | 173 | |
| 305 | 174 | typedef void IOEventHandler(void *opaque, int event); |
| 306 | 175 | |
| ... | ... | @@ -310,11 +179,13 @@ typedef struct CharDriverState { |
| 310 | 179 | IOCanRWHandler *fd_can_read, |
| 311 | 180 | IOReadHandler *fd_read, void *opaque); |
| 312 | 181 | IOEventHandler *chr_event; |
| 182 | + IOEventHandler *chr_send_event; | |
| 313 | 183 | void *opaque; |
| 314 | 184 | } CharDriverState; |
| 315 | 185 | |
| 316 | 186 | void qemu_chr_printf(CharDriverState *s, const char *fmt, ...); |
| 317 | 187 | int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len); |
| 188 | +void qemu_chr_send_event(CharDriverState *s, int event); | |
| 318 | 189 | void qemu_chr_add_read_handler(CharDriverState *s, |
| 319 | 190 | IOCanRWHandler *fd_can_read, |
| 320 | 191 | IOReadHandler *fd_read, void *opaque); |
| ... | ... | @@ -464,10 +335,23 @@ void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); |
| 464 | 335 | |
| 465 | 336 | /* block.c */ |
| 466 | 337 | typedef struct BlockDriverState BlockDriverState; |
| 467 | - | |
| 338 | +typedef struct BlockDriver BlockDriver; | |
| 339 | + | |
| 340 | +extern BlockDriver bdrv_raw; | |
| 341 | +extern BlockDriver bdrv_cow; | |
| 342 | +extern BlockDriver bdrv_qcow; | |
| 343 | +extern BlockDriver bdrv_vmdk; | |
| 344 | + | |
| 345 | +void bdrv_init(void); | |
| 346 | +BlockDriver *bdrv_find_format(const char *format_name); | |
| 347 | +int bdrv_create(BlockDriver *drv, | |
| 348 | + const char *filename, int64_t size_in_sectors, | |
| 349 | + const char *backing_file, int flags); | |
| 468 | 350 | BlockDriverState *bdrv_new(const char *device_name); |
| 469 | 351 | void bdrv_delete(BlockDriverState *bs); |
| 470 | 352 | int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot); |
| 353 | +int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, | |
| 354 | + BlockDriver *drv); | |
| 471 | 355 | void bdrv_close(BlockDriverState *bs); |
| 472 | 356 | int bdrv_read(BlockDriverState *bs, int64_t sector_num, |
| 473 | 357 | uint8_t *buf, int nb_sectors); |
| ... | ... | @@ -494,11 +378,21 @@ int bdrv_is_locked(BlockDriverState *bs); |
| 494 | 378 | void bdrv_set_locked(BlockDriverState *bs, int locked); |
| 495 | 379 | void bdrv_set_change_cb(BlockDriverState *bs, |
| 496 | 380 | void (*change_cb)(void *opaque), void *opaque); |
| 497 | - | |
| 381 | +void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); | |
| 498 | 382 | void bdrv_info(void); |
| 499 | 383 | BlockDriverState *bdrv_find(const char *name); |
| 500 | 384 | void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque); |
| 385 | +int bdrv_is_encrypted(BlockDriverState *bs); | |
| 386 | +int bdrv_set_key(BlockDriverState *bs, const char *key); | |
| 387 | +void bdrv_iterate_format(void (*it)(void *opaque, const char *name), | |
| 388 | + void *opaque); | |
| 389 | +const char *bdrv_get_device_name(BlockDriverState *bs); | |
| 501 | 390 | |
| 391 | +int qcow_get_cluster_size(BlockDriverState *bs); | |
| 392 | +int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, | |
| 393 | + const uint8_t *buf); | |
| 394 | + | |
| 395 | +#ifndef QEMU_TOOL | |
| 502 | 396 | /* ISA bus */ |
| 503 | 397 | |
| 504 | 398 | extern target_phys_addr_t isa_mem_base; |
| ... | ... | @@ -823,11 +717,28 @@ void adb_mouse_init(ADBBusState *bus); |
| 823 | 717 | extern ADBBusState adb_bus; |
| 824 | 718 | int cuda_init(openpic_t *openpic, int irq); |
| 825 | 719 | |
| 720 | +#endif /* defined(QEMU_TOOL) */ | |
| 721 | + | |
| 826 | 722 | /* monitor.c */ |
| 827 | 723 | void monitor_init(CharDriverState *hd, int show_banner); |
| 724 | +void term_puts(const char *str); | |
| 725 | +void term_vprintf(const char *fmt, va_list ap); | |
| 828 | 726 | void term_printf(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); |
| 829 | 727 | void term_flush(void); |
| 830 | 728 | void term_print_help(void); |
| 729 | +void monitor_readline(const char *prompt, int is_password, | |
| 730 | + char *buf, int buf_size); | |
| 731 | + | |
| 732 | +/* readline.c */ | |
| 733 | +typedef void ReadLineFunc(void *opaque, const char *str); | |
| 734 | + | |
| 735 | +extern int completion_index; | |
| 736 | +void add_completion(const char *str); | |
| 737 | +void readline_handle_byte(int ch); | |
| 738 | +void readline_find_completion(const char *cmdline); | |
| 739 | +const char *readline_get_history(unsigned int index); | |
| 740 | +void readline_start(const char *prompt, int is_password, | |
| 741 | + ReadLineFunc *readline_func, void *opaque); | |
| 831 | 742 | |
| 832 | 743 | /* gdbstub.c */ |
| 833 | 744 | ... | ... |