From nobody Mon Feb 9 11:30:28 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1554929961; cv=none; d=zoho.com; s=zohoarc; b=Hs6MR0Om4K74u0FBeMCxZCCx4/9mo8Yt4CJgC3ZVU8eQzyzw3+eto6Gx//ef5M6GG56PaK0hk3PsD5yKUiWUDitYBvEIXCJ2RlfPbQOp/oM+3Ct0h2pflYnyIUe47o8nep2lGIz0Lf4JebxALBjwM2racrNmAqG50y8943ywtCI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1554929961; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=oPZi7/+/s3OS3ymWMpDQSvLeDJJSieOOO1ipDsh/trU=; b=JKPbiF3CX+X0kLaQym7NJkElaYABZjUsyDVaL1XQNhXeaf1NfAOm0+KEZTgdHphFIjcfIk/ybdy4VT656MhSSC7ap3k21GzMOu5wxQTIoeuhP5UdCnnqRLSz2JP2wcRQa6sG2eIFhMvEb+Ce+Cijz6o6L12u9ufRDxNfT5hlIVU= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 155492996158179.71389177456479; Wed, 10 Apr 2019 13:59:21 -0700 (PDT) Received: from localhost ([127.0.0.1]:37803 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEKJQ-0001ap-Hb for importer@patchew.org; Wed, 10 Apr 2019 16:59:16 -0400 Received: from eggs.gnu.org ([209.51.188.92]:42662) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hEKHh-0000er-PY for qemu-devel@nongnu.org; Wed, 10 Apr 2019 16:57:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hEKHg-00089M-CJ for qemu-devel@nongnu.org; Wed, 10 Apr 2019 16:57:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:37058) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hEKHc-00086E-69; Wed, 10 Apr 2019 16:57:24 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 74648C075D73; Wed, 10 Apr 2019 20:57:23 +0000 (UTC) Received: from localhost (unknown [10.40.205.69]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D1BC95C73E; Wed, 10 Apr 2019 20:57:22 +0000 (UTC) From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 10 Apr 2019 22:57:11 +0200 Message-Id: <20190410205715.2209-3-mreitz@redhat.com> In-Reply-To: <20190410205715.2209-1-mreitz@redhat.com> References: <20190410205715.2209-1-mreitz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Wed, 10 Apr 2019 20:57:23 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2 2/6] qemu-img: Add salvaging mode to convert 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: Kevin Wolf , Vladimir Sementsov-Ogievskiy , qemu-devel@nongnu.org, Max Reitz , "Denis V . Lunev" , Andrey Shinkevich Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" This adds a salvaging mode (--salvage) to qemu-img convert which ignores read errors and treats the respective areas as containing only zeroes. This can be used for instance to at least partially recover the data from terminally corrupted qcow2 images. Signed-off-by: Max Reitz --- qemu-img.c | 85 ++++++++++++++++++++++++++++++++++++------------ qemu-img-cmds.hx | 4 +-- qemu-img.texi | 5 +++ 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index c53666aa41..c2216e67a6 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -66,6 +66,7 @@ enum { OPTION_SIZE =3D 264, OPTION_PREALLOCATION =3D 265, OPTION_SHRINK =3D 266, + OPTION_SALVAGE =3D 267, }; =20 typedef enum OutputFormat { @@ -1569,6 +1570,7 @@ typedef struct ImgConvertState { int64_t target_backing_sectors; /* negative if unknown */ bool wr_in_order; bool copy_range; + bool salvage; bool quiet; int min_sparse; int alignment; @@ -1616,25 +1618,44 @@ static int convert_iteration_sectors(ImgConvertStat= e *s, int64_t sector_num) } =20 if (s->sector_next_status <=3D sector_num) { - int64_t count =3D n * BDRV_SECTOR_SIZE; + uint64_t offset =3D (sector_num - src_cur_offset) * BDRV_SECTOR_SI= ZE; + int64_t count; =20 - if (s->target_has_backing) { + do { + count =3D n * BDRV_SECTOR_SIZE; + + if (s->target_has_backing) { + ret =3D bdrv_block_status(blk_bs(s->src[src_cur]), offset, + count, &count, NULL, NULL); + } else { + ret =3D bdrv_block_status_above(blk_bs(s->src[src_cur]), N= ULL, + offset, count, &count, NULL, + NULL); + } + + if (ret < 0) { + if (s->salvage) { + if (n =3D=3D 1) { + if (!s->quiet) { + warn_report("error while reading block status = at " + "offset %" PRIu64 ": %s", offset, + strerror(-ret)); + } + /* Just try to read the data, then */ + ret =3D BDRV_BLOCK_DATA; + count =3D BDRV_SECTOR_SIZE; + } else { + /* Retry on a shorter range */ + n =3D DIV_ROUND_UP(n, 4); + } + } else { + error_report("error while reading block status at offs= et " + "%" PRIu64 ": %s", offset, strerror(-ret)= ); + return ret; + } + } + } while (ret < 0); =20 - ret =3D bdrv_block_status(blk_bs(s->src[src_cur]), - (sector_num - src_cur_offset) * - BDRV_SECTOR_SIZE, - count, &count, NULL, NULL); - } else { - ret =3D bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL, - (sector_num - src_cur_offset) * - BDRV_SECTOR_SIZE, - count, &count, NULL, NULL); - } - if (ret < 0) { - error_report("error while reading block status of sector %" PR= Id64 - ": %s", sector_num, strerror(-ret)); - return ret; - } n =3D DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); =20 if (ret & BDRV_BLOCK_ZERO) { @@ -1671,6 +1692,7 @@ static int convert_iteration_sectors(ImgConvertState = *s, int64_t sector_num) static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector= _num, int nb_sectors, uint8_t *buf) { + uint64_t single_read_until =3D 0; int n, ret; QEMUIOVector qiov; =20 @@ -1679,6 +1701,7 @@ static int coroutine_fn convert_co_read(ImgConvertSta= te *s, int64_t sector_num, BlockBackend *blk; int src_cur; int64_t bs_sectors, src_cur_offset; + uint64_t offset; =20 /* In the case of compression with multiple source files, we can g= et a * nb_sectors that spreads into the next part. So we must be able = to @@ -1687,14 +1710,30 @@ static int coroutine_fn convert_co_read(ImgConvertS= tate *s, int64_t sector_num, blk =3D s->src[src_cur]; bs_sectors =3D s->src_sectors[src_cur]; =20 + offset =3D (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; + n =3D MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); + if (single_read_until > offset) { + n =3D 1; + } qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS); =20 - ret =3D blk_co_preadv( - blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, &qiov, 0); + ret =3D blk_co_preadv(blk, offset, n << BDRV_SECTOR_BITS, &qiov, 0= ); if (ret < 0) { - return ret; + if (s->salvage) { + if (n > 1) { + single_read_until =3D offset + (n << BDRV_SECTOR_BITS); + continue; + } else { + if (!s->quiet) { + warn_report("error while reading offset %" PRIu64 + ": %s", offset, strerror(-ret)); + } + memset(buf, 0, BDRV_SECTOR_SIZE); + } + } else { + return ret; + } } =20 sector_num +=3D n; @@ -2028,6 +2067,7 @@ static int img_convert(int argc, char **argv) {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS= }, + {"salvage", no_argument, 0, OPTION_SALVAGE}, {0, 0, 0, 0} }; c =3D getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", @@ -2145,6 +2185,9 @@ static int img_convert(int argc, char **argv) case OPTION_IMAGE_OPTS: image_opts =3D true; break; + case OPTION_SALVAGE: + s.salvage =3D true; + break; case OPTION_TARGET_IMAGE_OPTS: tgt_image_opts =3D true; break; diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 1526f327a5..d6f444586c 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -44,9 +44,9 @@ STEXI ETEXI =20 DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U= ] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fm= t] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m = num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U= ] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fm= t] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m = num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename= ") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-op= ts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_c= ache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l= @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W= ] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-op= ts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_c= ache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l= @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W= ] [--salvage] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI =20 DEF("create", img_create, diff --git a/qemu-img.texi b/qemu-img.texi index 3b6710a580..827ee9fe32 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -175,6 +175,11 @@ improve performance if the data is remote, such as wit= h NFS or iSCSI backends, but will not automatically sparsify zero sectors, and may result in a fully allocated target image depending on the host support for getting allocation information. +@item --salvage +Try to ignore I/O errors when reading. Unless in quiet mode (@code{-q}), = errors +will still be printed. Areas that cannot be read from the source will be +treated as containing only zeroes. This option has no effect in copy offl= oading +mode (@code{-C}). @end table =20 Parameters to dd subcommand: --=20 2.20.1