diff options
Diffstat (limited to 'block/io.c')
-rw-r--r-- | block/io.c | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/block/io.c b/block/io.c index 7e6abef269..2de7c77983 100644 --- a/block/io.c +++ b/block/io.c @@ -1803,10 +1803,13 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, /* Ignore errors. This is just providing extra information, it * is useful but not necessary. */ - if (!file_pnum) { - /* !file_pnum indicates an offset at or beyond the EOF; it is - * perfectly valid for the format block driver to point to such - * offsets, so catch it and mark everything as zero */ + if (ret2 & BDRV_BLOCK_EOF && + (!file_pnum || ret2 & BDRV_BLOCK_ZERO)) { + /* + * It is valid for the format block driver to read + * beyond the end of the underlying file's current + * size; such areas read as zero. + */ ret |= BDRV_BLOCK_ZERO; } else { /* Limit request to the range reported by the protocol driver */ @@ -1833,16 +1836,30 @@ static int64_t coroutine_fn bdrv_co_get_block_status_above(BlockDriverState *bs, { BlockDriverState *p; int64_t ret = 0; + bool first = true; assert(bs != base); for (p = bs; p != base; p = backing_bs(p)) { ret = bdrv_co_get_block_status(p, sector_num, nb_sectors, pnum, file); - if (ret < 0 || ret & BDRV_BLOCK_ALLOCATED) { + if (ret < 0) { + break; + } + if (ret & BDRV_BLOCK_ZERO && ret & BDRV_BLOCK_EOF && !first) { + /* + * Reading beyond the end of the file continues to read + * zeroes, but we can only widen the result to the + * unallocated length we learned from an earlier + * iteration. + */ + *pnum = nb_sectors; + } + if (ret & (BDRV_BLOCK_ZERO | BDRV_BLOCK_DATA)) { break; } /* [sector_num, pnum] unallocated on this layer, which could be only * the first part of [sector_num, nb_sectors]. */ nb_sectors = MIN(nb_sectors, *pnum); + first = false; } return ret; } |