📄 block-vmdk.c.svn-base
字号:
/* * Block driver for the VMDK format * * Copyright (c) 2004 Fabrice Bellard * Copyright (c) 2005 Filip Navara * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */#include "qemu-common.h"#include "block_int.h"#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')typedef struct { uint32_t version; uint32_t flags; uint32_t disk_sectors; uint32_t granularity; uint32_t l1dir_offset; uint32_t l1dir_size; uint32_t file_sectors; uint32_t cylinders; uint32_t heads; uint32_t sectors_per_track;} VMDK3Header;typedef struct { uint32_t version; uint32_t flags; int64_t capacity; int64_t granularity; int64_t desc_offset; int64_t desc_size; int32_t num_gtes_per_gte; int64_t rgd_offset; int64_t gd_offset; int64_t grain_offset; char filler[1]; char check_bytes[4];} __attribute__((packed)) VMDK4Header;#define L2_CACHE_SIZE 16typedef struct BDRVVmdkState { BlockDriverState *hd; int64_t l1_table_offset; int64_t l1_backup_table_offset; uint32_t *l1_table; uint32_t *l1_backup_table; unsigned int l1_size; uint32_t l1_entry_sectors; unsigned int l2_size; uint32_t *l2_cache; uint32_t l2_cache_offsets[L2_CACHE_SIZE]; uint32_t l2_cache_counts[L2_CACHE_SIZE]; unsigned int cluster_sectors; uint32_t parent_cid; int is_parent;} BDRVVmdkState;typedef struct VmdkMetaData { uint32_t offset; unsigned int l1_index; unsigned int l2_index; unsigned int l2_offset; int valid;} VmdkMetaData;typedef struct ActiveBDRVState{ BlockDriverState *hd; // active image handler uint64_t cluster_offset; // current write offset}ActiveBDRVState;static ActiveBDRVState activeBDRV;static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename){ uint32_t magic; if (buf_size < 4) return 0; magic = be32_to_cpu(*(uint32_t *)buf); if (magic == VMDK3_MAGIC || magic == VMDK4_MAGIC) return 100; else return 0;}#define CHECK_CID 1#define SECTOR_SIZE 512#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each#define HEADER_SIZE 512 // first sector of 512 bytesstatic uint32_t vmdk_read_cid(BlockDriverState *bs, int parent){ BDRVVmdkState *s = bs->opaque; char desc[DESC_SIZE]; uint32_t cid; char *p_name, *cid_str; size_t cid_str_size; /* the descriptor offset = 0x200 */ if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) return 0; if (parent) { cid_str = "parentCID"; cid_str_size = sizeof("parentCID"); } else { cid_str = "CID"; cid_str_size = sizeof("CID"); } if ((p_name = strstr(desc,cid_str)) != 0) { p_name += cid_str_size; sscanf(p_name,"%x",&cid); } return cid;}static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid){ BDRVVmdkState *s = bs->opaque; char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; char *p_name, *tmp_str; /* the descriptor offset = 0x200 */ if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) return -1; tmp_str = strstr(desc,"parentCID"); strcpy(tmp_desc, tmp_str); if ((p_name = strstr(desc,"CID")) != 0) { p_name += sizeof("CID"); sprintf(p_name,"%x\n",cid); strcat(desc,tmp_desc); } if (bdrv_pwrite(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) return -1; return 0;}static int vmdk_is_cid_valid(BlockDriverState *bs){#ifdef CHECK_CID BDRVVmdkState *s = bs->opaque; BlockDriverState *p_bs = s->hd->backing_hd; uint32_t cur_pcid; if (p_bs) { cur_pcid = vmdk_read_cid(p_bs,0); if (s->parent_cid != cur_pcid) // CID not valid return 0; }#endif // CID valid return 1;}static int vmdk_snapshot_create(const char *filename, const char *backing_file){ int snp_fd, p_fd; uint32_t p_cid; char *p_name, *gd_buf, *rgd_buf; const char *real_filename, *temp_str; VMDK4Header header; uint32_t gde_entries, gd_size; int64_t gd_offset, rgd_offset, capacity, gt_size; char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE]; char *desc_template = "# Disk DescriptorFile\n" "version=1\n" "CID=%x\n" "parentCID=%x\n" "createType=\"monolithicSparse\"\n" "parentFileNameHint=\"%s\"\n" "\n" "# Extent description\n" "RW %lu SPARSE \"%s\"\n" "\n" "# The Disk Data Base \n" "#DDB\n" "\n"; snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); if (snp_fd < 0) return -1; p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); if (p_fd < 0) { close(snp_fd); return -1; } /* read the header */ if (lseek(p_fd, 0x0, SEEK_SET) == -1) goto fail; if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) goto fail; /* write the header */ if (lseek(snp_fd, 0x0, SEEK_SET) == -1) goto fail; if (write(snp_fd, hdr, HEADER_SIZE) == -1) goto fail; memset(&header, 0, sizeof(header)); memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC ftruncate(snp_fd, header.grain_offset << 9); /* the descriptor offset = 0x200 */ if (lseek(p_fd, 0x200, SEEK_SET) == -1) goto fail; if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) goto fail; if ((p_name = strstr(p_desc,"CID")) != 0) { p_name += sizeof("CID"); sscanf(p_name,"%x",&p_cid); } real_filename = filename; if ((temp_str = strrchr(real_filename, '\\')) != NULL) real_filename = temp_str + 1; if ((temp_str = strrchr(real_filename, '/')) != NULL) real_filename = temp_str + 1; if ((temp_str = strrchr(real_filename, ':')) != NULL) real_filename = temp_str + 1; sprintf(s_desc, desc_template, p_cid, p_cid, backing_file , (uint32_t)header.capacity, real_filename); /* write the descriptor */ if (lseek(snp_fd, 0x200, SEEK_SET) == -1) goto fail; if (write(snp_fd, s_desc, strlen(s_desc)) == -1) goto fail; gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table capacity = header.capacity * SECTOR_SIZE; // Extent size /* * Each GDE span 32M disk, means: * 512 GTE per GT, each GTE points to grain */ gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; if (!gt_size) goto fail; gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde gd_size = gde_entries * sizeof(uint32_t); /* write RGD */ rgd_buf = qemu_malloc(gd_size); if (!rgd_buf) goto fail; if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) goto fail_rgd; if (read(p_fd, rgd_buf, gd_size) != gd_size) goto fail_rgd; if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) goto fail_rgd; if (write(snp_fd, rgd_buf, gd_size) == -1) goto fail_rgd; qemu_free(rgd_buf); /* write GD */ gd_buf = qemu_malloc(gd_size); if (!gd_buf) goto fail_rgd; if (lseek(p_fd, gd_offset, SEEK_SET) == -1) goto fail_gd; if (read(p_fd, gd_buf, gd_size) != gd_size) goto fail_gd; if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) goto fail_gd; if (write(snp_fd, gd_buf, gd_size) == -1) goto fail_gd; qemu_free(gd_buf); close(p_fd); close(snp_fd); return 0; fail_gd: qemu_free(gd_buf); fail_rgd: qemu_free(rgd_buf); fail: close(p_fd); close(snp_fd); return -1;}static void vmdk_parent_close(BlockDriverState *bs){ if (bs->backing_hd) bdrv_close(bs->backing_hd);}int parent_open = 0;static int vmdk_parent_open(BlockDriverState *bs, const char * filename){ BDRVVmdkState *s = bs->opaque; char *p_name; char desc[DESC_SIZE]; char parent_img_name[1024]; /* the descriptor offset = 0x200 */ if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) return -1; if ((p_name = strstr(desc,"parentFileNameHint")) != 0) { char *end_name; struct stat file_buf; p_name += sizeof("parentFileNameHint") + 1; if ((end_name = strchr(p_name,'\"')) == 0) return -1; strncpy(s->hd->backing_file, p_name, end_name - p_name); if (stat(s->hd->backing_file, &file_buf) != 0) { path_combine(parent_img_name, sizeof(parent_img_name), filename, s->hd->backing_file); } else { strcpy(parent_img_name, s->hd->backing_file); } s->hd->backing_hd = bdrv_new(""); if (!s->hd->backing_hd) { failure: bdrv_close(s->hd); return -1; } parent_open = 1; if (bdrv_open(s->hd->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0) goto failure; parent_open = 0; } return 0;}static int vmdk_open(BlockDriverState *bs, const char *filename, int flags){ BDRVVmdkState *s = bs->opaque; uint32_t magic; int l1_size, i, ret; if (parent_open) // Parent must be opened as RO. flags = BDRV_O_RDONLY; fprintf(stderr, "(VMDK) image open: flags=0x%x filename=%s\n", flags, bs->filename); ret = bdrv_file_open(&s->hd, filename, flags); if (ret < 0) return ret; if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic)) goto fail; magic = be32_to_cpu(magic); if (magic == VMDK3_MAGIC) { VMDK3Header header; if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) goto fail; s->cluster_sectors = le32_to_cpu(header.granularity); s->l2_size = 1 << 9; s->l1_size = 1 << 6; bs->total_sectors = le32_to_cpu(header.disk_sectors); s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; s->l1_backup_table_offset = 0; s->l1_entry_sectors = s->l2_size * s->cluster_sectors; } else if (magic == VMDK4_MAGIC) { VMDK4Header header; if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) goto fail; bs->total_sectors = le64_to_cpu(header.capacity); s->cluster_sectors = le64_to_cpu(header.granularity); s->l2_size = le32_to_cpu(header.num_gtes_per_gte); s->l1_entry_sectors = s->l2_size * s->cluster_sectors; if (s->l1_entry_sectors <= 0) goto fail; s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) / s->l1_entry_sectors; s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; if (parent_open) s->is_parent = 1; else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -