From patchwork Fri Sep 19 09:12:40 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucian Petrut X-Patchwork-Id: 391181 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 91B0E140187 for ; Fri, 19 Sep 2014 19:15:54 +1000 (EST) Received: from localhost ([::1]:57023 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XUuIB-0007Hi-Cg for incoming@patchwork.ozlabs.org; Fri, 19 Sep 2014 05:15:51 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:36491) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XUuFa-0006eM-VX for qemu-devel@nongnu.org; Fri, 19 Sep 2014 05:13:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XUuFV-0008V0-GE for qemu-devel@nongnu.org; Fri, 19 Sep 2014 05:13:10 -0400 Received: from mail-wg0-x232.google.com ([2a00:1450:400c:c00::232]:46621) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XUuFV-0008Ul-6Q for qemu-devel@nongnu.org; Fri, 19 Sep 2014 05:13:05 -0400 Received: by mail-wg0-f50.google.com with SMTP id x13so2121056wgg.9 for ; Fri, 19 Sep 2014 02:12:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:mime-version:content-type; bh=zlIBc+gD531m4Nd7HfiitmOMMHTAAAmEXrFhjvqAoQE=; b=v1BXClGVDoQjpymUpR3dEZUGSynXq4MZMj661GKNNK3WCl1SkPX6/2sVK5exO+XgqY kn4OFej9qa6m0c8wtTXJcNGUWhDrkKNKtOLUpXWmMWRdeIuZc9IstCjuG9sx7cAtDrj8 I5TN9hhv0BBH4D8TCKkVTQJJvSTFQ4qnafKVSW8GpWpghqQL/s1uCdCGr0K7MhDefXXR trsPd2dR39CdRF0JwPvUu48aovtLZqhhrpDkKxksjISv7Yd3568yIhg3bxANFz5Qt9hj +LVDRGZFXCxjJs/sqyo94mPWj8hJl5SHdWXE2hj4Z+qvM9RRR7xB4BzsP98GOfLpUE+h 7djA== X-Received: by 10.194.58.244 with SMTP id u20mr11193993wjq.36.1411117979075; Fri, 19 Sep 2014 02:12:59 -0700 (PDT) Received: from localhost.localdomain ([89.46.161.178]) by mx.google.com with ESMTPSA id ub19sm1416335wib.9.2014.09.19.02.12.57 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 19 Sep 2014 02:12:58 -0700 (PDT) From: Lucian Petrut X-Google-Original-From: Lucian Petrut To: qemu-devel@nongnu.org Date: Fri, 19 Sep 2014 12:12:40 +0300 Message-Id: <1411117960-28416-1-git-send-email-lpetrut@cloudbasesolutions.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:400c:c00::232 X-Mailman-Approved-At: Fri, 19 Sep 2014 05:15:24 -0400 Cc: kwolf@redhat.com, Lucian Petrut , sw@weilnetz.de, jcody@redhat.com, apilotti@cloudbasesolutions.com, stefanha@redhat.com Subject: [Qemu-devel] [PATCH 1/1] vpc.c: Add VHD resize support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch introduces resize support for dynamic and fixed VHD images. Note that differencing VHD images do not support this operation. In order to resize dynamic VHDs, the BAT region may need to be extended. This may require moving the first data blocks, making room for it to expand. This required updating the according BAT entries for the moved blocks as well, as well as initializing the new BAT entries. In case of fixed VHDs, the only thing that needs to be done is moving and updating the footer. Note that this patch assumes that all the data blocks are written right after the BAT. Signed-off-by: Lucian Petrut --- block/vpc.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/block/vpc.c b/block/vpc.c index 055efc4..f3895cb 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -417,7 +417,7 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, * * Returns 0 on success and < 0 on error */ -static int rewrite_footer(BlockDriverState* bs) +static int rewrite_footer(BlockDriverState* bs, bool update_header) { int ret; BDRVVPCState *s = bs->opaque; @@ -427,6 +427,12 @@ static int rewrite_footer(BlockDriverState* bs) if (ret < 0) return ret; + if (update_header) { + ret = bdrv_pwrite_sync(bs->file, 0, s->footer_buf, HEADER_SIZE); + if (ret < 0) + return ret; + } + return 0; } @@ -466,7 +472,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) // Write new footer (the old one will be overwritten) s->free_data_block_offset += s->block_size + s->bitmap_size; - ret = rewrite_footer(bs); + ret = rewrite_footer(bs, false); if (ret < 0) goto fail; @@ -852,6 +858,174 @@ out: return ret; } + +static int vpc_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVVPCState *s = bs->opaque; + VHDFooter *footer = (VHDFooter *) s->footer_buf; + VHDDynDiskHeader *dyndisk_header; + void *buf = NULL; + int64_t new_total_sectors, old_bat_size, new_bat_size, + block_offset, new_block_offset, bat_offset; + int32_t bat_value, data_blocks_required; + int ret = 0; + uint16_t cyls = 0; + uint8_t heads = 0; + uint8_t secs_per_cyl = 0; + uint32_t new_num_bat_entries; + uint64_t index, block_index, new_bat_right_limit; + + if (offset & 511) { + error_report("The new size must be a multiple of 512."); + return -EINVAL; + } + + if (offset < bs->total_sectors * 512) { + error_report("Shrinking vhd images is not supported."); + return -ENOTSUP; + } + + if (cpu_to_be32(footer->type) == VHD_DIFFERENCING){ + error_report("Resizing differencing vhd images is not supported."); + return -ENOTSUP; + } + + old_bat_size = (s->max_table_entries * 4 + 511) & ~511; + new_total_sectors = offset / BDRV_SECTOR_SIZE; + + for (index = 0; new_total_sectors > (int64_t)cyls * heads * secs_per_cyl; + index++) { + if (calculate_geometry(new_total_sectors + index, &cyls, &heads, + &secs_per_cyl)) + { + return -EFBIG; + } + } + new_total_sectors = (int64_t) cyls * heads * secs_per_cyl; + new_num_bat_entries = (new_total_sectors + s->block_size / 512) / + (s->block_size / 512); + + if (cpu_to_be32(footer->type) == VHD_DYNAMIC){ + new_bat_size = (new_num_bat_entries * 4 + 511) & ~511; + /* Number of blocks required for extending the BAT */ + data_blocks_required = (new_bat_size - old_bat_size + + s->block_size - 1) / s->block_size; + new_bat_right_limit = s->bat_offset + old_bat_size + + data_blocks_required * + (s->block_size + s->bitmap_size); + + for (block_index = 0; block_index < + data_blocks_required; block_index++){ + /* + * The BAT has to be extended. We'll have to move the first + * data block(s) to the end of the file, making room for the + * BAT to expand. Also, the BAT entries have to be updated for + * the moved blocks. + */ + + block_offset = s->bat_offset + old_bat_size + + block_index * (s->block_size + s->bitmap_size); + if (block_offset >= s->free_data_block_offset){ + /* + * Do not allocate a new block for the BAT if no data blocks + * were previously allocated to the vhd image. + */ + s->free_data_block_offset += (new_bat_size - old_bat_size); + break; + } + + if (block_index == 0){ + buf = g_malloc(s->block_size + s->bitmap_size); + } + + ret = bdrv_pread(bs->file, block_offset, buf, + s->block_size + s->bitmap_size); + if (ret < 0) { + goto out; + } + + new_block_offset = s->free_data_block_offset < new_bat_right_limit ? + new_bat_right_limit : s->free_data_block_offset; + bdrv_pwrite_sync(bs->file, new_block_offset, buf, + s->block_size + s->bitmap_size); + if (ret < 0) { + goto out; + } + + for (index = 0; index < s->max_table_entries; index++) { + if (s->pagetable[index] == block_offset / BDRV_SECTOR_SIZE) { + s->pagetable[index] = block_offset; + bat_offset = s->bat_offset + (4 * index); + bat_value = be32_to_cpu(new_block_offset / BDRV_SECTOR_SIZE); + ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); + if (ret < 0) + goto out; + break; + } + } + + s->free_data_block_offset = new_block_offset + s->block_size + s->bitmap_size; + g_free(buf); + buf = NULL; + } + + buf = g_malloc(512); + memset(buf, 0xFF, 512); + + /* Extend the BAT */ + offset = s->bat_offset + old_bat_size; + for (index = 0; + index < (new_bat_size - old_bat_size) / 512; + index++) { + ret = bdrv_pwrite_sync(bs->file, offset, buf, 512); + if (ret < 0) { + goto out; + } + offset += 512; + } + + g_free(buf); + buf = g_malloc(1024); + + /* Update the Dynamic Disk Header */ + ret = bdrv_pread(bs->file, 512, buf, + 1024); + if (ret < 0) { + goto out; + } + + dyndisk_header = (VHDDynDiskHeader *) buf; + dyndisk_header->max_table_entries = be32_to_cpu(new_num_bat_entries); + dyndisk_header->checksum = 0; + dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); + ret = bdrv_pwrite_sync(bs->file, 512, buf, 1024); + if (ret < 0) { + goto out; + } + + } else { + s->free_data_block_offset = new_total_sectors * BDRV_SECTOR_SIZE; + } + + footer->cyls = be16_to_cpu(cyls); + footer->heads = heads; + footer->secs_per_cyl = secs_per_cyl; + footer->size = be64_to_cpu(new_total_sectors * BDRV_SECTOR_SIZE); + footer->checksum = 0; + footer->checksum = be32_to_cpu(vpc_checksum(s->footer_buf, HEADER_SIZE)); + + /* Rewrite the footer, copying to the image header in case of a dynamic vhd */ + rewrite_footer(bs, (cpu_to_be32(footer->type) != VHD_FIXED)); + if (ret < 0) + goto out; + +out: + if (buf) { + g_free(buf); + } + return ret; +} + static int vpc_has_zero_init(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; @@ -916,6 +1090,7 @@ static BlockDriver bdrv_vpc = { .bdrv_get_info = vpc_get_info, + .bdrv_truncate = vpc_truncate, .create_opts = &vpc_create_opts, .bdrv_has_zero_init = vpc_has_zero_init, };