From patchwork Fri Apr 15 18:05:34 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Prasad Joshi X-Patchwork-Id: 711121 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p3FI5X9m004735 for ; Fri, 15 Apr 2011 18:05:33 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755639Ab1DOSFa (ORCPT ); Fri, 15 Apr 2011 14:05:30 -0400 Received: from mail-ww0-f44.google.com ([74.125.82.44]:65105 "EHLO mail-ww0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755519Ab1DOSF3 (ORCPT ); Fri, 15 Apr 2011 14:05:29 -0400 Received: by wwa36 with SMTP id 36so3504729wwa.1 for ; Fri, 15 Apr 2011 11:05:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:from:to:cc:subject:date:message-id:x-mailer; bh=oUzPycenhlRFEPpaRAkkOTcVfArpsUG/TIXWtLCvQ2w=; b=GBGz922uHuP9B4A8/IzQWbY7kepcWs0u8/yH2A606BYMzcYAzWXDPrVOFNkJsunT0f oIEMJ3Vz6PuIGzPfegPYIYBxxVWJ/U6mZOsUURYqpofzvMJPzABD7hdsAQzvE5XlwkYE Zbgu0v8PRyfHPGi8e+B9SbtDLa5PW/xhNJnX4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer; b=WnRS/HLO+OfASuLIrTpthnsRNhfSS/USDCxL8x9ziwtooNPxUp1lJFntAGDQBNIS5a GM0eh33k9sFgOrKMyCyq8IWybEm58FnDNVsX11iU8OWMGHwQehPmgljj+WmFzdUUgsYi Rqc7yYaPp/nwqz4RY+tGC98RLvvB2UE3CF2OA= Received: by 10.227.196.208 with SMTP id eh16mr2350981wbb.224.1302890727728; Fri, 15 Apr 2011 11:05:27 -0700 (PDT) Received: from prasad-kvm.localdomain (pineapple.rdg.ac.uk [134.225.206.123]) by mx.google.com with ESMTPS id e13sm1763982wbi.6.2011.04.15.11.05.23 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 15 Apr 2011 11:05:24 -0700 (PDT) Received: by prasad-kvm.localdomain (Postfix, from userid 1000) id 9BDCB26E006E; Fri, 15 Apr 2011 19:05:35 +0100 (BST) From: Prasad Joshi To: prasadjoshi124@gmail.com Cc: mingo@elte.hu, kvm@vger.kernel.org, penberg@kernel.org, asias.hejun@gmail.com, gorcunov@gmail.com, levinsasha928@gmail.com, kwolf@redhat.com, stefanha@linux.vnet.ibm.com, chaitanyakulkarni15@gmail.com Subject: [PATCH] kvm tool: QCOW version 1 write support. Date: Fri, 15 Apr 2011 19:05:34 +0100 Message-Id: <1302890734-7871-1-git-send-email-prasadjoshi124@gmail.com> X-Mailer: git-send-email 1.7.1 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Fri, 15 Apr 2011 18:05:33 +0000 (UTC) The code is based on the following QCOW 1 image format specification: http://people.gnome.org/~markmc/qcow-image-format-version-1.html Signed-off-by: Prasad Joshi --- tools/kvm/qcow.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 147 insertions(+), 1 deletions(-) diff --git a/tools/kvm/qcow.c b/tools/kvm/qcow.c index e6d5897..ff73c55 100644 --- a/tools/kvm/qcow.c +++ b/tools/kvm/qcow.c @@ -134,8 +134,154 @@ error: return -1; } -static int qcow1_write_sector(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len) +static inline u64 get_file_length(int fd) { + struct stat st; + + if (fstat(fd, &st) < 0) + return 0; + return st.st_size; +} + +static inline u64 align(u64 address, uint32_t size) +{ + return (address + size) & (~(size - 1)); +} + +static int qcow_pwrite_with_sync(int fd, void *buf, size_t count, off_t offset) +{ + if (pwrite_in_full(fd, buf, count, offset) < 0) + return -1; + + if (fsync(fd) < 0) + return -1; + + return 0; +} + +static uint32_t qcow1_write_cluster(struct qcow *q, uint64_t offset, void *buf, + uint32_t src_len) +{ + struct qcow1_header *header = q->header; + struct qcow_table *table = &q->table; + uint32_t length; + + uint32_t l2_table_size; + u64 l2_table_offset; + u64 *l2_table; + u64 l2_idx; + + uint32_t clust_size; + u64 clust_offset; + u64 clust_start; + + u64 l1_idx; + u64 tmp; + + l2_table_size = 1 << header->l2_bits; + clust_size = 1 << header->cluster_bits; + + l1_idx = get_l1_index(q, offset); + if (l1_idx >= table->table_size) + goto error; + + l2_idx = get_l2_index(q, offset); + if (l2_idx >= l2_table_size) + goto error; + + clust_offset = get_cluster_offset(q, offset); + if (clust_offset >= clust_size) + goto error; + + length = clust_size - clust_offset; + if (length > src_len) + length = src_len; + + l2_table = calloc(l2_table_size, sizeof(u64)); + if (!l2_table) + goto error; + + l2_table_offset = table->l1_table[l1_idx]; + if (l2_table_offset) { + if (pread_in_full(q->fd, l2_table, l2_table_size * sizeof(u64), + l2_table_offset) < 0) + goto free_l2; + } else { + /* + * 1. write a level 2 table of zeros at the end of the file + * 2. update the level1 table + */ + l2_table_offset = align(get_file_length(q->fd), clust_size); + table->l1_table[l1_idx] = l2_table_offset; + + if (qcow_pwrite_with_sync(q->fd, l2_table, + l2_table_size * sizeof(u64), + l2_table_offset) < 0) + goto free_l2; + + tmp = cpu_to_be64(l2_table_offset); + if (qcow_pwrite_with_sync(q->fd, &tmp, sizeof(tmp), + header->l1_table_offset + + (l1_idx * sizeof(u64))) < 0) + goto free_l2; + } + + clust_start = be64_to_cpu(l2_table[l2_idx]); + free(l2_table); + if (clust_start) { + if (qcow_pwrite_with_sync(q->fd, buf, length, + clust_start + clust_offset) < 0) + goto error; + } else { + /* + * Follow the write order to avoid metadata loss + * 1. write the data at the end of the file + * 2. update the l2_table with new + */ + clust_start = align(get_file_length(q->fd), clust_size); + if (qcow_pwrite_with_sync(q->fd, buf, length, + clust_start + clust_offset) < 0) + goto error; + + tmp = cpu_to_be64(clust_start); + if (qcow_pwrite_with_sync(q->fd, &tmp, sizeof(tmp), + l2_table_offset + + (l2_idx * sizeof(u64))) < 0) + goto error; + } + return length; +free_l2: + free(l2_table); +error: + return 0; +} + +static int qcow1_write_sector(struct disk_image *self, uint64_t sector, + void *src, uint32_t src_len) +{ + struct qcow *q = self->priv; + struct qcow1_header *header = q->header; + uint32_t length = 0; + char *buf = src; + uint64_t offset; + uint32_t nr; + + while (length < src_len) { + offset = sector << SECTOR_SHIFT; + if (offset >= header->size) + goto error; + + nr = qcow1_write_cluster(q, offset, buf, src_len - length); + if (!nr) + goto error; + + length += nr; + buf += nr; + sector += (nr >> SECTOR_SHIFT); + } + + return 0; +error: return -1; }