Commit e6d7ec32 authored by Max Reitz's avatar Max Reitz Committed by Stefan Hajnoczi
Browse files

raw-posix: Fix raw_co_get_block_status() after EOF



As its comment states, raw_co_get_block_status() should unconditionally
return 0 and set *pnum to 0 for after EOF.

An assertion after lseek(..., SEEK_HOLE) tried to catch this case by
asserting that errno != -ENXIO (which would indicate a position after
the EOF); but it should be errno != ENXIO instead. Regardless of that,
there should be no such assertion at all. If bdrv_getlength() returned
an outdated value and the image has been resized outside of qemu,
lseek() will return with errno == ENXIO. Just return that value as an
error then.

Setting *pnum to 0 and returning 0 should not be done here, as in that
case we should update the device length as well. So, from qemu's
perspective, the file has not been resized; it's just that there was an
error querying sectors beyond a certain point (the actual file size).

Additionally, nb_sectors should be clamped against the image end. This
was probably not an issue if FIEMAP or SEEK_HOLE/SEEK_DATA worked, but
the fallback did not take this case into account.

Reported-by: default avatarKevin Wolf <kwolf@redhat.com>
Signed-off-by: default avatarMax Reitz <mreitz@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
Reviewed-by: default avatarKevin Wolf <kwolf@redhat.com>
Message-id: 1414148280-17949-2-git-send-email-mreitz@redhat.com
Signed-off-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
parent f76faeda
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -1535,10 +1535,6 @@ static int64_t try_seek_hole(BlockDriverState *bs, off_t start, off_t *data,

    *hole = lseek(s->fd, start, SEEK_HOLE);
    if (*hole == -1) {
        /* -ENXIO indicates that sector_num was past the end of the file.
         * There is a virtual hole there.  */
        assert(errno != -ENXIO);

        return -errno;
    }

@@ -1578,6 +1574,7 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
                                                    int nb_sectors, int *pnum)
{
    off_t start, data = 0, hole = 0;
    int64_t total_size;
    int64_t ret;

    ret = fd_open(bs);
@@ -1586,6 +1583,15 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
    }

    start = sector_num * BDRV_SECTOR_SIZE;
    total_size = bdrv_getlength(bs);
    if (total_size < 0) {
        return total_size;
    } else if (start >= total_size) {
        *pnum = 0;
        return 0;
    } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) {
        nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE);
    }

    ret = try_seek_hole(bs, start, &data, &hole, pnum);
    if (ret < 0) {