Annotation of qemu/block-vmdk.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Block driver for the VMDK format
        !             3:  * 
        !             4:  * Copyright (c) 2004 Fabrice Bellard
        !             5:  * Copyright (c) 2005 Filip Navara
        !             6:  * 
        !             7:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             8:  * of this software and associated documentation files (the "Software"), to deal
        !             9:  * in the Software without restriction, including without limitation the rights
        !            10:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            11:  * copies of the Software, and to permit persons to whom the Software is
        !            12:  * furnished to do so, subject to the following conditions:
        !            13:  *
        !            14:  * The above copyright notice and this permission notice shall be included in
        !            15:  * all copies or substantial portions of the Software.
        !            16:  *
        !            17:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            18:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            19:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            20:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            21:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            22:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            23:  * THE SOFTWARE.
        !            24:  */
        !            25: #include "vl.h"
        !            26: #include "block_int.h"
        !            27: 
        !            28: #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
        !            29: #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
        !            30: 
        !            31: typedef struct {
        !            32:     uint32_t version;
        !            33:     uint32_t flags;
        !            34:     uint32_t disk_sectors;
        !            35:     uint32_t granularity;
        !            36:     uint32_t l1dir_offset;
        !            37:     uint32_t l1dir_size;
        !            38:     uint32_t file_sectors;
        !            39:     uint32_t cylinders;
        !            40:     uint32_t heads;
        !            41:     uint32_t sectors_per_track;
        !            42: } VMDK3Header;
        !            43: 
        !            44: typedef struct {
        !            45:     uint32_t version;
        !            46:     uint32_t flags;
        !            47:     int64_t capacity;
        !            48:     int64_t granularity;
        !            49:     int64_t desc_offset;
        !            50:     int64_t desc_size;
        !            51:     int32_t num_gtes_per_gte;
        !            52:     int64_t rgd_offset;
        !            53:     int64_t gd_offset;
        !            54:     int64_t grain_offset;
        !            55:     char filler[1];
        !            56:     char check_bytes[4];
        !            57: } __attribute__((packed)) VMDK4Header;
        !            58: 
        !            59: #define L2_CACHE_SIZE 16
        !            60: 
        !            61: typedef struct BDRVVmdkState {
        !            62:     int fd;
        !            63:     int64_t l1_table_offset;
        !            64:     int64_t l1_backup_table_offset;
        !            65:     uint32_t *l1_table;
        !            66:     uint32_t *l1_backup_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_RDWR | O_BINARY | O_LARGEFILE);
        !           100:     if (fd < 0) {
        !           101:         fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
        !           102:         if (fd < 0)
        !           103:             return -1;
        !           104:         bs->read_only = 1;
        !           105:     }
        !           106:     if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
        !           107:         goto fail;
        !           108:     magic = be32_to_cpu(magic);
        !           109:     if (magic == VMDK3_MAGIC) {
        !           110:         VMDK3Header header;
        !           111:         if (read(fd, &header, sizeof(header)) != 
        !           112:             sizeof(header))
        !           113:             goto fail;
        !           114:         s->cluster_sectors = le32_to_cpu(header.granularity);
        !           115:         s->l2_size = 1 << 9;
        !           116:         s->l1_size = 1 << 6;
        !           117:         bs->total_sectors = le32_to_cpu(header.disk_sectors);
        !           118:         s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
        !           119:         s->l1_backup_table_offset = 0;
        !           120:         s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
        !           121:     } else if (magic == VMDK4_MAGIC) {
        !           122:         VMDK4Header header;
        !           123:         
        !           124:         if (read(fd, &header, sizeof(header)) != sizeof(header))
        !           125:             goto fail;
        !           126:         bs->total_sectors = le32_to_cpu(header.capacity);
        !           127:         s->cluster_sectors = le32_to_cpu(header.granularity);
        !           128:         s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
        !           129:         s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
        !           130:         if (s->l1_entry_sectors <= 0)
        !           131:             goto fail;
        !           132:         s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) 
        !           133:             / s->l1_entry_sectors;
        !           134:         s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
        !           135:         s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
        !           136:     } else {
        !           137:         goto fail;
        !           138:     }
        !           139:     /* read the L1 table */
        !           140:     l1_size = s->l1_size * sizeof(uint32_t);
        !           141:     s->l1_table = qemu_malloc(l1_size);
        !           142:     if (!s->l1_table)
        !           143:         goto fail;
        !           144:     if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1)
        !           145:         goto fail;
        !           146:     if (read(fd, s->l1_table, l1_size) != l1_size)
        !           147:         goto fail;
        !           148:     for(i = 0; i < s->l1_size; i++) {
        !           149:         le32_to_cpus(&s->l1_table[i]);
        !           150:     }
        !           151: 
        !           152:     if (s->l1_backup_table_offset) {
        !           153:         s->l1_backup_table = qemu_malloc(l1_size);
        !           154:         if (!s->l1_backup_table)
        !           155:             goto fail;
        !           156:         if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
        !           157:             goto fail;
        !           158:         if (read(fd, s->l1_backup_table, l1_size) != l1_size)
        !           159:             goto fail;
        !           160:         for(i = 0; i < s->l1_size; i++) {
        !           161:             le32_to_cpus(&s->l1_backup_table[i]);
        !           162:         }
        !           163:     }
        !           164: 
        !           165:     s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
        !           166:     if (!s->l2_cache)
        !           167:         goto fail;
        !           168:     s->fd = fd;
        !           169:     return 0;
        !           170:  fail:
        !           171:     qemu_free(s->l1_backup_table);
        !           172:     qemu_free(s->l1_table);
        !           173:     qemu_free(s->l2_cache);
        !           174:     close(fd);
        !           175:     return -1;
        !           176: }
        !           177: 
        !           178: static uint64_t get_cluster_offset(BlockDriverState *bs,
        !           179:                                    uint64_t offset, int allocate)
        !           180: {
        !           181:     BDRVVmdkState *s = bs->opaque;
        !           182:     unsigned int l1_index, l2_offset, l2_index;
        !           183:     int min_index, i, j;
        !           184:     uint32_t min_count, *l2_table, tmp;
        !           185:     uint64_t cluster_offset;
        !           186:     
        !           187:     l1_index = (offset >> 9) / s->l1_entry_sectors;
        !           188:     if (l1_index >= s->l1_size)
        !           189:         return 0;
        !           190:     l2_offset = s->l1_table[l1_index];
        !           191:     if (!l2_offset)
        !           192:         return 0;
        !           193:     for(i = 0; i < L2_CACHE_SIZE; i++) {
        !           194:         if (l2_offset == s->l2_cache_offsets[i]) {
        !           195:             /* increment the hit count */
        !           196:             if (++s->l2_cache_counts[i] == 0xffffffff) {
        !           197:                 for(j = 0; j < L2_CACHE_SIZE; j++) {
        !           198:                     s->l2_cache_counts[j] >>= 1;
        !           199:                 }
        !           200:             }
        !           201:             l2_table = s->l2_cache + (i * s->l2_size);
        !           202:             goto found;
        !           203:         }
        !           204:     }
        !           205:     /* not found: load a new entry in the least used one */
        !           206:     min_index = 0;
        !           207:     min_count = 0xffffffff;
        !           208:     for(i = 0; i < L2_CACHE_SIZE; i++) {
        !           209:         if (s->l2_cache_counts[i] < min_count) {
        !           210:             min_count = s->l2_cache_counts[i];
        !           211:             min_index = i;
        !           212:         }
        !           213:     }
        !           214:     l2_table = s->l2_cache + (min_index * s->l2_size);
        !           215:     lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET);
        !           216:     if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != 
        !           217:         s->l2_size * sizeof(uint32_t))
        !           218:         return 0;
        !           219:     s->l2_cache_offsets[min_index] = l2_offset;
        !           220:     s->l2_cache_counts[min_index] = 1;
        !           221:  found:
        !           222:     l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
        !           223:     cluster_offset = le32_to_cpu(l2_table[l2_index]);
        !           224:     if (!cluster_offset) {
        !           225:         if (!allocate)
        !           226:             return 0;
        !           227:         cluster_offset = lseek(s->fd, 0, SEEK_END);
        !           228:         ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9));
        !           229:         cluster_offset >>= 9;
        !           230:         /* update L2 table */
        !           231:         tmp = cpu_to_le32(cluster_offset);
        !           232:         l2_table[l2_index] = tmp;
        !           233:         lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
        !           234:         if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
        !           235:             return 0;
        !           236:         /* update backup L2 table */
        !           237:         if (s->l1_backup_table_offset != 0) {
        !           238:             l2_offset = s->l1_backup_table[l1_index];
        !           239:             lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
        !           240:             if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
        !           241:                 return 0;
        !           242:         }
        !           243:     }
        !           244:     cluster_offset <<= 9;
        !           245:     return cluster_offset;
        !           246: }
        !           247: 
        !           248: static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, 
        !           249:                              int nb_sectors, int *pnum)
        !           250: {
        !           251:     BDRVVmdkState *s = bs->opaque;
        !           252:     int index_in_cluster, n;
        !           253:     uint64_t cluster_offset;
        !           254: 
        !           255:     cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
        !           256:     index_in_cluster = sector_num % s->cluster_sectors;
        !           257:     n = s->cluster_sectors - index_in_cluster;
        !           258:     if (n > nb_sectors)
        !           259:         n = nb_sectors;
        !           260:     *pnum = n;
        !           261:     return (cluster_offset != 0);
        !           262: }
        !           263: 
        !           264: static int vmdk_read(BlockDriverState *bs, int64_t sector_num, 
        !           265:                     uint8_t *buf, int nb_sectors)
        !           266: {
        !           267:     BDRVVmdkState *s = bs->opaque;
        !           268:     int ret, index_in_cluster, n;
        !           269:     uint64_t cluster_offset;
        !           270:     
        !           271:     while (nb_sectors > 0) {
        !           272:         cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
        !           273:         index_in_cluster = sector_num % s->cluster_sectors;
        !           274:         n = s->cluster_sectors - index_in_cluster;
        !           275:         if (n > nb_sectors)
        !           276:             n = nb_sectors;
        !           277:         if (!cluster_offset) {
        !           278:             memset(buf, 0, 512 * n);
        !           279:         } else {
        !           280:             lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
        !           281:             ret = read(s->fd, buf, n * 512);
        !           282:             if (ret != n * 512) 
        !           283:                 return -1;
        !           284:         }
        !           285:         nb_sectors -= n;
        !           286:         sector_num += n;
        !           287:         buf += n * 512;
        !           288:     }
        !           289:     return 0;
        !           290: }
        !           291: 
        !           292: static int vmdk_write(BlockDriverState *bs, int64_t sector_num, 
        !           293:                      const uint8_t *buf, int nb_sectors)
        !           294: {
        !           295:     BDRVVmdkState *s = bs->opaque;
        !           296:     int ret, index_in_cluster, n;
        !           297:     uint64_t cluster_offset;
        !           298: 
        !           299:     while (nb_sectors > 0) {
        !           300:         index_in_cluster = sector_num & (s->cluster_sectors - 1);
        !           301:         n = s->cluster_sectors - index_in_cluster;
        !           302:         if (n > nb_sectors)
        !           303:             n = nb_sectors;
        !           304:         cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
        !           305:         if (!cluster_offset)
        !           306:             return -1;
        !           307:         lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
        !           308:         ret = write(s->fd, buf, n * 512);
        !           309:         if (ret != n * 512)
        !           310:             return -1;
        !           311:         nb_sectors -= n;
        !           312:         sector_num += n;
        !           313:         buf += n * 512;
        !           314:     }
        !           315:     return 0;
        !           316: }
        !           317: 
        !           318: static int vmdk_create(const char *filename, int64_t total_size,
        !           319:                        const char *backing_file, int flags)
        !           320: {
        !           321:     int fd, i;
        !           322:     VMDK4Header header;
        !           323:     uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
        !           324:     char *desc_template =
        !           325:         "# Disk DescriptorFile\n"
        !           326:         "version=1\n"
        !           327:         "CID=%x\n"
        !           328:         "parentCID=ffffffff\n"
        !           329:         "createType=\"monolithicSparse\"\n"
        !           330:         "\n"
        !           331:         "# Extent description\n"
        !           332:         "RW %lu SPARSE \"%s\"\n"
        !           333:         "\n"
        !           334:         "# The Disk Data Base \n"
        !           335:         "#DDB\n"
        !           336:         "\n"
        !           337:         "ddb.virtualHWVersion = \"3\"\n"
        !           338:         "ddb.geometry.cylinders = \"%lu\"\n"
        !           339:         "ddb.geometry.heads = \"16\"\n"
        !           340:         "ddb.geometry.sectors = \"63\"\n"
        !           341:         "ddb.adapterType = \"ide\"\n";
        !           342:     char desc[1024];
        !           343:     const char *real_filename, *temp_str;
        !           344: 
        !           345:     /* XXX: add support for backing file */
        !           346: 
        !           347:     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
        !           348:               0644);
        !           349:     if (fd < 0)
        !           350:         return -1;
        !           351:     magic = cpu_to_be32(VMDK4_MAGIC);
        !           352:     memset(&header, 0, sizeof(header));
        !           353:     header.version = cpu_to_le32(1);
        !           354:     header.flags = cpu_to_le32(3); /* ?? */
        !           355:     header.capacity = cpu_to_le64(total_size);
        !           356:     header.granularity = cpu_to_le64(128);
        !           357:     header.num_gtes_per_gte = cpu_to_le32(512);
        !           358: 
        !           359:     grains = (total_size + header.granularity - 1) / header.granularity;
        !           360:     gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
        !           361:     gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
        !           362:     gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
        !           363: 
        !           364:     header.desc_offset = 1;
        !           365:     header.desc_size = 20;
        !           366:     header.rgd_offset = header.desc_offset + header.desc_size;
        !           367:     header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
        !           368:     header.grain_offset =
        !           369:        ((header.gd_offset + gd_size + (gt_size * gt_count) +
        !           370:          header.granularity - 1) / header.granularity) *
        !           371:         header.granularity;
        !           372: 
        !           373:     header.desc_offset = cpu_to_le64(header.desc_offset);
        !           374:     header.desc_size = cpu_to_le64(header.desc_size);
        !           375:     header.rgd_offset = cpu_to_le64(header.rgd_offset);
        !           376:     header.gd_offset = cpu_to_le64(header.gd_offset);
        !           377:     header.grain_offset = cpu_to_le64(header.grain_offset);
        !           378: 
        !           379:     header.check_bytes[0] = 0xa;
        !           380:     header.check_bytes[1] = 0x20;
        !           381:     header.check_bytes[2] = 0xd;
        !           382:     header.check_bytes[3] = 0xa;
        !           383:     
        !           384:     /* write all the data */    
        !           385:     write(fd, &magic, sizeof(magic));
        !           386:     write(fd, &header, sizeof(header));
        !           387: 
        !           388:     ftruncate(fd, header.grain_offset << 9);
        !           389: 
        !           390:     /* write grain directory */
        !           391:     lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
        !           392:     for (i = 0, tmp = header.rgd_offset + gd_size;
        !           393:          i < gt_count; i++, tmp += gt_size)
        !           394:         write(fd, &tmp, sizeof(tmp));
        !           395:    
        !           396:     /* write backup grain directory */
        !           397:     lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
        !           398:     for (i = 0, tmp = header.gd_offset + gd_size;
        !           399:          i < gt_count; i++, tmp += gt_size)
        !           400:         write(fd, &tmp, sizeof(tmp));
        !           401: 
        !           402:     /* compose the descriptor */
        !           403:     real_filename = filename;
        !           404:     if ((temp_str = strrchr(real_filename, '\\')) != NULL)
        !           405:         real_filename = temp_str + 1;
        !           406:     if ((temp_str = strrchr(real_filename, '/')) != NULL)
        !           407:         real_filename = temp_str + 1;
        !           408:     if ((temp_str = strrchr(real_filename, ':')) != NULL)
        !           409:         real_filename = temp_str + 1;
        !           410:     sprintf(desc, desc_template, time(NULL), (unsigned long)total_size,
        !           411:             real_filename, total_size / (63 * 16));
        !           412: 
        !           413:     /* write the descriptor */
        !           414:     lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
        !           415:     write(fd, desc, strlen(desc));
        !           416: 
        !           417:     close(fd);
        !           418:     return 0;
        !           419: }
        !           420: 
        !           421: static void vmdk_close(BlockDriverState *bs)
        !           422: {
        !           423:     BDRVVmdkState *s = bs->opaque;
        !           424:     qemu_free(s->l1_table);
        !           425:     qemu_free(s->l2_cache);
        !           426:     close(s->fd);
        !           427: }
        !           428: 
        !           429: BlockDriver bdrv_vmdk = {
        !           430:     "vmdk",
        !           431:     sizeof(BDRVVmdkState),
        !           432:     vmdk_probe,
        !           433:     vmdk_open,
        !           434:     vmdk_read,
        !           435:     vmdk_write,
        !           436:     vmdk_close,
        !           437:     vmdk_create,
        !           438:     vmdk_is_allocated,
        !           439: };

unix.superglobalmegacorp.com