From nobody Wed Oct 29 09:15:36 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1525268267210565.7047499524903; Wed, 2 May 2018 06:37:47 -0700 (PDT) Received: from localhost ([::1]:50524 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fDrx4-00074q-HU for importer@patchew.org; Wed, 02 May 2018 09:37:46 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44248) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fDruZ-0005ni-KE for qemu-devel@nongnu.org; Wed, 02 May 2018 09:35:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fDruW-00016m-F7 for qemu-devel@nongnu.org; Wed, 02 May 2018 09:35:11 -0400 Received: from mail-pg0-x241.google.com ([2607:f8b0:400e:c05::241]:32874) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fDruW-00016V-76; Wed, 02 May 2018 09:35:08 -0400 Received: by mail-pg0-x241.google.com with SMTP id i194-v6so10664961pgd.0; Wed, 02 May 2018 06:35:07 -0700 (PDT) Received: from VM_97_126_centos.localdomain ([119.28.64.94]) by smtp.gmail.com with ESMTPSA id w7sm21581732pfn.83.2018.05.02.06.35.05 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 02 May 2018 06:35:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=Q5A8A/ftti88D6dtw4tsBJgbLpWXYZ+/NoGMC5sJZ28=; b=dZbdChx5+2UjoCf+MJRl6AjBO+iD+HkeoAzqnB5Wp8/EargmpnpDmX+DpNCasHu4sT iuZTrFjDmavT8Fq0z8oMbbTFWSj5tWVpYOtBY1jBwvx+MC23OCb6C/DsCzIGhgaVwccL rmwvIRL/V38stB5en1Z+3qxJ/6suskw4ArHka5nYKjz6jmzCWP3noORWnwYaiXIgMH++ 95NvxZEsv0KV4fDuywMUZOgrbHbd5YOS1UusgCVElSMUUC6k4V4lq6de5Myjm0qzSiDL uPyK7YLPRtpnaIx6kwcHB/CSrR7a39mba5AS4wXDF71jarnmb+m5HxAeb8HNVPCu5BZK REeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=Q5A8A/ftti88D6dtw4tsBJgbLpWXYZ+/NoGMC5sJZ28=; b=UyIHRLgItQMLY4HN3uyOsJK5rjWCKQFZUIwqE9ZgNwqmpwoKy2K/fd2CG0iZBdbkxo YZlxMf+xdHjxRYoJuJIIrtp6lRYgokXqJpr9gttFVb+la3UJIkRj2y8hHxZmtcQ/NHSt rYhiYluC/C9zj1m0lb0O3PcLpV+VnqsdQ3QfEZmE7vO3rNVHvm6IXWsdly4nNlOPuh3k b7VZtqCX8INLrUGhNkbLomK+SM+c/H3FtiK2rXonDKnImDERfvh1cCY/vVs30t1rPuk8 z9b7MkUTZpn2a6y0bxBdPEDJA7OAi/0HcAUullXf9r6q3c8bq93WCS9Mw/uB29jpPuej JoGg== X-Gm-Message-State: ALQs6tAbuxFEWxTIwJfSIiUtjVK3JlPg/TZz0Z8bhzWlGAbGZeRXqWjA /q1M9N/lABrOK+bMk/OEuU+4I3iuhOCodJCA X-Google-Smtp-Source: AB8JxZojPP80F8uZVtSUOzLpkfaU/1LBGGMCmctC4ZYtywESNQsRWZNd69WAG19NfaNI4Cy7aaz/9Q== X-Received: by 2002:a17:902:f44:: with SMTP id 62-v6mr20347029ply.318.1525268106521; Wed, 02 May 2018 06:35:06 -0700 (PDT) From: Ivan Ren X-Google-Original-From: Ivan Ren To: qemu-devel@nongnu.org Date: Wed, 2 May 2018 21:34:53 +0800 Message-Id: <1525268093-531-1-git-send-email-ivanren@tencent.com> X-Mailer: git-send-email 1.8.3.1 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::241 Subject: [Qemu-devel] [PATCH] qemu-img: return allocated size for block device with qcow2 format X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, qemu-block@nongnu.org, mreitz@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" qemu-img info with a block device which has a qcow2 format always return 0 for disk size, and this can not reflect the qcow2 size and the used space of the block device. This patch return the allocated size of qcow2 as the disk size. Signed-off-by: Ivan Ren --- block/qcow2-bitmap.c | 69 +++++++++++++++++ block/qcow2.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++= ++++ block/qcow2.h | 42 ++++++++++ 3 files changed, 323 insertions(+) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 6e93ec4..2957a7a 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -636,6 +636,75 @@ fail: return NULL; } =20 +/* + * Get the highest allocated cluster index of bitmap + */ +int qcow2_get_bitmap_allocated_clusters(BlockDriverState *bs, + int64_t *p_highest_cluster_index, + int64_t nb_clusters) +{ + int ret; + BDRVQcow2State *s =3D bs->opaque; + Qcow2BitmapList *bm_list =3D NULL; + Qcow2Bitmap *bm; + + if (s->nb_bitmaps =3D=3D 0) { + return 0; + } + + ret =3D update_highest_cluster_index(s, s->bitmap_directory_offset, + s->bitmap_directory_size, + p_highest_cluster_index, nb_cluster= s); + if (ret =3D=3D 1) { + goto out; + } + + bm_list =3D bitmap_list_load(bs, s->bitmap_directory_offset, + s->bitmap_directory_size, NULL); + if (bm_list =3D=3D NULL) { + ret =3D -EINVAL; + goto out; + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { + uint64_t *bitmap_table =3D NULL; + int i; + + ret =3D update_highest_cluster_index(s, bm->table.offset, + bm->table.size * sizeof(uint64_t), + p_highest_cluster_index, nb_cluste= rs); + if (ret =3D=3D 1) { + goto out; + } + + ret =3D bitmap_table_load(bs, &bm->table, &bitmap_table); + if (ret < 0) { + goto out; + } + + for (i =3D 0; i < bm->table.size; ++i) { + uint64_t entry =3D bitmap_table[i]; + uint64_t offset =3D entry & BME_TABLE_ENTRY_OFFSET_MASK; + + /* ignore entry legality */ + if (offset =3D=3D 0) { + continue; + } + + ret =3D update_highest_cluster_index(s, offset, s->cluster_siz= e, + p_highest_cluster_index, nb_clust= ers); + if (ret =3D=3D 1) { + g_free(bitmap_table); + goto out; + } + } + g_free(bitmap_table); + } +out: + bitmap_list_free(bm_list); + return ret; +} + int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *r= es, void **refcount_table, int64_t *refcount_table_size) diff --git a/block/qcow2.c b/block/qcow2.c index ef68772..fa251af 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4493,6 +4493,217 @@ static QemuOptsList qcow2_create_opts =3D { } }; =20 +static int qcow2_get_l2_allocated_size(BlockDriverState *bs, + uint64_t l2_table_offset, + int64_t *p_highest_cluster_index, + int64_t nb_clusters) +{ + BDRVQcow2State *s =3D bs->opaque; + uint64_t *l2_table =3D NULL, l2_entry, cluster_offset; + int l2_size, ret, i, nb_csectors; + + /* malloc L2 table */ + l2_size =3D s->l2_size * sizeof(uint64_t); + if (l2_size > 0) { + l2_table =3D g_malloc(l2_size); + if (l2_table =3D=3D NULL) { + ret =3D -ENOMEM; + goto out; + } + /* load l2 table from image file */ + ret =3D bdrv_pread(bs->file, l2_table_offset, l2_table, l2_size); + if (ret < 0 || ret !=3D l2_size) { + goto out; + } + + ret =3D update_highest_cluster_index(s, l2_table_offset, l2_size, + p_highest_cluster_index, nb_clusters); + if (ret =3D=3D 1) { + goto out; + } + } + + for (i =3D 0; i < s->l2_size; i++) { + l2_entry =3D be64_to_cpu(l2_table[i]); + switch (qcow2_get_cluster_type(l2_entry)) { + case QCOW2_CLUSTER_COMPRESSED: + nb_csectors =3D ((l2_entry >> s->csize_shift) & s->csize_mask)= + 1; + l2_entry &=3D s->cluster_offset_mask; + ret =3D update_highest_cluster_index(s, l2_entry & ~511, + nb_csectors * 512, + p_highest_cluster_index, nb_cluste= rs); + if (ret =3D=3D 1) { + goto out; + } + break; + case QCOW2_CLUSTER_ZERO_ALLOC: + case QCOW2_CLUSTER_NORMAL: + cluster_offset =3D l2_entry & L2E_OFFSET_MASK; + + ret =3D update_highest_cluster_index(s, cluster_offset, + s->cluster_size, + p_highest_cluster_index, nb_cluste= rs); + if (ret =3D=3D 1) { + goto out; + } + + break; + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_UNALLOCATED: + break; + default: + abort(); /* some error happen */ + } + } + ret =3D 0; +out: + g_free(l2_table); + return ret; +} + +static int qcow2_get_l1_allocated_size(BlockDriverState *bs, + uint64_t l1_table_offset, + int l1_size, + int64_t *p_highest_cluster_index, + int64_t nb_clusters) +{ + BDRVQcow2State *s =3D bs->opaque; + uint64_t *l1_table =3D NULL, l2_offset; + int i, ret, l1_size2; + + /* malloca l1 table memory */ + l1_size2 =3D l1_size * sizeof(uint64_t); + if (l1_size2 > 0) { + l1_table =3D g_malloc(l1_size2); + if (!l1_table) { + ret =3D -ENOMEM; + goto out; + } + ret =3D bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); + if (ret < 0 || ret !=3D l1_size2) { + goto out; + } + + ret =3D update_highest_cluster_index(s, l1_table_offset, l1_size2, + p_highest_cluster_index, nb_cluste= rs); + if (ret =3D=3D 1) /* has reached the end */ + goto out; + } + + + for (i =3D 0; i < l1_size; i++) { + be64_to_cpus(&l1_table[i]); + l2_offset =3D l1_table[i]; + if (l2_offset) { + l2_offset &=3D L1E_OFFSET_MASK; + ret =3D qcow2_get_l2_allocated_size(bs, l2_offset, + p_highest_cluster_index, nb_clusters); + if (ret < 0 || ret =3D=3D 1) { + goto out; + } + } + } + + ret =3D 0; +out: + g_free(l1_table); + return ret; + +} + +/* Get block device allocated space for qcow2, aligned with cluster_size */ +static int64_t qcow2_get_block_allocated_size(BlockDriverState *bs) +{ + int64_t file_size, file_max_clusters; + uint64_t offset, allocated_size; + int64_t highest_cluster_index =3D 0; + int i, ret; + BDRVQcow2State *s =3D bs->opaque; + QCowSnapshot *sn; + + file_size =3D bdrv_getlength(bs->file->bs); + file_max_clusters =3D size_to_clusters(s, file_size); + + /* current image map */ + ret =3D qcow2_get_l1_allocated_size(bs, s->l1_table_offset, + s->l1_size, &highest_cluster_ind= ex, + file_max_clusters); + if (ret < 0 || ret =3D=3D 1) { + goto out; + } + + /* snapshot */ + for (i =3D 0; i < s->nb_snapshots; i++) { + sn =3D s->snapshots + i; + ret =3D qcow2_get_l1_allocated_size(bs, sn->l1_table_offset, + sn->l1_size, &highest_cluster_= index, + file_max_clusters); + if (ret < 0 || ret =3D=3D 1) { + goto out; + } + } + ret =3D update_highest_cluster_index(s, s->snapshots_offset, + s->snapshots_size, &highest_cluster_index, file_max_clus= ters); + if (ret =3D=3D 1) { + goto out; + } + + /* refcount table */ + ret =3D update_highest_cluster_index(s, s->refcount_table_offset, + s->refcount_table_size * sizeof(uint64_t), + &highest_cluster_index, file_max_clusters= ); + if (ret =3D=3D 1) { + goto out; + } + for (i =3D 0; i < s->refcount_table_size; i++) { + offset =3D s->refcount_table[i]; + ret =3D update_highest_cluster_index(s, offset, s->cluster_size, + &highest_cluster_index, file_max_clus= ters); + if (ret =3D=3D 1) { + goto out; + } + } + + /* encryption */ + if (s->crypto_header.length) { + ret =3D update_highest_cluster_index(s, s->crypto_header.offset, + s->crypto_header.length, + &highest_cluster_index, file_max_clust= ers); + if (ret =3D=3D 1) { + goto out; + } + } + + /* bitmap */ + ret =3D qcow2_get_bitmap_allocated_clusters(bs, &highest_cluster_index, + file_max_clusters); +out: + if (ret < 0) { + allocated_size =3D 0; /* if any error happen, return zero */ + } else { + allocated_size =3D MIN((highest_cluster_index + 1) * s->cluster_si= ze, + file_size); + } + + return allocated_size; +} + +static int64_t qcow2_get_allocated_file_size(BlockDriverState *bs) +{ + struct stat st; + if (stat(bs->filename, &st) < 0 || !S_ISBLK(st.st_mode)) { + goto get_file_size; + } + + return qcow2_get_block_allocated_size(bs); + +get_file_size: + if (bs->file) { + return bdrv_get_allocated_file_size(bs->file->bs); + } + return -ENOTSUP; +} + BlockDriver bdrv_qcow2 =3D { .format_name =3D "qcow2", .instance_size =3D sizeof(BDRVQcow2State), @@ -4516,6 +4727,7 @@ BlockDriver bdrv_qcow2 =3D { .bdrv_co_pwrite_zeroes =3D qcow2_co_pwrite_zeroes, .bdrv_co_pdiscard =3D qcow2_co_pdiscard, .bdrv_truncate =3D qcow2_truncate, + .bdrv_get_allocated_file_size =3D qcow2_get_allocated_file_size, .bdrv_co_pwritev_compressed =3D qcow2_co_pwritev_compressed, .bdrv_make_empty =3D qcow2_make_empty, =20 diff --git a/block/qcow2.h b/block/qcow2.h index adf5c39..9ed3771 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -466,6 +466,45 @@ static inline int64_t size_to_l1(BDRVQcow2State *s, in= t64_t size) return (size + (1ULL << shift) - 1) >> shift; } =20 +static inline int64_t offset_to_cluster_index(BDRVQcow2State *s, + uint64_t offset) +{ + return offset >> s->cluster_bits; +} + +/* + * Return value may be 0 or 1, 1 means the highest cluster + * index has reached the max device size, so There is no need + * to continue scanning. @*p_highest_cluster_index has the + * new highest cluster index + */ +static inline int update_highest_cluster_index(BDRVQcow2State *s, + int64_t offset, + int64_t size, + int64_t *p_highest_cluster_index, + int64_t nb_clusters) +{ + int64_t cluster_index; + cluster_index =3D offset_to_cluster_index(s, offset + size - 1); + + /* If necessary update the new highest index. + * Cluster Index exceed the device will be ignored. + */ + if (cluster_index > *p_highest_cluster_index + && cluster_index < nb_clusters) { + *p_highest_cluster_index =3D cluster_index; + } else { + return 0; + } + + + if (*p_highest_cluster_index =3D=3D (nb_clusters - 1)) { + return 1; /* have reached max */ + } else { + return 0; + } +} + static inline int offset_to_l1_index(BDRVQcow2State *s, uint64_t offset) { return offset >> (s->l2_bits + s->cluster_bits); @@ -668,6 +707,9 @@ void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64= _t offset); void qcow2_cache_discard(Qcow2Cache *c, void *table); =20 /* qcow2-bitmap.c functions */ +int qcow2_get_bitmap_allocated_clusters(BlockDriverState *bs, + int64_t *p_highest_cluster_index, + int64_t nb_clusters); int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *r= es, void **refcount_table, int64_t *refcount_table_size); --=20 1.8.3.1