Annotation of qemu/block/vpc.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Block driver for Conectix/Microsoft Virtual PC images
        !             3:  *
        !             4:  * Copyright (c) 2005 Alex Beregszaszi
        !             5:  * Copyright (c) 2009 Kevin Wolf <[email protected]>
        !             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 "qemu-common.h"
        !            26: #include "block_int.h"
        !            27: #include "module.h"
        !            28: 
        !            29: /**************************************************************/
        !            30: 
        !            31: #define HEADER_SIZE 512
        !            32: 
        !            33: //#define CACHE
        !            34: 
        !            35: enum vhd_type {
        !            36:     VHD_FIXED           = 2,
        !            37:     VHD_DYNAMIC         = 3,
        !            38:     VHD_DIFFERENCING    = 4,
        !            39: };
        !            40: 
        !            41: // Seconds since Jan 1, 2000 0:00:00 (UTC)
        !            42: #define VHD_TIMESTAMP_BASE 946684800
        !            43: 
        !            44: // always big-endian
        !            45: struct vhd_footer {
        !            46:     char        creator[8]; // "conectix"
        !            47:     uint32_t    features;
        !            48:     uint32_t    version;
        !            49: 
        !            50:     // Offset of next header structure, 0xFFFFFFFF if none
        !            51:     uint64_t    data_offset;
        !            52: 
        !            53:     // Seconds since Jan 1, 2000 0:00:00 (UTC)
        !            54:     uint32_t    timestamp;
        !            55: 
        !            56:     char        creator_app[4]; // "vpc "
        !            57:     uint16_t    major;
        !            58:     uint16_t    minor;
        !            59:     char        creator_os[4]; // "Wi2k"
        !            60: 
        !            61:     uint64_t    orig_size;
        !            62:     uint64_t    size;
        !            63: 
        !            64:     uint16_t    cyls;
        !            65:     uint8_t     heads;
        !            66:     uint8_t     secs_per_cyl;
        !            67: 
        !            68:     uint32_t    type;
        !            69: 
        !            70:     // Checksum of the Hard Disk Footer ("one's complement of the sum of all
        !            71:     // the bytes in the footer without the checksum field")
        !            72:     uint32_t    checksum;
        !            73: 
        !            74:     // UUID used to identify a parent hard disk (backing file)
        !            75:     uint8_t     uuid[16];
        !            76: 
        !            77:     uint8_t     in_saved_state;
        !            78: };
        !            79: 
        !            80: struct vhd_dyndisk_header {
        !            81:     char        magic[8]; // "cxsparse"
        !            82: 
        !            83:     // Offset of next header structure, 0xFFFFFFFF if none
        !            84:     uint64_t    data_offset;
        !            85: 
        !            86:     // Offset of the Block Allocation Table (BAT)
        !            87:     uint64_t    table_offset;
        !            88: 
        !            89:     uint32_t    version;
        !            90:     uint32_t    max_table_entries; // 32bit/entry
        !            91: 
        !            92:     // 2 MB by default, must be a power of two
        !            93:     uint32_t    block_size;
        !            94: 
        !            95:     uint32_t    checksum;
        !            96:     uint8_t     parent_uuid[16];
        !            97:     uint32_t    parent_timestamp;
        !            98:     uint32_t    reserved;
        !            99: 
        !           100:     // Backing file name (in UTF-16)
        !           101:     uint8_t     parent_name[512];
        !           102: 
        !           103:     struct {
        !           104:         uint32_t    platform;
        !           105:         uint32_t    data_space;
        !           106:         uint32_t    data_length;
        !           107:         uint32_t    reserved;
        !           108:         uint64_t    data_offset;
        !           109:     } parent_locator[8];
        !           110: };
        !           111: 
        !           112: typedef struct BDRVVPCState {
        !           113:     BlockDriverState *hd;
        !           114: 
        !           115:     uint8_t footer_buf[HEADER_SIZE];
        !           116:     uint64_t free_data_block_offset;
        !           117:     int max_table_entries;
        !           118:     uint32_t *pagetable;
        !           119:     uint64_t bat_offset;
        !           120:     uint64_t last_bitmap_offset;
        !           121: 
        !           122:     uint32_t block_size;
        !           123:     uint32_t bitmap_size;
        !           124: 
        !           125: #ifdef CACHE
        !           126:     uint8_t *pageentry_u8;
        !           127:     uint32_t *pageentry_u32;
        !           128:     uint16_t *pageentry_u16;
        !           129: 
        !           130:     uint64_t last_bitmap;
        !           131: #endif
        !           132: } BDRVVPCState;
        !           133: 
        !           134: static uint32_t vpc_checksum(uint8_t* buf, size_t size)
        !           135: {
        !           136:     uint32_t res = 0;
        !           137:     int i;
        !           138: 
        !           139:     for (i = 0; i < size; i++)
        !           140:         res += buf[i];
        !           141: 
        !           142:     return ~res;
        !           143: }
        !           144: 
        !           145: 
        !           146: static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
        !           147: {
        !           148:     if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
        !           149:        return 100;
        !           150:     return 0;
        !           151: }
        !           152: 
        !           153: static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
        !           154: {
        !           155:     BDRVVPCState *s = bs->opaque;
        !           156:     int ret, i;
        !           157:     struct vhd_footer* footer;
        !           158:     struct vhd_dyndisk_header* dyndisk_header;
        !           159:     uint8_t buf[HEADER_SIZE];
        !           160:     uint32_t checksum;
        !           161: 
        !           162:     ret = bdrv_file_open(&s->hd, filename, flags);
        !           163:     if (ret < 0)
        !           164:         return ret;
        !           165: 
        !           166:     if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
        !           167:         goto fail;
        !           168: 
        !           169:     footer = (struct vhd_footer*) s->footer_buf;
        !           170:     if (strncmp(footer->creator, "conectix", 8))
        !           171:         goto fail;
        !           172: 
        !           173:     checksum = be32_to_cpu(footer->checksum);
        !           174:     footer->checksum = 0;
        !           175:     if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
        !           176:         fprintf(stderr, "block-vpc: The header checksum of '%s' is "
        !           177:             "incorrect.\n", filename);
        !           178: 
        !           179:     // The visible size of a image in Virtual PC depends on the geometry
        !           180:     // rather than on the size stored in the footer (the size in the footer
        !           181:     // is too large usually)
        !           182:     bs->total_sectors = (int64_t)
        !           183:         be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
        !           184: 
        !           185:     if (bdrv_pread(s->hd, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
        !           186:             != HEADER_SIZE)
        !           187:         goto fail;
        !           188: 
        !           189:     dyndisk_header = (struct vhd_dyndisk_header*) buf;
        !           190: 
        !           191:     if (strncmp(dyndisk_header->magic, "cxsparse", 8))
        !           192:         goto fail;
        !           193: 
        !           194: 
        !           195:     s->block_size = be32_to_cpu(dyndisk_header->block_size);
        !           196:     s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
        !           197: 
        !           198:     s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
        !           199:     s->pagetable = qemu_malloc(s->max_table_entries * 4);
        !           200: 
        !           201:     s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
        !           202:     if (bdrv_pread(s->hd, s->bat_offset, s->pagetable,
        !           203:             s->max_table_entries * 4) != s->max_table_entries * 4)
        !           204:            goto fail;
        !           205: 
        !           206:     s->free_data_block_offset =
        !           207:         (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
        !           208: 
        !           209:     for (i = 0; i < s->max_table_entries; i++) {
        !           210:         be32_to_cpus(&s->pagetable[i]);
        !           211:         if (s->pagetable[i] != 0xFFFFFFFF) {
        !           212:             int64_t next = (512 * (int64_t) s->pagetable[i]) +
        !           213:                 s->bitmap_size + s->block_size;
        !           214: 
        !           215:             if (next> s->free_data_block_offset)
        !           216:                 s->free_data_block_offset = next;
        !           217:         }
        !           218:     }
        !           219: 
        !           220:     s->last_bitmap_offset = (int64_t) -1;
        !           221: 
        !           222: #ifdef CACHE
        !           223:     s->pageentry_u8 = qemu_malloc(512);
        !           224:     s->pageentry_u32 = s->pageentry_u8;
        !           225:     s->pageentry_u16 = s->pageentry_u8;
        !           226:     s->last_pagetable = -1;
        !           227: #endif
        !           228: 
        !           229:     return 0;
        !           230:  fail:
        !           231:     bdrv_delete(s->hd);
        !           232:     return -1;
        !           233: }
        !           234: 
        !           235: /*
        !           236:  * Returns the absolute byte offset of the given sector in the image file.
        !           237:  * If the sector is not allocated, -1 is returned instead.
        !           238:  *
        !           239:  * The parameter write must be 1 if the offset will be used for a write
        !           240:  * operation (the block bitmaps is updated then), 0 otherwise.
        !           241:  */
        !           242: static inline int64_t get_sector_offset(BlockDriverState *bs,
        !           243:     int64_t sector_num, int write)
        !           244: {
        !           245:     BDRVVPCState *s = bs->opaque;
        !           246:     uint64_t offset = sector_num * 512;
        !           247:     uint64_t bitmap_offset, block_offset;
        !           248:     uint32_t pagetable_index, pageentry_index;
        !           249: 
        !           250:     pagetable_index = offset / s->block_size;
        !           251:     pageentry_index = (offset % s->block_size) / 512;
        !           252: 
        !           253:     if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
        !           254:         return -1; // not allocated
        !           255: 
        !           256:     bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index];
        !           257:     block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index);
        !           258: 
        !           259:     // We must ensure that we don't write to any sectors which are marked as
        !           260:     // unused in the bitmap. We get away with setting all bits in the block
        !           261:     // bitmap each time we write to a new block. This might cause Virtual PC to
        !           262:     // miss sparse read optimization, but it's not a problem in terms of
        !           263:     // correctness.
        !           264:     if (write && (s->last_bitmap_offset != bitmap_offset)) {
        !           265:         uint8_t bitmap[s->bitmap_size];
        !           266: 
        !           267:         s->last_bitmap_offset = bitmap_offset;
        !           268:         memset(bitmap, 0xff, s->bitmap_size);
        !           269:         bdrv_pwrite(s->hd, bitmap_offset, bitmap, s->bitmap_size);
        !           270:     }
        !           271: 
        !           272: //    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
        !           273: //     sector_num, pagetable_index, pageentry_index,
        !           274: //     bitmap_offset, block_offset);
        !           275: 
        !           276: // disabled by reason
        !           277: #if 0
        !           278: #ifdef CACHE
        !           279:     if (bitmap_offset != s->last_bitmap)
        !           280:     {
        !           281:        lseek(s->fd, bitmap_offset, SEEK_SET);
        !           282: 
        !           283:        s->last_bitmap = bitmap_offset;
        !           284: 
        !           285:        // Scary! Bitmap is stored as big endian 32bit entries,
        !           286:        // while we used to look it up byte by byte
        !           287:        read(s->fd, s->pageentry_u8, 512);
        !           288:        for (i = 0; i < 128; i++)
        !           289:            be32_to_cpus(&s->pageentry_u32[i]);
        !           290:     }
        !           291: 
        !           292:     if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
        !           293:        return -1;
        !           294: #else
        !           295:     lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
        !           296: 
        !           297:     read(s->fd, &bitmap_entry, 1);
        !           298: 
        !           299:     if ((bitmap_entry >> (pageentry_index % 8)) & 1)
        !           300:        return -1; // not allocated
        !           301: #endif
        !           302: #endif
        !           303: 
        !           304:     return block_offset;
        !           305: }
        !           306: 
        !           307: /*
        !           308:  * Writes the footer to the end of the image file. This is needed when the
        !           309:  * file grows as it overwrites the old footer
        !           310:  *
        !           311:  * Returns 0 on success and < 0 on error
        !           312:  */
        !           313: static int rewrite_footer(BlockDriverState* bs)
        !           314: {
        !           315:     int ret;
        !           316:     BDRVVPCState *s = bs->opaque;
        !           317:     int64_t offset = s->free_data_block_offset;
        !           318: 
        !           319:     ret = bdrv_pwrite(s->hd, offset, s->footer_buf, HEADER_SIZE);
        !           320:     if (ret < 0)
        !           321:         return ret;
        !           322: 
        !           323:     return 0;
        !           324: }
        !           325: 
        !           326: /*
        !           327:  * Allocates a new block. This involves writing a new footer and updating
        !           328:  * the Block Allocation Table to use the space at the old end of the image
        !           329:  * file (overwriting the old footer)
        !           330:  *
        !           331:  * Returns the sectors' offset in the image file on success and < 0 on error
        !           332:  */
        !           333: static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
        !           334: {
        !           335:     BDRVVPCState *s = bs->opaque;
        !           336:     int64_t bat_offset;
        !           337:     uint32_t index, bat_value;
        !           338:     int ret;
        !           339:     uint8_t bitmap[s->bitmap_size];
        !           340: 
        !           341:     // Check if sector_num is valid
        !           342:     if ((sector_num < 0) || (sector_num > bs->total_sectors))
        !           343:         return -1;
        !           344: 
        !           345:     // Write entry into in-memory BAT
        !           346:     index = (sector_num * 512) / s->block_size;
        !           347:     if (s->pagetable[index] != 0xFFFFFFFF)
        !           348:         return -1;
        !           349: 
        !           350:     s->pagetable[index] = s->free_data_block_offset / 512;
        !           351: 
        !           352:     // Initialize the block's bitmap
        !           353:     memset(bitmap, 0xff, s->bitmap_size);
        !           354:     bdrv_pwrite(s->hd, s->free_data_block_offset, bitmap, s->bitmap_size);
        !           355: 
        !           356:     // Write new footer (the old one will be overwritten)
        !           357:     s->free_data_block_offset += s->block_size + s->bitmap_size;
        !           358:     ret = rewrite_footer(bs);
        !           359:     if (ret < 0)
        !           360:         goto fail;
        !           361: 
        !           362:     // Write BAT entry to disk
        !           363:     bat_offset = s->bat_offset + (4 * index);
        !           364:     bat_value = be32_to_cpu(s->pagetable[index]);
        !           365:     ret = bdrv_pwrite(s->hd, bat_offset, &bat_value, 4);
        !           366:     if (ret < 0)
        !           367:         goto fail;
        !           368: 
        !           369:     return get_sector_offset(bs, sector_num, 0);
        !           370: 
        !           371: fail:
        !           372:     s->free_data_block_offset -= (s->block_size + s->bitmap_size);
        !           373:     return -1;
        !           374: }
        !           375: 
        !           376: static int vpc_read(BlockDriverState *bs, int64_t sector_num,
        !           377:                     uint8_t *buf, int nb_sectors)
        !           378: {
        !           379:     BDRVVPCState *s = bs->opaque;
        !           380:     int ret;
        !           381:     int64_t offset;
        !           382: 
        !           383:     while (nb_sectors > 0) {
        !           384:         offset = get_sector_offset(bs, sector_num, 0);
        !           385: 
        !           386:         if (offset == -1) {
        !           387:             memset(buf, 0, 512);
        !           388:         } else {
        !           389:             ret = bdrv_pread(s->hd, offset, buf, 512);
        !           390:             if (ret != 512)
        !           391:                 return -1;
        !           392:         }
        !           393: 
        !           394:         nb_sectors--;
        !           395:         sector_num++;
        !           396:         buf += 512;
        !           397:     }
        !           398:     return 0;
        !           399: }
        !           400: 
        !           401: static int vpc_write(BlockDriverState *bs, int64_t sector_num,
        !           402:     const uint8_t *buf, int nb_sectors)
        !           403: {
        !           404:     BDRVVPCState *s = bs->opaque;
        !           405:     int64_t offset;
        !           406:     int ret;
        !           407: 
        !           408:     while (nb_sectors > 0) {
        !           409:         offset = get_sector_offset(bs, sector_num, 1);
        !           410: 
        !           411:         if (offset == -1) {
        !           412:             offset = alloc_block(bs, sector_num);
        !           413:             if (offset < 0)
        !           414:                 return -1;
        !           415:         }
        !           416: 
        !           417:         ret = bdrv_pwrite(s->hd, offset, buf, 512);
        !           418:         if (ret != 512)
        !           419:             return -1;
        !           420: 
        !           421:         nb_sectors--;
        !           422:         sector_num++;
        !           423:         buf += 512;
        !           424:     }
        !           425: 
        !           426:     return 0;
        !           427: }
        !           428: 
        !           429: 
        !           430: /*
        !           431:  * Calculates the number of cylinders, heads and sectors per cylinder
        !           432:  * based on a given number of sectors. This is the algorithm described
        !           433:  * in the VHD specification.
        !           434:  *
        !           435:  * Note that the geometry doesn't always exactly match total_sectors but
        !           436:  * may round it down.
        !           437:  *
        !           438:  * Returns 0 on success, -EFBIG if the size is larger than 127 GB
        !           439:  */
        !           440: static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
        !           441:     uint8_t* heads, uint8_t* secs_per_cyl)
        !           442: {
        !           443:     uint32_t cyls_times_heads;
        !           444: 
        !           445:     if (total_sectors > 65535 * 16 * 255)
        !           446:         return -EFBIG;
        !           447: 
        !           448:     if (total_sectors > 65535 * 16 * 63) {
        !           449:         *secs_per_cyl = 255;
        !           450:         *heads = 16;
        !           451:         cyls_times_heads = total_sectors / *secs_per_cyl;
        !           452:     } else {
        !           453:         *secs_per_cyl = 17;
        !           454:         cyls_times_heads = total_sectors / *secs_per_cyl;
        !           455:         *heads = (cyls_times_heads + 1023) / 1024;
        !           456: 
        !           457:         if (*heads < 4)
        !           458:             *heads = 4;
        !           459: 
        !           460:         if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
        !           461:             *secs_per_cyl = 31;
        !           462:             *heads = 16;
        !           463:             cyls_times_heads = total_sectors / *secs_per_cyl;
        !           464:         }
        !           465: 
        !           466:         if (cyls_times_heads >= (*heads * 1024)) {
        !           467:             *secs_per_cyl = 63;
        !           468:             *heads = 16;
        !           469:             cyls_times_heads = total_sectors / *secs_per_cyl;
        !           470:         }
        !           471:     }
        !           472: 
        !           473:     // Note: Rounding up deviates from the Virtual PC behaviour
        !           474:     // However, we need this to avoid truncating images in qemu-img convert
        !           475:     *cyls = (cyls_times_heads + *heads - 1) / *heads;
        !           476: 
        !           477:     return 0;
        !           478: }
        !           479: 
        !           480: static int vpc_create(const char *filename, QEMUOptionParameter *options)
        !           481: {
        !           482:     uint8_t buf[1024];
        !           483:     struct vhd_footer* footer = (struct vhd_footer*) buf;
        !           484:     struct vhd_dyndisk_header* dyndisk_header =
        !           485:         (struct vhd_dyndisk_header*) buf;
        !           486:     int fd, i;
        !           487:     uint16_t cyls;
        !           488:     uint8_t heads;
        !           489:     uint8_t secs_per_cyl;
        !           490:     size_t block_size, num_bat_entries;
        !           491:     int64_t total_sectors = 0;
        !           492: 
        !           493:     // Read out options
        !           494:     while (options && options->name) {
        !           495:         if (!strcmp(options->name, "size")) {
        !           496:             total_sectors = options->value.n / 512;
        !           497:         }
        !           498:         options++;
        !           499:     }
        !           500: 
        !           501:     // Create the file
        !           502:     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
        !           503:     if (fd < 0)
        !           504:         return -EIO;
        !           505: 
        !           506:     // Calculate matching total_size and geometry
        !           507:     if (calculate_geometry(total_sectors, &cyls, &heads, &secs_per_cyl))
        !           508:         return -EFBIG;
        !           509:     total_sectors = (int64_t) cyls * heads * secs_per_cyl;
        !           510: 
        !           511:     // Prepare the Hard Disk Footer
        !           512:     memset(buf, 0, 1024);
        !           513: 
        !           514:     strncpy(footer->creator, "conectix", 8);
        !           515:     // TODO Check if "qemu" creator_app is ok for VPC
        !           516:     strncpy(footer->creator_app, "qemu", 4);
        !           517:     strncpy(footer->creator_os, "Wi2k", 4);
        !           518: 
        !           519:     footer->features = be32_to_cpu(0x02);
        !           520:     footer->version = be32_to_cpu(0x00010000);
        !           521:     footer->data_offset = be64_to_cpu(HEADER_SIZE);
        !           522:     footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
        !           523: 
        !           524:     // Version of Virtual PC 2007
        !           525:     footer->major = be16_to_cpu(0x0005);
        !           526:     footer->minor =be16_to_cpu(0x0003);
        !           527: 
        !           528:     footer->orig_size = be64_to_cpu(total_sectors * 512);
        !           529:     footer->size = be64_to_cpu(total_sectors * 512);
        !           530: 
        !           531:     footer->cyls = be16_to_cpu(cyls);
        !           532:     footer->heads = heads;
        !           533:     footer->secs_per_cyl = secs_per_cyl;
        !           534: 
        !           535:     footer->type = be32_to_cpu(VHD_DYNAMIC);
        !           536: 
        !           537:     // TODO uuid is missing
        !           538: 
        !           539:     footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
        !           540: 
        !           541:     // Write the footer (twice: at the beginning and at the end)
        !           542:     block_size = 0x200000;
        !           543:     num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
        !           544: 
        !           545:     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
        !           546:         return -EIO;
        !           547: 
        !           548:     if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0)
        !           549:         return -EIO;
        !           550:     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
        !           551:         return -EIO;
        !           552: 
        !           553:     // Write the initial BAT
        !           554:     if (lseek(fd, 3 * 512, SEEK_SET) < 0)
        !           555:         return -EIO;
        !           556: 
        !           557:     memset(buf, 0xFF, 512);
        !           558:     for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++)
        !           559:         if (write(fd, buf, 512) != 512)
        !           560:             return -EIO;
        !           561: 
        !           562: 
        !           563:     // Prepare the Dynamic Disk Header
        !           564:     memset(buf, 0, 1024);
        !           565: 
        !           566:     strncpy(dyndisk_header->magic, "cxsparse", 8);
        !           567: 
        !           568:     dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF);
        !           569:     dyndisk_header->table_offset = be64_to_cpu(3 * 512);
        !           570:     dyndisk_header->version = be32_to_cpu(0x00010000);
        !           571:     dyndisk_header->block_size = be32_to_cpu(block_size);
        !           572:     dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries);
        !           573: 
        !           574:     dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
        !           575: 
        !           576:     // Write the header
        !           577:     if (lseek(fd, 512, SEEK_SET) < 0)
        !           578:         return -EIO;
        !           579:     if (write(fd, buf, 1024) != 1024)
        !           580:         return -EIO;
        !           581: 
        !           582:     close(fd);
        !           583:     return 0;
        !           584: }
        !           585: 
        !           586: static void vpc_close(BlockDriverState *bs)
        !           587: {
        !           588:     BDRVVPCState *s = bs->opaque;
        !           589:     qemu_free(s->pagetable);
        !           590: #ifdef CACHE
        !           591:     qemu_free(s->pageentry_u8);
        !           592: #endif
        !           593:     bdrv_delete(s->hd);
        !           594: }
        !           595: 
        !           596: static QEMUOptionParameter vpc_create_options[] = {
        !           597:     {
        !           598:         .name = BLOCK_OPT_SIZE,
        !           599:         .type = OPT_SIZE,
        !           600:         .help = "Virtual disk size"
        !           601:     },
        !           602:     { NULL }
        !           603: };
        !           604: 
        !           605: static BlockDriver bdrv_vpc = {
        !           606:     .format_name       = "vpc",
        !           607:     .instance_size     = sizeof(BDRVVPCState),
        !           608:     .bdrv_probe                = vpc_probe,
        !           609:     .bdrv_open         = vpc_open,
        !           610:     .bdrv_read         = vpc_read,
        !           611:     .bdrv_write                = vpc_write,
        !           612:     .bdrv_close                = vpc_close,
        !           613:     .bdrv_create       = vpc_create,
        !           614: 
        !           615:     .create_options = vpc_create_options,
        !           616: };
        !           617: 
        !           618: static void bdrv_vpc_init(void)
        !           619: {
        !           620:     bdrv_register(&bdrv_vpc);
        !           621: }
        !           622: 
        !           623: block_init(bdrv_vpc_init);

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.